RocketMQ of Producer

A step of sending a message Producer

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

Second, the different types of producers

Producer to write the message in the message queue, require a different write strategy according to different business scenarios, synchronous transmission, asynchronous transmission, and transmitting transaction message transmission delay and the like.

2.1 synchronous transmission

public class Producer {
    
    public static void main(String[] args) throws MQClientException, RemotingException,
            InterruptedException, MQBrokerException {
        // 创建生产者对象
        DefaultMQProducer producer = new DefaultMQProducer("producerGroupName");
        // 设置实例化名称
        producer.setInstanceName("SyncProducer");
        // 指定同步模式下,失败重试次数
        producer.setRetryTimesWhenSendFailed(5);                
        // 设置服务器地址
        producer.setNamesrvAddr(RocketMQConfig.NAME_SERVER);
        // 启动实例
        producer.start();
        // 实例化消息对象
        Message message = new Message("topicTest", "tagA", "同步消息发送".getBytes());
        // 同步发送消息
        SendResult sendResult = producer.send(message);
        System.out.printf("%s%n", sendResult);
        // 关闭生产者
        producer.shutdown();
    }
}

2.2 Asynchronous Transmission

public class Producer {

    public static void main(String[] args) throws MQClientException, RemotingException,
            InterruptedException, MQBrokerException {
        // 创建生产者对象
        DefaultMQProducer producer = new DefaultMQProducer("producerGroupName");
        // 设置实例化名称
        producer.setInstanceName("AsyncProducer");
        // 指定异步模式下,失败重试次数
        producer.setRetryTimesWhenSendAsyncFailed(5);
        // 设置服务器地址
        producer.setNamesrvAddr(RocketMQConfig.NAME_SERVER);
        // 启动实例
        producer.start();
        // 实例化消息对象
        Message message = new Message("topicTest", "tagA", "异步消息发送".getBytes());
        // 异步发送消息
        producer.send(message, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.printf("%s%n", sendResult);
                // 关闭生产者
                producer.shutdown();
            }

            @Override
            public void onException(Throwable e) {
                e.printStackTrace();
            }
        });
    }
}    

2.3 transmission delay

RocketMQ support message transmission delay, Broker after receiving such messages, will be delayed for some time to process, so that messages take effect after a period of time specified.

Use delayed messages is creating Message object, the call setDelayTimeLevel (int level) method to set the delay time. Currently does not support custom time, using only a predefined time length, such as setDelayTimeLevel (3) represents the delay 10s.

public class Producer {

    public static void main(String[] args) throws MQClientException, RemotingException,
            InterruptedException, MQBrokerException {
        // 创建生产者对象
        DefaultMQProducer producer = new DefaultMQProducer("producerGroupName");
        // 设置实例化名称
        producer.setInstanceName("SyncProducer");
        // 指定同步模式下,失败重试次数
        producer.setRetryTimesWhenSendFailed(5);
        // 设置服务器地址
        producer.setNamesrvAddr(RocketMQConfig.NAME_SERVER);
        // 启动实例
        producer.start();
        // 实例化消息对象
        Message message = new Message("topicTest", "tagA", "延迟消息发送".getBytes());
        // 设置延迟时间,时间长度为(1s/5s/10s/30s/1m/2m/3m/4m/5m/6m/7m/8m/9m/10m/20m/30m/1h/2h)
        message.setDelayTimeLevel(3);
        // 发送消息
        SendResult sendResult = producer.send(message);
        System.out.printf("%s%n", sendResult);
        // 关闭实例
        producer.shutdown();
    }
}    

2.4 Customizing transmission rules

A Topic have a plurality of Message Queue , Producer default configuration will turn to each Message Queue transmitted message. Consumer in the consumer message, based on the load balancing policy, consumption is assigned to the Message Queue . If you want the message to the specified the Message Queue , you can use the Message-QueueSelector .

