|
该版本仍在开发中,尚未被视为稳定。对于最新稳定版本,请使用 spring-cloud-stream 5.0.0! |
春季数据集成历程简史
Spring 在数据集成领域的旅程始于 Spring Integration。通过其编程模型,它为开发者提供了一致的体验,帮助构建能够拥抱企业集成模式以连接外部系统(如数据库、消息代理等)的应用程序。
快进到云时代,微服务在企业环境中变得突出。Spring Boot 彻底改变了开发者构建应用程序的方式。借助 Spring 的编程模型和 Spring Boot 处理的运行时责任,开发独立的基于 Spring 的生产级微服务变得无缝。
为了将此扩展到数据集成工作负载,Spring Integration 和 Spring Boot 被合并成一个新项目。春云溪由此诞生。
通过 Spring Cloud Stream,开发者可以:
-
独立构建、测试和部署以数据为中心的应用。
-
应用现代微服务架构模式,包括通过消息进行组合。
-
将应用责任与事件中心思维分开。事件可以代表时间中发生的某件事,下游消费者应用可以对此做出反应,而不知道事件的起源或生产者的身份。
-
将业务逻辑移植到消息代理(如RabbitMQ、Apache Kafka、Amazon Kinesis)。
-
依赖框架对常见用例的自动内容类型支持。扩展到不同类型的数据转换是可能的。
-
还有更多......
快速入门
你可以在不到5分钟内体验春云溪流,甚至在进入细节之前,就可以通过这份三步指南完成。
我们会向你展示如何创建一个 Spring Cloud Stream 应用,它能接收来自你选择的消息中间件的消息(后面会详细说明),并将收到的消息记录到控制台。
我们称之为LoggingConsumer.
虽然不太实用,但它很好地介绍了一些主要概念
以及抽象内容,使用户指南的其余部分更容易消化。
三个步骤如下:
使用 Spring Initializr 创建示例应用
要开始,请访问Spring Initializr。从那里,你可以生成我们的LoggingConsumer应用。具体做法:
-
在依赖部分,开始输入
流. 当“云流”选项出现时,选择它。 -
开始输入“kafka”或“rabbit”。
-
选择“Kafka”或“RabbitMQ”。
基本上,你选择应用绑定的消息中间件。 我们建议使用你已经安装好的那台,或者你对安装和运行更熟悉的那台。 此外,正如你从Starters界面看到的,还有其他几个选项可供选择。 比如,你可以选择Gradle作为构建工具,而不是默认的Maven。
-
在工件字段中输入“logging-consumer”。
工件字段的值即为应用程序名称。 如果你选择了RabbitMQ作为中间件,你的Spring初始化器现在应该如下:
-
点击“生成项目”按钮。
这样做后,生成项目的压缩版本会下载到你的硬盘。
-
把文件解压到你想用作项目目录的文件夹里。
| 我们鼓励你探索春季初始化器中提供的多种可能性。 它允许你创建多种不同类型的 Spring 应用程序。 |
将项目导入你的IDE
现在你可以把项目导入到你的IDE里了。 请记住,根据不同IDE,你可能需要遵循特定的导入流程。 例如,根据项目的生成方式(Maven 或 Gradle),你可能需要遵循特定的导入流程(例如在 Eclipse 或 STS 中,你需要使用 File → 导入 → Maven → 现有 Maven 项目)。
导入后,项目必须没有任何错误。也src/main/java应包含com.example.loggingconsumer.LoggingConsumerApplication.
从技术上讲,此时你可以运行应用的主类。 它已经是一个有效的 Spring Boot 应用。 不过它什么都没做,所以我们想添加一些代码。
添加消息处理程序、构建和运行
修改com.example.loggingconsumer.LoggingConsumerApplication类别的表现如下:
@SpringBootApplication
public class LoggingConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(LoggingConsumerApplication.class, args);
}
@Bean
public Consumer<Person> log() {
return person -> {
System.out.println("Received: " + person);
};
}
public static class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
}
}
正如你从前面的列表中看到的:
-
我们使用函数式编程模型(参见[Spring Cloud Function支持])来定义单一消息处理程序为
消费者. -
我们依赖框架惯例将该处理程序绑定到绑定器暴露的输入目的地绑定。
这样做还能让你看到框架的一个核心功能:它尝试自动将收到的消息有效载荷转换为类型人.
你现在拥有了一个功能齐全的 Spring Cloud Stream 应用,能够监听消息。
为了简化起见,我们假设你在第一步就选择了RabbitMQ。
假设你已经安装并运行了 RabbitMQ,可以通过运行它的程序启动应用程序主要方法在你的IDE中。
你应该会看到以下输出:
--- [ main] c.s.b.r.p.RabbitExchangeQueueProvisioner : declaring queue for inbound: input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg, bound to: input
--- [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [localhost:5672]
--- [ main] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#2a3a299:0/SimpleConnection@66c83fc8. . .
. . .
--- [ main] o.s.i.a.i.AmqpInboundChannelAdapter : started inbound.input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg
. . .
--- [ main] c.e.l.LoggingConsumerApplication : Started LoggingConsumerApplication in 2.531 seconds (JVM running for 2.897)
进入RabbitMQ管理控制台或其他任何RabbitMQ客户端,发送消息到input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg.
这匿名。CbMIwdkJSBO1ZoPDOtHtCg部分代表组名,并且是生成的,所以在你的环境中它必然不同。
为了更可预测的,你可以通过设置明确的组名Spring.cloud.stream.bindings.input.group=hello(或者你喜欢的任何名字)。
消息内容应为人类别如下:
{"name":"Sam Spade"}
然后,在你的控制台里,你应该会看到:
收到:山姆·斯佩德
你也可以将应用构建并打包到启动jar中(通过使用./mvnw 干净安装并通过使用Java -jar命令。
现在你就有一个能用(虽然非常基础的)Spring Cloud Stream 应用。
Spring表达式语言(SpEL)在流数据背景下的应用
在这本参考手册中,你将遇到许多可以使用 Spring 表达语言(SpEL)的功能和示例。在使用时,了解某些限制非常重要。
SpEL不仅能访问当前消息,还能访问你正在运行的应用上下文。
然而,理解SpEL能看到什么类型的数据,尤其是在收到消息的上下文中,这一点非常重要。
从中介处发送的消息以字节[]的形式到达。然后它被转换为消息<字节[]>而你可以看到消息的有效载荷保持其原始形式。消息的头部为<字符串,对象>,其中值通常是另一个原语或一个元语的集合/数组,因此称为对象。
这是因为 Binder 不知道所需的输入类型,因为它无法访问用户代码(函数)。因此,装订机实际上送达了一个信封,里面有有效载荷和一些可读的元数据,以消息头的形式出现,就像邮件送达的信件一样。
这意味着虽然可以访问消息的有效载荷,但你只能以原始数据(即字节[])访问。虽然开发者通常会要求能够以具体类型(如Foo、Bar等)访问载荷对象的字段,但你也能理解实现这一点有多难甚至不可能。
这里有一个例子来说明问题;想象你有一个路由表达式,根据有效载荷类型将路由到不同的函数。这一要求意味着将有效载荷从字节[]转换为特定类型,然后应用SpEL。然而,要进行这种转换,我们需要知道实际要传递给转换器的类型,而这来自函数的签名,而我们不知道是哪一种。解决这一要求的更好方法是将类型信息传递为消息头(例如,application/json;type=foo.bar.Baz).你会得到一个清晰可读的字符串值,一年内就能访问和评估,还有易于阅读的SpEL表达式。
此外,使用有效载荷进行路由决策被认为是非常糟糕的做法,因为有效载荷被视为特权数据——仅供最终接收者读取的数据。同样,用邮件投递的比喻来说,你不希望邮递员打开你的信封并阅读信件内容来做出投递决定。同样的概念在这里也适用,尤其是在生成消息时包含此类信息相对容易时。它对网络传输数据的设计以及哪些数据部分被视为公共、哪些被优先权,要求一定程度的纪律。