Thrift源码系列----6.TThreadedSelectorServer源码实现
来源:互联网 发布:nginx反向代理什么意思 编辑:程序博客网 时间:2024/06/16 06:45
前言
在上一章我们介绍了Thrift非阻塞型服务的父类AbstractNonblockingServer,因为内部的逻辑较复杂,且源码过多,所以主要以贴源码、添加注解的形式讲解。而本章在探索TThreadedSelectorServer源码时,我们按照AbstractNonblockingServer的serve()方法中的调用顺序来梳理整个过程。
回顾
研究源码前,我们先回顾下TThreadedSelectorServer的模型图,并对每个模块的功能加以说明,从而更易理解源码。
可以看到,整个服务框架分为以下几个部分:
- 一个AcceptThread线程对象,专门用于处理监听socket上的新连接。
- 若干个SelectorThread对象专门用于处理业务socket的网络I/O操作,所有网络数据的读写均是由这些线程来完成。
- 一个负载均衡器SelectorThreadLoadBalancer对象,主要用于AcceptThread线程接收到一个新socket连接请求时,决定将这个新连接请求分配给哪个SelectorThread线程。
- 一个ExecutorService类型的工作线程池,在SelectorThread线程中,监听到有业务socket中有调用请求过来,则将请求读取之后,交个ExecutorService线程池中的线程完成此次调用的具体执行。
TThreadedSelectorServer源码
TThreadedSelectorServer.Args
首先还是先看看Args类的变化 :
public static class Args extends AbstractNonblockingServerArgs<Args> { public int selectorThreads = 2;//用于读写IO操作的线程数 private int workerThreads = 5;//执行业务的线程池的线程数 private int stopTimeoutVal = 60;//等待服务停止的检查间格 private TimeUnit stopTimeoutUnit = TimeUnit.SECONDS;//线程池参数 private ExecutorService executorService = null;//执行业务的线程池 private int acceptQueueSizePerThread = 4;//读写IO线程接收请求的队列大小 //接收 客户端新请求的策略类型 public static enum AcceptPolicy { //已接收的连接请求,需要在线程池中注册,如果线程池已经满了,将立即关闭连接,由于调度将会稍微增加延迟 FAIR_ACCEPT, //忽略线程池的状态,尽可能快的处理 客户端连接 FAST_ACCEPT } private AcceptPolicy acceptPolicy = AcceptPolicy.FAST_ACCEPT;//默认使用快速模式 public Args(TNonblockingServerTransport transport) { super(transport); } //对线程池、读写IO的参数进行校验 public void validate() { if (selectorThreads <= 0) { throw new IllegalArgumentException("selectorThreads must be positive."); } if (workerThreads < 0) { throw new IllegalArgumentException("workerThreads must be non-negative."); } if (acceptQueueSizePerThread <= 0) { throw new IllegalArgumentException("acceptQueueSizePerThread must be positive."); } } //省略get set方法 }
TThreadedSelectorServer的变量
了解了Args参数后,我们需要清楚TThreadedSelectorServer的成员变量与构造方法,方法的作用这里我们不列出来,调用时再讲解。
private AcceptThread acceptThread;//用于接收客户端连接的线程 private final Set<SelectorThread> selectorThreads = new HashSet<SelectorThread>();//处理IO读写操作的线程集合 private final ExecutorService invoker;//线程池两个功能1.将accept的连接添加给SelectorThread 2.执行业务 private final Args args;参数对象, //构造方法中不多讲,如果没有传入线程池,会创建默认线程池,有兴趣的朋友自己看createDefaultExecutor的实现 public TThreadedSelectorServer(Args args) { super(args); args.validate(); invoker = args.executorService == null ? createDefaultExecutor(args) : args.executorService; this.args = args; }
在介绍了Args和TThreadedSelectorServer的参数和作用后,我们来开始研究一个服务是如何启动的。
AbstractNonblockingServer.Serve()
这里还是先回顾下,我们启动服务调用的serve()方法内部是如何调用的。
public void serve() { //启动IO线程 if (!startThreads()) { return; } //开始监听端口,接收客户端请求 if (!startListening()) { return; } //修改服务状态为正在服务中 setServing(true); //启动服务后的阻塞方法,服务停止后通过 waitForShutdown(); //修改服务状态为结束服务 setServing(false); //停止监听端口 stopListening(); }
TThreadedSelectorServer.startThreads
源码如下:
protected boolean startThreads() { try { //根据读写IO线程数的设置,创建相应的读写IO线程放入集合 for (int i = 0; i < args.selectorThreads; ++i) { selectorThreads.add(new SelectorThread(args.acceptQueueSizePerThread)); } //根据serverTransport创建监控线程,来接收客户端的请求,createSelectorThreadLoadBalancer方法这里不算过多讲解,详情见AcceptThread acceptThread = new AcceptThread((TNonblockingServerTransport) serverTransport_, createSelectorThreadLoadBalancer(selectorThreads)); //启动所有的读写IO线程 for (SelectorThread thread : selectorThreads) { thread.start(); } //启动监控线程 acceptThread.start(); return true; } catch (IOException e) { LOGGER.error("Failed to start threads!"&e); return false; } }
看完源码,我们进一步研究AcceptThread和SelectorThread的功能与联系。
AcceptThread与SelectorThread
protected class AcceptThread extends Thread { private final TNonblockingServerTransport serverTransport;//监听端口的Socket private final Selector acceptSelector;//Java Nio selector private final SelectorThreadLoadBalancer threadChooser;//该类内部拥有一个selectorThreads集合的引用,主要就是每次可以从中获取一个SelectorThread对象 public AcceptThread(TNonblockingServerTransport serverTransport, SelectorThreadLoadBalancer threadChooser) throws IOException { this.serverTransport = serverTransport; this.threadChooser = threadChooser; this.acceptSelector = SelectorProvider.provider().openSelector(); this.serverTransport.registerSelector(acceptSelector);//会将serverTransport中的serverSocketChannel对象注册到Selector中 } //线程运行方法 public void run() { try { //忽略 if (eventHandler_ != null) { eventHandler_.preServe(); } //服务未停止时,不断调用select() while (!stopped_) { select(); } } catch (Throwable t) { LOGGER.error("run() on AcceptThread exiting due to uncaught error", t); } finally { try { acceptSelector.close();//关闭selector } catch (IOException e) { LOGGER.error("Got an IOException while closing accept selector!", e); } TThreadedSelectorServer.this.stop();//这一步是为了唤醒SelectorThreads中的读写IO线程 } } //唤醒selector public void wakeupSelector() { acceptSelector.wakeup(); } //不断监测客户端请求连接事件,并处理请求 private void select() { try { //等待请求事件 acceptSelector.select(); //执行接收到的事件 Iterator<SelectionKey> selectedKeys = acceptSelector.selectedKeys().iterator(); while (!stopped_ && selectedKeys.hasNext()) { SelectionKey key = selectedKeys.next(); selectedKeys.remove(); //非法的忽略 if (!key.isValid()) { continue; } //处理连接请求 if (key.isAcceptable()) { handleAccept(); } else { LOGGER.warn("Unexpected state in select! " + key.interestOps()); } } } catch (IOException e) { LOGGER.warn("Got an IOException while selecting!", e); } } //接收一个连接 private void handleAccept() { final TNonblockingTransport client = doAccept();//获取连接 if (client != null) { // Pass this connection to a selector thread final SelectorThread targetThread = threadChooser.nextThread();//选出一个SelectThread线程 //FAST_ACCEPT或无线程池时,直接将连接添加给SelectThread if (args.acceptPolicy == Args.AcceptPolicy.FAST_ACCEPT || invoker == null) { doAddAccept(targetThread, client); } else { // FAIR_ACCEPT模式下将连接作为一个任务提交给线程池,稍后处理 try { invoker.submit(new Runnable() { public void run() { doAddAccept(targetThread, client); } }); } catch (RejectedExecutionException rx) { LOGGER.warn("ExecutorService rejected accept registration!", rx); client.close(); } } } } //从serverTransport中接收一个请求 private TNonblockingTransport doAccept() { try { return (TNonblockingTransport) serverTransport.accept(); } catch (TTransportException tte) { // something went wrong accepting. LOGGER.warn("Exception trying to accept!", tte); return null; } } //将新连接添加到SelectorThread的队列中 private void doAddAccept(SelectorThread thread, TNonblockingTransport client) { if (!thread.addAcceptedConnection(client)) { client.close(); } } }
SelectorThread是继承了AbstractSelectThread,所以AbstractSelectThread的方法可调用,忘记AbstractSelectThread方法作用的同学请回顾上一篇文章。
protected class SelectorThread extends AbstractSelectThread { private final BlockingQueue<TNonblockingTransport> acceptedQueue;//用于保存AcceptThread传过来的 连接 public SelectorThread() throws IOException { this(new LinkedBlockingQueue<TNonblockingTransport>()); } public SelectorThread(int maxPendingAccepts) throws IOException { this(createDefaultAcceptQueue(maxPendingAccepts)); } public SelectorThread(BlockingQueue<TNonblockingTransport> acceptedQueue) throws IOException { this.acceptedQueue = acceptedQueue; } //将连接添加到队列中 public boolean addAcceptedConnection(TNonblockingTransport accepted) { try { acceptedQueue.put(accepted); } catch (InterruptedException e) { LOGGER.warn("Interrupted while adding accepted connection!", e); return false; } selector.wakeup();//selector唤醒,这步很重要,因为selectorThread会阻塞在select()方法,而一开始就没有任务时会永远阻塞,所以这里要唤醒 return true; } //工作循环,处理 IO读写事件,管理连接 public void run() { try { //服务未停止时无限循环 while (!stopped_) { select();//处理IO读写 processAcceptedConnections();//处理队列中的新连接 processInterestChanges();//处理现有连接 注册的事件修改请求 } //关闭时的清理工作 for (SelectionKey selectionKey : selector.keys()) { cleanupSelectionKey(selectionKey); } } catch (Throwable t) { LOGGER.error("run() on SelectorThread exiting due to uncaught error", t); } finally { try { selector.close(); } catch (IOException e) { LOGGER.error("Got an IOException while closing selector!", e); } TThreadedSelectorServer.this.stop();//唤醒其他accept thread 和 selector threads } } //处理IO事件 private void select() { try { //等待IO事件 selector.select(); //处理接收到的IO事件 Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator(); while (!stopped_ && selectedKeys.hasNext()) { SelectionKey key = selectedKeys.next(); selectedKeys.remove(); //非法key忽略 if (!key.isValid()) { cleanupSelectionKey(key); continue; } if (key.isReadable()) { handleRead(key);//处理读事件,父类实现的方法 } else if (key.isWritable()) { handleWrite(key);//处理写事件,父类实现的方法 } else { LOGGER.warn("Unexpected state in select! " + key.interestOps()); } } } catch (IOException e) { LOGGER.warn("Got an IOException while selecting!", e); } } //处理队列中的连接请求,将他们注册到本地selector中 private void processAcceptedConnections() { // Register accepted connections while (!stopped_) { TNonblockingTransport accepted = acceptedQueue.poll(); if (accepted == null) { break; } registerAccepted(accepted); } } //将一个连接封装为一个FrameBuffer protected FrameBuffer createFrameBuffer(final TNonblockingTransport trans, final SelectionKey selectionKey, final AbstractSelectThread selectThread) { return processorFactory_.isAsyncProcessor() ? new AsyncFrameBuffer(trans, selectionKey, selectThread) : new FrameBuffer(trans, selectionKey, selectThread); } //将新连接注册到selector中,并设置为读事件,同时创建一个FrameBuffer与SelectionKey进行绑定 private void registerAccepted(TNonblockingTransport accepted) { SelectionKey clientKey = null; try { clientKey = accepted.registerSelector(selector, SelectionKey.OP_READ); FrameBuffer frameBuffer = createFrameBuffer(accepted, clientKey, SelectorThread.this); clientKey.attach(frameBuffer); } catch (IOException e) { //清理工作 LOGGER.warn("Failed to register accepted connection to selector!", e); if (clientKey != null) { cleanupSelectionKey(clientKey); } accepted.close(); } } }
看完代码相信大家还是一头雾水,照旧,画一幅方法调用图,来理清AcceptThread与SelectorThread的关系。
结合序号我们按一次请求的完整处理流程来讲解:
1.AcceptThread 开始运行,发现有新请求时,进入2步骤。
2.获取到新连接,选取一个SelectorThread,并调用doAddAccept方法(备注:这一步可能由线程池来执行doAddAccept,默认模式下由AcceptThread调用该方法)
3.直接调用addAcceptedConnection方法。
4.将新连接加入到SelectorThread的队列中。
5.SelectorThread开始第一次运行。
6.处理队列中的连接,具体的处理方式调用registerAccepted方法。
7.将新连接注册到当前SelectorThread线程的Selector中,并封装为FrameBuffer绑定到注册好的SelectorKey上。
8.SelectorThread第二次运行,发现新连接有数据要读(这里假设一次读完了,然后线程池也将业务执行完毕了,向SelectorThread注册了将由read向write转换的事件)。
9.第三次运行发现,有事件修改需要处理,则将读通道转为写通道。
10.第四次运行发现 ,有写事件要处理,这时将结果返回给客户端。处理完后,再次注册事件修改,可能再次转为读事件(客户端长连接),也可能断掉(客户端短连接)
11.第五次再处理10中的事件修改。
至此,理清了AcceptThread与SelectorThread的关系后,startThreads()方法也就很容易理解了,serve()中的方法都已在上节介绍过,只有waitForShutdown()是子类实现,下节看该方法的源码;
TThreadedSelectorServer.waitForShutdown
@Override protected void waitForShutdown() { try { joinThreads(); } catch (InterruptedException e) { // Non-graceful shutdown occurred LOGGER.error("Interrupted while joining threads!", e); } gracefullyShutdownInvokerPool();//处理线程池关闭操作,不详述 } //等待AcceptThread和SelectorThread都停止运行 protected void joinThreads() throws InterruptedException { acceptThread.join();//阻塞到这一步直到AcceptThread的无限循环跳出 for (SelectorThread thread : selectorThreads) { thread.join();//阻塞到这一步直到SelectorThread的无限循环跳出 } }
还是比较容易理解的,就是起到一个阻塞的作用,直到AcceptThread、SelectorThread线程执行完毕。至次整个启动的过程就讲解完毕了,下面需要介绍几个其他地方用到的方法。
TThreadedSelectorServer.stop
这个方法是AcceptThead、SelectorThread中任意一个在运行时抛出异常时调用,这个时候认为服务不可用,所以调用该方法。
@Override public void stop() { stopped_ = true;//停止服务,其他AcceptThead、SelectorThread线程读到时跳出run方法 stopListening(); //停止接收新请求 if (acceptThread != null) { acceptThread.wakeupSelector();//可能acceptThread处于阻塞中 } if (selectorThreads != null) { for (SelectorThread thread : selectorThreads) { if (thread != null) thread.wakeupSelector();//可能SelectorThread处于阻塞中 } } }
TThreadedSelectorServer.requestInvoke
该方法是AbstractNonblockingServer中的抽象方法,在handleRead方法中读完数据时调用,来执行业务处理。
//将frameBuffer封装成Runnable对象扔进线程池,这里较易看懂,不详述protected boolean requestInvoke(FrameBuffer frameBuffer) { Runnable invocation = getRunnable(frameBuffer); if (invoker != null) { try { invoker.execute(invocation); return true; } catch (RejectedExecutionException rx) { LOGGER.warn("ExecutorService rejected execution!", rx); return false; } } else { // Invoke on the caller's thread invocation.run(); return true; } } protected Runnable getRunnable(FrameBuffer frameBuffer) { return new Invocation(frameBuffer); } class Invocation implements Runnable { private final FrameBuffer frameBuffer; public Invocation(final FrameBuffer frameBuffer) { this.frameBuffer = frameBuffer; } public void run() { frameBuffer.invoke(); }}
总结
本章至此TThreadedSelectorServer的源码介绍完毕,其中理解了AcceptThread与SelectThread的关系,整个服务的运行流程也就非常容易理解,对于细节大家参考源码中的注释即可。
- Thrift源码系列----6.TThreadedSelectorServer源码实现
- TThreadedSelectorServer模式源码分析
- Thrift源码系列----5.AbstractNonblockingServer源码
- Thrift源码系列----1.服务端类体系
- Thrift源码系列----3.TProtocol层功能分析
- Thrift源码系列----2.TTransport层源码分析
- Thrift源码系列----4.数据的解析与发送、接收
- Thrift 源码修改
- Thrift源码剖析
- Thrift源码分析(九)-- 扩展Thrift框架来实现Attachable的RPC调用
- Thrift for Java 源码解析
- Thrift之Protocol源码分析
- Thrift 多线程半同步半异步的服务模型-TThreadedSelectorServer
- java thrift TSimpleServer TNonblockingServer THsHaServer TThreadedSelectorServer TThreadPoolServer比较
- 聊聊Thrift(二) thrift java lib源码编译
- jquery源码系列:append方法实现过程
- thrift之compiler源码详解-1
- thrift之compiler源码详解-2
- lintcode,快乐数
- gradle执行脚本报错unsupported major.minor version 52.0
- BZOJ 3295 [CQOI2011] 动态逆序对
- Java总结篇系列:Java泛型
- HTML5中表单验证的8种方法
- Thrift源码系列----6.TThreadedSelectorServer源码实现
- HIHOCODER 1329 stl
- SpringMVC注记式验证学习笔记——指定验证出错提示信息资源文件及其编码
- html+selenium+webdriver+java,一个前端自动化Demo
- 022 Generate Parentheses
- 使用Messenger
- jdk7 Collections.sort()方法报错分析
- 1107. Social Clusters (并查集 )
- 小博老师解析Java经典面试题 ——常见Http状态号