Thrift基本原理

前言

基于上一次的简单介绍做一次稍微深入的分析

正文

  1. 客户端如何发送
    由上次结尾的一个简单demo作为分析的例子,看下作为客户端如何发送数据的。
public class HelloServiceClient {
    public static void main(String[] args) throws TException {
        System.out.println("客户端启动.....");
        //1.初始化传输工具
        TTransport transport =new TSocket("10.10.163.11", 8999, 30000);
        //2.绑定传输协议
        TProtocol protocol = new TBinaryProtocol(transport);
        //3.client绑定传输协议
        Hello.Client client = new Hello.Client(protocol);
        transport.open();
        //4.client调用,发送消息
        String result = client.helloWorld("TOM");
        System.out.println(result);
    }
}

看一下Hello.Client调用方法

public String helloWorld(String para) throws org.apache.thrift.TException
    {
      send_helloWorld(para);
      return recv_helloWorld();
    }

    public void send_helloWorld(String para) throws org.apache.thrift.TException
    {
      //初始化并设置参数对象
      helloWorld_args args = new helloWorld_args();
      args.setPara(para);
      //发送信息
      sendBase("helloWorld", args);
    }

Hello.Client中sendBase方法

protected void sendBase(String methodName, TBase<?,?> args) throws TException {
    sendBase(methodName, args, TMessageType.CALL);
  }
private void sendBase(String methodName, TBase<?,?> args, byte type) throws TException {
    //封装消息并写入传输协议中
    oprot_.writeMessageBegin(new TMessage(methodName, type, ++seqid_));
    args.write(oprot_);
    oprot_.writeMessageEnd();
    //发送消息
    oprot_.getTransport().flush();
  }

TMessage对象定义了三个成员变量,重点还是在writeMessageBegin方法中
由于我们的demo绑定的传输协议是TBinaryProtocol(二进制编码格式进行数据传输)
所以我们查看TBinaryProtocol的writeMessageBegin方法

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 writeI32(int i32) throws TException {
    inoutTemp[0] = (byte)(0xff & (i32 >> 24));
    inoutTemp[1] = (byte)(0xff & (i32 >> 16));
    inoutTemp[2] = (byte)(0xff & (i32 >> 8));
    inoutTemp[3] = (byte)(0xff & (i32));
    trans_.write(inoutTemp, 0, 4);
  }
public void writeString(String str) throws TException {
    try {
      byte[] dat = str.getBytes("UTF-8");
      writeI32(dat.length);
      trans_.write(dat, 0, dat.length);
    } catch (UnsupportedEncodingException uex) {
      throw new TException("JVM DOES NOT SUPPORT UTF-8");
    }
  }

基本上就是写入版本,写入方法长度,写入方法名,最后在写入seqid
最后再由Hello.helloWorld_args类调用helloWorld_argsStandardScheme中write方法写入参数信息

public void write(org.apache.thrift.protocol.TProtocol oprot, helloWorld_args struct) throws org.apache.thrift.TException {
        struct.validate();//空方法
        oprot.writeStructBegin(STRUCT_DESC);//空方法
        if (struct.para != null) {
          //写入参数信息
          oprot.writeFieldBegin(PARA_FIELD_DESC);
          oprot.writeString(struct.para);
          oprot.writeFieldEnd();
        }
        oprot.writeFieldStop();
        oprot.writeStructEnd();
      }

下面看下接受信息的处理

//Hello.Client
public String recv_helloWorld() throws org.apache.thrift.TException
    {
      helloWorld_result result = new helloWorld_result();
      receiveBase(result, "helloWorld");
      if (result.isSetSuccess()) {
        return result.success;
      }
     ...
    }
protected void receiveBase(TBase<?,?> result, String methodName) throws TException {
    TMessage msg = iprot_.readMessageBegin();
    if (msg.type == TMessageType.EXCEPTION) {
      ...
    }
    System.out.format("Received %d%n", msg.seqid);
    if (msg.seqid != seqid_) {
    ...
    }
    result.read(iprot_);
    iprot_.readMessageEnd();
  }

