RocketMQ事务消费和顺序消费详解

2、RocketMQ的消息模型 在通过 producer.sendMessageInTransaction发送事务消息后,如果消息发送成功,就会调用到这里的executeLocalTransaction方法,来执行本地事务。如果是返回未知状态,RocketMQ Broker服务器会以1分钟的间隔时间不断回查,直至达到事务回查最大检测数,如果超过这个数字还未查询到事务状态,则回滚此消息。值得注意的是,如果发送事务消息,在这里我们的创建的实例必须是 TransactionMQProducer。上面的代码中,主要就是创建事务消息的发送者。 阅读详情

一、RocketMq有3中消息类型

1.普通消费

2. 顺序消费

3.事务消费

  • 顺序消费场景

在网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一、创建订单 ,第二:订单付款,第三:订单完成。也就是这个三个环节要有顺序,这个订单才有意义。RocketMQ可以保证顺序消费。

  • rocketMq实现顺序消费的原理

 produce在发送消息的时候,把消息发到同一个队列(queue)中,消费者注册消息监听器为MessageListenerOrderly,这样就可以保证消费端只有一个线程去消费消息

注意:是把把消息发到同一个队列(queue),不是同一个topic,默认情况下一个topic包括4个queue

单个节点(Producer端1个、Consumer端1个)

1、Producer.java 

复制代码
package order;  
  
import java.util.List;  
  
import com.alibaba.rocketmq.client.exception.MQBrokerException;  
import com.alibaba.rocketmq.client.exception.MQClientException;  
import com.alibaba.rocketmq.client.producer.DefaultMQProducer;  
import com.alibaba.rocketmq.client.producer.MessageQueueSelector;  
import com.alibaba.rocketmq.client.producer.SendResult;  
import com.alibaba.rocketmq.common.message.Message;  
import com.alibaba.rocketmq.common.message.MessageQueue;  
import com.alibaba.rocketmq.remoting.exception.RemotingException;  
  
/** 
 * Producer,发送顺序消息 
 */  
public class Producer {  
    public static void main(String[] args) {  
        try {  
            DefaultMQProducer producer = new DefaultMQProducer("order_Producer");  
            producer.setNamesrvAddr("192.168.100.145:9876;192.168.100.146:9876;192.168.100.149:9876;192.168.100.239:9876");  
  
            producer.start();  
  
            // String[] tags = new String[] { "TagA", "TagB", "TagC", "TagD",  
            // "TagE" };  
  
            for (int i = 1; i <= 5; i++) {  
  
                Message msg = new Message("TopicOrderTest", "order_1", "KEY" + i, ("order_1 " + i).getBytes());  
  
                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);  
                    }  
                }, 0);  
  
                System.out.println(sendResult);  
            }  
  
            producer.shutdown();  
        } catch (MQClientException e) {  
            e.printStackTrace();  
        } catch (RemotingException e) {  
            e.printStackTrace();  
        } catch (MQBrokerException e) {  
            e.printStackTrace();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}  
复制代码

2、Consumer.java

复制代码
package order;   
import java.util.List;  
import java.util.concurrent.TimeUnit;  
import java.util.concurrent.atomic.AtomicLong;   
import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer;  
import com.alibaba.rocketmq.client.consumer.listener.ConsumeOrderlyContext;  
import com.alibaba.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;  
import com.alibaba.rocketmq.client.consumer.listener.MessageListenerOrderly;  
import com.alibaba.rocketmq.client.exception.MQClientException;  
import com.alibaba.rocketmq.common.consumer.ConsumeFromWhere;  
import com.alibaba.rocketmq.common.message.MessageExt;  
  
/** 
 * 顺序消息消费,带事务方式(应用可控制Offset什么时候提交) 
 */  
public class Consumer1 {  
  
    public static void main(String[] args) throws MQClientException {  
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order_Consumer");  
        consumer.setNamesrvAddr("192.168.100.145:9876;192.168.100.146:9876;192.168.100.149:9876;192.168.100.239:9876");  
  
        /** 
         * 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br> 
         * 如果非第一次启动,那么按照上次消费的位置继续消费 
         */  
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);  
  
        consumer.subscribe("TopicOrderTest", "*");  
  
