剑指架构师系列-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 ...
随机推荐
- RFID 仿真/模拟/监控/拦截/检测/嗅探器
Sound card based RFID sniffer/emulator (Too tired after recon.cx to do draw the schematics better th ...
- 【VerySky原创】如何查找SNRO编号范围的使用情况;
SAP所有编号范围的对象都可以在表NRIV中找到:
- 【转】兼容iOS 10 资料整理
1.Notification(通知) 自从Notification被引入之后,苹果就不断的更新优化,但这些更新优化只是小打小闹,直至现在iOS 10开始真正的进行大改重构,这让开发者也体会到UserN ...
- #pragma data_seg 共享数据区(转)
原文地址:http://www.cnblogs.com/CBDoctor/archive/2013/01/26/2878201.html 1)#pragma data_seg()一般用于DLL中.也就 ...
- mybatis中传入String类型参数异常
在使用mybatis时,写了一条sql语句,只有一个String类型的参数, 示例代码 <select id="getApplyNum" parameterType=&quo ...
- Dalvik VM (DVM) 与Java VM (JVM)之间有哪些区别?
Dalvik虚拟机存在于Android系统,JVM是java虚拟机,两者都是虚拟机,本文就对两者进行比较,讲述它们的不同. Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的 ...
- 一种可以避免数据迁移的分库分表scale-out扩容方式
原文地址:http://jm-blog.aliapp.com/?p=590 目前绝大多数应用采取的两种分库分表规则 mod方式 dayofweek系列日期方式(所有星期1的数据在一个库/表,或所有?月 ...
- 从零开始--系统深入学习IOS(使用Swift---带链接)
这是一篇面向IOS新手的文档.同时提供一些系统知识的链接,让你系统学习IOS.它提供一些信息帮助你采用技术和编程接口来开发苹果软件产品,本人不保证会在将来更新.学习它,需要你掌握一些基本的编程知识 1 ...
- Windows XP 中设置VPN(PPTP连接方式)
第一步:点开始-网上邻居或者控制面板-网络连接,选择-创建一个新的连接 第二步:点击-下一步 第三步:选择-连接到我的工作场所的网络,点击-下一步 第四步:选择-虚拟专用网络连接,点击-下一步 第五步 ...
- IT技术团队行而有效的管理之道
在博客园看到的一份读书笔记,如果日后有机会去管理一个技术团队,那么下面这些经验可能会让你更好地实施工作. IT技术团队员工的特点 高学历,知识密集型,技术立身. 人际关系简单,摩擦大多数由技术产生,相 ...