thrift協(xié)議分析顾翼、skywalking消息頭實(shí)現(xiàn)

本篇分兩塊來(lái)介紹thrift協(xié)議投放。
thrift定義文件:

struct MiRequest {
   2: required string name;
   3: optional i32 age;
}

exception MiRequestException {
   1: required i32 code;
   2: optional string reason;
}

service MiTestService {
   string miTestMethod(1: MiRequest request) throws (1:MiRequestException qe); 
   string miTestOtherMethod(1: MiRequest request, 2: string pTwo) throws (1:MiRequestException qe); 

}

執(zhí)行thrift --gen java:beans Test.thrift
生成MiTestService.java,本篇后文的通信協(xié)議介紹都是基于MiTestService.java适贸,類結(jié)構(gòu)如下:


image.png

簡(jiǎn)介:
MiTestService.AsyncClient:異步調(diào)用-調(diào)用方協(xié)議處理類
MiTestService.AsyncIface:異步調(diào)用-接口定義
MiTestService.Client:同步調(diào)用-調(diào)用方協(xié)議處理類
MiTestService.Iface:同步調(diào)用-接口定義
MiTestService.miTestMethod_args:方法miTestMethod的入?yún)?br> MiTestService.miTestMethod_result:方法miTestMethod的返回值
MiTestService.miTestOtherMethod_args:方法miTestOtherMethod的入?yún)?br> MiTestService.miTestOtherMethod_result:方法miTestOtherMethod的返回值
MiTestService.Processor:服務(wù)方協(xié)議處理器

以同步遠(yuǎn)程調(diào)用miTestOtherMethod方法為例:

一灸芳、調(diào)用方--方法調(diào)用和消息發(fā)送

MiTestService.Client構(gòu)造方法

    public Client(TProtocol prot)
    {
      this(prot, prot);
    }

    public Client(TProtocol iprot, TProtocol oprot)
    {
      iprot_ = iprot;
      oprot_ = oprot;
    }

TProtocol為底層通信處理類。
備注:
MiTestService.Client協(xié)議處理類:負(fù)責(zé)通信協(xié)議消息體的定義拜姿,消息體的發(fā)送和二進(jìn)制流的解析
TProtocol通信處理類:負(fù)責(zé)處理二進(jìn)制字節(jié)流的發(fā)送和接收

調(diào)用方發(fā)起方法調(diào)用:MiTestService.Client.miTestOtherMethod

public String miTestOtherMethod(MiRequest request, String pTwo) throws MiRequestException, TException
    {
      send_miTestOtherMethod(request, pTwo);
      return recv_miTestOtherMethod();
    }

    public void send_miTestOtherMethod(MiRequest request, String pTwo) throws TException
    {
      oprot_.writeMessageBegin(new TMessage("miTestOtherMethod", TMessageType.CALL, ++seqid_));
      miTestOtherMethod_args args = new miTestOtherMethod_args();
      args.setRequest(request);
      args.setPTwo(pTwo);
      args.write(oprot_);
      oprot_.writeMessageEnd();
      oprot_.getTransport().flush();
    }

重點(diǎn)說(shuō)明:
TProtocol.writeMessageBegin發(fā)送TMessage指定本次調(diào)用的是哪個(gè)方法
miTestOtherMethod_args.write發(fā)送本次方法調(diào)用的入?yún)?/p>

兩塊的詳細(xì)源碼和說(shuō)明如下:

public void writeMessageBegin(TMessage message) throws TException {
    if (strictWrite_) {
      int version = VERSION_1 | message.type;
      writeI32(version);
      writeString(message.name);
      writeI32(message.seqid);
    } else {
      writeString(message.name);
      writeByte(message.type);
      writeI32(message.seqid);
    }
}

public void write(TProtocol oprot) throws TException {
      validate();
      //空實(shí)現(xiàn)
      oprot.writeStructBegin(STRUCT_DESC);
      //發(fā)送入?yún)ⅲ簭?fù)合對(duì)象request
      if (this.request != null) {
        //REQUEST_FIELD_DESC = new TField("request", TType.STRUCT, (short)1);
        //寫入TField.type烙样、TField.id
        oprot.writeFieldBegin(REQUEST_FIELD_DESC);
        //寫入具體內(nèi)容
        this.request.write(oprot);
        //空實(shí)現(xiàn)
        oprot.writeFieldEnd();
      }
      //發(fā)送入?yún)ⅲ夯A(chǔ)類型string
      if (this.pTwo != null) {
        //P_TWO_FIELD_DESC = new TField("pTwo", TType.STRING, (short)2);
        //寫入TField.type、TField.id
        oprot.writeFieldBegin(P_TWO_FIELD_DESC);
        //寫入具體內(nèi)容
        oprot.writeString(this.pTwo);
        //空實(shí)現(xiàn)
        oprot.writeFieldEnd();
      }
      //寫入一個(gè)字節(jié):TType.STOP(整型1)
      oprot.writeFieldStop();
      //空實(shí)現(xiàn)
      oprot.writeStructEnd();
}

遠(yuǎn)程調(diào)用請(qǐng)求信息發(fā)送完畢蕊肥。

二谒获、服務(wù)方--消息接收和解析

MiTestService.Processor構(gòu)造方法:傳入MiTestService.Iface的具體實(shí)現(xiàn)類

public Processor(Iface iface)
    {
      iface_ = iface;
      //方法名-方法處理類映射,每個(gè)方法生成了一個(gè)具體的處理類
      //可以看出方法名稱必須唯一壁却,不支持方法重載
      //方法處理類實(shí)現(xiàn)了MiTestService.Processor.ProcessFunction接口
      processMap_.put("miTestMethod", new miTestMethod());
      processMap_.put("miTestOtherMethod", new miTestOtherMethod());
}

