本文相关代码:gitee
这一章我们继续使用go-plugins
库中插件,以Wrapper方式集成链路追踪工具jaeger
。
Jaeger是一个GO语言开发的微服务链路追踪工具,用于分布式系统监控和故障排除,他的主要特性包括:
以上内容翻译自jaeger官方文档,更多内容请查阅官网:jaeger
演示原因,这里以官方文档默认docker命令启动服务:
docker run -d --name jaeger \
-e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
-p 5775:5775/udp \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 14268:14268 \
-p 14250:14250 \
-p 9411:9411 \
jaegertracing/all-in-one:1.20
go-plugins
库并没有开发专门的jaeger插件,而是引入了OpenTracing
。
OpenTracing我理解是一套标准的追踪接口,通过提供平台无关、厂商无关的API,使得开发人员能够方便的添加(或更换)追踪系统的实现。而 jaeger兼容OpenTracing API,所以我们使用OpenTracing的程序库就可以方便的接入jaeger。
OpenTracing中文文档: https://wu-sheng.gitbooks.io/opentracing-io/content/
OpenTracing插件:https://github.com/micro/go-plugins/tree/master/wrapper/trace/opentracing/v2
由于我们的服务都将注册到同一个jaeger中(否则也谈不上链路追踪)配置大同小异,创建common
文件夹为所有服务提供统一配置函数,这里我们提取了jaeger配置函数和之前的mongo配置函数:
jaeger.go:
package tracer
import (
"github.com/opentracing/opentracing-go"
"github.com/uber/jaeger-client-go"
jaegercfg "github.com/uber/jaeger-client-go/config"
"io"
"time"
)
// 配置jaeger
func NewJaegerTracer(serviceName string, addr string) (opentracing.Tracer, io.Closer, error) {
cfg := jaegercfg.Configuration{
ServiceName: serviceName,
Sampler: &jaegercfg.SamplerConfig{
Type: jaeger.SamplerTypeConst,
Param: 1,
},
Reporter: &jaegercfg.ReporterConfig{
LogSpans: true,
BufferFlushInterval: 1 * time.Second,
},
}
sender, err := jaeger.NewUDPTransport(addr, 0)
if err != nil {
return nil, nil, err
}
reporter := jaeger.NewRemoteReporter(sender)
tracer, closer, err := cfg.NewTracer(
jaegercfg.Reporter(reporter),
)
return tracer, closer, err
}
查看opentracing
插件代码,插件中提供了4种wrapper,分别用于不同服务类型:
下面我们重点演示server、client和Subscriber的引入方法
以task-srv
服务为例,修改main.go
:
package main
import (
...
// 引入插件
"github.com/micro/go-plugins/wrapper/trace/opentracing/v2"
// 引入公共的自定义配置函数
"go-todolist/common/tracer"
...
)
// 所有port都是服务默认端口,ip请根据实际情况配置
const (
// 项目服务名和注册到Jaeger的服务名一致,保障更容易定位到具体服务
ServerName = "go.micro.service.task"
...
// 服务地址
JaegerAddr = "172.18.0.58:6831"
)
// task-srv服务
func main() {
...
// 配置jaeger连接
jaegerTracer, closer, err := tracer.NewJaegerTracer(ServerName, JaegerAddr)
if err != nil {
log.Fatal(err)
}
defer closer.Close()
// New Service
service := micro.NewService(
micro.Name(ServerName),
...
// 配置链路追踪为jaeger
micro.WrapHandler(opentracing.NewHandlerWrapper(jaegerTracer)),
)
...
}
以task-api
服务为例,之前我们已经在main.go
中注册了一个WrapClient——hystrix,现在再增加opentracing,注意插件是注册在调用服务上而不是web服务上:
package main
import (
...
// 引入插件
"github.com/micro/go-plugins/wrapper/trace/opentracing/v2"
// 引入公共的自定义配置函数
"go-todolist/common/tracer"
...
)
const (
// 项目服务名和注册到Jaeger的服务名一致,保障更容易定位到具体服务
ServerName = "go.micro.api.task"
...
// 服务地址
JaegerAddr = "172.18.0.58:6831"
)
// task-srv服务的restful api映射
func main() {
// 配置jaeger连接
jaegerTracer, closer, err := tracer.NewJaegerTracer(ServerName, JaegerAddr)
if err != nil {
log.Fatal(err)
}
defer closer.Close()
...
app := micro.NewService(
micro.Name("go.micro.client.task"),
...
micro.WrapClient(
// 引入hystrix包装器
hystrix.NewClientWrapper(),
// 配置链路追踪为jaeger
opentracing.NewClientWrapper(jaegerTracer),
),
)
}
网上教程大多只提到server和client,很少提到消息服务,虽然他们注册方式大同小异,但我觉得还是有必要演示一下。
以achievement-srv
服务为例,修改main.go
:
package main
import (
...
// 引入插件
"github.com/micro/go-plugins/wrapper/trace/opentracing/v2"
// 引入公共的自定义配置函数
"go-todolist/common/tracer"
...
)
// 这里是我内网的mongo地址,请根据你得实际情况配置,推荐使用dockers部署
const (
// 项目服务名和注册到Jaeger的服务名一致,保障更容易定位到具体服务
ServerName = "go.micro.service.achievement"
...
// 服务地址
JaegerAddr = "172.18.0.58:6831"
)
// achievement-srv服务
func main() {
...
// 配置jaeger连接
jaegerTracer, closer, err := tracer.NewJaegerTracer(ServerName, JaegerAddr)
if err != nil {
log.Fatal(err)
}
defer closer.Close()
// New Service
service := micro.NewService(
micro.Name(ServerName),
...
// 配置链路追踪为jaeger
micro.WrapSubscriber(opentracing.NewSubscriberWrapper(jaegerTracer)),
)
...
}
之前task-api
只实现了一个Search接口,这边为了测试消息队列的追踪,我们再实现一下任务完成的Finished接口。
提取之前main.go中的Router方法到新建的handler/router.go
:
package handler
import (
"github.com/gin-gonic/gin"
pb "go-todolist/task-srv/proto/task"
)
var service pb.TaskService
func Router(g *gin.Engine, taskService pb.TaskService) {
service = taskService
v1 := g.Group("/task")
{
v1.GET("/search", Search)
v1.POST("/finished", Finished)
}
}
func Search(c *gin.Context) {
...
}
func Finished(c *gin.Context) {
req := new(pb.Task)
if err := c.BindJSON(req); err != nil {
c.JSON(200, gin.H{
"code": "500",
"msg": "bad param",
})
return
}
if resp, err := service.Finished(c, req); err != nil {
c.JSON(200, gin.H{
"code": "500",
"msg": err.Error(),
})
} else {
c.JSON(200, gin.H{
"code": "200",
"data": resp,
})
}
}
使用postman访问finished接口:
再次打开jaeger管理界面,点击左下角Find Traces
按钮,就会看到之前的调用记录:
可以看到所有涉及的微服务都已被记录,点击这个条目还能看到详细信息,这里就不赘述了。
本章我们展示了在不同服务中通过OpenTracing
集成jaeger
服务。
如果你还看过其他教程,应该还会看到如何将jaeger
集成到micro api
网关中,这涉及到micro
包的plugin
插件接口开发,不能简单集成go-plugins
插件,而且那些教程实测并不能很好的实现链路整合,后期如果我找到好的实现方案了会单独写文分享。
原创不易,买杯咖啡,谢谢:p