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进行解码
,并执行相应的服务程序
-
单一服务接口
//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();
-
多服务接口
//定义多服务接口,此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
主要步骤:
- 就是定义好远程server地址
- 定义数据传输(通信)方式transport(一般都是远程socket通信),然后开放这个transport(端口/fd),以便数据写入写出
- 然后再定义Protocol,对这个fd中的数据按照protocol的协议(二进制、json等数据格式)进行解析
- 再定义好了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;
……
}