Spring整合tomcat的WebSocket詳細(xì)邏輯(圖解)
主要解決存在的疑問
- 為什么存在2種spring整合websocket的方式,一種是使用@ServerEndpoint注解的方式,一種是使用@EnableWebSocket注解的方式,這2種有什么區(qū)別和聯(lián)系?可以共存嗎?它們實(shí)現(xiàn)的原理是什么?它們的各種配置到底是什么意思?@EnableWebSocket是如何完成配置的?
- 在服務(wù)端和客戶端建立websocket連接的時(shí)候,如何做認(rèn)證?不能讓任意的客戶端連接到websocket服務(wù)端,而且不應(yīng)該在建立websocket連接之后再認(rèn)證,而是應(yīng)該在握手的時(shí)候,就去做認(rèn)證,該如何實(shí)現(xiàn)?
spring、tomcat是如何配合完成websocket
這篇文章覺得不錯(cuò),原文地址:spring、tomcat是如何配合完成websocket
綜述
像IM這一類web系統(tǒng),需要有機(jī)制知道是否有新消息,沒有websocket前主要靠輪訓(xùn)。
輪訓(xùn)頻率設(shè)得過高,有效輪訓(xùn)率低,不僅消耗網(wǎng)絡(luò)資源,還占用cpu資源;輪訓(xùn)頻率設(shè)得過低,又會(huì)造成消息延時(shí)較大。
為此誕生了websocket,消息可以由服務(wù)端主動(dòng)推送到客戶端,不僅實(shí)時(shí)性高,效率也是拉滿。
為了盡量減少對(duì)現(xiàn)有系統(tǒng)進(jìn)行改造,websocket是在建立在http的基礎(chǔ)之上的,這樣不僅可以復(fù)用http的端口,服務(wù)端及客戶端的改造都更小。
websocket協(xié)議
- 握手階段:如上所述websocket是基于http,主要是握手階段仍使用http協(xié)議,當(dāng)HTTP處理器發(fā)現(xiàn)頭部帶“Upgrade: websocket”,則認(rèn)為連接是要升級(jí)到websocket的協(xié)議;如果支持websocket則返回允許升級(jí)到websocket的響應(yīng)。
- 通信階段:客戶端接受到服務(wù)端允許升級(jí)協(xié)議的響應(yīng)后,則認(rèn)為握手完成,后續(xù)都以websocket格式發(fā)送報(bào)文。
websocke報(bào)文格式如下:
websocket協(xié)議本身很簡單,各個(gè)字段的含義可以參考其他文章,這里不再詳述,本文主要分析tomcat如何實(shí)現(xiàn)websocket以及spring boot如何基于tomcat集成websocket。
本文基于tomcat NIO模式進(jìn)行分析。
分析前可以大致梳理一下,有哪些關(guān)鍵點(diǎn)。
- tomcat NIO模式下,會(huì)為每個(gè)請(qǐng)求分配一個(gè)線程進(jìn)行處理,websocket是長連接,這個(gè)線程是否會(huì)與websocket連接綁定,而一直被同一個(gè)websocket占有。
- websocket協(xié)議升級(jí)是在那個(gè)點(diǎn)觸發(fā)的。
- tomcat是如何在http的基礎(chǔ)之上支持websocket
- tomcat的websocket是如何暴露接口給spring去集成的。
tomcat NIO模式下線程模型
各個(gè)線程的初始化詳見org.apache.tomcat.util.net.NioEndpoint#startInternal
- Acceptor線程主邏輯用于調(diào)用accept方法接受請(qǐng)求,生成socket并在Poller中注冊(cè)
- Poller線程主邏輯是調(diào)用select方法,獲取可讀寫的socket,并創(chuàng)建SocketProcessor,并丟給Processor線程進(jìn)行處理。
- Processor線程主要邏輯是根據(jù)協(xié)議解析socket中的數(shù)據(jù),并調(diào)用servlet容器進(jìn)行業(yè)務(wù)處理。
tomcat NIO模式下請(qǐng)求處理流程
本文不詳細(xì)解析tomcat的內(nèi)部實(shí)現(xiàn),http協(xié)議本身很簡單,沒有握手的過程,僅僅只是簡單的請(qǐng)求/應(yīng)答。理論上,只需要 接受請(qǐng)求 -> 解析報(bào)文 -> 丟給servlet處理 這幾個(gè)過程。
tomcat為了實(shí)現(xiàn)更豐富的功能抽象出Engine,Host,Context等概念,為了便于理解我們簡化一下tomcat模型,我們將CoyoteAdapter到StandardWrapper當(dāng)成servlet容器內(nèi)部的行為,將其合并為Servlet Container,其功能就是將request路由到正確的servlet進(jìn)行處理。
那么請(qǐng)求的處理過程可以簡化成文章來源:http://www.zghlxwxcb.cn/news/detail-729767.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-729767.html
- SocktProcessor主要做協(xié)議解析,將socket中的字節(jié)流轉(zhuǎn)換成一幀幀HTTP報(bào)文
- servlet container主要請(qǐng)求路由,跟url信息轉(zhuǎn)發(fā)到正確的servlet進(jìn)行處理
- FilterChain這個(gè)是servlet規(guī)范中的FilterChain,在請(qǐng)求在交給servlet處理前,會(huì)經(jīng)過一系列的Filter進(jìn)行處理
- servlet這個(gè)就不解釋了。
websocket協(xié)議升級(jí)涉及的點(diǎn)
- websocket與http有點(diǎn)區(qū)別,websocket是長連接(雖然http也可以配置 keepalive在實(shí)現(xiàn)長連接,但是服務(wù)端并不保證客戶端一直沒有發(fā)請(qǐng)求的情況下仍然保持連接),正常情況下只有雙方其中一端顯式關(guān)閉連接,才能結(jié)束這個(gè)socket。所以SocktProcessor要了解到該socket連接是有升級(jí)過協(xié)議的;不僅如此因?yàn)閣ebsocket是基于http,共用端口,所以SocktProcessor也要感知到socket連接是升級(jí)過協(xié)議,這樣完成握手后的請(qǐng)求,要由websocket的協(xié)議處理器去做協(xié)議解析。
- websocket協(xié)議本身并沒有鑒權(quán)等設(shè)計(jì),這個(gè)需要委托給握手階段的http報(bào)文的處理。為此握手階段的報(bào)文要當(dāng)成正常的http協(xié)議處理,需要走所有已配置的Filter;所以握手階段的處理一定會(huì)走到servlet容器里面,而且spring應(yīng)該會(huì)注冊(cè)專門的handler去處理升級(jí)前的http報(bào)文。
- servlet處理模型與websocket不一致,所以框架里面應(yīng)該會(huì)有注冊(cè)websocket的handler的邏輯。
到了這里,關(guān)于Spring整合tomcat的WebSocket詳細(xì)邏輯(圖解)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!