RocketMQ(二)RocketMQ实战

文章目录

    • 一、RocketMQ实战
      • 1.1 批量消息发送(消息体为多条消息)
      • 1.2 消息发送队列自选择
      • 1.3 事务消息
      • 1.4 SpringCloud集成RocketMQ
    • 二、最佳实践
      • 2.1 生产者
        • 2.1.1 发送消息注意事项(设置tags标识/设置keys唯一索引/日志打印)
        • 2.1.2 消息发送失败处理方式(RocketMQ会自动重试/发送失败要做补偿机制)
      • 2.2 消费者
        • 2.2.1 消费过程幂等(保存消费结果进DB,避免重复消费)
        • 2.2.2 消费打印日志
      • 2.3 Broker
    • 三、相关问题
      • 3.1 为什么要使用消息队列
      • 3.2 为什么要选择RocketMQ
      • 3.3 RocketMQ有什么优缺点
      • 3.4 消息队列有哪些消息模型(队列模型和发布订阅模型)
      • 3.5 RocketMQ用什么消息模型(发布-订阅)
      • 3.6 RocketMQ消息的消费模式(集群和广播)
      • 6.7 RoctetMQ架构
      • 3.8 如何保证消息的可靠性不丢失
      • 3.8.1 生产阶段(发送失败时要有补偿机制)
      • 3.8.2 存储阶段(同步刷盘更可靠)
      • 3.8.3 消费阶段(执行完所有消费业务逻辑之后,再发送消费确认;将消息保存到DB再慢慢消费)
      • 3.9 如何处理消息重复的问题(消息中的keys字段一般是唯一的,可以根据keys字段保存消费结果到DB,进而避免重复消费)
      • 3.10 怎么处理消息积压
      • 3.11 顺序消息如何实现
      • 3.12 如何实现消息过滤(tag/SQL表达式)
      • 3.13 延时消息(RocketMQ本身支持延时消息/message.setDelayTimeLevel)
      • 3.14 【怎么实现分布式消息事务的】
      • 3.15 死信队列(保存多次消费失败的消息/默认保存时间72小时)
      • 3.16 如何保证RocketMQ的高可用(Broker集群配置)
      • 3.17 RocketMQ的整体工作流程
      • 3.18 为什么RocketMQ不使用Zookeeper作为注册中心(ZK并不能保证实时可用性,选举时不可用)
      • 3.19 Broker是怎么保存数据的(CommitLog消息存储文件/ConsumeQueue基于topic的索引文件/IndexFile基于key或时间区间的索引文件)
      • 3.20 RocketMQ怎么对文件进行读写的
      • 3.21 消息刷盘怎么实现的(同步刷盘和异步刷盘)
      • 3.22 什么时候清理过期消息
      • 3.23 RocketMQ的负载均衡是如何实现的(生产者和消费者都有负载均衡)
      • 3.24 消息队列设计成推消息还是拉消息
      • 3.25 如何设计一个消息队列*
      • 3.26 RocketMQ消息体过大的解决方案(压缩/拆分消息)
      • 3.27 如何保证幂等性(消息有唯一id,消费时保存每条消息的消费结果到DB,从而避免重复消费)

  本系列文章:
    RocketMQ(一)消息的发送、存储与消费
    RocketMQ(二)RocketMQ实战

一、RocketMQ实战

1.1 批量消息发送(消息体为多条消息)

  RocketMQ批量消息发送是将同一主题的多条消息打包后一次性发送到消息服务端,减少网络调用的次数和网络通信资源的损耗,提高网络传输效率。示例:

		DefaultMQProducer producer=new DefaultMQProducer("BatchProducerGroupName");
		producer.setNamesrvAddr("127.0.0.1:9876");
		producer.start();
		String topic = "BatchTest";
		List<Message> messages = new ArrayList<>();
		messages.add(new Message(topic, "Tag", "OrderID001",
			"Hello world1".getBytes()));
		messages.add(new Message(topic, "Tag", "OrderID002",
			"Hello world2".getBytes()));
		System.out.println(producer.send(messages));
		producer.shutdown();

