核心概念
应用模型
应用通过Binder与消息队列绑定在一起,Binder由SpringCloud实现或第三方实现,例如:Spring Cloud Alibaba RocketMQ Binder, 你的代码通过inputs和outputs(它们都称为bindings,下文做详细介绍)与Binder相连接。

destination binder
与外部消息系统通信的组件,为构造 Binding提供了 2 个方法,分别是 bindConsumer 和 bindProducer ,它们分别用于构造生产者和消费者。Binder使Spring Cloud Stream应用程序可以灵活地连接到中间件,目前spring为kafka、rabbitmq提供binder。
destination binding
Binding 是连接应用程序跟消息中间件的桥梁,用于消息的消费和生产,由binder创建。
Consumer Groups
组内只有1个实例消费。如果不设置group,则stream会自动为每个实例创建匿名且独立的group——于是每个实例都会消费。
组内单次只有1个实例消费,并且会轮询负载均衡。通常,在将应用程序绑定到给定目标时,最好始终指定consumer group。
使用spring.cloud.stream.bindings.<bindingName>.group属性来指定它所属的组名称。例如下图所示的属性配置为:spring.cloud.stream.bindings.<bindingName>.group=hdfsWrite和spring.cloud.stream.bindings.<bindingName>.group=average

Partitioning Support
所有订阅同一源的消费者组都会收到同一份数据,但每个组中只有一个消费者可以接受到消息。也就是说,各个组之间不存在竞争关系,但同一个组中的消费者存在竞争关系。
一个或多个生产者将数据发送到多个消费者,并确保有共同特征标识的数据由同一个消费者处理。默认是对消息进行hashCode,然后根据分区个数取余,所以对于相同的消息,总会落到同一个消费者上。
编程模型

Binders
Bindings
Bindings用来将你的代码与消息队列连接,下面例子中展示了如何接受消息并转换成大写发送消息:
@SpringBootApplication
public class SampleApplication {
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
@Bean
public Function<String, String> uppercase() {
return value -> {
System.out.println("Received: " + value);
return value.toUpperCase()
};
}
}
在Spring容器中,Function (支持input和output)、Supplier(支持output)、Consumer(支持input)类型的bean都会作为消息处理器,并且为它们提过了一个约定式的binding names:
input: <functionName>-in-<index>
output: <functionName>-out-<index>
index的下标从0开始
通过 spring.cloud.stream.bindings.<bindingName>.destination=myTopic 来将它们与topic绑定。
上面的代码的配置如下:
spring.cloud.stream.bindings.uppercase-in-0.destination=my-topic
消费者拉模式名称
下面例子显示如何绑定一个拉模式的消费者,它的名称为bindingName为orders
public interface PolledBarista {
@Input
PollableMessageSource orders();
. . .
}
生产消费消息
函数式生产者
@SpringBootApplication
public static class SupplierConfiguration {
@Bean
public Supplier<String> stringSupplier() {
return () -> "Hello from Supplier";
}
}
上面生产者返回一个字符串,它的bindingName为stringSupplier-out-0, 但问题是,这个方法谁调用它,多久调用一次?框架提供了一个默认的轮询机制,默认每秒触发一次,如何自定义轮询参考:Polling Configuration Properties
函数式消费者(推模式)
函数式消费者的bean类型为 Consumer 或者 Function, 它们都是推模式
拉模式消费者
也称为轮询式消费者,按需轮询PollableMessageSource ,使用spring.cloud.stream.pollable-source属性配置轮询消费者,例如:
spring.cloud.stream.pollable-source=myDestination
一个轮询消费者的例子:
@Bean
public ApplicationRunner poller(PollableMessageSource destIn, MessageChannel destOut) {
return args -> {
while (someCondition()) {
try {
if (!destIn.poll(m -> {
String newPayload = ((String) m.getPayload()).toUpperCase();
// 将消息发送到destOut
destOut.send(new GenericMessage<>(newPayload));
})) {
// 未拉取到消息,等待1s再拉取
Thread.sleep(1000);
}
}
catch (Exception e) {
// handle failure
}
}
};
}
一个scheduled任务bean例子:
@Scheduled(fixedDelay = 5_000)
public void poll() {
System.out.println("Polling...");
this.source.poll(m -> {
System.out.println(m.getPayload());
}, new ParameterizedTypeReference<Foo>() { });
}
PollableMessageSource.poll() 该方法结接收一个MessageHandler参数,通常是一个lambda表达式,如果接收到消息该方法返回true。
和推模式的消费者一样,如果MessageHandler抛出异常,消息会进入error channels,详细参考错误处理。
poll还有一个重载方法,如下定义:
poll(MessageHandler handler, ParameterizedTypeReference<?> type)
type允许message payload被转换为指定类型。
错误处理
当MessageHandler抛出异常,ErrorMessage就会被发送到error channels, 也就是<destination>.<group>.errors,该错误也与全局异常errorChannel桥接
你可以通过@ServiceActivator订阅error channel 去处理异常,如果没有任何订阅,该错误将会简单的打印,并认为消息成功确认。如果@ServiceActivator的方法抛出异常,该消息将会被拒绝,而不会重投。如果@ServiceActivator的方法抛出RequeueCurrentMessageException,该消息将会在broker中重新排队。如果在MessageHandler中直接抛出RequeueCurrentMessageException,该消息就像上面所说的一样,重新排队,不会进入到error channels。
参考
https://docs.spring.io/spring-cloud-stream/docs/3.1.2/reference/html/spring-cloud-stream.html
http://www.imooc.com/article/290489
https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/zh-cn/index.html#_spring_cloud_alibaba_rocketmq_binder
本文介绍了如何使用Spring Cloud Stream将应用程序与各种消息中间件(如Kafka、RabbitMQ)集成,包括Binder的概念、不同类型的Bindings、Consumer Groups、PartitioningSupport,以及生产消费模型的详细配置和错误处理机制。
1067

被折叠的 条评论
为什么被折叠?