        consumer.registerMessageListener(new MessageListenerOrderly() {  
            AtomicLong consumeTimes = new AtomicLong(0);  
  
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {  
                // 设置自动提交  
                context.setAutoCommit(true);  
                for (MessageExt msg : msgs) {  
                    System.out.println(msg + ",内容:" + new String(msg.getBody()));  
                }  
  
                try {  
                    TimeUnit.SECONDS.sleep(5L);  
                } catch (InterruptedException e) {  
  
                    e.printStackTrace();  
                }  
                ;  
  
                return ConsumeOrderlyStatus.SUCCESS;  
            }  
        });  
  
        consumer.start();  
  
        System.out.println("Consumer1 Started.");  
    }  
  
}  
复制代码

结果如下图所示:

这个五条数据被顺序消费了

  • 多个节点(Producer端1个、Consumer端2个)

Producer.java

复制代码
package order;  
  
import java.util.List;  
  
import com.alibaba.rocketmq.client.exception.MQBrokerException;  
import com.alibaba.rocketmq.client.exception.MQClientException;  
import com.alibaba.rocketmq.client.producer.DefaultMQProducer;  
import com.alibaba.rocketmq.client.producer.MessageQueueSelector;  
import com.alibaba.rocketmq.client.producer.SendResult;  
import com.alibaba.rocketmq.common.message.Message;  
import com.alibaba.rocketmq.common.message.MessageQueue;  
import com.alibaba.rocketmq.remoting.exception.RemotingException;  
  
/** 
 * Producer,发送顺序消息 
 */  
public class Producer {  
    public static void main(String[] args) {  
        try {  
            DefaultMQProducer producer = new DefaultMQProducer("order_Producer");  
            producer.setNamesrvAddr("192.168.100.145:9876;192.168.100.146:9876;192.168.100.149:9876;192.168.100.239:9876");  
  
            producer.start();  
  
            // String[] tags = new String[] { "TagA", "TagB", "TagC", "TagD",  
            // "TagE" };  
  
            for (int i = 1; i <= 5; i++) {  
  
                Message msg = new Message("TopicOrderTest", "order_1", "KEY" + i, ("order_1 " + i).getBytes());  
  
                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);  
                    }  
                }, 0);  
  
                System.out.println(sendResult);  
            }  
            for (int i = 1; i <= 5; i++) {  
  
                Message msg = new Message("TopicOrderTest", "order_2", "KEY" + i, ("order_2 " + i).getBytes());  
  
                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);  
                    }  
                }, 1);  
  
                System.out.println(sendResult);  
            }  
            for (int i = 1; i <= 5; i++) {  
  
                Message msg = new Message("TopicOrderTest", "order_3", "KEY" + i, ("order_3 " + i).getBytes());  
  
                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);  
                    }  
                }, 2);  
  
                System.out.println(sendResult);  
            }  
  
            producer.shutdown();  
        } catch (MQClientException e) {  
            e.printStackTrace();  
        } catch (RemotingException e) {  
            e.printStackTrace();  
        } catch (MQBrokerException e) {  
            e.printStackTrace();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}  
复制代码

Consumer1.java

 
复制代码
/** 
 * 顺序消息消费,带事务方式(应用可控制Offset什么时候提交) 
 */  
public class Consumer1 {  
  
    public static void main(String[] args) throws MQClientException {  
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order_Consumer");  
        consumer.setNamesrvAddr("192.168.100.145:9876;192.168.100.146:9876;192.168.100.149:9876;192.168.100.239:9876");  
  
        /** 
         * 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br> 
         * 如果非第一次启动,那么按照上次消费的位置继续消费 
         */  
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);  
  
        consumer.subscribe("TopicOrderTest", "*");  
          
        /** 
         * 实现了MessageListenerOrderly表示一个队列只会被一个线程取到  
         *,第二个线程无法访问这个队列 
         */  
        consumer.registerMessageListener(new MessageListenerOrderly() {  
            AtomicLong consumeTimes = new AtomicLong(0);  
  
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {  
                // 设置自动提交  
                context.setAutoCommit(true);  
                for (MessageExt msg : msgs) {  
                    System.out.println(msg + ",内容:" + new String(msg.getBody()));  
                }  
  
                try {  
                    TimeUnit.SECONDS.sleep(5L);  
                } catch (InterruptedException e) {  
  
                    e.printStackTrace();  
                }  
                ;  
  
                return ConsumeOrderlyStatus.SUCCESS;  
            }  
        });  
  
