• (转)tomcat架构&session共享


    (二期)16、tomcat的整体架构与session共享方案

    【课程16】tomcat...共享.xmind47.6KB

    【课程16】tomcat...流程.xmind0.6MB

    【课程16】tomcat...组件.xmind0.6MB

    【课程16】tomcat...架构.xmind0.9MB

    【课程16】手写一...mcat.xmind0.1MB

    【课程16预习】tom...架构.xmind0.6MB

    课程目标:

    • 理解tomcat的基本组成与工作流程
    • 学会手动写一个迷你tomcat

    讲课顺序

    • tomcat简介
    • 整体架构
    • 架构组成了解
    • 工作流程
    • 两大组件
    • 手写一个简易tomcat
    • 优化方向
    • tomcat的session插件演示
    tomcat维基百科

    Tomcat是由Apache软件基金会下属的Jakarta项目开发的一个Servlet容器,按照Sun Microsystems提供的技术规范,实现了对Servlet和JavaServer Page(JSP)的支持,并提供了作为Web服务器的一些特有功能,如Tomcat管理和控制平台、安全域管理和Tomcat阀等。

    由于Tomcat本身也内含了一个HTTP服务器,它也可以被视作一个单独的Web服务器。

    http server与tomcat

    划重点:

    • http server与application server的不一样在哪?

    严格的来说,Apache/Nginx 应该叫做「HTTP Server」;而 Tomcat 则是一个「Application Server」,或者更准确的来说,是一个「Servlet/JSP」应用的容器。

    一个 HTTP Server 关心的是 HTTP 协议层面的传输和访问控制,所以在 Apache/Nginx 上你可以看到代理、负载均衡等功能。客户端通过 HTTP Server 访问服务器上存储的资源(HTML 文件、图片文件等等)。一个 HTTP Server 始终只是把服务器上的文件如实的通过 HTTP 协议传输给客户端。

    对于 Tomcat 来说,就是需要提供 JSP/Sevlet 运行需要的标准类库、Interface 等。为了方便,应用服务器往往也会集成 HTTP Server 的功能,但是不如专业的 HTTP Server 那么强大,所以应用服务器往往是运行在 HTTP Server 的背后,执行应用,将动态的内容转化为静态的内容之后,通过 HTTP Server 分发到客户端。

    HTTP服务器本质上也是一种应用程序——它通常运行在服务器之上,绑定服务器的IP地址并监听某一个tcp端口来接收并处理HTTP请求。

    Tomcat运行在JVM之上,它和HTTP服务器一样,绑定IP地址并监听TCP端口,同时还包含以下指责:

    • 管理Servlet程序的生命周期
    • 将URL映射到指定的Servlet进行处理
    • 与Servlet程序合作处理HTTP请求——根据HTTP请求生成HttpServletRequest对象并传递给Servlet进行处理,将Servlet中的HttpServletResponse对象生成的内容返回给浏览器。
    tomcat的整体架构
    整体架构

    划重点:

    • 主要两个组件是哪两个?
    • 两个重要组件的功能分别是啥?

    Tomcat中最顶层的容器是Server,代表着整个服务器,从上图中可以看出,一个Server可以包含至少一个Service,用于具体提供服务。

    Service主要包含两个部分:Connector和Container。从上图中可以看出 Tomcat 的心脏就是这两个组件,他们的作用如下:

    • 1、Connector用于处理连接相关的事,并提供Socket与Request和Response相关的转化; 
    • 2、Container用于封装和管理Servlet,以及具体处理Request请求

    其他组件说明:

    • Jasper:负责jsp页面的解析,jsp属性的验证,同时负责将jsp动态转换为java代码并编译成class。
    • Naming:资源管理,负责数据库连接池、EJB、mail等通过JDNI获取的内容。
    • Session:会话管理的组件
    • Logging:日志相关
    • JMX:性能监控等

    小结:

    (1)Tomcat中只有一个Server,一个Server可以有多个Service,一个Service可以有多个Connector和一个Container; 

    (2)Server掌管着整个Tomcat的生死大权; 

    (4)Service 是对外提供服务的; 

    (5)Connector用于接受请求并将请求封装成Request和Response来具体处理; 

    (6)Container用于封装和管理Servlet,以及具体处理request请求;

    两大组件 --- Connector

    划重点:

    • connector的工作流程?
    • connector有哪些优化手段?
    基本功能

    一个Connecter将在某个指定的端口上侦听客户请求,接收浏览器的发过来的 tcp 连接请求,创建一个 Request 和 Response 对象分别用于和请求端交换数据,然后会产生一个线程来处理这个请求并把产生的 Request 和 Response 对象传给处理Engine(Container中的一部分),从Engine出获得响应并返回客户。 

    Tomcat中有两个经典的Connector,

    • 一个直接侦听来自Browser的HTTP请求,
    • 另外一个来自其他的WebServer请求。

    HTTP/1.1 Connector在端口8080处侦听来自客户Browser的HTTP请求,AJP/1.3 Connector在端口8009处侦听其他Web Server(其他的HTTP服务器)的Servlet/JSP请求。 

    Connector 最重要的功能就是接收连接请求然后分配线程让 Container 来处理这个请求,所以这必然是多线程的,多线程的处理是 Connector 设计的核心。

    Connector 中具体是用ProtocolHandler 来处理请求的,不同的ProtocolHandler 代表不同的连接类型,比如, Http11Protocol 使用的是普通Socket 来连接的, Http 11 NioProtocol 使用的是NioSocket 来连接的。 

    ProtocolHandler 里面有3 个非常重要的组件: Endpoint 、Processor 和Adapter。

    • Endpoint用于处理底层Socket 的网络连接,
    • Acceptor 用于监昕请求
    • AsyncTimeout 用于检查异步request 的超时
    • Handler 用于处理接收到的Socket,在内部调用了Processor 进行处理。
    • Processor 用于将Endpoint 接收到的Socket 封装成Request,
    • Adapter 用于将封装好的Request 交给Container 进行具体处理。

    也就是说Endpoint用来实现TCP/IP 协议, Processor 用来实现HTTP 协议, Adapter 将请求适配到Servlet 容器进行具体处理。

    优化方向

    在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,就是在这里做优化。 可以说,Servlet容器处理请求,是需要Connector进行调度和控制的,Connector是Tomcat处理请求的主干 ,因此Connector的配置和使用对Tomcat的性能有着重要的影响。

    优化包括NIO/BIO模式、线程池、连接数等。

    优化一:指定protocol -- BIO、NIO、NIO2,APR。

    如果没有指定protocol,则使用默认值HTTP/1.1,其含义如下:

    • 在Tomcat7中,自动选取使用BIO或APR(如果找到APR需要的本地库,则使用APR,否则使用BIO);
    • 在Tomcat8中,自动选取使用NIO或APR(如果找到APR需要的本地库,则使用APR,否则使用NIO)。

    优化二:3个参数:acceptCount、maxConnections、maxThreads

    当客户端向服务器发送请求时,如果客户端与OS 完成三次握手建立了连接,则OS 将该连接放入accept 队列。

    对应三个节点,请求进入accept队列中,请求获取到链接,请求获得线程处理请求。

    <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" acceptCount="2" maxConnections="10" maxThreads="2"
               connectionTimeout="20000"
               redirectPort="8443" />
    • 1、acceptCount

    accept队列的长度;当accept队列中连接的个数达到acceptCount时,队列满,进来的请求一律被拒绝。默认值是100。

    acceptCount的设置,与应用在连接过高情况下希望做出什么反应有关系。如果设置过大,后面进入的请求等待时间会很长;如果设置过小,后面进入的请求立马返回connection refused。

    • 2、maxConnections

    Tomcat在任意时刻接收和处理的最大连接数。当Tomcat接收的连接数达到maxConnections时,Acceptor线程不会读取accept队列中的连接;这时accept队列中的线程会一直阻塞着,直到Tomcat接收的连接数小于maxConnections。如果设置为-1,则连接数不受限制。

    maxConnections的设置与Tomcat的运行模式有关。

    1. 如果tomcat使用的是BIO,那么maxConnections的值应该与maxThreads一致;
    1. 如果tomcat使用的是NIO,那么类似于Tomcat的默认值,maxConnections值应该远大于maxThreads。
    • 3、maxThreads

    请求处理线程的最大数量。默认值是200(Tomcat7和8都是的)。如果该Connector绑定了Executor,这个值会被忽略,因为该Connector将使用绑定的Executor,而不是内置的线程池来执行任务。

    优化三:线程池Executor

    Executor元素代表Tomcat中的线程池,可以由其他组件共享使用;要使用该线程池,组件需要通过executor属性指定该线程池。

    <Executor name="tomcatThreadPool" namePrefix ="catalina-exec-" maxThreads="150" minSpareThreads="4" />
    
    
    <Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" acceptCount="1000" />
    • name:该线程池的标记
    • maxThreads:线程池中最大活跃线程数,默认值200(Tomcat7和8都是)
    • minSpareThreads:线程池中保持的最小线程数,最小值是25
    • maxIdleTime:线程空闲的最大时间,当空闲超过该值时关闭线程(除非线程数小于minSpareThreads),单位是ms,默认值60000(1分钟)
    • daemon:是否后台线程,默认值true
    • threadPriority:线程优先级,默认值5
    • namePrefix:线程名字的前缀,线程池中线程名字为:namePrefix+线程编号

    linux下的一些有用命令

     netstat命令是一个监控TCP/IP网络的非常有用的工具

    #查看连接情况
    netstat -nat | grep 8080

    1、LISTEN

    服务侦听中(LISTEN)状态。

    2、ESTABLISHED

    ESTABLISHED的意思是建立连接。表示两台机器正在通信。

    3、CLOSE_WAIT

    对方主动关闭连接或者网络异常导致连接中断,这时我方的状态会变成CLOSE_WAIT 此时我方要调用close()来使得连接正确关闭

    4、TIME_WAIT

    我方主动调用close()断开连接,收到对方确认后状态变为TIME_WAIT。

    #获取tomcat的pid
    ps -ef | grep tomcat
    
    
    #查看该进程内有多少个线程
    #其中,nlwp含义是number of light-weight process。
    ps -o nlwp 32283
    
    
    #ps -eLo pid ,stat可以找出所有线程,并打印其所在的进程号和线程当前的状态
    #两个grep命令分别筛选进程号和线程状态;
    #wc统计个数
    ps -eLo pid,stat | grep 32283| grep running | wc -l

    (pid是32283)

    (该进程内有32个线程)

    (查看进程的运行情况,SL表示空闲)

    (筛选出运行中的线程,结果是0)

    两大组件 --- Container

    划重点:

    • container的每一个组件分别代表着项目的那些部分?
    • 注意组件之间的上下隶属关系
    组成部分

    Container用于封装和管理Servlet,以及具体处理Request请求,在Connector内部包含了4个子容器

    4个子容器的作用分别是:

    (1)Engine:引擎,用来管理多个站点,一个Service最多只能有一个Engine; 

    (2)Host:代表一个站点,也可以叫虚拟主机,通过配置Host就可以添加站点; 

    (3)Context:代表一个应用程序,对应着平时开发的一套程序,或者一个WEB-INF目录以及下面的web.xml文件; 

    (4)Wrapper:每一Wrapper封装着一个Servlet;

    tomcat的基本工作流程

    划重点

    • tomcat的工作流程是怎么样的?
    • 能否根据流程抽象一些重要步骤出来?

      

    如何确定请求由谁处理?

    当请求被发送到Tomcat所在的主机时,如何确定最终哪个Web应用来处理该请求呢?

    (1)根据协议和端口号选定Service和Engine

    Service中的Connector组件可以接收特定端口的请求,因此,当Tomcat启动时,Service组件就会监听特定的端口。在第一部分的例子中,Catalina这个Service监听了8080端口(基于HTTP协议)和8009端口(基于AJP协议)。当请求进来时,Tomcat便可以根据协议和端口号选定处理请求的Service;Service一旦选定,Engine也就确定。

    通过在Server中配置多个Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。

    (2)根据域名或IP地址选定Host

    Service确定后,Tomcat在Service中寻找名称与域名/IP地址匹配的Host处理该请求。如果没有找到,则使用Engine中指定的defaultHost来处理该请求。在第一部分的例子中,由于只有一个Host(name属性为localhost),因此该Service/Engine的所有请求都交给该Host处理。

    (3)根据URI选定Context/Web应用

    这一点在Context一节有详细的说明:Tomcat根据应用的 path属性与URI的匹配程度来选择Web应用处理相应请求,这里不再赘述。

    (HTTP请求过程)

     

    Tomcat Server处理一个HTTP请求的过程

    1、用户点击网页内容,请求被发送到本机端口8080,被在那里监听的Coyote HTTP/1.1 Connector获得。 

    2、Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的回应。 

    3、Engine获得请求localhost/test/index.jsp,匹配所有的虚拟主机Host。 

    4、Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机),名为localhost的Host获得请求/test/index.jsp,匹配它所拥有的所有的Context。Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为“ ”的Context去处理)。 

    5、path=“/test”的Context获得请求/index.jsp,在它的mapping table中寻找出对应的Servlet。Context匹配到URL PATTERN为*.jsp的Servlet,对应于JspServlet类。 

    6、构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet()或doPost().执行业务逻辑、数据存储等程序。 

    7、Context把执行完之后的HttpServletResponse对象返回给Host。 

    8、Host把HttpServletResponse对象返回给Engine。 

    9、Engine把HttpServletResponse对象返回Connector。 

    10、Connector把HttpServletResponse对象返回给客户Browser。

    一些相关图

    (配置文件server.xml)

     

    (tomcat的组件启动流程图)

    手写一个迷你tomcat

    代码作者:张丰哲  https://www.jianshu.com/p/dce1ee01fb90

    • 需求

    编写一个迷你tomcat

    • 需求分析

    tomcat是一个专门处理servlet的web容器。前提需要了解tomcat的工作原理与流程。

    一般容器处理流程是这样的,客户端发起请求,web容器接受请求,处理请求,然后把得到结果返回给客户端。

    所以提取要点如下:

    • 请求端口监控
    • 把请求的信息封装起来
    • 请求处理
    • 把响应结果封装起来

    因此至少需要以下几个类

    • Request 封装请求
    • Servlet 请求处理的Servlet
    • Response 封装响应结果
    • MyTomcat 启动监控和处理服务

    以上可以说我们已经设计出了一个超级简单的tomcat,但是这个tomcat有点局限性,只有一个servert, 也就是说,这个tomcat只能处理一个url,为了让服务器能够处理多个servlet,我们必须抽象封装servlet。 请求url与servelt是一一对应关系,所以,我们可以通过一个map把这些关系在启动过程中就初始化加载进来, 因此我们可以封装一个ServletMapping类用于封装映射关系, 然后初始化的话可以放在一个config类中统一管理(真实的tomcat是直接在web.xml配置映射关系) 因此又多了两个类

    • ServletMapping 封装url与Servlet的对应关系
    • ServletMappingConfig 配置项目的Servlet与Url关系,用于初始化

    然后,现在可以通过url找到对应的servlet了,每个Servrt的结构应该一直,因此抽象一个MyServlet类,固定处理的方法是service(); 后面添加业务处理的时候都要继承这个MyServlet抽象类。比如我要加一个HelloWorldServlet,对应url是"/hello", 我就需要显示MyServlet抽象类,编写业务逻辑,然后在ServletMappingConfig中配置映射关系。

    现在好了,我编写了很多url对应的servelt,如果找不到url,我应该抛出404错误,这个我们先跳过。 当我获取到url对应的servelt名称之后,现在又有一个问题来了,我如何去运行这个servlet的service()业务代码呢? 同学们可能说直接new HelloWorldServlet().service()不就行了么,这里涉及到一个问题,因为后面业务越来越多,需要的servelt也越来越多, 所以不可能全部通过一个个去判断然后new对象,这时候我们自然想到可以通过反射方式获取servlet实例,然后调用service(); 所以在tomcat的处理请求阶段,我们可以通过反射方式去调用业务逻辑。

    至此。需求分析完毕。

    具体代码请看具体项目。

    为什么要实现session共享

    我们使用单台Tomcat的时候不会有共享sesssion的疑虑,只要使用Tomcat的默认配置即可,session即可存储在Tomcat上。

    但是随着业务的扩大,增加Tomcat节点构成Tomcat集群大势所趋,分布式带来了增加更大规模并发请求的优势,但是也随之到来了一个问题,每个Tomcat只存储来访问自己的请求产生的session,如果Tomcat-A已经为客户端C创建了会话session,那么Tomcat-B并不知道客户端已与集群中的Tomcat-A产生了会话,在访问时就会为C再创建一份session,如果是基于session的验证会话权限的接口(如用户登录认证后才可访问的数据接口),将会导致在访问集群中不同节点的时候重复认证。session的不共享导致原来的会话管理机制在Tomcat集群中无法工作。

    tomcat中的session是如何工作的

    深度好文:https://www.cnblogs.com/kismetv/p/7228274.html

    tomcat集群环境下实现session共享的几种方式

    第一种:粘性session,基于nginx的ip-hash,当前用户的请求都集中定位到一台服务器中,这样单台服务器保存了用户的session登录信息。

    第二种:session复制共享:sessionreplication,如tomcat自带session共享,主要是指集群环境下,多台应用服务器之间同步session,使session保持一致,对外透明。

    第三种:基于cache DB缓存的session共享,比如基于 memcache/redis缓存的 session 共享。

    注意点,只支持tomcat7,tomcat8以上不支持。

    实现过程

    第一步:首先导入相关的jar包到tomcat的lib文件夹中,如redis和tomcat关联的jar包,redis的包等。

    commons-pool2-2.2.jar0.1MB

    tomcat-redis-session-m....0.0.jar21KB

    jedis-2.5.2.jar0.3MB

    第二步:配置tomcat。打开context.xml,在<Context>标签内添加如下代码,其中,host是redis的ip,port是端口。:

    context.xml1.7KB

    <Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
     <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager" host="localhost"  port="6379" database="0" maxInactiveInterval="60" />
    
    

    第三步:接下来就是可以直接把项目放在webapp下面运行项目啦,你会发现,tomcat的session信息已经存到redis中。多个tomcat可以改变端口,然后用nginx做下负载均衡,可以随时切换tomcat,然后session已经共享了。

    index.jsp0.6KB

    • 试验大环境:(分别8080,8081端口)

    https://pan.baidu.com/s/1IXyXGgkGkHqLNKmyewGF1A

  • 相关阅读:
    activiti5.13工作流系列(一)-初识
    java通过http调用服务
    Eclipse快捷键大全(转载)
    java作用域-转
    ajax两种不同方式的不同结果
    MySQL索引背后的数据结构及算法原理 --转
    解决json包含html标签无法显示的问题
    js下的sleep实现
    json使用
    比较靠谱的网页分页代码-转
  • 原文地址:https://www.cnblogs.com/free-wings/p/9841678.html
Copyright © 2020-2023  润新知