TarsGo新版本发布,支持protobuf,zipkin和自定义插件

sandyskies · · 485 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

#TarsGo新版本发布,支持protobuf,zipkin和自定义插件 Tars是腾讯从2008年到今天一直在使用的后台逻辑层的统一应用框架,目前支持C++,Java,PHP,Nodejs,Golang语言。该框架为用户提供了涉及到开发、运维、以及测试的一整套解决方案,帮助一个产品或者服务快速开发、部署、测试、上线。 它集可扩展协议编解码、高性能RPC通信框架、名字路由与发现、发布监控、日志统计、配置管理等于一体,通过它可以快速用微服务的方式构建自己的稳定可靠的分布式应用,并实现完整有效的服务治理。目前该框架在腾讯内部,各大核心业务都在使用,颇受欢迎,基于该框架部署运行的服务节点规模达到上万个。 Tars 于2017年4月开源,并于2018年6月加入Linux 基金会。 TarsGo 是Tars 的Go语言实现版本, 于2018年9月开源, 项目地址 https://github.com/TarsCloud/TarsGo ,欢迎star 。 ##TarsGo 新版本发布 在上次开源之后,有些用户反馈了一些需求,基于用户反馈的需求,我们进行了实现,并发布了1.1.0版本。 本次发布新增了:支持pb、支持zipkin分布式追踪、支持filter(自定义插件编写)、支持context 等,除此之外还做了一系列优化和bugfix。 ### 新功能:PB支持 Protocol Buffers (简称 PB )是 Google 的一种数据交换的格式,它独立于语言,独立于平台,最早公布于 2008年7月。随着微服务架构的发展及自身的优异表现,ProtoBuf 可用于诸如网络传输、配置文件、数据存储等诸多领域,目前在互联网上有着大量应用。 如果对于现有已使用grpc,使用proto文件,想转换成tars协议的用户而言,需要将上面的proto文件翻译成Tars文件。这种翻译会比较繁琐,而且容易出错。 为此我们决定编写插件支持proto文件直接生成tars的rpc逻辑。protoc-gen-go的代码逻辑里面是预留了插件编写的规范的,参照grpc,主要有 grpc/grpc.go 和一个导入插件的link_grpc.go 。 这里我们编写 tarsrpc/tarsrpc.go 和 link_tarsrpc.go 使用方面: - 将这两个文件放到protoc-gen-go 下面,go install重新生成protoc-gen-go 二进制 - 定义proto 文件 - 使用重新编译安装的protoc-gen-go生成序列化和rpc相关接口代码 ```shell protoc --go_out=plugins=tarsrpc:. helloworld.proto ``` - 编写tars 客户端和服务端代码,参数使用pb生成的结构体,其余代码逻辑和正常的tars服务一致。 - 详细原理和使用文档,阅读 [腾讯云社区文章](https://cloud.tencent.com/developer/article/1353058) ### 新功能: filter机制, 支持zipkin分布式追踪 为了支持用户编写插件,我们支持了filter机制,分为服务端的过滤器和客户端过滤器,用户可以基于这个机制,实现自己的TarsGo插件。 ```golang //服务端过滤器, 传入dispatch,和f, 用于调用用户代码, req, 和resp为传入的用户请求和服务端相应包体 type ServerFilter func(ctx context.Context, d Dispatch, f interface{}, req *requestf.RequestPacket, resp *requestf.ResponsePacket, withContext bool) (err error) //客户端过滤器, 传入msg(包含obj信息,adapter信息,req和resp包体), 还有用户设定的调用超时 type ClientFilter func(ctx context.Context, msg *Message, invoke Invoke, timeout time.Duration) (err error) //注册服务端过滤器 //func RegisterServerFilter(f ServerFilter) //注册客户端过滤器 //func RegisterClientFilter(f ClientFilter) ``` 有了过滤器,我们就能对服务端和客户端的请求做一些过滤,比如使用 hook用于分布式追踪的opentracing 的span。 我们来看下客户端filter的例子: ``` //生成客户端tars filter,通过注册这个filter来实现span的注入 func ZipkinClientFilter() tars.ClientFilter { return func(ctx context.Context, msg *tars.Message, invoke tars.Invoke, timeout time.Duration) (err error) { var pCtx opentracing.SpanContext req := msg.Req //先从客户端调用的context 里面看下有没有传递来调用链的信息, //如果有,则以这个做为父span,如果没有,则起一个新的span,span名字是RPC请求的函数名 if parent := opentracing.SpanFromContext(ctx); parent != nil { pCtx = parent.Context() } cSpan := opentracing.GlobalTracer().StartSpan( req.SFuncName, opentracing.ChildOf(pCtx), ext.SpanKindRPCClient, ) defer cSpan.Finish() cfg := tars.GetServerConfig() //设置span的信息,比如我们调用的客户端的ip地址,请求的接口,方法,协议,客户端版本等信息 cSpan.SetTag("client.ipv4", cfg.LocalIP) cSpan.SetTag("tars.interface", req.SServantName) cSpan.SetTag("tars.method", req.SFuncName) cSpan.SetTag("tars.protocol", "tars") cSpan.SetTag("tars.client.version", tars.TarsVersion) //将span注入到 请求包体的 Status里面,status 是一个map[strint]string 的结构体 if req.Status != nil { err = opentracing.GlobalTracer().Inject(cSpan.Context(), opentracing.TextMap, opentracing.TextMapCarrier(req.Status)) if err != nil { logger.Error("inject span to status error:", err) } } else { s := make(map[string]string) err = opentracing.GlobalTracer().Inject(cSpan.Context(), opentracing.TextMap, opentracing.TextMapCarrier(s)) if err != nil { logger.Error("inject span to status error:", err) } else { req.Status = s } } //没什么其他需要修改的,就进行客户端调用 err = invoke(ctx, msg, timeout) if err != nil { //调用错误,则记录span的错误信息 ext.Error.Set(cSpan, true) cSpan.LogFields(oplog.String("event", "error"), oplog.String("message", err.Error())) } return err } ``` 服务端也会注册一个filter,主要功能就是从request包体的status 提取调用链的上下文,以这个作为父span,进行调用信息的记录。 整体的一个效果: ![zipkin](./zipkin.png) 详细代码参见 TarsGo/tars/plugin/zipkintracing 完整的zipkin tracing的客户端和服务端例子,详见 TarsGo/examples下面的ZipkinTraceClient和ZipkinTraceServer ### 新功能: 支持context TarsGo 之前在生成的客户端代码,或者用户传入的实现代码里面,都没有使用context。 这使得我们想传递一些框架的信息,比如客户端ip,端口等,或者用户传递一些调用链的信息给框架,都很难于实现。 通过接口的一次重构,支持了context,这些上下文的信息,将都通过context来实现。 这次重构为了兼容老的用户行为,采用了完全兼容的设计。 服务端使用context ```golang type ContextTestImp struct { } //只需在接口上添加 ctx context.Context参数 func (imp *ContextTestImp) Add(ctx context.Context, a int32, b int32, c *int32) (int32, error) { //我们可以通过context 获取框架传递的信息,比如下面的获取ip, 甚至返回一些信息给框架,详见tars/util/current下面的接口 ip, ok := current.GetClientIPFromContext(ctx) if !ok { logger.Error("Error getting ip from context") } return 0, nil } //以前使用AddServant ,现在只需改成AddServantWithContext app.AddServantWithContext(imp, cfg.App+"."+cfg.Server+".ContextTestObj") ``` 客户端使用context ``` ctx := context.Background() c := make(map[string]string) c["a"] = "b" //以前使用app.Add 进行客户端调用,这里只要变成app.AddWithContext ,就可以传递context给框架,如果要设置给tars请求的context //可以多传入参数,比如c,参数c是可选的,格式是 ...[string]string ret, err := app.AddWithContext(ctx, i, i*2, &out, c) ``` 服务端和客户端的完整例子,详见 TarGo/examples ## 其他优化和修复 - 将request package 的Sbuffer字段由vector<unsigned byte> 改成vector<byte>,解决和其他语言通信问题 - 修复stat监控上报问题 - 日志级别从远端更新 - 修复路由刷新协程极端情况下死锁问题 - 优化协程池方案,并添加协程池方案 - 修复go协程启动顺序导致panic问题 - golint大部分代码

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

485 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传