You are on page 1of 63


致谢
快速开始
核心模块
编译
配置
日志
服务模块
HTTP服务
gRPC服务
治理服务
任务模块
短时任务Job
定时任务Cron
客户端模块
gRPC
HTTP
GORM
Redis
治理模块
监控信息
最佳实践
监控查询语句
阿里云日志查询语句
错误处理
服务关闭

本文档使用 书栈网 · BookStack.CN 构建 - 2 -


致谢

致谢

当前文档 《ego v0.3.10 使用教程》 由 进击的皇虫 使用 书栈网(BookStack.CN) 进行构


建,生成于 2021-03-15。

书栈网仅提供文档编写、整理、归类等功能,以及对文档内容的生成和导出工具。

文档内容由网友们编写和整理,书栈网难以确认文档内容知识点是否错漏。如果您在阅读文档获取
知识的时候,发现文档内容有不恰当的地方,请向我们反馈,让我们共同携手,将知识准确、高效且有
效地传递给每一个人。

同时,如果您在日常工作、生活和学习中遇到有价值有营养的知识文档,欢迎分享到书栈网,为知
识的传承献上您的一份力量!

如果当前文档生成时间太久,请到书栈网获取最新的文档,以跟上知识更新换代的步伐。

内容来源:gocn https://ego.gocn.vip/

文档地址:http://www.bookstack.cn/books/ego-0.3.10-zh

书栈官网:https://www.bookstack.cn

书栈开源:https://github.com/TruthHun

分享,让知识传承更久远! 感谢知识的创造者,感谢知识的分享者,也感谢每一位阅读到此处的
读者,因为我们都将成为知识的传承者。

本文档使用 书栈网 · BookStack.CN 构建 - 3 -


快速开始

快速开始

Example

项目地址 (opens new window)

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. }

使用命令行运行

1. export EGO_DEBUG=true # 默认日志输出到logs目录,开启后日志输出到终端


2. go run main.go --config=config.toml

如下所示

本文档使用 书栈网 · BookStack.CN 构建 - 4 -


快速开始

这个时候我们可以发送一个指令,得到如下结果

1. ➜ helloworld git:(master) ✗ curl http://127.0.0.1:9001/hello


2. "Hello Ego"%

本文档使用 书栈网 · BookStack.CN 构建 - 5 -


核心模块

编译
配置
日志

本文档使用 书栈网 · BookStack.CN 构建 - 6 -


编译

编译

Example

项目地址 (opens new window)

使用EGO框架的应用会在编译期注入许多必要信息,方便后续排查问题。该方案被大量Go应用所使用,
例如istio、prometheus等。我们使用的编译脚本核心内容如下所示。

编译脚本

go build -o bin/hello -pkgdir=/Users/askuy/go/pkg/linux_amd64 -ldflags -extldflags


github.com/gotomicro/ego/core/eapp.appName=hello -X
github.com/gotomicro/ego/core/eapp.buildVersion=b0807b91aca95b6eb6daafa9195c467fac0c350
github.com/gotomicro/ego/core/eapp.buildAppVersion=b0807b91aca95b6eb6daafa9195c467fac0c
-X github.com/gotomicro/ego/core/eapp.buildStatus=Modified -X
github.com/gotomicro/ego/core/eapp.buildTag= -X
github.com/gotomicro/ego/core/eapp.buildUser=askuy -X
github.com/gotomicro/ego/core/eapp.buildHost=askuydeMacBook-Pro.local -X
1. github.com/gotomicro/ego/core/eapp.buildTime=2020-12-04--11:34:55

查看编译版本信息

查看帮助文档
输入 --help 可以看到应用支持的指令

本文档使用 书栈网 · BookStack.CN 构建 - 7 -


编译

查看运行时信息
我们启动服务

请求治理端口的/metrics接口,可以看到 ego_build_info 的信息,这里会将编译信息放入到


prometheus 中,并且还会把运行时的环境信息和启动时间也加入进来。

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

本文档使用 书栈网 · BookStack.CN 构建 - 8 -


配置

配置

Example
动态配置

获取单个配置信息 (opens new window)

获取结构体信息 (opens new window)


静态配置

获取单个配置信息 (opens new window)

获取结构体信息 (opens new window)

系统内置
文本配置
框架默认开启动态配置
框架默认内志支持文本更改日志级别,动态生效

代码示例

动态配置获取单个配置信息

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 {

本文档使用 书栈网 · BookStack.CN 构建 - 9 -


配置

16. time.Sleep(3 * time.Second)


17. peopleName := econf.GetString("people.name")
elog.Info("people info", elog.String("name", peopleName),
18. elog.String("type", "onelineByFileWatch"))
19. }
20. }()
21. return nil
22. }).Run(); err != nil {
23. elog.Panic("startup", elog.FieldErr(err))
24. }
25. }

动态配置获取结构体配置信息

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. // 循环打印配置

