java rocketmq 配置文件 rocketmq producer配置_System

      右侧的部署模式 ,双机互为主备

 发送消息的步骤

1) 设置 Producer 的 GroupName(Producer Group是一类 Producer 的集合);
2) 设置 InstanceName,当一个 JVM 需要启动多个 Producer 时,需要指定不同的 InstanceName 来区分,不显式设置时使用默认名称 "DEFAULT";
3) 设置发送失败重试次数,默认值是2次,可能会出现重复消息,因此需要消费端进行控制;
4) 设置 NameServer 地址;
5) 组装数据并发送

     核心参数

* producerGroup:生产者组名
* createTopicKey:创建 Topic,生产环境一般不直接从代码层面创建而是在控制台创建    
* defaultTopicQueueNums:每个 Topic 下的队列数量,默认数量是4
* sendMsgTimeout:消息发送超时时间,单位ms
* compressMsgBodyOverHowmuch:当消息大小超过指定字节就会开启压缩,默认字节为4096
* retryTimesWhenSendFailed:同步模式下,消息发送失败重试次数,默认2次
* retryTimesWhenSendAsyncFailed:异步模式下,消息发送失败重试次数,默认2次
* retryAnotherBrokerWhenNotStoreOK:当 broker 接收失败时,是否切换另一个 broker ,默认为 false 
* maxMessageSize:最大的消息容量限制,默认是4M

     Producer端org.apache.rocketmq.client.impl.CommunicationMode 枚举类中定义3种发送方式

public enum CommunicationMode {
    SYNC,
    ASYNC,
    ONEWAY,
}
  • Sync:同步的发送方式,会等待发送结果后才返回
  • Async:异步的发送方式,发送完后,立刻返回。Client 在拿到 Broker 的响应结果后,会回调指定的 callback. 可以指定 Timeout。默认的 3000ms.
  • Oneway:发出去后,什么都不管直接返回

发送结果

       org.apache.rocketmq.client.produce.SendStatus 枚举类中定义了如下4种发送结果

package org.apache.rocketmq.client.producer;

public enum SendStatus {
    SEND_OK,
    FLUSH_DISK_TIMEOUT,
    FLUSH_SLAVE_TIMEOUT,
    SLAVE_NOT_AVAILABLE,
}

  SEND_OK : 消息发送成功。注意的是消息发送成功也不意味着它是可靠的。要确保不会丢失任何消息,还应启用同步Master服务器或同步刷盘,即SYNC_MASTER或 SYNC_FLUSH。

       FLUSH_DISK_TIMEOUT:消息发送成功但是服务器刷盘超时。此时消息已经进入服务器队列(内存),只有服务器宕机,消息才会丢失。消息存储配置参数中可以设置刷盘方式和同步刷盘时间长度,如果Broker服务器设置了刷盘方式为同步刷盘,即FlushDiskType=SYNC_FLUSH(默认为异步刷盘方式),当Broker服务器未在同步刷盘时间内(默认为5s)完成刷盘,则将返回该状态——刷盘超时。

       FLUSH_SLAVE_TIMEOUT :消息发送成功,但是服务器同步到Slave时超时。此时消息已经进入服务器队列,只有服务器宕机,消息才会丢失。如果Broker服务器的角色是同步Master,即SYNC_MASTER(默认是异步Master即ASYNC_MASTER),并且从Broker服务器未在同步刷盘时间(默认为5秒)内完成与主服务器的同步,则将返回该状态——数据同步到Slave服务器超时

       SLAVE_NOT_AVAILABLE:消息发送成功,但是此时Slave不可用。如果Broker服务器的角色是同步Master,即SYNC_MASTER(默认是异步Master服务器即ASYNC_MASTER),但没有配置slaveBroker服务器,则将返回该状态——无Slave服务器可用。

发送同步消息

public class SyncProducer {

    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {
        DefaultMQProducer producerGroup = new DefaultMQProducer("producer_group");
        producerGroup.setNamesrvAddr("192.168.21.29:9876");
        producerGroup.setSendMsgTimeout(10000);
        producerGroup.start();

        Message msg = new Message("newLand", "tagLand", "hello rocketmq".getBytes(RemotingHelper.DEFAULT_CHARSET));
        SendResult sendResult = producerGroup.send(msg);
        System.out.print("sendResult:" + sendResult);
        producerGroup.shutdown();
    }
}

