springboot整合websocket基础入门,常见注解使用 Demo (基于stomp协议发布订阅方式实现)

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


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版权协议,转载请附上原文出处链接和本声明。