• java特种兵读书笔记(4-4)——java通信之tomcat对IO请求的处理


    tomcat配置


    tomcat的配置体系,如Server,Service,Executor,HTTP等。这是tomcat从外层到内层的访问机制。

    tomcat启动时,是启动java的main方法。tomcat的lib目录下有一个catalina的jar包,引导方法main就在这个jar包的BootStrap类中,用jps等命令可以在JVM进程中看到。

    跟踪源码会发现,系统启动时会相继的

    ①启动Server,Tomcat中对应类是StandardServer

    ②设置Listener信息,相继调用initialize方法,该方法用于启动Service,对应Tomcat中的StandardService

    ③在StandardService初始化方法中开始初始化Connector,Engine等相关信息

    Connector配置


    配置Connector时会设置protocol协议,有HTTP/1.1,AJP/1.3等等。它们表示tomcat会使用哪一种协议来处理请求,对应到代码里即哪一套程序来处理IO请求。

    源码在Connector类的setProtocol方法中。

    如果要tomcat支持NIO,我们可以传入Http11NioProtocol这个协议。

    每一种协议都有一个XXXEndPoint属性。

    ①XXXProtocol调用init初始化方法,创建SocketFactory,并设置到XXXEndPoint中。

    ②调用XXXEndPoint的init方法,设置acceptorThreadCount,标识接受请求的线程数(可以有多个线程同时接受请求,默认是1,一般不用修改)。

    ③创建SocketServer对象。

    ④系统开始启动start方法(前面说的系统自动关联的一系列操作),最后会调用到XXXProtocol的start方法,并接着调用XXXEndPoint的start方法。

    XXXEndPoint的start方法——创建socket+创建默认线程池(组)


    ①在没有设置Executor时创建一个线程组来处理请求(数组管理线程,不是java本身的ThreadGroup)。Executor的单独配置在server.xml中。

    ②接受请求的线程启动起来,它们仅仅用于接受请求。线程启动后的动作取决于启动时传入的任务new Thread(new Acceptor(...));

    ③Acceptor类——初始化:

    通过serverSocketFactory对象的acceptSocket方法间接调用ServerSocket的accept方法,得到一个socket连接。

    ④Acceptor类——线程的使用

    processSocket方法对socket进行处理,如果配置了线程池,会使用配置的Executor提交SocketProcess(把socket封装成实现Runnable接口的任务类)去执行。如果没有配置线程池,则会使用先前提到过的线程组来处理。如果线程组内有闲置线程,分配一个处理;若没有闲置线程,会创建一个线程处理,在任务结束后(线程不会结束),会将当前线程放入workers线程组(定义的存放在数组中的线程)中。

    request和response


    分配线程的类是XXXEndPoint类的一个内部类Worker。

    ①setSocketOptions方法:设置socket的相关参数,比如TcpNoDelay,SoTimeout等参数。

    ②XXXProtocol的XXXConnectionHandler的process方法:首先会去获取一个XXXProcessor对象,如果没有获取,会创建一个新的。该XXXProcessor对象是ActionHook的实现类,会调用ActionHook的action方法。

    拉模式与推模式


    用浏览器访问解释,用户需要什么信息就去访问,那么就是拉取。如果一打开浏览器,就弹出了一大堆页面,就是推送。推送一般不关心反馈,类似于群发短信的方式。

    推模式响应更加及时,因为不等你去拉,我这边准备好了或者有什么信息就告诉你,可能等你需要并且拉的时候,这个信息已经都是很陈旧的信息了。

    推送方式的几种实现:

    ①每个客户端每隔很短的时间就去请求服务器一次,获取最新信息。但是这样有缺点,会有很多请求是浪费的,因为客户端不知道什么时候该去获取数据,可能获取的时候数据还没准备好;或者已经获取过了,不需要再获取了,但是这之后还是会发请求,服务端也会应答。

    如果这些请求都打在服务器的数据库上,对服务器压力很大。如果基于C/S模式,每个浏览器的每个选项卡每隔5秒钟都要请求服务器一次,那么请求量会成倍的上涨。

    ②真正的推送:当信息真正发生变化的时候,触发被改变的内容去通知每个终端,也许会直接将数据交给终端,也许只是一个通知,让客户端决定是否要去获取对应的数据或者做相应操作。

    长连接与推送


    要完成推送, 客户端和服务器端的连接是长连接,理论上长连接是不断开的一种连接,在web层面最简单的方式,是不断开response输出流(comet启用后就不会调用finishResponse方法)。不断开的response输出内部处理很复杂,但是现在很多开源框架已经实现。

    长连接建立后,服务器对消息的处理有主动性,消息可以由服务器端准备好,放入内存,不用针对每个请求去分配内存。

    长连接进来以后,需要解决的就是连接与线程的解耦问题,因为这样已经不是一个请求对应一个线程了,因为一个线程可能会操作多个连接,所以前面提到的非阻塞IO的引入是必然的。

  • 相关阅读:
    Java 第三阶段增强分析需求,代码实现能力【连接池】
    语法基础(函数)
    Encrypt 加密/解密
    安装、配置 Java JDK 和 JRE,并卸载自带 OpenJDK
    【HuggingFace文档学习】Bert的token分类与句分类
    WebRTC新增FFmpeg视频编解码模块
    (附源码)springboot在线考试系统 毕业设计 160935
    Mybatis 二级缓存(使用Ehcache作为二级缓存)
    源码解析springbatch的job是如何运行的?
    软件公司如何提升效能?研发团队的北极星指标
  • 原文地址:https://blog.csdn.net/xocupid/article/details/125601868