发送异步消息

public class AsyncProducer {

    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException {
        DefaultMQProducer productGroup = new DefaultMQProducer("productGroup");
        productGroup.setNamesrvAddr("192.168.21.29:9876");

        //设置发送失败重试次数
        productGroup.setRetryTimesWhenSendAsyncFailed(5);
        //Launch the instance
        productGroup.start();

        int count = 5;
        CountDownLatch countDownLatch = new CountDownLatch(count);
        for (int i = 0; i < count; i++) {
            final int index = i;
            Message message = new Message("topicAsync", "tagAsync",
                    "orderId", "sending msg content".getBytes(RemotingHelper.DEFAULT_CHARSET));

            productGroup.send(message, new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    countDownLatch.countDown();
                    System.out.printf("%-10d OK %s %n", index, sendResult.getMsgId());
                }

                @Override
                public void onException(Throwable e) {
                    countDownLatch.countDown();
                    System.out.printf("%-10d Exception %s %n", index, e);
                }
            });
        }
        countDownLatch.await();
        productGroup.shutdown();
    }
}

延迟发送

  RocketMQ 支持发送延迟消息,Broker 在收到这类消息后,会延迟一段时间再处理,使消息在规定的一段时间后生效。

  延迟消息的使用方法是在创建 Message 对象时,调用 setDelayTimeLevel(int level) 方法设置延迟时间。目前不支持自定义时间,只能使用预定义的时间长度,如 setDelayTimeLevel(3) 表示延迟10s。

public class DelayProducer {

    public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
        DefaultMQProducer producer = new DefaultMQProducer("productGroup");
        //实例化名称
        producer.setInstanceName("syncProducer");
        producer.setRetryTimesWhenSendFailed(5);
        producer.setNamesrvAddr("192.168.21.29:9876");
        producer.start();

        Message message = new Message("delayTopic", "delayTag", "delay message".getBytes());
        // 设置延迟时间,时间长度为(1s/5s/10s/30s/1m/2m/3m/4m/5m/6m/7m/8m/9m/10m/20m/30m/1h/2h)
        message.setDelayTimeLevel(2);
        SendResult sendResult = producer.send(message);
        System.out.printf("%s%n", sendResult);
        producer.shutdown();
    }
}

自定义发送规则

  一个 Topic 会有多个 Message Queue , Producer 默认配置会轮流向各个 Message Queue 发送消息。Consumer 在消费消息时,会根据负载均衡策略,消费被分配到的 Message Queue。如果要把消息发送到指定的 Message Queue,可以使用 Message-QueueSelector。

public class MyMessageQueueSelector implements MessageQueueSelector {
    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
        int id = Integer.parseInt(arg.toString());
        int idMainIndex = id / 10;
        int size = mqs.size();
        int index = idMainIndex % size;
        return mqs.get(index);
    }
}


    public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
        DefaultMQProducer producer = new DefaultMQProducer("productGroup");
        //实例化名称
        producer.setInstanceName("syncProducer");
        producer.setRetryTimesWhenSendFailed(5);
        producer.setNamesrvAddr("192.168.21.29:9876");
        producer.start();

        Message message = new Message("delayTopic", "delayTag", "delay message".getBytes());
        // 设置延迟时间,时间长度为(1s/5s/10s/30s/1m/2m/3m/4m/5m/6m/7m/8m/9m/10m/20m/30m/1h/2h)
//        message.setDelayTimeLevel(2);
        SendResult sendResult = producer.send(message, new MyMessageQueueSelector(), 1000);
        System.out.printf("%s%n", sendResult);
        producer.shutdown();
    }

  延迟队列的核心思路: 【利用中间队列临时存储】—>所有的延迟消息由producer消息发出之后,都会存放在一个topic下 (SHCEDULE_TOPIC_XXXX), 不同的延迟级别对应不同的队列序号,当延迟时间到了之后,由定时线程读取转换为普通的消息存到真实指定的topic下,此时对于consumer端此消息才可见,从而被consumer消费。

事务消息

  当某几件事需要同时成功或失败的时候,就需要使用到事务功能,如银行转账:A 银行的某账户要转一万元到 B 银行的某账户:

