10、Tomcat优化

2020-09-15  本文已影响0人  技术灭霸

1、Tomcat如何扩展Java线程池?

通过比较FixedThreadPool和CachedThreadPool,我们发现它们传给ThreadPoolExecutor的参数有两个关键点:

  1. 是否限制线程个数。
  2. 是否限制队列长度。
    对于Tomcat来说,这两个资源都需要限制,也就是说要对高并发进行控制,否则CPU和内存有资源耗尽的风险。

Tomcat扩展了Java线程池的核心类ThreadPoolExecutor,并重写了它的execute方法,定制了自己的任务处理流程。同时Tomcat还实现了定制版的任务队列,重写了offer方法,使得在任务队列长度无限制的情况下,线程池仍然有机会创建新的线程。

2、Tomcat如何支持WebSocket?

WebSocket技术实现了Tomcat与浏览器的双向通信,Tomcat可以主动向浏览器推送数据,可以用来实现对数据实时性要求比较高的应用。这需要浏览器和Web服务器同时支持WebSocket标准,Tomcat启动时通过SCI技术来扫描和加载WebSocket的处理类ServerEndpoint,并且建立起了URL到ServerEndpoint的映射关系。

当第一个WebSocket请求到达时,Tomcat将HTTP协议升级成WebSocket协议,并将该Socket连接的Processor替换成UpgradeProcessor。这个Socket不会立即关闭,对接下来的请求,Tomcat通过UpgradeProcessor直接调用相应的ServerEndpoint来处理。

3、Tomcat的对象池技术

Tomcat和Jetty中的对象池技术

public class SynchronizedStack<T> {
    //内部维护⼀个对象数组,⽤数组实现栈的功能
    private Object[] stack;
    //这个⽅法⽤来归还对象,⽤synchronized进⾏线程同步
    public synchronized boolean push(T obj) {
        ++this.index;
        if (this.index == this.size) {
            if (this.limit != -1 && this.size >= this.limit) {
                --this.index;
                return false;
            }
            this.expand();//对象不够⽤了,扩展对象数组
        }

        this.stack[this.index] = obj;
        return true;
    }
    //这个⽅法⽤来获取对象
    public synchronized T pop() {
        if (this.index == -1) {
            return null;
        } else {
            T result = this.stack[this.index];
            this.stack[this.index--] = null;
            return result;
        }
    }


   //扩展对象数组⻓度,以2倍⼤⼩扩展
    private void expand() {
        int newSize = this.size * 2;
        if (this.limit != -1 && newSize > this.limit) {
            newSize = this.limit;
        }
        //扩展策略是创建⼀个数组⻓度为原来两倍的新数组
        Object[] newStack = new Object[newSize];
        //将⽼数组对象引⽤复制到新数组
        System.arraycopy(this.stack, 0, newStack, 0, this.size);
        //将stack指向新数组,⽼数组可以被GC掉了
        this.stack = newStack;
        this.size = newSize;
    }
}

这个代码逻辑比较清晰,主要是SynchronizedStack内部维护了一个对象数组,并且用数组来实现栈的接口:push和pop方法,这两个方法分别用来归还对象和获取对象。你可能好奇为什么Tomcat使用一个看起来比较简单的SynchronizedStack来做对象容器,为什么不使用高级一点的并发容器比如
ConcurrentLinkedQueue呢?

这是因为SynchronizedStack用数组而不是链表来维护对象,可以减少结点维护的内存开销,并且它本身只支持扩容不支持缩容,也就是说数组对象在使用过程中不会被重新赋值,也就不会被GC。这样设计的目的是用最低的内存和GC的代价来实现无界容器,同时Tomcat的最大同时请求数是有限制的,因此不需要担心对象的数量会无限膨胀

4、Tomcat高性能、高并发之道

Tomcat中用到了大量的高性能、高并发的设计,我总结了几点:I/O和线程模型、减少系统调用、池化、零拷贝、高效的并发编程。

1、I/O和线程模型

采用非阻塞I/O或者异步I/O

2、减少系统调用

系统调用最多的就是网络通信操作了,一个Channel上的write就是系统调用,为了降低系统调用的次数,最直接的方法就是使用缓冲,当输出数据达到一定的大小才flush缓冲区

3、池化、零拷贝

池化的本质就是用内存换CPU;而零拷贝就是不做无用功,减少资源浪费。

4、高效的并发编程

1、缩小锁的范围
2、用原子变量和CAS取代锁
3、并发容器的使用
4、volatile关键字的使用

上一篇 下一篇

猜你喜欢

热点阅读