SSM+RabbitMQ+Stomp模拟推送关注的人发布动态/登录上线

效果如下用户 【me】和【xiaoming】的共同关注用户【up】上线后,两个粉丝均能实时接收到上线的推送消息。

在这里插入图片描述在这里插入图片描述
零、RabbitMQ的安装
1、使用docker拉取RabbitMQ镜像
2、开启RabbitMQ,并暴露端口

 docker run -d --hostname rabbit-stomp 
     --name rabbit-stomp-test2 -p 15672:15672 
     -p 1883:1883 -p 61613:61613 -p 5672:5672 
     -p 15674:15674 rabbitmq:3-management

3、安装stomp插件,stomp默认接口15674,
官方使用说明:https://www.rabbitmq.com/web-stomp.html

//进入rabbit操作
[root@codeman ~]# docker container exec -it rabbit-stomp-test2 bash
//开启stomp插件
[root@rabbit-stomp:]/# rabbitmq-plugins enable rabbitmq_web_stomp
Enabling plugins on node rabbit@rabbit-stomp:
rabbitmq_web_stomp
The following plugins have been configured:
  rabbitmq_management
  rabbitmq_management_agent
  rabbitmq_stomp
  rabbitmq_web_dispatch
  rabbitmq_web_stomp
Applying plugin configuration to rabbit@rabbit-stomp...
The following plugins have been enabled:
  rabbitmq_stomp
  rabbitmq_web_stomp

started 2 plugins.

安装完毕,打开RabbitMQ管理页面(http://ip:15672 用户名密码默认guest)可以看到stomp
在这里插入图片描述
一、jar包准备

<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit</artifactId>
    <version>1.7.5.RELEASE</version>
</dependency>

二、配置RabbitMQ连接工厂以及消息生产者(封装了RabbitMQ的方法)
1、配置RabbitMQ信息RabiitMQ.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/rabbit
     http://www.springframework.org/schema/rabbit/spring-rabbit-1.2.xsd">
    <!--引入properties配置文件-->
    <bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:rabbit.properties</value>
            </list>
        </property>
    </bean>
    <!--配置connection-factory,指定连接rabbit server参数 -->
    <rabbit:connection-factory id="connectionFactory"
                               username="${rmq.manager.user}" 
                               password="${rmq.manager.password}"
                               host="${rmq.ip}"
                               port="${rmq.port}"
    />
    <!--生产者-->
    <bean id="messageSender" class="com.codeman.rabbit.message.MessageSender">
        <constructor-arg ref="connectionFactory"/>
    </bean>
</beans>

rabbit.properties

rmq.ip=192.168.1.111
rmq.producer.num=20
rmq.port=5672
rmq.manager.user=guest
rmq.manager.password=guest

2、消息生产者:MessageSender.java(其实用其自带方法就足够了)

package com.codeman.rabbit.message;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Set;

/** 定制发送信心
 * Created with IntelliJ IDEA.
 *
 * @author 张鸿杰
 * Date:2019-04-19
 * Time:15:06
 */
public class MessageSender {
    private ConnectionFactory connectionFactory;
    private AmqpTemplate amqpTemplate;
    private RabbitAdmin rabbitAdmin;
    private Queue queue;
    private Exchange exchange = new FanoutExchange("followmessage");

    public MessageSender(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
        rabbitAdmin = new RabbitAdmin(connectionFactory);
        amqpTemplate = new RabbitTemplate(connectionFactory);
         ((RabbitTemplate) amqpTemplate).setMessageConverter(new Jackson2JsonMessageConverter()); //可以自定序列化格式为json
    }

    public Exchange getExchange() {
        return exchange;
    }

    public void setExchange(Exchange exchange) {
        this.exchange = exchange;
    }

    public ConnectionFactory getConnectionFactory() {
        return connectionFactory;
    }

    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
        rabbitAdmin = new RabbitAdmin(connectionFactory);
        amqpTemplate = new RabbitTemplate(connectionFactory);
    }

    public AmqpTemplate getAmqpTemplate() {
        return amqpTemplate;
    }
    public void setAmqpTemplate(AmqpTemplate amqpTemplate) {
        this.amqpTemplate = amqpTemplate;
    }
    public void sendDataToQueue(Object message) {
        amqpTemplate.convertAndSend(message);
    }

    /**
     * 发送到自己的频道上,粉丝关注后需要绑定自己频道的规则,就能形成广播效果
     * @param routingKey  自己的频道
     * @param message 消息
     * @return void
     * @author zhj
     * @creed: Talk is cheap,show me the code
     * @date 2019/4/19
     */
    public void sendDataToQueue(String  routingKey, String suffix, Object message) {
            amqpTemplate.convertAndSend(exchange.getName(), routingKey + suffix, message);
    }

    public void sendDataToQueue(String exchange, String routingKey,String suffix, Object message) {
            amqpTemplate.convertAndSend(exchange, routingKey+"."+suffix,  message);
    }

    public void createExchange(Exchange exchange) {
        rabbitAdmin.declareExchange(exchange);
    }
    public String createQueue(String queue) {
        this.queue = new Queue(queue);
        return rabbitAdmin.declareQueue(this.queue);
    }

    /**
     * 创建自己的频道,绑定规则
     * @param routKey 设定唯一用户标识.动态类型.编号(用户标识.# /用户标识.*.*)
     * @return void
     * @author zhj
     * @creed: Talk is cheap,show me the code
     * @date 2019/4/19
     */
    public void bindQueue2Exchange(String queue, String routKey) {
        rabbitAdmin.declareBinding(new Binding(
                queue,
                Binding.DestinationType.QUEUE,
                exchange.getName(),
                routKey,
                null));
    }

    /**
     * 解绑频道,比如退出登录就解绑好友登录提醒
     * @param routKey
     * @return void
     * @author zhj
     * @creed: Talk is cheap,show me the code
     * @date 2019/4/19
     */
    public void removeBinding(String routKey, String queue) {
        rabbitAdmin.removeBinding(new Binding(
                queue,
                Binding.DestinationType.QUEUE,
                exchange.getName(),
                routKey,
                null));
    }
    /**
     * 删除自己的频道
     * @param queue 队列
     * @return void
     * @author zhj
     * @creed: Talk is cheap,show me the code
     * @date 2019/4/19
     */
    public boolean deleQueue(String queue) {
        return rabbitAdmin.deleteQueue(queue);
    }

    /**
     * 清空队列中的锁头消息
     * @param queueName 队列名
     * @param noWait 是否等待
     * @return boolean
     * @author zhj
     * @creed: Talk is cheap,show me the code
     * @date 2019/4/20
     */
    public void purgeQueue(final String queueName, final boolean noWait) {
        rabbitAdmin.purgeQueue(queueName, noWait);
    }
}

