RocketMQ-04 编程示例

一、前言

首先声明:本篇中编写的案例,基本都是来自官网。

简单的入门案例:
http://rocketmq.apache.org/docs/simple-example/

更多案例:https://github.com/apache/rocketmq/tree/master/example

常见消息用法:

  1. 简单消息
  2. 广播消息
  3. 延时发送(计划消息)
  4. 批量发送消息
  5. 顺序消息
  6. 消息筛选
  7. 事务消息(这里不做讲解,专门在分布式事务里面讲解)

二、开发环境搭建

不管是生产者还是消费者,搭建都很简单,引入rocketmq-client相关包即可。

  <dependencies>
    <dependency>
      <groupId>org.apache.rocketmq</groupId>
      <artifactId>rocketmq-client</artifactId>
      <version>4.5.1</version>
    </dependency>
  </dependencies>

三、简单消息

(一)Consumer

这里简单的使用DefaultMQPushConsumer来接受消息,基于方法回调来接受消息

package com.firewolf.rocketmq;

import java.util.List;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;

/**
 * 作者:刘兴 时间:2019-07-11
 **/
public class Consumer {

  public static void main(String[] args) throws InterruptedException, MQClientException {

    //创建消费者实例,指定消费者组
    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-group1");

    //指定nameserver地址,多个用;隔开
    consumer.setNamesrvAddr("10.211.55.6:9876");

    //指定订阅的主题和tag,*表示订阅这个主题下的所有消息
    consumer.subscribe("mytopic", "*");

    //注册接受到消息的回调函数
    consumer.registerMessageListener(new MessageListenerConcurrently() {
      public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
          ConsumeConcurrentlyContext consumeConcurrentlyContext) {
        System.out.printf("%s 接收到消息: %s %n", Thread.currentThread().getName(), msgs);
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
      }
    });

    //Launch the consumer instance.
    consumer.start();

    System.out.printf("消费者启动了...%n");
  }
}

(二)Producer

这里使用三种方式发送消息:

  • 同步(同步等待投递消息的结构)。大部分场景都会使用这种模式,如:重要的通知消息、SMS等。
  • 异步(回调方法异步接受处理的结果)。用于对响应时间敏感的商业场景。
  • 单向(不关系结果)。用于可靠性要求不高的场景,如:记录日志。

其实就是调用的方法不同而已

1. 同步

package com.firewolf.rocketmq;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;

/**
 * 作者:刘兴 时间:2019-07-11
 * 同步发送,就是立马知道消息投递的结果
 **/
public class SyncProducer {

  public static void main(String[] args) throws Exception {
    //创建生产者,指定所在的组
    DefaultMQProducer producer = new
        DefaultMQProducer("producer-group1");
    //指定nameserver,如果有多个,用;隔开
    producer.setNamesrvAddr("10.211.55.6:9876");
    //启动生产者
    producer.start();
    //发送消息
    for (int i = 0; i < 20; i++) {
      //创建要发送的消息对象
      Message msg = new Message("mytopic" /* Topic */,
          "TagA" /* Tag */,
          ("Hello RocketMQ " +
              i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
      );
      //发送消息
      SendResult sendResult = producer.send(msg);
      System.out.printf("%s%n", sendResult);
    }
    //关闭生产者
    producer.shutdown();
  }
}

2. 异步

package com.firewolf.rocketmq;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;

/**
 * 作者:刘兴 时间:2019-07-11 异步发送,通过回调函数来处理发送的结果
 **/
public class AsyncProducer {

  public static void main(String[] args) throws Exception {
    DefaultMQProducer mqProducer = new DefaultMQProducer("producer-group1");

    mqProducer.setNamesrvAddr("10.211.55.6:9876");

    mqProducer.start();

    for (int i = 0; i < 10; i++) {
      Message message = new Message("mytopic", "TagN", ("liuxing" + i).getBytes("UTF-8"));
      mqProducer.send(message,
          //回调函数,用来处理请求的结果,
          new SendCallback() {
            //投递成功
            public void onSuccess(SendResult sendResult) {
              System.out.println(sendResult);
            }
            //投递失败
            public void onException(Throwable throwable) {
              throwable.printStackTrace();
            }
          });
    }
  }

}

3. 单向

package com.firewolf.rocketmq;

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;

/**
 * 作者:刘兴 时间:2019-07-11
 * 单向生产者,不关心投递的结果
 **/
public class OneWayProducer {