protected static interface ProcessFunction {
      public void process(int seqid, TProtocol iprot, TProtocol oprot) throws TException;
}

//private class miTestMethod implements ProcessFunction

//private class miTestOtherMethod implements ProcessFunction

服務(wù)方調(diào)用TServer.serve開啟端口監(jiān)聽批狱,
接收到消息后將調(diào)用MiTestService.Processor.process方法

    public boolean process(TProtocol iprot, TProtocol oprot) throws TException
    {
      //解析出TMessage消息,主要包含請(qǐng)求調(diào)用的方法名稱
      TMessage msg = iprot.readMessageBegin();
      //
      ProcessFunction fn = processMap_.get(msg.name);
      fn.process(msg.seqid, iprot, oprot);
      return true;
    }

以miTestOtherMethod處理流程為例展东,主要代碼如下:

public void process(int seqid, TProtocol iprot, TProtocol oprot) throws TException
{
        //解析參數(shù)
        miTestOtherMethod_args args = new miTestOtherMethod_args();
        //此處劃重點(diǎn):涉及第三部分可擴(kuò)展性的講解
        args.read(iprot);
        //空實(shí)現(xiàn)
        iprot.readMessageEnd();
        //結(jié)果對(duì)象
        miTestOtherMethod_result result = new miTestOtherMethod_result();
        //發(fā)起方法調(diào)用
        result.success = iface_.miTestOtherMethod(args.request, args.pTwo);
        //調(diào)用結(jié)果寫回
        oprot.writeMessageBegin(new TMessage("miTestOtherMethod", TMessageType.REPLY, seqid));
        result.write(oprot);
        //空實(shí)現(xiàn)
        oprot.writeMessageEnd();
        oprot.getTransport().flush();
}

三赔硫、thrift協(xié)議--字節(jié)碼增強(qiáng)、skywalking可擴(kuò)展消息頭

第三部分將根據(jù)miTestOtherMethod_args.read方法的具體實(shí)現(xiàn)盐肃,
來(lái)解讀thrift協(xié)議的可擴(kuò)展消息頭的實(shí)現(xiàn)原理

public void read(TProtocol iprot) throws TException {
      TField field;
      //非讀--空實(shí)現(xiàn)
      iprot.readStructBegin();
      while (true)
      {
        //讀取Field.type爪膊、Field.id
        field = iprot.readFieldBegin();
        if (field.type == TType.STOP) { 
          break;
        }
        //根據(jù)Field.id匹配關(guān)系,解析出每一個(gè)方法入?yún)?        switch (field.id) {
          case 1: // REQUEST
            if (field.type == TType.STRUCT) {
              this.request = new MiRequest();
              this.request.read(iprot);
            } else { 
              TProtocolUtil.skip(iprot, field.type);
            }
            break;
          case 2: // P_TWO
            if (field.type == TType.STRING) {
              this.pTwo = iprot.readString();
            } else { 
              TProtocolUtil.skip(iprot, field.type);
            }
            break;
          //匹配不上的消息則跳過(guò)
          default:
            TProtocolUtil.skip(iprot, field.type);
        }
        iprot.readFieldEnd();
      }
      //空實(shí)現(xiàn)
      iprot.readStructEnd();
      validate();
    }

由上面的服務(wù)方方法參數(shù)解析實(shí)現(xiàn)可以得出:
1砸王、每一個(gè)方法的方法入?yún)⒍紘?yán)格按照定義的Filed.id來(lái)匹配
2惊完、匹配失敗則走到default邏輯,跳過(guò)匹配異常的消息體

由此可以實(shí)現(xiàn):
字節(jié)碼增強(qiáng)TBinaryProtocol.writeMessageBegin处硬,在此方法執(zhí)行后延(即寫入TMessage后)寫入自定義Field.id的消息體小槐;
字節(jié)碼增強(qiáng)TBinaryProtocol.readMessageBegin 或者 MiTestService.Processor.process,在字節(jié)流解析出TMessage后解析自定義Field.id的消息體荷辕。

自定義消息體可以為任何自定義類型的thrift復(fù)合類型或基礎(chǔ)類型凿跳,傳入skywalking上下文信息。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疮方,一起剝皮案震驚了整個(gè)濱河市控嗜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骡显,老刑警劉巖疆栏,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曾掂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡壁顶,警方通過(guò)查閱死者的電腦和手機(jī)珠洗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)若专,“玉大人许蓖,你說(shuō)我怎么就攤上這事〉魉ィ” “怎么了膊爪?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嚎莉。 經(jīng)常有香客問(wèn)我米酬,道長(zhǎng),這世上最難降的妖魔是什么趋箩? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任赃额,我火速辦了婚禮,結(jié)果婚禮上阁簸,老公的妹妹穿的比我還像新娘爬早。我一直安慰自己哼丈,他們只是感情好启妹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著醉旦,像睡著了一般饶米。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上车胡,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天檬输,我揣著相機(jī)與錄音,去河邊找鬼匈棘。 笑死丧慈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的主卫。 我是一名探鬼主播逃默,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼簇搅!你這毒婦竟也來(lái)了完域?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤瘩将,失蹤者是張志新(化名)和其女友劉穎吟税,沒想到半個(gè)月后凹耙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肠仪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年肖抱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片藤韵。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虐沥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出泽艘,到底是詐尸還是另有隱情欲险,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布匹涮,位于F島的核電站天试,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏然低。R本人自食惡果不足惜喜每,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雳攘。 院中可真熱鬧带兜,春花似錦、人聲如沸吨灭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)喧兄。三九已至无畔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吠冤,已是汗流浹背浑彰。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拯辙,地道東北人郭变。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像涯保,于是被迫代替她去往敵國(guó)和親诉濒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354