设为首页 收藏本站
查看: 695|回复: 0

[经验分享] tomcat 6的JIoEndpoint

[复制链接]

尚未签到

发表于 2017-1-26 13:34:35 | 显示全部楼层 |阅读模式
  先上个图先,一个只有我自己能看懂的url时序图.

DSC0000.jpg



这个基本上是connentor初始化的时候,初始化了Http11Protocol,接着初始化JIoEndpoint,初始化介绍后,connentor调用start()方法开始工作鸟,接着调用Http11Protocol,JIoEndpoint的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);
}
// 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();
}
}
}
  start()方法中如果没有外部的executor的话,会使用的自己内部的简单的工作线程池WorkerStack,这个线程池放着处理请求的线程集合,当请求的数量超过定义的最大支持线程数,那么后面的请求一直阻塞等待只到有可以使用的线程为止。
  看代码

   for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(daemon);
acceptorThread.start();
}
  acceptorThreadCount一般的值就是1,这里另外开启一个线程,Acceptor是个内部类, 看代码:

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);
serverSocketFactory.initSocket(socket);
// Hand this socket off to an appropriate processor
if (!processSocket(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
}
}
}
  Acceptor实现runnable接口,在run方法里面是一个while循环,当JIoEndpoint的start()的已经执行过的话,那么running为true,while循环就一直运行下去,只到JIoEndpoint执行了resume,stop等方法。while循环里面利用工厂类来产生一个socket,这里只是把socket编程的步骤移动到了工厂里面了,符合类单一责任原则。
  最主要的是调用JIoEndpoint的processSocket方法,另外如果方法调用返回false的话,socket会被关闭,因为false一般代表了这次请求处理可能有问题,另外在这个类中,发现如果出现异常的话,基本上是不用throw异常的,一是可能考虑到这只是对一个请求而已,抛出异常没有意义,二是为了性能的考虑,所有一般的方法都是以boolean来判断异常和正常与否。我们看下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;
}
  有外部executor的话,那么外部的executor来执行这个socket;没有的话,就需要JIoEndpoint的内部线程类Worker来完成了
  先看如果有executor的情况,此时调用很简单,一个runnable实现SocketProcessor被调用

       public void run() {
// 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;
}
  设置socket的属性,然后调用内部接口Handler的process处理socket的具体内容,Handler的具体实现在Http11Protocol内部类Http11ConnectionHandler中,这个后面再讲。
  这样子JIoEndpoint的工作基本完成了。
  再来看当executor==null的时候的分支,首先需要获得一个工作线程,但是如果线程池设置了上限的话,可能会阻塞只到有可用的线程或者可以创建新的线程,之后我们来看Worker的assign方法,看这个方法需要和Worker的await方法一起来看:

protected boolean available = false;
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();
}
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时,是不用wait的,但是await方法就会阻塞在这里,这里这样做的第1个原因出现:
  assign()方法设置了socket后才设置available,这里主要是为了await()方法返回的socket是正确的,不为空的,是这个线程的,当assign()最后nofifyAll(),那么await()方法可以自由的执行了
  反过来看,当available为true的时候,await()可以执行,而assign()方法阻塞了,这里有第2个原因出现,Worker存活在线程池中,在这两个方法中我们用的同一个属性socket,所以这个每个请求来了之后都会改变这个属性,所以这两个方法必须同步,在await()返回当前的socket之前,是不能设置这个socket的.
  我们在来看Worker的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);
}
}
   这个里面调用了await()方法,就是上面所说的获得正确的socket,然后执行和SocketProcessor内部类实现的一样的功能的代码,最后做一些线程回收的工作
  这里有一点可能会影响到性能,就是Worker线程池最大时,就需要阻塞等待,知道有可以用的线程,这个可能是考虑到如果线程开太多,内部压力大,线程的切换消耗大,这个可以作为tomcat调优的一个点,另外一个就是可以使用外部的线程池,这个也可以尝试
  哇,真长!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-333751-1-1.html 上篇帖子: Spirng Tomcat ActiveMQ JMS 下篇帖子: Tomcat Realm的使用
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表