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();}}