前言
最近内部使用的web管理后台系统中新增了一个报销单审批的功能,由员工发起报销申请,然后首先直属主管进行审批,主管审批通过后流程就到了经理那里,经理审批通过后流程再转到财务那里。
本来这功能无非就是些CRUD的功能,没啥难度,但是架不住产品爱搞事啊!产品提出了一个需求:每个审批操作都需要给下一级处理人主动发送一个通知。什么意思呢?比如直属主管审批通过后,系统需要在后台给经理主动推送一条消息,提醒他有报销单需要他处理。
那么web项目要怎么实现服务器向浏览器推送消息呢?
目录
- web项目实现消息推送有哪几种方式?
- WebSocket是怎么建立起连接的?
- Spring Boot集成WebSocket实现消息推送
- 总结
web项目实现消息推送有哪几种方式?
传统的web项目浏览器与服务器基于HTTP协议进行交互,一次交互流程总是先由浏览器发送一个HTTP请求到服务器,服务器收到请求后再返回响应数据。
![e4610996b6fcf3cfbacbf18ef6bc000c.png](https://i-blog.csdnimg.cn/blog_migrate/bacd97846236dda01fbe5f645029d276.jpeg)
浏览器可以主动发起请求,服务器只能被动进行响应,服务器无法主动联系浏览器,这是HTTP最显著的特征之一。
这样说服务器没有办法将消息主动推送到浏览器了?其实不然,换个思路既然服务器不能主动推送消息,那么就浏览器主动去取好了。
1、ajax轮询
ajax轮询原理很简单,就是让浏览器每隔几秒钟就发送一次请求到服务器询问有没有新消息。
![c3de0b71b7b1d71b630635459216496e.png](https://i-blog.csdnimg.cn/blog_migrate/7082fb615f5d739db0f9b41dc3abd20e.jpeg)
情景再现:
浏览器:服务器老哥,有没有新消息?(发起请求)
服务器:没有新消息(响应)
过了几秒……
浏览器:服务器老哥,有没有新消息?(发起请求)
服务器:没有新消息(响应)
又过了几秒……
浏览器:服务器老哥,我又来啦!有没有新消息?(发起请求)
服务器:没有,你好烦啊!(响应)
重复这个……
2、Long Polling
Long Polling的原理其实和ajax轮询类似,但是做了一些优化,Long Polling采用的是阻塞的方式。浏览器发起请求到服务器,此时如果服务器没有新消息就会一直阻塞不返回响应,直到有新消息才返回,浏览收到返回后,立即再次发起一个新的请求到服务器并继续等待响应。
![2c19a095311138f031ce4caba431aaef.png](https://i-blog.csdnimg.cn/blog_migrate/698c87dc859b5047d01aff4d3427b6c4.jpeg)
场景重现:
浏览器:服务器老哥,有没有新消息?没有新消息我就在这一直等(请求)
服务器:目前没有新消息,你先等着……(连接处于阻塞状态)
服务器:来新消息了!(响应)
浏览器:服务器老哥,有没有新消息?没有新消息我就在这一直等(请求)
重复这个过程……
不管是ajax轮询还是Long Polling,了解流程之后可以看出这两种方式都算不上完美的方案。ajax轮询定时发请求,如果这个时候服务器没有新消息,那么这次连接做的就是无用功;Long Polling采用阻塞的方式虽然减少了连接的开销,但是服务器维持连接同样需要消耗资源,如果连接并发很大,服务器将会承受巨大压力。
WebSocket的出现就是为了彻底解决服务器向浏览器推送消息的问题。
WebSocket是HTML5新增的一种通信协议。WebSocket支持双向通信的协议,它和HTTP协议一样通过TCP连接来进行数据传输。但是不同于HTTP协议的是基于WebSocket浏览器和服务器只需要完成一次握手,两者之间就直接建立起长连接,并可进行双向数据传输。
WebSocket是怎么建立起连接的?
WebSocket协议虽然跟HTTP协议是两个协议,但其实两者并不是毫无关系,我更喜欢把WebSocket协议看作是对HTTP协议的一个补充,因为WebSocket建立连接的过程就是借助HTTP协议来完成握手的。
![022516ecd9e3d9c1a2f9d95e03e82111.png](https://i-blog.csdnimg.cn/blog_migrate/3202672d3f93d445520431ff9fb9e956.jpeg)
WebSocket建立连接流程:
- 浏览器向服务器发出一个HTTP请求,请求中除了包含常规的信息,还在请求头中表明希望将本次通信的HTTP协议升级为WebSocket协议。这个升级请求协议的过程就叫做握手。
- 服务端收到升级请求后会验证客户端发送的请求头信息,如果符合规范则同意将HTTP协议替换成 WebSocket协议,并向浏览器返回升级成功的信息。
- 双方建立起WenSocket连接就可以互相推送信息了。
在浏览器中看一下实际建立WebSocket连接的过程:
![55e50fc092faea30cd6ba961ae39d932.png](https://i-blog.csdnimg.cn/blog_migrate/f1f9def4ca6b0a8035a547d2bbfbadec.jpeg)
浏览器发送HTTP握手请求阶段:
- 请求头Connection:Upgrade表明这是一个升级请求;
- 请求头Upgrade:websocket告诉服务器将请求升级成websocket协议;
- 请求头Sec-WebSocket-Version:13表明使用websocket协议的版本为13;
- 请求头Sec-WebSocket-Key校验key,防止恶意攻击者伪造请求;
服务器升级协议返回响应阶段:
- 响应状态码101表明该次升级请求被正确处理;
- 响应头Connection:Upgrade和Upgrade:websocket表明协议已升级成websocket;
- 响应头Sec-WebSocket-Accept服务端根据请求头Sec-WebSocket-Key计算得出,用于检验;
Spring Boot集成WebSocket实现消息推送
因为WebSocket是比较底层的协议,类似于TCP,我们开发web应用并不会直接使用TCP,而是在TCP之上加了一层请求响应模型即HTTP协议。
同样的,在Spring Boot项目中集成WebSocket,我们也不会直接使用WebSocket,而是使用STOMP协议。
STOMP之于WebScoket就像HTTP之于TCP,是一种基于WebScoket的高层协议,STOMP提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。
集成步骤:
1、在pom.xml文件里添加websocket依赖。
![0aa48b90c392bf3d188b7af5e4c8d546.png](https://i-blog.csdnimg.cn/blog_migrate/b56ae6c4212adf8a83bc6404dd34ca82.jpeg)
2、提供一个websocket配置文件:
![dc48eaf973176497f5ca9c600453eb7c.png](https://i-blog.csdnimg.cn/blog_migrate/9a43d56d11cd171dc45e6c2edbf85e32.jpeg)
- 对外暴露/ws端点作为客户端连接websocket的入口;
- 定义了客户端可以订阅的主题前缀;
3、客户端页面:
![359958e8b7052a173603978c6fdef4b0.png](https://i-blog.csdnimg.cn/blog_migrate/5d5b2b70c89a6c18754934b6d7bb59ad.jpeg)
- 通过STOMP协议发起webscoket连接;
- 订阅系统广播和个人消息两个主题;
4、 服务器推送消息方法:
![7648c62fd98ef7933692f6c57720f46b.png](https://i-blog.csdnimg.cn/blog_migrate/3580a0e60140f2ad7fa5a8b7d61ddb7e.jpeg)
- 所有人只是要连上了服务器都能收到广播消息;
- 发给指定用户的消息只有指定个人能收到;
5、运行项目,浏览器打开两个页面分别以用户a和用户b两个不同的身份连接websocket,当服务器发送广播消息时,用户a和用户b都收到了消息;当服务器发送一条给用户a的消息时只有用户a收到了消息。
![1ddb7972d53ff559919e7140c5788e6a.png](https://i-blog.csdnimg.cn/blog_migrate/9014ceae6c91bb90c5e6e1bf0ce5e506.jpeg)
总结
服务器向客户端推送数据,可以通过HTTP协议以ajax轮询和Long Polling等客户端主动拉数据的方法实现,不管是ajax轮询还是Long Polling,都需要不断的发送请求,这会造成服务器资源的浪费。
WebSocket是一种支持双向通信的协议,通过一次HTTP升级请求完成websocket连接之后就可以多次通信了。
“分享知识,收获快乐”
我是一名程序员,喜欢我的文章欢迎 关注 及 转发,我会经常与大家分享一些工作当中的编程技巧与经验。