SpringBoot——整合WebSocket(STOMP协议)

藏宝库编辑 2024-9-8 00:03:38 48 0 来自 中国
前言

HTTP 协议是一种无状态的、无毗连的、单向的应用层协议。它接纳了哀求/相应模子。通讯哀求只能由客户端发起,服务端对哀求做出应答处理处罚。这种通讯模子有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。
这种单向哀求的缺点,假如服务器有连续的状态变革,客户端要获知就非常贫困。大多数 Web 应用步调将通过频仍的异步JavaScript和XML(AJAX)哀求实现长轮询。轮询的效率低,非常浪费资源(因为必须不绝毗连,大概 HTTP 毗连始终打开)。
WebSocket 毗连答应客户端和服务器之间举行全双工通讯,以便任一方都可以通过创建的毗连将数据推送到另一端。WebSocket 只须要创建一次毗连,就可以一直保持毗连状态。这相比于轮询方式的不绝创建毗连显然效率要大大进步。
一、为什么须要STOMP?

WebSocket 协议是一种相称低级的协议。它定义了怎样将字节省转换为帧。帧可以包罗文本或二进制消息。由于消息本身不提供有关怎样路由或处理处罚它的任何其他信息,因此很难在不编写其他代码的情况下实现更复杂的应用步调。荣幸的是,WebSocket 规范答应在更高的应用步调级别上使用子协议。
别的,单单使用WebSocket完成群聊、私聊功能时,须要本身管理session信息,通过STOMP协议时,spring已经封装好,开辟者只须要关注本身的主题、订阅关系即可。
二、STOMP详解

STOMP 中文为“面向消息的简单文本协议”,STOMP 提供了可以或许协作的报文格式,以至于 STOMP 客户端可以与任何 STOMP 消息署理(Brokers)举行通讯,从而为多语言,多平台和 Brokers 集群提供简单且广泛的消息协作。STOMP 协议可以创建在WebSocket 之上,也可以创建在其他应用层协议之上。通过 Websocket创建 STOMP 毗连,也就是说在 Websocket 毗连的根本上再创建 STOMP 毗连。终极实现如上图所示,这一点可以在代码中有一个精良的体现。
业界已经有许多精良的 STOMP 的服务器/客户端的开源实现

  • STOMP 服务器:ActiveMQ、RabbitMQ、StompServer、…
  • STOMP 客户端库:stomp.js(javascript)
Stomp 的特点是客户端的实现很轻易,服务端相称于消息队列的 broker 大概是 server,一样平常不须要我们去实现,以是重点关注一下客户端怎样使用

  • CONNECT:启动与服务器的流或 TCP 毗连
  • SEND:发送消息
  • SUBSCRIBE:订阅主题
  • UNSUBSCRIBE:取消订阅
  • BEGIN:启动事物
  • COMMIT:提交事物
  • ABORT:回滚事物
  • ACK:确认来自订阅的消息的消耗
  • NACK:告诉服务器客户端没有消耗该消息
  • DISCONNECT:断开毗连
其实STOMP协议并不是为WS所计划的,它其实是消息队列的一种协议,和AMQP、JMS是平级的。 只不外由于它的简单性可巧可以用于定义WS的消息体格式。 现在许多服务端消息队列都已经支持了STOMP,好比RabbitMQ、Apache ActiveMQ等。许多语言也都有STOMP协议的客户端剖析库,像JAVA的Gozirra、C的libstomp、Python的pyactivemq、JavaScript的stomp.js等等。
STOMP协议官方文档
三、SpringBoot集成STOMP代码示例

3.1 架构图

3.2、服务端代码


  • 1、添加依靠
<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>2.3.10.RELEASE</version>    <relativePath/> <!-- lookup parent from repository --></parent><properties>    <java.version>1.8</java.version></properties><dependencies>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <dependency>        <groupId>org.apache.commons</groupId>        <artifactId>commons-lang3</artifactId>    </dependency>    <dependency>        <groupId>org.projectlombok</groupId>        <artifactId>lombok</artifactId>        <version>1.18.12</version>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-websocket</artifactId>    </dependency>    <dependency>        <groupId>com.alibaba</groupId>        <artifactId>fastjson</artifactId>        <version>1.2.62</version>    </dependency></dependencies><build>    <plugins>        <plugin>            <groupId>org.apache.maven.plugins</groupId>            <artifactId>maven-compiler-plugin</artifactId>            <configuration>                <source>1.8</source>                <target>1.8</target>            </configuration>        </plugin>    </plugins></build>

  • 2、WebSocket的设置类
