SpringBoot2.0整合websocket,后端主动实时向前端页面传递数据

昨天接到一个需求,excel导入时,实时的向前端界面传递数据,增加大数据量的客户友好性。让用户可以感知到导入的操作。该怎么实现呢?之前没有搞过类似的东西,所以就问了同事。同事建议两种方式:第一,使用流实时的向前台写数据;第二,使用websocket长连接,实时的向前端推数据。我使用了第二种方式,因为之前没有用过websocket(惭愧),所以在网上找了找相关的信息,很大一部分是参考这篇博客中代码(地址:https://blog.csdn.net/qq_35387940/article/details/93483678),感谢!下面的代码很大一部分都是上述博客中的代码。

现在就描述一下我对websocket一些浅薄的见解,只能说是最基本的使用,后续还需要深入的去了解。这篇博文也是为了方便自己去回顾这个现象。

想要使用websocket进行通讯,我们就需要有客户端以及对应的服务端。我们现在是在后台中创建一个服务端,前台作为客户端。那在springBoot里面想要使用webscoket需要满足以下条件。

1.pom依赖文件

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

2.创建节点配置类MyEndpointConfigure.java


public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
    private static volatile BeanFactory context;
 
    @Override
    public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
        return context.getBean(clazz);
    }
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        MyEndpointConfigure.context = applicationContext;
    }
}

3.然后是WebSocket配置类,WebSocketConfig.java


public class WebSocketConfig {
 
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
 
    @Bean
    public MyEndpointConfigure newConfigure() {
        return new MyEndpointConfigure();
    }

4.然后是关键,WebSocket的各个监听方法,ProductWebSocket.java

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
 
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import com.jc.websocket.config.MyEndpointConfigure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
 * @Author:JCccc
 * @Description:
 * @Date: created in 15:56 2019/5/13
 */
 
@Component
@ServerEndpoint(value = "/productWebSocket/{userId}", configurator = MyEndpointConfigure.class)
public class ProductWebSocket {
 
    // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static final AtomicInteger OnlineCount = new AtomicInteger(0);
 
    // concurrent包的线程安全Set,用来存放每个客户端对应的ProductWebSocket对象。
    private static CopyOnWriteArraySet<ProductWebSocket> webSocketSet = new CopyOnWriteArraySet<ProductWebSocket>();
 
    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
 
 
    private Logger log = LoggerFactory.getLogger(ProductWebSocket.class);
 
    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(@PathParam("userId")String userId, Session session) {
        log.info("新客户端连入,用户id:" + userId);
        this.session = session;
        webSocketSet.add(this); // 加入set中
        addOnlineCount(); // 在线数加1
        if(userId!=null) {
            List<String> totalPushMsgs = new ArrayList<String>();
            totalPushMsgs.add(userId+"连接成功-"+"-当前在线人数为:"+getOnlineCount());
 
 
            if(totalPushMsgs != null && !totalPushMsgs.isEmpty()) {
                totalPushMsgs.forEach(e -> sendMessage(e));
            }
        }
 
    }
 
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        log.info("一个客户端关闭连接");
        webSocketSet.remove(this); // 从set中删除
        subOnlineCount(); // 在线数减1
    }
 
    /**
     * 收到客户端消息后调用的方法
     *
     * @param message
     *            客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
         log.info("用户发送过来的消息为:"+message);
    }
 
    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("websocket出现错误");
        error.printStackTrace();
    }
 
    public void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
            log.info("推送消息成功,消息为:" + message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message) throws IOException {
        for (ProductWebSocket productWebSocket : webSocketSet) {
            productWebSocket.sendMessage(message);
        }
    }
 
    public static synchronized int getOnlineCount() {
        return OnlineCount.get();
    }
 
    public static synchronized void addOnlineCount() {
        OnlineCount.incrementAndGet(); // 在线数加1
    }
 
    public static synchronized void subOnlineCount() {
        OnlineCount.decrementAndGet(); // 在线数加1
    }
}

接下来用一个HTML5 页面,连接当前的WebSocket节点,接/发消息, index.html

<!DOCTYPE HTML>
<html>
<head>
    <title>Test My WebSocket</title>
</head>
 
 
<body>
Test<br/>
<input id="text" type="text" /><button οnclick="send()">Send</button>    <button οnclick="closeWebSocket()">Close</button>
<div id="message">
</div>
</body>
 
 
<script type="text/javascript">
    var websocket = null;
 
 
    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        //连接WebSocket节点 
        websocket = new WebSocket("ws://localhost:8066/productWebSocket/001");
    }
    else{
        alert('Not support websocket')
    }
 
 
    //连接发生错误的回调方法
    websocket.onerror = function(){
        setMessageInnerHTML("error");
    };
 
 
    //连接成功建立的回调方法
    websocket.onopen = function(event){
        setMessageInnerHTML("open");
    }
 
 
    //接收到消息的回调方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }
 
 
    //连接关闭的回调方法
    websocket.onclose = function(){
        setMessageInnerHTML("close");
    }
 
 
    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
        websocket.close();
    }
 
 
    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML){
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }
 
 
    //关闭连接
    function closeWebSocket(){
        websocket.close();
    }
 
 
    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>

代码都在上面了,下面说一下使用的心得。

首先看后端的代码,最主要的就是ProductWebSocket .java这个文件,其他的都是一些配置类。目前在我需要的场景里面,最重要的方法就是   sendInfo()方法。这个实际上是遍历我实际启动的客户端,然后推送信息给对应的客户端。当我目前只有一个客户端,所以只会推送给当前客户端。我是在我的业务方法中调用了这个方法。先不考虑效率的问题,如果我是按照excel的条数,循环插入数据库,那么在每次我插入数据库之后,都需要调用一次推送方法,把插入成功的消息传递给前端界面。界面上面实时展示对应的消息。

再来看前端代码,最主要的在我看来是两个地方。第一,和客户端建立链接,代码如下:

    var websocket = null;


    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        //连接WebSocket节点
        websocket = new WebSocket("ws://localhost:8083/productWebSocket/001");
    }
    else{
        alert('Not support websocket')
    }

这个可以放在任何地方,我是放在了js头部,也就是在我刚进入这个界面的时候,就和服务端建立链接。

第二,接受服务端推送消息的代码,代码如下

//接收到消息的回调方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }

这里面的event.data就是服务端给客户端推送的消息。我们只要在这个方法中将接受到的信息推送到界面上就可以了。

  • 2
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值