RocketMQ源码解析(一)——RocketMQ快速入门

RocketMQ源码解析(一)——RocketMQ的使用

快速入门(官方Quick Start)

1.环境准备:
  1. 推荐64位 OS, Linux/Unix/Mac;
  2. 64bit JDK 1.8以上;
  3. Maven 3.2.x;
  4. Git;
  5. 为Broker server 准备4GB以上的磁盘空间
2.下载、构建

官方提供两种形式,binary可以直接下载使用,source需要进行build,下载链接地址:http://rocketmq.apache.org/release_notes/
使用source进行构建:

  > unzip rocketmq-all-4.4.0-source-release.zip
  > cd rocketmq-all-4.4.0/
  > mvn -Prelease-all -DskipTests clean install -U
  > cd distribution/target/apache-rocketmq

启动NameServer

  > nohup sh bin/mqnamesrv &
  > tail -f ~/logs/rocketmqlogs/namesrv.log
  The Name Server boot success...

启动broker

  > nohup sh bin/mqbroker -n localhost:9876 &
  > tail -f ~/logs/rocketmqlogs/broker.log 
  The broker[%s, 172.30.30.233:10911] boot success...

关闭broker,NameServer

> sh bin/mqshutdown broker
The mqbroker(36695) is running...
Send shutdown request to mqbroker(36695) OK

> sh bin/mqshutdown namesrv
The mqnamesrv(36664) is running...
Send shutdown request to mqnamesrv(36664) OK

3.发送,接收消息

官方提供source中提供有example工程,NameServer和broker启动后即可通过这个工程进行简单的测试,参考路径:org.apache.rocketmq.example.quickstart

Producer:
/**
 * 此类展示如何使用DefaultMQProducer发送消息
 */
public class Producer {
    public static void main(String[] args) throws MQClientException, InterruptedException {
    
      	//初始化producer实例,参数是producerGroup的名字,自行修改
        DefaultMQProducer producer = new DefaultMQProducer("ordinary-producer");
        //设置NameServer的服务地址,本地一般是localhost:9876
        producer.setNamesrvAddr("localhost:9876");
        //执行start操作
        producer.start();
		//循环生成消息
        for (int i = 0; i < 1000; i++) {
            try {
                //创建消息实例,三个参数分别是话题名称(string)、标签(string,用于过滤消息)、消息体(byte[])
                Message msg = new Message("TopicTest" /* Topic */,
                    "TagA" /* Tag */,
                    ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
                );
                //调用send方法发送消息到broker,并拿到发送的结果SendResult
                SendResult sendResult = producer.send(msg);
                //SendResult的toString方法被重写了,此处可以直接打印内部封装的message处理详情
                System.out.printf("%s%n", sendResult);
            } catch (Exception e) {
                e.printStackTrace();
                //线程休眠1000ms
                Thread.sleep(1000);
            }
        }
        //消息发送完毕后,关闭此producer实例
        producer.shutdown();
    }
Consumer:
/**
 * 此类展示如何使用DefaultMQPushConsumer订阅,消费消息
 */
public class Consumer {