  public static void main(String[] args) throws Exception {
    DefaultMQProducer producer = new
        DefaultMQProducer("producer-group1");
    producer.setNamesrvAddr("10.211.55.6:9876");
    producer.start();
    for (int i = 0; i < 5; i++) {
      //创建要发送的消息对象
      Message msg = new Message("mytopic" /* Topic */,
          "TagA" /* Tag */,
          ("OneWay Message " +
              i).getBytes("UTF-8") /* Message body */
      );
      //发送消息
      producer.sendOneway(msg);
    }
    //关闭生产者
    producer.shutdown();
  }

}

启动生产者和消费者,就能看到控制台打印的消息投递和接受的信息,当然,我们也可以通过web管理控制台来查看。

四、广播消息

消息模型有两种:广播消息和集群,被定义在MessageModel中

public enum MessageModel {
  BROADCASTING("BROADCASTING"),
  CLUSTERING("CLUSTERING");
  ....

默认情况下,消费者会使用CLUSTERING,这种模式下,各个消费者只处理其中的一部分,而BROADCASTING模式下,每个消费者会处理所有的消息。

用法

在消费者注册监听之前调用Consumer.setMessageModel进行设置即可

五、延时发送

通过给Message设置Message.setDelayTimeLevel(int level)即可让消息延迟发送,默认级别有:“1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”,当我们把级别设置成3的时候,就会延时10秒。

六、批量发送消息

批量发送调用MQProducer的类似于SendResult send(final Collection<Message> msgs)的方法即可,注意的是,批量发送要求一次的消息量不能大于1M。

七、顺序消息

(一)分析

实际上,RocketMQ是支持顺序消费的。 但这个顺序,不是全局顺序,只是分区(就是一个Message Queue)顺序,要全局顺序只能一个分区。
默认情况下,我们收到的消息看起来不是顺序的,是因为发送消息的时候,消息发送默认是会采用轮询的方式发送到不通的queue(分区)。而消费端消费的时候,是会分配到多个queue的,多个queue是同时拉取提交消费。
但是同一条queue里面,RocketMQ的确是能保证FIFO的。那么要做到顺序消息,需要如下两方面进行处理:

  1. 把消息确保投递到同一条queue。 rocketmq消息生产端把需要保证顺序的消息放到同一个MessageQueue中。如:实例中把订单号取了做了一个取模运算再丢到selector中,selector保证同一个模的都会投递到同一条queue。 即: 相同订单号的—>有相同的模—>有相同的queue。
  2. 保证消费端顺序消费。通过第1条,会出现如下情况:同一批你需要做到顺序消费的肯定会投递到同一个queue,同一个queue肯定会投递到同一个消费实例,同一个消费实例肯定是顺序拉取并顺序提交线程池的,接下来,只要保证消费端顺序消费,就可以保证消息有序了。
    保证消费有序有两种方式:
  • 使用MessageListenerOrderly来进行消费,这个已经保证了顺序;
  • 使用MessageListenerConcurrently,把消费者线程设置成单线程,即可

(二)示例

生产者

package com.firewolf.rocketmq;

import java.util.List;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.common.RemotingHelper;

public class OrderedProducer {

  public static void main(String[] args) throws Exception {
    //Instantiate with a producer group name.
    DefaultMQProducer producer = new DefaultMQProducer("example_group_name");
    //Launch the instance.
    producer.setNamesrvAddr("10.211.55.6:9876");
    producer.start();
    String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"};
    for (int i = 0; i < 100; i++) {
      int orderId = i % 10;
      Message msg = new Message("mytopic", tags[i % tags.length],
          ("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);
    }
    //server shutdown
    producer.shutdown();
  }
}

消费者

package com.firewolf.rocketmq;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;

public class OrderedConsumer {

  public static void main(String[] args) throws Exception {
    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("example_group_name");
    consumer.setNamesrvAddr("10.211.55.6:9876");
    consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
    consumer.subscribe("mytopic", "*");
    consumer.registerMessageListener(new MessageListenerOrderly() {
      public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs,
          ConsumeOrderlyContext context) {
        for (MessageExt msg : msgs) {
          System.out.println(msg.getQueueId() + "," + new String(msg.getBody()));
        }
        return ConsumeOrderlyStatus.SUCCESS;
      }
    });

    consumer.start();
    System.out.printf("Consumer Started.%n");
  }
}

八、消息筛选

可以通过简单的类似SQL的语法对消息进行过滤。

(一)支持的语法

数字比较: >, >=, <, <=, BETWEEN, =;
字符操作:<>, IN;
空值判断:IS NULL 、IS NOT NULL;
逻辑操作:AND, OR, NOT;

(二)生产者

DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
producer.start();

Message msg = new Message("TopicTest",
    tag,
    ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET)
);
// Set some properties.
msg.putUserProperty("a", String.valueOf(i));

SendResult sendResult = producer.send(msg);
   
producer.shutdown();

(三)消费者

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");

// only subsribe messages have property a, also a >=0 and a <= 3
consumer.subscribe("TopicTest", MessageSelector.bySql("a between 0 and 3");

consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});
consumer.start();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值