/** * 通过EnableWebSocketMessageBroker * 开启使用STOMP协议来传输基于署理(message broker)的消息, * 此时浏览器支持使用@MessageMapping 就像支持@RequestMapping一样。 *///WebSocket的设置类@Configuration//开启对WebSocket的支持@EnableWebSocketMessageBrokerpublic class WebSocketConfig implements WebSocketMessageBrokerConfigurer{    /**     * 注册stomp的端点     * 注册一个STOMP协议的节点,并映射到指定的URL     */    @Override    public void registerStompEndpoints(StompEndpointRegistry registry) {        //endPoint 注册协议节点,并映射指定的URl点对点-用        //注册一个名字为"/endpointSocket" 的endpoint,并指定 SockJS协议。        //答应使用socketJs方式访问,访问点为webSocketServer,答应跨域        //毗连前缀        registry.addEndpoint("/endpointSocket")                .setAllowedOrigins("*")  // 跨域处理处罚                .withSockJS();  //支持socketJs    }    /**     * 设置消息署理(message broker)     * 启用一个简单的基于内存的消息署理     * @param registry     */    @Override    public void configureMessageBroker(MessageBrokerRegistry config) {        //订阅Broker名称:topic 代表发布广播,即群发        //queue 代表点对点,即发指定用户        registry.enableSimpleBroker("/queue", "/topic");                //send下令时须要带上/app前缀        // 全局使用的消息前缀(客户端订阅路径上会体现出来)        config.setApplicationDestinationPrefixes("/app");                //修改convertAndSendToUser方法前缀        //点对点使用的订阅前缀(客户端订阅路径上会体现出来),        // 不设置的话,默认也是/user/        //config.setUserDestinationPrefix ("/user/");    }}@EnableWebSocketMessageBroker注解启用 WebSocket 消息处理处罚,由消息署理支持。
SockJS 有一些浏览器中缺少对 WebSocket 的支持,而 SockJS 是一个浏览器的 JavaScript库,它提供了一个雷同于网络的对象,SockJS 提供了一个连贯的,跨浏览器的JavaScriptAPI,它在浏览器和 Web 服务器之间创建了一个低延长、全双工、跨域通讯通道。SockJS 的一大利益在于提供了浏览器兼容性。即优先使用原生WebSocket,假如浏览器不支持 WebSocket,会自动降为轮询的方式。假如你使用 Java 做服务端,同时又恰好使用 Spring Framework 作为框架,那么保举使用SockJS。

  • 3、控制器代码
