Servlet+tomcat
serverlet
定义:是一个接口,定义了java类被浏览器(tomcat识别)的规则
所以我们需要自定义一个类,实现severlet接口复写方法
通过配置类实现路径和servlet的对应关系
执行原理
- 当用户在浏览器输入路径,会解析请求的URL路径,获取访问的serverlet资源路径
- 查找web.xml文件,是否又对应的标签体内哦让那个
- 如果有,则在找到对应的的全类名
4.tomcat会将字节码文件加载进内存,并创建器对象
生命周期
init
方法:在Servelet被创建的时候执行,只会执行一次
serverce
提供服务的方法,每次提供方法的时候就会执行
destory
正常在服务器被关闭的时候会执行,只会执行一次
创建:默认情况下第一次被访问的时候被创建,或者通过配置文件修改为启动服务器的时候被创建-<load-on-startup>
,并且serverlet的类是单列的,是共享资源,所以不要定义成员变量
提供服务
销毁:在serverlet被销毁之前的时候执行,用来释放资源
SverlectConfig
获得serverlet的配置信息的
getServletConfig
获得serverlet的信息
可以使用 @WebServelet
的注解配置去解读
ideal与tomcat的关系
会在日志中的server.xml
里面修改对应的tomcat的文件
一个项目会存储在工作空间项目还有会存在tomcat的项目
tomcat真正的访问的是tomcat的项目这个项目对应的是工作空间的web目录下的所有还有一个class目录下里面存储的又工作空间编译生成的class类,并且在工作目录下中的WEB-INF下的资源无法部署在tomcat中
体系结构
静态资源:所有用户访问后,得到的结果都是一样的,称为静态资源——可以直接返回给浏览器(有静态资源解析的引擎)
动态资源:所有用户访问后,得到的结果都是不一样的,称为动态资源——需要先转化成静态资源,然后返回给浏览器(响应)
URI:资源名称 共和国
URL:资源路径+资源名称 中华人民共和国
Request请求转发
服务器内部的资源跳转步骤
1.RequestDispatcher getRequestDispatcher(String path)
这个方法用获得转发器对象
2. 使用RequstDispatcher对象进行转发:forward(ServletRequest request, ServletResponse response)
// 1. 获取转发器
RequestDispatcher requestDispatcher = request.getRequestDispatcher("转发的目标地址URI");
// 2. 使用转发器转发
requestDispatcher.forward(request, response);
特点:
浏览器地址栏路径不发生改变
只能访问当前服务器内部的URI,不能访问外部的资源
转发是一次请求,所以request对象和response对象都是同一个
共享数据
域对象:有作用范围的对象,在一定范围共享数据
默认域为一次请求
方法:
setAttribute(String name, Object value)
设置域数据在转发之前设置
getAttribute(String name)
获取域数据
removeAttribute(String name)
移除域数据
所以跨域问题,其实就是发送了两次请求,并且希望携带cookie,所以需要浏览器通过实现跨域问题,否则浏览器默认不允许跨域,防止CSRF攻击
tomcat
tomcat 自己的Main方法在startup文件夹之下boot_start
架构
TOMCAT不会直接调用servlet,而是调用servlet容器,让容器调用接口。容器先定位再判断是否存在servlet类,如果没有就加载一个然后调用服务再返回
设计是通过两个核心连接器(connector)和容器(cpmyaomer)来实现,连接器对外交流,容器对内部处理
连接器
架构:Coyote 作为独立的模块,只负责具体的协议和IO相关的操作
IO 模型
NIO:同步非阻塞IO,一个线程可以处理多个请求
NIO2:异步非阻塞IO,一个线程可以处理多个请求
APR:本地库,可以提高性能
一个容器可以对应多个连接器
可以通过调用适配器的方法将request转化为servletRequest的方法
EndPoint:是一个处理TCP请求的组件,是将具体的Socket接受和发送处理器
Processor:是对TCP/IP 传输过来的字节流进行解析即实现HTPP/AJP的解读成为一个Request对象
coyote:是连接器
Catalina:是容器的实现-实现具体的逻辑处理
Catalina
执行的原理是:
由Catalina开始解析配置文件,从而管理server,而server表示的是整个服务器。服务器下面有多个服务,每个服务可以连接多个连接器和一个容器
Container
存在四层结构是父子关系
Engine:引擎,管理多个虚拟主机/虚拟站点,一个容器内只能有一个引擎
Host:虚拟主机/站点——多个网站
Context:Context是特定Web应用的表示,就是一个项目
Wrapper:Wrapper是特定Servlet的表示,一个Wrapper代表一个Servlet,最底层的容器
tomcat启动流程
首先调用BootStrap的main()方法,对各个类进行初始化,初始化之后对各个类进行启动
父祖教调用自身并且启动子组件,最后启动PritocolHandler组件,进行监听和处理请求的
Lifecycle:是生命周期组件,可以监听组件的启动和关闭,并且可以调用组件的启动和关闭方法
请求处理流程
发送的请求首先经过连接器,连接器解析请求,然后交给引擎,引擎会解析本地的Host请求/DNS服务器去找到对应的IP地址,然后就是自己写的服务中的配置找到对应的映射即Context
EndPoint会启动Socket去接受到一个请求然后交给Processor去解析,然后交给CoyoteAdapter进行处理此时作为request请求,
他会先去Mapper去找到请求的路径映射映射,然后转化为Engina所需要的request请求,交给Host再到Context再到Wrapper
他就会构造一个过滤器链然后再执行各个过滤器最后执行servlet
pipeline:管道的作用就是传输valve——松耦合,责任链
根据请求方式决定执行哪个方法
Jasper
因为服务器最终响应到浏览器的最终只能是静态页面,所以不包含任何java相关的语法。
当浏览器发送的是一个jsp的请求的时候,服务器会先根据自己的JspServlet去寻找对应的index.jsp文件然后将他转化为java文件,在经过编译成为class的可执行文件,就会直接返回response给浏览器其中含有html的页面
运行时编译:即class文件一开始没有是变运行变编译的
预编译:一次性将所有的JSP页面编译完成,在启动tomcat的时候就编译完成
JSP编译原理
生成的java类之后会编译成为class文件里面就会包含的有html页面生成的函数
服务器的配置文件
server.xml文件(核心配置文件)——包含了容器的所有配置
<Listener /> 表示监听器
<Service > 表示服务——创建Service实现
<Executor /> 表示共享线程池,多个connector可以共享一个线程池,默认情况下各个使用的是自己线程池
<Connector /> 表示连接器,可以通过配置Connector的配置去——里面有一个executor属性去指定共享线程池名称
<Engine defaultHost="www.tomcat.com" name="Catalina"> 表示引擎,默认访问主机是localhost,name是引擎的名称 <Host appBase="webapps" name="www.tomcat.com" appBase="webapps" unpackWARs="true" autoDeploy="true">
webapps 表示相对目录下的文件目录
name 表示当前Host通用的网络名称,必须与DNS服务器上的注册信息一致。
appBase 表示当前Host的根目录,默认是webapps
unpackWARs="true" 表示是否解压当前下的WAR包,如果为true则将WAR包解压为文件夹,如果为false则不进行解压,但是仍然可以使用war包内的
autoDeploy="true" 表示是否自动部署,如果为true则自动部署,如果为false则不自动部署
修改name中的需要对应的dns或者本地host去替代 <Context docBase="webapps/ROOT" path="/" reloadable="true"/>
docBase 表示当前Context的根目录,默认是webapps/ROOT,就是表示访问的是哪一个文件夹
path 表示当前Context的访问路径,默认是/,服务器的资源路径
reloadable="true" 表示是否自动加载,如果为true则自动加载,如果为false则不自动加载
</Host>
</Engine> </Service>
tomcat_users.xml文件——是对用户进行管理
web.xml文件
Web 应用配置
<context-param> 表示上下文参数,在这里设置的参数可以再servletContext中获取利用req.getServletContext().getAttribute("key")获取
<param-name> 表示参数名称 </param-name>
<param-value> 表示参数值 </param-value>
</context-param>
会话配置
浏览器和web服务器之间建立会话,会话是基于cookie的,cookie是基于session的
<session-config> 表示会话配置
<session-timeout> 表示会话超时时间,单位为分钟,默认是30分钟
</session-timeout>
<cookie-config> 表示cookie配置
<cookie-name> 表示cookie名称根据名称查sessionId
<domain> 表示cookie的域名,默认是当前域名
<http-only> 表示cookie是否只能通过http协议访问,默认是true,不能跨域
<secure> 表示cookie是否只能通过https协议访问,默认是false
<cookie-max-age> 表示cookie最大存活时间,单位为秒,默认是-1,表示浏览器关闭后cookie失效
<tracking-mode> 表示cookie的跟踪模式,默认是COOKIE,表示cookie跟踪模式,可以设置为COOKIE或者URL
</cookie-config>
</session-config>
可以通过application.getSession()获取session对象
servlet配置
主要包括servlet的配置和servlet-mapping的配置
<servlet>
<servlet-name> 表示servlet名称
<servlet-class> 表示servlet类
<load-on-startup> 表示servlet的加载顺序,默认是0,表示在启动服务器的时候加载,如果为负数表示在服务器启动后加载,如果为正数表示在服务器启动前加载
<enabled> 表示servlet是否启用,默认是true,表示启用,如果为false表示禁用
</servlet>
<servlet-mapping>
<servlet-name> 表示servlet名称
<url-pattern> 表示servlet的访问路径
</servlet-mapping>
监听器
<listener>
过滤器
拦截请求,用于认证、日志、加密、数据转化
<filter>
<filter-name> 表示过滤器名称
<filter-class> 表示过滤器类
<async-supported> 表示过滤器是否支持异步,默认是false,表示不支持,如果为true表示支持
</filter>
<filter-mapping>
<filter-name> 表示过滤器名称
<url-pattern> 表示过滤器的访问路径
</filter-mapping>
欢迎页面和错误页面配置
<welcome-file-list>
<welcome-file> 表示欢迎页面,默认的全局的欢迎页面
</welcome-file>
</welcome-file-list>//由于资源和网络占用会出现异常,不希望给用户查看
<error-page>
<error-code> 表示错误代码(404,500,403) </error-code>
<location> 表示错误页面 </location>
</error-page>
Tomcat管理配置
在tomcat-users.xml文件中配置用户名和密码
<role roleName="manager-gui">
<role roleName="admin-gui">
<user username="admin" password="admin" roles="manager-gui,admin-gui"/>
提供一个虚拟主机的管理页面
相对APP应用程序进行管理
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-jmx"/>
<role rolename="manager-status"/>
<user username="admin" password="admin" roles="manager-gui,manager-script,manager-jmx,manager-status"/>
JVM 的配置
JVM内存分配
集群
单个tomcat的承载能力是有限的,单排tomcat不能满足这需求,所以有一个nginx的负载均衡器,将请求分发到多个tomcat上,从而提高系统的吞吐量和可靠性。
负载均衡的测率:
轮询:将请求均匀的分配到多个tomcat上
加权轮询:根据tomcat的性能不同,分配不同的权重,性能好的分配的权重高,性能差的分配的权重低
ip_hash:根据ip地址分配,同一个ip地址的请求会分配到同一个tomcat上
集群的问题:
session共享问题
1.通过ip_hash方式,同一个请求访问的同一个ip所以session是共享的
2.session复制:将session复制到多个tomcat上,从而实现session共享,通过tomcat的广播机制——通过添加集群的配置。
在engine下添加集群的配置
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> <Manager className="org.apache.catalina.ha.session.DeltaManager"expireTime="10000"/>
</Cluster>
在web.xml中添加<distributable/>
标签,表示当前的web应用是可分布的
3.单点登录——SSO
Tomcat的安全
配置安全
删除所有webapps下的项目
关闭删除用户所有权限
关闭/修改8005端口,修改关闭之类
设置错误页面
将密钥库文件复制到tomcat/conf目录下
修改server.xml文件
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"connectionTimeout="20000"scheme="https"secure="true"SSLEnabled="true"><SSLHostConfig certificateVerification="false"><Certificate certificateKeyFile="conf/localhost-rsa.jks"certificateFile="conf/localhost-rsa.jks"//密钥库文件certificateChainFile="conf/localhost-rsa.jks"//证书链文件type="RSA" // 密钥库类型/></SSLHostConfig>
</Connector>
性能测试
yum install -y httpd-tools
ab -v //查看版本
上传war包
并且移动到webapps下-里面剩余的其他文件全部删除
ab -n 1000 -c 100 -p data.json http://192.168.1.100:8080/项目名/index.jsp
-p表示post的是一个json格式文件
-n表示总共请求次数
-c表示并发数
Jconslo可以查看远程的垃圾回收机制
连接器
maxConnections——当达到最大的连接数,服务器接受但不会处理更多的请求额外的请求会阻塞,知道连接数低于最大连接数
maxThreads——当达到最大的线程
acceptCount——最大的排队等待数量,就是maxConnections之后
WebSocket
是一个在浏览器和服务器之间建立一个不受限制的双向通信的协议
传统的的请求响应功能,可以使用轮询的方式,但是服务器的压力比较大。
websocket就不是Http这样请求响应的协议,而是一种全双通信,服务和客户端之间相互通信,基于http协议
相当于对http请求进行升级,相当于用ws作为开头地址
使用两种方式定义Endpoint:
第一种编程式子,继承javax.websocket.Endpoint类
第二种注解式子,使用@ServerEndpoint注解
@OnOpen-记录session,httpSession,在线会话,广播消息
@OnMessage-解析消息判定收件人,发送消息
@OnClose-销毁session,httpSession,在线会话,广播消息
相当于前端也有一个websocket的类然后也有特定的session只需要通过send指令就可以发送过去了
服务端始终只是使用一个类,通过使用session保存独有的websocket的所有信息,
httpSession表示在客户端与服务端传输的标记,onlineUser表示的就是用来确定此时请求的用户是谁
通过构造一个websocket的类,然后继承Endpoint类,然后重写方法
public class MyEndpointConfig extends ServerEndpointConfig.Configurator{@Overridepublic void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {HttpSession httpSession = (HttpSession)request.getHttpSession(); config.getUserProperties().put(HttpSession.class.getName(),httpSession);}
}@ServerEndpoint("/websocket",configuration=MyEndpointConfig.class) //请求路径
public class ChatSocket {private static Session session; //用来表示记录websocket的session信息private static HttpSession httpSession; //用来表示当前登录后的用户的httpSession private static Map<HttpSession,Chatsocket> onlineUsers = new HashMap<>(); //用来表示记录的session@OnOpen //表示打开会话的时候会触发的方法 //这里面的config就是用来获取HttpSession的,所以每次有人登录就会触发一次public void onOpen(Session session , EndpointConfig config){this.session = session;HttpSession httpSession = (HttpSession)config.getUserProperties().get(HttpSession.class.getName());this.httpSession = httpSession;if(httpSession.getAttribute("user") != null){onlineUsers.put(httpSession,this);}String name = getName(onlineUsers);// session.getBasicRemote().sendText(name);//表示发送单条数据给当前的会话 broadcast(name);//表示发送广播数据给所有的会话 }private String broadcast(String name){for(HttpSession httpSession : onlineUsers.keySet()){onlineUsers.get(httpSession).session.getBasicRemote().sendText(name);}}@OnMessage //表示收到消息的时候会触发的方法 public void onMessage(String message,Session session){//1.获取客户端的消息并且解析 Map<String,String> messageMap = JSON.parseObject(message,Map.class); String fromName = messageMap.get("fromName");String toName = messageMap.get("toName");String content = messageMap.get("content");if(toName != null && !toName.equals("")){return ;}String message = MessageUtil.getMessage(fromName,content); singleMessage(message,fromName,toName);
}private String singleMessage(String message,String fromName,String toName){ for(HttpSession httpSession : onlineUsers.keySet()){if(httpSession.getAttribute("user").equals(toName)){boolean flag = true;}}if(flag){for(HttpSession httpSession : onlineUsers.keySet()){if(httpSession.getAttribute("user").equals(toName)||httpSession.getAttribute("user").equals(fromName)){onlineUsers.get(httpSession).session.getBasicRemote().sendText(message);}}}}@OnClose //表示关闭会话的时候会触发的方法 public void onClose(Session session,CloseReason closeReason){onlineUsers.remove(httpSession);}@OnError //表示发生错误的时候会触发的方法 public void onError(Session session,Throwable throwable){throwable.printStackTrace();}}