Spring-boot-websocket
什么是WebSocket
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
与HTTP不同,WebSocket提供全双工通信。此外,WebSocket还可以在TCP之上实现消息流。TCP单独处理字节流,没有固有的消息概念。 在WebSocket之前,使用Comet可以实现全双工通信。但是Comet存在TCP握手和HTTP头的开销,因此对于小消息来说效率很低。WebSocket协议旨在解决这些问题。
为什么使用WebSocket
很多网站需要实现推送技术,所用的技术都是轮询,轮询是指由浏览器每隔一段时间(如每秒)向服务器发出HTTP请求,然后服务器返回最新的数据给客户端。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求与回复可能会包含较长的头部,其中真正有效的数据可能只是很小的一部分,所以这样会消耗很多带宽资源。WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
WebSocket的优点
- 较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。
- 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。
- 保持连接状态。与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
- 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
- 可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
- 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。
常见的WebSocket使用场景
- 社交网站,在线聊天,在线教育,在线编程。。。
- 数据实时更新,例如股票实时更新,体育状况实时更新,弹幕。。。
- 一些框架,html页面修改了以后,浏览器也能实时刷新。例如webpack。
如何集成spring boot + websocket
服务端代码:
-
pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
-
configuration
@Configuration @EnableWebSocket // 开启websocket @EnableWebSocketMessageBroker // WebSocketMessageBroker public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { // 对应js中 new SockJS("http://localhost:8080/ws"); 会注册这个埋点请求 registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { // 对应js中 stompClient.subscribe("/topic/message", callback) // stompClient.subscribe("/user/1/message",callback) registry.enableSimpleBroker("/topic", "/user"); } }
-
job
@Component public class WebSocketMessageJob { @Autowired private SimpMessagingTemplate template; @Scheduled(fixedRate = 1000) public void sendMessage() { // 广播 template.convertAndSend("/topic/message", System.currentTimeMillis()); } @Scheduled(fixedRate = 3000) public void sendMessageToUser() { // 指定用户推送 template.convertAndSendToUser("2", "/message", System.currentTimeMillis()); } }
-
main
@SpringBootApplication @EnableScheduling public class WebSocketApplication { public static void main(String[] args) { SpringApplication.run(WebSocketApplication.class, args); } }
客户端代码:
// 引入sockjs.min.js,stomp.js,jquery-3.5.1.js
const sock = new SockJS("http://localhost:8080/ws");
const stompClient = Stomp.over(sock);
stompClient.connect({}, (frame) => {
stompClient.subscribe("/topic/message", (response) => {
server = JSON.parse(response.body);
console.log(server);
});
stompClient.subscribe("/user/1/message", (response) => {
server = JSON.parse(response.body);
console.log(server);
});
});
网友评论