springboot整合Websocket以及一种使用场景

一、websocket的执行流程

借用b站大佬的图讲解下:分三步走。

 

二、springboot整合websocket

1.导入依赖

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

2.初始化 ServerEndpointExporter

package com.yblue.chat.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author JiaXinMa
 * @description 初始化 ServerEndpointExporter
 *              注意:如果使用独立的servlet容器,就不需要注入ServerEndpointExporter,
 *              因为servlet容器会自己提供和管理
 * @date 2021/8/16
 */
@Configuration
public class WebsocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

3.配置ServerEndpoint的方法

package com.yblue.chat.config;

import lombok.extern.slf4j.Slf4j;
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.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author JiaXinMa
 * @description Websocket的主要调用方法
 * @date 2021/8/16
 */
//  ws://localhost:8080/websocket/test
@Slf4j
@ServerEndpoint("/websocket/test/{userId}")
@Component
public class WebsocketEndpoint {

    //注意;每次刷新页面(就是重新连接)session都会变
    // 同时服务端会自动去调用关闭连接,然后再建立连接

    private static Map<Object, Session> sessionMap = new ConcurrentHashMap<>();

    //建立连接
    @OnOpen
    public void onOpen(Session session,@PathParam("userId) Integer userId) {
        sessionMap.put(userId, session);
        log.info("建立连接成功!!!");

        log.info("现有的连接数:" + sessionMap.size());
    }

    //关闭连接
    @OnClose
    public void onClose(Session session,@PathParam("userId") Integer userId) {
        try {
            sessionMap.remove(userId);
            session.close();
            log.info("关闭连接成功!!!");
            log.info("现有的连接数:" + sessionMap.size());
        } catch (IOException e) {
            log.info("关闭连接失败!!!");
        }
    }


    //群发消息
    @OnMessage
    public void onMessage(String message) throws IOException {
        Collection<Session> sessions = sessionMap.values();
        Iterator<Session> iterator = sessions.iterator();

        while (iterator.hasNext()) {
            iterator.next().getBasicRemote().sendText(message);
        }
        log.info("实时通讯成功!!!");
    }


}

 

4.可能遇到的场景:

4.1 聊天室(我这个demo不适用,可以去修改,原理懂了都一样),建议可以采用 环信即时通讯 的技术

4.2 就是那些实时监控数据变化的的场景(比如我所在公司有个社区开门记录数据实时更新)  

      该场景逻辑是:人脸开门,然后后台有个echarts统计的表格实时变化

下面写个小demo测试下:

控制层代码:

package com.yblue.chat.controller;

import com.yblue.chat.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author JiaXinMa
 * @description
 * @date 2021/8/16
 */
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    //不用返回数据的,因为我在业务层调用WebsocketEndpoint的方法,他实时同步过去了
    @GetMapping("/findById")
    public void findById(Integer userId) {
        userService.findById(userId);
    }

    //这里get请求主要是我想在地址栏测试而已,不要在意这些细节
    @GetMapping("/update")
    public void update(Integer userId) {
        userService.update(userId);
    }

}

业务层代码:

package com.yblue.chat.service;

import com.yblue.chat.config.WebsocketEndpoint;
import com.yblue.chat.mapper.UserMapper;
import com.yblue.chat.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.websocket.Session;
import java.io.IOException;

/**
 * @author JiaXinMa
 * @description
 * @date 2021/8/16
 */
@Service
@Transactional
@Slf4j
public class UserService {

    @Autowired(required = false)
    UserMapper userMapper;

    @Autowired
    WebsocketEndpoint websocketEndpoint;

    public void findById(Integer userId) {
        try {
            User user = userMapper.selectById(userId);
            //这里调用,虽然可以在WebsocketEndpoint连接的时候调用
            // 但是要注意bean的生命周期,需要特殊处理下,我菜暂时不会
            //这里推送数据用json字符串,可以考虑用阿里巴巴的JSONObject这个类去转换
            websocketEndpoint.onMessage(user.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void update(Integer userId) {
        try {
            User user = new User();
            user.setUserId(userId);
            user.setName("我被改了");
            userMapper.updateById(user);

            user = userMapper.selectById(userId);
            //这里调用也调用WebsocketEndpoint的推送数据方法
            websocketEndpoint.onMessage(user.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
}

5.测试

我是综合这个网站(http://coolaf.com/tool/chattest)测试和地址栏测试的,效果如下

5.1创建连接

5.2查询数据

5.3修改数据

5.4关闭窗口刷新界面测试

    如下效果,测试结果是会自动调用关闭连接方法(避免了只存不移除导致没有被垃圾回收,造成内存溢出),为了能够被回收,提高系统性能

 

 

这只是我对websocket比较浅薄的见解,如果有不对的地方,大佬帮忙指正。

 

想看更多精彩内容,可以关注我的CSDN

我的CSDN

posted @ 2021-08-17 18:39  Yblue  阅读(236)  评论(0编辑  收藏  举报