1) 从 A 账户扣除一万元
2) 对 B 账户增加一万元

  两个操作需要同时成功或同时失败, RocketMQ 采用两阶段提交的方式实现事务消息,TransactionMqRroducer 处理流程如下:

java rocketmq 配置文件 rocketmq producer配置_System_02

1) 发送方向 RocketMQ 发送 "待确认" 消息;
2) RocketMQ 将收到的 "待确认" 消息持久化成功后,向发送方回复消息已经发送成功,此时第一阶段消息发送完成;
3) 发送开始执行本地事件逻辑
4) 发送方根据本地事件执行结果向 RocketMQ 发送二次确认(Commit 或 Rollback) 消息:
    * 接收到 commit 消息,将把第一阶段消息标记为可投递,订阅方将会收到该消息;
    * 接收到 rollback 消息,将删除第一阶段消息,订阅方不会接受到该消息;
5) 如果出现异常情况,步骤4 提交的二次确认最终未到达 RocketMQ ,服务器将经过固定时间段后将对 "待确认" 消息发起回查请求;
6) 发送方收到消息回传请求后(如果第一阶段发送的 Producer 不能工作时,将会回传给同一个 ProducerGroup 的其他 Producer),通过对检查对应消息的本地事件执行结果返回 Commit 或 Rollback 状态;
7) RocketMQ 收到回查请求后,按照步骤4) 流程继续处理

RocketMQ 通过以下类来支持用户实现事务消息:

  • TransactionMQProducer
和 DefaultMQProducer 用户类似,通过它启动事务消息,相比 DefaultMQProducer 需要多设置本地事务处理函数和回查状态函数
  • TransactionListener
提供本地执行方法和回查方法,返回 LocalTransactionState 状态标识:
    * LocalTransactionState.COMMIT_MESSAGE:提交
    * LocalTransactionState.ROLLBACK_MESSAGE:回滚
    * LocalTransactionState.UNKNOW:未知,需要回查
public class MyTransactionListener implements TransactionListener {
    private AtomicInteger transactionIndex = new AtomicInteger(0);
    private AtomicInteger checkTimes = new AtomicInteger(0);

    private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<>();

    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        String keys = msg.getKeys();
        System.out.println("start execute local transaction " + keys);
        LocalTransactionState state;
        if (keys.contains("1")) {
            state = LocalTransactionState.COMMIT_MESSAGE;
        } else if (keys.contains("2")) {
            state = LocalTransactionState.ROLLBACK_MESSAGE;
        } else {
            state = LocalTransactionState.UNKNOW;
            localTrans.put(keys, transactionIndex.incrementAndGet());
        }
        System.out.println("executeLocalTransaction:" + msg.getKeys() + ",execute state:" + state + ",current time:" + System.currentTimeMillis());
        return state;
    }

    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        String keys = msg.getKeys();
        System.out.println("start check local transaction " + keys);
        Integer state = localTrans.get(keys);
        switch (state) {
            case 1:
                System.out.println("check result unknown 回查次数" + checkTimes.incrementAndGet());
                return LocalTransactionState.UNKNOW;
            case 2:
                System.out.println("check result commit message, 回查次数" + checkTimes.incrementAndGet());
                return LocalTransactionState.COMMIT_MESSAGE;
            case 3:
                System.out.println("check result rollback message, 回查次数" + checkTimes.incrementAndGet());
                return LocalTransactionState.ROLLBACK_MESSAGE;
            default:
                return LocalTransactionState.COMMIT_MESSAGE;
        }
    }
}

public class TransactionProducer {

    public static void main(String[] args) throws MQClientException {
        TransactionMQProducer producer = new TransactionMQProducer("producerGroupName");
        producer.setInstanceName("SyncProducer");
        producer.setRetryTimesWhenSendFailed(5);
        producer.setTransactionListener(new MyTransactionListener());
        producer.setNamesrvAddr("192.168.21.29:9876");
        producer.start();

        for (int i = 0; i < 5; i++) {
            // 实例化消息对象
            Message message = new Message("topicTest", "tagA","msg-" + i, ("事务消息发送" + ":" +  i).getBytes());
            // 发送消息
            SendResult sendResult = producer.sendMessageInTransaction(message, i);
            System.out.printf("%s%n", sendResult);
        }
    }
}

状态

