邮箱发送:用户注册后投递消息到rabbitmq中,由消息的消费方异步的发送邮件,提升系统响应速度
流量削峰:一般在秒杀活动中应用广泛,秒杀会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。用于控制活动人数,将超过此一定阀值的订单直接丢弃。缓解短时间的高流量压垮应用。
订单超时:利用rabbitmq的延迟队列,可以很简单的实现订单超时的功能,比如用户在下单后30分钟未支付取消订单。.
.各种消息中间件性能的比较:
TPS比较 一ZeroMq 最好,RabbitMq 次之, ActiveMq 最差。
持久化消息比较—zeroMq不支持,activeMq和rabbitMq都支持。持久化消息主要是指:MQ down或者MQ所在的服务器down了,消息不会丢失的机制。
可靠性、灵活的路由、集群、事务、高可用的队列、消息排序、问题追踪、可视化管理工具、插件系统、社区—RabbitMq最好,ActiveMq次之,ZeroMq最差。
高并发—从实现语言来看,RabbitMQ最高,原因是它的实现语言是天生具备高并发高可用的erlang语言。
综上所述:RabbitMQ的性能相对来说更好更全面,是消息中间件的首选。
Broker:它提供一种传输服务,它的角色就是维护一条从生产者到消费者的路线,保证数据能按照指定的方式进行传输,
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:消息的载体,每个消息都会被投到一个或多个队列。
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来.
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
vhost:虚拟主机,一个broker里可以有多个vhost,用作不同用户的权限分离。
Producer:消息生产者,就是投递消息的程序.
Consumer:消息消费者,就是接受消息的程序.
Channel:消息通道,在客户端的每个连接里,可建立多个channel.
所以在安装rabbitMQ之前,需要先安装Erlang 。
配置好环境变量
命令行输入命令:erl ,验证是否安装成功
下载运行rabbitmq-server-3.6.5 ,需要其他版本或者32位系统的,可以去官网下载。
依旧可以不改变默认进行安装。
需要注意:默认安装的RabbitMQ 监听端口是5672
然后是配置环境变量。
使用RabbitMQ 管理插件,可以更好的可视化方式查看Rabbit MQ 服务器实例的状态。
打开命令窗口:
输入命令:
“D:\JavaApp\RabbitMq\RabbitMQ Server\rabbitmq_server-3.7.13\sbin\rabbitmq-plugins.bat” enable rabbitmq_management
net stop RabbitMQ;
net start RabbitMQ
rabbitmqctl.bat list_users 查看用户和权限
add_user xudong 123456 增加用户
rabbitmqctl.bat set_user_tags xudong administrator 设置用户权限
org.springframework.boot
spring-boot-starter-amqp
1.5.2.RELEASE
package com.example.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created by Administrator on 2019/3/22.
* 定义队列queue
*/
@Configuration
public class RabbitConfig {
public static final String QUEUE_A = "QueueA";
public static final String QUEUE_B = "QueueB";
public static final String QUEUE_C = "QueueC";
public static final String EXCHANGE_A = "exchangeA";
public static final String EXCHANGE_B = "exchangeB";
public static final String EXCHANGE_C = "exchangeC";
public static final String ROUTINGKEY_A = "Routingkey_A";
public static final String ROUTINGKEY_B = "Routingkey_B";
public static final String ROUTINGKEY_C = "Routingkey_C";
/**
* 获取交换机: EXCHANGE_A
* 针对消费者配置交换机
* 1. 设置交换机类型
* 2. 将队列绑定到交换机
* FanoutExchange: 将消息分发到所有的绑定队列,无routingkey的概念
* HeadersExchange :通过添加属性key-value匹配
* DirectExchange:按照routingkey分发到指定队列
* TopicExchange:多关键字匹配
*/
@Bean
public DirectExchange getExchangeA() {
return new DirectExchange(EXCHANGE_A);
}
/**
* 获取队列 :QUEUE_A
* @return
*/
@Bean
public Queue queueA() {
return new Queue(QUEUE_A, true); //第一个参数队列名称,第二个队列持久化,遇到宕机不会丢失数据
}
/**
* binding:将交换机和队列通过路由关键字绑定,不绑定也可以,用默认的交换机
* @return
*/
@Bean
public Binding binding() {
return BindingBuilder.bind(queueC()).to(getExchangeC()).with(RabbitConfig.ROUTINGKEY_C);
}
/**
* 获取队列 :QUEUE_B
* @return
*/
@Bean
public Queue queueB() {
return new Queue(QUEUE_B, true); //第一个参数队列名称,第二个队列持久化,遇到宕机不会丢失数据
}
/**
* 获取队列 :QUEUE_C
* @return
*/
@Bean
public Queue queueC() {
return new Queue(QUEUE_C, true); //第一个参数队列名称,第二个队列持久化,遇到宕机不会丢失数据
}
@Bean
public DirectExchange getExchangeC() {
return new DirectExchange(EXCHANGE_C);
}
}
##rabbitMq
spring.rabbitmq.username=xudong
spring.rabbitmq.password=123456
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.virtual-host=/
# 手动ACK 不开启自动ACK模式,目的是防止报错后未正确处理消息丢失 默认 为 none
spring.rabbitmq.listener.simple.acknowledge-mode=manual
package com.example.listener;
import com.example.config.RabbitConfig;
import com.example.model.Student;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* Created by Administrator on 2019/3/22.
*/
@Component
public class BookHandler {
private static final Logger log = LoggerFactory.getLogger(BookHandler.class);
/**
* TODO 该方案是 spring-boot-data-amqp 默认的方式,不太推荐。具体推荐使用 listenerManualAck()
* 默认情况下,如果没有配置手动ACK, 那么Spring Data AMQP 会在消息消费完毕后自动帮我们去ACK
* 存在问题:如果报错了,消息不会丢失,但是会无限循环消费,一直报错,如果开启了错误日志很容易就吧磁盘空间耗完
* 解决方案:手动ACK,或者try-catch 然后在 catch 里面讲错误的消息转移到其它的系列中去
* spring.rabbitmq.listener.simple.acknowledge-mode=manual
*
*
* @param stu 监听的内容
*/
@RabbitListener(queues = {RabbitConfig.QUEUE_B})
public void listenerAutoAck(Student stu, Message message, Channel channel) {
// TODO 如果手动ACK,消息会被监听消费,但是消息在队列中依旧存在,如果 未配置 acknowledge-mode 默认是会在消费完毕后自动ACK掉
final long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
log.info("[listenerAutoAck 监听的消息] - [{}]", stu.toString());
// TODO 通知 MQ 消息已被成功消费,可以ACK了
channel.basicAck(deliveryTag, false);
} catch (IOException e) {
try {
// TODO 处理失败,重新压入MQ
channel.basicRecover();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
@RabbitListener(queues = {RabbitConfig.QUEUE_C})
public void listenerManualAck(Student stu, Message message, Channel channel) {
log.info("[listenerManualAck 监听的消息] - [{}]", stu.toString());
try {
// TODO 通知 MQ 消息已被成功消费,可以ACK了
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (IOException e) {
// TODO 如果报错了,那么我们可以进行容错处理,比如转移当前消息进入其它队列
try {
// TODO 处理失败,重新压入MQ
channel.basicRecover();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
package com.example.controller;
import com.example.config.RabbitConfig;
import com.example.listener.BookHandler;
import com.example.model.Student;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Created by Administrator on 2019/3/22.
*/
@Controller
@RequestMapping("/rabbigMq")
@Api(value="StudentRabbitMqController",description = "rabbitMq测试学生")
public class StudentRabbitMqController {
private final RabbitTemplate rabbitTemplate;
private static final Logger log = LoggerFactory.getLogger(StudentRabbitMqController.class);
@Autowired
public StudentRabbitMqController(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
/**
* this.rabbitTemplate.convertAndSend(RabbitConfig.DEFAULT_BOOK_QUEUE, book); 对应 {@link BookHandler#listenerAutoAck}
* this.rabbitTemplate.convertAndSend(RabbitConfig.MANUAL_BOOK_QUEUE, book); 对应 {@link BookHandler#listenerManualAck}
*/
@ApiOperation(value = "rabbitmq消息队列")
@RequestMapping(value = "/stu",method = {RequestMethod.GET})
@ResponseBody
public String proMsg() {
Student stu = new Student();
stu.setId((short)1);
stu.setName("学习mq的旭东");
log.info("生产者生产消息:"+stu);
this.rabbitTemplate.convertAndSend(RabbitConfig.QUEUE_B, stu);
this.rabbitTemplate.convertAndSend(RabbitConfig.QUEUE_C, stu);
return "success";
}
}