从Thrift到I/O多路复用

本文深入探讨Thrift,一个跨语言的RPC框架,详细介绍了Thrift的架构、协议、传输层和不同服务器端的特点,包括TSimpleServer、TThreadPoolServer、TNonblockingServer和THsHaServer。同时,文章还提到了I/O多路复用的概念,对比分析了各种Thrift服务器端的优缺点。
摘要由CSDN通过智能技术生成

Thrift

什么是Thrift?

Thrift是一个轻量级、跨语言的远程服务调用框架,支持C++JavaPythonPHPRuby等。通过代码生成引擎自动生成RPC接口Thrift IDL文件对应的各种主流语言的RPC服务端/客户端模板代码,省去自定义和维护接口编解码、消息传输、服务器多线程模型等基础工作,服务端只需要编写接口实现类,客户端根据服务对象调用远端服务。

Thrift架构

Thrift架构从下往上为传输层、协议层、处理层和服务层。

  • 传输层主要负责从网络中读/写数据,定义了网络传输协议。
  • 协议层定义了数据传输格式,负责处理网络数据的序列化和反序列化。
  • 处理层由IDL生成,封装具体的底层网络传输和序列化方式,并委托给用户实现的Handle进行处理。
  • 服务层提供网络I/O服务模型。

Thrift协议有哪些?

Thrift可以让用户选择客户端与服务端之间传输通信协议的类别。

  • TBinaryProtocol:使用二进制编码格式传输,Thrift的默认传输协议。
  • TCompactProtocol:使用压缩格式传输。
  • TJSONProtocol:使用JSON格式传输。
  • TDebugProtocol:使用文本格式传输,便于debug
  • TSimpleJSONProtocol:提供JSON只写的协议,适用于通过脚本语言解析。

Thrift传输层有哪些?

  • TSocket:阻塞式I/O,用在客户端。
  • TServerSocket:非阻塞式I/O,用于服务器端监听TSocket
  • TNonblockingSocket:非阻塞式I/O,用于构建异步客户端。
  • TMemoryInputTransport:封装了一个字节数组byte[]做输入流。
  • TFramedTransport:非阻塞式I/O,按块的大小进行传输(类似于NIO)。

Thrift服务器端有哪些?

TServer

TServer定义了静态内部类ArgsArgs继承自抽象类AbstractServerArgsAbstractServerArgs采用了建造者模式,向TServer提供各种工厂。

属性 类型 作用
processorFactory TProcessorFactory 处理层工厂类,用于创建TProcessor对象
inputTransportFactory TTransportFactory 传输层输入工厂类,用于创建TTransport对象
outputTransportFactory TTransportFactory 传输层输出工厂类,用于创建TTransport对象
inputProtocolFactory TProtocolFactory 协议层输入工厂类,用于创建TProtocol对象
outputProtocolFactory TProtocolFactory 协议层输出工厂类,用于创建TProtocol对象

TServer核心方法:

serve():启动服务。serve()为抽象方法,不同实现类的启动方式不一样,可各自实现。
stop():关闭服务。
isServing():检测服务状态(启动/关闭)。
setServing(boolean serving):设置服务状态。

TSimpleServer

1. 特点

单线程,阻塞I/O。

2. 设计思想

由主线程负责监听、数据读写、业务处理(一次只能接收和处理一个socket连接)。

3. 使用

客户端:

public class HelloClient {
   
    private static final Logger LOGGER = Logger.getLogger(HelloClient.class.getName());
    public static void main(String[] args) {
   
        TTransport transport = null;
        try {
   
            //传输层使用阻塞I/O
            transport = new TSocket("127.0.0.1", 9090);
            transport.open();
            //使用二进制协议传输数据
            TProtocol protocol = new TBinaryProtocol(transport);
            //使用同步客户端
            GreetingService.Client client = new GreetingService.Client(protocol);
            String name = "XuDT";
            LOGGER.info("HelloClient 请求参数[name]=" + name);
            //调用接口
            String result = client.sayHello(name);
            LOGGER.info("Server 返回结果为" + result);
        } catch (TException e) {
   
            e.printStackTrace();
        } finally {
   
            transport.close();
        }
    }
}

服务端:

public class SimpleServer {
   