以上就是客户端的整个处理流程,大概时序图如下


client-process.png

下面看下服务端的处理流程
先看下服务端的代码

public class HelloServiceServer {
    public static void main(String[] args) throws TTransportException {
        System.out.println("服务端开启......");
        TProcessor tProcessor=new Hello.Processor<Hello.Iface>(new HelloServiceImpl());
        TServerSocket serverSocket = new TServerSocket(8999);
        TServer.Args tArgs = new TServer.Args(serverSocket);
        tArgs.processor(tProcessor);
        tArgs.protocolFactory(new TBinaryProtocol.Factory());
        TServer server = new TSimpleServer(tArgs);
        server.serve();
    }
}

前面的都是一些绑定processor,protocol还有启动绑定serversocket,主要看一下server.serve();

public void serve() {
   ....
        client = serverTransport_.accept();
        if (client != null) {
          processor = processorFactory_.getProcessor(client);
          inputTransport = inputTransportFactory_.getTransport(client);
          outputTransport = outputTransportFactory_.getTransport(client);
          inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
          outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
          if (eventHandler_ != null) {
            connectionContext = eventHandler_.createContext(inputProtocol, outputProtocol);
          }
          while (true) {
            if (eventHandler_ != null) {
              eventHandler_.processContext(connectionContext, inputTransport, outputTransport);
            }
            if(!processor.process(inputProtocol, outputProtocol)) {
              break;
            }
          }
        }
     ....
  }

前面的代码主要是从客户端获取对应的数据,主要看下处理流程即processor.process()

public boolean process(TProtocol in, TProtocol out) throws TException {
    TMessage msg = in.readMessageBegin();//获取TMessage
    ProcessFunction fn = processMap.get(msg.name);//根据方法名获取对应的方法处理类
    if (fn == null) {
      .....
    }
    fn.process(msg.seqid, in, out, iface);
    return true;
  }

当获取方法处理类时,执行处理方法

//Hello.java 中helloWorld类
public helloWorld_args getEmptyArgsInstance() {
        return new helloWorld_args();
      }
//helloWorld_args类中read方法,与客户端对应
public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
      schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
    }
//Hello.java 中Processor类执行了实现类HelloServiceImpl
public helloWorld_result getResult(I iface, helloWorld_args args) throws org.apache.thrift.TException {
        helloWorld_result result = new helloWorld_result();
        result.success = iface.helloWorld(args.para);
        return result;
      }
public final void process(int seqid, TProtocol iprot, TProtocol oprot, I iface) throws TException {
    T args = getEmptyArgsInstance();
    try {
      args.read(iprot);
    } catch (TProtocolException e) {
     ....
    }
    iprot.readMessageEnd();
    TBase result = null;

    try {
      result = getResult(iface, args);
    } catch(TException tex) {
     ....
    }

    if(!isOneway()) {//跟客户端差不多了
      oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.REPLY, seqid));
      result.write(oprot);//结果写到输出流
      oprot.writeMessageEnd();
      oprot.getTransport().flush();
    }
  }

总结

我们再来看一下Thrift的协议栈以及上述流程的抓包信息


thrift.PNG

Thrift_protocol.png

基本上对Thrift的流程熟悉了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容

  • Thrift是什么? Thrift是Facebook于2007年开发的跨语言的rpc服框架,提供多语言的编译功能,...
    jiangmo阅读 9,414评论 0 6
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 前言: 目前流行的服务调用方式有很多种,例如基于 SOAP 消息格式的 Web Service,基于 JSON 消...
    我是嘻哈大哥阅读 1,557评论 0 9
  • 转自:http://blog.csdn.net/kesonyk/article/details/50924489 ...
    晴天哥_王志阅读 24,802评论 2 38
  • 何为无常?生灭变化不定。世事难料,永无定数。 课间休息时,忽然接到同事-------一个小妹妹的微...
    千观阅读 245评论 2 1