Thrift笔记(七)--回调源码分析
网上找了写代码,东拼西凑写了个demo。开始server用的是阻塞io,不行,换成非阻塞的io就可以。这里可能需要注意下
thrift文件
namespace java com.gxf.thrift
enum RequestType {
SAY_HELLO, //问好
QUERY_TIME, //询问时间
}
struct Request {
1: required RequestType type; // 请求的类型,必选
2: required string name; // 发起请求的人的名字,必选
3: optional i32 age; // 发起请求的人的年龄,可选
}
exception RequestException {
1: required i32 code;
2: optional string reason;
}
// 服务名
service HelloWordService {
string doAction(1: Request request) throws (1:RequestException qe); // 可能抛出异常。
}
Server接口实现
import org.apache.commons.lang3.StringUtils;
import java.util.Date;
public class HelloWordServiceImpl implements HelloWordService.Iface {
// 实现这个方法完成具体的逻辑。
public String doAction(Request request)
throws RequestException, org.apache.thrift.TException {
System.out.println("Get request: " + request);
if (StringUtils.isBlank(request.getName()) || request.getType() == null) {
throw new com.gxf.thrift.RequestException();
}
String result = "Hello, " + request.getName();
if (request.getType() == com.gxf.thrift.RequestType.SAY_HELLO) {
result += ", Welcome!";
} else {
result += ", Now is " + new Date().toLocaleString();
}
return result;
}
}
Server启动代码
import org.apache.thrift.TProcessor;
import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TServerSocket; import java.net.ServerSocket; public class HelloWordServer { public static void main(String[] args) throws Exception {
asynServer();
} private static void asynServer() throws Exception{
int port = 7912;
TNonblockingServerSocket socket = new TNonblockingServerSocket(port);
final HelloWordService.Processor processor = new HelloWordService.Processor(new HelloWordServiceImpl());
THsHaServer.Args arg = new THsHaServer.Args(socket);
// 高效率的、密集的二进制编码格式进行数据传输
// 使用非阻塞方式,按块的大小进行传输,类似于 Java 中的 NIO
arg.protocolFactory(new TCompactProtocol.Factory());
arg.transportFactory(new TFramedTransport.Factory());
arg.processorFactory(new TProcessorFactory(processor));
TServer server = new THsHaServer(arg);
server.serve();
} private static void sampleServer () throws Exception{
ServerSocket socket = new ServerSocket(7912);
TServerSocket serverTransport = new TServerSocket(socket);
// com.gxf.thrift.HelloWordService.Processor processor = new com.gxf.thrift.HelloWordService.Processor(
// new HelloWordServiceImpl());
// TServer server = new TSimpleServer(processor, serverTransport);
// System.out.println("Running server...");
// server.serve();
TBinaryProtocol.Factory proFactory = new TBinaryProtocol.Factory(); /**
* 关联处理器与GreetingService服务实现
*/
TProcessor processor = new HelloWordService.Processor(new HelloWordServiceImpl()); TThreadPoolServer.Args serverArgs = new TThreadPoolServer.Args(serverTransport);
serverArgs.processor(processor);
serverArgs.protocolFactory(proFactory);
TServer server = new TThreadPoolServer(serverArgs);
System.out.println("Start server on port 7912..."); server.serve();
} }
Callback实现类
import org.apache.thrift.async.AsyncMethodCallback;
public class ThriftCallback implements AsyncMethodCallback<String> {
@Override
public void onComplete(String response) {
System.out.println("onComplete");
System.out.println(response);
}
@Override
public void onError(Exception exception) {
System.out.println("onError");
exception.printStackTrace();
}
}
客户端测试代码
import org.apache.thrift.async.TAsyncClientManager;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.transport.TNonblockingSocket;
import org.apache.thrift.transport.TNonblockingTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport; public class HelloWordClient {
public static void main(String[] args) throws Exception {
asynCall();
} private static void sampleCall() throws Exception{
TTransport transport = new TSocket("127.0.0.1", 7912);
TProtocol protocol = new TBinaryProtocol(transport); // 创建client
com.gxf.thrift.HelloWordService.Client client = new com.gxf.thrift.HelloWordService.Client(protocol); transport.open(); // 建立连接 // 第一种请求类型
com.gxf.thrift.Request request = new com.gxf.thrift.Request()
.setType(com.gxf.thrift.RequestType.SAY_HELLO).setName("guanxianseng").setAge(28);
System.out.println(client.doAction(request)); // 第二种请求类型
request.setType(com.gxf.thrift.RequestType.QUERY_TIME).setName("guanxianseng");
System.out.println(client.doAction(request)); transport.close(); // 请求结束,断开连接
} private static void asynCall() throws Exception {
String address = "127.0.0.1";
int port = 7912;
int clientTimeout = 3000;
TAsyncClientManager clientManager = new TAsyncClientManager();
TNonblockingTransport transport = new TNonblockingSocket(address, port, clientTimeout);
TProtocolFactory protocol = new TCompactProtocol.Factory();
HelloWordService.AsyncClient asyncClient = new HelloWordService.AsyncClient(protocol, clientManager, transport); com.gxf.thrift.Request request = new com.gxf.thrift.Request()
.setType(com.gxf.thrift.RequestType.SAY_HELLO).setName("guanxianseng").setAge(28);
asyncClient.doAction(request, new ThriftCallback()); while(true){
Thread.sleep(1000);
}
} }
ok。上面是demo
跟进源码前,说下大概流程。client使用nio channel发送数据。将channel注册到selector中,监听对应的accept, read, write等事件。
主要分析客户端代码,跟进
TAsyncClientManager
public TAsyncClientManager() throws IOException {
this.selectThread = new SelectThread();
selectThread.start();
}
这里可以看出起了一个select线程,跟进这个线程实现类
private class SelectThread extends Thread {
private final Selector selector;
private volatile boolean running;
private final TreeSet<TAsyncMethodCall> timeoutWatchSet = new TreeSet<TAsyncMethodCall>(new TAsyncMethodCallTimeoutComparator());
public SelectThread() throws IOException {
this.selector = SelectorProvider.provider().openSelector();
this.running = true;
this.setName("TAsyncClientManager#SelectorThread " + this.getId());
// We don't want to hold up the JVM when shutting down
setDaemon(true);
}
可以看出这里有个Selector对象,后面的channel会注册进来。看下run方法
public void run() {
while (running) {
try {
try {
if (timeoutWatchSet.size() == 0) {
// No timeouts, so select indefinitely
selector.select();
} else {
// We have a timeout pending, so calculate the time until then and select appropriately
long nextTimeout = timeoutWatchSet.first().getTimeoutTimestamp();
long selectTime = nextTimeout - System.currentTimeMillis();
if (selectTime > 0) {
// Next timeout is in the future, select and wake up then
selector.select(selectTime);
} else {
// Next timeout is now or in past, select immediately so we can time out
selector.selectNow();
}
}
} catch (IOException e) {
LOGGER.error("Caught IOException in TAsyncClientManager!", e);
}
transitionMethods();
timeoutMethods();
startPendingMethods();
} catch (Exception exception) {
LOGGER.error("Ignoring uncaught exception in SelectThread", exception);
}
}
try {
selector.close();
} catch (IOException ex) {
LOGGER.warn("Could not close selector. This may result in leaked resources!", ex);
}
}
这里可以看出,客户单select注册的channel,接收服务端返回的结果。注意这里会阻塞到select()方法,也没有注册。后面会有一个wakeup()方法会结束阻塞,完成channel注册和select轮询
private static void asynCall() throws Exception {
String address = "127.0.0.1";
int port = 7912;
int clientTimeout = 3000;
TAsyncClientManager clientManager = new TAsyncClientManager();
TNonblockingTransport transport = new TNonblockingSocket(address, port, clientTimeout);
TProtocolFactory protocol = new TCompactProtocol.Factory();
HelloWordService.AsyncClient asyncClient = new HelloWordService.AsyncClient(protocol, clientManager, transport);
com.gxf.thrift.Request request = new com.gxf.thrift.Request()
.setType(com.gxf.thrift.RequestType.SAY_HELLO).setName("guanxianseng").setAge(28);
asyncClient.doAction(request, new ThriftCallback());
while(true){
Thread.sleep(1000);
}
}
中间部分代码,是一些注册,常用的观察者模式的初始化。主要看下doAction(),即调用服务端rpc
public void doAction(Request request, org.apache.thrift.async.AsyncMethodCallback<String> resultHandler) throws org.apache.thrift.TException {
checkReady();
doAction_call method_call = new doAction_call(request, resultHandler, this, ___protocolFactory, ___transport);
this.___currentMethod = method_call;
___manager.call(method_call);
}
跟进call方法
public void call(TAsyncMethodCall method) throws TException {
if (!isRunning()) {
throw new TException("SelectThread is not running");
}
method.prepareMethodCall();
pendingCalls.add(method);
selectThread.getSelector().wakeup();
}
这里有个wakeup()方法,会中断前面selector线程的select()阻塞。回到前面的阻塞代码,即run方法
public void run() {
while (running) {
try {
try {
if (timeoutWatchSet.size() == 0) {
// No timeouts, so select indefinitely
selector.select();
} else {
// We have a timeout pending, so calculate the time until then and select appropriately
long nextTimeout = timeoutWatchSet.first().getTimeoutTimestamp();
long selectTime = nextTimeout - System.currentTimeMillis();
if (selectTime > 0) {
// Next timeout is in the future, select and wake up then
selector.select(selectTime);
} else {
// Next timeout is now or in past, select immediately so we can time out
selector.selectNow();
}
}
} catch (IOException e) {
LOGGER.error("Caught IOException in TAsyncClientManager!", e);
}
transitionMethods();
timeoutMethods();
startPendingMethods();
} catch (Exception exception) {
LOGGER.error("Ignoring uncaught exception in SelectThread", exception);
}
}
跟进 transitionMethods()方法
// Transition methods for ready keys
private void transitionMethods() {
try {
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (!key.isValid()) {
// this can happen if the method call experienced an error and the
// key was cancelled. can also happen if we timeout a method, which
// results in a channel close.
// just skip
continue;
}
TAsyncMethodCall methodCall = (TAsyncMethodCall)key.attachment();
methodCall.transition(key); // If done or error occurred, remove from timeout watch set
if (methodCall.isFinished() || methodCall.getClient().hasError()) {
timeoutWatchSet.remove(methodCall);
}
}
} catch (ClosedSelectorException e) {
LOGGER.error("Caught ClosedSelectorException in TAsyncClientManager!", e);
}
}
这里主要是处理注册channel返回的key.回到前面run,跟进下面方法
// Start any new calls
private void startPendingMethods() {
TAsyncMethodCall methodCall;
while ((methodCall = pendingCalls.poll()) != null) {
// Catch registration errors. method will catch transition errors and cleanup.
try {
methodCall.start(selector); // If timeout specified and first transition went smoothly, add to timeout watch set
TAsyncClient client = methodCall.getClient();
if (client.hasTimeout() && !client.hasError()) {
timeoutWatchSet.add(methodCall);
}
} catch (Exception exception) {
LOGGER.warn("Caught exception in TAsyncClientManager!", exception);
methodCall.onError(exception);
}
}
}
}
跟进start方法
/**
* Register with selector and start first state, which could be either connecting or writing.
* @throws IOException if register or starting fails
*/
void start(Selector sel) throws IOException {
SelectionKey key;
if (transport.isOpen()) {
state = State.WRITING_REQUEST_SIZE;
key = transport.registerSelector(sel, SelectionKey.OP_WRITE);
} else {
state = State.CONNECTING;
key = transport.registerSelector(sel, SelectionKey.OP_CONNECT); // non-blocking connect can complete immediately,
// in which case we should not expect the OP_CONNECT
if (transport.startConnect()) {
registerForFirstWrite(key);
}
} key.attach(this);
}
这里可以看到channel在selector中的注册
Thrift笔记(七)--回调源码分析的更多相关文章
- memcached学习笔记——存储命令源码分析下篇
上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制 ...
- memcached学习笔记——存储命令源码分析上篇
原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...
- tornado 学习笔记6 Application 源码分析
Application 是Tornado重要的模块之一,主要是配置访问路由表及其他应用参数的设置. 源代码位于虚拟运行环境文件夹下(我的是env),具体位置为env > lib>sit-p ...
- OA学习笔记-010-Struts部分源码分析、Intercepter、ModelDriver、OGNL、EL
一.分析 二. 1.OGNL 在访问action前,要经过各种intercepter,其中ParameterFilterInterceptor会把各咱参数放到ValueStack里,从而使OGNL可以 ...
- 正式学习React (七) react-router 源码分析
学习react已经有10来天了,对于react redux react-redux 的使用流程和原理,也已经有一定的了解,在我上一篇的实战项目里,我用到了react-route,其实对它还只是 停留在 ...
- Java并发编程笔记之FutureTask源码分析
FutureTask可用于异步获取执行结果或取消执行任务的场景.通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过Fu ...
- Java并发编程笔记之SimpleDateFormat源码分析
SimpleDateFormat 是 Java 提供的一个格式化和解析日期的工具类,日常开发中应该经常会用到,但是由于它是线程不安全的,多线程公用一个 SimpleDateFormat 实例对日期进行 ...
- Java并发编程笔记之Timer源码分析
timer在JDK里面,是很早的一个API了.具有延时的,并具有周期性的任务,在newScheduledThreadPool出来之前我们一般会用Timer和TimerTask来做,但是Timer存在一 ...
- Java并发编程笔记之CyclicBarrier源码分析
JUC 中 回环屏障 CyclicBarrier 的使用与分析,它也可以实现像 CountDownLatch 一样让一组线程全部到达一个状态后再全部同时执行,但是 CyclicBarrier 可以被复 ...
随机推荐
- JS关闭页面弹窗提醒
<html> <head><title>JS测试</title> <script type="text/javascript" ...
- 模拟实现STL库
最近在复习STL,感觉再看的时候比刚开始学的时候通透很多.以前模拟实现了一个STL库,最近复习完又重构了一遍.代码放出来以供后面学习.如果有写的不好的地方欢迎大家批评指正. STL_List.h #p ...
- CSS3过渡效果 兼容IE6、IE7、IE8
<style> .box{ width:120px;height:40px;background:yellowgreen;line-height:40px;transition:width ...
- [集合]Collection集合框架源码分析
Collection接口 在java的集合类库中,基本接口是Collection,该接口的在集合中的源码定义如下(将源码中的注释删掉了): public interface Collection< ...
- 理解webpack中的publicPath
outPut中的publicPath 默认值: 空字符串. publicPath是非常有必要配置的,他是项目中引入静态资源(js.css)时的基础路径. 例如: outPut.publicPath = ...
- 浅谈Object.create
在网上发现了Object.create的用法,感觉很是奇怪,所以学习记录下 var o = Object.create(null); console.log(o); // {} o.name = 'j ...
- python库安装如:requests,selenium等
安装方式: 1.pip安装: 如:pip install requests 2.wheel安装: 在PyPI上下载对应的wheel文件:如要下载requests的wheel文件,打开:http://p ...
- 实验三敏捷开发与XP实践《Java开发环境的熟悉》实验报告
一.实验要求 1.没有Linux基础的同学建议先学习<Linux基础入门(新版)><Vim编辑器> 课程 2.完成实验.撰写实验报告,实验报告以博客方式发表在博客园,注意实验报 ...
- session和cookie的知识总结
1.HTTP协议 由HTTP客户端发起一个请求,建立一个到服务器指定端口(默认是80端口)的TCP连接.HTTP服务器则在那个端口监听客户端发送过来的请求.一旦收到请求,服务器(向客户端)发回一个状态 ...
- (二)Audio子系统之new AudioRecord()(Android4.4)
在上一篇文章<(一)Audio子系统之AudioRecord.getMinBufferSize>中已经介绍了AudioRecord如何获取最小缓冲区大小,接下来,继续分析AudioReco ...