1.2 消息发送队列自选择

  消息发送默认根据主题的路由信息(主题消息队列)进行负载均衡,负载均衡机制为轮询策略。假设这样一个场景,订单的状态变更消息发送到特定主题,为了避免消息消费者同时消费同一订单不同状态的变更消息,在开发过程中我们应该使用顺序消息。为了提高消息消费的并发度,如果我们能根据某种负载算法,将相同订单不同的消息统一发送到同一个消息消费队列上,则可以避免引入分布式锁,RocketMQ在消息发送时提供了消息队列选择器MessageQueueSelector。示例:

	String[] tags = new String[] {
   "TagA", "TagB", "TagC", "TagD","TagE"};
	for (int i = 0; i < 100; i++) {
   
		int orderId = i % 10;
		Message msg = new Message("TopicTestjjj", tags[i % tags.length], "KEY" + i,
			("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
		SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
   
			public MessageQueue select(List<MessageQueue> mqs,Message msg, Object arg){
   
				Integer id = (Integer) arg;
				int index = id % mqs.size();
				return mqs.get(index);
			}
		}, orderId);
		System.out.printf("%s%n", sendResult);
	}

1.3 事务消息

  事务消息是RocketMQ提供的一种高级消息类型,支持在分布式场景下保障消息生产和本地事务的最终一致性。

  • 事务消息处理流程
    RocketMQ(二)RocketMQ实战_第1张图片
  1. 生产者将消息发送至RocketMQ服务端。
  2. RocketMQ服务端将消息持久化成功之后,向生产者返回Ack确认消息已经发送成功,此时消息被标记为"暂不能投递",这种状态下的消息即为半事务消息。
  3. 生产者开始执行本地事务逻辑。
  4. 生产者根据本地事务执行结果向服务端提交二次确认结果(Commit或是Rollback),服务端收到确认结果后处理逻辑如下:

  二次确认结果为Commit:服务端将半事务消息标记为可投递,并投递给消费者。
  二次确认结果为Rollback:服务端将回滚事务,不会将半事务消息投递给消费者。

  1. 在断网或者是生产者应用重启的特殊情况下,若服务端未收到发送者提交的二次确认结果,或服务端收到的二次确认结果为Unknown未知状态,经过固定时间后,服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查。 说明 服务端回查的间隔时间和最大回查次数,请参见参数限制。
  2. 生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
  3. 生产者根据检查到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤4对半事务消息进行处理。
  • 使用限制
      1、消息类型一致性。事务消息仅支持在 MessageType 为 Transaction 的主题内使用,即事务消息只能发送至类型为事务消息的主题中,发送的消息的类型必须和主题的类型一致。
      2、消费事务性。RocketMQ 事务消息保证本地主分支事务和下游消息发送事务的一致性,但不保证消息消费结果和上游事务的一致性。因此需要下游业务分支自行保证消息正确处理,建议消费端做好消费重试,如果有短暂失败可以利用重试机制保证最终处理成功。
      3、中间状态可见性。RocketMQ 事务消息为最终一致性,即在消息提交到下游消费端处理完成之前,下游分支和上游事务之间的状态会不一致。因此,事务消息仅适合接受异步执行的事务场景。
      4、事务超时机制。RocketMQ 事务消息的生命周期存在超时机制,即半事务消息被生产者发送服务端后,如果在指定时间内服务端无法确认提交或者回滚状态,则消息默认会被回滚。
  • 使用示例
      事务消息相比普通消息发送时需要修改以下几点:

  发送事务消息前,需要开启事务并关联本地的事务执行。
  为保证事务一致性,在构建生产者时,必须设置事务检查器和预绑定事务消息发送的主题列表,客户端内置的事务检查器会对绑定的事务主题做异常状态恢复。

    //演示demo,模拟订单表查询服务,用来确认订单事务是否提交成功。
    private static boolean checkOrderById(String orderId) {
   
        return true;
    }
    //演示demo,模拟本地事务的执行结果。
    private static boolean doLocalTransaction() {
   
        return true;
    }
    public static void main(String[] args) throws ClientException {
   
        ClientServiceProvider provider = new ClientServiceProvider();
        MessageBuilder messageBuilder = new MessageBuilderImpl();
        //构造事务生产者:事务消息需要生产者构建一个事务检查器,用于检查确认异常半事务的中间状态。
        Producer producer = provider.newProducerBuilder()
                .setTransactionChecker(messageView -> {
   
                    /**
                     * 事务检查器一般是根据业务的ID去检查本地事务是否正确提交还是回滚,此处以订单ID属性为例。
                     * 在订单表找到了这个订单,说明本地事务插入订单的操作已经正确提交;如果订单表没有订单,说明本地事务已经回滚。
                     */
                    final String orderId = messageView.getProperties().get("OrderId");
                    if (Strings.isNullOrEmpty<

你可能感兴趣的:(【RocketMQ】,rocketmq,开发语言,java-rocketmq)