【thrift】 thrift原理浅析

本文深入剖析Thrift的工作原理,从创建非阻塞socket开始,逐步讲解如何构建协议层、实现业务逻辑处理、创建IDL接口的实现类以及处理器。Thrift通过封装socket、协议、处理器,实现数据读写和业务处理,最终在服务器端通过reactor模型处理网络事件。客户端发起RPC调用,Thrift自动处理数据序列化和反序列化,简化通信过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【thrift】 thrift原理浅析

上文介绍了 thrift 入门例子
下面简单介绍 thrift 的基本原理

这里以 thrift server端为例子

1.创建socket
/*
             * thrift 封装的 socket 层,使用端口7911构建一个非阻塞的socket
             */
        TNonblockingServerTransport serverTransport =  new  TNonblockingServerSocket(7911);

server端创建的socket ,这是非阻塞的serversocket,使用的将是nio

看下源码: TNonblockingServerSocket 构造方法最终会调用下面的构造方法
public   TNonblockingServerSocket(InetSocketAddress bindAddr,   int   clientTimeout)   throws   TTransportException {
      clientTimeout_   = clientTimeout;
      try   {
        serverSocketChannel   = ServerSocketChannel. open();
        serverSocketChannel .configureBlocking(   false );

        // Make server socket
        serverSocket_   =   serverSocketChannel .socket();
        // Prevent 2MSL delay problem on server restarts
        serverSocket_ .setReuseAddress(   true );
        // Bind to listening port
        serverSocket_ .bind(bindAddr);
    }   catch   (IOException ioe) {
        serverSocket_   =   null ;
        throw   new   TTransportException(   "Could not create ServerSocket on address "   + bindAddr.toString() +   "." );
    }
  }
构造方法包含的内容很明显,就是创建一个serversocket,并绑定到指定的端口

2.创建输入输出流协议对象
         /*
         * thrift 协议层,这里使用的是 二进制协议
         */
        Factory proFactory =  new  TBinaryProtocol.Factory();

抽取其中部分代码,如下, TBinaryProtocol 对象定义了数据序列化和反序列化的操作
private   byte   []   i32out   =   new   byte [4];
    public   void   writeI32(   int   i32)   throws   TException {
      i32out [0] = ( byte )(0xff & (i32 >> 24));
      i32out [1] = ( byte )(0xff & (i32 >> 16));
      i32out [2] = ( byte )(0xff & (i32 >> 8));
      i32out [3] = ( byte )(0xff & (i32));
      trans_ .write( i32out , 0, 4);
  }

private   byte   []   i32rd   =   new   byte [4];
    public   int   readI32()   throws   TException {
      byte [] buf =   i32rd ;
      int   off = 0;

      if   ( trans_ .getBytesRemainingInBuffer() >= 4) {
      buf =   trans_ .getBuffer();
      off =   trans_ .getBufferPosition();
        trans_ .consumeBuffer(4);
    }   else   {
      readAll( i32rd , 0, 4);
    }
      return
      ((buf[off] & 0xff) << 24) |
      ((buf[off+1] & 0xff) << 16) |
      ((buf[off+2] & 0xff) <<  8) |
      ((buf[off+3] & 0xff));
  }

3.数据 读写 处理
/*
         * thrift idl接口的实现类
         */
        HelloServiceImpl testimpl =  new  HelloServiceImpl();
       
         /*
         * thrift Processor 业务逻辑处理层
         */
        TProcessor processor =  new  HelloService.Processor<HelloServiceImpl>(testimpl);

定义我们Service的实现类  HelloServiceImpl  
创建Processor, HelloServiceImpl  实现类作为参数传入Processor构造方法里
processor主要完成的事情:
1.把idl里定义的方法进行封装,最终暴露出一个统一的接口给thrift server进行调用
2.封装protocol和transport层,包括输入输出流的处理细节、序列化反序列化

