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