前言
有时候前端需要实时的获取后端的信息,比如订单的消息,发货之类的通知,实时的服务器信息,股票信息等。有一种方法是ajax轮训,就是间隔一段时间查询服务器一次,但是这种方式对服务器不太友好。所以,还有另一种方法,websocket。
一、引入websocket
pom文件引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
二、配置
写一个配置类,继承AbstractWebSocketMessageBrokerConfigurer,这个类配置的是关于STOMP协议。前端使用的是stomp.js,关于stomp.js可以看一下这一篇博客:https://www.cnblogs.com/goloving/p/10746378.html 。stomp.js 具体的使用方法下面会写。
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig extends AbstractWebSocketMessageBrokerConfigurer {
/**
* 注册stomp的端点,发布或者订阅消息时需要连接此端点
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/endpoint").setAllowedOrigins("*").withSockJS();
}
/**
* 配置信息代理(中介转发)
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 订阅Broker名称
registry.enableSimpleBroker("/queue","/topic","/chat");
// 全局使用的消息前缀(客户端订阅路径上会体现出来)
registry.setApplicationDestinationPrefixes("/app");
}
}
三、使用
首先定义两个传输数据的对象
public class InMessage {
private String from;
private String to;
private LocalDateTime time = LocalDateTime.now();
private String content;
//省略setter和getter
}
public class OutMessage {
private String from;
private LocalDateTime time = LocalDateTime.now();
private String content;
//省略setter和getter
}
使用起来也是很简单,有两种方法。
1、使用@SendTo注解来转发数据
value属性值的第一值 /topic 必须在enableSimpleBroker中配置,@MessageMapping和@RequestMapping的作用类似。
@Controller
public class GameInfoController {
@MessageMapping("/v1/game_chat")
@SendTo("/topic/game_chat")
public OutMessage gameInfo(InMessage message){
return new OutMessage("",message.getContent());
}
}
2、注入SimpMessagingTemplate推送消息
destination的第一个值必须在上面 的enableSimpleBroker中配置,与@SendTo的value使用方法一致。
@Component
public class WebSocketService {
@Autowired
private SimpMessagingTemplate template;
public void sengTextMsg(String destination,Object message){
template.convertAndSend(destination,message);
}
}
@Controller
public class GameInfoV2Controller {
@Autowired
private WebSocketService webSocketService;
@MessageMapping("/v2/chat")
public void gameInfo(InMessage message){
webSocketService.sendTopicMessage("/topic/game_rank", message);
}
}
这两种方法的主要区别在于@SendTo的路径是写死的,SimpMessagingTemplate的路径可以动态设置。
3、给特定的人推送消息
以上都是群发消息,我们可以在路径上绑定id信息,这样就可以给特定的人推送消息了。在WebSocketService 中添加以下方法。
public void sendChatMessage(InMessage message) {
template.convertAndSend("/chat/single/"+message.getTo(),new OutMessage(message.getFrom(),message.getContent()));
}
@Controller
public class ChatRoomV3Controller {
@Autowired
private WebSocketService webSocketService;
@MessageMapping("v3/single/chat")
public void singleChat(InMessage message){
webSocketService.sendChatMessage(message);
}
}
4、实时数据推送
我们可以使用@Scheduled注解来实现实时的数据推送,先在启用类上添加@EnableScheduling注解
再添加一个方法
public void sendServerInfo() {
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
template.convertAndSend("/topic/server_info",new OutMessage(now.format(df)));
}
然后直接使用@Scheduled注解一个方法就可以了,每秒推送一次数据
@Controller
public class ServerInfoV4Controller {
@Autowired
private WebSocketService webSocketService;
@Scheduled(fixedRate = 1000)
public void sendServerInfo(){
webSocketService.sendServerInfo();
}
}
四、stomp.js 的使用
使用起来也是挺简单的。看注释应该就明白了。要注意的是 subscribe的地址在后台要有,也就是和@SendTo、destination的值对应。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="http://cdn.static.runoob.com/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
<script type="text/javascript">
// 建立连接对象(还未发起连接)
var socket = new SockJS("http://localhost:8080/endpoint");
// 获取 STOMP 子协议的客户端对象
var stompClient = Stomp.over(socket);
stompClient.debug = null;
// 向服务器发起websocket连接并发送CONNECT帧
stompClient.connect({},
function connectCallback(frame) {
// 连接成功时(服务器响应 CONNECTED 帧)的回调方法
setMessageInnerHTML("连接成功");
stompClient.subscribe('/topic/server_info', function (res) {
re = JSON.parse(res.body);
console.log(re);
setMessageInnerHTML("")
setMessageInnerHTML("你接收到的消息为:" + re.content);
});
},
function errorCallBack(error) {
// 连接失败时(服务器响应 ERROR 帧)的回调方法
setMessageInnerHTML("连接失败");
}
);
//发送消息
function send() {
var message = $("#content").val();
var messageJson = JSON.stringify({ "content": message });
stompClient.send("/app/v2/chat", {}, messageJson);
sendMessageInnerHTML("/app/v2/chat 你发送的消息:" + message);
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
$("#in").html(innerHTML + '<br/>');
}
function sendMessageInnerHTML(innerHTML) {
$("#out").append(innerHTML + '<br/>');
}
$(function(){
$("#btn").click(function(){
send();
});
})
</script>
</head>
<body>
<input id="content" class="form-control">
<button id="btn" class="btn btn-info">发送</button>
<div id="in"></div>
<div id="out"></div>
</body>
</html>
写在后面的话
还有基于H5的websocket,后面会再写