TarsGo

    • Tarsgo是基于Golang编程语言使用Tars协议的高性能RPC框架。随着docker,k8s,etcd等容器化技术的兴起,Go语言变得流行起来。Go的goroutine并发机制使Go非常适合用于大规模高并发后端服务程序的开发。 Go语言具有接近C/C++的性能和接近python的生产力。在腾讯,一部分现有的C++开发人员正逐渐向Go转型,Tars作为广泛使用的RPC框架,现已支持C++/Java/Nodejs/Php,其与Go语言的结合已成为大势所趋。因此,在广大用户的呼声中我们推出了Tarsgo,并且已经将它应用于腾讯地图、应用宝、互联网+以及其他项目中。
    • 关于tars的整体架构和设计理念,请阅读 Tars介绍

    功能特性

    • Tars2go工具: tars文件自动生成并转换为go语言,包含用go语言实现的RPC服务端/客户端代码
    • go语言版本的tars的序列化和反序列化包
    • 服务端支持心跳上报,统计监控上报,自定义命令处理,基础日志
    • 客户端支持直接连接和路由访问,自动重新连接,定期刷新节点状态以及支持UDP/TCP协议
    • 提供远程日志
    • 提供特性监控上报
    • 提供set分组
    • 提供 protocol buffers 支持, 详见 pb2tarsgo

    安装

    快速开始

    性能数据

    使用

    • 下面是一个完整的示例,用于说明如何使用tarsgo去构建服务端。

    接口定义

    编译接口定义文件

    构建 tars2go

    编译并安装tars2go工具

    编译tars文件并转成go文

    1. tars2go --outdir=./vendor hello.tars

    接口实现

    1. package main
    2. import (
    3. "github.com/TarsCloud/TarsGo/tars"
    4. "TestApp"
    5. )
    6. type HelloImp struct {
    7. }
    8. //implete the Test interface
    9. func (imp *HelloImp) Test() (int32, error) {
    10. return 0, nil
    11. }
    12. //implete the testHello interface
    13. func (imp *HelloImp) TestHello(in string, out *string) (int32, error) {
    14. *out = in
    15. return 0, nil
    16. }
    17. func main() { //Init servant
    18. imp := new(HelloImp) //New Imp
    19. app := new(TestApp.Hello) //New init the A Tars
    20. cfg := tars.GetServerConfig() //Get Config File Object
    21. app.AddServant(imp, cfg.App+"."+cfg.Server+".HelloObj") //Register Servant
    22. tars.Run()
    23. }

    说明:

    • HelloImp是结构体,你在里面实现Hello和Test接口, 注意Test和Hello必须以大写字母开头才能被导出,这是唯一与tars文件定义有所不同的地方。
    • TestApp.Hello是由tar2go工具生成的,它可以在./vendor/TestApp/Hello_IF.go中找到,其中包含一个名为TestApp的软件包,它与tars文件的TestApp模块一样。
    • tars.GetServerConfig()用于获得服务端配置。
    • cfg.App+”.”+cfg.Server+”.HelloObj” 是绑定到Servant的对象名,客户端将使用此名称访问服务端.

    tars.GetServerConfig()返回服务端配置,其定义如下:

    1. type serverConfig struct {
    2. Node string
    3. App string
    4. Server string
    5. LogPath string
    6. LogSize string
    7. LogLevel string
    8. Version string
    9. LocalIP string
    10. BasePath string
    11. DataPath string
    12. config string
    13. notify string
    14. log string
    15. netThread int
    16. Adapters map[string]adapterConfig
    17. Container string
    18. Isdocker bool
    19. Enableset bool
    20. Setdivision string
    21. }
    • Node: 本地tarsnode地址,只有你使用tars平台部署才会使用这个参数.
    • APP: 应用名.
    • Server: 服务名.
    • LogPath: 保存日志的目录.
    • LogSize: 轮换日志的大小.
    • LogLevel: 轮换日志的级别.
    • Version: Tarsg的版本.
    • LocalIP: 本地ip地址.
    • BasePath: 二进制文件的基本路径.
    • DataPath: 一些缓存文件存储路径.
    • config: 获取配置的配置中心,如tars.tarsconfig.ConfigObj
    • notify: 上报通知报告的通知中心,如tars.tarsnotify.NotifyObj
    • log: 远程日志中心,如tars.tarslog.LogObj
    • netThread: 保留用于控制接收和发送包的go线程.
    • Adapters: 每个adapter适配器的指定配置.
    • Contianer: 保留供以后使用,用于存储容器名称.
    • Isdocker: 保留供以后使用,用于指定服务是否在容器内运行.
    • Enableset: 如果使用了set,则为True.
    • Setdivision: 指定哪个set,如gray.sz.*

    如下是一个服务端配置的例子:

    1. <tars>
    2. <application>
    3. enableset=Y
    4. setdivision=gray.sz.*
    5. <server>
    6. node=tars.tarsnode.ServerObj@tcp -h 10.120.129.226 -p 19386 -t 60000
    7. app=TestApp
    8. server=HelloServer
    9. localip=10.120.129.226
    10. local=tcp -h 127.0.0.1 -p 20001 -t 3000
    11. basepath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/bin/
    12. datapath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/data/
    13. logpath=/usr/local/app/tars/app_log/
    14. logsize=10M
    15. config=tars.tarsconfig.ConfigObj
    16. notify=tars.tarsnotify.NotifyObj
    17. log=tars.tarslog.LogObj
    18. #timeout for deactiving , ms.
    19. deactivating-timeout=2000
    20. logLevel=DEBUG
    21. </server>
    22. </application>
    23. </tars>

    适配器

    适配器为每个对象绑定ip和端口.在服务端代码实现的例子中, app.AddServant(imp, cfg.App+”.”+cfg.Server+”.HelloObj”)完成HelloObj的适配器配置和实现的绑定。适配器的完整例子如下:

    1. <tars>
    2. <application>
    3. <server>
    4. #each adapter configuration
    5. <TestApp.HelloServer.HelloObjAdapter>
    6. allow
    7. # ip and port to listen on
    8. endpoint=tcp -h 10.120.129.226 -p 20001 -t 60000
    9. #handlegroup
    10. handlegroup=TestApp.HelloServer.HelloObjAdapter
    11. #max connection
    12. maxconns=200000
    13. #portocol, only tars for now.
    14. protocol=tars
    15. #max capbility in handle queue.
    16. queuecap=10000
    17. #timeout in ms for the request in the queue.
    18. queuetimeout=60000
    19. #servant
    20. servant=TestApp.HelloServer.HelloObj
    21. #threads in handle server side implement code. goroutine for golang.
    22. threads=5
    23. </TestApp.HelloServer.HelloObjAdapter>
    24. </server>
    25. </application>
    26. </tars>

    服务端启动

    如下命令用于启动服务端:

    1. ./HelloServer --config=config.conf

    请参阅下面的config.conf的完整示例,稍后我们将解释客户端配置。

    1. <tars>
    2. <application>
    3. enableset=n
    4. setdivision=NULL
    5. <server>
    6. node=tars.tarsnode.ServerObj@tcp -h 10.120.129.226 -p 19386 -t 60000
    7. app=TestApp
    8. server=HelloServer
    9. localip=10.120.129.226
    10. local=tcp -h 127.0.0.1 -p 20001 -t 3000
    11. basepath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/bin/
    12. datapath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/data/
    13. logpath=/usr/local/app/tars/app_log/
    14. logsize=10M
    15. config=tars.tarsconfig.ConfigObj
    16. notify=tars.tarsnotify.NotifyObj
    17. log=tars.tarslog.LogObj
    18. deactivating-timeout=2000
    19. logLevel=DEBUG
    20. allow
    21. endpoint=tcp -h 10.120.129.226 -p 20001 -t 60000
    22. handlegroup=TestApp.HelloServer.HelloObjAdapter
    23. maxconns=200000
    24. protocol=tars
    25. queuecap=10000
    26. queuetimeout=60000
    27. servant=TestApp.HelloServer.HelloObj
    28. threads=5
    29. </TestApp.HelloServer.HelloObjAdapter>
    30. </server>
    31. <client>
    32. locator=tars.tarsregistry.QueryObj@tcp -h 10.120.129.226 -p 17890
    33. sync-invoke-timeout=3000
    34. async-invoke-timeout=5000
    35. refresh-endpoint-interval=60000
    36. report-interval=60000
    37. sample-rate=100000
    38. max-sample-count=50
    39. asyncthread=3
    40. modulename=TestApp.HelloServer
    41. </client>
    42. </application>
    43. </tars>

    用户可以轻松编写客户端代码,而无需编写任何指定协议的通信代码.

    请参阅下面的一个客户端例子:

    1. package main
    2. import (
    3. "fmt"
    4. "github.com/TarsCloud/TarsGo/tars"
    5. "TestApp"
    6. )
    7. //tars.Communicator should only init once and be global
    8. var comm *tars.Communicator
    9. func main() {
    10. comm = tars.NewCommunicator()
    11. obj := "TestApp.TestServer.HelloObj@tcp -h 127.0.0.1 -p 10015 -t 60000"
    12. app := new(TestApp.Hello)
    13. comm.StringToProxy(obj, app)
    14. var req string="Hello Wold"
    15. var res string
    16. ret, err := app.TestHello(req, &out)
    17. if err != nil {
    18. fmt.Println(err)
    19. return
    20. }
    21. fmt.Println(ret, out)

    说明:

    • TestApp包是由tars2go工具使用tars协议文件生成的.
    • comm: Communicator用于与服务端进行通信,它应该只初始化一次并且是全局的.
    • obj: 对象名称,用于指定服务端的ip和端口。通常在”@”符号之前我们只需要对象名称.
    • app: 与tars文件中的接口关联的应用程序。 在本例中它是TestApp.Hello.
    • StringToProxy: StringToProxy方法用于绑定对象名称和应用程序,如果不这样做,通信器将不知道谁与应用程序通信 .
    • req, res: 在tars文件中定义的输入和输出参数,用于在TestHello方法中.
    • app.TestHello用于调用tars文件中定义的方法,并返回ret和err.

    通信器

    通信器是为客户端发送和接收包的一组资源,其最终管理每个对象的socket通信。在一个程序中你只需要一个通信器。

    1. var comm *tars.Communicato
    2. comm = tars.NewCommunicator()
    3. comm.SetProperty("property", "tars.tarsproperty.PropertyObj")
    4. comm.SetProperty("locator", "tars.tarsregistry.QueryObj@tcp -h ... -p ...")

    描述:

    通信器属性描述:

    通信器配置文件的格式如下:

    超时控制

    如果你想在客户端使用超时控制,请使用以ms为单位的TarsSetTimeout。

    1. app := new(TestApp.Hello)
    2. comm.StringToProxy(obj, app)
    3. app.TarsSetTimeout(3000)

    本节详细介绍了Tars客户端如何远程调用服务端。

    首先,简要描述Tars客户端的寻址模式。 其次,它将介绍客户端的调用方法,包括但不限于单向调用,同步调用,异步调用,hash调用等。

    寻址模式简介

    Tars服务的寻址模式通常可以分为两种方式:服务名称在master上注册了,服务名称未在master上注册。 master是专用于注册服务节点信息的名字服务(路由服务)。

    把服务名添加到名字服务中是通过操作管理平台实现。

    对于未在master中注册的服务,可以将其分类为直接寻址,即在调用服务之前需要指定服务提供者的IP地址。 客户端需要在调用服务时指定HelloObj对象的特定地址,即Test.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985

    Test.HelloServer.HelloObj: 对象名

    -h:指定主机地址,这里是127.0.0.1

    -p:端口,这里是9985

    如果HelloServer在两台服务器上运行,则应用程序初始化如下:

    1. obj:= "Test.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985:tcp -h 192.168.1.1 -p 9983"
    2. app := new(TestApp.Hello)
    3. comm.StringToProxy(obj, app)

    HelloObj的地址设置为两个服务器的地址。 此时,请求将被分发到两个服务器(可以指定分发方法,这里不再介绍)。 如果一台服务器关闭,请求将自动分配给另一台服务器,服务器将定期重新启动。

    对于在master中注册的服务,将根据服务名称对服务进行寻址。 当客户端请求服务时,它不需要指定HelloServer的特定地址,但是在生成通信器或初始化通信器时需要指定registry的地址。

    以下通过设置通信器的参数显示主控的地址:

    1. var *tars.Communicator
    2. comm = tars.NewCommunicator()
    3. comm.SetProperty("locator", "tars.tarsregistry.QueryObj@tcp -h ... -p ...")

    由于客户端需要依赖主控的地址,因此主控还必须具有容错能力。 主控的容错方法与上面相同,即指定了两个主控的地址。

    单向调用

    TODO. tarsgo暂未支持.

    同步调用

    1. package main
    2. import (
    3. "fmt"
    4. "github.com/TarsCloud/TarsGo/tars"
    5. "TestApp"
    6. )
    7. var *tars.Communicator
    8. func main() {
    9. comm = tars.NewCommunicator()
    10. obj := "TestApp.TestServer.HelloObj@tcp -h 127.0.0.1 -p 10015 -t 60000"
    11. app := new(TestApp.Hello)
    12. comm.StringToProxy(obj, app)
    13. var req string="Hello Wold"
    14. var res string
    15. ret, err := app.TestHello(req, &out)
    16. if err != nil {
    17. fmt.Println(err)
    18. return
    19. }
    20. fmt.Println(ret, out)

    异步调用

    tarsgo可以使用goroutine轻松使用异步调用。 与cpp不同,我们不需要实现回调函数。

    1. package main
    2. import (
    3. "fmt"
    4. "github.com/TarsCloud/TarsGo/tars"
    5. "time"
    6. "TestApp"
    7. )
    8. var *tars.Communicator
    9. func main() {
    10. comm = tars.NewCommunicator()
    11. obj := "TestApp.TestServer.HelloObj@tcp -h 127.0.0.1 -p 10015 -t 60000"
    12. app := new(TestApp.Hello)
    13. comm.StringToProxy(obj, app)
    14. go func(){
    15. var res string
    16. ret, err := app.TestHello(req, &out)
    17. if err != nil {
    18. fmt.Println(err)
    19. return
    20. }
    21. fmt.Println(ret, out)
    22. }()
    23. time.Sleep(1)

    通过set调用

    客户端可以通过set来调用服务端,只需要配置上文提到的配置文件,其中enableset置为y,setdivision比如设置为gray.sz. *。 有关更多详细信息,请参阅https://github.com/TarsCloud/Tars/blob/master/docs-en/tars_idc_set.md。 如果您想手动通过set调用,tarsgo将很快支持此功能。

    Hash调用

    由于可以部署多个服务端,因此客户端的请求会随机分发到服务端上,但在某些情况下,希望始终将某些请求发送到特定的服务端。 在这种情况下,Tars提供了一种简单的实现方法,称为hash调用。 Tarsgo很快将支持此功能。

    tars定义的返回码

    1. //Define the return code given by the TARS service
    2. const int TARSSERVERSUCCESS = 0; //Server-side processing succeeded
    3. const int TARSSERVERDECODEERR = -1; //Server-side decoding exception
    4. const int TARSSERVERENCODEERR = -2; //Server-side encoding exception
    5. const int TARSSERVERNOFUNCERR = -3; //There is no such function on the server side
    6. const int TARSSERVERNOSERVANTERR = -4; //The server does not have the Servant object
    7. const int TARSSERVERRESETGRID = -5; // server grayscale state is inconsistent
    8. const int TARSSERVERQUEUETIMEOUT = -6; //server queue exceeds limit
    9. const int TARSASYNCCALLTIMEOUT = -7; // Asynchronous call timeout
    10. const int TARSINVOKETIMEOUT = -7; //call timeout
    11. const int TARSPROXYCONNECTERR = -8; //proxy link exception
    12. const int TARSSERVEROVERLOAD = -9; //Server overload, exceeding queue length
    13. const int TARSADAPTERNULL = -10; //The client routing is empty, the service does not exist or all services are down.
    14. const int TARSINVOKEBYINVALIDESET = -11; //The client calls the set rule illegally
    15. const int TARSCLIENTDECODEERR = -12; //Client decoding exception
    16. const int TARSSERVERUNKNOWNERR = -99; //The server is in an abnormal position

    日志

    使用tarsgo轮换日志的快速示例:

    1. TLOG.Debug("Debug logging")

    这将创建一个在tars/util/rogger中定义的*Rogger.Logger,并在调用GetLogger之后,会在config.conf中定义的Logpath下创建一个日志文件,其名称为cfg.App + “.” + cfg.Server + “_“ +名称,该日志文件将在100MB(默认)后轮换,最大轮换文件数为10(默认)。

    如果你不想按文件大小轮换日志。 例如,你想要按天轮换,使用:

    1. TLOG := tars.GetDayLogger("TLOG",1)
    2. TLOG.Debug("Debug logging")

    使用GetHourLogger(“TLOG”,1)按小时轮换日志。如果你想打日志到config.conf中定义的名为tars.tarslog.LogObj的远程服务器上,你不得不先配置一个日志服务器。可以在tars/protocol/res/LogF.tars中找到完整的tars文件定义,可以在Tencent/Tars/cpp/framework/LogServer中查找日志服务器。快速示例如下:

    1. TLOG := GetRemoteLogger("TLOG")
    2. TLOG.Debug("Debug logging")

    如果你想设置日志等级,你可以在Tencent/Tars/web下的tars项目提供的OSS平台上设置它。 如果你想自定义你的日志,请在tars/util/logger,tars/logger.go 和tars/remotelogger.go中的查看更多细节。

    服务管理

    Tars服务框架支持动态接收命令来处理相关的业务逻辑,例如动态更新配置

    tarsgo目前有tars.viewversion / tars.setloglevel管理命令。 用户可以从oss发送管理命令来查看版本或设置日志等级。

    如果你想定义你自己的管理命令,请看下面的例子:

    1. func helloAdmin(who string ) (string, error) {
    2. return who, nil
    3. }
    4. tars.RegisterAdmin("tars.helloAdmin", helloAdmin)

    然后你可以发送自定义的管理命令“tars.helloAdmin tarsgo”,tarsgo将在浏览器中显示。

    举例:

    统计上报

    客户端调用上报接口后,会暂时将信息存储在内存中,当到达某个时间点时,会向tarsstat服务上报(默认为1分钟上报一次)。 我们将两个上报时间点之间的时间间隔称为统计间隔,在统计间隔中会执行诸如聚合和比较相同key的一些操作。 示例代码如下:

    1. //for error
    2. ReportStat(msg, 0, 1, 0)
    3. //for success
    4. ReportStat(msg, 1, 0, 0)
    5. //func ReportStat(msg *Message, succ int32, timeout int32, exec int32)
    6. //see more detail in tars/statf.go

    描述:

    异常上报

    为了更好地监控,TARS框架支持直接向程序中的tarsnotify上报异常情况,并可在WEB管理页面上查看。

    该框架提供了三个宏来上报不同类型的异常:

    1. tars.reportNotifyInfo("Get data from mysql error!")

    Info是一个字符串,可以直接将字符串上报给tarsnotify。 上报的字符串可以在页面上看到,随后,我们可以根据上报的信息进行报警。

    为了便于业务统计,TARS框架还支持在Web管理平台上显示信息。

    目前支持的统计类型包括:

    示例代码如下:

    1. sum := tars.NewSum()
    2. count := tars.NewCount()
    3. max := tars.NewMax()
    4. min := tars.NewMin()
    5. d := []int{10, 20, 30, 50}
    6. distr := tars.NewDistr(d)
    7. p := tars.CreatePropertyReport("testproperty", sum, count, max, min, distr)
    8. for i := 0; i < 5; i++ {
    9. v := rand.Intn(100)
    10. p.Report(v)
    11. }

    描述:

    远程配置

    用户可以从OSS设置远程配置。详情请查看https://github.com/TarsCloud/TarsFramework/blob/master/docs-en/tars_config.md . 如下示例用于说明如何使用此api从远程获取配置文件。

    1. import "github.com/TarsCloud/TarsGo/tars"
    2. ...
    3. cfg := tars.GetServerConfig()
    4. remoteConf := tars.NewRConf(cfg.App, cfg.Server, cfg.BasePath)
    5. config, _ := remoteConf.GetConfig("test.conf")
    6. ...

    setting.go

    tars包中的setting.go用于控制tarsgo性能和特性。有些选项应该从Getserverconfig()中更新。

    1. //number of woker routine to handle client request
    2. //zero means no contorl ,just one goroutine for a client request.
    3. //runtime.NumCpu() usually best performance in the benchmark.
    4. var MaxInvoke int = 0
    5. const (
    6. //for now ,some option shuold update from remote config
    7. //version
    8. TarsVsersion string = "1.0.0"
    9. //server
    10. AcceptTimeout time.Duration = 500 * time.Millisecond
    11. //zero for not set read deadline for Conn (better performance)
    12. ReadTimeout time.Duration = 0 * time.Millisecond
    13. //zero for not set write deadline for Conn (better performance)
    14. WriteTimeout time.Duration = 0 * time.Millisecond
    15. //zero for not set deadline for invoke user interface (better performance)
    16. HandleTimeout time.Duration = 0 * time.Millisecond
    17. IdleTimeout time.Duration = 600000 * time.Millisecond
    18. ZombileTimeout time.Duration = time.Second * 10
    19. QueueCap int = 10000000
    20. //client
    21. ClientQueueLen int = 10000
    22. ClientIdleTimeout time.Duration = time.Second * 600
    23. ClientReadTimeout time.Duration = time.Millisecond * 100
    24. ClientWriteTimeout time.Duration = time.Millisecond * 3000
    25. ReqDefaultTimeout int32 = 3000
    26. ObjQueueMax int32 = 10000
    27. //report
    28. PropertyReportInterval time.Duration = 10 * time.Second
    29. StatReportInterval time.Duration = 10 * time.Second
    30. //mainloop
    31. MainLoopTicker time.Duration = 10 * time.Second
    32. //adapter
    33. AdapterProxyTicker time.Duration = 10 * time.Second
    34. AdapterProxyResetCount int = 5
    35. //communicator default ,update from remote config
    36. refreshEndpointInterval int = 60000
    37. reportInterval int = 10000
    38. AsyncInvokeTimeout int = 3000
    39. //tcp network config
    40. TCPReadBuffer = 128 * 1024 * 1024
    41. TCPWriteBuffer = 128 * 1024 * 1024
    42. TCPNoDelay = false
    43. )

    HTTP支持

    目前的tar.TarsHttpMux和golang内置http.ServeMux使用方式是一致的,其中pattern参数做为监控数据的接口名,后续会参考github.com/gorilla/mux实现功能更强大的路由功能。

    具体实现可参考下面的例子:

    1. package main
    2. import (
    3. "net/http"
    4. "github.com/TarsCloud/TarsGo/tars"
    5. )
    6. func main() {
    7. mux := &tars.TarsHttpMux{}
    8. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    9. w.Write([]byte("Hello tafgo"))
    10. })
    11. cfg := tars.GetServerConfig()
    12. tars.AddHttpServant(mux, cfg.App+"."+cfg.Server+".HttpObj") //Register http server
    13. tars.Run()
    14. }

    Context 支持

    TarsGo 之前在生成的客户端代码,或者用户传入的实现代码里面,都没有使用context。 这使得我们想传递一些框架的信息,比如客户端ip,端口等,或者用户传递一些调用链的信息给框架,都很难于实现。 通过接口的一次重构,支持了context,这些上下文的信息,将都通过context来实现。 这次重构为了兼容老的用户行为,采用了完全兼容的设计。

    服务端使用context

    1. type ContextTestImp struct {
    2. }
    3. //只需在接口上添加 ctx context.Context参数
    4. func (imp *ContextTestImp) Add(ctx context.Context, a int32, b int32, c *int32) (int32, error) {
    5. //我们可以通过context 获取框架传递的信息,比如下面的获取ip, 甚至返回一些信息给框架,详见tars/util/current下面的接口
    6. ip, ok := current.GetClientIPFromContext(ctx)
    7. if !ok {
    8. logger.Error("Error getting ip from context")
    9. }
    10. return 0, nil
    11. }
    12. //以前使用AddServant ,现在只需改成AddServantWithContext

    客户端使用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

    filter机制(插件) 和 zipkin opentracing

    为了支持用户编写插件,我们支持了filter机制,分为服务端的过滤器和客户端过滤器

    //服务端过滤器, 传入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的例子:

    
    

    服务端也会注册一个filter,主要功能就是从request包体的status 提取调用链的上下文,以这个作为父span,进行调用信息的记录。

    详细代码参见 TarsGo/tars/plugin/zipkintracing 完整的zipkin tracing的客户端和服务端例子,详见 TarsGo/examples下面的ZipkinTraceClient和ZipkinTraceServer