【注意】最后更新于 June 17, 2020,文中内容可能已过时,请谨慎使用。
空指针问题
就在昨日使用 Go go-playground/validator
库时,因忘记手动 new validate
实例,被坑了一个多小时。
问题场景,在 Go 一个项目里,我使用了第三方验证库github.com/go-playground/validator/v10
一使用这行代码,err := validate.Struct(&device)
1
2
3
4
5
6
7
8
9
10
11
12
| echo: http: panic serving [::1]:51013: runtime error: invalid memory address or nil pointer dereference
goroutine 5 [running]:
net/http.(*conn).serve.func1(0xc00007a000)
/usr/local/go/src/net/http/server.go:1772 +0x139
panic(0x17365e0, 0x1e2d660)
/usr/local/go/src/runtime/panic.go:975 +0x3e3
github.com/go-playground/validator/v10.(*Validate).StructCtx(0x0, 0x1990360, 0xc0000b4008, 0x175b8e0, 0xc0004840e0, 0x100, 0x23fffff)
/Users/bing/go/pkg/mod/github.com/go-playground/validator/v10@v10.3.0/validator_instance.go:299 +0x21c
github.com/go-playground/validator/v10.(*Validate).Struct(...)
/Users/bing/go/pkg/mod/github.com/go-playground/validator/v10@v10.3.0/validator_instance.go:277
neox-device/handler.Store(0x19a2980, 0xc00007a140, 0x10bdc56, 0x5ee981c3)
/Users/bing/website/neox-project/neox-device/handler/device.go:35 +0x20b
|
直到我看到了,这个 issues 中的这句话,Forgot to initialise validator validate = validator.New()
事后和大学同学吐糟,他推荐我用这个https://github.com/asaskevich/govalidator
,(话说不用 New)
配置文件的烦恼
目前我司 Go 项目还尚小,再做项目环境配置分离时,刚开始我便选择了 viper,采用 .yaml
文件的方式,主要配置目前也很简单,是数据库链接相关,如下:
1
2
3
4
| serverAddress: ":9112"
mongodb:
dsn: "mongodb://host:port"
database: "neox_test"
|
读取配置文件,也就是使用 viper 提供的提供的方式做,那么问题来了?
- 这个 config.yaml 文件要不进入版本控制?
答案:肯定是不能进入版本控制的,因为开发环境、测试环境、生成环境是配置的值是不一样的。
我在构建这个服务时是通过 Dockerfile 来做的(阿里云提供的镜像服务,根据 tag 自动化根据 Dockerfile 再私有镜像服务内完成构建),再发布机使用 docker pull
拉去远端镜像,然后docker run
启动此服务,那么问题就来了。
- 启动此服务时是需要将配置文件
config.yaml
再宿主机创建,然后再挂载到容器内的,否则错误Failed to find configuration config/config.yml
答案1:简单服务器上创建一个 config.yaml 的配置文件,每次docker run
服务,挂载进去不就好了吗,(是一种方案)
答案2:我们能不能像 Laravel 一样使用 env
这种解决方式,如果有 env 则走 env 文件,否则走环境变量的方式,生产和测试服再启动 docker 容器时,将环境变量注入进去。
和同事聊了一下,第一种方案一看到了头,第二种方案至少我还没捣鼓过,二话不说,直接第二种方案。
因为我使用的是 viper 库,而我需要的是读取 env 配置文件,这就坑了,此库啥都好,就是对读取 env 配置文件不友好(死活读不到),折腾了一个多小时,果断放弃(再有限的时间没找到最优解),采用了这个库godotenv
一行代码搞定(爽歪歪),以下贴出部分代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| package main
import (
"fmt"
_ "github.com/joho/godotenv/autoload"
"os"
)
func init() {
dsn := os.Getenv("MONGODB_DSN")
db := os.Getenv("MONGODB_DB")
fmt.Printf("DSN:%s DB:%s", dsn, db)
fmt.Println("____________________________________O/_______")
if err := mgm.SetDefaultConfig(&mgm.Config{CtxTimeout: 12 * time.Second}, db, options.Client().ApplyURI(dsn)); err != nil {
panic(fmt.Errorf("Init mongodb error : %s", err))
}
}
|
这里是通过项目项目的 .env
文件读取了,本地的数据库连接配置,那么如果部署到测试或者线上呢?
刚不是说了吗,通过启动 docker 容器将环境变量注入进去,如我的启动命令:
docker run -p 9112:9112 --env MONGODB_DSN="mongodb://10.0.13.118:27017" --env MONGODB_DB="neox_test" --env SERVER_ADDR=":9112" device:0.01
这里有个小姿势,每增加一个环境变量就需要一个额外 --env
参数(不是只有一个)
Docker 设置多个环境变量
应用一
通过-e
、--env
、--env-file
示例:如下
.env-example 文件内容如下:
1
2
3
| (base) ➜ docker run -e MYVAR1 --env MYVAR2=foo --env-file ./.env-example ubuntu env|grep -E 'App|MY'
App="NeoX device"
MYVAR2=foo
|
应用二
导出并使用本地的环境变量:
1
2
3
| (base) ➜ export VAR1=VALUE1
(base) ➜ export VAR2=VALUE2
(base) ➜ docker run --env VAR1 --env VAR2 ubuntu env | grep VAR
|
应用三
Dockerfile 里设置
1
2
| ENV VAR1=VALUE1
ENV VAR2=VALUE2
|