        consumer.start();  
  
        System.out.println("Consumer1 Started.");  
    }  
  
}  
复制代码

Consumer2.java

复制代码
/** 
 * 顺序消息消费,带事务方式(应用可控制Offset什么时候提交) 
 */  
public class Consumer2 {  
  
    public static void main(String[] args) throws MQClientException {  
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order_Consumer");  
        consumer.setNamesrvAddr("192.168.100.145:9876;192.168.100.146:9876;192.168.100.149:9876;192.168.100.239:9876");  
  
        /** 
         * 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br> 
         * 如果非第一次启动,那么按照上次消费的位置继续消费 
         */  
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);  
  
        consumer.subscribe("TopicOrderTest", "*");  
          
        /** 
         * 实现了MessageListenerOrderly表示一个队列只会被一个线程取到  
         *,第二个线程无法访问这个队列 
         */  
        consumer.registerMessageListener(new MessageListenerOrderly() {  
            AtomicLong consumeTimes = new AtomicLong(0);  
  
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {  
                // 设置自动提交  
                context.setAutoCommit(true);  
                for (MessageExt msg : msgs) {  
                    System.out.println(msg + ",内容:" + new String(msg.getBody()));  
                }  
  
                try {  
                    TimeUnit.SECONDS.sleep(5L);  
                } catch (InterruptedException e) {  
  
                    e.printStackTrace();  
                }  
                ;  
  
                return ConsumeOrderlyStatus.SUCCESS;  
            }  
        });  
  
        consumer.start();  
  
        System.out.println("Consumer2 Started.");  
    }  
  
}  
复制代码

先启动Consumer1和Consumer2,然后启动Producer,Producer会发送15条消息
Consumer1消费情况如图,都按照顺序执行了




Consumer2消费情况如图,都按照顺序执行了

二、事务消费

这里说的主要是分布式事物。下面的例子的数据库分别安装在不同的节点上。

事物消费需要先说说什么是事务。比如说:我们跨行转账,从工商银行转到建设银行,也就是我从工商银行扣除1000元之后,我的建设银行也必须加1000元。这样才能保证数据的一致性。假如工商银行转1000元之后,建设银行的服务器突然宕机,那么我扣除了1000,但是并没有在建设银行给我加1000,就出现了数据的不一致。因此加1000和减1000才行,减1000和减1000必须一起成功,一起失败。

再比如,我们进行网购的时候,我们下单之后,订单提交成功,仓库商品的数量必须减一。但是订单可能是一个数据库,仓库数量可能又是在另个数据库里面。有可能订单提交成功之后,仓库数量服务器突然宕机。这样也出现了数据不一致的问题。

使用消息队列来解决分布式事物:

现在我们去外面饭店吃饭,很多时候都不会直接给了钱之后直接在付款的窗口递饭菜,而是付款之后他会给你一张小票,你拿着这个小票去出饭的窗口取饭。这里和我们的系统类似,提高了吞吐量。即使你到第二个窗口,师傅告诉你已经没饭了,你可以拿着这个凭证去退款,即使中途由于出了意外你无法到达窗口进行取饭,但是只要凭证还在,可以将钱退给你。这样就保证了数据的一致性。

如何保证凭证(消息)有2种方法:

1、在工商银行扣款的时候,余额表扣除1000,同时记录日志,而且这2个表是在同一个数据库实例中,可以使用本地事物解决。然后我们通知建设银行需要加1000给该用户,建设银行收到之后给我返回已经加了1000给用户的确认信息之后,我再标记日志表里面的日志为已经完成。

2、通过消息中间件

原文地址:http://www.jianshu.com/p/453c6e7ff81c

 

RocketMQ第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改消息的状态。

细心的你可能又发现问题了,如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事物消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认,Bob的钱到底是减了还是没减呢?如果减了是回滚还是继续发送确认消息呢?RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。

例子:

Consumer.java

 

 

复制代码
package transaction;  
  
import java.util.List;  
  
import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer;  
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;  
import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;  
import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently;  
import com.alibaba.rocketmq.client.exception.MQClientException;  
import com.alibaba.rocketmq.common.consumer.ConsumeFromWhere;  
import com.alibaba.rocketmq.common.message.MessageExt;  
  
/** 
 * Consumer,订阅消息 
 */  
