溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

TarsGo新版本發(fā)布,支持protobuf,zipkin和自定義插件

發(fā)布時(shí)間:2020-06-15 21:55:51 來(lái)源:網(wǎng)絡(luò) 閱讀:457 作者:TARS小助手 欄目:云計(jì)算

本文作者:陳明杰(sandyskies)
Tars是騰訊從2008年到今天一直在使用的后臺(tái)邏輯層的統(tǒng)一應(yīng)用框架,目前支持C++,Java,PHP,Nodejs,Golang語(yǔ)言。該框架為用戶提供了涉及到開發(fā)、運(yùn)維、以及測(cè)試的一整套解決方案,幫助一個(gè)產(chǎn)品或者服務(wù)快速開發(fā)、部署、測(cè)試、上線。 它集可擴(kuò)展協(xié)議編解碼、高性能RPC通信框架、名字路由與發(fā)現(xiàn)、發(fā)布監(jiān)控、日志統(tǒng)計(jì)、配置管理等于一體,通過(guò)它可以快速用微服務(wù)的方式構(gòu)建自己的穩(wěn)定可靠的分布式應(yīng)用,并實(shí)現(xiàn)完整有效的服務(wù)治理。目前該框架在騰訊內(nèi)部,各大核心業(yè)務(wù)都在使用,頗受歡迎,基于該框架部署運(yùn)行的服務(wù)節(jié)點(diǎn)規(guī)模達(dá)到上萬(wàn)個(gè)。
Tars 于2017年4月開源,并于2018年6月加入Linux 基金會(huì)。
TarsGo 是Tars 的Go語(yǔ)言實(shí)現(xiàn)版本, 于2018年9月開源, 項(xiàng)目地址 https://github.com/TarsCloud/TarsGo ,歡迎star 。

TarsGo 新版本發(fā)布

在上次開源之后,有些用戶反饋了一些需求,基于用戶反饋的需求,我們進(jìn)行了實(shí)現(xiàn),并發(fā)布了1.1.0版本。 本次發(fā)布新增了:支持pb、支持zipkin分布式追蹤、支持filter(自定義插件編寫)、支持context 等,除此之外還做了一系列優(yōu)化和bugfix。

新功能:PB支持

Protocol Buffers (簡(jiǎn)稱 PB )是 Google 的一種數(shù)據(jù)交換的格式,它獨(dú)立于語(yǔ)言,獨(dú)立于平臺(tái),最早公布于 2008年7月。隨著微服務(wù)架構(gòu)的發(fā)展及自身的優(yōu)異表現(xiàn),ProtoBuf 可用于諸如網(wǎng)絡(luò)傳輸、配置文件、數(shù)據(jù)存儲(chǔ)等諸多領(lǐng)域,目前在互聯(lián)網(wǎng)上有著大量應(yīng)用。
如果對(duì)于現(xiàn)有已使用grpc,使用proto文件,想轉(zhuǎn)換成tars協(xié)議的用戶而言,需要將上面的proto文件翻譯成Tars文件。這種翻譯會(huì)比較繁瑣,而且容易出錯(cuò)。 為此我們決定編寫插件支持proto文件直接生成tars的rpc邏輯。protoc-gen-go的代碼邏輯里面是預(yù)留了插件編寫的規(guī)范的,參照grpc,主要有 grpc/grpc.go 和一個(gè)導(dǎo)入插件的link_grpc.go 。 這里我們編寫 tarsrpc/tarsrpc.go 和 link_tarsrpc.go
使用方面:

  • 將這兩個(gè)文件放到protoc-gen-go 下面,go install重新生成protoc-gen-go 二進(jìn)制

  • 定義proto 文件

  • 使用重新編譯安裝的protoc-gen-go生成序列化和rpc相關(guān)接口代碼
protoc --go_out=plugins=tarsrpc:. helloworld.proto
  • 編寫tars 客戶端和服務(wù)端代碼,參數(shù)使用pb生成的結(jié)構(gòu)體,其余代碼邏輯和正常的tars服務(wù)一致。

  • 詳細(xì)原理和使用文檔,閱讀 騰訊云社區(qū)文章

新功能: filter機(jī)制, 支持zipkin分布式追蹤

為了支持用戶編寫插件,我們支持了filter機(jī)制,分為服務(wù)端的過(guò)濾器和客戶端過(guò)濾器,用戶可以基于這個(gè)機(jī)制,實(shí)現(xiàn)自己的TarsGo插件。

//服務(wù)端過(guò)濾器, 傳入dispatch,和f, 用于調(diào)用用戶代碼, req, 和resp為傳入的用戶請(qǐng)求和服務(wù)端相應(yīng)包體
type ServerFilter func(ctx context.Context, d Dispatch, f interface{}, req *requestf.RequestPacket, resp *requestf.ResponsePacket, withContext bool) (err error)
//客戶端過(guò)濾器, 傳入msg(包含obj信息,adapter信息,req和resp包體), 還有用戶設(shè)定的調(diào)用超時(shí)
type ClientFilter func(ctx context.Context, msg *Message, invoke Invoke, timeout time.Duration) (err error)
//注冊(cè)服務(wù)端過(guò)濾器
//func RegisterServerFilter(f ServerFilter)
//注冊(cè)客戶端過(guò)濾器
//func RegisterClientFilter(f ClientFilter)

