Professional Documents
Culture Documents
ego v0.3.10 使用教程
ego v0.3.10 使用教程
录
致谢
快速开始
核心模块
编译
配置
日志
服务模块
HTTP服务
gRPC服务
治理服务
任务模块
短时任务Job
定时任务Cron
客户端模块
gRPC
HTTP
GORM
Redis
治理模块
监控信息
最佳实践
监控查询语句
阿里云日志查询语句
错误处理
服务关闭
致谢
书栈网仅提供文档编写、整理、归类等功能,以及对文档内容的生成和导出工具。
文档内容由网友们编写和整理,书栈网难以确认文档内容知识点是否错漏。如果您在阅读文档获取
知识的时候,发现文档内容有不恰当的地方,请向我们反馈,让我们共同携手,将知识准确、高效且有
效地传递给每一个人。
同时,如果您在日常工作、生活和学习中遇到有价值有营养的知识文档,欢迎分享到书栈网,为知
识的传承献上您的一份力量!
如果当前文档生成时间太久,请到书栈网获取最新的文档,以跟上知识更新换代的步伐。
内容来源:gocn https://ego.gocn.vip/
文档地址:http://www.bookstack.cn/books/ego-0.3.10-zh
书栈官网:https://www.bookstack.cn
书栈开源:https://github.com/TruthHun
分享,让知识传承更久远! 感谢知识的创造者,感谢知识的分享者,也感谢每一位阅读到此处的
读者,因为我们都将成为知识的传承者。
快速开始
Example
HelloWorld
1. package main
2. import (
3. "github.com/gin-gonic/gin"
4. "github.com/gotomicro/ego"
5. "github.com/gotomicro/ego/core/elog"
6. "github.com/gotomicro/ego/server"
7. "github.com/gotomicro/ego/server/egin"
8. )
9. // export EGO_DEBUG=true && go run main.go --config=config.toml
10. func main() {
11. if err := ego.New().Serve(func() *egin.Component {
12. server := egin.Load("server.http").Build()
13. server.GET("/hello", func(ctx *gin.Context) {
14. ctx.JSON(200, "Hello Ego")
15. return
16. })
17. return server
18. }()).Run(); err != nil {
19. elog.Panic("startup", elog.Any("err", err))
20. }
21. }
使用命令行运行
如下所示
这个时候我们可以发送一个指令,得到如下结果
编译
配置
日志
编译
Example
使用EGO框架的应用会在编译期注入许多必要信息,方便后续排查问题。该方案被大量Go应用所使用,
例如istio、prometheus等。我们使用的编译脚本核心内容如下所示。
编译脚本
查看编译版本信息
查看帮助文档
输入 --help 可以看到应用支持的指令
查看运行时信息
我们启动服务
1. # HELP ego_build_info
2. # TYPE ego_build_info gauge
ego_build_info{app_version="b0807b91aca95b6eb6daafa9195c467fac0c350b-dirty",build_time
11:48:35",ego_version="0.1.0",go_version="go1.15.2",mode="dev",name="hello",region
3. 12-04 11:49:02",zone="ali-3"} 1.607053742679e+12
配置
Example
动态配置
系统内置
文本配置
框架默认开启动态配置
框架默认内志支持文本更改日志级别,动态生效
代码示例
动态配置获取单个配置信息
1. package main
2.
3. import (
4. "github.com/gotomicro/ego"
5. "github.com/gotomicro/ego/core/econf"
6. "github.com/gotomicro/ego/core/elog"
7. "time"
8. )
9.
10. // export EGO_DEBUG=true && go run main.go --config=config.toml
11. func main() {
12. if err := ego.New(ego.WithHang(true)).Invoker(func() error {
13. go func() {
14. // 循环打印配置
15. for {
动态配置获取结构体配置信息
1. package main
2.
3. import (
4. "github.com/gotomicro/ego"
5. "github.com/gotomicro/ego/core/econf"
6. "github.com/gotomicro/ego/core/elog"
7. "time"
8. )
9.
10. // export EGO_DEBUG=true && go run main.go --config=config.toml
11. func main() {
12. if err := ego.New(ego.WithHang(true)).Invoker(func() error {
13. p := People{}
14. // 初始化
15. err := econf.UnmarshalKey("people", &p)
16. if err != nil {
17. panic(err.Error())
18. }
19. // 监听
20. econf.OnChange(func(config *econf.Configuration) {
21. err := config.UnmarshalKey("people", &p)
22. if err != nil {
23. elog.Panic("unmarshal", elog.FieldErr(err))
24. }
25. })
26.
27. go func() {
28. // 循环打印配置
29. for {
30. time.Sleep(1 * time.Second)
elog.Info("people info", elog.String("name", p.Name),
31. elog.String("type", "structByFileWatch"))
32. }
33. }()
34. return nil
35. }).Run(); err != nil {
36. elog.Panic("startup", elog.FieldErr(err))
37. }
38. }
39.
40. type People struct {
41. Name string
42. }
日志
Example
日志配置
框架在处理的日志区分为框架日志和业务日志,了解日志,请阅读日志和错误处理。
日志配置的数据结构如下
1. // Config ...
2. type Config struct {
3. Debug bool // 是否双写至文件控制日志输出到终端
4. Level string // 日志初始等级,默认info级别
5. Dir string // [fileWriter]日志输出目录,默认logs
Name string // [fileWriter]日志文件名称,默认框架日志
6. mocro.sys,业务日志default.log
MaxSize int // [fileWriter]日志输出文件最大长度,超过
7. 改值则截断,默认500M
MaxAge int // [fileWriter]日志存储最大时间,默认最大
8. 保存天数为7天
MaxBackup int // [fileWriter]日志存储最大数量,默认最大
9. 保存文件个数为10个
10. RotateInterval time.Duration // [fileWriter]日志轮转时间,默认1天
11. EnableAddCaller bool // 是否添加调用者信息,默认不加调用者信息
12. EnableAsync bool // 是否异步,默认异步
13. FlushBufferSize int // 缓冲大小,默认256 * 1024B
14. FlushBufferInterval time.Duration // 缓冲时间,默认5秒
15. Writer string // 使用哪种Writer,可选[file|ali]
16. AliAccessKeyID string // [aliWriter]阿里云sls AKID,必填
17. AliAccessKeySecret string // [aliWriter]阿里云sls AKSecret,必填
18. AliEndpoint string // [aliWriter]阿里云sls endpoint,必填
AliProject string // [aliWriter]阿里云sls Project名称,必
19. 填
终端显示日志
在运行程序前开启环境变量 EGO_DEBUG=true ,可以把所有日志输出到终端。并且开启了该指令
后,日志的时间变成 time.Time 数据结构。
1. package main
2.
3. import (
4. "github.com/gotomicro/ego"
5. "github.com/gotomicro/ego/core/elog"
6. )
7.
8. // export EGO_DEBUG=true && go run main.go
9. func main() {
10. err := ego.New().Invoker(func() error {
elog.Info("logger info", elog.String("gopher", "ego"),
11. elog.String("type", "command"))
12. return nil
13. }).Run()
14. if err != nil {
15. elog.Panic("startup", elog.Any("err", err))
16. }
17. }
文件显示日志
当 EGO_DEBUG 环境变量不存在或者 EGO_DEBUG=false 的时候,日志默认输出到 logs 目录下。
1. package main
2.
3. import (
4. "github.com/gotomicro/ego"
5. "github.com/gotomicro/ego/core/elog"
6. )
7.
8. // export EGO_DEBUG=false && go run main.go
9. func main() {
10. err := ego.New().Invoker(func() error {
elog.Info("logger info", elog.String("gopher", "ego"),
11. elog.String("type", "command"))
12. return nil
13. }).Run()
14. if err != nil {
15. elog.Panic("startup", elog.Any("err", err))
16. }
17. }
动态日志级别
框架里自带的框架日志和业务日志都默认支持动态更改日志级别。当程序启动后,你可以在配置文件里
更改lv的级别从info改为debug,就可以看到动态生效的debug日志,该方法非常利于研发排查线上
问题,倡导大家线下多打debug日志,线上用info级别日志,出现线上问题可以改变日志级别,快速排
查问题。
1. [logger.default]
2. level = "info"
1. package main
2.
3. import (
4. "github.com/gotomicro/ego"
5. "github.com/gotomicro/ego/core/elog"
6. "time"
7. )
8.
9. // export EGO_DEBUG=true && go run main.go --config=config.toml
10. func main() {
11. err := ego.New(ego.WithHang(true)).Invoker(func() error {
12. go func() {
13. for {
elog.Info("logger info", elog.String("gopher", "ego1"),
14. elog.String("type", "file"))
elog.Debug("logger debug", elog.String("gopher", "ego2"),
15. elog.String("type", "file"))
16. time.Sleep(1 * time.Second)
17. }
18. }()
19. return nil
20. }).Run()
21. if err != nil {
22. elog.Panic("startup", elog.Any("err", err))
23. }
24. }
日志字段
EGO的字段是确定类型的,通过正交查询方式,减少索引字段个数,同时方便创建索引。后续字段类型
名称 类型 描述
lv string 日志级别
ts string 时间戳
color string 染色
依赖的实例名称。以mysql为例,”dsn =
addr string “root:root@tcp(127.0.0.1:3306)/ego?charset=utf8”,addr为
“127.0.0.1:3306”.
ip string 主机IP
日志
慢日志
错误日志
HTTP服务
gRPC服务
治理服务
HTTP服务
Example
HTTP配置
用户配置
1. [server.http]
2. host = "127.0.0.1"
3. port = 9001
用户代码
配置创建一个 http 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置key
是 server.http
1. package main
2.
3. import (
4. "github.com/gin-gonic/gin"
5. "github.com/gotomicro/ego"
6. "github.com/gotomicro/ego/core/elog"
7. "github.com/gotomicro/ego/server/egin"
8. )
9.
10. // export EGO_DEBUG=true && go run main.go --config=config.toml
11. func main() {
12. if err := ego.New().Serve(func() *egin.Component {
13. server := egin.Load("server.http").Build()
14. server.GET("/hello", func(ctx *gin.Context) {
15. ctx.JSON(200, "Hello EGO")
16. return
17. })
18. return server
19. }()).Run(); err != nil {
20. elog.Panic("startup", elog.FieldErr(err))
21. }
22. }
gRPC服务
Example
HTTP配置
用户配置
1. [server.grpc]
2. host = "127.0.0.1"
3. port = 9002
用户代码
配置创建一个 grpc 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置key
是 server.grpc
1. package main
2.
3. import (
4. "context"
5. "github.com/gotomicro/ego"
6. "github.com/gotomicro/ego/core/elog"
7. "github.com/gotomicro/ego/server"
8. "github.com/gotomicro/ego/server/egrpc"
9. "google.golang.org/grpc/examples/helloworld/helloworld"
10. )
11.
12. // export EGO_DEBUG=true && go run main.go --config=config.toml
13. func main() {
14. if err := ego.New().Serve(func() server.Server {
15. server := egrpc.Load("server.grpc").Build()
16. helloworld.RegisterGreeterServer(server.Server, &Greeter{})
17. return server
18. }()).Run(); err != nil {
19. elog.Panic("startup", elog.Any("err", err))
20. }
21. }
22.
23. type Greeter struct {
24. server *egrpc.Component
25. }
26.
func (g Greeter) SayHello(context context.Context, request
27. *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
28. return &helloworld.HelloReply{
29. Message: "Hello EGO, I'm " + g.server.Address(),
30. }, nil
31. }
治理服务
Example
背景
Go 不像 Java 和 PHP ,有虚拟机帮助程序员对程序运行的内部情况的观测,但这种观测对于程
序员而言排查故障,解决性能问题是非常重要的。
EGO 着眼于可观测性,在各个组件里引入拦截器,提取有用信息,通过实现一个治理服务,将程序运
行数据吐出来,方便用户做治理平台,排查各类问题。
可观测数据
路由 说明
/ 展示所有可观测的路由
/metrics 监控数据
/debug/pprof/* pprorf信息
/module/info 应用依赖模块信息
/build/info 应用编译信息
/env/info 应用环境信息
/code/info 状态码信息,待完成
/component/info 组件信息,待完成
用户配置
1. [server.governor]
2. host = "0.0.0.0"
3. port = 9003
用户代码
本文档使用 书栈网 · BookStack.CN 构建 - 22 -
治理服务
1. package main
2.
3. import (
4. "github.com/gin-gonic/gin"
5. "github.com/gotomicro/ego"
6. "github.com/gotomicro/ego/core/elog"
7. "github.com/gotomicro/ego/server/egin"
8. "github.com/gotomicro/ego/server/egovernor"
9. )
10.
11. func main() {
12. if err := ego.New().
13. Serve(
14. egovernor.Load("server.governor").Build(),
15. serverHttp(),
16. ).Run(); err != nil {
17. elog.Panic("startup", elog.FieldErr(err))
18. }
19. }
20.
21. func serverHttp() *egin.Component {
22. server := egin.Load("server.http").Build()
23. server.GET("/hello", func(ctx *gin.Context) {
24. ctx.JSON(200, "Hello")
25. return
26. })
27. return server
28. }
查看运行的metric信息
我们启动服务
1. # HELP ego_build_info
2. # TYPE ego_build_info gauge
ego_build_info{app_version="b0807b91aca95b6eb6daafa9195c467fac0c350b-dirty",build_time
11:48:35",ego_version="0.1.0",go_version="go1.15.2",mode="dev",name="hello",region
3. 12-04 11:49:02",zone="ali-3"} 1.607053742679e+12
短时任务Job
定时任务Cron
短时任务Job
背景
通常我们有许多程序是短时任务,执行一下就结束。这种场景通常有以下两种方式:
执行某个一次性任务,例如:执行程序的安装,或者mock数据
将生命周期托管给例如k8s job或者xxljob,由他们控制job的执行时间
Example
用户代码
如果命令行参数里有 --job ,那么框架会优先执行这个 job ,停止所有的 server 和
cron 。 job 可以执行一个,也可以执行多个。执行一个方式 --job=jobname ,执行多
个方式,用逗号分割 jobname ,例如: --job=jobname1,jobname2,jobname3
1. package main
2.
3. import (
4. "errors"
5. "fmt"
6. "github.com/gotomicro/ego"
7. "github.com/gotomicro/ego/core/elog"
8. "github.com/gotomicro/ego/task/ejob"
9. "go.uber.org/zap"
10. )
11.
12. // export EGO_DEBUG=true && go run main.go --job=jobrunner
13. func main() {
14. if err := ego.New().Job(NewJobRunner()).Run(); err != nil {
15. elog.Error("start up", zap.Error(err))
16. }
17. }
18.
19. func NewJobRunner() *ejob.Component {
定时任务Cron
1 Example
2 定时任务配置
3 常规定时任务
3.1 用户配置
1. [cron.test]
enableDistributedTask = false # 是否分布式任务,默认否,如果存在分布式任务,
2. 会只执行该定时人物
3. enableImmediatelyRun = false # 是否立刻执行,默认否
4. enableWithSeconds = false # 是否使用秒作解析器,默认否
delayExecType = "skip" # skip,queue,concurrent,如果上一个任务执行较慢,到达了新任
5. 务执行时间,那么新任务选择跳过,排队,并发执行的策略,新任务默认选择skip策略
3.2 用户代码
配置创建一个 的配置项,其中内容按照上文HTTP的配置进行填写。以上这个示例里这个配置key
是 cron.test
1. package main
2.
3. import (
4. "errors"
5. "fmt"
6. "github.com/gotomicro/ego"
7. "github.com/gotomicro/ego/core/elog"
8. "github.com/gotomicro/ego/task/ecron"
9. "time"
10. )
11.
12. // export EGO_DEBUG=true && go run main.go --config=config.toml
13. func main() {
14. err := ego.New().Cron(cron1()).Run()
15. if err != nil {
16. elog.Panic("startup engine", elog.Any("err", err))
17. }
18. }
19.
20. func cron1() ecron.Ecron {
21. cron := ecron.Load("cron.test").Build()
22. cron.Schedule(ecron.Every(time.Second*10), ecron.FuncJob(execJob))
23. cron.Schedule(ecron.Every(time.Second*10), ecron.FuncJob(execJob2))
24. return cron
25. }
26.
27. // 异常任务
28. func execJob() error {
29. elog.Info("info job")
30. elog.Warn("warn job")
31. fmt.Println("run job")
32. return errors.New("exec job1 error")
33. }
34.
35. // 正常任务
36. func execJob2() error {
37. elog.Info("info job2")
4 分布式定时任务
4.1 用户配置
1. [cron.test]
enableDistributedTask = true # 是否分布式任务,默认否,如果存在分布式任务,会
2. 只执行该定时人物
3. enableImmediatelyRun = false # 是否立刻执行,默认否
4. enableWithSeconds = false # 是否使用秒作解析器,默认否
delayExecType = "skip" # skip,queue,concurrent,如果上一个任务执行较慢,到达了新任
5. 务执行时间,那么新任务选择跳过,排队,并发执行的策略,新任务默认选择skip策略
4.2 用户代码
配置创建一个 的配置项,其中内容按照上文HTTP的配置进行填写。以上这个示例里这个配置key
是 cron.test
1. package main
2.
3. import (
4. "errors"
5. "fmt"
6. "github.com/gotomicro/ego"
7. "github.com/gotomicro/ego/core/elog"
8. "github.com/gotomicro/ego/task/ecron"
9. "time"
10. )
11.
12. // export EGO_DEBUG=true && go run main.go --config=config.toml
13. func main() {
14. err := ego.New().Cron(cron1()).Run()
15. if err != nil {
16. elog.Panic("startup engine", elog.Any("err", err))
17. }
18. }
19.
20. func cron1() ecron.Ecron {
21. lock := ecronlock.Load("").Build(ecronlock.WithClientRedis(invoker.Redis))
22. cron := ecron.Load("cron.test").Build(ecron.WithLocker(lock))
23. cron.Schedule(ecron.Every(time.Second*10), ecron.FuncJob(execJob))
24. return cron
25. }
26.
27. func execJob() error {
28. elog.Info("info job")
29. elog.Warn("warn job")
30. fmt.Println("run job")
31. return errors.New("exec job1 error")
32. }
gRPC
HTTP
GORM
Redis
gRPC
1 Example
2 gRPC配置
3 优雅的Debug
通过开启 debug 配置和命令行的 export EGO_DEBUG=true ,我们就可以在测试环境里看到请求里
的配置名、地址、耗时、请求数据、响应数据
4 直连gRPC
4.1 用户配置
1. [grpc.test]
debug = true # 开启后并加上export EGO_DEBUG=true,可以看到每次grpc请求,配置名、地址、
2. 耗时、请求数据、响应数据
3. addr = "127.0.0.1:9002"
4.2 用户代码
配置创建一个 grpc 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置key
是 grpc.test
1. package main
2.
3. import (
4. "context"
5. "github.com/gotomicro/ego"
6. "github.com/gotomicro/ego/client/egrpc"
7. "github.com/gotomicro/ego/core/elog"
8. "google.golang.org/grpc/examples/helloworld/helloworld"
9. )
10.
11. func main() {
12. if err := ego.New().Invoker(
13. invokerGrpc,
14. callGrpc,
15. ).Run(); err != nil {
16. elog.Error("startup", elog.FieldErr(err))
17. }
18. }
19.
20. var grpcComp helloworld.GreeterClient
21.
22. func invokerGrpc() error {
23. grpcConn := egrpc.Load("grpc.test").Build()
5 使用ETCD的gRPC
5.1 用户配置
1. [grpc.test]
debug = true # 开启后并加上export EGO_DEBUG=true,可以看到每次grpc请求,配置名、地址、
2. 耗时、请求数据、响应数据
3. addr = "etcd:///name-srv"
5.2 用户代码
配置创建一个 grpc 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置key
是 grpc.test
1. package main
2.
3. import (
4. "context"
5. "github.com/gotomicro/ego"
6. "github.com/gotomicro/ego/core/elog"
7. "github.com/gotomicro/ego-component/eetcd"
8. "github.com/gotomicro/ego-component/eetcd/registry"
9. "github.com/gotomicro/ego/client/egrpc"
10. "github.com/gotomicro/ego/client/egrpc/resolver"
11. "google.golang.org/grpc/examples/helloworld/helloworld"
12. )
13.
14. func main() {
15. if err := ego.New().Invoker(
16. invokerGrpc,
17. callGrpc,
18. ).Run(); err != nil {
19. elog.Error("startup", elog.FieldErr(err))
20. }
21. }
22.
23. var grpcComp helloworld.GreeterClient
24.
25. func invokerGrpc() error {
26. // 必须注册在grpc前面
resolver.Register("etcd",
27. registry.Load("registry").Build(registry.WithClientEtcd(eetcd.Load("etcd").Build())))
28.
29. grpcConn := egrpc.Load("grpc.test").Build()
30. grpcComp = helloworld.NewGreeterClient(grpcConn.ClientConn)
31. return nil
32. }
33.
34. func callGrpc() error {
35. _, err := grpcComp.SayHello(context.Background(), &helloworld.HelloRequest{
36. Name: "i am client",
37. })
38. if err != nil {
39. return err
40. }
41.
42. _, err = grpcComp.SayHello(context.Background(), &helloworld.HelloRequest{
6 使用K8S的gRPC
6.1 用户配置
1. [grpc.test]
debug = true # 开启后并加上export EGO_DEBUG=true,可以看到每次grpc请求,配置名、地址、
2. 耗时、请求数据、响应数据
3. addr = "k8s:///name-srv"
6.2 用户代码
配置创建一个 grpc 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置key
是 grpc.test
1. package main
2.
3. import (
4. "context"
5. "github.com/gotomicro/ego"
6. "github.com/gotomicro/ego/core/elog"
7. "github.com/gotomicro/ego-component/ek8s"
8. "github.com/gotomicro/ego-component/ek8s/registry"
9. "github.com/gotomicro/ego/client/egrpc"
10. "github.com/gotomicro/ego/client/egrpc/resolver"
11. "google.golang.org/grpc/examples/helloworld/helloworld"
12. )
13.
14. func main() {
HTTP
Example
HTTP配置
用户配置
1. [http.test]
addr = "http://127.0.0.1:9007" # 开启后并加上export EGO_DEBUG=true,可以看到每次
2. http请求,配置名、地址、耗时、请求数据、响应数据
3. debug = true
优雅的Debug
通过开启 debug 配置和命令行的 export EGO_DEBUG=true ,我们就可以在测试环境里看到请求里
的配置名、地址、耗时、请求数据、响应数据
用户代码
配置创建一个 http 的配置项,其中内容按照上文HTTP的配置进行填写。以上这个示例里这个配
置key是 http.test
1. package main
2.
3. import (
4. "fmt"
5. "github.com/gotomicro/ego"
6. "github.com/gotomicro/ego/client/ehttp"
7. "github.com/gotomicro/ego/core/elog"
8. )
9.
10. func main() {
11. if err := ego.New().Invoker(
12. invokerHTTP,
13. callHTTP,
14. ).Run(); err != nil {
15. elog.Error("startup", elog.FieldErr(err))
16. }
17. }
18.
19. var httpComp *ehttp.Component
20.
21. func invokerHTTP() error {
22. httpComp = ehttp.Load("http.test").Build()
23. return nil
24. }
25.
26. func callHTTP() error {
27. info, err := httpComp.R().Get("/hello")
28. if err != nil {
29. return err
30. }
31. fmt.Println(info)
32. return nil
33. }
GORM
Example
GORM配置
用户配置
1. [mysql.test]
debug = true # ego重写gorm debug,打开后可以看到,配置名、地址、耗时、请求数据、响应
2. 数据
dsn = "root:root@tcp(127.0.0.1:3306)/ego?
3. charset=utf8&parseTime=True&loc=Local&readTimeout=1s&timeout=1s&writeTimeout=3s"
优雅的Debug
通过开启 debug 配置和命令行的 export EGO_DEBUG=true ,我们就可以在测试环境里看到请求里
的配置名、地址、耗时、请求数据、响应数据
用户代码
TIP
1. go get github.com/gotomicro/ego-component/egorm
1. package main
2.
3. import (
4. "github.com/gotomicro/ego"
5. "github.com/gotomicro/ego-component/egorm"
6. "github.com/gotomicro/ego/core/elog"
7. )
8.
9. /**
10. 1.新建一个数据库叫test
11. 2.执行以下example,export EGO_DEBUG=true && go run main.go --config=config.toml
12. */
13. type User struct {
14. Id int `gorm:"not null" json:"id"`
15. Nickname string `gorm:"not null" json:"name"`
16. }
17.
18. func (User) TableName() string {
19. return "user2"
20. }
21.
22. func main() {
23. err := ego.New().Invoker(
24. openDB,
25. testDB,
26. ).Run()
27. if err != nil {
28. elog.Error("startup", elog.Any("err", err))
29. }
30. }
31.
32. var gormDB *egorm.Component
33.
34. func openDB() error {
35. gormDB = egorm.Load("mysql.test").Build()
36. models := []interface{}{
37. &User{},
38. }
39. gormDB.SingularTable(true)
40. gormDB.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(models...)
41. gormDB.Create(&User{
42. Nickname: "ego",
43. })
44. return nil
45. }
46.
47. func testDB() error {
48. var user User
49. err := gormDB.Where("id = ?", 100).Find(&user).Error
50. elog.Info("user info", elog.String("name", user.Nickname))
51. return err
52. }
Redis
Example
Redis配置
23. }
用户配置
1. # 常用的stub单实例配置示例
2. [redis.stub]
debug = true # ego增加redis debug,打开后可以看到,配置名、地址、耗时、请求数据、响应
3. 数据
4. mode = "stub" # 默认为stub单实例模式,可选"stub|cluster|sentinel"
5. addr = "127.0.0.1:6379"
6.
7. # cluster集群模式配置示例
8. [redis.cluster]
9. debug = true
10. mode = "cluster" # 设置为"cluster"模式,该模式下必须配置"addrs"
addrs = ["127.0.0.1:6379", "127.0.0.1:6380", "127.0.0.1:6381"] # cluster模
11. 式下必须配置"addrs"
12.
13. # sentinel哨兵模式配置示例
14. [redis.sentinel]
15. debug = true
16. mode = "sentinel" # 设置为"sentinel"模式,该模式下必须配置"addrs"和"masterName"
addrs = ["127.0.0.1:26379", "127.0.0.1:26380", "127.0.0.1:26381"] # sentinel
17. 模式下必须配置"addrs"
18. masterName = "my-sentinel-master-name" # sentinel 模式下必须配置"masterName"
优雅的Debug
通过开启 debug 配置和命令行的 export EGO_DEBUG=true ,我们就可以在测试环境里看到请
求里的配置名、地址、耗时、请求数据、响应数据
用户代码
TIP
1. go get github.com/gotomicro/ego-component/eredis
1. package main
2.
3. import (
4. "fmt"
5.
6. "github.com/gotomicro/ego"
7. "github.com/gotomicro/ego-component/eredis"
8. "github.com/gotomicro/ego/core/elog"
9. )
10.
11. // export EGO_DEBUG=true && go run main.go --config=config.toml
12. func main() {
13. err := ego.New().Invoker(
14. invokerRedis,
15. testRedis,
16. ).Run()
17. if err != nil {
18. elog.Panic("startup", elog.FieldErr(err))
19. }
20. }
21.
22. var eredisStubClient *eredis.Component
23. var eredisClusterClient *eredis.Component
24. var eredisSentinelClient *eredis.Component
25.
26. func invokerRedis() error {
27. // 读取"redis.stub"配置,并初始化redis stub component实例
28. eredisStubClient = eredis.Load("redis.stub").Build()
71. }
监控信息
监控信息
服务端标准
以下是框架记录的指标,prometheus在采集时加入指标,例如job或者app等数据,所以可以由他来
过滤一些应用信息数据
server端计数器
server_handle_total
名称 描述 用法
type 类型
method 方法
peer 对端节点
code 状态码
type 有三种类型
http
unary
stream
method 有两种类型
HTTP的method是 c.Request.Method+”.”+c.Request.URL.Path
gRPC的method是 grpc.UnaryServerInfo.FullMethod
peer 取数据
HTTP的peer是从header头里取出app,这个为对端节点的应用名
gRPC的peer是从header头里取出app,这个为对端节点的应用名
code 取数据
HTTP的code是HTTP状态码
gRPC的code是gRPC返回的Message,只记录系统错误码,系统错误码成功为OK,非系统错误码
全部记录为 biz err ,防止prometheus错误爆炸
server直方图
server_handle_seconds
名称 描述 用法
type 类型
method 方法
peer 对端节点
type 有三种类型
http
unary
stream
method 有两种类型
HTTP的method是 c.Request.Method+”.”+c.Request.URL.Path
gRPC的method是 grpc.UnaryServerInfo.FullMethod
peer 取数据
HTTP的peer是从header头里取出app,这个为对端节点的应用名
gRPC的peer是从header头里取出app,这个为对端节点的应用名
监控查询语句
阿里云日志查询语句
监控查询语句
1 全部请求量
全部请求量 sum(irate(ego_server_handle_total{}[1m]))
某个应用请求量 sum(irate(ego_server_handle_total{app=”你的应用名称”}[1m]))
排行榜前10应用 topk(10,sum (rate (ego_server_handle_total{}[1m])) by
(app))
聚合应用 sum(irate(ego_server_handle_total{}[1m])) by (app)
2 CPU负载
排行榜前10应用 topk(10,sum(rate(process_cpu_seconds_total{}[1m])) by
(app))
阿里云日志查询语句
1 gRPC服务端日志
1.1 获取gRPC请求应用分布
1.2 获取gRPC请求的应用状态码分布
1.3 获取gRPC请求的应用崩溃分布
1.4 获取gRPC请求的应用错误分布
1.5 获取gRPC请求的应用慢日志分布
1.6 获取某个应用gRPC的对端地址的错误分布
其他崩溃日志、慢日志与该语句类似
1.7 获取某个应用gRPC的对端应用名的错误分布
其他崩溃日志、慢日志与该语句类似
2 HTTP服务端日志
2.1 获取HTTP请求应用分布
2.2 获取HTTP请求的应用状态码分布
2.3 获取HTTP请求的应用崩溃分布
2.4 获取HTTP请求的应用错误分布
2.5 获取HTTP请求的应用慢日志分布
2.6 获取某个应用HTTP的对端地址的错误分布
其他崩溃日志、慢日志与该语句类似
2.7 获取某个应用HTTP的对端应用名的错误分布
其他崩溃日志、慢日志与该语句类似
3 gRPC客户端日志
3.1 获取gRPC请求应用分布
3.2 获取gRPC请求的应用状态码分布
3.3 获取gRPC请求的应用崩溃分布
3.4 获取gRPC请求的应用错误分布
3.5 获取gRPC请求的应用慢日志分布
3.6 获取某个应用gRPC的配置项错误分布
其他崩溃日志、慢日志与该语句类似
4 HTTP客户端日志
4.1 获取HTTP请求应用分布
4.2 获取HTTP请求的应用状态码分布
4.3 获取HTTP请求的应用崩溃分布
4.4 获取HTTP请求的应用错误分布
4.5 获取HTTP请求的应用慢日志分布
其他崩溃日志、慢日志与该语句类似
5 Gorm客户端日志
5.1 获取Gorm请求应用分布
5.2 获取Gorm请求的应用状态码分布
5.3 获取Gorm请求的应用崩溃分布
5.4 获取Gorm请求的应用错误分布
5.5 获取Gomr请求的应用慢日志分布
5.6 获取某个应用Gorm的配置项错误分布
其他崩溃日志、慢日志与该语句类似
6 Redis客户端日志
6.1 获取Redis请求应用分布
6.2 获取Gorm请求的应用状态码分布
6.3 获取Redis请求的应用崩溃分布
6.4 获取Redis请求的应用错误分布
6.5 获取Redis请求的应用慢日志分布
6.6 获取某个应用Redis的配置项错误分布
其他崩溃日志、慢日志与该语句类似
错误处理
要点
Error需要常量,不能是变量
Error需要wrap,方便排查问题
Error需要用IS判断,不能用=判断
Error需要收敛
Error是常量
第一个问题是io.EOF公共变量-导入io包的任何代码都可能更改的值io.EOF。事实证明,在大多数情
况下,这并不是什么大问题,但可能数据被人篡改,引发不必要的问题。
正确的用法,应该如下所示
Error需要wrap
GO1.13支持了error wrap。我们可以在错误以下方法,将原始错误进行包装。fmt.Errorf里是%w
这里需要提醒一点,go官方的error wrap没有堆栈信息,还是比较坑爹
Error需要IS
以往我们对错误判断都是=,但是如果使用了wrap,在用=是无法相等的:
Error需要收敛
Error说明
目前官方error没有支持堆栈,可能使用pkg/errors排查问题更方便。 但ego为了支持官方后续升
级,还是决定使用官方error用法。
引用文献
服务关闭
服务关闭需要注意的点
服务关闭错误抛出
多服务并发关闭,不能因为并发报错误,停止其他服务的关闭
服务优雅关闭,超时后需要转成强制关闭
服务超时仍然无法关闭,需要转成进一步的强制关闭
HTTP的shutdown会导致,listen and server的立刻返回,需要支持register on
shutdown
errgroup的context超时传递,需要每个服务里实现ctx.done和ctx.err,否则会导致服务
无法正常cancel
服务关闭常规写法