Springboot+RabbitMQ + WebSocket 给前端推送消息 + 定时队列

环境 :
JDK : 1.8
Springboot : 2.1.6.RELEASE
ErLang : otp_win64_22.0.exe
RabbitMQ : 3.7.16

下载地址

开启RabbitMQ的stomp插件 .
在RabbitMQ安装目录sbin文件夹里执行命令 :

rabbitmq-plugins enable rabbitmq_stomp

Springboot pom.xml引入RabbitMQ 和 WebSocket

	<!--RabbitMQ-->
    <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-amqp</artifactId>
     </dependency>
     <!--WebSocket-->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-websocket</artifactId>
     </dependency>
     <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.46</version>
    </dependency>
    <!--JSON-->
    <dependency>
        <groupId>net.sf.json-lib</groupId>
        <artifactId>json-lib</artifactId>
        <version>2.4</version>
        <classifier>jdk15</classifier>
    </dependency>

application.yml

 # RabbitMQ 配置
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    # 开启消息发送确认
    publisher-confirms: true
    publisher-returns: true
    listener:
      simple:
        acknowledge-mode: manual

RabbitMQ

配置类

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;


/**
 * @ClassName RabbitConfig
 * @Description RabbitMQ 配置类
 * @Date 2019-07-20 11:44
 * @Version 1.0.0
 **/
@Configuration
public class RabbitConfig {

    //websocket 消息队列
    public static final String msg_queue = "msg_queue";

    //消息交换机
    public static final String msg_exchang = "msg_exchang";

    //消息路由键
    public static final String msg_routing_key = "msg_routing_key";

    /**
     * 消息队列
     * @return
     */
    @Bean
    public Queue msgQueue(){
        return new Queue(msg_queue);
    }

    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange(msg_exchang);
    }

    /**
     * 消息队列绑定消息交换机
     * @return
     */
    @Bean
    public Binding msgBinding(){
        return BindingBuilder.bind(msgQueue()).to(directExchange()).with(msg_routing_key);
    }
}

消息提供者

import com.alibaba.fastjson.JSONObject;
import com.lawyer.entity.Msg;
import com.lawyer.webSocket.WebSocketServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.UUID;

/**
 * @ClassName RabbitConsultProduct
 * @Description RabbitMQ 消息提供者
 * @Date 2019-07-20 11:54
 * @Version 1.0.0
 **/
@Slf4j
@Component
public class RabbitProduct {

    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 构造方法注入rabbitTemplate
     */
    @Autowired
    public RabbitProduct(RabbitTemplate rabbitTemplate){
        this.rabbitTemplate = rabbitTemplate;
    }



    //发送消息 推送到websocket    参数自定义 转为String发送消息
    public void sendMSG(Msg msg){
        CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
        //把消息对象放入路由对应的队列当中去
        rabbitTemplate.convertAndSend(RabbitConfig.msg_exchang,RabbitConfig.msg_routing_key, JSONObject.toJSON(msg).toString(), correlationId);
    }

}

消息消费者

import com.lawyer.entity.Msg;
import com.lawyer.utils.DateUtils;
import com.lawyer.webSocket.WebSocketServerEndpoint;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;

/**
 * @ClassName RabbitReceive
 * @Description RabbitMQ 定时消息队列 消费监听回调
 * @Date 2019-07-20 12:09
 * @Version 1.0.0
 **/
@Slf4j
@Component
public class RabbitConsumer {

    private static RabbitConsumer rabbitConsumer;
    @Resource
    private WebSocketServerEndpoint webSocketServerEndpoint; //引入WebSocket

    /**
     * 构造方法注入rabbitTemplate
     */
    @PostConstruct
    public void init() {
        rabbitConsumer = this;
        rabbitConsumer.webSocketServerEndpoint = webSocketServerEndpoint;
    }

    @RabbitListener(queues = RabbitConfig.msg_queue) //监听队列
    public void msgReceive(String content, Message message, Channel channel) throws IOException {
        log.info("----------------接收到消息--------------------"+content);
        //发送给WebSocket 由WebSocket推送给前端
        rabbitConsumer.webSocketServerEndpoint.sendMessageOnline(content);
        // 确认消息已接收
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

}

WebSocket配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * WebSocket配置类
 */
@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;


/**
 *  WebSocket 服务配置类
 *  定义 userId 为当前连接(在线) WebSocket 的用户
 */
@Slf4j
@Component
@ServerEndpoint(value = "/ws/{userId}")
public class WebSocketServerEndpoint {

