乐优商城项目总结——10解决数据同步(RabbitMQ)

为什么要用RabbitMQ

前面已经完成了商品详情和搜索系统的开发,但是还有一些问题

  • 商品的原始数据保存在数据库中,增删改查都在数据库中完成。
  • 搜索服务数据来源是索引库,如果数据库商品发生变化,索引库数据不能及时更新。
  • 商品详情做了页面静态化,静态页面数据也不会随着数据库商品发生变化。

如果后台更改了商品价格,搜索页面和商品详情页显示的还是是旧的价格。

所以要用消息队列(MQ)来解决这个问题。

RabbitMQ介绍

消息队列是典型的:生产者、消费者模型。生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,这样就实现了生产者和消费者的解耦。

也就是

  • 商品服务对商品增删改以后,不用去操作索引库或静态页面,只是发送一条消息,也不关心消息被谁接收。
  • 搜索服务和静态页面服务接收消息,分别去处理索引库和静态页面。

消息队列实现有两种方式:
AMQP和JMS

区别:

  • JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式
  • JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。
  • JMS规定了两种消息模型;而AMQP的消息模型更加丰富

RabbitMQ基于AMQP协议,erlang语言开发,稳定性更好。项目使用RabbitMQ。

RabbitMQ几种模式

第一种:生产者——消息队列(MQ)——消费者

第二种:生产者——消息队列(MQ)——消费者 x 2(可以多个)

第三种:生产者——交换机——消息队列(MQ)——消费者 x 2(可以多个)

  • 交换机决定消息交给谁(一般都给),生产者不能决定给谁。

第四种:生产者——交换机——消息队列(MQ)——消费者 x 2(可以多个)

  • 生产者可以设置RoutingKey(一个标识)来决定给哪个队列。

第五种:生产者——交换机——消息队列(MQ)——消费者 x 2(可以多个)

  • 跟第四种一样,只不过可以使用通配符,比第四种更加灵活。

第六种: 不是消息队列,干掉不学。

在这里插入图片描述
项目中使用的是第五种。

实际使用

以发送短息的服务为例子

service:(生产者)
因为RabbitMQ是AMQP的一种实现方式,所以这里注入的是AmqpTemplate
其中:

  • ly.sms.exchange是 交换机的名字
  • sms.verify.code 是 RoutingKey
  	@Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * 验证手机号
     *
     * @param phone
     */
    public void sendCode(String phone) {
//        生成key
        String key = KEY_PREFIX + phone;
//        生成验证码
        HashMap<String, Object> msg = new HashMap<>();
//        验证码
        String code = NumberUtils.generateCode(6);
        msg.put("phone", phone);
        msg.put("code", code);
//        发送验证码
        amqpTemplate.convertAndSend("ly.sms.exchange", "sms.verify.code", msg);
//        保存验证码
        redisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES);
    }

SmsListener:(消费者,在短信微服务里)

@RabbitListener是在本地创建RabbitMQ里的交换机,队列等信息,也可以在RabbitMQ管理页面创建,不过这里创建更安全。

@Slf4j
@Component
@EnableConfigurationProperties(value = SmsProperties.class)
public class SmsListener {

    @Autowired
    private SmsProperties props;

    @Autowired
    private SmsUtil smsUtil;

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "ly.sms.verify.queue"),
            exchange = @Exchange(name = "ly.sms.exchange", type = ExchangeTypes.TOPIC),
            key = "sms.verify.code"
    ))
    public void listenVerifyCode(Map<String, Object> msg) {
        if (msg == null) {
            return;
        }
        String phone = (String) msg.remove("phone");
        if (StringUtils.isBlank(phone)) {
            return;
        }
        smsUtil.sendSms(props.getSignName(), props.getVerifyCodeTemplate(), phone, msg);
    }


}

消息丢失问题

消息丢失一共有四种解决办法

  1. ack(消费者确认)
  2. 持久化
  3. 发送消息前,消息持久化到数据库,并记录消息状态(可靠消息服务)
  4. 生产者确认(publisher confirm)

第一种:

当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。

  • 自动ACK:消息一旦被接收,消费者自动发送ACK
  • 手动ACK:消息接收后,不会发送ACK,需要手动调用

看情况选择哪一种,重要的手动,不重要的选择自动就可以。

第二种:
在这里插入图片描述
了解这几种持久化方式就好,SpringBoot已经默认开启。

第三种:

发送消息前,消息持久化到数据库,并记录消息状态(可靠消息服务)

发送消息专门有一个微服务,有准备状态(在这个状态持久化),等待发送,如果发送失败,写一个扫描程序,扫描发送失败的消息,重新发送。不过要保证幂等性(统一接口重复执行,结果一致),假如通知转账,有网络原因发送两次,可以通过设置标识,如果之前已经消费过一次,第二次就不执行了。

第四种:
最后一行就是开启生产者确认(publisher confirm),RabbitMQ接收到消息向生存者发送一条回值,说明MQ已经收到了。
在这里插入图片描述
RabbitMQ,有第四种方式,但其他的mq没有。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值