一、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比较浅薄的见解,如果有不对的地方,大佬帮忙指正。
想看更多精彩内容,可以关注我的博客园
我的博客园