说明:基于0.8.0版本
Thrift rpc只提供单个接口的模式,一个TServerSocket只能实例化一个Processor,实际上,当接口的模式再实际应用的过程中也许会比较少,不知道为什facebook要这么做,每开放一个服务接口都得占用一个端口,对于服务的管理也会带来不便。采用共用端口的模式可以解决端口问题,但是单个进程好像还是可以加载一个Processor,不知道是不是没找对方法。
为了实现加载多Processor需求,开始对thrift源码做相关的修改。
【服务端相关改造】
需要修改的类:
TSimpleServer(阻塞,简单模式,一般不怎么用)
TThreadPoolServer.WorkerProcess(阻塞、线程池模式)
AbstractNonblockingServer(非阻塞模式)
TBaseProcessor
TProcessorFactory
【客户端相关改造】
需要修改的类:
TServiceClient(阻塞)
TCompactProtocol(非阻塞)
【新增的类】
ClassScanner(类扫描器,加载processor)
Service(服务注解)
代码分析:
先看看服务端的启动的例子
TServerSocket serverTransport = new TServerSocket(8811);
ServiceDemo.Processor processor = new ServiceDemo.Processor(newServiceDemoImpl());
Factory protFactory = new TBinaryProtocol.Factory(true,true);
Args rpcArgs = new Args(serverTransport);
rpcArgs.processor(processor);
rpcArgs.protocolFactory(protFactory);
TServer server = new TThreadPoolServer(rpcArgs);
server.serve();
所有生成代码的Processor均继承TBaseProcessor,因此首先分析TBaseProcessor。
具体可看TBaseProcessor的属性private final I iface,这里的iface实例声明是final,同时生成的客户端代吗也可以看出。
privatestatic <Iextends 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("test",new test());
return processMap;
}
这里的processMap的key为方法名,在发送的时候,该key将会组装到TMessage的name中,并没有把类的相关信息带过去。这也是为啥thrift不支持方法重载的原因,应该是考虑到其他有些语言不支持重载的原因,比如C。
再看TProcessorFactory
public class TProcessorFactory {
private final TProcessor processor_;
public TProcessorFactory(TProcessor processor) {
processor_ = processor;
}
public TProcessor getProcessor(TTransport trans) {
return processor_;
}
}
这里的TProcessor也是final类型,服务器启动过程中,会调用该构造函数,实例化processor_属性,执行rpcArgs.processor(processor)的时候,会初始化ProcessorFactory,具体可以看AbstractServerArgs的方法
public Tprocessor(TProcessor processor) {
this.processorFactory = new TProcessorFactory(processor);
return (T) this;
}
看到这里,大概的思路就清楚了,主要有两点:
1、 把final类型的processor改为从初始化到容器中;
2、 客户端传输TMessage加入类的信息;
先看客户调用的修改,分阻塞和非阻塞两种模式
对于阻塞模式,只修改一个类TServiceClient
由于生成工具无法修改,因此无法从修改生成代码结构处理,只能从调用的库上做文章,实际上,所有Client的接口具有两步处理,发送-接收,均调用TServiceClient中的方法sendBase /receiveBase,只需要在sendBase中加入类信息到TMessage中即可,如下:
protected voidsendBase(String methodName, TBase args) throws TException {
// 处理调用接口
String className = "";
StackTraceElement stack[] = (new Throwable()).getStackTrace();
if (stack.length > 1) {
StackTraceElementste = stack[1];
className =ste.getFileName();
className =className.split("\\.")[0];
}
oprot_.writeMessageBegin(new TMessage(className+ "_" + methodName, TMessageType.CALL, ++seqid_));
args.write(oprot_);
oprot_.writeMessageEnd();
oprot_.getTransport().flush();
}
红色部分为获取类名,writeMessageBegin的时候加入即可,默认格式:类名_方法名;这里去的类名并非内部类Processor,而是生成的服务接口文件名,比如ServiceDemo,其内部结构如下:
同样,在服务端初始化Processor容器的时候也取改类文件名。
对于非阻塞模式,需要修改TCompactProtocol类,
同样的原理,修改
publicvoidwriteMessageBegin(TMessagemessage)throws TException {
//处理调用接口
String className = "";
StackTraceElement stack[] = (new Throwable()).getStackTrace();
if (stack.length > 1) {
StackTraceElement ste = stack[1];
className = ste.getFileName();
className = className.split("\\.")[0];
}
writeByteDirect(PROTOCOL_ID);
writeByteDirect((VERSION &VERSION_MASK) |((message.type <<TYPE_SHIFT_AMOUNT) &TYPE_MASK));
writeVarint32(message.seqid);
writeString(className + "_" + message.name); //加入类名,格式:类名_方法名
}
至此,第一步完成。
下面修改服务端处理
1、 修改TProcessorFactory
保持原来的内容,只新增容器,新增Processor存取方法,加入processor容器加载的处理,需要用到类扫描器(ClassScanner)和对应的服务注解类(org.apache.thrift.Service),后面会介绍初始化的处理方法,具体如下:
publicclass TProcessorFactory {
privatestatic Loggerlogger = Logger.getLogger(TProcessorFactory.class);
privatefinal TProcessorprocessor_;
//新增processor容器
privatestatic MapprocessorMap =new HashMap();
//新增function容器
privatestatic MapfunctionMap =new HashMap();
public TProcessorFactory(TProcessor processor) {
processor_ = processor;
}
public TProcessor getProcessor(TTransport trans) {
returnprocessor_;
}
/**
* @Title: addProcessor
* @Description:新增方法-加入processor到容器
* @param@param key
* @param@param processor
* @return void
*/
publicstaticvoid addProcessor(String key, Object processor) {
if (processorMap.containsKey(key)) {
return;
}
processorMap.put(key, processor);
logger.info("加载Processor:" +processor.getClass