    private static final Logger LOGGER = Logger.getLogger(SimpleServer.class.getName());
    public static void main(String[] args) {
   
        try {
   
            //监听端口9090
            TServerSocket serverTransport = new TServerSocket(9090);
            //使用二进制协议传输数据
            TBinaryProtocol.Factory proFactory = new TBinaryProtocol.Factory();
            //关联处理器与HelloService服务实现
            TProcessor processor = new HelloService.Processor(new HelloServiceImpl());
            TSimpleServer.Args serverArgs = new TSimpleServer.Args(serverTransport);
            serverArgs.processor(processor);
            serverArgs.protocolFactory(proFactory);
            //使用TSimpleServer服务端
            TServer server = new TSimpleServer(serverArgs);
            LOGGER.info("Start SimpleServer on port 9090...");
            //启动服务
            server.serve();
        } catch (TTransportException e) {
   
            e.printStackTrace();
        }
    }
}
Processor为HelloService的内部类,调用HelloService.Processor(new HelloServiceImpl())会生成一个processMap,key为接口名称,value为该方法调用对象,后续TBaseProcessor.process()就是通过对processMap进行processMap.get(接口名称)操作获取接口。
4. TSimpleServer源码分析

TSimpleServer继承了TServer,实现了TServerserve()stop()方法。

public class TSimpleServer extends TServer {
   

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

  public TSimpleServer(AbstractServerArgs args) {
   
    super(args);
  }

  /**
   * 启动服务
   */
  public void serve() {
   
    try {
   
      //监听端口
      serverTransport_.listen();
    } catch (TTransportException ttx) {
   
      LOGGER.error("Error occurred during listening.", ttx);
      return;
    }

    // Run the preServe event
    if (eventHandler_ != null) {
   
      eventHandler_.preServe();
    }
    //开启服务
    setServing(true);
    //循环等待客户端请求
    while (!stopped_) {
   
      TTransport client = null;
      TProcessor processor = null;
      TTransport inputTransport = null;
      TTransport outputTransport = null;
      TProtocol inputProtocol = null;
      TProtocol outputProtocol = null;
      ServerContext connectionContext = null;
      try {
   
        //接受连接
        client = serverTransport_.accept();
        if (client != null) {
   
          //TProcessorFactory处理器
          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);
            }
            //处理请求
            processor.process(inputProtocol, outputProtocol);
          }
        }
      } catch (TTransportException ttx) {
   
        // Client died, just move on
      } catch (TException tx) {
   
        if (!stopped_) {
   
          LOGGER.error("Thrift error occurred during processing of message.", tx);
        }
      } catch (Exception x) {
   
        if (!stopped_) {
   
          LOGGER.error("Error occurred during processing of message.", x);
        }
      }
      if (eventHandler_ != null) {
   
        //删除事件
        eventHandler_.deleteContext(connectionContext, inputProtocol, outputProtocol);
      }
      //关闭输入通道
      if (inputTransport != null) {
   
        inputTransport.close();
      }
      //关闭输出通道
      if (outputTransport != null) {
   
        outputTransport.close();
      }
    }
    //关闭服务
    setServing(false);
  }

 /**
   * 停止服务
   */
  public void stop() {
   
    stopped_ = true;
    serverTransport_.interrupt();
  }
}

TBaseProcessor.process():处理请求信息,调用处理方法。

  public void 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();
    } else {
   
      //处理请求
      fn.process(msg.seqid, in, out, iface);
    }
  }
ProcessFunction是一个抽象类,子类也是根据IDL自动生成,与IDL中的函数一一对应,为代理处理器。

ProcessFunction.process():调用接口处理业务请求并返回结果。

public final void process(int seqid, TProtocol iprot, TProtocol oprot, I iface) throws TException {
   
    //获取一个空的参数封装
    T args = getEmptyArgsInstance();
    try {
   
      //从inputProtocol中获取参数赋给args
      args.read(iprot);
    } catch (TProtocolException e) {
   
      //异常处理
    }
    iprot.readMessageEnd();
    TSerializable result = null;
    byte msgType = TMessageType.REPLY;
    try {
   
      //根据参数args调用接口
      result = getResult(iface, args);
    } catch (TTransportException ex) {
   
      //异常处理
    }
    if(!isOneway()) {
   
      //输出调用结果到outputProtocol
      oprot.writeMessageBegin(new TMessage(getMethodName(), msgType, seqid));
      result.write(oprot);
      oprot.writeMessageEnd();
      oprot.getTransport().flush();
    }
  }