将RabbitMQ.xml引入applicationContext.xml中

    <!--导入RabbitMQ配置文件-->
    <import resource="rabbitMQ.xml"/>

至此,后端配置全部完成。

三、前端配置:使用Stomp来实时监听RabbitMQ消息
js依赖:

<!-- https://mvnrepository.com/artifact/org.webjars.bower/stomp-websocket -->
<dependency>
    <groupId>org.webjars.bower</groupId>
    <artifactId>stomp-websocket</artifactId>
    <version>2.3.4</version>
</dependency>

jsp页面common/sockjs_stomp_rabbitmq.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2019/4/20
  Time: 9:42
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head >
    <title>测试</title>
    <script src="/webjars/stomp-websocket/2.3.4/lib/stomp.min.js"></script>
</head>
<body lang="en">
<script>
    var ws = new WebSocket('ws://192.168.1.111:15674/ws');
    var client = Stomp.over(ws);
    var on_connect = function() {
        //data.body是接收到的数据
        client.subscribe("me2.loginStatus", function(data) {
            var msg = data.body;
            alert("收到数据:" + msg);
    });
    };
    var on_error =  function() {
        console.log('error');
    };
    client.connect('guest', 'guest', on_connect, on_error, '/');
</script>
</body>
</html>

测试代码:

package com.codeman.rabbit.controller;

import com.codeman.rabbit.entity.User;
import com.codeman.rabbit.message.MessageSender;
import com.codeman.rabbit.service.UserService;
import org.apache.log4j.Logger;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * Created with IntelliJ IDEA.
 *
 * @author 张鸿杰
 * Date:2019-04-17
 * Time:20:40
 */
@Controller
public class LoginController  {
    @Autowired
    private MessageSender messageSender;
    @Autowired
    Logger logger;
    @Autowired
    private UserService userService;
    @GetMapping(value = "/goLogin")
    public String goLogin() {
        return "redirect:/index.jsp";
    }
    @PostMapping("/login")
    public String login(User user, HttpServletRequest request) {
        logger.warn("=====================登录了哦");
        User user1 = userService.login(user);
        if (user1 != null) {
            //TODO 0.创建队列,这些创建队列应该是在注册的时候创建的,这边作为模拟
            messageSender.createQueue(user1.getName() + user1.getId()+ ".loginStatus");
            messageSender.createQueue(user1.getName() + user1.getId() + ".news");
            //1.清空队列中的消息
            //清空队列中的消息
            messageSender.purgeQueue(user1.getName() + user1.getId() + ".loginStatus", true);
            //2.发布登录消息到自己的频道上
            messageSender.sendDataToQueue(user1.getName() + user1.getId() ,".loginStatus", user1.getName() + "," + new Date() + ",登录了");
            logger.warn("================发送完毕");
            request.getSession().setAttribute("user", user1);
            return "main";
        }
        request.setAttribute("loginError", "登录失败!");
        return "redirect:/index.jsp";
    }
    @GetMapping("/logout")
    public String logout(HttpServletRequest request) {
        User user = (User) request.getSession().getAttribute("user");
        request.getSession().removeAttribute("user");
        request.setAttribute("loginError", "请重新登录!");
        return "forward:/index.jsp";
    }
    @GetMapping("/follow/{nameNID}")
    public void follow(@PathVariable String nameNID, HttpSession session) {
        User user1  = (User) session.getAttribute("user");
        //关注了人之后,绑定规则到Exchange
        //绑定频道接口
        messageSender.bindQueue2Exchange(user1.getName() + user1.getId()+ ".loginStatus",nameNID + ".loginStatus");
        //新动态一直都会有,要绑在创建用户那边
        messageSender.bindQueue2Exchange(user1.getName() + user1.getId() + ".news", nameNID + ".news");
    }

    @GetMapping("/unFollow/{nameNID}")
    public void unFollow(@PathVariable String nameNID, HttpSession session) {
        User user1  = (User) session.getAttribute("user");
        //取消关注后,解除绑定
        messageSender.removeBinding(nameNID + ".loginStatus", user1.getName() + user1.getId()+ ".loginStatus");
        messageSender.removeBinding(nameNID + ".news", user1.getName() + user1.getId()+ ".news");
    }
    @GetMapping("/link2RabbitMQ")
    public String link2RabbitMQ() {
        return "/common/sockjs_stomp_rabbitmq";
    }
}

`-----------------------------------------------------------------
后续的js方法提取封装,后端RabbitMQ发送消息、创建绑定队列等等可以使用AOP进行封装,这边就不详细写出了,大家自己进行拓展吧。
有什么不足之处,还请大家留下评论进行交流。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值