Thrift步骤

1.定义Thrift服务

即编写.thrift文件:

struct User {
    1: i32 ID
    2: string Name
  }
 
service UserService {
    User GetUserByID(1:i32 userID)
    list<User> GetAllUser() 
} 

service定义的是服务类,struct是传入或者传出的复杂数据类型(支持对象级联)。
语法规范 http://thrift.apache.org/docs/idl

2.编译Thrift

用thrift工具编译上面定义的.thrift文件,然后客户端和服务端都必须持有对应开发语言的服务接口类(这里会生成两个类:User.cs和UserService.cs,假设用C#开发)
在这里插入图片描述
上面是Thrift编译生成的文件中最重要的几个概念:

  • Isync、Iface:相当于服务接口,RPC的主要目的就是让server和client两者通过这个接口进行交互
  • Client:顾名思义客户端需要使用的,客户端持有这个服务对象,并借由这个对象中的Client来向server发起RPC请求(需要以Protocol为参数创建)
  • Processor:serve端使用的,主要是用来解析client的RPC调用,并负责执行响应client(需要以服务具体实现类xxxImp作为参数创建)

3.定义server

A Server pulls together all of the various features described above:

  • Create a transport:实质上就是创建一个socket对象,相当于是对网络输入输出流的抽象
  • Create input/output protocols for the transport:定义原始网络数据流的编码解码规则,不同的序列化格式编码解码规则不同
  • Create a processor based on the input/output protocols:按照某种protocol(编码/解码规则),写入或写出数据流到socket中
  • Create server by certain I/O model:定义服务端IO模式,阻塞式IO、非阻塞式IO(IO多路复用)、多线程阻塞式IO、多线程非阻塞式IO
  • Start server :开启server
  • Wait for incoming connections and hand them off to the processor:等待连接到来,如果连接到来,由processor进行解码
    ,并执行相应的服务程序
  1. 单一服务接口

    //Create a transport
    TServerSocket serverTransport = new TServerSocket(8080, 0, false);
    //Create a processor
    UserService.Processor processor = new UserService.Processor(new UserServiceImp());
    //Create server by certain I/O model
    TServer server = new TThreadPoolServer(processor, serverTransport);
    //Start server and wait for incoming connections and hand them off to the processor
    server.Serve();
    
  2. 多服务接口

    //定义多服务接口,此TMultiplexedProcessor能够处理多个服务的输入输出流
    TMultiplexedProcessor processor = new TMultiplexedProcessor();
    TServerTransport transport = new TServerSocket(port);//监听8800端口
    TServer server = new TThreadPoolServer(processor, transport);
    
    processor.RegisterProcessor("UserService", new UserService.Processor(new UserServiceImp()));
    processor.RegisterProcessor("HelloWorldService", new HelloWorldService.Processor(new HelloWorldServiceImp()));
    
    server.Serve();
    

4.编写Client

在这里插入图片描述
主要步骤:

  1. 就是定义好远程server地址
  2. 定义数据传输(通信)方式transport(一般都是远程socket通信),然后开放这个transport(端口/fd),以便数据写入写出
  3. 然后再定义Protocol,对这个fd中的数据按照protocol的协议(二进制、json等数据格式)进行解析
  4. 再定义好了Protocol之后,便相当于知道了需要传递的数据的格式,以及通信方式,以及远程地址,于是就可以以protocol为参数创建Client了,相当于这个Client中定义了数据序列化方式、通信方式、远程服务地址,所以就可以直接通过Client对象调用远程RPC方法了

5.源码分析

https://blog.csdn.net/qq_18297675/article/details/102512811
Processor是thrift的关键,它实际上是一个代理类,它封装了具体RPC方法的调用执行流程,数据解析协议Protocol。首先这里得提到processMap,它用于存储rpc方法名,和rpc方法的映射,key即为rpc方法名,value即为rpc方法抽象接口的实现类,这样通过查找processMap,即可得到RPC方法,然后调用RPC方法执行即可。

server:

public void serve() {
    try {
      //设置accept等待时间
      serverTransport_.listen();
    } catch (TTransportException ttx) {
      LOGGER.error("Error occurred during listening.", ttx);
      return;
    }

    //事件处理器,扩展用
    if (eventHandler_ != null) {
      eventHandler_.preServe();
    }

    setServing(true);

    //stopped_是一个volatile的,说明可以从任意线程停止服务器
    while (!stopped_) {
      TTransport client = null;
      TProcessor processor = null;
      TTransport inputTransport = null;
      TTransport outputTransport = null;
      TProtocol inputProtocol = null;
      TProtocol outputProtocol = null;
      ServerContext connectionContext = null;
      try {
        //客户端Socket
        client = serverTransport_.accept();
        if (client != null) {
          //获取服务端接口处理器,这个工厂什么也没做,直接返回了processor(client就是一个processor)
          processor = processorFactory_.getProcessor(client);
          //获取输入输出Transport,可以利用工厂进行包装,但是当前没做任何操作直接返回
          inputTransport = inputTransportFactory_.getTransport(client);
          outputTransport = outputTransportFactory_.getTransport(client);
          //获取输入输出Protocol,两个都有相同的Transport,只是new了两个Protocol
          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;
            }
          }
        }
      } 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);
  }

再看看TProcessor 接口的抽象类实现

public abstract class TBaseProcessor<I> implements TProcessor {
  private final I iface;
  private final Map<String,ProcessFunction<I, ? extends TBase>> processMap;

  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);
  }

  @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;
    }
    //这个process方法里面是真正处理接口请求调用的
    fn.process(msg.seqid, in, out, iface);
    return true;
  }
}

再看看ProcessFunction这个类:

public abstract class ProcessFunction<I, T extends TBase> {
  ……
  public final void process(int seqid, TProtocol iprot, TProtocol oprot, I iface) throws TException {
    //这个就是获取参数的实例,比如这次请求的接口是calculate(Expression exp)
    //那么就会返回Expression的包装类
    T args = getEmptyArgsInstance();
    try {
      //这个包装类里,有自己的write/read方法实现
      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;
    }
    //到这里接口调用参数就接收并解码完毕了,相当于将Expression获取到了
    iprot.readMessageEnd();
    TSerializable result = null;
    byte msgType = TMessageType.REPLY;

    try {
      //这里就相当于是调用本地实现,获取结果
      result = getResult(iface, args);
    } catch (TTransportException ex) {
      LOGGER.error("Transport error while processing " + getMethodName(), ex);
      throw ex;
    } catch (TApplicationException ex) {
      LOGGER.error("Internal application error processing " + getMethodName(), ex);
      result = ex;
      msgType = TMessageType.EXCEPTION;
    } catch (Exception ex) {
      LOGGER.error("Internal error processing " + getMethodName(), ex);
      if(!isOneway()) {
        result = new TApplicationException(TApplicationException.INTERNAL_ERROR,
            "Internal error processing " + getMethodName());
        msgType = TMessageType.EXCEPTION;
      }
    }
    //OneWay的意思是不需要回应Client的请求
    //所以!isOneway() 就是回应了呗
    if(!isOneway()) {
      oprot.writeMessageBegin(new TMessage(getMethodName(), msgType, seqid));
      //将结果发送给Client,result也有自己的read和write实现
      //例如这里的result就是ExpressionResult
      result.write(oprot);
      oprot.writeMessageEnd();
      oprot.getTransport().flush();
    }
  }

	……
  public abstract T getEmptyArgsInstance();
  public abstract TBase getResult(I iface, T args) throws TException;
  	……
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值