看下thrift 生成的 HelloService 里的 Processor 类:
processor类继承  TBaseProcessor
   public   static   class   Processor<I   extends   Iface>   extends   org.apache.thrift.TBaseProcessor<I>   implements   org.apache.thrift.TProcessor {
      private   static   final   Logger   LOGGER   = LoggerFactory.getLogger(Processor. class .getName());

// Processor的构造方法
// getProcessMap 里把我们定义的方法 helloWorld 封装成对象并设置到map里
      public   Processor(I iface) {
        super (iface, getProcessMap( new   HashMap<String, org.apache.thrift.ProcessFunction<I, ?   extends   org.apache.thrift.TBase>>()));
    }

      protected   Processor(I iface, Map<String,  org.apache.thrift.ProcessFunction<I, ?   extends     org.apache.thrift.TBase>> processMap) {
        super (iface, getProcessMap(processMap));
    }

      private   static   <I   extends   Iface> Map<String,  org.apache.thrift.ProcessFunction<I, ?   extends     org.apache.thrift.TBase>> getProcessMap(Map<String,  org.apache.thrift.ProcessFunction<I, ?   extends     org.apache.thrift.TBase>> processMap) {
      processMap.put( "helloWorld" ,   new   helloWorld());
        return   processMap;
    }

// 这里定义了一个对象,里面封装了 对外暴露的接口方法,如demo里,我定义了 helloWorld 方法
      public   static   class   helloWorld<I   extends   Iface>   extends   org.apache.thrift.ProcessFunction<I, helloWorld_args> {
        public   helloWorld() {
          super ( "helloWorld" );
      }

        public   helloWorld_args getEmptyArgsInstance() {
          return   new   helloWorld_args();
      }

        protected   boolean   isOneway() {
          return   false ;
      }

// getResult 重写了父类的方法
// 这个getResult里 iface.helloWorld 会实际调用 我们的业务方法并返回结果
        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.   hello );
          return   result;
      }
    }

// Processor 继承的  TBaseProcessor 类
public   abstract   class   TBaseProcessor<I>   implements   TProcessor {
    private   final   I   iface ;
    private   final   Map<String,ProcessFunction<I, ?   extends   TBase>>   processMap ;

// Processor构造方法调用  TBaseProcessor 方法,设置Service类及对外提供的接口方法map
    protected   TBaseProcessor (I iface, Map<String, ProcessFunction<I, ?   extends   TBase>> processFunctionMap) {
      this . iface   = iface;
      this . processMap   = processFunctionMap;
  }

    public   Map<String,ProcessFunction<I, ?   extends   TBase>> getProcessMapView() {
      return   Collections.unmodifiableMap( processMap   );
  }
// 这个process 方法是processor最终暴露出去的方法
// 方法的整体逻辑很明确:从输入流读取数据,解析得到要调用的方法并进行调用

    @Override
    public   boolean   process(TProtocol in, TProtocol out)   throws   TException {
    TMessage msg = in.readMessageBegin();
    ProcessFunction fn =   processMap .get(msg. name );
      if   (fn ==   null ) {
      TProtocolUtil.skip(in, TType. STRUCT );
      in.readMessageEnd();
      TApplicationException x =   new   TApplicationException(TApplicationException. UNKNOWN_METHOD   ,   "Invalid method name: '" +msg. name + "'" );
      out.writeMessageBegin(   new   TMessage(msg.   name , TMessageType. EXCEPTION , msg. seqid   ));
      x.write(out);
      out.writeMessageEnd();
      out.getTransport().flush();
        return   true ;
    }

    fn.process(msg. seqid , in, out,   iface );
      return   true ;
  }
}

在processor中,我们看到 helloWorld 类 继承了  ProcessFunction
在processFunction 的 process 方法里,可以看到processor处理输入输出流的具体流程
public   abstract   class   ProcessFunction<I, T   extends   TBase> {
    private   final   String   methodName ;

