首先在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的更多相关文章

  1. 剑指架构师系列-tomcat6通过IO复用实现connector

    由于tomcat6的配置文件如下: <Connector port="80" protocol="org.apache.coyote.http11.Http11Ni ...

  2. 剑指架构师系列-spring boot的logback日志记录

    Spring Boot集成了Logback日志系统. Logback的核心对象主要有3个:Logger.Appender.Layout 1.Logback Logger:日志的记录器 主要用于存放日志 ...

  3. 剑指架构师系列-持续集成之Maven+Nexus+Jenkins+git+Spring boot

    1.Nexus与Maven 先说一下这个Maven是什么呢?大家都知道,Java社区发展的非常强大,封装各种功能的Jar包满天飞,那么如何才能方便的引入我们项目,为我所用呢?答案就是Maven,只需要 ...

  4. 剑指架构师系列-Struts2构造函数的循环依赖注入

    Struts2可以完成构造函数的循环依赖注入,来看看Struts2的大师们是怎么做到的吧! 首先定义IBlood与BloodImpl类: public interface IBlood { } pub ...

  5. 剑指架构师系列-Struts2的缓存

    Struts2的缓存中最重要的两个类就是ReferenceMap与ReferenceCache.下面来解释下ReferenceCache中的get()方法. public V get(final Ob ...

  6. 剑指架构师系列-Hibernate需要掌握的Annotation

    1.一对多的关系配置 @Entity @Table(name = "t_order") public class Order { @Id @GeneratedValue priva ...

  7. 剑指架构师系列-InnoDB存储引擎、Spring事务与缓存

    事务与锁是不同的.事务具有ACID属性: 原子性:持久性:由redo log重做日志来保证事务的原子性和持久性,一致性:undo log用来保证事务的一致性隔离性:一个事务在操作过程中看到了其他事务的 ...

  8. 剑指架构师系列-Linux下的调优

    1.I/O调优 CentOS下的iostat命令输出如下: $iostat -d -k 1 2 # 查看TPS和吞吐量 参数 -d 表示,显示设备(磁盘)使用状态:-k某些使用block为单位的列强制 ...

  9. 剑指架构师系列-MySQL调优

    介绍MySQL的调优手段,主要包括慢日志查询分析与Explain查询分析SQL执行计划 1.MySQL优化 1.慢日志查询分析 首先需要对慢日志进行一些设置,如下: SHOW VARIABLES LI ...

随机推荐

  1. OpenGL学习进程(12)第九课:矩阵乘法实现3D变换

    本节是OpenGL学习的第九个课时,下面将详细介绍OpenGL的多种3D变换和如何操作矩阵堆栈.     (1)3D变换: OpenGL中绘制3D世界的空间变换包括:模型变换.视图变换.投影变换和视口 ...

  2. Windows Server 2012 如何实现多个用户远程桌面登陆?

    Windows Server 2012 如何实现多个用户远程桌面登陆?说明:Windows Server 2012默认情况下,只运行2个用户远程桌面登陆,这里我们可以通过安装远程桌面会话主机配置来实现 ...

  3. 深入理解.NET程序的原理 谈一谈破解.NET软件的工具和方法

    最近一段时间不忙,闲下来的空闲时间,重读了一下CLR的原理,回味一下有关程序集的的知识,顺便练了一下手,学习致用,破解了若干个.NET平台的软件.以此来反观.NET程序开发中,需要注意的一些问题. 基 ...

  4. eclipse 引用自己开发的模块

    这样就可以 生成的是LIB 工程需要设置“Is Library”

  5. HTTP状态码(HTTP Status Code)及常用场景

    常见的状态码: HTTP: Status 200 – 服务器成功返回网页HTTP: Status 3xx - 表示要完成请求,需要进一步操作. 通常,这些状态代码用来重定向HTTP: Status 4 ...

  6. 关闭/开启 ubuntu 自动更新提示

    发现vps登陆后只有apt update后才知道有多少包需要更新不是很傻么,本地的ubuntu在登录时就有很好的提示,并且还能告知系统负载情况,很有用,这里就想开起来.首先这个提示的名字叫Motd. ...

  7. Javascript 固定表格表头

    遇到一个简单的需求: 客户有一个表格可能有很多内容,当内容很多的时候,表格就会出现滚动条 客户希望当表格内容很多时,只滚动表格而不滚动浏览器窗口 在网上找到很多相关的插件,要不就是太复杂,要不就是满足 ...

  8. GOOGLE不能访问的解决方法

    1VPN gate 2自 由 门 3修改Windows\System32\drivers\etc hosts (复制https://github.com/txthinking/google-hosts ...

  9. .net core 1.0 实现负载多服务器单点登录

    前言 .net core 出来有一时间了,这段时间也一直在做技术准备,目前想做一个单点登录(SSO)系统,在这之前用.net时我用习惯了machineKey ,也顺手在.net core 中尝试了一上 ...

  10. Swift编程语言中的方法引用

    由于Apple官方的<The Swift Programming Guide>对Swift编程语言中的方法引用介绍得不多,所以这里将更深入.详细地介绍Swift中的方法引用. Swift与 ...