public enum SendStatus {
    // 表示发送成功
    SEND_OK,
    // 表示没有在指定时间内完成刷盘(需要 Broker 的刷盘策略被设置成SYNC_FLUSH)
    FLUSH_DISK_TIMEOUT,
    // 表示在主备模式下,并且 Broker 被设置成SYNC_MASTER,没有在指定时间内完成主从同步
    FLUSH_SLAVE_TIMEOUT,
    // 表示在主备模式下,并且 Broker 被设置成SYNC_MASTER,没有找到被配置成 Slave 的 Broker
    SLAVE_NOT_AVAILABLE,
}

消费者核心参数

* consumerGroup:消费者组名  
* MessageModel:消息模型,定义了消息传递到消费者的方式,默认是 MessageModel.CLUSTERING
    * MessageModel.BROADCASTING:广播
    * MessageModel.CLUSTERING:集群
* consumeFromWhere: 消费者开始消费的位置,默认值是 ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET
    * ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET:从队列最后的位置开始消费
    * ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET:从队列前面最开始消费
    * ConsumeFromWhere.CONSUME_FROM_TIMESTAMP: 从指定时间开始消费,之前的消息将会被忽略    
* consumeTimestamp:
* allocateMessageQueueStrategy:消息分配策略
* subscription:订阅关系
* offsetStore:存储消息偏移量
* consumeThreadMin:线程池最小值,默认值是20
* consumeThreadMax:线程池最大值,默认值是20
* consumeConcurrentlyMaxSpan:单个队列并行消费最大的跨度,默认2000
* pullThresholdForQueue:一个队列最大的消费个数,默认1000
* pullInterval:消息拉取的时间间隔
* pullBatchSize:消息拉取的个数,默认32啊
* consumeMessageBatchMaxSize:批量消费量,默认1  
* messageListener:消息监听器,用来处理消息,它有两个实现类
    * MessageListenerOrderly:按顺序一个个消费    
    * MessageListenerConcurrently:并行消费

集群模式

        同一个 consumerGroup 里并且订阅的 tag 也必须是一样的,这样的 consumer 实例才能组成 consumer 集群; 当 consumer 使用集群消费时,每条消息只会被 consumer 集群内的任意一个 consumer 实例消费一次; 默认的消费模式就是集群模式; 集群模式天然实现负载均衡机制

广播模式

        同一个 consumerGroup 里的 Consumer 会消费订阅 Topic 的全部消息 

        通过 consumer.setMessageModel(MessageModel.BROADCASTING) 方法设置

  Offset 是指某个 Topic 下的一条消息在某个 MessageQueue 里的位置;集群模式下因为每个 Consumer 消费所订阅主题的一部分,所以采用远程文件存储 Offset;

广播模式下,由于每个 Consumer 需要消费所有的消息,所以采用本地文件存储 Offset。

  根据对读取操作的控制情况,可以消费者分为两种类型。一个是 DefaultMQPushConsumer,由系统控制读取操作,收到消息后自动调用传入的处理方法来处理;另一个是 DefaultMQPullConsumer ,读取操作中的大部分功能由使用者自主控制。

消费消息-push

public class PushConsumer {

    public static void main(String[] args) throws MQClientException {
        DefaultMQPushConsumer pushConsumer = new DefaultMQPushConsumer("productGroup");
        pushConsumer.setNamesrvAddr("192.168.21.29:9876");

        // Subscribe one more more topics to consume.
        pushConsumer.subscribe("topicAsync", "*");
        //
        pushConsumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt msg : msgs) {
                    System.out.println(new String(msg.getBody()));
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        pushConsumer.start();
        System.out.print("Push consumer started");
    }
}

消息pull消费

public static void main(String[] args) throws MQClientException {
        DefaultLitePullConsumer pullConsumer = new DefaultLitePullConsumer("LitePullConsumer");
        pullConsumer.setNamesrvAddr("192.168.21.29:9876");
        // 关闭自动提交偏移量
        pullConsumer.setAutoCommit(false);
        pullConsumer.subscribe("delayTopic", "*");
        pullConsumer.start();

        try {
            //循环开始消费消息
            while (true) {
                List<MessageExt> messageExts = pullConsumer.poll();
                System.out.printf("%s%n", messageExts);
            }
        } finally {
            pullConsumer.shutdown();
        }
    }