public class MyMessageQueueSelector implements MessageQueueSelector {
    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object key) {\
        // 自定义选择 Message Queue 规则
        int id = Integer.parseInt(key.toString());
        int idMainIndex = id / 100;
        int size = mqs.size();
        int index = idMainIndex % size;
        return mqs.get(index);
    }
}

When sending a message, the MessageQueueSelector object as a parameter.

public class Producer {

    public static void main(String[] args) throws MQClientException, RemotingException,
            InterruptedException, MQBrokerException {
        // 创建生产者对象
        DefaultMQProducer producer = new DefaultMQProducer("producerGroupName");
        // 设置实例化名称
        producer.setInstanceName("SyncProducer");
        // 指定同步模式下,失败重试次数
        producer.setRetryTimesWhenSendFailed(5);
        // 设置服务器地址
        producer.setNamesrvAddr(RocketMQConfig.NAME_SERVER);
        // 启动实例
        producer.start();
        // 实例化消息对象
        Message message = new Message("topicTest", "tagA", "自定义消息发送".getBytes());
        // 发送消息
        SendResult sendResult = producer.send(message,new MyMessageQueueSelector(),1000);
        System.out.printf("%s%n", sendResult);
        // 关闭实例
        producer.shutdown();
    }
}    

2.5 transaction message

When a few things at the same time when you need success or failure, you need to use transactional features, such as bank transfer: A bank account to transfer some ten thousand yuan a bank account to B:

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

These two operations need to succeed or fail, RocketMQ by way of the realization of two-phase commit transaction message, TransactionMqRroducer process is as follows:

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

RocketMQ to support the user by several types of transaction message:

  • TransactionMQProducer

    和 DefaultMQProducer 用户类似,通过它启动事务消息,相比 DefaultMQProducer 需要多设置本地事务处理函数和回查状态函数
  • TransactionListener

    提供本地执行方法和回查方法,返回 LocalTransactionState 状态标识:
        * LocalTransactionState.COMMIT_MESSAGE:提交
        * LocalTransactionState.ROLLBACK_MESSAGE:回滚
        * LocalTransactionState.UNKNOW:未知,需要回查

Achieve TransactionListener Interface

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 msgKey = msg.getKeys();
        System.out.println("start execute local transaction " + msgKey);
        LocalTransactionState state;
        if (msgKey.contains("1")) {
            // 第一条消息让他通过
            state = LocalTransactionState.COMMIT_MESSAGE;
        } else if (msgKey.contains("2")) {
            // 第二条消息模拟异常,明确回复回滚操作
            state = LocalTransactionState.ROLLBACK_MESSAGE;
        } else {
            // 第三条消息无响应,让它调用回查事务方法
            state = LocalTransactionState.UNKNOW;
            // 给剩下3条消息,放1,2,3三种状态
            localTrans.put(msgKey, transactionIndex.incrementAndGet());
        }
        System.out.println("executeLocalTransaction:" + msg.getKeys() + ",execute state:" + state + ",current time:" + System.currentTimeMillis());
        return state;
    }

    // 检查本地事务结果
    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        String msgKey = msg.getKeys();
        System.out.println("start check local transaction " + msgKey);
        Integer state = localTrans.get(msgKey);
        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;
        }
//        return  LocalTransactionState.COMMIT_MESSAGE;
    }
}

Implementing transactional message producer

public class Producer {

    public static void main(String[] args) throws MQClientException, RemotingException,
            InterruptedException, MQBrokerException {
        // 创建事务生产者对象
        TransactionMQProducer producer = new TransactionMQProducer("producerGroupName");
        // 设置实例化名称
        producer.setInstanceName("SyncProducer");
        // 指定同步模式下,失败重试次数
        producer.setRetryTimesWhenSendFailed(5);
        // 设置事务监听器
        producer.setTransactionListener(new MyTransactionListener());
        // 设置服务器地址
        producer.setNamesrvAddr(RocketMQConfig.NAME_SERVER);

        // 启动实例
        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);
        }

        // 关闭实例
        // producer.shutdown();
    }

}

Guess you like

Origin www.cnblogs.com/markLogZhu/p/12539612.html