# 前言

go之前对第三方包的管理不上心,其他语言比如python有pip,nodejs有npm,而go却没有一个官方的管理工具。

在go 1.11之前,开发者需要要关注GOPATH环境变量,这对于开发者来说不友好。

经过几次变更后,go于1.12版本开始正式使用go Module,go终于有了一个官方的处理方式,开发者也可以抛弃GOPATH了。

本次使用的go版本为1.15.6,建议使用1.13或以上的版本,旧的方式就不要再关注了,让它随风而去吧。

go version go1.15.6 darwin/amd64

本次使用的系统为Ubuntu,实验路径/opt/golang

root@xxg:/opt/golang# pwd
/opt/golang

# 第三方包

假设我们有个项目叫caseshow,我们先创建这个目录,并初始化一下

mkdir caseshow
cd caseshow
go mod init caseshow

此时在caseshow目录下会自动产生一个go.mod文件,内容如下:

module caseshow

go 1.15

比如我们想使用一个redis客户端:github.com/go-redis/redis/v8,在当前路径下直接执行go get命令即可:

go get github.com/go-redis/redis/v8

最近访问github很慢,可以先设置一下代理

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.io,direct

设置好之后,下载第三方库

root@xxg:/opt/golang/caseshow# go get github.com/go-redis/redis/v8
go: downloading github.com/go-redis/redis v6.15.9+incompatible
go: downloading github.com/go-redis/redis/v8 v8.4.2
go: github.com/go-redis/redis/v8 upgrade => v8.4.2
go: downloading go.opentelemetry.io/otel v0.14.0
go: downloading github.com/cespare/xxhash/v2 v2.1.1
go: downloading github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f

目录下会多一个go.sum文件,go.mod文件里面也多了关于redis库的内容

root@xxg:/opt/golang/caseshow# ls
go.mod  go.sum
root@xxg:/opt/golang/caseshow# cat go.mod 
module caseshow

go 1.15

require github.com/go-redis/redis/v8 v8.4.2 // indirect

之后就可以直接用了,写个redis.go测试下:

package main

import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
)

var ctx = context.Background()

func ExampleClient() {
	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password set
		DB:       0,  // use default DB
	})

	err := rdb.Set(ctx, "key", "value", 0).Err()
	if err != nil {
		panic(err)
	}

	val, err := rdb.Get(ctx, "key").Result()
	if err != nil {
		panic(err)
	}
	fmt.Println("key", val)

	val2, err := rdb.Get(ctx, "key2").Result()
	if err == redis.Nil {
		fmt.Println("key2 does not exist")
	} else if err != nil {
		panic(err)
	} else {
		fmt.Println("key2", val2)
	}
	// Output: key value
	// key2 does not exist
}

func main() {
	ExampleClient()
}

测试正常:

root@xxg:/opt/golang/caseshow# go run redis.go
key value
key2 does not exist

整个过程没有GOPATH什么事。

# 本地包

在项目开发中会复用本地的一些代码,这就涉及到了本地包的引入。

# 情况0 同路径引用其他文件

无需import,直接使用对应的方法。

go build时,在后面加上要用的文件名即可,比如

go build main.go main_command.go 

go run调试时,在后面加上要用的文件名即可,比如

go run main.go main_command.go 

# 情况1 同目录下

在同路径下不能存在多个package名,如果要定义其他的package,可以创建一个对应名字的目录,把代码放下面,之后再引用。

假设在caseshow中有个本地开发的包mymodule,里面有个方法Pprint,结构如下

# tree
.
├── go.mod
├── go.sum
├── mymodule
│   └── pprint.go
└── redis.go

1 directory, 4 files

pprint.go的代码如下:

package mymodule

import "fmt"

func Pprint(a string){
    fmt.Println("pprint ", a)
}

如果caseshow目录下的其他地方要用这个方法,则使用caseshow + package名称导入即可。 比如main.go:

package main

import "caseshow/mymodule"

func main(){
    mymodule.Pprint("jack")
}

直接运行:

# go run main.go 
pprint  jack

# 情况2 不同目录下

假设有另一个项目caselive,与caseshow同级,该目录下有个包yourmodule,里面有个方法Yprint:

# tree
.
├── caselive
│   ├── go.mod
│   ├── main.go
│   └── yourmodule
│       └── yprint.go
└── caseshow
    ├── go.mod
    ├── go.sum
    ├── main.go
    ├── mymodule
    │   └── pprint.go
    └── redis.go

4 directories, 8 files

yprint.go的内容如下:

package yourmodule

import "fmt"

func Yprint(a string){
    fmt.Println("yprint ", a)
}

此时在caseshow中要调用caselive中yourmodule的Yprint方法,可以用replace参数来实现。

在caseshow的go.mod中增加caselive的引入:

require mypackage v0.0.0
replace mypackage => ../mypackage

完整内容如下:

module caseshow

go 1.15

require github.com/go-redis/redis/v8 v8.4.2 // indirect
require caselive v0.0.0

replace caselive => ../caselive

之后就能直接调用了:

package main

import "caseshow/mymodule"
import "caselive/yourmodule"

func main(){
    mymodule.Pprint("jack")
    yourmodule.Yprint("jack")
}

运行测试下:

# go run main.go 
pprint  jack
yprint  jack