Tomcat线程居高不下的原因

前言

笔者在解决业务的问题中发现,如果Tomcat在并发延迟突变高后,很容易线程数量飙高,而且短时间是降不下去的,除非重启,但是不能线程飙高就重启吧,实际上是线程池的逻辑,然而Tomcat开放的配置是限制的。

1. Tomcat运行的线程配置

Tomcat运行过程一般都是在server.xml中配置catalina-exec-xxx和http-nio-8080-xxx

另外的配置

 

2. Tomcat线程飙高很久的原因

线程池的线程数量要超过core数量必须要正在执行的任务线程数达到core且队列打满,才会启动max-core的线程做最后的挣扎😄。

根据这个原理: Tomcat线程数超过core必须是持续并发超过core,但是又是max线程数能接受的范畴。

根据Tomcat 8.5.73 Tomcat启动创建的源码,创建catalina-exec线程池的代码:

org.apache.catalina.core.StandardThreadExecutor

startInternal,根据注释是
org.apache.catalina.util.LifecycleBase#startInternal()
    /**
     * Start the component and implement the requirements
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    @Override
    protected void startInternal() throws LifecycleException {

        taskqueue = new TaskQueue(maxQueueSize);
        TaskThreadFactory tf = new TaskThreadFactory(namePrefix,daemon,getThreadPriority());
        executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime, TimeUnit.MILLISECONDS,taskqueue, tf);
        executor.setThreadRenewalDelay(threadRenewalDelay);
        if (prestartminSpareThreads) {
            executor.prestartAllCoreThreads();
        }
        taskqueue.setParent(executor);

        setState(LifecycleState.STARTING);
    }

可以看到可以调节core max max超时时间,以及队列数,均可以调节。

创建http-nio-8080-xxx的代码,实际上Tomcat的http处理就是这个线程池最终执行的

org.apache.tomcat.util.net.AbstractEndpoint

    public void createExecutor() {
        internalExecutor = true;
        TaskQueue taskqueue = new TaskQueue();
        TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
        executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
        taskqueue.setParent( (ThreadPoolExecutor) executor);
    }

可以看到队列是不可以调节的无界队列,只能设置core和max线程数,其他参数都不可更改。

public class TaskQueue extends LinkedBlockingQueue<Runnable> {

    private static final long serialVersionUID = 1L;
    protected static final StringManager sm = StringManager.getManager(TaskQueue.class);
    private static final int DEFAULT_FORCED_REMAINING_CAPACITY = -1;

    private transient volatile ThreadPoolExecutor parent = null;

    // No need to be volatile. This is written and read in a single thread
    // (when stopping a context and firing the listeners)
    private int forcedRemainingCapacity = -1;

    public TaskQueue() {
        super();
    }

-----------------------------------

    /**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

前缀的来源

org.apache.coyote.AbstractProtocol.

getNamePrefix

选择一个NIO

    protected String getNamePrefix() {
        if (isSSLEnabled()) {
            return ("https-" + getSslImplementationShortName()+ "-nio");
        } else {
            return ("http-nio");
        }
    }

3. Tomcat的调节

实际上知道源码就可以很简单的处理问题,调节参数就可以了,一般的调节core的数量,调大一些就可以提高并发量,另外catalina-exec-xxx可以调节max超时时间和队列数,来达到尽量不把队列打满的情况。

总结

实际上Tomcat在演进中,为了提高并发量,IO设计经过了几次迭代BIO NIO NIO2 APR

BIO

阻塞式IO,通过输入输出流的方式,通过线程并发,IO能力弱,已经被Tomcat默认不选择。

NIO/NIO2

NIO是多路复用,一个线程复用多个socket连接,NIO2增加了AIO的支持,Tomcat分别采用Future方式实现阻塞读写,采用CompletionHandler方式实现非阻塞读写。

Tomcat8默认NIO方式,也是通用的配置,根据实际可以配置NIO2.

APR

采用底层JNI的模式IO,性能最强,但是依赖native的包:Apache Tomcat Native Library - Documentation Index

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值