Servlet+tomcat

serverlet

定义:是一个接口,定义了java类被浏览器(tomcat识别)的规则

所以我们需要自定义一个类,实现severlet接口复写方法

通过配置类实现路径和servlet的对应关系

执行原理

  1. 当用户在浏览器输入路径,会解析请求的URL路径,获取访问的serverlet资源路径
  2. 查找web.xml文件,是否又对应的标签体内哦让那个
  3. 如果有,则在找到对应的的全类名
    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)这个方法用获得转发器对象

  1. 使用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{
    @Override
    public 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();
        }

}
相关推荐
小梦白35 分钟前
RPG7.准备GAS的工作
java·开发语言
武昌库里写JAVA37 分钟前
【iview】icon样式
java·开发语言·spring boot·学习·课程设计
不太可爱的叶某人39 分钟前
【学习笔记】深入理解Java虚拟机学习笔记——第1章 走进Java
java·jvm·笔记·学习
颇有几分姿色1 小时前
Spring Boot 实现多种来源的 Zip 多层目录打包下载(本地文件&HTTP混合)
java·spring boot·后端
百锦再1 小时前
Android Studio中OpenCV应用详解:图像处理、颜色对比与OCR识别
android·java·图像处理·opencv·kotlin·app·android studio
-XWB-1 小时前
【Java】打印运行环境中某个类引用的jar版本路径
java·开发语言
Brookty2 小时前
【Java学习】通配符?
java·学习
fhgfyrsg2 小时前
【无标题】
java
佩奇的技术笔记3 小时前
Java学习手册:关系型数据库基础
java·数据库·学习
forestsea3 小时前
Maven 实现多模块项目依赖管理
java·maven