基于发布,订阅,主题 模式,实现原理图

1 项目最终的结构图,源码示例下载
https://download.csdn.net/download/qq_41712271/16107255

2 maven依赖,springboot 版本 1.5.8.RELEASE ,太高的版本会涉及到跨域问题,还在研究中
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
<!-- httpClient相关依赖包begin -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.15</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>9.3.7.v20160115</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
<!-- httpClient相关依赖包end -->3 核心类 WebSocketConfig 代码
package cn.huawei.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
/**
* 开启WebSocket代理
* 也就是开启stomp协议来传输基于消息的broker消息
* 像rabbitmq的模式一样,有发布,订阅,主题的概念
*/
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
/**
* 注册端点,发布或者订阅消息的时候需要连接此端点,(客户端将通过这里配置的 URL 来建立 WebSocket 连接)
*/
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/endpoint-websocket")
//setAllowedOrigins 非必须,*表示允许其他域进行连接
.setAllowedOrigins("*")
//withSockJS 表示开始sockejs支持,可以解决浏览器对 WebSocket 的兼容性问题
.withSockJS();
}
/**
* 配置消息代理(中介)
* enableSimpleBroker 服务端推送给客户端的路径前缀
* setApplicationDestinationPrefixes 客户端发送数据给服务器端的一个前缀
*/
public void configureMessageBroker(MessageBrokerRegistry registry) {
/**
* 设置消息代理前缀
* 即如果消息的前缀是 /topic ,就会将消息转发给消息代理(broker),
* 再由消息代理将消息广播给当前连接的客户端。
*
* 相当于rabbitmq里的主题
* 简单说,就是 客户端js订阅的代码,要写成 stompClient.subscribe('/topic_1/shou_2')
* 服务端 @SendTo("/topic_1/shou_2")
*/
registry.enableSimpleBroker("/topic_1", "/topic_2", "/topic_3");
/**
* 表示配置一个或多个前缀,通过这些前缀过滤出需要被注解方法处理的消息。
* 例如,前缀为 /server_1 的 destination 可以通过@MessageMapping注解的方法处理,
* 而其他 destination (例如 /topic /queue)将被直接交给 broker 处理
*
* 简单说,客户端的js发送的代码 要写成 stompClient.send("/server_1/fa_1")
* 就是 setApplicationDestinationPrefixes + @MessageMapping("/fa_1")
*/
registry.setApplicationDestinationPrefixes("/server_1", "/server_2");
}
}4 Controller层 Controller_Test_1 代码编写
package cn.huawei.controller;
import cn.huawei.model.InMessage;
import cn.huawei.model.OutMessage;
import cn.huawei.service.StockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import java.util.Iterator;
import java.util.Map;
@Controller
public class Controller_Test_1 {
/**
* 总结
* 1、SendTo 不通用,固定发送给指定的订阅者,只是入门演示下
* SimpMessagingTemplate 灵活,支持多种发送方式
*
* 2、@MessageMapping("/fa_1")
* 相当于 @RequestMapping,前台js代码 stompClient.send("/server_1/fa_1")
* 就是 setApplicationDestinationPrefixes + @MessageMapping("/fa_1")
*
* 3、template.convertAndSend("/topic_2/game_rank")
* 向 订阅了 stompClient.subscribe('/topic_2/game_rank')的发送消息,此为前台js代码
*
*/
@Autowired
private SimpMessagingTemplate template;
/**
* 案例 1 最基本入门
*/
@MessageMapping("/fa_1")
@SendTo("/topic_1/shou_1")
public OutMessage gameInfo_1(InMessage message){
return new OutMessage(message.getContent()+"---发1");
}
/**
* 案例 2
* SimpMessagingTemplate 基本使用 1
*/
@MessageMapping("/fa_2")
public void gameInfo_3(InMessage message) throws InterruptedException {
for(int i=0; i<20; i++){
Thread.sleep(500L);
template.convertAndSend("/topic_2/game_rank", new OutMessage(message.getContent()+i));
}
}
/**
* 案例 3
* SimpMessagingTemplate 基本使用 2
*/
@MessageMapping("/single/chat")
public void gameInfo_4(InMessage message) throws InterruptedException {
template.convertAndSend("/topic_3/single/"+message.getTo(),
new OutMessage(message.getFrom()+" 发送:"+ message.getContent()));
}
/**
* 案例 4
* 实时推送服务器的JVM负载,已用内存等消息
* 被注解@Scheduled(fixedRate 单位是毫秒) 标记的方法,是不能有参数,不然会报错
*/
@MessageMapping("/schedule/push")
@Scheduled(fixedRate = 30000)
public void sendServerInfo(){
int processors = Runtime.getRuntime().availableProcessors();
Long freeMem = Runtime.getRuntime().freeMemory();
Long maxMem = Runtime.getRuntime().maxMemory();
String message = String.format("服务器可用处理器:%s; 虚拟机空闲内容大小: %s; 最大内存大小: %s", processors,freeMem,maxMem );
template.convertAndSend("/topic_1/server_info",new OutMessage(message));
}
/**
* 案例 5
* 演示调用 快递接口获取数据,实时推送
* 这里不需要客户端向服务器发消息,所以没有 @MessageMapping
*/
@Scheduled(fixedRate = 30000)
public void sendGuPiaoInfo(){
Map<String, String> stockInfoMap = StockService.getStockInfo();
Iterator<Map.Entry<String, String>> iterator = stockInfoMap.entrySet().iterator();
while (iterator.hasNext())
{
Map.Entry<String, String> next = iterator.next();
template.convertAndSend("/topic_1/stock_info",new OutMessage(next.getKey()+" "+next.getValue()));
}
}
}5 开始测试,共5个小Demo
版权声明:本文为qq_41712271原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。