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 可以被复 ...
随机推荐
- linux 安全配置随笔
1. 禁止Ctrl+Alt+Del直接重启服务器 /bin/mv /etc/init/control-alt-delete.conf /etc/init/control-alt-delete.conf ...
- linux开机后第一个程序--init
背景知识 计算机启动基本流程如下 1)首先开机自检-POST, 2)自检通过后根据CMOS的配置找到第一个可启动磁盘的MBR中的boot loader程序(一般在启动磁盘的第一个物理扇区,占446字节 ...
- 关于<ul> 下的 <li> 里面的<a> 标签字体颜色不能控制
1.元展示 <ul class="ul"> <li><a href="#">菜单一</a></li> ...
- 2016级算法第四次上机-G.ModricWang的序列问题 II
1021 ModricWang的序列问题II 思路 此题与上一题区别不是很大,只是增加了一个长度限制,当场通过的人数就少了很多. 大体解题过程与上一题相同.区别在于对\(f[]\) 的操作.没有长度限 ...
- 动手玩转Docker(二)
CentOS7下安装docker: 通过命令uname -r 查看linux内核版本,版本低的话不支持docker. [tim@num root]$ uname -r 3.10.0-514.el7.x ...
- EXTJS文档地址
文档地址:http://docs.sencha.com/extjs/4.2.3/#!/api/Ext.form.field.Field-event-change
- centos 7 下安装nginx-1.15.7
安装所需环境 Nginx 是 C语言 开发,建议在 Linux 上运行,当然,也可以安装 Windows 版本,本篇则使用 CentOS 7 作为安装环境. 一. gcc 安装安装 nginx 需要先 ...
- win10操作系统系统,小米路由器,小米3 的问题
注意 , 置顶 单独一篇 : { win10 局域网共享 小米路由器,操作盘太卡 } 开发中用专业版 , 别用家庭版 比如有远程桌面程序 和 HV 虚拟机 查看激活信息 和 是不是永久激活 参考 h ...
- 【OpenCV-Python】-图像阀值
参考:Opencv官方教程 1.简单阀值 cv2.threshold , cv2.adaptiveThreshold当像素值高于阀值时,我们给这个像素赋予一个新值(可能是白色),否则我们给它赋予另外一 ...
- 3dsmax2019卸载/安装失败/如何彻底卸载清除干净3dsmax2019注册表和文件的方法
3dsmax2019提示安装未完成,某些产品无法安装该怎样解决呢?一些朋友在win7或者win10系统下安装3dsmax2019失败提示3dsmax2019安装未完成,某些产品无法安装,也有时候想重新 ...