    private   static   final   Logger   LOGGER   = LoggerFactory.getLogger(ProcessFunction.   class .getName());

    public   ProcessFunction(String methodName) {
      this . methodName   = methodName;
  }

    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();
      TApplicationException x =   new   TApplicationException(TApplicationException. PROTOCOL_ERROR   , e.getMessage());
      oprot.writeMessageBegin(   new   TMessage(getMethodName(), TMessageType. EXCEPTION , seqid));
      x.write(oprot);
      oprot.writeMessageEnd();
      oprot.getTransport().flush();
        return ;
    }
    iprot.readMessageEnd();
    TBase result =   null ;

// 然后调用子类的重写的 getResult 方法,即最终是调用我们写的业务方法
      try   {
      result = getResult(iface, args);
    }   catch (TException tex) {
        LOGGER .error( "Internal error processing "   + getMethodName(), tex);
      TApplicationException x =   new   TApplicationException(TApplicationException. INTERNAL_ERROR   ,
          "Internal error processing "   + getMethodName());
      oprot.writeMessageBegin(   new   TMessage(getMethodName(), TMessageType. EXCEPTION , seqid));
      x.write(oprot);
      oprot.writeMessageEnd();
      oprot.getTransport().flush();
        return ;
    }
// 然后把结果返回,thrift 里有个oneway 方法,oneway的方法调用完不等待直接返回
      if (!isOneway()) {
      oprot.writeMessageBegin(   new   TMessage(getMethodName(), TMessageType. REPLY , seqid));
      result.write(oprot);
      oprot.writeMessageEnd();
      oprot.getTransport().flush();
    }
  }

}

在thrift里,把方法给封装成了对象,通过方法名进行关联
通信的过程通过两个变量进行定位调用
iface 变量定位到具体的实现类,server与client共用一套idl生成的代码,编译期就确定好了
ProcessFunction 会定位到具体的方法,这个是运行中动态确定的

4.server服务

Args rpcArgs =  new  Args(serverTransport);

        rpcArgs.processor(processor);

        rpcArgs.protocolFactory(proFactory);

         /*
         * thrift server
         */
        TServer server =  new  THsHaServer(rpcArgs);
       
        server.serve();
这个是server的初始化及启动

thrift server模型大概4种,分别为:
TSimpleServer:单线程阻塞模型,accept一个连接、处理,然后返回结果
THsHaServer:nio非阻塞,使用了reactor模型,网络连接处理和IO读写由一个线程完成,业务处理由线程池处理
TThreadPoolServer:线程池阻塞,主线程侦听连接,accept的连接放到线程池工作线程处理
TThreadedSelectorServer:nio非阻塞,使用了两层reactor
     网络连接处理由一个reactor线程处理,IO读写由另外的reactor线程处理,业务处理由线程池处理


下面基于 THsHaServer模型来分析

TServer server =  new  THsHaServer(rpcArgs);
这个构造方法的作用就是创建并初始化 server,初始化数据就是前面我们创建的 transport、protocol和Processor

我们从   server.serve() 这个方法来入手
/**
   * Begin accepting connections and processing invocations.
   */
    public   void   serve() {
      // start any IO threads
      if   (!startThreads ()) {
        return ;
    }

      // start listening, or exit
      if   (!startListening()) {
        return ;
    }

    setServing( true );

      // this will block while we serve
    waitForShutdown();

    setServing( false );

      // do a little cleanup
    stopListening ();
  }

serve 方法里主要的是  startThreads 这个方法
startThreads 方法主要作用就是创建 reactor 线程 SelectAcceptThread
protected   boolean   startThreads() {
      // start the selector
      try   {
        selectAcceptThread_   =   new   SelectAcceptThread((TNonblockingServerTransport) serverTransport_   );
        selectAcceptThread_ .start();
        return   true ;
    }   catch   (IOException e) {
        LOGGER .error( "Failed to start selector thread!" , e);
        return   false ;
    }
  }

