【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、付费专栏及课程。

余额充值