Spring Boot使用WebSocket实现后台消息推送
为什么需要WebSocket?
已经有了
HTTP
协议,为什么还需要另一个协议?这是因为
HTTP
协议有一个缺陷:通信只能由客户端发起。
当客户端想知道某个订单是否已经完成支付,使用
HTTP
协议只能由客户端向服务器发出请求,服务器返回查询结果,所以
HTTP
协议做不到服务器主动向客户端推送信息。
这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。
轮询的效率低,非常浪费资源(因为必须不停连接,或者
HTTP
连接始终打开)。
WebSocket
就是这样发明的,它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
参考博客:WebSocket 教程
正文
pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kaven</groupId>
<artifactId>websocket</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>websocket</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
主要是引入
websocket
依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
启动类:
package com.kaven.websocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebsocketApplication {
public static void main(String[] args) {
SpringApplication.run(WebsocketApplication.class, args);
}
}
配置类:
package com.kaven.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
这个配置类很重要,想要了解可以去看一下
ServerEndpointExporter
的源码。
websocket
组件:
package com.kaven.websocket.component;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@ServerEndpoint("/websocket")
public class WebSocketComponent {
private Session session;
private CopyOnWriteArraySet<WebSocketComponent> webSocketComponentSet = new CopyOnWriteArraySet<>();
@OnOpen
public void onOpen(Session session) throws IOException {
this.session = session;
webSocketComponentSet.add(this);
System.out.println("连接成功");
this.sendMessage("发送给客户端的消息");
}
@OnClose
public void onClose(){
webSocketComponentSet.remove(this);
System.out.println("连接关闭");
}
@OnMessage
public void onMessage(String message){
System.out.println("【接收消息】:"+message);
}
public void sendMessage(String message) throws IOException {
System.out.println("【发送消息】:"+message);
this.session.getBasicRemote().sendText(message);
}
}
前端代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebSocket</title>
</head>
<body>
</body>
</html>
<script>
let websocket = null;
if('WebSocket' in window){
websocket = new WebSocket('ws://localhost:8080/websocket');
websocket.onopen = ()=>{
console.log('连接成功');
}
websocket.onclose = ()=>{
console.log('连接关闭');
}
websocket.onmessage = (event)=>{
console.log('【服务端消息】: '+event.data);
websocket.send('收到消息');
}
websocket.onerror = ()=>{
console.log('连接发生错误');
}
}
else{
console.log('该浏览器不支持WebSocket');
}
</script>
测试:
结果显然是正确的。