(转)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

猜你喜欢

转载自www.cnblogs.com/free-wings/p/9841678.html