• Spring+Stomp+ActiveMq实现websocket长连接


    stomp.js+spring+sockjs+activemq实现websocket长连接,使用java配置。

    pom.xml(只列出除了spring基本依赖意外的依赖,spring-version为4.3.3.RELEASE):

    复制代码
    <dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
            <version>1.1</version>
            <scope>provided</scope> 
            <!-- 注意,scope必须为provided,否则runtime会冲突,如果使用tomcat 8,还需要将TOMCAT_HOME/lib下的javax.websocket-api.jar一并删除 -->
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jms</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.apache.xbean</groupId>
          <artifactId>xbean-spring</artifactId>
          <version>3.16</version>
        </dependency>
        <dependency>
          <groupId>org.apache.activemq</groupId>
          <artifactId>activemq-core</artifactId>
          <version>5.7.0</version>
        </dependency>
        <dependency>
          <groupId>org.apache.activemq</groupId>
          <artifactId>activemq-pool</artifactId>
          <version>5.12.1</version>
        </dependency>
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-core</artifactId>
          <version>2.8.1</version>
        </dependency>
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.8.1</version>
        </dependency>
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-annotations</artifactId>
          <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-net</artifactId>
            <version>2.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.0.33.Final</version>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core</artifactId>
            <version>2.0.8.RELEASE</version>
        </dependency>
    复制代码

    StompConfig.java

    复制代码
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.env.Environment;
    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;
    
    /**
     * @EnableWebSocketMessageBroker包含@EnableWebSocket
     * @author TD
     *
     */
    @Configuration
    @EnableWebSocketMessageBroker
    @PropertySource("classpath:activemq.properties")
    public class StompConfig extends AbstractWebSocketMessageBrokerConfigurer{
    
        @Autowired
        private Environment env;
        /**
         * 注册代理,暴露节点用于连接。
         */
        @Override
        public void registerStompEndpoints(StompEndpointRegistry registry) {
            // TODO Auto-generated method stub
            registry.addEndpoint("/stompEndPoint.do").withSockJS();
        }
        
        /**
         * 修改消息代理的配置,默认处理以/topic为前缀的消息
         * setApplicationDestinationPrefixes:配置请求的根路径,表示通过MessageMapping处理/td/*请求,不会发送到代理
         * enableStompBrokerRelay:配置代理,匹配路径的请求会进入代理:mq等
         */
        @Override
        public void configureMessageBroker(MessageBrokerRegistry registry) {
            /*基于内存实现的stomp代理,单机适用
            registry.enableSimpleBroker("/queue","/topic");
            */
            //以/td为目的地的消息使用MessageMapping控制器处理,不走代理
            registry.setApplicationDestinationPrefixes("/td","/app");
            /**
             * 设置单独发送到某个user需要添加的前缀,用户订阅地址/user/topic/td1地址后会去掉/user,并加上用户名(需要springsecurity支持)等唯一标识组成新的目的地发送回去,
             * 对于这个url来说 加上后缀之后走代理。发送时需要制定用户名:convertAndSendToUser或者sendtouser注解.
            registry.setUserDestinationPrefix("/user")
             */
            /*基于mq实现stomp代理,适用于集群。
             * 以/topic和/queue开头的消息会发送到stomp代理中:mq等。
             * 每个mq适用的前缀不一样且有限制。activemq支持stomp的端口为61613
    */ registry.enableStompBrokerRelay("/topic","/queue") .setRelayHost(env.getProperty("mq.brokenHost")) .setRelayPort(Integer.parseInt(env.getProperty("mq.brokenPort"))) .setSystemLogin(env.getProperty("mq.username")) .setSystemPasscode(env.getProperty("mq.password")) .setClientLogin(env.getProperty("mq.username")) .setClientPasscode(env.getProperty("mq.password")); /* * systemLogin:设置代理所需的密码 * client:设置客户端连接代理所需的密码,默认为guest */ } }
    复制代码

    JSP页面:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>stomp测试</title>
    </head>
    <body>
    <button onclick="test()">stomp发送(服务端返回信息走订阅2)</button>
    <button onclick="test2()">stomp订阅1@subscribeMapping,不过代理)</button>
    <button onclick="test3()">stomp订阅2 :通过代理</button>
    </body>
    <script type="text/javascript" src="/spring15_socket/js/sockjs-0.3.4.min.js"></script>
    <script type="text/javascript" src="/spring15_socket/js/stomp.js"></script>
    <script type="text/javascript">
    var url = 'http://localhost:8089/spring15_socket/stompEndPoint.do';
    //创建sockjs链接
    var sock = new SockJS(url);
    //创建stomp客户端
    var stomp = Stomp.over(sock);
    var msg = JSON.stringify({'name':'td','age':13});
    stomp.connect({},function(frame){
        console.log('connecting...'+frame)
        stomp.send('/app/stomp1.do',{},msg);
    })
     
    function test(){
        stomp.send('/app/stomp1.do',{},msg);
    }
     
    function test2(){
        stomp.subscribe('/app/stomp2.do',function(msg){
            console.log("subscribemapping:"+JSON.parse(msg.body).content);
        })
         
    }
     
    function test3(){
        stomp.subscribe('/topic/hello',function(msg){
            console.log("topicHello:"+JSON.parse(msg.body).content);
        })
         
    }
     
    </script>
    </html>

      

    控制器:

    复制代码
    package spring15_socket.controller;
    
    import java.sql.SQLException;
    
    import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
    import org.springframework.messaging.handler.annotation.MessageMapping;
    import org.springframework.messaging.handler.annotation.SendTo;
    import org.springframework.messaging.simp.annotation.SubscribeMapping;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import spring15_socket.bean.User;
    
    @Controller
    public class TestController {
        
        
        @RequestMapping(value="/sockjs.do")
        public String test0(Model model){
            return "sockjs";
        }    
        
        @RequestMapping(value="/stompjs.do")
        public String test1(Model model){
            return "stomp";
        }    
        
        /**
         * 表示该方法处理客户端发来的/td/stomp1.do或者/app/stomp1.do。
         * sendTo:重新指定发送的位置,默认原路返回(url会加上/topic前缀)
         * 需走代理
         */
        @MessageMapping("/stomp1.do")
        @SendTo("/topic/hello")
        public User handleStomp(User user) {
            System.out.println("stomp接收到客户端的请求:"+user);
            user.setName("messagemapping返回user");
            return user;
            
        }
        
        /**
         * 用于处理messagemapping抛出的异常,类比exceptionhandler
         * @return
         */
        @MessageExceptionHandler({Exception.class,SQLException.class})
        @SendTo("/topic/errorTopic")
        public User errorHandler(Throwable t) {
            System.out.println("异常统一处理");
            User user = new User();
            user.setName("异常统一处理:"+t.getMessage());;
            return user;
        }
        
        
        /**
         * 触发方式和messagemapping一致。
         * sendTo:重新指定发送的位置,默认原路返回(url会加上/topic前缀)
         * 使用subscribemapping不走代理
         */
        @SubscribeMapping("/stomp2.do")
        @SendTo("/topic/hello")
        public User subsTest() {
            User user2 = new User();
            user2.setName("订阅name");
            user2.setPhone("subscribePhone");
            return user2;
        }
        
        
    }
    复制代码

    进入activemq的控制台,点击connection可以看到stomp连接:(这里连接了两个客户端,可以点击超链接查看连接状态)

    查看启动日志可以发现:表明启动成功

    进入页面点击第一个按钮,会触发send:

    同时控制台可以看到:

    单独点击第二个按钮不会有返回消息,点击第三个按钮之后会订阅/topic/hello,此时点击第一个触发send。在服务端执行完毕后通过sendto注解发布到/topic/hello,此时浏览器控制台会输出该频道发布的内容

    注意点:

    1:基于activemq作为代理,连接的端口号为61613。

    2:setSystemLogin表示设置服务器连接代理(activemq)的账号密码,setClientLogin表示连接过来的客户端连接代理所需要的账号密码。

  • 相关阅读:
    每日随笔
    每日随笔
    每日随笔
    每日随笔
    nginx的Rewrite重写
    多台机器做动静分离
    单台机器动静分离
    四层负载均衡实践
    四层负载均衡特点
    四层负载均衡做端口转发
  • 原文地址:https://www.cnblogs.com/Jeely/p/11133543.html
Copyright © 2020-2023  润新知