写在前面
本节结合Spring cloud stream文档以及个人的理解,详细分析关于绑定器
SPI
背后的主要概念、主要组件和特定于实现细节的信息。
学习微服务最好使用容器来搭建,对于正在学习编程的小伙伴推荐买上属于自己的一台服务器,用来练练手,也顺带学习 Docker,这很重要。最近,阿里在搞活动,新用户 1C2G 只要 98 一年,我也比较了很多,还是比较划算的,我自己也入手了,可以点进来看看,对了,最便宜的一款在 【全部必抢爆款】 里面: 阿里云服务器,助力云上云!
Spring Cloud Stream提供了一个绑定器抽象,用于连接外部中间件上的物理目的地。
1. 生产者、消费者
下图展示了一般的生产者和消费者关系:
一个生产者是能够发送消息到通道的任何组件。通过 Binder
实现,通道能够绑定到外部的消息中间件。Binder
接口能够在 Spring Cloud Stream
包中找到:
public interface Binder<T, C extends ConsumerProperties, P extends ProducerProperties> {
Binding<T> bindConsumer(String name, String group, T inboundBindTarget,
C consumerProperties);
Binding<T> bindProducer(String name, T outboundBindTarget, P producerProperties);
}
当调用bindProducer()
方法时,第一个参数是代理中的目的地的名称,第二个参数是生产者向其发送消息的本地通道实例,第三个参数包含要在为该通道创建的适配器中使用的属性(例如分区键表达式)。
消费者是从通道接收消息的任何组件。与生产者一样,消费者的通道可以绑定到外部消息代理。在调用bindConsumer()
方法时,第一个参数是目标名称,第二个参数提供逻辑使用者组的名称。由给定目的地的使用者绑定表示的每个组接收生产者发送到该目的地的每个消息的副本(也就是说,它遵循正常的发布-订阅语义)。如果有多个消费者实例绑定了相同的组名,则这些消费者实例对消息进行负载平衡,以便每个组中只有一个消费者实例使用生产者发送的每个消息(也就是说,它遵循正常的队列语义)。
2. 绑定器SPI
绑定器SPI
由许多接口、开箱即用的实用程序类和发现策略组成,这些发现策略为连接外部中间件提供了可插拔的机制。
SPI
的关键点是绑定器接口,它是将输入和输出连接到外部中间件的策略。
接口是支持泛型的,提供了一些扩展点:
-
输入和输出绑定目标。从1.0版开始,只支持
MessageChannel
,但将来打算将其用作扩展点。 -
扩展的使用者和生产者属性,允许特定的绑定器实现添加可以类型安全方式支持的补充属性。
典型的绑定器实现包括以下内容:
-
实现绑定器接口的类;
-
一个Spring
@Configuration
类,它创建绑定器类型的bean以及中间件连接基础设施。 -
一个
META-INF/spring.binders
。在类路径中找到一个或多个绑定器定义的绑定器文件,如下例所示:kafka:\ org.springframework.cloud.stream.binder.kafka.config.KafkaBinderConfiguration
Binder 的具体注册可以参考 BinderFactoryConfiguration
类的 binderTypeRegistry()方法
:
@Bean
public BinderTypeRegistry binderTypeRegistry(
ConfigurableApplicationContext configurableApplicationContext) {
Map<String, BinderType> binderTypes = new HashMap<>();
ClassLoader classLoader = configurableApplicationContext.getClassLoader();
// the above can never be null since it will default to
// ClassUtils.getDefaultClassLoader(..)
try {
Enumeration<URL> resources = classLoader
.getResources("META-INF/spring.binders");
if (!Boolean.valueOf(this.selfContained)
&& (resources == null || !resources.hasMoreElements())) {
this.logger.debug(
"Failed to locate 'META-INF/spring.binders' resources on the classpath."
+ " Assuming standard boot 'META-INF/spring.factories' configuration is used");
}
else {
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
UrlResource resource = new UrlResource(url);
for (BinderType binderType : parseBinderConfigurations(classLoader,
resource)) {
binderTypes.put(binderType.getDefaultName(), binderType);
}
}
}
}
catch (IOException | ClassNotFoundException e) {
throw new BeanCreationException("Cannot create binder factory:", e);
}
return new DefaultBinderTypeRegistry(binderTypes);
}
3. 绑定器检测
Spring Cloud Stream
依赖于绑定器SPI
的实现来执行将通道连接到消息代理的任务。每个绑定器实现通常连接到一种类型的消息传递系统。
默认情况下,Spring Cloud Stream
依赖于Spring Boot
的自动配置来配置绑定过程。如果在类路径中找到一个绑定器实现,Spring Cloud Stream
将自动使用它。例如,一个只绑定到RabbitMQ
的 Spring cloud stream
可以添加以下依赖项:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
参考前面描述的绑定器实现内容,我们能够在 Spring cloud stream binder rabbit
的 META-INF
中找到spring.binders
文件:
spring.binders
rabbit:\
org.springframework.cloud.stream.binder.rabbit.config.RabbitServiceAutoConfiguration
键表示绑定器实现的一个标识名,而值是一个逗号分隔的配置类列表,每个配置类都包含且只有一个org.springframe .cloud.stream.binder.Binder
类型的bean定义。
有关SPI
的服务发现机制可以去百度下,个人认为,Spring 在这里的实现还是和Java
原生有区别的。
4. 多绑定器
当类路径上存在多个绑定时,应用程序必须指出每个通道绑定将使用哪个绑定。
绑定器的选择可以全局执行,使用spring.cloud.stream.defaultBinder
属性(例如,spring.cloud.stream.defaultBinder=rabbit
),也可以单独执行,方法是在每个通道绑定上配置绑定器。例如,从Kafka读取和从RabbitMQ写入的处理器应用程序(它有分别为读写指定输入和输出的通道)可以指定以下配置:
spring.cloud.stream.bindings.input.binder=kafka
spring.cloud.stream.bindings.output.binder=rabbit
5. 连接到同类型绑定器的多个实例
默认情况下,绑定器共享应用程序的Spring引导自动配置,这样就创建了类路径上找到的每个绑定器的一个实例。如果应用程序应该连接到同一类型的多个代理,则可以指定多个绑定器配置,每个绑定器配置具有不同的环境设置。
打开显式绑定器配置将完全禁用默认绑定器配置过程。如果这样做,则必须在配置中包含所使用的所有绑定器。希望透明地使用
Spring Cloud Stream
的框架可以创建可以通过名称引用的绑定器配置,但是它们不会影响默认的绑定器配置。为此,绑定器配置可以将其defaultCandidate
标记设置为false(例如,spring.cloud.stream.binder.<configurationName>.defaultCandidate=false
)。这表示独立于缺省绑定器配置过程而存在的配置。
以下示例显示连接到两个RabbitMQ
代理实例的处理器应用程序的典型配置:
spring:
cloud:
stream:
bindings:
input:
destination: thing1
binder: rabbit1
output:
destination: thing2
binder: rabbit2
binders:
rabbit1:
type: rabbit
environment:
spring:
rabbitmq:
host: <host1>
rabbit2:
type: rabbit
environment:
spring:
rabbitmq:
host: <host2>
特定绑定的Environment
属性也可以用于任何Spring Boot
属性,包括这个Spring .main.sources
可用于为特定绑定添加额外配置的源,例如覆盖自动配置的bean。
environment:
spring:
main:
sources: com.acme.config.MyCustomBinderConfiguration
要激活特定绑定环境的特定配置文件,使用spring.profiles.active
属性:
environment:
spring:
profiles:
active: myBinderProfile
6. 绑定可视化和控制
2.0 版本以来,Spring Cloud Stream
支持 Binding
通过 actuator
端点可视化和控制(我们需要添加Spring Boot
的 web
和 actuator
的依赖)。
端点实现可以在
Spring Cloud Stream
的endpoint
包下找到。该包下还有一个channels
端点,也可以暴露该端点获取信息。
通过以下命令启用端点:management.endpoints.web.exposure.include=bindings
。
查看当前绑定,访问http://<host>:<port>/actuator/bindings
。
另外,要查看单个绑定,访问 http://<host>:<port>/actuator/bindings/<bindingName>
;
还可以停止、启动、暂停和恢复单个绑定,方法是在提供JSON
状态参数的同时将其提交到相同的URL,如下面的示例所示:
curl -d '{"state":"STOPPED"}' -H "Content-Type: application/json" -X POST http://<host>:<port>/actuator/bindings/myBindingName
curl -d '{"state":"STARTED"}' -H "Content-Type: application/json" -X POST http://<host>:<port>/actuator/bindings/myBindingName
curl -d '{"state":"PAUSED"}' -H "Content-Type: application/json" -X POST http://<host>:<port>/actuator/bindings/myBindingName
curl -d '{"state":"RESUMED"}' -H "Content-Type: application/json" -X POST http://<host>:<port>/actuator/bindings/myBindingName
只有当相应的绑定器及其底层技术支持它时,才会
PAUSED
并RESUMED
工作。否则,您将在日志中看到警告消息。目前,只有Kafka binder支持暂停和恢复状态。
如果你觉得我的文章对你有所帮助,欢迎关注我的公众号。赞!
认认真真学习,做思想的产出者,而不是文字的搬运工。错误之处,还望指出!