SelectAcceptThread这个线程的代码逻辑很明确,就是处理侦听和IO读写
public   void   run() {
        try   {
          if   ( eventHandler_   !=   null ) {
            eventHandler_ .preServe();
        }
// 线程不断轮询,处理网络事件
          while   (! stopped_ ) {
          select();
          processInterestChanges();
        }
          for   (SelectionKey selectionKey :   selector .keys()) {
          cleanupSelectionKey(selectionKey);
        }
      }   catch   (Throwable t) {
          LOGGER .error( "run() exiting due to uncaught error" , t);
      }   finally   {
          stopped_   =   true ;
      }
    }

// select 方法侦听网络事件,根据事件类型进行不同处理
      /**
     * Select and process IO events appropriately:
     * If there are connections to be accepted, accept them.
     * If there are existing connections with data waiting to be read, read it,
     * buffering until a whole frame has been read.
     * If there are any pending responses, buffer them until their target client
     * is available, and then send the data.
     */
      private   void   select() {
        try   {
          // wait for io events.
          selector .select();

          // process the io events we received
        Iterator<SelectionKey> selectedKeys =   selector .selectedKeys().iterator();
          while   (! stopped_   && selectedKeys.hasNext()) {
          SelectionKey key = selectedKeys.next();
          selectedKeys.remove();

            // skip if not valid
            if   (!key.isValid()) {
            cleanupSelectionKey(key);
              continue ;
          }

            // if the key is marked Accept, then it has to be the server
            // transport.
            if   (key.isAcceptable()) {
            handleAccept();
          }   else   if   (key.isReadable()) {
              // deal with reads
            handleRead(key);
          }   else   if   (key.isWritable()) {
              // deal with writes
            handleWrite(key);
          }   else   {
              LOGGER .warn(   "Unexpected state in select! "   + key.interestOps());
          }
        }
      }   catch   (IOException e) {
          LOGGER .warn( "Got an IOException while selecting!" , e);
      }
    }

下面分别看下处理 网络连接和读的情况
/**
     * Accept a new connection.
     */
      private   void   handleAccept ()   throws   IOException {
      SelectionKey clientKey =   null ;
      TNonblockingTransport client =   null ;
        try   {
          // accept the connection
        client = (TNonblockingTransport)   serverTransport .accept();
        clientKey = client.registerSelector(   selector , SelectionKey. OP_READ );

// 创建的链接,会构建出一个buffer对象,封装链接对象、输入输出流对象,数据读写细节
          // add this key to the map
          FrameBuffer frameBuffer =   processorFactory_ .isAsyncProcessor() ?
                    new   AsyncFrameBuffer(client, clientKey,SelectAcceptThread. this   ) :
                    new   FrameBuffer(client, clientKey,SelectAcceptThread. this   );

          clientKey.attach(frameBuffer);
      }   catch   (TTransportException tte) {
          // something went wrong accepting.
          LOGGER .warn( "Exception trying to accept!" , tte);
        tte.printStackTrace();
          if   (clientKey !=   null ) cleanupSelectionKey(clientKey);
          if   (client !=   null ) client.close();
      }
    }


  /**
     * Do the work required to read from a readable client. If the frame is
     * fully read, then invoke the method call.
     */
      protected   void   handleRead(SelectionKey key) {
      FrameBuffer buffer = (FrameBuffer) key.attachment();
// 从输入流读取数据放入 buffer中
        if   (!buffer.read()) {
        cleanupSelectionKey(key);
          return ;
      }

        // if the buffer's frame read is complete, invoke the method.
        if   (buffer.isFrameFullyRead()) {
// 如果数据读取完成了,就调用 THsHaServer. requestInvoke 方法
          if   (!requestInvoke(buffer)) {
          cleanupSelectionKey(key);
        }
      }
    }
