剑指架构师系列-tomcat6通过伪异步实现connector
首先在StandardService中start接收请求的线程,如下:
synchronized (connectors) { for (int i = 0; i < connectors.length; i++) { try { ((Lifecycle) connectors[i]).start(); } catch (Exception e) { log.error(sm.getString("standardService.connector.startFailed",connectors[i]), e); } } }
然后进入Connector,在这个类中调用了org.apache.coyote.http11.Http11Protocol类
protocolHandler.start();
在Http11Protocol类中又调用了org.apache.tomcat.util.net.JIoEndpoint类
endpoint.start();
下面看一下JIoEndpoint类中的start源代码,如下:
public void start() throws Exception { // Initialize socket if not done before if (!initialized) { init(); } if (!running) { running = true; paused = false; // Create worker collection if (executor == null) { workers = new WorkerStack(maxThreads); // maxThreads值为200,可同时处理200个请求 } // Start acceptor threads for (int i = 0; i < acceptorThreadCount; i++) { // acceptorThreadCount值为1,只有一个接收请求的线程 Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i); acceptorThread.setPriority(threadPriority); acceptorThread.setDaemon(daemon); acceptorThread.start(); } } }
WorkerStack类使用了定长的数组来方便存取worker,也就是真正处理请求的线程。重点看一下Acceptor这个Runnable类的实现。
/** * Server socket acceptor thread. */ protected class Acceptor implements Runnable { /** * The background thread that listens for incoming TCP/IP connections * and hands them off to an appropriate processor. */ public void run() { // Loop until we receive a shutdown command while (running) { // Loop if endpoint is paused while (paused) { try { Thread.sleep(1000); } catch (InterruptedException e) { // Ignore } } // Accept the next incoming connection from the server socket try { Socket socket = serverSocketFactory.acceptSocket(serverSocket); // 阻塞接收socket请求 serverSocketFactory.initSocket(socket); // Hand this socket off to an appropriate processor if (!processSocket(socket)) { // 处理socket请求 // Close socket right away try { socket.close(); } catch (IOException e) { // Ignore } } } catch (IOException x) { if (running) log.error(sm.getString("endpoint.accept.fail"), x); } catch (Throwable t) { log.error(sm.getString("endpoint.accept.fail"), t); } // The processor will recycle itself when it finishes ??? } }// end run }
最重要的就是processSocket()方法了,看下源代码:
protected boolean processSocket(Socket socket) { try { if (executor == null) { // 默认情况 getWorkerThread().assign(socket); } else { // 用户自己指定了执行任务的线程池 executor.execute(new SocketProcessor(socket)); } } catch (Throwable t) { // This means we got an OOM or similar creating a thread, or that // the pool and its queue are full log.error(sm.getString("endpoint.process.fail"), t); return false; } return true; }
在默认情况下,首先要get到一个Worker的Thread,然后才能assign任务。
看一下getWorkerThread()这条逻辑:
/** * Return a new worker thread, and block while to worker is available. */ protected Worker getWorkerThread() { // Allocate a new worker thread synchronized (workers) { // 获取workers锁 Worker workerThread; while ((workerThread = createWorkerThread()) == null) { try { workers.wait(); // 没有可用线程时释放workers锁,等待notify } catch (InterruptedException e) { // Ignore } } return workerThread; } }
代码要通过createWorkerThread()方法来获取一个workerThread,阅读如下代码就可以知道,这个方法有可能返回null。这样这个线程就需要让锁等待了,直到有线程notify。想一下就知道,肯定是分配出去执行任务的线程执行完成后,就可以notify接口请求的线程。接收请求的线程继续while循环,直到获取到一个workerThread为止。
createWorkerThread()方法源代码:
protected Worker createWorkerThread() { synchronized (workers) { if (workers.size() > 0) { // 通过WorkerStack提供的方法来操作Worker curThreadsBusy++; return workers.pop(); } if ((maxThreads > 0) && (curThreads < maxThreads)) { // 保证不能大于指定的最大线程数 curThreadsBusy++; if (curThreadsBusy == maxThreads) { log.info(sm.getString("endpoint.info.maxThreads", Integer.toString(maxThreads), address,Integer.toString(port))); } return (newWorkerThread()); } else { if (maxThreads < 0) { // maxThreads小于0时会无限制的new WorkerThread,表示不限制 curThreadsBusy++; return (newWorkerThread()); } else { // 当curThreads等于maxThreads或者大于maxThreads且maxThreads大于0的情况 return (null); } } } }
recycleWorkerThread()方法源代码:
protected void recycleWorkerThread(Worker workerThread) { synchronized (workers) { workers.push(workerThread); curThreadsBusy--; workers.notify(); } }
这个方法被谁调用了呢?当然是被执行任何的线程调用了。
下面来看一下最重要的Worker类中非常重要的几个方法,如下:
protected class Worker implements Runnable { protected Thread thread = null; protected boolean available = false; // available初始化为false protected Socket socket = null; /** * The background thread that listens for incoming TCP/IP connections * and hands them off to an appropriate processor. */ public void run() { // Process requests until we receive a shutdown signal while (running) { // Wait for the next socket to be assigned Socket socket = await(); // 1 if (socket == null) continue; // Process the request from this socket if (!setSocketOptions(socket) || !handler.process(socket)) { // Close socket try { socket.close(); } catch (IOException e) { } } // Finish up this request socket = null; recycleWorkerThread(this); } } /** * Start the background processing thread. */ public void start() { thread = new Thread(this); thread.setName(getName() + "-" + (++curThreads)); thread.setDaemon(true); thread.start(); } }
这个线程在assign任务之前是start的,看一下run()方法中的第1步调用了await()方法,在await()方法中由于available值默认为false,所以进入了while循环后让出了线程锁并等待assign()方法notifyAll()。
/** * Await a newly assigned Socket from our Connector, or * null if we are supposed to shut down. */ private synchronized Socket await() { // Wait for the Connector to provide a new Socket while (!available) { try { wait(); } catch (InterruptedException e) { } } // Notify the Connector that we have received this Socket Socket socket = this.socket; available = false; notifyAll(); return (socket); }
当我们assign任务后,调用的assign()方法如下:
/** * Process an incoming TCP/IP connection on the specified socket. Any * exception that occurs during processing must be logged and swallowed. * NOTE: This method is called from our Connector's thread. We * must assign it to our own thread so that multiple simultaneous * requests can be handled. */ synchronized void assign(Socket socket) { // Wait for the Processor to get the previous Socket while (available) { try { wait(); } catch (InterruptedException e) { } } // Store the newly available Socket and notify our thread this.socket = socket; available = true; notifyAll(); }
没有进入while循环,置available为true后notifyAll()。这样await()方法就跳出循环并置available为false后返回一个局部变量socket(为什么要返回一个局部变量socket呢?),这样run()方法就可以开始往下走了,完成后调用recycleWorkerThread()方法进行线程回收。
这个run()方法再次进入while循环,调用await()方法后,由于await()方法在之前跳出循环时将available设置为false,所以就进入了让锁等待,等待请求线程调用assign()方法指定任务,这样就回到了开始叙述的地方了。
为什么在await()方法中使用局部变量socket呢?
摘自深入剖析Tomcat:因为使用局部变量可以在当前Socket对象处理完之前,继续接收下一个Socket对象。
个人认为是怕在run()方法运行的过程中其它线程调用这个Worker对象的assign()方法,毕竟这个对象的引用是可以被其它线程获取到的。为什么可以调用assign()方法重新指定呢?因为run()方法没有加synchronized关键字,所以不能与assign()方法互斥访问socket资源。还是为了安全性吧。
剑指架构师系列-tomcat6通过伪异步实现connector的更多相关文章
- 剑指架构师系列-tomcat6通过IO复用实现connector
由于tomcat6的配置文件如下: <Connector port="80" protocol="org.apache.coyote.http11.Http11Ni ...
- 剑指架构师系列-spring boot的logback日志记录
Spring Boot集成了Logback日志系统. Logback的核心对象主要有3个:Logger.Appender.Layout 1.Logback Logger:日志的记录器 主要用于存放日志 ...
- 剑指架构师系列-持续集成之Maven+Nexus+Jenkins+git+Spring boot
1.Nexus与Maven 先说一下这个Maven是什么呢?大家都知道,Java社区发展的非常强大,封装各种功能的Jar包满天飞,那么如何才能方便的引入我们项目,为我所用呢?答案就是Maven,只需要 ...
- 剑指架构师系列-Struts2构造函数的循环依赖注入
Struts2可以完成构造函数的循环依赖注入,来看看Struts2的大师们是怎么做到的吧! 首先定义IBlood与BloodImpl类: public interface IBlood { } pub ...
- 剑指架构师系列-Struts2的缓存
Struts2的缓存中最重要的两个类就是ReferenceMap与ReferenceCache.下面来解释下ReferenceCache中的get()方法. public V get(final Ob ...
- 剑指架构师系列-Hibernate需要掌握的Annotation
1.一对多的关系配置 @Entity @Table(name = "t_order") public class Order { @Id @GeneratedValue priva ...
- 剑指架构师系列-InnoDB存储引擎、Spring事务与缓存
事务与锁是不同的.事务具有ACID属性: 原子性:持久性:由redo log重做日志来保证事务的原子性和持久性,一致性:undo log用来保证事务的一致性隔离性:一个事务在操作过程中看到了其他事务的 ...
- 剑指架构师系列-Linux下的调优
1.I/O调优 CentOS下的iostat命令输出如下: $iostat -d -k 1 2 # 查看TPS和吞吐量 参数 -d 表示,显示设备(磁盘)使用状态:-k某些使用block为单位的列强制 ...
- 剑指架构师系列-MySQL调优
介绍MySQL的调优手段,主要包括慢日志查询分析与Explain查询分析SQL执行计划 1.MySQL优化 1.慢日志查询分析 首先需要对慢日志进行一些设置,如下: SHOW VARIABLES LI ...
随机推荐
- Leetcode-283 Move Zeroes
#283. Move Zeroes Given an array nums, write a function to move all 0's to the end of it while mai ...
- Mars的自语重出江湖,祝大家端午节安康
上一篇博客似乎已是非常久远的回忆了,不再码字也已经很多年.<三国演义>里,刘备投靠曹操的那段时间里,2个兄弟问刘备未来,刘备说: 屈身守分,以待天时,不可与命争也. 这样一个时代,每个老百 ...
- Clojure上手
Clojure,这是什么鬼?一门基于JVM(现在也有基于.NET CLR的了:Clojure CLR) 的函数式编程语言.在JVM平台运行的时候,会被编译为JVM的字节码进行运算..为什么要学它?其设 ...
- Python学习笔记(3):数据集操作-列的统一操作
对数据库查询,将得到一个数据集: rs=AccessDB.GetData("select * from log where f_code='600259' limit 5,5") ...
- 21个高质量的Swift开源iOS App
原文:21 Amazing Open Source iOS Apps Written in Swift 对Swift初学者来说,学习开源项目,阅读源码是个不错的方法.在这篇文章中,基于对代码质量和排名 ...
- Android——BitMap(位图)相关知识总结贴
Android中文API(136) —— Bitmap http://www.apkbus.com/android-54644-1-1.html Android 4.0 r1 API—Bitmap(S ...
- 40页PPT勾画“互联网颠覆性思维”----诠释互联网思维
本文PPT内容涉及移动互联网的三个分支——移动电商.在线教育和新媒体. 不同领域一直是可以相互借鉴.相互渗透.相互学习的,在盈利模式和思维方式上有很多是共通的.
- C++ STL 学习 :for_each与仿函数(functor)
简单来将,仿函数(functor)就是一个重载了"()"运算符的struct或class,利用对象支持operator()的特性,来达到模拟函数调用效果的技术. 我们平时对一个集合 ...
- LCLFramework框架之开发约束
Entity编写 1:所有的实体类都必须继承DomainEntity 2:所有的表都必须有 ID 3:所有表的关系字段必须是ID [Serializable] public class User: D ...
- BW:如何加载和生成自定义的层次结构,在不使用平面文件的SAP业务信息仓库
介绍 通常情况下,报告需要在一个类似树的结构来显示数据.通过启用此特性在SAP BW层次结构.高级数据显示的层次结构的顶层节点.更详细的数据可以向下钻取到的层次结构中的下级节点的可视化. 考虑一个例子 ...