public class Consumer {  
  
    public static void main(String[] args) throws InterruptedException, MQClientException {  
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("transaction_Consumer");  
        consumer.setNamesrvAddr("192.168.100.145:9876;192.168.100.146:9876;192.168.100.149:9876;192.168.100.239:9876");  
        consumer.setConsumeMessageBatchMaxSize(10);  
        /** 
         * 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br> 
         * 如果非第一次启动,那么按照上次消费的位置继续消费 
         */  
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);  
  
        consumer.subscribe("TopicTransactionTest", "*");  
  
        consumer.registerMessageListener(new MessageListenerConcurrently() {  
  
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {  
  
                try {  
  
                    for (MessageExt msg : msgs) {  
                        System.out.println(msg + ",内容:" + new String(msg.getBody()));  
                    }  
  
                } catch (Exception e) {  
                    e.printStackTrace();  
  
                    return ConsumeConcurrentlyStatus.RECONSUME_LATER;// 重试  
  
                }  
  
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;// 成功  
            }  
        });  
  
        consumer.start();  
  
        System.out.println("transaction_Consumer Started.");  
    }  
}  
复制代码

Producer.java

 

  

复制代码
package transaction;  
  
import com.alibaba.rocketmq.client.exception.MQClientException;  
import com.alibaba.rocketmq.client.producer.SendResult;  
import com.alibaba.rocketmq.client.producer.TransactionCheckListener;  
import com.alibaba.rocketmq.client.producer.TransactionMQProducer;  
import com.alibaba.rocketmq.common.message.Message;  
  
/** 
 * 发送事务消息例子 
 *  
 */  
public class Producer {  
    public static void main(String[] args) throws MQClientException, InterruptedException {  
  
        TransactionCheckListener transactionCheckListener = new TransactionCheckListenerImpl();  
        TransactionMQProducer producer = new TransactionMQProducer("transaction_Producer");  
        producer.setNamesrvAddr("192.168.100.145:9876;192.168.100.146:9876;192.168.100.149:9876;192.168.100.239:9876");  
        // 事务回查最小并发数  
        producer.setCheckThreadPoolMinSize(2);  
        // 事务回查最大并发数  
        producer.setCheckThreadPoolMaxSize(2);  
        // 队列数  
        producer.setCheckRequestHoldMax(2000);  
        producer.setTransactionCheckListener(transactionCheckListener);  
        producer.start();  
  
        // String[] tags = new String[] { "TagA", "TagB", "TagC", "TagD", "TagE"  
        // };  
        TransactionExecuterImpl tranExecuter = new TransactionExecuterImpl();  
        for (int i = 1; i <= 2; i++) {  
            try {  
                Message msg = new Message("TopicTransactionTest", "transaction" + i, "KEY" + i,  
                        ("Hello RocketMQ " + i).getBytes());  
                SendResult sendResult = producer.sendMessageInTransaction(msg, tranExecuter, null);  
                System.out.println(sendResult);  
  
                Thread.sleep(10);  
            } catch (MQClientException e) {  
                e.printStackTrace();  
            }  
        }  
  
        for (int i = 0; i < 100000; i++) {  
            Thread.sleep(1000);  
        }  
  
        producer.shutdown();  
  
    }  
}  
复制代码

TransactionExecuterImpl .java --执行本地事务

复制代码
package transaction;  
  
import com.alibaba.rocketmq.client.producer.LocalTransactionExecuter;  
import com.alibaba.rocketmq.client.producer.LocalTransactionState;  
import com.alibaba.rocketmq.common.message.Message;  
  
/** 
 * 执行本地事务 
 */  
public class TransactionExecuterImpl implements LocalTransactionExecuter {  
    // private AtomicInteger transactionIndex = new AtomicInteger(1);  
  
    public LocalTransactionState executeLocalTransactionBranch(final Message msg, final Object arg) {  
  
        System.out.println("执行本地事务msg = " + new String(msg.getBody()));  
  
        System.out.println("执行本地事务arg = " + arg);  
  
        String tags = msg.getTags();  
  
        if (tags.equals("transaction2")) {  
            System.out.println("======我的操作============,失败了  -进行ROLLBACK");  
            return LocalTransactionState.ROLLBACK_MESSAGE;  
        }  
        return LocalTransactionState.COMMIT_MESSAGE;  
        // return LocalTransactionState.UNKNOW;  
    }  
}  
复制代码


