Tomcat的Connector:Coyote

5月 20th, 2013 3,006 留下评论 阅读评论

上一篇博文讲了Tomcat的系统架构,今天花了一天时间研究了下coyote的源码,大致对普通IO版本有所了解。被同学提醒后发现,Tomcat6已经支持异步IO了,但默认是普通IO版本,需要在Connector的protocol参数中定义为Http11NioProtocol才开启异步IO模式。

主要的代码,Socket的服务在org.apache.tomcat.util.net.JIoEndpoint类,解析处理HTTP请求在org.apache.coyote.http11包的Http11Protocol类和Http11Processor类,org.apache.catalina.connector.CoyoteAdapter负责连接Connector模块(coyote)和Container模块。

线程池默认用的JIoEndpoint类中的子类WorkerStack,若在server.xml配置使用了Executor,则使用org.apache.catalina.core.StandardThreadExecutor(对ThreadPoolExecutor的一层包装)。并发量高的情况下Executor的效率会高一些。

Coyote普通IO流程

在Catalina主线程启动过程中:

StandardService的init()中,在JIoEndpoint.init()中,ServerSocket(port = 8080, backlog = 100)被new出来。

StandardService的start()中,在JIoEndpoint.start()中,一个新的线程(名叫http-8080-Acceptor-0)被start(),这个由JIoEndpoint.Accept内部类执行run()的线程干的活就是不停地从serverSocket.accept(),然后根据线程池的选择拿一个线程去处理socket。因为处理在新的线程,Acceptor线程继续等待accept()。

若使用默认的WorkerStack线程池,JioEndpoint.getWorkerThread().assign(socket);使Worker线程获得socket实例后,就可以在新线程中处理socket。

若使用Executor线程池,则new出一个JioEndpoint.SocketProcessor()交给Executor.execute(),在新线程中处理socket。

不管哪种线程池方法,新线程中使用一样的代码,调用Http11Protocol$Http11ConnectionHandler.process(socket),这里使用了一个ConcurrentLinkedQueue用来保存Http11Processor实例,拿一个进行处理。

Http11Protocol.process()具体解析HTTP请求。解析过程中,将参数塞进request和response,再调用CoyoteAdapter.service(request, response),这样就进入Container模块去调用Servlet代码。

结束后,回收Http11Processor实例,回收线程。

———————–流程结束分割线—————————

好像代码也没有太难看不懂,大多数复杂的线程同步操作都由java.util.concurrnt包里的类实现了。

Worker线程的assign()和await()

Acceptor线程调用Worker.assign( socket ),将socket对象赋给Worker线程,然后Worker线程中的await()方法通过线程同步,Worker线程被唤醒并拿到了socket就可以在自己线程里顺利执行了。

这块线程同步的代码蛮有意思的,而且Worker线程也借助这边的wait()和notifyall()实现挂起和唤醒。

server.xml参数调优

开启异步IO

Connector的配置中,将protocol=“HTTP/1.1”改为“org.apache.coyote.http11.Http11NioProtocol”就可以使用nio包的异步IO,效率高不少。

对java.nio包特别是异步IO这一块了解甚浅,而且Tomcat对这一块的实现略复杂,看懂略有难度,努力!

同样地方还能改为“org.apache.coyote.http11.Http11AprProtocol”,据说利用底层OS的异步IO,需要APR和Native这其他两个组件的支持才能工作,效率更高,不是太确定。

开启Executor并在Connector中使用

Executor的2个配置参数,minSpareThreads和maxThreads,对应到ThreadPoolExecutor(int corePoolSize,  int maximumPoolSize, …)这个构造的前面2个参数。分别表示活跃线程数和最大线程数。

Connector的参数:

  • maxThreads,对应JIoEndpoint类中的maxThreads变量,默认为200。若有Executor加入则maxThreads被取代。
  • acceptCount,对应JIoEndpoint类中的backlog变量,默认为100,用作ServerSocket的第二个参数。ServerSocket里等待队列的长度。
Categories: Java 标签:, , , , ,
  1. 还没有评论呢。