背景:
假如我们有三个任务,任务1ABC,任务2DQ,任务3NQR,ABC这些字母都代表一个业务消息都要按照自己的内部的顺序消费。
1 全局有序
这个时候我们可以往一个队列里面写入数据,也只选择一个消费者进行消费,那么这个时候肯定是由序的。哪怕我们有ABDC 中间穿插了D但是最终还是一致的。可以解决上面我们这个场景,但是这样的解决方案不是最好的,降低了消费效率和吞吐量。
2 局部有序
通过上面的思考我知道其实保证每个任务内部有序就行了,那么如果我们每个任务的消息都路由到同一个队列岂不是就可以了吗?如下面这张图,那么是不是大大的提高了消费的能力呢?
3 代码
启动类,这里开启了两个通道一个通道验证全局有序性,一个通道验证局部有序性。
@SpringBootApplication
@EnableBinding({CustomSink.class})
public class RocketOrderlyApplication {
public static void main(String[] args) {
SpringApplication.run(RocketOrderlyApplication.class, args);
}
@StreamListener("input")
public void receiveInput(String receiveMsg) {
System.out.println("input receive: " + receiveMsg);
}
@StreamListener("input2")
public void receiveInputSecond(String receiveMsg) {
System.out.println("input2 receive: " + receiveMsg);
}
}
配置
server:
port: 9520
spring:
application:
name: rocket-orderly
cloud:
stream:
bindings:
input:
content-type: application/json
destination: GLOBAL_ORDER_TOPIC
group: test-group-order
input2:
content-type: application/json
destination: PART_ORDER_TOPIC
group: test-order
rocketmq:
bindings:
input:
consumer:
orderly : true #配置是否有序
binder:
name-server: ip:9876
group: rocket-demo
下面来看全局有序性的生产者
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("producer_group");
producer.setNamesrvAddr("ip:9876");
producer.start();
List<Order> F = OrderBuilder.build(1, "A", "B", "C");
List<Order> S = OrderBuilder.build(2, "D", "Q");
List<Order> T = OrderBuilder.build(3, "N", "Q", "R");
ArrayList<Order> orders = new ArrayList<Order>() {{
addAll(F);
addAll(S);
addAll(T);
}};
for (Order order : orders) {
Message msg = new Message("GLOBAL_ORDER_TOPIC", "GLOBAL_ORDER_TOPIC_STR", order.toString().getBytes());
msg.setKeys("GLOBAL_ORDER_TOPIC_TRACE");
producer.send(msg);
}
}
public class Producer2 {
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("producer_group");
producer.setNamesrvAddr("ip:9876");
producer.start();
List<Order> F = OrderBuilder.build(1, "A", "B", "C");
List<Order> S = OrderBuilder.build(2, "D", "Q");
List<Order> T = OrderBuilder.build(3, "N", "Q", "R");
ArrayList<Order> orders = new ArrayList<Order>() {{
addAll(F);
addAll(S);
addAll(T);
}};
for (Order order : orders) {
Message msg = new Message("PART_ORDER_TOPIC", "PART_ORDER_TOPIC_STR", order.toString().getBytes());
msg.setKeys("PART_ORDER_TOPIC_TRACE");
producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
// 重要的逻辑在这里,这里通过取余的方式将同一个任务的ID路由到同一个队列
int size = mqs.size();
int idx = (int) arg;
return mqs.get(idx % size);
}
}, order.getOrderID());
}
}
}