TransactionCheckListenerImpl--未决事务,服务器回查客户端(目前已经被阉割啦)

 
复制代码
package transaction;  
  
import com.alibaba.rocketmq.client.producer.LocalTransactionState;  
import com.alibaba.rocketmq.client.producer.TransactionCheckListener;  
import com.alibaba.rocketmq.common.message.MessageExt;  
  
/** 
 * 未决事务,服务器回查客户端 
 */  
public class TransactionCheckListenerImpl implements TransactionCheckListener {  
    // private AtomicInteger transactionIndex = new AtomicInteger(0);  
  
    //在这里,我们可以根据由MQ回传的key去数据库查询,这条数据到底是成功了还是失败了。  
    public LocalTransactionState checkLocalTransactionState(MessageExt msg) {  
        System.out.println("未决事务,服务器回查客户端msg =" + new String(msg.getBody().toString()));  
        // return LocalTransactionState.ROLLBACK_MESSAGE;  
  
        return LocalTransactionState.COMMIT_MESSAGE;  
  
        // return LocalTransactionState.UNKNOW;  
    }  
}  
复制代码

producer端:发送数据到MQ,并且处理本地事物。这里模拟了一个成功一个失败。Consumer只会接收到本地事物成功的数据。第二个数据失败了,不会被消费。



Consumer只会接收到一个,第二个数据不会被接收到

 

RocketMQ客户端消息确认机制 生产者确认:通过同步/异步回调确保消息发送到 Broker。消费者确认:通过返回状态或手动 ACK 控制消息重试逻辑。可靠性保障:结合重试队列、消费进度持久化和死信队列实现端到端可靠性。源码中的关键逻辑集中在(发送)和(消费),通过状态机管理消息生命周期。 阅读详情

相关推荐

【Python】RocketMQ 生产消费全流程实战指南

本文详细介绍了如何使用Python与RocketMQ实现高效的消息生产与消费,涵盖环境配置、SDK安装、生产者与消费者的实战代码示例,以及云平台集成和生产环境调优技巧。通过电商订单系统的真实案例,展示Python+RocketMQ组合在高并发场景下的卓越表现。

weixin_29088655的博客 932

RocketMQ的消息类型

RocketMQ的消息类型有以下三种: 普通消息 顺序消息 事务消息 下边分别介绍三种类型消息的使用场景,以及使用示例。 一、普通消息 对于普通消息,RocketMQ提供了三种发送方式:可靠同步发送、可靠异步发送和单向发送。 可靠同步发送 同步发送是指消息发送方发出数据后,会在收到接收方发回响应之后才会发送下一个数据包的通讯方式。 此种方式应用场景非常广泛,例如重要通知邮件、报名短信通知、营销短信系统等。 可靠异步发送 异步发送是指发送方发出数据后,不等接收方发回响应,接着发送下一

qq_31155349的博客 5011

使用R语言(ggplot2)绘制KEGG信号通路富集气泡图

本文详细介绍了如何使用R语言的ggplot2包绘制KEGG信号通路富集气泡图,从数据预处理到高级定制技巧,帮助生物信息学研究者直观展示富集分析结果。文章涵盖了气泡图的核心绘制方法、布局优化、常见问题解决方案以及交互式可视化扩展,是进行KEGG富集分析可视化的实用指南。

weixin_42625070的博客 447

RocketMQ基础篇-消息类型

一、看前提问: 1、RocketMQ支持的消息类型有哪些? 2、RocketMQ各消息类型的特点有哪些? 3、RocketMQ各消息类型的优缺点? 4、RocketMQ各消息类型的实现原理? 5、RocketMQ各消息类型的应用场景? 二、消息类型: 1、普通消息: 概念: 普通消息是 RocketMQ 版中⽆特性的消息。 普通消息主要包含了:同步消息、异步消息两种。 同步消息的发送: 原理:同步消息就是生产者(消息发送方)发送一条消息后必须要收到服务端返回响应后才发下一条消息。 ...

Alogger的博客 2192

ArcGIS 10.8软件安装包免费下载及安装教程

ArcGIS 10.8安装教程

qq_44894692的博客 2万+

3分钟接入满血版DeepSeek-R1:字节火山引擎手把手教程!免费50万tokens!

