org.apache.tomcat.util.net包的内容都与网络连接和socket有关,比较主要和常见的是JIOEndpoint这个类,前面提到Coyote连接器的时候,就有涉及到JIOEndpoint,它用于监听某个socket端口、将socket对象交给coyote,并提供基本的线程池功能。除了JIOEndpoint,还有AprEndpoint、NioEndpoint等。由于对apr和nio不熟悉,所以只研究了一下JIOEndpoint
org.apache.tomcat.util.net.JIoEndpoint
JIOEndpoint其实和我们本科时上计算机网络或者分布式系统,做实验写的socket服务器差不多,结构也是经典的“Listen-Accept-Handle”,这里简单描述一下:JIOEndpoint使用JDK的ServerSocket类监听某个端口,有socket连接进来的时候便返回一个socket对象,交给专门的处理器。当然,具体的实现没那么简单,下面会按照socket的处理过程,详细说明其中的机理。
初始化
public void init()
throws Exception {
if (initialized)
return;
// Initialize thread count defaults for acceptor
if (acceptorThreadCount == 0) {
acceptorThreadCount = 1;
}
if (serverSocketFactory == null) {
serverSocketFactory = ServerSocketFactory.getDefault();
}
if (serverSocket == null) {
try {
if (address == null) {
serverSocket = serverSocketFactory.createSocket(port, backlog);
} else {
serverSocket = serverSocketFactory.createSocket(port, backlog, address);
}
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
}
}
//if( serverTimeout >= 0 )
// serverSocket.setSoTimeout( serverTimeout );
initialized = true;
}
在这里,利用serverSocketFactory新建了一个serverSocket对象,用于监听特定的端口
启动JIOEndpoint
// Create worker collection
if (executor == null) {
workers = new WorkerStack(maxThreads);
}
// Start acceptor threads
for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(daemon);
acceptorThread.start();
}
这里有几个类:worker,workerStack,Acceptor。这些都是JIOEndpoint的一些内部类,下面按照处理顺序依次讲述
Acceptor内部类
Acceptor实现了Runnable接口,只有一个方法run,做的事情就是通过ServerSocket.accept方法,得到socket,然后调用JIOEndpoint的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;
}
在这里,JIOEndpoint有两种处理socket的方式:使用JDK5的executor,或者内部的worker类。executor怎么用大家可以直接翻书了,我们继续讨论第二种方法。
首先我们要通过getWorkerThread()方法,得到一个Worker对象。具体逻辑是,看看WorkerStack(存放所有worker的一个堆栈)里面有没有空余的worker,有则直接拿来用,无则看看能不能新建一个worker线程,假如不能(比如超出了最大线程数限制),则返回null,那样就只能委屈一下这个acceptor,稍微等一下了(wait()方法),直到有新的worker可用时,通过notify方法唤醒等待的acceptor
/**
* Return a new worker thread, and block while to worker is available.
*/
protected Worker getWorkerThread() {
// Allocate a new worker thread
Worker workerThread = createWorkerThread();
while (workerThread == null) {
try {
synchronized (workers) {
workers.wait();
}
} catch (InterruptedException e) {
// Ignore
}
workerThread = createWorkerThread();
}
return workerThread;
}
如下,当有worker被回收后,通知等待的acceptor
protected void recycleWorkerThread(Worker workerThread) {
synchronized (workers) {
workers.push(workerThread);
curThreadsBusy--;
workers.notify();
}
}
ok,回到前面的processSocket方法,得到worker后,通过worker.assign方法,将socket对象传递给worker
Worker内部类
Worker也实现了runnable接口,有三个方法:assign、await、start和run
所谓的start方法,就是new一个Thread对象,把worker自己传进去,我们知道这个thread就开始执行run方法了。
public void run() {
// Process requests until we receive a shutdown signal
while (running) {
// Wait for the next socket to be assigned
Socket socket = await();
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);
}
}
run方法首先调用await方法
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);
}
通过标记位available,如果当前的worker是“非available”的,则线程会开始等待。直到我们调用的assign方法,把一个可用的socket给worker后,才会notifyall,唤醒一个线程,进而取得assign过来的socekt
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();
}
所以,assign和await方法相当于生产者和消费者方法,两者通过available进行互斥,而this.socket则相当于被竞争的资源
现在,又回到Worker.run方法。在取得socket后,通过setSocketOptions(socket)方法设置socket的相关选项(例如超时值),最后通过handler.process(socket),终于把socket这个接力棒交给coyote了!
handler是什么?就是个简单的接口,如下:
/**
* Bare bones interface used for socket processing. Per thread data is to be
* stored in the ThreadWithAttributes extra folders, or alternately in
* thread local fields.
*/
public interface Handler {
public boolean process(Socket socket);
}
回顾一下org.apache.coyote.http11.Http11Protocol 的Http11ConnectionHandler内部类,实现的正是这个接口
运维网声明
1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网 享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com