rabbitMQ原理
Broker:简单来说就是消息队列服务器实体。
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。
producer:消息生产者,就是投递消息的程序。
consumer:消息消费者,就是接受消息的程序。
channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
消息队列的使用过程大概如下
总结:exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。
Direct交换机
完全根据key进行投递的叫做Direct交换机,例如,绑定时设置了routing key为”abc”,那么客户端提交的消息,只有设置了key为”abc”的才会投递到队列。
所有发送到Direct Exchange的消息被转发到RouteKey中指定的Queue。
Direct模式,可以使用rabbitMQ自带的Exchange:default Exchange 。所以不需要将Exchange进行任何绑定(binding)操作 。消息传递时,RouteKey必须完全匹配,才会被队列接收,否则该消息会被抛弃。
2.1框架
rabbitMQ-Direct交换机模式是最简单的模式,就发送一串字符串,这个字符串为key,接收的时候也完全以这个字符串本来来确定,不需要绑定任何exchange,使用默认的就行。我们以这个模式开始在原来的项目上继续集成。
首先是引入依赖:
org.springframework.boot
spring-boot-starter-amqp
appilication.yml:
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtual-host: /
listener:
simple:
concurrency: 10
max-concurrency: 10
prefetch: 1
auto-startup: true
default-requeue-rejected: true
template:
retry:
enabled: true
initial-interval: 1000
max-attempts: 3
max-interval: 10000
multiplier: 1.0
rabbitMQ配置类MQConfig:
@Configuration
public class MQConfig {
//MQ name
public static final String DIRECT_QUEUE_NAME = "queue";
@Bean
public Queue queue(){
return new Queue(QUEUE_NAME,true);
}
}
发送者MQSender:
@Service
@Slf4j
public class MQSender {
@Autowired
private AmqpTemplate amqpTemplate;
public void send(Object message){
amqpTemplate.convertAndSend(MQConfig.DIRECT_QUEUE_NAME,message);
log.info("send:{}",message);
}
}
接收者MQReceiver:
@Service
@Slf4j
public class MQReceiver {
@RabbitListener(queues = MQConfig.DIRECT_QUEUE_NAME)
public void receive(String message){
log.info("receive:{}",message);
}
}
这样,就完成了最简单的一个字符串的发送-接受。可以在controller中随便测试一下:
@Controller
@RequestMapping("/test")
public class TestController {
@Autowired
private MQSender mqSender;
@RequestMapping("/mq")
@ResponseBody
public String mq(){
mqSender.send("hello world");
return "success";
}
}
2.2具体应用
秒杀用户入队:
//发送方
@Autowired
MQSender sender;
//用户入队
MiaoshaMessage mm = new MiaoshaMessage();
mm.setUser(user);
mm.setGoodsId(goodsId);
sender.sendMiaoshaMessage(mm);
具体的sender:
@Service
public class MQSender {
@Autowired
AmqpTemplate amqpTemplate ;
public void sendMiaoshaMessage(MiaoshaMessage mm) {
String msg = RedisService.beanToString(mm);
amqpTemplate.convertAndSend(MQConfig.MIAOSHA_QUEUE, msg);
}
}
具体的receiver:
@RabbitListener(queues = MQConfig.MIAOSHA_QUEUE)
public void receive(String message){
log.info("receive message:{}",message);
MiaoshaMessage msg = RedisService.stringToBean(message,MiaoshaMessage.class);
MiaoshaUser user = msg.getUser();
long goodsId = msg.getGoodsId();
//判断数据库库存是否真的足够
GoodsVo goodsVo = goodsService.getGoodsVoByGoodsId(goodsId);
if(goodsVo.getStockCount() <= 0){
return;
}
//判断是否已经秒杀到了
MiaoshaOrder miaoshaOrder = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(),goodsId);
if(miaoshaOrder != null){
return;
}
//减库存、下订单、写入秒杀订单,需要在一个事务中执行
OrderInfo orderInfo = miaoshaService.miaosha(user,goodsVo);
}
入队的用户,一定能秒杀到商品吗?