3分钟接入满血版DeepSeek-R1:字节火山引擎手把手教程!免费50万tokens!

hunteritself的博客 2万+

Rocketmq消息类型

Message partOrderMsg = new Message("part_order_topic_test", "order_" + orderId, "KEY_" + orderId, ("局部顺序消息处理_" + orderId + ";Message message = new Message("GLOBAL_ORDER_TOPIC", "", ("全局有序消息" + i).getBytes(RemotingHelper.DEFAULT_CHARSET));

Martin_rui的博客 1292

Silvaco TCAD仿真例库——肖特基二极管反向击穿特性(案例)

本文详细解析了使用Silvaco TCAD仿真例库进行肖特基二极管反向击穿特性分析的完整案例。通过构建包含浮空P型保护环的平面结构,重点阐述了针对雪崩击穿仿真的关键步骤,包括高精度网格划分、碰撞电离模型(impact selb)的启用以及反向电压的精细扫描策略。文章深入探讨了漂移区掺杂浓度、保护环设计等参数对击穿电压的影响,并提供了实用的调试心得,旨在帮助工程师精准预测和优化功率肖特基二极管的反向耐压能力。

fire9的博客 724

rocketmq 消费方式_RocketMQ事务消费和顺序消费详解

一、RocketMq有3中消息类型1.普通消费2. 顺序消费3.事务消费顺序消费场景在网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一、创建订单 ,第二:订单付款,第三:订单完成。也就是这个三个环节要有顺序,这个订单才有意义。RocketMQ可以保证顺序消费。rocketMq实现顺序消费的原理produce在发送消息的时候,把消息发到同一个队列(queue)中,消费者注册消息监听器为Me...

weixin_39747087的博客 300

Aurora 8B/10B IP核介绍 —— Xilinx 技术文档Pg046

一、概述该IP核与用户接口之间支持AXI4-Stream协议,且基于ZYNQ SOC、A7、K7、V7以及UltraScale、UltraScale+系列FPGA的高速收发器(the high-speed serial transceivers)进行实现。多达 16 个连续绑定4 个连续绑定的 GTP 收发器framingstreaming以及全双工或单工加扰消扰CRC校验热插拔Aurora 8B/10B核心(图1-1)是一种可扩展的、轻量级的、用于高速串行通信的链路层协议;

qq_32147275的博客 1639

“反激电源”是AC/DC隔离电源还是DC/DC隔离电源?

所以在一些场景,我们的输入电压是一个DC,但是我们仍然需要隔离电源的需求,我们就会只采用图9.2的右半部分。在这个场景下“反激电源”是DC/DC。所以此AC/DC的本质是将AC变成高压的DC之后,用带隔离变压器的反激式开关电源产生一个隔离的、稳定的直流电压。一般在讲述各种电源的拓扑的文章和书籍都是在讲述AC/DC的电源拓扑分类的时候,讲述Flyback。反激式开关电源是指使用反激高频变压器隔离输入输出回路的开关电源,反激式隔离变压器开关电源重点表述的是开关电源包含变压器的拓扑结构。

qq_43416206的博客 1294

RocketMQ(二)事务消费和顺序消费详解

一、RocketMq有3中消息类型 1.普通消费 2. 顺序消费 3.事务消费 顺序消费场景 在网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一、创建订单 ,第二:订单付款,第三:订单完成。也就是这个三个环节要有顺序,这个订单才有意义。RocketMQ可以保证顺序消费。 rocketMq实现顺序消费的原理 produce在发送消息的时候,把消息发到同一个队列(queue)中,...

asdf57847225的博客 485

SDMA 如何实现设备对主机内存的访问

摘要:SDMA(System Direct Memory Access)是PCIe设备直接访问主机内存的核心机制,使外设无需CPU干预即可高效读写系统RAM。其实现原理包括:设备作为主控发起DMA请求,通过PCIe Memory TLP访问主机物理地址;完整通路涉及驱动分配DMA安全内存、设备配置、地址转换(IOMMU)和中断通知等步骤;关键技术涵盖地址类型(HPA/IOVA)、TLP格式、缓存一致性和安全防护机制。SDMA广泛应用于GPU、网卡、NVMe等高性能场景,是现代异构计算的基础技术,需软硬件协同

MHD0815的博客 240

RocketMQ事务消费和顺序消费详解,小票

http://www.cnblogs.com/520playboy/p/6750023.html 一、RocketMq有3中消息类型 1.普通消费 2. 顺序消费 3.事务消费 顺序消费场景 在网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一、创建订单 ,第二:订单付款,第三:订单完成。也就是这个三个环节要有顺序,这个订单才有意义。RocketMQ

linuxheik的专栏 860

【DaVinci Developer工具实战】03 -导入xml文件

本文档描述了 DaVinci Developer 导入数据的的具体功能 ,它适用于导入AUTOSAR SWC 模板【AUTOSAR XML 文件】 文件。

1991

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

【转】RocketMQ事务消费和顺序消费详解

RocketMQ事务消费和顺序消费详解 转载说明:该文章纯转载,若有侵权或给原作者造成不便望告知,仅供学习参考。 一、RocketMq有3中消息类型 1.普通消费 2. 顺序消费 3.事务消费 顺序消费场景 在网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一、创建订单 ,第二:订单付款,第三:订单完成。也就是这个三个环节要有顺序,这个订单才有意义。RocketMQ可...

weixin_30300225的博客 98

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

二、RocketMQ 实战:顺序消息、事务消息与 SpringBoot 集成

本文介绍了Apache RocketMQ在分布式系统中的关键应用,重点探讨顺序消息和事务消息的实现原理与SpringBoot集成方案。顺序消息通过队列分区和消费队列绑定确保业务场景(如电商订单流程)的有序消费;事务消息采用半消息+事务回查机制解决分布式事务一致性问题。文章提供了完整的代码示例,包括顺序消息的路由发送、事务消息的本地事务处理,以及SpringBoot中的配置与实现。同时总结了实践中的常见问题(如顺序消息并发瓶颈、事务回查频繁等)及优化建议,并附可直接运行的Demo工程结构,帮助开发者快速落地生

NeoLshu的博客 1029

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

初始RocketMQ

RocketMQ基本概念

qq_45888932的博客 2046

RocketMQ学习

RocketMQ学习之路

生命不息,编程不止的博客 2685

RocketMQ--消息类型

《RocketMQ实战与原理解析》学习笔记 https://help.aliyun.com/document_detail/29533.html 名词解释 topic && tag Topic : 消息主题,一级消息类型,通过 Topic 对消息进行分类。 Tag : 消息标签,二级消息类型,用来进一步区分某个 Topic 下的消息分类。 RMQ 允许消费者按照 Tag对消息...

dark 3007

消息队列实战:破解重复消费、顺序消费与分布式事务三大难题

消息队列作为分布式系统架构中的核心组件,其核心价值在于实现系统间的解耦、异步通信和流量削峰。其工作原理基于生产者-消费者模型,通过Broker进行消息的存储与转发。在工程实践中,消息队列的技术价值在于提升系统吞吐量和可扩展性,广泛应用于电商交易、日志处理、数据同步等场景。然而,分布式环境下的网络分区和节点故障等固有挑战,引出了消息可靠投递的经典问题,其中重复消费和顺序消费尤为突出。重复消费源于消息队列为保障“至少一次”投递语义而设计的确认与重试机制,需通过幂等性设计来应对。顺序消费的难点则在于并行消费与分区

weixin_29230901的博客 306

RocketMQ - 基于实现削峰填谷:秒杀场景下的消息处理方案

RocketMQ在秒杀场景下的应用 摘要 本文介绍了如何利用RocketMQ实现秒杀场景中的削峰填谷策略。秒杀活动具有瞬时高并发、资源争抢激烈等特点,直接请求数据库会导致系统崩溃。通过RocketMQ的消息缓冲能力,可以将请求异步化处理,实现流量削峰。 RocketMQ相比其他消息中间件具有毫秒级延迟、事务消息支持等优势,特别适合秒杀场景。系统架构包含前置限流、缓存预热、事务消息处理等关键模块。其中使用Redis预减库存和RocketMQ事务消息机制,可有效防止超卖问题。 核心实现包括: 使用Sentine

千淘万漉虽辛苦,吹尽狂沙始到金 2万+
上一篇: RocketMQ最佳实践
下一篇: rocketmq CONSUME_FROM_LAST_OFFSET CONSUME_FROM_LAST_OFFSET
琅琊山二当家
博客等级 码龄18年 447粉丝 322原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值