RocketMQ

在这里插入图片描述
在这里插入图片描述
生产者:

//
DefaultMQProducer producer = new DefaultMQProducer("Producer"); 
producer.setNamesrvAddr("127.0.0.1:9876");
 try {
            producer.start();
 			//创建名为PushTopic,tag为push的消息
            Message msg = new Message("PushTopic",
                    "push",
                    "1",
                    "Just for test.".getBytes());
 
            SendResult result = producer.send(msg);
            System.out.println("id:" + result.getMsgId() +
                    " result:" + result.getSendStatus());
}catch (Exception e) {
            e.printStackTrace();
        }finally{
            producer.shutdown();
        }
  }

//发送消息的三种类型
1.可靠同步发送:producer.send(msg);
2.异步发送消息:producer.send(msg,new sendCallback(){...});
3.单向发送(只发送,不等待回应)producer.sendOneWay(msg);

消费者:

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("PushConsumer");
consumer.setNamesrvAddr("127.0.0.1:9876");
try {
    //订阅PushTopic下Tag为push的消息
    consumer.subscribe("PushTopic", "push");

    //程序第一次启动从消息队列头取数据
    consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
    consumer.registerMessageListener(new MessageListenerConcurrently() {
          public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext Context) {
              Message msg = list.get(0);

              String topic = msg.getTopic();
              System.out.println("topic = " + topic);
              byte[] body = msg.getBody();
              System.out.println("body:  " + new String(body));
              String keys = msg.getKeys();
              System.out.println("keys = " + keys);
              String tags = msg.getTags();
              System.out.println("tags = " + tags);
              System.out.println("-----------------------------------------------");

              return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
          }
      }
    );
    consumer.start();
} catch (Exception e) {
    e.printStackTrace();
}

部署架构设计如图:

在这里插入图片描述

在这里插入图片描述

Name Server
主要作用是为消息生产者和消息消费者提供关于主题Topic的路由信息。
是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。

Broker部署相对复杂,Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与Name Server集群中的所有节点建立长连接,定时注册Topic信息到所有Name Server。

Producer与Name Server集群中的其中一个节点(随机选择)建立长连接,定期从Name Server取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。

Consumer与Name Server集群中的其中一个节点(随机选择)建立长连接,定期从Name Server取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。

Produce Group标识同一类消息的Producer。

Consumer Group标识一类Consumer的集合名称。同一个Consumer Group下的各个实例将共同消费topic的消息,起到负载均衡的作用。不同Consumer Group并行消费,互不影响。

Topic标识一类消息的逻辑名字,消息的逻辑管理单位。无论消息生产还是消费,都需要指定Topic。

**Message Queue(通道)**消息物理管理单位。一个Topic可以在任意一个broker上创立多个queue。是负载均衡的基本单位。

RocketMQ的基本架构和原理

1.NameServer中路由表的构建
Broker定时给NameServer发送心跳包,心跳包包含了该broker中有哪些topic,每个topic在这个broker上有几个queue。
在这里插入图片描述

2.生产者的负载均衡
生产者从NameServer获取目标topic的路由信息然后默认以轮询的方式把消费发送到各个队列。
3.消费者的负载均衡
消费者吉间定时往他订阅的topic所在的所有broker发送心跳包,心跳中有消费者的consumer group和clientId。
broker根据接收到的心跳包,构建出consunmer group到客户端列表的映射表。
在这里插入图片描述
消费者会定时进行负载均衡的计算。计算出自己应该消费的队列集合。从NameServer获取topic的路由信息即topic queue的集合。从broker获取相同consumer group的客户端列表。对topic路由信息进行排序。对客户端列表进行排序。计算出自己在客户端列中的index。根据index选取自己应该消费的topic queue的集合。
4.broker的动态增加
新增的broker会往NameServer发心跳包,导致NameServer上路由表的改变。
生产者定时从NameServer获取Topic的最新路由信息,更新缓存。
消费者定时进行负载均衡计算,此时会获得新的Topic路由表,并重新进行queue的分配。
消费者逐个完成新的负载均衡
5.Consumer的动态增加
新增的消费者客户端会往broker发心跳包注册。Broker中的消费组客户端列表会得到更新。
消费者在负载均衡时,拉取到新的客户端列表,达到新的负载均衡。
6.NameServer的容错容灾
NameServer可以是一个集群,broker会给NameServer集群中的每一台NameServer都发心跳包,producer和consumer在查询路由信息时会进行failover重试,只要能连上其中一台NameServer就能工作。
7.Broker的容错容灾
一台broker挂掉之后,producer生产的消息会被发送到其他健康的broker上,消费继续进行。
同一个broker主从的容错容灾:一个broker中可以部署一台主机和多台备机,主机可以写也可以读,备机只读不写。备机挂掉对系统可用性无影响。主机挂掉后,该主机上未消费的消息,可以从备机拉取。新生产的消息会被发到其他broker上,系统依然可用。
8.消费进度管理
同一个消费组的一个consumer挂掉后,该consumer原来负责的queue会在负载均衡中被分配给其他consumer消费。
每次消费成功后,本地的消费进度会被更新,然后由定时器定时(5秒)同步到broker,以此持久化消费进度。
消费者开始消费一个queue时先从broker获取queue的消费进度,然后在本地维护消费进度,定时上报。
9.消费失败重试
消费者在订阅用户要求的topic的同时,会额外订阅一个重试队列,重试队列名字为%RETRY%groupName。
消费者处理消息返回失败或者抛出异常后,会把消息发送回broker,送到重试队列。重试队列的消息会延时一段时间再递送。重试超过一定次数后,不会再重试,并把消息发送到死信队列。

RocketMQ相关概念:

1.RocketMQ中的队列不支持优先级,所以需要手动创建几个队列来划分优先级。
2.严格保证消息有序。
3.Broker端消息过滤(Broker端压力大,但少了网络传输)。
4.Consumer端消息过滤(过滤方式自由,增加网络传输)。
5.RocketMQ使用长轮询Pull方式,可保证消息非常实时,且consumer先消费完再向服务器返回ack。
6.不保证发生和消费不重复(性能上的问题),需要在业务上进行去重。
7.RocketMQ支持按照时间回溯消费,可向前向后回溯。
8.消息堆积。
9.分布式事务。
10.定时消息。
11.消息重试。
12.RocketMQ采取一种数据与索引分离的存储方式。

RocketMQ的最佳实践

1.确保消息幂等性
对于任何的消息系统消息的不可丢失和消息重复性永远是一对矛盾。
消息重复的原因:消息发送端导致的重复。消费者消费进度更新策略引起的重复。一些异常导致的重复。
去重策略:以消息的唯一键,可以是msgId,也可以是消息内容中的唯一标识字段等作为去重依据。有强一致性要求的业务,使用数据库事务做去重。数据库性能有限,可同时使用redis做过滤。
在这里插入图片描述
2.指定消息的key便于定位问题
MQ的消息发送接口方法中提供了一个key参数,该参数的作用在于通过为要发送的消息指定一个key,用户在定位问题时就可以通过key来查询到具有这个key的消费的id是什么。
在这里插入图片描述
3.同组的消费者要订阅相同的topic
RocketMQ的默认负载均衡算法默认为同分组的消费者会订阅相同的topic
如果同组的消费客户端订阅的topic不一致,会出现topic的部分queue无人消费的情况
在这里插入图片描述
一个消费分组只在一个应用中使用,不要在多个应该使用同一个消费组。
一个应用可以使用多个消费组。
4.消费者确保消息处理逻辑在有限时间内返回
RocketMQ是以consumer group+queue为单位管理消费进度的,以一个consumer offset标记这个消费组在这条queue上的消费进度。
每次记录消费进度的时候,只会把一批消息中最小的offset值作为消费进度值上报到服务器。
RocketMQ监控系统已上报的消费进度和生产进度之差作为堆积数。
消息处理逻辑没有返回的异常通常发生在需要通过http访问外网第三方服务的时候。http请求默认没有超时时间,所以访问外网一定要主动设置超时时间。
5.消息异步发送接口
RocketMQ的发送消息延时平均在10ms以下,但由于消息需要同步到备机上返回,加之java程序的GC,有部分消息发送延时会超过100ms。可使用异步发送消息。

MQ的局限性

1.MQ的引入增加了时间成本,同一个应用内的异步解耦不建议使用MQ,如果需要可考虑同应用异步处理方案。
2.应用使用MQ分离之后,给调试定位问题和对账带来了困难,跨系统数据一致性要求较高的场景,建议发送消息的时候自己落数据库,接收方也落数据库。
3.RocketMQ本身也可能处于不可用,业务要在实现时要考虑这一点。当RocketMQ不可用时,消费者可能暂时接收不到消息,需要等到系统恢复后才能接收到。生产者发送消息可能失败,如果需要重试发送建议间隔较长一段时间。

MQ的队列选择与容错策略:

  1. 不开启容错时,发送到队列失败,则直接过滤该队列。
  2. 开启容错,发送到队列前,会通过预测机制来预测一个broker是否可用。(RocketMQ为每个Broker预测了个可用时间(当前时间+不可用时间),只有当前时间大于该时间,才代表broker可用。)
  3. 如果上次失败的broker可用,依旧选择该broker的队列。(猜测上一次发送失败,可能下个队列会成功)
  4. 如果上述都失败,则随机选择一个broker。
  5. Producer记录发送到broker出现报错的时间,根据此去预测broker的可用时间。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值