    public static void main(String[] args) throws InterruptedException, MQClientException {
    	//创建consumer实例,指定consumerGroup的名称
    	DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ordinary-consumer");
		//指定NameServer地址
        consumer.setNamesrvAddr("localhost:9876");
		//指定consumer开始消费的位置			
		consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

        //订阅话题,次数topic的名称要和上面producer中的保持一致,*对应producer中的tag,表示不过滤,如果此时写tag-a,那就只消费被打上tag-a标签的message
        consumer.subscribe("TopicTest", "*");

        //注册listener监听消息消费
        consumer.registerMessageListener(new MessageListenerConcurrently() {

            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
                ConsumeConcurrentlyContext context) {
                //MessageExt是message的子类,封装了包括queueid在内的更多信息
                System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //启动消费者
        consumer.start();
        System.out.printf("Consumer Started.%n");
    }
}

启动producer,成功显示日志,默认情形下queueid为0,1,2,3,这是因为系统默认创建的队列是4个

...
SendResult [sendStatus=SEND_OK, msgId=0A3C005D1A980E1641C0466AC3D20290, offsetMsgId=0A3C005D00002A9F0000000000024E46, messageQueue=MessageQueue [topic=TopicTest, brokerName=LZZ-PC, queueId=1], queueOffset=164]
SendResult [sendStatus=SEND_OK, msgId=0A3C005D1A980E1641C0466AC3D30291, offsetMsgId=0A3C005D00002A9F0000000000024EFA, messageQueue=MessageQueue [topic=TopicTest, brokerName=LZZ-PC, queueId=2], queueOffset=164]
SendResult [sendStatus=SEND_OK, msgId=0A3C005D1A980E1641C0466AC3D40292, offsetMsgId=0A3C005D00002A9F0000000000024FAE, messageQueue=MessageQueue [topic=TopicTest, brokerName=LZZ-PC, queueId=3], queueOffset=164]
SendResult [sendStatus=SEND_OK, msgId=0A3C005D1A980E1641C0466AC3D60293, offsetMsgId=0A3C005D00002A9F0000000000025062, messageQueue=MessageQueue [topic=TopicTest, brokerName=LZZ-PC, queueId=0], queueOffset=164]
...

启动consumer,成功显示日志

ConsumeMessageThread_3 Receive New Messages: [MessageExt [queueId=2, storeSize=180, queueOffset=208, sysFlag=0, bornTimestamp=1565770202335, bornHost=/10.60.0.93:49939, storeTimestamp=1565770202336, storeHost=/10.60.0.93:10911, msgId=0A3C005D00002A9F000000000002CABA, commitLogOffset=182970, bodyCRC=1948249169, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={TAGS=TagA, WAIT=true, UNIQ_KEY=0A3C005D1A980E1641C0466AC4DF0341, MAX_OFFSET=222, CONSUME_START_TIME=1565770445958, MIN_OFFSET=0}, body=[72, 101, 108, 108, 111, 32, 82, 111, 99, 107, 101, 116, 77, 81, 32, 57, 52, 55], transactionId='null'}]] 
ConsumeMessageThread_5 Receive New Messages: [MessageExt [queueId=2, storeSize=180, queueOffset=206, sysFlag=0, bornTimestamp=1565770202324, bornHost=/10.60.0.93:49939, storeTimestamp=1565770202325, storeHost=/10.60.0.93:10911, msgId=0A3C005D00002A9F000000000002C51A, commitLogOffset=181530, bodyCRC=1558599569, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={TAGS=TagA, WAIT=true, UNIQ_KEY=0A3C005D1A980E1641C0466AC4D40339, MAX_OFFSET=222, CONSUME_START_TIME=1565770445958, MIN_OFFSET=0}, body=[72, 101, 108, 108, 111, 32, 82, 111, 99, 107, 101, 116, 77, 81, 32, 57, 51, 57], transactionId='null'}]] 
ConsumeMessageThread_2 Receive New Messages: [MessageExt [queueId=2, storeSize=180, queueOffset=205, sysFlag=0, bornTimestamp=1565770202320, bornHost=/10.60.0.93:49939, storeTimestamp=1565770202321, storeHost=/10.60.0.93:10911, msgId=0A3C005D00002A9F000000000002C24A, commitLogOffset=180810, bodyCRC=1431313338, reconsumeTimes=0, preparedTransactionOffset=0, toString()=Message{topic='TopicTest', flag=0, properties={TAGS=TagA, WAIT=true, UNIQ_KEY=0A3C005D1A980E1641C0466AC4D00335, MAX_OFFSET=222, CONSUME_START_TIME=1565770445958, MIN_OFFSET=0}, body=[72, 101, 108, 108, 111, 32, 82, 111, 99, 107, 101, 116, 77, 81, 32, 57, 51, 53], transactionId='null'}]] 

此时,发送端和消费端都已经将消息处理完毕。可见使用消息队列有以下好处:

  1. 解耦。上面的例子中,producer和consumer互不依赖,双发通过NameServer获取broker的路由信息,在broker中完成信息的发送和消费,系统的耦合性大大降低。
  2. 削峰。海量数据处理中,单体应用往往无法承受住高并发的冲击。此时可以将数据放到 队列去,然后通过consumer异步消费,降低系统运行的压力。比如秒杀场景下,用户点击下单后即存放到消息rocketmq中,并不需要同步地完成实际的订单操作。等到秒杀结束,consumer再去消费消息完成物品出仓,物流跟踪等。
  3. 复用。producer发送消息到broker中后,可以启动多个consumer去订阅,消费消息,这样就可以为多个系统提供稳定的数据支撑。比如电商场景下,一个订单的数据需要提供给支付,仓储,物流,售后服务,用户行为分析等多套系统使用,此时使用消息队列可以很好的提供这种支持。

消息队列并不是完美的,使用时着消息丢失,重复消费等问题,那么RocketMQ是如何设计来保证系统的高可用呢?这就需要我们对RocketMQ的源码进一步分析。

参考资料:1.RocketMQ官方文档

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值