Thrift源码系列----6.TThreadedSelectorServer源码实现

来源:互联网 发布:nginx反向代理什么意思 编辑:程序博客网 时间:2024/06/16 06:45

前言

        在上一章我们介绍了Thrift非阻塞型服务的父类AbstractNonblockingServer,因为内部的逻辑较复杂,且源码过多,所以主要以贴源码、添加注解的形式讲解。而本章在探索TThreadedSelectorServer源码时,我们按照AbstractNonblockingServer的serve()方法中的调用顺序来梳理整个过程。

回顾

        研究源码前,我们先回顾下TThreadedSelectorServer的模型图,并对每个模块的功能加以说明,从而更易理解源码。


这里写图片描述


        可以看到,整个服务框架分为以下几个部分:

  1. 一个AcceptThread线程对象,专门用于处理监听socket上的新连接。
  2. 若干个SelectorThread对象专门用于处理业务socket的网络I/O操作,所有网络数据的读写均是由这些线程来完成。
  3. 一个负载均衡器SelectorThreadLoadBalancer对象,主要用于AcceptThread线程接收到一个新socket连接请求时,决定将这个新连接请求分配给哪个SelectorThread线程。
  4. 一个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的关系,整个服务的运行流程也就非常容易理解,对于细节大家参考源码中的注释即可。

1 0
原创粉丝点击