requestInvoke 方法实际上是构建一个Runnable对象,扔到线程池去处理,实际最终会调用  FrameBuffer.invoke 方法
  protected   boolean   requestInvoke(FrameBuffer frameBuffer) {
      try   {
      Runnable invocation = getRunnable(frameBuffer);
// 构建线程任务,扔到线程池
        invoker .execute(invocation);
        return   true ;
    }   catch   (RejectedExecutionException rx) {
        LOGGER .warn( "ExecutorService rejected execution!" , rx);
        return   false ;
    }
  }

看下  FrameBuffer.invoke  , 里面我们可以看到  processorFactory_ .getProcessor(  inTrans_ ).process(  inProt_ outProt_ );
这里我看到了之前 说的 Processor 的方法的入口
/**
     * Actually invoke the method signified by this FrameBuffer.
     */
      public   void   invoke() {
        frameTrans_ .reset( buffer_ .array());
        response_ .reset();
     
        try   {
          if   ( eventHandler_   !=   null ) {
            eventHandler_ .processContext(   context_ ,   inTrans_ ,   outTrans_ );
        }
// process 方法就会触发Processor层的处理了
          processorFactory_ .getProcessor(   inTrans_ ).process(   inProt_ ,   outProt_ );
        responseReady();
          return ;
      }   catch   (TException te) {
          LOGGER .warn( "Exception while invoking!" , te);
      }   catch   (Throwable t) {
          LOGGER .error( "Unexpected throwable while invoking!" , t);
      }
        // This will only be reached when there is a throwable.
        state_   = FrameBufferState.   AWAITING_CLOSE ;
      requestSelectInterestChange();
    }


5.client
下面看下client端,是如何发起调用的
/*
          * 这里的client 为thrift 编译生成的代码 HelloService 里client类
          */
         Client client =  new  Client(protocol);
        
          /*
          * 调用 方法 helloWorld  整个 RPC调用过程由thrift处理,feel so easy
          */
         String res = client.helloWorld(  "I'm client" );

看下 client类的 helloWorld 方法
逻辑很明确, 发起调用请求,然后等待获取调用结果
  public   String helloWorld(String hello)   throws   org.apache.thrift.TException
    {
      send_helloWorld(hello);
        return   recv_helloWorld();
    }

      public   void   send_helloWorld(String hello)   throws   org.apache.thrift.TException
    {
      helloWorld_args args =   new   helloWorld_args();
      args.setHello(hello);
      sendBase( "helloWorld" , args);
    }

      public   String recv_helloWorld()   throws   org.apache.thrift.TException
    {
      helloWorld_result result =   new   helloWorld_result();
      receiveBase(result,   "helloWorld" );
        if   (result.isSetSuccess()) {
          return   result.   success ;
      }
        throw   new   org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException. MISSING_RESULT ,   "helloWorld failed: unknown result" );
    }

调用的  sendBase  receiveBase 这两个方法封装了数据读写的逻辑
protected   void   sendBase(String methodName, TBase args)   throws   TException {
      oprot_ .writeMessageBegin(   new   TMessage(methodName, TMessageType.   CALL , ++ seqid_ ));
    args.write( oprot_ );
      oprot_ .writeMessageEnd();
      oprot_ .getTransport().flush();
  }

    protected   void   receiveBase(TBase result, String methodName)   throws   TException {
    TMessage msg =   iprot_ .readMessageBegin();
      if   (msg. type   == TMessageType.   EXCEPTION ) {
      TApplicationException x = TApplicationException. read( iprot_ );
        iprot_ .readMessageEnd();
        throw   x;
    }
      if   (msg. seqid   !=   seqid_ ) {
        throw   new   TApplicationException(TApplicationException. BAD_SEQUENCE_ID   , methodName +   " failed: out of sequence response"   );
    }
    result.read( iprot_ );
      iprot_ .readMessageEnd();
  }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值