Tomcat-connector的微调(3): processorCache与socket.processorCache

简介:

tomcat在处理每个连接时,Acceptor角色负责将socket上下文封装为一个任务SocketProcessor然后提交给线程池处理。在BIO和APR模式下,每次有新请求时,会创建一个新的SocketProcessor实例(在之前的tomcat对keep-alive的实现逻辑里也介绍过可以简单的通过SocketProcessorSocketWrapper实例数对比socket的复用情况);而在NIO里,为了追求性能,对SocketProcessor也做了cache,用完后将对象状态清空然后放入cache,下次有新的请求过来先从cache里获取对象,获取不到再创建一个新的。

这个cache是一个ConcurrentLinkedQueue,默认最多可缓存500个对象(见SocketProperties)。可以通过socket.processorCache来设置这个缓存的大小,注意这个参数是NIO特有的。

接下来在SocketProcessor执行过程中,真正的业务逻辑是通过一个org.apache.coyote.Processor的接口来封装的,默认这个Processor的实现是org.apache.coyote.http11.Http11Processor。我们看一下SocketProcessor.process(...)方法的大致逻辑:


public SocketState process(SocketWrapper<S> wrapper, SocketStatus status) {
    ...
    // 针对长轮询或upgrade情况
    Processor<S> processor = connections.get(socket);
    ...

    if (processor == null) {
        // 1) 尝试从回收队列里获取对象
        processor = recycledProcessors.poll();
    }
    if (processor == null) {
        // 2) 没有再创建新的
        processor = createProcessor();
    }

    ...
    state = processor.process(wrapper);

    ...
    release(wrapper, processor, ...);

    ...
    return SocketState.CLOSED;
}


上面的方法是在AbstractProtocol模板类里,所以BIO/APR/NIO都走这段逻辑,这里使用了一个回收队列来缓存Processor,这个回收队列是ConcurrentLinkedQueue的一个子类,队列的长度可通过server.xml里connector节点的processorCache属性来设置,默认值是200,如果不做限制的话可以设置为-1,这样cache的上限将是最大连接数maxConnections的大小。

在原有的一张ppt上加工了一下把这两个缓存队列所在位置标示了一下,图有点乱,重点是两个绿颜色的cache队列:

图中位于上面的socket.processorCache队列是NIO独有的,下面的processorCache是三种连接器都可以设置的。processorCache这个参数在并发量比较大的情况下也蛮重要的,如果设置的太小,可能引起瓶颈。我们模拟一下,看看这个瓶颈是怎么回事。先修改server.xml里的connector节点,把processorCache设置为0:


    <Connector port="7001"
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           connectionTimeout="20000"
           redirectPort="8443" 
           processorCache="0"/>


启动tomcat后,使用ab模拟并发请求:


$ ab -n100000 -c10 http://localhost:7001/main


然后在ab的执行过程中立刻执行jstack观察堆栈信息,会发现一大半线程阻塞在AbstractConnectionHandler.registerAbstractConnectionHandler.unregister方法上:


"http-nio-7001-exec-11" #34 daemon prio=5 os_prio=31 tid=0x00007fd05ab05000 nid=0x8903 waiting for monitor entry [0x000000012b3b7000]
 java.lang.Thread.State: BLOCKED (on object monitor)
 at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.register(AbstractProtocol.java:746)
 - waiting to lock <0x00000007403b8950> (a org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler)
 at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.createProcessor(Http11NioProtocol.java:277)
 at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.createProcessor(Http11NioProtocol.java:139)
 at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
 at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1720)
 at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1679)

 ...
"http-nio-7001-exec-4" #27 daemon prio=5 os_prio=31 tid=0x00007fd0593e3000 nid=0x7b03 waiting for monitor entry [0x000000012aca2000]
 java.lang.Thread.State: BLOCKED (on object monitor)
 at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.unregister(AbstractProtocol.java:773)
 - locked <0x00000007403b8950> (a org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler)
 at org.apache.coyote.AbstractProtocol$RecycledProcessors.offer(AbstractProtocol.java:820)
 at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.release(Http11NioProtocol.java:219)
 at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:690)
 at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1720)


