Tomcat学习之Acceptor

来源:互联网 发布:个人可以注册cn域名吗 编辑:程序博客网 时间:2024/05/19 05:33

http://blog.csdn.net/aesop_wubo/article/details/7627772

简介

Acceptor顾名思义就是接收器,用于接收用户请求,这节主要是分析Acceptor的启动和处理请求!

首先来看Acceptor的类图


从图中可以看出Acceptor实现了Runnable接口,可以作为一个线程启动,且都是Endpoint的内部类。Acceptor有3种实现JIo、Apr、Nio本节不会介绍他们之间的区别,常用的是JIoEndpoint.Acceptor,后面的例子,如果没有做特别说明指的是JIoEndpoint类里面的接收器。


启动


在tomcat启动的时候会启动Endpoint,会调用它的startInternal方法,来看看这个方法做了哪些事:

[java] view plain copy
 print?
  1. public void startInternal() throws Exception {  
  2.   
  3.     if (!running) {  
  4.         running = true;  
  5.         paused = false;  
  6.   
  7.         // Create worker collection  
  8.         if (getExecutor() == null) {  
  9.             createExecutor();  
  10.         }  
  11.   
  12.         initializeConnectionLatch();  
  13.   
  14.         startAcceptorThreads();  
  15.   
  16.         // Start async timeout thread  
  17.         Thread timeoutThread = new Thread(new AsyncTimeout(),  
  18.                 getName() + "-AsyncTimeout");  
  19.         timeoutThread.setPriority(threadPriority);  
  20.         timeoutThread.setDaemon(true);  
  21.         timeoutThread.start();  
  22.     }  
  23. }  
1、初始化一个闭锁,这个闭锁用于控制连接的,每释放一个连接,闭锁就调用countDown方法一次。getMaxConnections方法返回10000,也就是默认连接数为10000

[java] view plain copy
 print?
  1. protected LimitLatch initializeConnectionLatch() {  
  2.     if (maxConnections==-1return null;  
  3.     if (connectionLimitLatch==null) {  
  4.         connectionLimitLatch = new LimitLatch(getMaxConnections());  
  5.     }  
  6.     return connectionLimitLatch;  
  7. }  

2、启动带有接收器的线程

[java] view plain copy
 print?
  1. protected final void startAcceptorThreads() {  
  2.     int count = getAcceptorThreadCount();  
  3.     acceptors = new Acceptor[count];  
  4.   
  5.     for (int i = 0; i < count; i++) {  
  6.         acceptors[i] = createAcceptor();  
  7.         Thread t = new Thread(acceptors[i], getName() + "-Acceptor-" + i);  
  8.         t.setPriority(getAcceptorThreadPriority());  
  9.         t.setDaemon(getDaemon());  
  10.         t.start();  
  11.     }  
  12. }  

(1)getAcceptorThreadCount:在endpoint初始化时,调用AbstractEndpoint.init方法,紧接着调用bind方法,实现是在子类实现的,看看JIOEndpoint类的

bind方法

[java] view plain copy
 print?
  1.   public void bind() throws Exception {  
  2.   
  3.         // Initialize thread count defaults for acceptor  
  4.         if (acceptorThreadCount == 0) {  
  5.             acceptorThreadCount = 1;  
  6.         }  
  7. ......  
  8. }  
所以在基于JIO的实现方式getAcceptorThreadCount默认为1,也就是默认只有一个线程上有接收器

(2)然后创建count个线程,每个线程上都带有一个接收器,并把这些线程设置为后台线程,最后启动这些线程


处理请求

下面看看启动这些线程后,这些线程做了哪些事?

由于org.apache.tomcat.util.net.AbstractEndpoint.Acceptor实现了runnable接口,启动这些线程,也就是运行

org.apache.tomcat.util.net.AbstractEndpoint.Acceptor实现类的run方法,还是以JIO的实现方式为例来看,以下代码省略了很多异常处理

[java] view plain copy
 print?
  1. public void run() {  
  2.     while (running) {  
  3.         while (paused && running) {  
  4.             Thread.sleep(50);  
  5.         }  
  6.   
  7.                 countUpOrAwaitConnection();  
  8.   
  9.         socket = serverSocketFactory.acceptSocket(serverSocket);  
  10.   
  11.         if (running && !paused && setSocketOptions(socket)) {  
  12.             if (!processSocket(socket)) {  
  13.                 countDownConnection();  
  14.                ......  
  15.             }  
  16.         } else {  
  17.             countDownConnection();  
  18.         }  
  19.     }   
  20. }  

1、整体来看是一个while循环,只要服务器没有被关闭,这个while循环会一直运行,当没有请求到来时,这些线程就会sleep

2、获取一个接连,底层调用的是AQS的sync.acquireSharedInterruptibly(1)方法,有可能需要阻塞

3、如果有请求到来就会调用serverSocketFactory.acceptSocket方法,该方法不细讲,底层调用的就是socket.accept()方法

4、方法调用成功就会分享一个线程来处理这次请求,处理请求的代码在processSocket方法中完成,后面会细讲这个方法

5、如果在处理的过程中或者在acceptSocket的过程中有异常都会调用countDownConnection方法减少连接数,如果socket还打开就关闭socket


参考资料

Tomcat性能调校之连接器模块JIO、APR和NIO

0 0
原创粉丝点击