天天看点

Spring Boot使用WebSocket实现后台消息推送

Spring Boot使用WebSocket实现后台消息推送

为什么需要WebSocket?

已经有了​

​HTTP​

​​协议,为什么还需要另一个协议?这是因为​

​HTTP​

​协议有一个缺陷:通信只能由客户端发起。

当客户端想知道某个订单是否已经完成支付,使用​

​HTTP​

​​协议只能由客户端向服务器发出请求,服务器返回查询结果,所以​

​HTTP​

​协议做不到服务器主动向客户端推送信息。

这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。

轮询的效率低,非常浪费资源(因为必须不停连接,或者​

​HTTP​

​连接始终打开)。

​WebSocket​

​就是这样发明的,它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

Spring Boot使用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>      

测试:

Spring Boot使用WebSocket实现后台消息推送
Spring Boot使用WebSocket实现后台消息推送

结果显然是正确的。