@Slf4j@RestControllerpublic class TestController{    @Autowired    private SimpMessagingTemplate simpMessagingTemplate;     @MessageMapping("/hello")    @SendTo ("/topic/greetings")    public Greeting greeting(HelloMessage message) throws Exception {        Thread.sleep(1000); // simulated delay        return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");    }     @MessageMapping("/topic/greetings")    public Greeting greeting2(HelloMessage message) throws Exception {        Thread.sleep(1000); // simulated delay        log.info ("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");        return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");    }    @GetMapping ("/hello2")    public void greeting3(HelloMessage message) throws Exception {        Thread.sleep(1000); // simulated delay        simpMessagingTemplate.convertAndSend ("/topic/greetings",                new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"));    }      @MessageMapping("/sendToUser")    public void sendToUser(HelloMessage message) throws Exception {        Thread.sleep(1000); // simulated delay        log.info ("userId:{},msg:{}",message.getUserId (),message.getName ());//      simpMessagingTemplate.convertAndSendToUser (message.getUserId (),"/sendToUser",//              new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"));//      simpMessagingTemplate.convertAndSend ("/user/1/sendToUser",//              new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"));        simpMessagingTemplate.convertAndSend ("/topic/user/"+message.getUserId ()+"/sendToUser",                new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"));    }}

  • @MessageMapping:功能与RequestMapping注解雷同。send指令发送信息时添加此注解。
  • @SendTo/@SendToUser:将信息输出到该主题。客户端订阅同样的主题后就会收到信息。
  • 在只有指定@MessageMapping时@MessageMapping == “/topic” + @SendTo。
  • 假如想使用rest接口发送消息。可以通过SimpMessagingTemplate举行发送。
  • 点对点谈天时,可以使用SimpMessagingTemplate.convertAndSendToUser方法发送。个人意味比注解@SendToUser更加轻易明白,更加方便。
  • convertAndSendToUser方法和convertAndSend雷同,区别在于convertAndSendToUser方法会在主题默认添加/user/为前缀。因此,示例代码中convertAndSend方法直接传入"/topic/user/"+message.getUserId ()+"/sendToUser" 也是点对点发送。topic此中是默认前缀。
  • 假如想修改convertAndSendToUser默认前缀可在设置类举行设置,可在WebSocketConfig类中查察。
四、SimpMessagingTemplate的作用


  • 1、SimpMessagingTemplate可以在应用的恣意地方发送消息。
    Spring的SimpMessagingTemplate可以或许在应用的任何地方发送消息,甚至不必以起首吸收一条消息作为条件。使用SimpMessagingTemplate的最简单方式是将它(大概其接口SimpMessageSendingOperations)自动装配到所需的对象中。
  • 2、SimpMessagingTemplate可以为指定的用户发送消息。
    SimpMessagingTemplate还提供了convertAndSendToUser()方法。convertAndSendToUser()方法可以或许让我们给特定用户发送消息。
simpMessageSendingOperations.convertAndSendToUser("1", "/message", "测试convertAndSendToUser");stomp.subscribe('/users/1/message', function(message){ });客户端吸收一对一消息的主题是"/users/"+usersId+"/message",这里的用户Id可以是一个平凡字符串,只要每个客户端都使用本身的Id而且服务器端知道每个用户的Id就行了。
五、神奇的@SendTo和@SendToUser

5.1 @SendTo

使用 @SendTo 注解,使方法的返回值推送到消息署理器中,由消息署理器广播到订阅路径中去。但并没有具体的先容消息是怎样被Spring框架处理处罚,末了发送广播出去的。
@MessageMapping("/hello") //使用MessageMapping注解来标识全部发送到“/hello”这个destination的消息,都会被路由到这个方法举行处理处罚. @SendTo("/topic/greetings") //使用SendTo注解来标识这个方法返回的结果,都会被发送到它指定的destination,“/topic/greetings”. //传入的参数Message为客户端发送过来的消息,是自动绑定的。 public Greeting greeting(HelloMessage message) throws Exception {    Thread.sleep(1000); // 模仿处理处罚延时     return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); //根据传入的信息,返回一个欢迎消息. } 上面方法中的返回值,会被广播到 /topic/greetings 这个订阅路径中,只要客户端订阅了这个路径,都会吸收到消息。Spring处理处罚消息的重要类是 SimpleBrokerMessageHandler , 当须要发送广播消息时,终极会调用此中的 sendMessageToSubscribers() 方法:
方法内部会循环调用当前全部订阅此 Broker 的客户端 Session ,然后逐个发送消息。这里,入参 destination 就是 Broker 的地点,而 message,就是我们返复书息的封装,其他细节这里就不睁开讲了。
那么假如我只是想用WebSocket向服务器发出查询哀求,然后服务器你就把查询结果给我就行了,其他用户就不消你广播推送了,简单点,就是我哀求,你就推送给我。这又该怎么办呢?是的, @SendToUser 就能办理这个标题。
5.2 @SendToUser

@MessageMapping("/hello") //使用MessageMapping注解来标识全部发送到“/hello”这个destination的消息,都会被路由到这个方法举行处理处罚. @SendToUser("/topic/greetings") //使用SendToUser注解来标识这个方法返回的结果,都会被发送到哀求它的用户的destination. //传入的参数Message为客户端发送过来的消息,是自动绑定的。 public Greeting greeting(HelloMessage message) throws Exception {    Thread.sleep(1000); // 模仿处理处罚延时     return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); //根据传入的信息,返回一个欢迎消息. } spring参考文档
websocket参考文档
到此这篇关于SpringBoot+STOMP协议实现就先容到这了。
参考:
https://www.jb51.net/article/214655.htm
https://developer.aliyun.com/article/763747
https://www.codercto.com/a/22176.html
https://www.cnblogs.com/goloving/p/15023025.html
https://www.cnblogs.com/question-sky/p/9636756.html
http://events.jianshu.io/p/c9ea50050acd
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-23 20:02, Processed in 0.188827 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表