本文档使用 书栈网 · BookStack.CN 构建 - 10 -


配置

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. }

本文档使用 书栈网 · BookStack.CN 构建 - 11 -


日志

日志

Example

终端显示日志 (opens new window)

文本显示日志 (opens new window)

日志动态修改级别 (opens new window)

日志配置
框架在处理的日志区分为框架日志和业务日志,了解日志,请阅读日志和错误处理。

日志配置的数据结构如下

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. 填

本文档使用 书栈网 · BookStack.CN 构建 - 12 -


日志

AliLogstore string // [aliWriter]阿里云sls logstore名称,必


20. 填
AliApiBulkSize int // [aliWriter]阿里云sls API单次请求发送最
21. 大日志条数,最少256条,默认256条
AliApiTimeout time.Duration // [aliWriter]阿里云sls API接口超时,默认
22. 3秒
AliApiRetryCount int // [aliWriter]阿里云sls API接口重试次数,
23. 默认3次
AliApiRetryWaitTime time.Duration // [aliWriter]阿里云sls API接口重试默认等
24. 待间隔,默认1秒
AliApiRetryMaxWaitTime time.Duration // [aliWriter]阿里云sls API接口重试最大等
25. 待间隔,默认3秒
26. }

终端显示日志
在运行程序前开启环境变量 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 目录下。

本文档使用 书栈网 · BookStack.CN 构建 - 13 -


日志

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 {

本文档使用 书栈网 · BookStack.CN 构建 - 14 -


日志

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的字段是确定类型的,通过正交查询方式,减少索引字段个数,同时方便创建索引。后续字段类型

尽量像opentrace (opens new window)靠拢

名称 类型 描述

lv string 日志级别

ts string 时间戳

msg string 日志信息

app string 应用名称

iid string 应用实例id

tid string 请求trace id

color string 染色

comp string 类库或组件。如 “grpc”, “http”, “redis”.

compName string 组件配置key作为唯一标识

依赖的实例名称。以mysql为例,”dsn =
addr string “root:root@tcp(127.0.0.1:3306)/ego?charset=utf8”,addr为
“127.0.0.1:3306”.

cost int 耗时时间

code int 用户侧响应的状态码

meth string 对于redis是command、对于http是url、对于mysql是sql

host string 主机名

ip string 主机IP

peerApp string 对端应用名称

本文档使用 书栈网 · BookStack.CN 构建 - 15 -


日志

peerHost string 对端主机名

errKind string 错误类型,用于收敛

err string 错误信息

日志
慢日志
错误日志

本文档使用 书栈网 · BookStack.CN 构建 - 16 -


服务模块

HTTP服务
gRPC服务
治理服务

本文档使用 书栈网 · BookStack.CN 构建 - 17 -


HTTP服务

HTTP服务

Example

项目地址 (opens new window)

HTTP配置

1. type Config struct {


2. Host string // IP地址,默认127.0.0.1
3. Port int // PORT端口,默认9001
4. Mode string // gin的模式,默认是release模式
5. EnableMetricInterceptor bool // 是否开启监控,默认开启
6. EnableTraceInterceptor bool // 是否开启链路追踪,默认开启
7. EnableLocalMainIP bool // 自动获取ip地址
8. SlowLogThreshold time.Duration // 服务慢日志,默认500ms
9. }

用户配置

1. [server.http]
2. host = "127.0.0.1"
3. port = 9001

用户代码
配置创建一个 http 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置key
是 server.http

代码中创建一个 HTTP 服务, egin.Load(“”).Build() ,代码中的 key 和配置中的


key 要保持一致。创建完 HTTP 服务后, 将他添加到 ego new 出来应用的 Serve
方法中,之后使用的方法和 gin 就完全一致。

1. package main
2.
3. import (

本文档使用 书栈网 · BookStack.CN 构建 - 18 -


HTTP服务

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. }

本文档使用 书栈网 · BookStack.CN 构建 - 19 -


gRPC服务

gRPC服务

Example

项目地址 (opens new window)

HTTP配置

1. type Config struct {


2. Host string // IP地址,默认0.0.0.0
3. Port int // Port端口,默认9002
4. Deployment string // 部署区域
5. Network string // 网络类型,默认tcp4
6. EnableMetricInterceptor bool // 是否开启监控,默认开启
7. EnableTraceInterceptor bool // 是否开启链路追踪,默认开启
8. SlowLogThreshold time.Duration // 服务慢日志,默认500ms
9. EnableAccessInterceptorReq bool // 是否开启记录请求参数,默认不开启
10. EnableAccessInterceptorRes bool // 是否开启记录响应参数,默认不开启
11. EnableLocalMainIP bool // 自动获取ip地址
12. }

用户配置

1. [server.grpc]
2. host = "127.0.0.1"
3. port = 9002

用户代码
配置创建一个 grpc 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置key
是 server.grpc

代码中创建一个 gRPC 服务, egrpc.Load(“”).Build(),代码中的 key 和配置中的


key 要保持一致。创建完 gRPC 服务后, 将他添加到 ego new 出来应用的 Serve
方法中,之后使用的方法和 gRPC 就完全一致。

本文档使用 书栈网 · BookStack.CN 构建 - 20 -


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. }

