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