    private Session session; //建立连接的会话
    private String userId; //当前连接用户id   路径参数

    /**
     * 存放存活的Session集合(map保存)
     */
    private static ConcurrentHashMap<String , WebSocketServerEndpoint> livingSession = new ConcurrentHashMap<>();

    /**
     *  建立连接的回调
     *  session 建立连接的会话
     *  userId 当前连接用户id   路径参数
     */
    @OnOpen
    public void onOpen(Session session,  @PathParam("userId") String userId){
        this.session = session;
        this.userId = userId;
        livingSession.put(userId, this);

        log.debug("----[ WebSocket ]---- 用户id为 : {} 的用户进入WebSocket连接 ! 当前在线人数为 : {} 人 !--------",userId,livingSession.size());
    }

    /**
     *  关闭连接的回调
     *  移除用户在线状态
     */
    @OnClose
    public void onClose(){
        livingSession.remove(userId);
        log.debug("----[ WebSocket ]---- 用户id为 : {} 的用户退出WebSocket连接 ! 当前在线人数为 : {} 人 !--------",userId,livingSession.size());
    }

    @OnMessage
    public void onMessage(String message, Session session,  @PathParam("userId") String userId) {
        log.debug("--------收到用户id为 : {} 的用户发送的消息 ! 消息内容为 : ------------------",userId,message);
        //sendMessageToAll(userId + " : " + message);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("----------------WebSocket发生错误----------------");
        log.error(error.getStackTrace() + "");
    }

    /**
     *  根据userId发送给用户
     * @param userId
     * @param message
     */
    public void sendMessageById(String userId, String message) {
        livingSession.forEach((sessionId, session) -> {
            //发给指定的接收用户
            if (userId.equals(session.userId)) {
                sendMessageBySession(session.session, message);
            }
        });
    }

    /**
     *  根据Session发送消息给用户
     * @param session
     * @param message
     */
    public void sendMessageBySession(Session session, String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error("----[ WebSocket ]------给用户发送消息失败---------");
            e.printStackTrace();
        }
    }

    /**
     *  给在线用户发送消息
     * @param message
     */
    public void sendMessageOnline(String message) {
        livingSession.forEach((sessionId, session) -> {
            if(session.session.isOpen()){
                sendMessageBySession(session.session, message);
            }
        });
    }

}

前端VUE代码

created() {
    var websocket = null
    if ('WebSocket' in window) {
      var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://'
      websocket = new WebSocket(protocol + 'localhost:8090/ws/0/1')
    } else {
      alert('该浏览器不支持WebSocket')
    }

    websocket.onopen = function(event) {
      // heartCheck.reset().start();
      console.log('建立WebSocket连接')
    }

    websocket.onclose = function(event) {
      console.log('断开WebSocket连接')
    }

    websocket.onmessage = function(event) {
      console.log('收到消息' + event.data)
    }

    websocket.onerror = function(event) {
      console.log('websocket通信发生错误')
    }

    window.onbeforeunload = function(event) {
      websocket.close()
    }
  },
  methods: {
    handleClickOutside() {
      this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
    }
  }

这里需要做心跳连接机制 :
自行设置

var heartCheck = {
        timeout: 55000,        // 在接近断开的情况下以通信的方式去重置连接时间。
        timeoutObj: null,
        serverTimeoutObj: null,
        reset: function(){
            clearTimeout(this.timeoutObj);
            clearTimeout(this.serverTimeoutObj);
            return this;
        },
        start: function(){
            this.serverTimeoutObj = setInterval(function(){
                if(websocket.readyState == 1){
                    console.log("连接状态,发送消息保持连接");
                    websocket.send("ping");
                    heartCheck.reset().start();    // 如果获取到消息,说明连接是正常的,重置心跳检测
                }else{
                    console.log("断开状态,尝试重连");
                }
            }, this.timeout)
        }
    }

延迟(定时)队列文章地址

  • 3
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值