registerunregister分别是在创建和回收processor的时候调用的;看一下createProcessor方法里的大致逻辑:


public Http11NioProcessor createProcessor() {
    Http11NioProcessor processor = new Http11NioProcessor(...);
    processor.setXXX(...);
    ...

    // 这里,注册到jmx
    register(processor);
    return processor;
}


tomcat对jmx支持的非常好,运行时信息也有很多可以通过jmx获取,所以在每个新连接处理的时候,会在创建processor对象的时候注册一把,然后在processor处理完回收的时候再反注册一把;但这两个方法的实现都是同步的,同步的锁是一个全局的ConnectionHandler对象,造成了多个线程会在这里串行。

绝大部分应用没有特别高的访问量,通常并不需要调整processorCache参数,但对于网关或代理一类的应用(尤其是使用servlet3的情况)这个地方可以设置的大一些,比如调到1000或者-1。 

目录
相关文章
|
监控 druid Java
监控druid数据库连接池连接泄露的思路
监控druid数据库连接池连接泄露的思路
1824 2
|
监控 负载均衡 Java
5 大 SpringCloud 核心组件详解,8 张图彻底弄懂
本文图文详解 Spring Cloud 的五大核心组件,帮助深入理解和掌握微服务架构。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
5 大 SpringCloud 核心组件详解,8 张图彻底弄懂
|
小程序 UED
uniapp 添加分包页面,配置分包预下载
uniapp 添加分包页面,配置分包预下载
330 2
|
敏捷开发 JavaScript 测试技术
深入理解与应用软件测试中的Mock技术
【5月更文挑战第5天】 在现代软件开发过程中,单元测试作为保障代码质量的重要环节,其独立性和可靠性至关重要。Mock技术应运而生,为开发者提供了一种在隔离环境下模拟外部依赖的方法。本文将深入探讨Mock技术的概念、实现方式及其在软件测试中的应用,旨在帮助读者更好地理解和运用这一强大的测试工具,以提升测试效率和软件质量。
|
机器学习/深度学习 人工智能 自然语言处理
探索Spring AI:将人工智能与软件开发无缝融合
这是一个简单的Spring AI入门示例,演示了如何使用Spring Boot和Spring AI来构建一个简单的机器学习应用程序。通过这个示例,你可以了解到如何利用Spring AI轻松地集成机器学习功能到你的应用程序中。
637 0
|
缓存 监控 Java
【建议收藏】Tomcat 优化总结
以上是V 哥整理的 Tomcat 优化的一些点,希望对你有所帮助,目前Tomcat已更新到11版本,你在项目中是使用哪个版本呢,如果有一些独到的优化策略,欢迎留言告诉我,感谢。
526 0
|
数据可视化 数据挖掘 Python
Scipy 中级教程——信号处理
Scipy 中级教程——信号处理【1月更文挑战第8篇】
804 2
|
存储 算法 C语言
【C++入门到精通】C++入门 —— set & multiset (STL)
探索C++ STL中的重要成员:`set`与`multiset`。`set`是实现有序、不重复元素集合的容器,基于红黑树,提供高效操作。`multiset`则允许元素重复,两者均支持插入、删除、查找等操作。`set`强调元素唯一性,而`multiset`允许元素重复。两者在插入、查找、删除上的时间复杂度均为O(logN)。使用迭代器可遍历元素,但不支持下标访问。了解它们的特点和选择适用场景是关键。
535 0
|
应用服务中间件 数据格式 XML
解决tomcat stop报Illegal access: this web application instance has been stopped异常方法
解决tomcat stop报Illegal access: this web application instance has been stopped异常方法 运行shutdown.bat/shutdown.
5970 0
|
存储
链表的学习:链表的头插法和尾插法以及HashMap中链表结点的插入方式
链表的学习:链表的头插法和尾插法以及HashMap中链表结点的插入方式 链表是一种常用的数据结构,它采用链式存储结构存储数据,相对于数组具有更灵活的操作和更高的效率。链表插入元素的方式有头插法和尾插法。
961 0

热门文章

最新文章