本文档使用 书栈网 · BookStack.CN 构建 - 21 -


治理服务

治理服务

Example

项目地址 (opens new window)

背景
Go 不像 Java 和 PHP ,有虚拟机帮助程序员对程序运行的内部情况的观测,但这种观测对于程
序员而言排查故障,解决性能问题是非常重要的。

EGO 着眼于可观测性,在各个组件里引入拦截器,提取有用信息,通过实现一个治理服务,将程序运
行数据吐出来,方便用户做治理平台,排查各类问题。

可观测数据
路由 说明

/ 展示所有可观测的路由

/metrics 监控数据

/debug/pprof/* pprorf信息

/config/json config json数据

/config/raw config 原始数据

/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 -
治理服务

配置创建一个 governor 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置


key是 server.governor

代码中创建一个 governor 服务, egin.Load(“”).Build() ,代码中的 key 和配置中


的 key 要保持一致。创建完 http 服务后, 将他添加到 ego new 出来应用的
Serve 方法中,之后使用的方法和 gRPC 就完全一致。

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信息
我们启动服务

本文档使用 书栈网 · BookStack.CN 构建 - 23 -


治理服务

请求治理端口的/metrics接口,可以看到 ego_build_info 的信息,这里会将编译信息放入到


prometheus 中,并且还会把运行时的环境信息和启动时间也加入进来。

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

本文档使用 书栈网 · BookStack.CN 构建 - 24 -


任务模块

短时任务Job
定时任务Cron

本文档使用 书栈网 · BookStack.CN 构建 - 25 -


短时任务Job

短时任务Job

背景
通常我们有许多程序是短时任务,执行一下就结束。这种场景通常有以下两种方式:

执行某个一次性任务,例如:执行程序的安装,或者mock数据
将生命周期托管给例如k8s job或者xxljob,由他们控制job的执行时间

Example

项目地址 (opens new window)

用户代码
如果命令行参数里有 --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 {

本文档使用 书栈网 · BookStack.CN 构建 - 26 -


短时任务Job

20. return ejob.DefaultContainer().Build(


21. ejob.WithName("jobrunner"),
22. ejob.WithStartFunc(runner),
23. )
24. }
25.
26. func runner() error {
27. fmt.Println("i am job runner")
28. return errors.New("i am error")
29. }

本文档使用 书栈网 · BookStack.CN 构建 - 27 -


定时任务Cron

定时任务Cron

1 Example

项目地址 (opens new window) ego版本: ego@v0.3.11

2 定时任务配置

1. type Config struct {


2. WaitLockTime time.Duration // 抢锁等待时间,默认60s
3. LockTTL time.Duration // 租期,默认60s
4. LockDir string // 定时任务锁目录
5. RefreshTTL time.Duration // 刷新ttl,默认60s
6. WaitUnlockTime time.Duration // 抢锁等待时间,默认1s
DelayExecType string // skip,queue,concurrent,如果上一个任务
7. 执行较慢,到达了新的任务执行时间,那么新的任务选择跳过,排队,并发执行的策略
EnableDistributedTask bool // 是否分布式任务,默认否,如果存在分布式任
8. 务,会只执行该定时人物
9. EnableImmediatelyRun bool // 是否立刻执行,默认否
10. EnableWithSeconds bool // 是否使用秒作解析器,默认否
11. }

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

本文档使用 书栈网 · BookStack.CN 构建 - 28 -


定时任务Cron

是 cron.test

代码中创建一个 cron 服务, ecron.Load(“”).Build() ,代码中的 key 和配置中的


key 。创建完 cron 后, 将他添加到 ego new 出来应用的 Schedule 方法中。

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")

本文档使用 书栈网 · BookStack.CN 构建 - 29 -


定时任务Cron

38. elog.Warn("warn job2")


39. fmt.Println("run job2")
40. return nil
41. }

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

代码中创建一个 cron 服务, ecron.Load(“”).Build() ,代码中的 key 和配置中的


key 。创建完 cron 后, 将他添加到 ego new 出来应用的 Schedule 方法中。

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))

本文档使用 书栈网 · BookStack.CN 构建 - 30 -


定时任务Cron

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. }

本文档使用 书栈网 · BookStack.CN 构建 - 31 -


客户端模块

gRPC
HTTP
GORM
Redis

本文档使用 书栈网 · BookStack.CN 构建 - 32 -


gRPC

gRPC

1 Example

项目地址 (opens new window) ego版本: ego@v0.3.11

2 gRPC配置

1. type Config struct {


Addr string // 连接地址,直连为127.0.0.1:9001,服
2. 务发现为etcd:///appname
3. BalancerName string // 负载均衡方式,默认round robin
4. OnFail string // 失败后的处理方式,panic | error
5. DialTimeout time.Duration // 连接超时,默认3s
6. ReadTimeout time.Duration // 读超时,默认1s
7. SlowLogThreshold time.Duration // 慢日志记录的阈值,默认600ms
Debug bool // 是否开启调试,默认不开启,开启后并加
8. 上export EGO_DEBUG=true,可以看到每次请求,配置名、地址、耗时、请求数据、响应数据
9. EnableBlock bool // 是否开启阻塞,默认开启
10. EnableWithInsecure bool // 是否开启非安全传输,默认开启
11. EnableMetricInterceptor bool // 是否开启监控,默认开启
12. EnableTraceInterceptor bool // 是否开启链路追踪,默认开启
13. EnableAppNameInterceptor bool // 是否开启传递应用名,默认开启
14. EnableTimeoutInterceptor bool // 是否开启超时传递,默认开启
15. EnableAccessInterceptor bool // 是否开启记录请求数据,默认不开启
16. EnableAccessInterceptorReq bool // 是否开启记录请求参数,默认不开启
17. EnableAccessInterceptorRes bool // 是否开启记录响应参数,默认不开启
18. }

3 优雅的Debug
通过开启 debug 配置和命令行的 export EGO_DEBUG=true ,我们就可以在测试环境里看到请求里
的配置名、地址、耗时、请求数据、响应数据

本文档使用 书栈网 · BookStack.CN 构建 - 33 -


gRPC

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

代码中创建一个 gRPC 客户端, egrpc.Load("key").Build() ,代码中的 key 和配置中


的 key 要保持一致。创建完 gRPC 客户端后, 将他添加到你所需要的Client里即可。

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()

本文档使用 书栈网 · BookStack.CN 构建 - 34 -


gRPC

24. grpcComp = helloworld.NewGreeterClient(grpcConn.ClientConn)


25. return nil
26. }
27.
28. func callGrpc() error {
29. _, err := grpcComp.SayHello(context.Background(), &helloworld.HelloRequest{
30. Name: "i am client",
31. })
32. if err != nil {
33. return err
34. }
35.
36. _, err = grpcComp.SayHello(context.Background(), &helloworld.HelloRequest{
37. Name: "error",
38. })
39. if err != nil {
40. return err
41. }
42. return nil
43. }

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

代码中创建一个 gRPC 客户端, egrpc.Load("key").Build() ,代码中的 key 和配置中


的 key 要保持一致。创建完 gRPC 客户端后, 将他添加到你所需要的Client里即可。

1. package main

本文档使用 书栈网 · BookStack.CN 构建 - 35 -


gRPC

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{

本文档使用 书栈网 · BookStack.CN 构建 - 36 -


gRPC

43. Name: "error",


44. })
45. if err != nil {
46. return err
47. }
48. return nil
49. }

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

代码中创建一个 gRPC 客户端, egrpc.Load("key").Build() ,代码中的 key 和配置中


的 key 要保持一致。创建完 gRPC 客户端后, 将他添加到你所需要的Client里即可。

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() {

本文档使用 书栈网 · BookStack.CN 构建 - 37 -


gRPC

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("k8s",
27. registry.Load("registry").Build(registry.WithClientKubernetes(ek8s.Load("k8s").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{
43. Name: "error",
44. })
45. if err != nil {
46. return err
47. }
48. return nil
49. }

本文档使用 书栈网 · BookStack.CN 构建 - 38 -


HTTP

HTTP

Example

项目地址 (opens new window)

HTTP配置

1. type Config struct {


2. Addr string // 连接地址
Debug bool // 是否开启调试,默认不开启,开启后并
3. 加上export EGO_DEBUG=true,可以看到每次请求,配置名、地址、耗时、请求数据、响应数据
4. RawDebug bool // 是否开启原生调试,默认不开启
5. ReadTimeout time.Duration // 读超时,默认2s
6. SlowLogThreshold time.Duration // 慢日志记录的阈值,默认500ms
7. EnableAccessInterceptor bool // 是否开启记录请求数据,默认不开启
8. EnableAccessInterceptorReply bool // 是否开启记录响应参数,默认不开启
9. }

用户配置

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 ,我们就可以在测试环境里看到请求里
的配置名、地址、耗时、请求数据、响应数据

本文档使用 书栈网 · BookStack.CN 构建 - 39 -


HTTP

当然你也可以开启 http 原生的调试,将 rawDebug 设置为 true

用户代码
配置创建一个 http 的配置项,其中内容按照上文HTTP的配置进行填写。以上这个示例里这个配
置key是 http.test

代码中创建一个 HTTP 客户端, ehttp.Load("key").Build() ,代码中的 key 和配置中


的 key 要保持一致。创建完 HTTP 客户端后, 将他添加到你所需要的Client里即可。

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. }

本文档使用 书栈网 · BookStack.CN 构建 - 40 -


HTTP

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. }

本文档使用 书栈网 · BookStack.CN 构建 - 41 -


GORM

GORM

Example

项目地址 (opens new window)

GORM配置

1. type Config struct {


2. Dialect string // 选择数据库种类,默认mysql
DSN string // DSN地址: mysql://root:secret@tcp(127.0
3. charset=utf8mb4&collation=utf8mb4_general_ci&parseTime=True&loc=Local&timeout=1s&readTi
Debug bool // 是否开启调试,默认不开启,开启后并
4. 加上export EGO_DEBUG=true,可以看到每次请求,配置名、地址、耗时、请求数据、响应数据
5. RawDebug bool // 是否开启原生调试开关,默认不开启
6. MaxIdleConns int // 最大空闲连接数,默认10
7. MaxOpenConns int // 最大活动连接数,默认100
8. ConnMaxLifetime time.Duration // 连接的最大存活时间,默认300s
OnFail string // 创建连接的错误级别,=panic时,如
9. 果创建失败,立即panic,默认连接不上panic
10. SlowLogThreshold time.Duration // 慢日志阈值,默认500ms
11. EnableMetricInterceptor bool // 是否开启监控,默认开启
12. EnableTraceInterceptor bool // 是否开启链路追踪,默认开启
EnableDetailSQL bool // 记录错误sql时,是否打印包含参数的完
13. 整sql语句,select * from aid = ?;
14. EnableAccessInterceptor bool // 是否开启,记录请求数据
15. EnableAccessInterceptorReply bool // 是否开启记录响应参数
16. EnableAccessInterceptorReq bool // 是否开启记录请求参数
17. }

用户配置

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"

本文档使用 书栈网 · BookStack.CN 构建 - 42 -


GORM

优雅的Debug
通过开启 debug 配置和命令行的 export EGO_DEBUG=true ,我们就可以在测试环境里看到请求里
的配置名、地址、耗时、请求数据、响应数据

当然你也可以开启 gorm 原生的调试,将 rawDebug 设置为 true

用户代码
TIP

客户端组件均使用go mod子包管理,使用该组件,一定要使用下面的go get命令

1. go get github.com/gotomicro/ego-component/egorm

配置创建一个 gorm 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置key


是 gorm.test

代码中创建一个 gorm 实例 egorm.Load("key").Build() ,代码中的 key 和配置中的


key 要保持一致。创建完 gorm 实例后,就可以直接使用他对 db 进行 crud 。

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"

本文档使用 书栈网 · BookStack.CN 构建 - 43 -


GORM

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. }

本文档使用 书栈网 · BookStack.CN 构建 - 44 -


Redis

Redis

Example

项目地址 (opens new window)

Redis配置

1. type config struct {


2. Addrs []string // Addrs 实例配置地址
3. Addr string // Addr stubConfig 实例配置地址
Mode string // Mode Redis模式
4. cluster|stub|sentinel
MasterName string // MasterName 哨兵主节点名称,
5. sentinel模式下需要配置此项
6. Password string // Password 密码
DB int // DB,默认为0, 一般应用不推荐使用DB分
7. 片
PoolSize int // PoolSize 集群内每个节点的最大连接池
8. 限制 默认每个CPU10个连接
MaxRetries int // MaxRetries 网络相关的错误最大重试次
9. 数 默认8次
10. MinIdleConns int // MinIdleConns 最小空闲连接数
11. DialTimeout time.Duration // DialTimeout 拨超时时间
12. ReadTimeout time.Duration // ReadTimeout 读超时 默认3s
13. WriteTimeout time.Duration // WriteTimeout 读超时 默认3s
IdleTimeout time.Duration // IdleTimeout 连接最大空闲时间,默认
14. 60s, 超过该时间,连接会被主动关闭
Debug bool // Debug开关, 是否开启调试,默认不开
启,开启后并加上export EGO_DEBUG=true,可以看到每次请求,配置名、地址、耗时、请求数据、响
15. 应数据
ReadOnly bool // ReadOnly 集群模式 在从属节点上启用
16. 读模式
SlowLogThreshold time.Duration // 慢日志门限值,超过该门限值的请求,将
17. 被记录到慢日志中
18. OnFail string // OnFail panic|error
19. EnableMetricInterceptor bool // 是否开启监控,默认开启
20. EnableAccessInterceptor bool // 是否开启,记录请求数据
21. EnableAccessInterceptorReq bool // 是否开启记录请求参数
22. EnableAccessInterceptorRes bool // 是否开启记录响应参数

本文档使用 书栈网 · BookStack.CN 构建 - 45 -


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 ,我们就可以在测试环境里看到请
求里的配置名、地址、耗时、请求数据、响应数据

本文档使用 书栈网 · BookStack.CN 构建 - 46 -


Redis

用户代码
TIP

客户端组件均使用go mod子包管理,使用该组件,一定要使用下面的go get命令

1. go get github.com/gotomicro/ego-component/eredis

配置创建一个 redis 的配置项,其中内容按照上文配置进行填写。以上这个示例里这个配置key


是 redis.test

代码中创建一个 redis 实例 eredis.Load("key").Build() ,代码中的 key 和配置中的


key 要保持一致。创建完 redis 实例后,就可以直接使用他对 redis 进行 crud 。

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()

本文档使用 书栈网 · BookStack.CN 构建 - 47 -


Redis

29. // 读取"redis.cluster"配置,并初始化redis cluster component实例


30. eredisClusterClient = eredis.Load("redis.cluster").Build()
31. // 读取"redis.sentinel"配置,并初始化redis sentinel component实例
32. eredisSentinelClient = eredis.Load("redis.sentinel").Build()
33. return nil
34. }
35.
36. func testRedis() error {
37. // 使用redis stub component进行set、get操作
38. err := eredisStubClient.Set("hello", "world", 0)
39. if err != nil {
40. log.Println(err)
41. }
42. str, err := eredisStubClient.GetString("hello")
43. if err != nil {
44. log.Println(err)
45. }
46. fmt.Println(str)
47.
48. // 使用redis cluster component进行set、get操作
49. err := eredisClusterClient.Set("hello", "world", 0)
50. if err != nil {
51. log.Println(err)
52. }
53. str, err := eredisClusterClient.GetString("hello")
54. if err != nil {
55. log.Println(err)
56. }
57. fmt.Println(str)
58. return nil
59.
60. // 使用redis sentinel component进行set、get操作
61. err := eredisSentinelClient.Set("hello", "world", 0)
62. if err != nil {
63. log.Println(err)
64. }
65. str, err := eredisSentinelClient.GetString("hello")
66. if err != nil {
67. log.Println(err)
68. }
69. fmt.Println(str)
70. return nil

本文档使用 书栈网 · BookStack.CN 构建 - 48 -


Redis

71. }

本文档使用 书栈网 · BookStack.CN 构建 - 49 -


治理模块

监控信息

本文档使用 书栈网 · BookStack.CN 构建 - 50 -


监控信息

监控信息

服务端标准
以下是框架记录的指标,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

本文档使用 书栈网 · BookStack.CN 构建 - 51 -


监控信息

名称 描述 用法

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,这个为对端节点的应用名

本文档使用 书栈网 · BookStack.CN 构建 - 52 -


最佳实践

监控查询语句
阿里云日志查询语句

本文档使用 书栈网 · BookStack.CN 构建 - 53 -


监控查询语句

监控查询语句

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))

本文档使用 书栈网 · BookStack.CN 构建 - 54 -


阿里云日志查询语句

阿里云日志查询语句

1 gRPC服务端日志

1.1 获取gRPC请求应用分布

* and comp:"server.egrpc" and msg:"access" | SELECT COUNT(*) as count, app


1. GROUP by app order by count desc limit 10

1.2 获取gRPC请求的应用状态码分布

* and comp:"server.egrpc" and msg:"access" | SELECT COUNT(*) as count, code


1. GROUP by code order by count desc limit 10

1.3 获取gRPC请求的应用崩溃分布

* and comp:"server.egrpc" and msg:"access" and lv:"panic" | SELECT COUNT(*) as


1. count, app GROUP by app order by count desc limit 10

1.4 获取gRPC请求的应用错误分布

* and comp:"server.egrpc" and msg:"access" and lv:"error" | SELECT COUNT(*) as


1. count, app GROUP by app order by count desc limit 10

1.5 获取gRPC请求的应用慢日志分布

* and comp:"server.egrpc" and msg:"access" and event:"slow" | SELECT COUNT(*)


1. as count, app GROUP by app order by count desc limit 10

1.6 获取某个应用gRPC的对端地址的错误分布

其他崩溃日志、慢日志与该语句类似

* and comp:"server.egrpc" and msg:"access" and lv:"error" and app:"appname" |


1. SELECT COUNT(*) as count, peerIp GROUP by peerIp order by count desc limit 10

1.7 获取某个应用gRPC的对端应用名的错误分布

其他崩溃日志、慢日志与该语句类似

本文档使用 书栈网 · BookStack.CN 构建 - 55 -


阿里云日志查询语句

* and comp:"server.egrpc" and msg:"access" and lv:"error" and app:"appname" |


SELECT COUNT(*) as count, peerName GROUP by peerName order by count desc limit
1. 10

2 HTTP服务端日志

2.1 获取HTTP请求应用分布

* and comp:"server.ehttp" and msg:"access" | SELECT COUNT(*) as count, app


1. GROUP by app order by count desc limit 10

2.2 获取HTTP请求的应用状态码分布

* and comp:"server.ehttp" and msg:"access" | SELECT COUNT(*) as count, code


1. GROUP by code order by count desc limit 10

2.3 获取HTTP请求的应用崩溃分布

* and comp:"server.ehttp" and msg:"access" and lv:"panic" | SELECT COUNT(*) as


1. count, app GROUP by app order by count desc limit 10

2.4 获取HTTP请求的应用错误分布

* and comp:"server.ehttp" and msg:"access" and lv:"error" | SELECT COUNT(*) as


1. count, app GROUP by app order by count desc limit 10

2.5 获取HTTP请求的应用慢日志分布

* and comp:"server.ehttp" and msg:"access" and event:"slow" | SELECT COUNT(*)


1. as count, app GROUP by app order by count desc limit 10

2.6 获取某个应用HTTP的对端地址的错误分布

其他崩溃日志、慢日志与该语句类似

* and comp:"server.ehttp" and msg:"access" and lv:"error" and app:"appname" |


1. SELECT COUNT(*) as count, peerIp GROUP by peerIp order by count desc limit 10

2.7 获取某个应用HTTP的对端应用名的错误分布

本文档使用 书栈网 · BookStack.CN 构建 - 56 -


阿里云日志查询语句

其他崩溃日志、慢日志与该语句类似

* and comp:"server.ehttp" and msg:"access" and lv:"error" and app:"appname" |


SELECT COUNT(*) as count, peerName GROUP by peerName order by count desc limit
1. 10

3 gRPC客户端日志

3.1 获取gRPC请求应用分布

* and comp:"client.egrpc" and msg:"access" | SELECT COUNT(*) as count, app


1. GROUP by app order by count desc limit 10

3.2 获取gRPC请求的应用状态码分布

* and comp:"client.egrpc" and msg:"access" | SELECT COUNT(*) as count, code


1. GROUP by code order by count desc limit 10

3.3 获取gRPC请求的应用崩溃分布

* and comp:"client.egrpc" and msg:"access" and lv:"panic" | SELECT COUNT(*) as


1. count, app GROUP by app order by count desc limit 10

3.4 获取gRPC请求的应用错误分布

* and comp:"client.egrpc" and msg:"access" and lv:"error" | SELECT COUNT(*) as


1. count, app GROUP by app order by count desc limit 10

3.5 获取gRPC请求的应用慢日志分布

* and comp:"client.egrpc" and msg:"access" and event:"slow" | SELECT COUNT(*)


1. as count, app GROUP by app order by count desc limit 10

3.6 获取某个应用gRPC的配置项错误分布

其他崩溃日志、慢日志与该语句类似

* and comp:"client.egrpc" and msg:"access" and lv:"error" and app:"appname" |


SELECT COUNT(*) as count, compName GROUP by compName order by count desc limit
1. 10

本文档使用 书栈网 · BookStack.CN 构建 - 57 -


阿里云日志查询语句

4 HTTP客户端日志

4.1 获取HTTP请求应用分布

* and comp:"client.ehttp" and msg:"access" | SELECT COUNT(*) as count, app


1. GROUP by app order by count desc limit 10

4.2 获取HTTP请求的应用状态码分布

* and comp:"client.ehttp" and msg:"access" | SELECT COUNT(*) as count, code


1. GROUP by code order by count desc limit 10

4.3 获取HTTP请求的应用崩溃分布

* and comp:"client.ehttp" and msg:"access" and lv:"panic" | SELECT COUNT(*) as


1. count, app GROUP by app order by count desc limit 10

4.4 获取HTTP请求的应用错误分布

* and comp:"client.ehttp" and msg:"access" and lv:"error" | SELECT COUNT(*) as


1. count, app GROUP by app order by count desc limit 10

4.5 获取HTTP请求的应用慢日志分布

* and comp:"client.ehttp" and msg:"access" and event:"slow" | SELECT COUNT(*)


1. as count, app GROUP by app order by count desc limit 10

4.6 获取某个应用HTTP 的配置项错误分布

其他崩溃日志、慢日志与该语句类似

* and comp:"client.ehttp" and msg:"access" and lv:"error" and app:"appname" |


SELECT COUNT(*) as count, compName GROUP by compName order by count desc limit
1. 10

5 Gorm客户端日志

5.1 获取Gorm请求应用分布

本文档使用 书栈网 · BookStack.CN 构建 - 58 -


阿里云日志查询语句

* and comp:"client.egorm" and msg:"access" | SELECT COUNT(*) as count, app


1. GROUP by app order by count desc limit 10

5.2 获取Gorm请求的应用状态码分布

* and comp:"client.egorm" and msg:"access" | SELECT COUNT(*) as count, code


1. GROUP by code order by count desc limit 10

5.3 获取Gorm请求的应用崩溃分布

* and comp:"client.egorm" and msg:"access" and lv:"panic" | SELECT COUNT(*) as


1. count, app GROUP by app order by count desc limit 10

5.4 获取Gorm请求的应用错误分布

* and comp:"client.egorm" and msg:"access" and lv:"error" | SELECT COUNT(*) as


1. count, app GROUP by app order by count desc limit 10

5.5 获取Gomr请求的应用慢日志分布

* and comp:"client.egorm" and msg:"access" and event:"slow" | SELECT COUNT(*)


1. as count, app GROUP by app order by count desc limit 10

5.6 获取某个应用Gorm的配置项错误分布

其他崩溃日志、慢日志与该语句类似

* and comp:"client.egorm" and msg:"access" and lv:"error" and app:"appname" |


SELECT COUNT(*) as count, compName GROUP by compName order by count desc limit
1. 10

6 Redis客户端日志

6.1 获取Redis请求应用分布

* and comp:"client.eredis" and msg:"access" | SELECT COUNT(*) as count, app


1. GROUP by app order by count desc limit 10

6.2 获取Gorm请求的应用状态码分布

本文档使用 书栈网 · BookStack.CN 构建 - 59 -


阿里云日志查询语句

* and comp:"client.eredis" and msg:"access" | SELECT COUNT(*) as count, code


1. GROUP by code order by count desc limit 10

6.3 获取Redis请求的应用崩溃分布

* and comp:"client.eredis" and msg:"access" and lv:"panic" | SELECT COUNT(*)


1. as count, app GROUP by app order by count desc limit 10

6.4 获取Redis请求的应用错误分布

* and comp:"client.eredis" and msg:"access" and lv:"error" | SELECT COUNT(*)


1. as count, app GROUP by app order by count desc limit 10

6.5 获取Redis请求的应用慢日志分布

* and comp:"client.eredis" and msg:"access" and event:"slow" | SELECT COUNT(*)


1. as count, app GROUP by app order by count desc limit 10

6.6 获取某个应用Redis的配置项错误分布

其他崩溃日志、慢日志与该语句类似

* and comp:"client.eredis" and msg:"access" and lv:"error" and app:"appname" |


SELECT COUNT(*) as count, compName GROUP by compName order by count desc limit
1. 10

本文档使用 书栈网 · BookStack.CN 构建 - 60 -


错误处理

错误处理

要点
Error需要常量,不能是变量
Error需要wrap,方便排查问题
Error需要用IS判断,不能用=判断
Error需要收敛

Error是常量
第一个问题是io.EOF公共变量-导入io包的任何代码都可能更改的值io.EOF。事实证明,在大多数情
况下,这并不是什么大问题,但可能数据被人篡改,引发不必要的问题。

1. fmt.Println(io.EOF == io.EOF) // true


2. x := io.EOF
3. fmt.Println(io.EOF == x) // true
4.
5. io.EOF = fmt.Errorf("whoops")
6. fmt.Println(io.EOF == io.EOF) // true
7. fmt.Println(x == io.EOF) // false

正确的用法,应该如下所示

1. const eof = Error("eof")


2.
3. func (r * Reader) Read([] byte) (int, error){
4. return 0, eof
5. }
6.
7. func main () {
8. var r Reader
9. _,err: = r.Read([] byte {})
10. fmt.Println(err == eof)// true
11. }

Error需要wrap

本文档使用 书栈网 · BookStack.CN 构建 - 61 -


错误处理

GO1.13支持了error wrap。我们可以在错误以下方法,将原始错误进行包装。fmt.Errorf里是%w

1. err = fmt.Errorf("wrap error %w",err)

这里需要提醒一点,go官方的error wrap没有堆栈信息,还是比较坑爹

Error需要IS
以往我们对错误判断都是=,但是如果使用了wrap,在用=是无法相等的:

1. selectErr := fmt.Errorf("select info err: %w",gorm.IsNotRecord)


2. fmt.Println(selectErr,gorm.IsNotRecord) // false
3. fmt.Println(errors.Is(selectErr,gorm.IsNotRecord)) // true

Error需要收敛

Error说明
目前官方error没有支持堆栈,可能使用pkg/errors排查问题更方便。 但ego为了支持官方后续升
级,还是决定使用官方error用法。

引用文献

常量error (opens new window)

1.13 Error Wrap深度分析 (opens new window)

本文档使用 书栈网 · BookStack.CN 构建 - 62 -


服务关闭

服务关闭

服务关闭需要注意的点
服务关闭错误抛出
多服务并发关闭,不能因为并发报错误,停止其他服务的关闭
服务优雅关闭,超时后需要转成强制关闭
服务超时仍然无法关闭,需要转成进一步的强制关闭
HTTP的shutdown会导致,listen and server的立刻返回,需要支持register on
shutdown
errgroup的context超时传递,需要每个服务里实现ctx.done和ctx.err,否则会导致服务
无法正常cancel

服务关闭常规写法

本文档使用 书栈网 · BookStack.CN 构建 - 63 -

You might also like