美文网首页
10、Tomcat优化

10、Tomcat优化

作者: 技术灭霸 | 来源:发表于2020-09-15 09:25 被阅读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关键字的使用

    相关文章

      网友评论

          本文标题:10、Tomcat优化

          本文链接:https://www.haomeiwen.com/subject/frwldktx.html