5. 时序图

6. 不足

TSimpleServer一次只能处理一个socket连接,效率较低。

TSimpleServer效率低,那有什么办法能提高效率呢?

TThreadPoolServer

1. 特点

多线程,阻塞I/O,主线程负责阻塞监听socket连接,具体的业务处理交由一个线程池来处理。

2. 设计思想

主线程负责阻塞监听socket,当有socket就绪时,将其封装成一个WorkerProcess对象提交到线程池,由线程池负责业务处理后将结果返回给客户端。

线程池默认最小线程数为5,最大线程数为Integer.MAX_VALUE。

3. 使用

客户端同TSimpleServer

服务端:

public class ThreadPoolServer {
   
    private static final Logger LOGGER = Logger.getLogger(ThreadPoolServer.class.getName());

    public static void main(String[] args) {
   
        try {
   
            //监听端口9090
            TServerSocket serverTransport = new TServerSocket(9090);
            //使用二进制协议传输数据
            TBinaryProtocol.Factory proFactory = new TBinaryProtocol.Factory();
            //关联处理器与HelloService服务实现
            TProcessor processor = new HelloService.Processor(new HelloServiceImpl());
            TThreadPoolServer.Args serverArgs = new TThreadPoolServer.Args(serverTransport);
            serverArgs.processor(processor);
            serverArgs.protocolFactory(proFactory);
            //使用TThreadPoolServer服务端
            TServer server = new TThreadPoolServer(serverArgs);
            LOGGER.info("Start ThreadPoolServer on port 9090...");
            //启动服务
            server.serve();
        } catch (TTransportException e) {
   
            e.printStackTrace();
        }
    }
}
4. 源码分析

TThreadPoolServer继承了TServer,实现了TServerserve()stop()方法。

public class TThreadPoolServer extends TServer {
   
  private static final Logger LOGGER = LoggerFactory.getLogger(TThreadPoolServer.class.getName());
  //参数初始化
  public static class Args extends AbstractServerArgs<Args> {
   
  	//线程池参数
    public int minWorkerThreads = 5;
    public int maxWorkerThreads = Integer.MAX_VALUE;
    public ExecutorService executorService;
    public int stopTimeoutVal = 60;
    public TimeUnit stopTimeoutUnit = TimeUnit.SECONDS;
    public int requestTimeout = 20;
    public TimeUnit requestTimeoutUnit = TimeUnit.SECONDS;
    public int beBackoffSlotLength = 100;
    public TimeUnit beBackoffSlotLengthUnit = TimeUnit.MILLISECONDS;

    public Args(TServerTransport transport) {
   
      super(transport);
    }
    public Args minWorkerThreads(int n) {
   
      minWorkerThreads = n;
      return this;
    }
    public Args maxWorkerThreads(int n) {
   
      maxWorkerThreads = n;
      return this;
    }
    public Args stopTimeoutVal(int n) {
   
      stopTimeoutVal = n;
      return this;
    }
    public Args stopTimeoutUnit(TimeUnit tu) {
   
      stopTimeoutUnit = tu;
      return this;
    }
    public Args requestTimeout(int n) {
   
      requestTimeout = n;
      return this;
    }
    public Args requestTimeoutUnit(TimeUnit tu) {
   
      requestTimeoutUnit = tu;
      return this;
    }
    //Binary exponential backoff slot length
    public Args beBackoffSlotLength(int n) {
   
      beBackoffSlotLength = n;
      return this;
    }
    //Binary exponential backoff slot time unit
    public Args beBackoffSlotLengthUnit(TimeUnit tu) {
   
      beBackoffSlotLengthUnit = tu;
      return this;
    }
    public Args executorService(ExecutorService executorService) {
   
      this.executorService = executorService;
      return this;
    }
  }
  //工作线程池
  private ExecutorService executorService_;
  private final TimeUnit stopTimeoutUnit;
  private final long stopTimeoutVal;
  private final TimeUnit requestTimeoutUnit;
  private final long requestTimeout;
  private final long beBackoffSlotInMillis;
  private Random random = new Random(System.currentTimeMillis())
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值