有了過(guò)濾器,我們就能對(duì)服務(wù)端和客戶端的請(qǐng)求做一些過(guò)濾,比如使用 hook用于分布式追蹤的opentracing 的span。
我們來(lái)看下客戶端filter的例子:

//生成客戶端tars filter,通過(guò)注冊(cè)這個(gè)filter來(lái)實(shí)現(xiàn)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
        //先從客戶端調(diào)用的context 里面看下有沒(méi)有傳遞來(lái)調(diào)用鏈的信息,
        //如果有,則以這個(gè)做為父span,如果沒(méi)有,則起一個(gè)新的span,span名字是RPC請(qǐng)求的函數(shù)名
        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()

        //設(shè)置span的信息,比如我們調(diào)用的客戶端的ip地址,請(qǐng)求的接口,方法,協(xié)議,客戶端版本等信息
        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注入到 請(qǐng)求包體的  Status里面,status 是一個(gè)map[strint]string 的結(jié)構(gòu)體
        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
            }
        }
        //沒(méi)什么其他需要修改的,就進(jìn)行客戶端調(diào)用
        err = invoke(ctx, msg, timeout)
        if err != nil {
            //調(diào)用錯(cuò)誤,則記錄span的錯(cuò)誤信息
            ext.Error.Set(cSpan, true)
            cSpan.LogFields(oplog.String("event", "error"), oplog.String("message", err.Error()))
        }

        return err
    }

服務(wù)端也會(huì)注冊(cè)一個(gè)filter,主要功能就是從request包體的status 提取調(diào)用鏈的上下文,以這個(gè)作為父span,進(jìn)行調(diào)用信息的記錄。
整體的一個(gè)效果:
TarsGo新版本發(fā)布,支持protobuf,zipkin和自定義插件

詳細(xì)代碼參見 TarsGo/tars/plugin/zipkintracing
完整的zipkin tracing的客戶端和服務(wù)端例子,詳見 TarsGo/examples下面的ZipkinTraceClient和ZipkinTraceServer

新功能: 支持context

TarsGo 之前在生成的客戶端代碼,或者用戶傳入的實(shí)現(xiàn)代碼里面,都沒(méi)有使用context。 這使得我們想傳遞一些框架的信息,比如客戶端ip,端口等,或者用戶傳遞一些調(diào)用鏈的信息給框架,都很難于實(shí)現(xiàn)。 通過(guò)接口的一次重構(gòu),支持了context,這些上下文的信息,將都通過(guò)context來(lái)實(shí)現(xiàn)。 這次重構(gòu)為了兼容老的用戶行為,采用了完全兼容的設(shè)計(jì)。

服務(wù)端使用context

type ContextTestImp struct {
}
//只需在接口上添加 ctx context.Context參數(shù)
func (imp *ContextTestImp) Add(ctx context.Context, a int32, b int32, c *int32) (int32, error) {
    //我們可以通過(guò)context 獲取框架傳遞的信息,比如下面的獲取ip, 甚至返回一些信息給框架,詳見tars/util/current下面的接口
    ip, ok := current.GetClientIPFromContext(ctx)
    if !ok {
        logger.Error("Error getting ip from context")
    }  
    return 0, nil
}
//以前使用AddServant ,現(xiàn)在只需改成AddServantWithContext
app.AddServantWithContext(imp, cfg.App+"."+cfg.Server+".ContextTestObj")

客戶端使用context


    ctx := context.Background()
    c := make(map[string]string)
    c["a"] = "b" 
//以前使用app.Add 進(jìn)行客戶端調(diào)用,這里只要變成app.AddWithContext ,就可以傳遞context給框架,如果要設(shè)置給tars請(qǐng)求的context
//可以多傳入?yún)?shù),比如c,參數(shù)c是可選的,格式是 ...[string]string
    ret, err := app.AddWithContext(ctx, i, i*2, &out, c)

服務(wù)端和客戶端的完整例子,詳見 TarGo/examples

其他優(yōu)化和修復(fù)

  • 將request package 的Sbuffer字段由vector<unsigned byte> 改成vector<byte>,解決和其他語(yǔ)言通信問(wèn)題
  • 修復(fù)stat監(jiān)控上報(bào)問(wèn)題
  • 日志級(jí)別從遠(yuǎn)端更新
  • 修復(fù)路由刷新協(xié)程極端情況下死鎖問(wèn)題
  • 優(yōu)化協(xié)程池方案,并添加協(xié)程池方案
  • 修復(fù)go協(xié)程啟動(dòng)順序?qū)е聀anic問(wèn)題
  • golint大部分代碼
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI