RocketMq介绍及应用

介绍功能
1、应用解耦
复杂的应用里会存在多个子系统,如果各个子系统之间的耦合性太高,整体系统的可用性就会大幅降低。比如一个电商系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。
当转变成基于消息队列的方式后,系统可用性就高多了,比如物流系统因为发生故障,需要几分钟的时间来修复,在这几分钟的时间里,物流系统要处理的内容被缓存在消息队列里,用户的下单操作可以正常完成。当物流系统恢复后,补充处理存储在消息队列里的订单信息即可,终端用户感知不到物流系统发生过几分钟的故障。
2、流量消峰
通过缓冲机制,承受住短时大流量的冲击。通过利用消息队列,把大量的请求暂存起来,分散到相对长的一段时间内处理,能大大提高系统的稳定性和用户体验。
3、 消息分发
在大数据时代,数据对很多公司来说就像金矿,公司需要依赖对数据的分析,进行用户画像、精准推送、流程优化等各种操作,并且对处理的实时性要求越来越高。数据是不断产生的,各个分析团队、算法团队都要依赖这些数据来进行工作,这个时候有个可持久化的消息队列就非常重要。数据的产生方只需要把各自的数据写入一个消息队列即可,数据使用方根据各自需求订阅感兴趣的数据,不同数据团队所订阅的数据可以重复也可以不重复,互不干扰,也不必和数据产生方关联。

组成及功能

RocketMQ由四部分组成,先来直观地了解一下这些角色以及各自的功能。https://www.jianshu.com/p/2838890f3284(推荐)

分布式消息队列是用来高效地传输消息的,它的功能和现实生活中的邮局收发信件很类似,我们类比地说一下相应的模块。现实生活中的邮政系统要正常运行,离不开下面这四个角色,一是发信者,二是收信者,三是负责暂存、传输的邮局,四是负责协调各个地方邮局的管理机构。对应到RocketMQ中,这四个角色就是Producer、Consumer、Broker和NameServer。

发信者     =====>    Producer                                        收信者     =====>   Consumer 

负责暂存  =====>  Broker                                            传输的邮局 ====> NameServer

大概就是下面这个样子(这个图很迷,望指点):

解释一下:生产者(集群)根据topic发消息到broker,消费者从broker接收消息。

启动 RocketMQ 的顺序是先启动 NameServer,再启动 Broker,这时候消 息队列已 经可以提供服务了,想发送消息就使用 Producer来发送,想接收消息 就使用 Consumer来接收 。 很多应用程序既要发送,又要接收,可以启动多个Producer 和 Consumer 来发送多种消息,同时接收多种消息 。

 

NameServer 维护这些配置信息 、 状态信 息,其他角色都通过 NameServer 来协同执行

NameServer是整个消息队列中 的状态服务器,集群的各个组件通过它来了 解全局的信息 。 同时,各个角色的机器都要定期 向 NameServer上报自己的状态,超时不上报的话,NameServer 会认为 某个机器出故障不可用了,其他的组 件会把这个机器从可用列表里移除 。

NameServer可以部署多个,相互之间独立,其他角色同时向多个 NameServer 机器上报状态信息,从而达到热备份的目的。 NameServer本身是无状态的,也就 是说 NameServer 中的 Broker、 Topic 等状态信息不会持久存储,都是由各个角色定时上报并存储到内存中的。

 

集群状态的存储结构及位置  NameServer 的主要工作就是维 护这五个变量中存储的信息

private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;
说明:topicQueueTable 这个结构的 Key 是 Topic 的名称,它存储了所有 Topic 的属性信息 。
 Value 是个 QueueData 队列 , 队里的长度 等于这 个 Topic 数据存储的 MasterBroker的个数, 
QueueData里存储着 Broker的名称、 读写queue的数量、 同步标识等。
 
private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
说明:以 BrokerName 为 索 引 ,相 同 名 称的 Broker 可能存在多台机器, 一个 Master 
和多个 Slave。 这个结构存储着一个 BrokerName 对应的属性信 息,包括所属的 Cluster 名称,
 一 个 Master Broker 和多个 Slave Broker 的地址信息 。
 
 
private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
说明:存储的是集群中 Cluster 的信息,结果很简单,就是一个 Cluster 名称对 应一个由 BrokerName组成的集合。
 
 
private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;
说明:这个结构和 BrokerAddrTable有关系,但是内容完全不同,这个结构的 Key 是 BrokerAddr,也就是对应着一台 机器, 
BrokerAddrTable 中的 Key 是BrokerName, 多个机器的BrokerName可以相同。 BrokerLiveTable 存储的内容是这台 
Broker机器的实时状态,包括上次更新状态的时间 戳, NameServer会定期检查这个时间戳,超时没有更新就认为这个 Broker无效了,
将其从 Broker列表里清除。
 
 
private final HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;
说明:Filter Server是过滤服务器,是 RocketMQ 的一种服务端过滤方式,一 个 Broker 可以有 一个 或 多个 Filter Server。 
这个结构的 Key 是 Broker 的地址, Value 是和这个 Broker关联的多个 Filter Server 的地址 。

状态维护逻辑

因为其他角色会主动向 NameServer上报状态,所以 NameServer 的主 要逻 辑在 DefaultRequest­ Processor类中,根据上报消息里的请求码做相 应 的处理, 更新存 储的对应 信息 。 此外,连接断开的 事 件也 会 触发状态 更新。

当NameServer和Broker的长连接断掉以后,NameServer会把这个 Broker的信息清理出去。

NameServer还有定时检查时间戳的逻辑, Broker向 NameServer发送的心 跳会更新时间戳, 当 NameServer检查到时间戳长时间没有更新后,便会触发 清理逻辑(10秒检查一次,时间戳超过 2分钟则认为 Broker已 失效。)。

 

了解了四种角色以后,再介绍一下Topic和Message Queue。一个分布式消息队列中间件部署好以后,可以给很多个业务提供服务,同一个业务也有不同类型的消息要投递,这些不同类型的消息以不同的Topic名称来区分。所以发送和接收消息前,先创建Topic,针对某个Topic发送和接收消息。有了Topic以后,还需要解决性能问题。如果一个Topic要发送和接收的数据量非常大,需要能支持增加并行处理的机器来提高处理速度,这时候一个Topic可以根据需求设置一个或多个Message Queue, Message Queue类似分区或Partition。Topic有了多个Message Queue后,消息可以并行地向各个Message Queue发送,消费者也可以并行地从多个Message Queue读取消息并消费。

 

1、生产者和消费都是通过topic找到具体某大类消息类型进行消息的投递和消费,topic是rocketmq中一大类消息传递的桥梁

2、一个topic可以创建在一个broker上,也可以创建在多个broker上。如果消息创建在一个broker上那么消息会全部发送到指定的 broker中,一旦broker发生故障,那么当前topic对应的生产者和消费者是无法进行消息的投递和消费的,因此rocketmq提供了HA模式即高可用master-slave。

对当前brokerName进行下利用这主从配置,防止单点故障.一般数据量的情况种配置完全可以满足生产环境rocketmq的使用。但是消息数量达到一定程度的时候势必会对当前broker造成很大的压力,因此引出了另一种配置方式,一个topic配置在多个broker下面,多个broker共同完成消息的消费,分散broker的压力.

3、创建一个topic的时候会首先指定broker,然后指定broker下的读队列数量和写队列数量,还有当前topic的权限(2||4||6 ,2是写权限,4是读权限,6是读写权限)。我们可以理解为producer发送消息到设置好的MessageQueue中,单台broker的情况下MessageQueue是负载均衡的,一个topic的消息和负载的投递到不同的MessageQueue中,我们也可以在producer中指定发送到具体某个MessageQueue中。多个broker且集群模式下,MessageQueue是不共享的(当前broler只处理自己的MessageQueue),

而且从broker只负责读操作,并不负责写操作.只有主broker挂掉的时候从节点才会进行写操作.

4、ProcessQueue是MessageQueue的快照队列,在PushConsumer模式运行的情况下,每个MessageqQueue都会对应的创建一个ProcessQueue用具记录MessageQueue消息处理的快照。ProcessQueue对象中主要结构是一个TreeMap和一个读写锁,TreeMap中以MessageQueue的offset作为key,以消息内容的引用作为value,保存所有从MessageQueue中获取到但还没有被消费掉的消息.其中读写所控制着多个线程对于treeMap的访问.
 

下载安装http://rocketmq.apache.org/dowloading/releases/

注意:一定要使用1.8+版本,1.7版本不能识别rocketMQ中的metaspace特性,该特性由1.8最新提出

提示:rocketmq下载zip压缩包即可,binary是无需配置maven的。pom.xml

 

spring简单应用:

pom.xml配置

<!-- https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-client -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>${rocketmq.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-common -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-common</artifactId>
            <version>${rocketmq.version}</version>
        </dependency>

 

mq配置:

@Configuration
public class RocketmqConfiguration {

    @Value("${apache.rocketmq.namesrvAddr}")
    private String nameServer;

    @Value("${apache.rocketmq.producer.producerGroup}")
    private String produceGroupName;

    @Bean
    public DefaultMQProducer defaultMQProducer() throws MQClientException {
        DefaultMQProducer defaultMQProducer =  new DefaultMQProducer(produceGroupName);
        defaultMQProducer.setNamesrvAddr(nameServer);
        defaultMQProducer.setVipChannelEnabled(false);
        defaultMQProducer.setInstanceName("test-producer");
        defaultMQProducer.setRetryTimesWhenSendAsyncFailed(10);
        defaultMQProducer.start();
        return defaultMQProducer;
    }
}

 

消息生产者启动:

@Configuration
@SpringBootApplication
public class DataSyncApplication implements CommandLineRunner {

    @Autowired
    private DefaultMQProducer defaultMQProducer;

    public static void main(String[] args) {
        new SpringApplicationBuilder(DataSyncApplication.class).web(false).run(args);
    }

    @Override
    public void run(String... args) throws Exception {
        Message message = new Message("TopicTest","push","Hello World!!!!!!!".getBytes(RemotingHelper.DEFAULT_CHARSET));
        SendResult result = defaultMQProducer.send(message);
    }
}

 

消息消费者:

@Component
public class RocketmqConsumer {

    @Value("${apache.rocketmq.consumer.PushConsumer}")
    private String consumerGroupName;

    @Value("${apache.rocketmq.namesrvAddr}")
    private String nameServer;

    @PostConstruct
    public void consumer(){
        //消费者的组名
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroupName);

        //指定NameServer地址,多个地址以 ; 隔开
        consumer.setNamesrvAddr(nameServer);
        consumer.setVipChannelEnabled(false);
        try {
            //订阅PushTopic下Tag为push的消息
            consumer.subscribe("TopicTest", "push");
            //设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费
            //如果非第一次启动,那么按照上次消费的位置继续消费
            consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
            consumer.registerMessageListener((MessageListenerConcurrently) (list, context) -> {
                try {
                    for (MessageExt messageExt : list) {
                        System.out.println("messageExt: " + messageExt);//输出消息内容
                        String messageBody = new String(messageExt.getBody(), RemotingHelper.DEFAULT_CHARSET);
                        System.out.println("消费响应:msgId : " + messageExt.getMsgId() + ",  msgBody : " + messageBody);//输出消息内容
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return ConsumeConcurrentlyStatus.RECONSUME_LATER; //稍后再试
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; //消费成功
            });
            consumer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

介绍一本书《RocketMQ实战与原理解析》-杨开元

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值