Spring Cloud


目录

1.特点
一.云原生应用
2. Spring Cloud上下文:应用程序上下文服务
2.1.Bootstrap应用程序上下文
2.2.应用程序上下文层次结构
2.3.更改引导程序Properties的位置
2.4.覆盖远程Properties的值
2.5.自定义引导程序配置
2.6.自定义Bootstrap Property源
2.7.记录配置
2.8.环境变化
2.9.刷新范围
2.10.加密与解密
2.11.终点
3. Spring Cloud Commons:通用抽象
3.1.@EnableDiscoveryClient
3.1.1.健康指标
3.1.2.订购DiscoveryClient实例
3.2.服务注册
3.2.1.ServiceRegistry自动注册
ServiceRegistry自动注册Events
3.2.2.服务注册表执行器端点
3.3.Spring RestTemplate作为负载均衡器客户端
3.4.Spring WebClient作为负载均衡器客户端
3.4.1.重试失败的请求
3.5.多个RestTemplate对象
3.6.多个WebClient对象
3.7.Spring WebFlux WebClient作为负载均衡器客户端
3.7.1.Spring具有响应式负载均衡器的WebFlux WebClient
3.7.2.Spring WebFlux WebClient,带有非反应式负载均衡器客户端
3.7.3.传递自己的负载均衡客户端配置
3.8.忽略网络接口
3.9.HTTP客户端工厂
3.10.启用的功能
3.10.1.功能类型
3.10.2.声明功能
3.11.Spring Cloud兼容性验证
二.Spring Cloud Config
4.快速入门
4.1.客户端使用
5. Spring Cloud Config服务器
5.1.环境Repository
5.1.1.Git后端
跳过SSL证书验证
设置HTTP连接超时
Git URI中的占位符
模式匹配和多个Repositories
认证方式
使用AWS CodeCommit进行身份验证
使用属性进行Git SSH配置
Git搜索路径中的占位符
强制拉动Git Repositories
删除Git Repositories中未跟踪的分支
Git刷新率
5.1.2.版本控制后端文件系统使用
5.1.3.文件系统后端
5.1.4.Vault后端
多个Properties来源
5.1.5.通过代理访问后端
5.1.6.与所有应用程序共享配置
基于文件的Repositories
Vault服务器
CredHub服务器
5.1.7.JDBC后端
5.1.8.CredHub后端
OAuth 2.0
5.1.9.复合环境Repositories
定制复合环境Repositories
5.1.10.Property覆盖
5.2.健康指标
5.3.安全
5.4.加密与解密
5.5.密钥管理
5.6.创建密钥库进行测试
5.7.使用多个按键和按键旋转
5.8.提供加密的Properties
6.提供其他格式
7.提供纯文本
8.嵌入配置服务器
9.推送通知和Spring Cloud Bus
10. Spring Cloud Config客户
10.1.配置第一个引导程序
10.2.发现第一引导
10.3.快速配置客户端失败
10.4.配置客户端重试
10.5.查找远程配置资源
10.6.为配置服务器指定多个URL
10.7.配置超时
10.8.安全
10.8.1.健康指标
10.8.2.提供自定义的RestTemplate
10.8.3.Vault
10.9.Vault中的嵌套键
三.Spring Cloud Netflix
11.服务发现:Eureka个客户端
11.1.如何包括Eureka客户
11.2.向Eureka注册
11.3.通过Eureka服务器进行身份验证
11.4.状态页和健康指示器
11.5.注册安全的应用程序
11.6.Eureka的健康检查
11.7.Eureka实例和客户端的元数据
11.7.1.在Cloud Foundry上使用Eureka
11.7.2.在AWS上使用Eureka
11.7.3.更改Eureka实例ID
11.8.使用EurekaClient
11.8.1.没有Jersey的EurekaClient
11.9.本地Netflix EurekaClient的替代产品
11.10.为什么注册服务这么慢?
11.11.区域
11.12.刷新Eureka个客户端
12.服务发现:Eureka服务器
12.1.如何包括Eureka服务器
12.2.如何运行Eureka服务器
12.3.高可用性,区域和区域
12.4.独立模式
12.5.同行意识
12.6.何时首选IP地址
12.7.保护Eureka服务器
12.8.JDK 11支持
13.断路器:Hystrix个客户
13.1.如何包含Hystrix
13.2.传播安全上下文或使用Spring范围
13.3.健康指标
13.4.Hystrix指标流
14.断路器:Hystrix仪表板
15. Hystrix超时和Ribbon客户
15.1.如何包括Hystrix信息中心
15.2.Turbine
15.2.1.集群端点
15.3.Turbine Stream
16.客户端负载平衡器:Ribbon
16.1.如何包含Ribbon
16.2.自定义Ribbon客户端
16.3.为所有Ribbon客户自定义默认值
16.4.通过设置Properties自定义Ribbon客户端
16.5.将Ribbon与Eureka一起使用
16.6.示例:如何在没有Eureka的情况下使用Ribbon
16.7.示例:在Ribbon中禁用Eureka
16.8.直接使用Ribbon API
16.9.缓存Ribbon配置
16.10.如何配置Hystrix线程池
16.11.如何提供Ribbon的IRule的密钥
17.外部配置:Archaius
18.路由器和过滤器:Zuul
18.1.如何包含Zuul
18.2.嵌入式Zuul反向代理
18.3.Zuul Http客户端
18.4.Cookie和敏感标题
18.5.忽略标题
18.6.管理端点
18.6.1.路线终点
18.6.2.过滤端点
18.7.勒索模式和局部Forwards
18.8.通过Zuul上传文件
18.9.查询字符串编码
18.10.请求URI编码
18.11.普通嵌入式Zuul
18.12.禁用Zuul过滤器
18.13.提供路线的Hystrix后备广告
18.14.Zuul超时
18.15.重写Location标头
18.16.启用跨源请求
18.17.指标
18.18.Zuul开发人员指南
18.18.1.Zuul Servlet
18.18.2.Zuul RequestContext
18.18.3.@EnableZuulProxy@EnableZuulServer
18.18.4.@EnableZuulServer过滤器
18.18.5.@EnableZuulProxy过滤器
18.18.6.自定义Zuul过滤器示例
如何编写前置过滤器
如何编写路由过滤器
如何编写帖子过滤器
18.18.7.Zuul错误如何工作
18.18.8.Zuul急切的应用程序上下文加载
19. Polyglot支持Sidecar
20.重试失败的请求
20.1.退避政策
20.2.组态
20.2.1.Zuul
21. HTTP客户端
22.维护模式下的模块
四.Spring Cloud OpenFeign
23.声明式REST客户端:Feign
23.1.如何包含Feign
23.2.覆盖Feign默认值
23.3.手动创建Feign客户
23.4.Feign Hystrix支持
23.5.Feign Hystrix后备
23.6.Feign和@Primary
23.7.Feign继承支持
23.8.Feign请求/响应压缩
23.9.Feign记录
23.10.Feign @QueryMap支持
23.11.故障排除
23.11.1.早期初始化错误
五. Spring Cloud Stream
24. Spring的数据集成之旅简史
25.快速入门
25.1.使用Spring Initializr创建示例应用程序
25.2.将项目导入到IDE中
25.3.添加消息处理程序,构建并运行
26. 2.0中有什么新功能?
26.1.新功能和组件
26.2.显着增强
26.2.1.执行器和Web依赖性现在都是可选的
26.2.2.内容类型协商的改进
26.3.显着的弃用
26.3.1.Java序列化(Java本机和Kryo​​)
26.3.2.不推荐使用的类和方法
27.简介Spring Cloud Stream
28.主要概念
28.1.应用模式
28.1.1.胖子
28.2.Binder抽象
28.3.持久的发布-订阅支持
28.4.消费群体
28.5.消费者类型
28.5.1.耐用性
28.6.分区支持
29.编程模型
29.1.目的地Binders
29.2.目标绑定
29.3.生产和消费信息
29.3.1.Spring Integration支持
29.3.2.使用@StreamListener注释
29.3.3.使用@StreamListener进行基于内容的路由
29.3.4.Spring Cloud功能支持
功能组成
29.3.5.使用轮询的消费者
总览
处理错误
29.4.错误处理
29.4.1.应用错误处理
29.4.2.系统错误处理
删除失败的消息
DLQ-死信队列
重新排队失败的消息
29.4.3.重试模板
29.5.反应式编程支持
29.5.1.基于Reactor的处理程序
29.5.2.反应源
30. Binders
30.1.生产者和消费者
30.2.Binder SPI
30.3.Binder检测
30.3.1.类路径检测
30.4.类路径上的多个Binders
30.5.连接到多个系统
30.6.绑定可视化和控制
30.7.Binder配置Properties
31.配置选项
31.1.绑定服务Properties
31.2.绑定Properties
31.2.1.通用绑定Properties
31.2.2.消费者Properties
31.2.3.生产者Properties
31.3.使用动态绑定的目的地
32.内容类型协商
32.1.机械学
32.1.1.内容类型与参数类型
32.1.2.讯息转换器
32.2.提供的MessageConverters
32.3.用户定义的消息转换器
33. Schema进化支持
33.1.Schema注册客户端
33.1.1.Schema注册机构客户Properties
33.2.Avro Schema注册客户端消息转换器
33.2.1.Avro Schema注册表消息转换器Properties
33.3.Apache Avro消息转换器
33.4.支持Schema的转换器
33.5.Schema注册服务器
33.5.1.Schema Registry Server API
注册新的Schema
通过主题,格式和版本检索现有的Schema
通过主题和格式检索现有的Schema
通过ID检索现有的Schema
按主题,格式和版本删除Schema
按ID删除Schema
按主题删除Schema
33.5.2.使用Confluent的Schema注册表
33.6.Schema注册和解决
33.6.1.Schema注册流程(序列化)
33.6.2.Schema解析过程(反序列化)
34.应用程序间通信
34.1.连接多个应用程序实例
34.2.实例索引和实例计数
34.3.分区
34.3.1.配置输出绑定以进行分区
34.3.2.配置输入绑定以进行分区
35.测试
35.1.禁用测试Binder自动配置
36.健康指标
37.指标发射器
38.示例
38.1.在CloudFoundry上部署流应用程序
六.Binder实现
39. Apache Kafka Binder
39.1.用法
39.2.Apache Kafka Binder概述
39.3.配置选项
39.3.1.Kafka Binder Properties
39.3.2.Kafka消费者Properties
39.3.3.Kafka生产者Properties
39.3.4.用法示例
示例:将autoCommitOffset设置为false并依靠手动进行
示例:安全配置
示例:暂停和恢复使用方
39.4.错误通道
39.5.Kafka指标
39.6.Dead-Letter主题处理
39.7.用Kafka Binder进行分区
40. Apache Kafka流Binder
40.1.用法
40.2.Kafka流Binder概述
40.2.1.流DSL
40.3.配置选项
40.3.1.Kafka流Properties
40.3.2.TimeWindow属性:
40.4.多个输入绑定
40.4.1.多个输入绑定作为接收器
40.4.2.多个输入绑定作为处理器
40.5.多个输出绑定(又名分支)
40.6.讯息转换
40.6.1.出站序列化
40.6.2.入站反序列化
40.7.错误处理
40.7.1.处理反序列化异常
40.7.2.处理非反序列化异常
40.8.国营商店
40.9.互动查询
40.10.访问基础的KafkaStreams对象
40.11.状态清理
41. RabbitMQ Binder
41.1.用法
41.2.RabbitMQ Binder概述
41.3.配置选项
41.3.1.RabbitMQ Binder Properties
41.3.2.RabbitMQ消费者Properties
41.3.3.高级侦听器容器配置
41.3.4.Rabbit生产者Properties
41.4.重试RabbitMQ Binder
41.4.1.放在一起
41.5.错误通道
41.6.Dead-Letter队列处理
41.6.1.非分区目的地
41.6.2.分区目的地
republishToDlq=false
republishToDlq=true
41.7.用RabbitMQ Binder分区
七.Spring Cloud Bus
42.快速入门
43. Bus端点
43.1.Bus刷新端点
43.2.Bus环保端点
44.寻址实例
45.解决服务的所有实例
46.服务ID必须唯一
47.定制消息代理
48.追踪Bus Events
49.广播自己的Events
49.1.在自定义包中注册事件
八.Spring Cloud Sleuth
50.引言
50.1.术语
50.2.目的
50.2.1.Zipkin的分布式跟踪
50.2.2.可视化错误
50.2.3.勇敢的分布式跟踪
50.2.4.现场例子
50.2.5.日志关联
使用Logstash进行JSON Logback
50.2.6.传播Span上下文
行李与Span标签
50.3.将侦探添加到项目中
50.3.1.仅侦探(对数相关)
50.3.2.通过HTTP与Zipkin一起侦听
50.3.3.侦探,其中Zipkin高于RabbitMQ或Kafka
50.4.覆盖Zipkin的自动配置
51.其他资源
52.特点
52.1.勇敢简介
52.1.1.追踪
52.1.2.本地追踪
52.1.3.自定义跨度
52.1.4.隐式查找当前的Span
52.1.5.RPC追踪
单向跟踪
53.抽样
53.1.声明式抽样
53.2.定制采样
53.3.在Spring Cloud Sleuth中采样
54.传播
54.1.传播额外的字段
54.1.1.前缀字段
54.1.2.提取传播的上下文
54.1.3.在客户端和服务器之间共享范围ID
54.1.4.实施传播
55.当前跟踪组件
56.当前Span
56.1.手动设置范围
57.仪器仪表
58. Span生命周期
58.1.创建并完成spans
58.2.连续跨度
58.3.使用显式父级创建Span
59.命名spans
59.1.@SpanName注释
59.2.toString()方法
60.使用Annotations管理跨度
60.1.基本原理
60.2.创建新的跨度
60.3.连续跨度
60.4.进阶标签设定
60.4.1.定制提取器
60.4.2.为值解析表达式
60.4.3.使用toString()方法
61.定制
61.1.定制者
61.2.HTTP
61.3.TracingFilter
61.4.RPC
61.5.定制服务名称
61.6.自定义报告的跨度
61.7.主机定位器
62.将跨度发送到Zipkin
63. Zipkin流Span消费者
64.整合
64.1.开放追踪
64.2.可运行和可调用
64.3.Hystrix
64.3.1.自定义并发策略
64.3.2.手动指令设定
64.4.RxJava
64.5.HTTP整合
64.5.1.HTTP过滤器
64.5.2.处理程序拦截器
64.5.3.异步Servlet支持
64.5.4.WebFlux支持
64.5.5.Dubbo RPC支持
64.6.HTTP客户端集成
64.6.1.同步休息模板
64.6.2.异步休息模板
多个异步休息模板
64.6.3.WebClient
64.6.4.特拉弗森
64.6.5.Apache HttpClientBuilderHttpAsyncClientBuilder
64.6.6.净值HttpClient
64.6.7.UserInfoRestTemplateCustomizer
64.7.Feign
64.8.gRPC
64.8.1.变体1
依存关系
服务器检测
客户端工具
64.8.2.变体2
64.9.异步通讯
64.9.1.@Async带注释的方法
64.9.2.@Scheduled带注释的方法
64.9.3.Executor,ExecutorService和ScheduledExecutorService
定制执行者
64.10.讯息传递
64.10.1.Spring Integration和Spring Cloud Stream
64.10.2.Spring RabbitMq
64.10.3.Spring Kafka
64.10.4.Spring JMS
64.11.Zuul
64.12.项目Reactor
65.运行示例
九.Spring Cloud Consul
66.安装Consul
67. Consul Agent
68.使用Consul进行服务发现
68.1.如何激活
68.2.向Consul注册
68.2.1.将管理注册为单独的服务
68.3.HTTP运行状况检查
68.3.1.元数据和Consul标签
68.3.2.使Consul实例ID唯一
68.3.3.将标头应用于健康检查请求
68.4.查找服务
68.4.1.使用Ribbon
68.4.2.使用DiscoveryClient
68.5.Consul目录观看
69.使用Consul的分布式配置
69.1.如何激活
69.2.客制化
69.3.配置监视
69.4.YAML或带有配置的Properties
69.5.git2consul与配置
69.6.快速失败
70. Consul重试
71. Spring Cloud Bus与Consul
71.1.如何激活
72.带有Hystrix的断路器
73. Hystrix通过Turbine和Consul进行指标汇总
十.Spring Cloud Zookeeper
74.安装Zookeeper
75.使用Zookeeper进行服务发现
75.1.激活
75.2.向Zookeeper注册
75.3.使用DiscoveryClient
76.将Spring Cloud Zookeeper与Spring Cloud Netflix组件一起使用
76.1.Ribbon与Zookeeper
77. Spring Cloud Zookeeper和服务注册中心
77.1.实例状态
78. Zookeeper依赖项
78.1.使用Zookeeper依赖项
78.2.激活Zookeeper依赖项
78.3.设置Zookeeper依赖项
78.3.1.别名
78.3.2.路径
78.3.3.负载均衡器类型
78.3.4.Content-Type模板和版本
78.3.5.默认标题
78.3.6.必需的依赖关系
78.3.7.存根
78.4.配置Spring Cloud Zookeeper依赖项
79. Spring Cloud Zookeeper依赖性观察者
79.1.激活
79.2.注册听众
79.3.使用状态检查器
80.使用Zookeeper的分布式配置
80.1.激活
80.2.客制化
80.3.访问控制列表(ACL)
十一.Spring Cloud Security
81.快速入门
81.1.OAuth2单点登录
81.2.OAuth2受保护的资源
82.更多细节
82.1.单点登录
82.2.令牌中继
82.2.1.Spring Cloud网关中的客户端令牌中继
82.2.2.客户令牌中继
82.2.3.Zuul代理中的客户端令牌中继
82.2.4.资源服务器令牌中继
83.配置Zuul代理的下游身份验证
十二.Spring Cloud for Cloud Foundry
84.发现
85.单点登录
十三.Spring Cloud Contract
86. Spring Cloud Contract
87. Spring Cloud Contract验证程序简介
87.1.历史
87.2.为什么要签约验证人?
87.2.1.测试问题
87.3.目的
87.4.这个怎么运作
87.4.1.三秒游
在生产者方面
在消费者方面
87.4.2.三分钟游
在生产者方面
在消费者方面
87.4.3.定义合同
87.4.4.客户端
87.4.5.服务器端
87.5.消费者驱动Contracts(CDC)分步指南
87.5.1.技术说明
87.5.2.消费者方(贷款发行)
87.5.3.生产者方(欺诈检测服务器)
87.5.4.消费者方(贷款发行)最后一步
87.6.依存关系
87.7.附加链接
87.7.1.Spring Cloud Contract个视频
87.7.2.读物
87.8.示例
88. Spring Cloud Contract常见问题
88.1.为什么使用Spring Cloud Contract验证程序而不使用X?
88.2.我不想在Groovy中写合同!
88.3.这个值是什么(consumer(),producer())?
88.4.如何进行存根版本控制?
88.4.1.API版本控制
88.4.2.JAR版本控制
88.4.3.开发或产品存根
88.5.合同共同回购
88.5.1.回购结构
88.5.2.工作流程
88.5.3.消费者
88.5.4.制片人
88.5.5.如何为每个主题而不是每个生产者定义消息传递合同?
对于Maven项目
对于Gradle项目
88.6.我需要二进制存储吗?我不能使用Git吗?
88.6.1.协议约定
88.6.2.制片人
88.6.3.生产者,合同存储在本地
与生产者和存根之间的合同保持一致
88.6.4.消费者
88.7.我可以使用契约代理吗?
88.7.1.契约消费者
88.7.2.制片人
88.7.3.契约消费者(生产者合同法)
88.8.如何调试由生成的测试客户端发送的请求/响应?
88.8.1.如何调试WireMock发送的映射/请求/响应?
88.8.2.如何查看在HTTP服务器存根中注册了什么?
88.8.3.我可以引用文件中的文本吗?
89. Spring Cloud Contract验证程序设置
89.1.Gradle项目
89.1.1.先决条件
90.添加具有依赖性的Gradle插件
90.1.Gradle和“保证放心的2.0”
90.2.Gradle的快照版本
90.3.添加存根
90.4.运行插件
90.5.默认设置
90.6.配置插件
90.7.配置选项
90.8.所有测试的单一基类
90.9.Contracts的不同基类
90.10.调用生成的测试
90.11.将存根推送到SCM
90.12.Spring Cloud Contract消费者方面的验证者
90.13.Maven项目
90.13.1.添加Maven插件
90.13.2.Maven和“保证放心” 2.0
90.13.3.Maven的快照版本
90.13.4.添加存根
90.13.5.运行插件
90.13.6.配置插件
90.13.7.配置选项
90.13.8.所有测试的单一基类
90.13.9.合同的不同基本类别
90.13.10.调用生成的测试
90.13.11.将存根推送到SCM
90.13.12.Maven插件和STS
90.13.13.Maven具有Spock测试的插件
90.14.存根和传递依存关系
90.15.情境
90.16.Docker项目
90.16.1.Maven,JAR和二进制存储的简短介绍
90.16.2.这个怎么运作
环境变量
90.16.3.使用例
90.16.4.服务器端(nodejs)
91. Spring Cloud Contract验证者消息
91.1.整合方式
91.2.手动集成测试
91.3.发布方测试生成
91.3.1.方案1:无输入消息
91.3.2.方案2:由输入触发的输出
91.3.3.方案3:无输出消息
91.4.消费者存根生成
92. Spring Cloud Contract Stub Runner
92.1.快照版本
92.2.将存根发布为JAR
92.3.Stub Runner核心
92.3.1.检索存根
存根下载
类路径扫描
配置HTTP服务器存根
92.3.2.正在运行的存根
使用主应用程序运行
HTTP存根
查看注册的映射
信息存根
92.4.Stub Runner JUnit规则和Stub Runner JUnit5扩展
92.4.1.Maven设置
92.4.2.提供固定端口
92.4.3.流利的API
92.4.4.Stub Runner与Spring
92.5.Stub Runner Spring Cloud
92.5.1.存根服务发现
测试配置文件和服务发现
92.5.2.附加配置
92.6.Stub Runner引导应用程序
92.6.1.如何使用它?
Stub Runner服务器
Stub Runner服务器胖子
Spring Cloud CLI
92.6.2.终点
HTTP
讯息传递
92.6.3.
92.6.4.Stub Runner使用服务发现启动
92.7.每个消费者存根
92.8.共同
92.8.1.JUnit和Spring的通用Properties
92.8.2.Stub Runner存根ID
92.9.Stub Runner Docker
92.9.1.如何使用它
92.9.2.非JVM项目中的客户端用法示例
93. Stub Runner用于消息传递
93.1.存根触发
93.1.1.按标签触发
93.1.2.按组和工件ID触发
93.1.3.由工件ID触发
93.1.4.触发所有讯息
93.2.Stub Runner Camel
93.2.1.将其添加到项目中
93.2.2.禁用功能
93.2.3.例子
存根结构
方案1(无输入消息)
场景2(由输入触发输出)
方案3(输入无输出)
93.3.Stub Runner整合
93.3.1.将跑步者添加到项目
93.3.2.禁用功能
方案1(无输入消息)
场景2(由输入触发输出)
方案3(输入无输出)
93.4.Stub Runner Stream
93.4.1.将跑步者添加到项目
93.4.2.禁用功能
方案1(无输入消息)
场景2(由输入触发输出)
方案3(输入无输出)
93.5.Stub Runner Spring AMQP
93.5.1.将跑步者添加到项目
触发消息
Spring AMQP测试配置
94. Contract DSL
94.1.局限性
94.2.常见的顶级元素
94.2.1.描述
94.2.2.名称
94.2.3.忽略Contracts
94.2.4.从文件传递值
94.2.5.HTTP顶级元素
94.3.请求
94.4.响应
94.5.动态特性
94.5.1.体内的动态特性
94.5.2.常用表达
94.5.3.传递可选参数
94.5.4.在服务器端执行自定义方法
94.5.5.引用响应中的请求
94.5.6.注册自己的WireMock扩展
94.5.7.匹配器部分中的动态Properties
94.6.JAX-RS支持
94.7.异步支持
94.8.使用上下文路径
94.9.使用WebFlux
94.9.1.WebFlux与WebTestClient
94.9.2.WebFlux具有显式模式
94.10.REST的XML支持
94.11.消息传递顶级元素
94.11.1.方法触发的输出
94.11.2.消息触发的输出
94.11.3.消费者/生产者
94.11.4.共同
94.12.一个文件中多个Contracts
94.13.从合同中生成Spring REST Docs片段
95.定制化
95.1.扩展DSL
95.1.1.普通JAR
95.1.2.将依赖项添加到项目中
95.1.3.在项目的依赖关系中测试依赖关系
95.1.4.在插件的依赖项中测试依赖项
95.1.5.引用DSL中的类
96.使用可插拔架构
96.1.定制合同转换器
96.1.1.契约转换器
96.1.2.契约合同
96.1.3.生产者契约
96.1.4.消费者契约
96.2.使用自定义测试生成器
96.3.使用自定义存根生成器
96.4.使用自定义Stub Runner
96.5.使用自定义存根下载器
96.6.使用SCM存根下载器
96.7.使用契约存根下载器
97. Spring Cloud Contract WireMock
97.1.自动注册存根
97.2.使用文件指定存根实体
97.3.替代方案:使用JUnit规则
97.4.放松模板的SSL验证
97.5.WireMock和Spring MVC模拟
97.6.定制WireMock配置
97.7.使用REST文档生成存根
97.8.使用REST文档生成Contracts
98.迁移
98.1.1.0.x→1.1.x
98.1.1.生成的存根的新结构
98.2.1.1.x→1.2.x
98.2.1.自定义HttpServerStub
98.2.2.用于生成测试的新软件包
98.2.3.TemplateProcessor中的新方法
98.2.4.RestAssured 3.0
98.3.1.2.x→2.0.x
99.链接
十四.Spring Cloud Vault
100.快速入门
101.客户端使用
101.1.认证方式
102.认证方式
102.1.令牌认证
102.2.AppId验证
102.2.1.自定义用户ID
102.3.AppRole身份验证
102.4.AWS-EC2身份验证
102.5.AWS-IAM身份验证
102.6.Azure MSI身份验证
102.7.TLS证书认证
102.8.隔间认证
102.9.GCP-GCE认证
102.10.GCP-IAM认证
102.11.Kubernetes认证
103.秘密后端
103.1.通用后端
103.2.版本化键值后端
103.3.Consul
103.4.RabbitMQ
103.5.AWS
104.数据库后端
104.1.数据库
104.2.阿帕奇Cassandra
104.3.MongoDB
104.4.的MySQL
104.5.PostgreSQL的
105.配置PropertySourceLocator行为
106.服务注册表配置
107. Vault客户端快速失败
108. Vault客户端SSL配置
109.租赁生命周期管理(续订和撤销)
十五.Spring Cloud网关
110.如何包括Spring Cloud网关
111.词汇表
112.工作原理
113.配置路由谓词工厂和网关过滤工厂
113.1.快捷方式配置
113.2.完全展开的论点
114.路由谓词工厂
114.1.后路线谓词工厂
114.2.路线谓词工厂之前
114.3.路线谓词工厂之间
114.4.Cookie路线谓词工厂
114.5.标头路由谓词工厂
114.6.主机路由谓词工厂
114.7.方法路线谓词工厂
114.8.路径路线谓词工厂
114.9.查询路由谓词工厂
114.10.RemoteAddr路由谓词工厂
114.11.重量路线谓词工厂
114.11.1.修改远程地址的解析方式
115.网关过滤器工厂
115.1.AddRequestHeader GatewayFilter工厂
115.2.AddRequestParameter GatewayFilter工厂
115.3.AddResponseHeader GatewayFilter工厂
115.4.DedupeResponseHeader GatewayFilter工厂
115.5.Hystrix GatewayFilter工厂
115.6.FallbackHeaders GatewayFilter工厂
115.7.MapRequestHeader GatewayFilter工厂
115.8.PrefixPath GatewayFilter工厂
115.9.PreserveHostHeader GatewayFilter工厂
115.10.RequestRateLimiter GatewayFilter工厂
115.10.1.Redis RateLimiter
115.11.重定向到GatewayFilter工厂
115.12.RemoveRequestHeader GatewayFilter工厂
115.13.RemoveResponseHeader GatewayFilter工厂
115.14.RewritePath GatewayFilter工厂
115.15.RewriteLocationResponseHeader GatewayFilter工厂
115.16.RewriteResponseHeader GatewayFilter工厂
115.17.SaveSession GatewayFilter工厂
115.18.SecureHeaders GatewayFilter工厂
115.19.SetPath GatewayFilter工厂
115.20.SetRequestHeader GatewayFilter工厂
115.21.SetResponseHeader GatewayFilter工厂
115.22.SetStatus GatewayFilter工厂
115.23.StripPrefix GatewayFilter工厂
115.24.重试GatewayFilter工厂
115.25.RequestSize GatewayFilter工厂
115.26.修改请求正文GatewayFilter工厂
115.27.修改响应主体GatewayFilter工厂
115.28.默认过滤器
116.全局过滤器
116.1.全局过滤器和GatewayFilter的组合订购
116.2.正向路由过滤器
116.3.LoadBalancerClient筛选器
116.4.ReactiveLoadBalancerClientFilter
116.5.网络路由过滤器
116.6.净写响应过滤器
116.7.RouteToRequestUrl过滤器
116.8.Websocket路由过滤器
116.9.网关指标过滤器
116.10.将交换标记为已路由
117. HttpHeadersFilters
117.1.转发的标题过滤器
117.2.RemoveHopByHop标头过滤器
117.3.XForwarded标头过滤器
118. TLS / SSL
118.1.TLS握手
119.配置
119.1.流利的Java Routes API
119.2.DiscoveryClient路由定义定位器
119.2.1.为DiscoveryClient路由配置谓词和过滤器
120. Reactor Netty访问日志
121. CORS配置
122.执行器API
122.1.详细执行器格式
122.2.检索路由过滤器
122.2.1.全局过滤器
122.2.2.路线过滤器
122.3.刷新路由缓存
122.4.检索网关中定义的路由
122.5.检索有关特定路线的信息
122.6.创建和删除特定路线
122.7.回顾:所有端点的列表
123.故障排除
123.1.日志级别
123.2.窃听
124.开发人员指南
124.1.编写自定义路线谓词工厂
124.2.编写自定义GatewayFilter工厂
124.3.编写自定义全局过滤器
125.使用Spring MVC或Webflux构建一个简单的网关
十六.Spring Cloud功能
126.引言
127.入门
128.构建和运行功能
129.功能目录和灵活的功能签名
129.1.Java 8功能支持
129.2.Kotlin Lambda支持
130.独立的Web应用程序
131.独立流应用程序
132.部署打包功能
133.功能性Bean定义
133.1.将功能与传统的Bean定义进行比较
133.2.测试功能应用
133.3.功能性Bean声明的局限性
134.动态编译
135.无服务器平台适配器
135.1.AWS Lambda
135.1.1.介绍
135.1.2.有关JAR布局的注意事项
135.1.3.上载
135.1.4.Platfom的特定功能
HTTP和API网关
135.2.Azure功能
135.2.1.有关JAR布局的注意事项
135.2.2.建立
135.2.3.运行示例
135.3.Apache Openwhisk
135.3.1.快速开始
十七.Spring Cloud Kubernetes
136.为什么需要Spring Cloud Kubernetes?
137. Starters
138. Kubernetes的DiscoveryClient
139. Kubernetes本机服务发现
140. Kubernetes PropertySource实现
140.1.使用ConfigMapPropertySource
140.2.秘密PropertySource
140.3.PropertySource重新加载
141. Ribbon在Kubernetes中发现
142. Kubernetes生态系统意识
142.1.Kubernetes配置文件自动配置
142.2.Istio意识
143.豆荚健康指标
144. Leader选举
145. Kubernetes内部的安全配置
145.1.命名空间
145.2.服务帐号
146.服务注册中心的实施
147.范例
148.其他资源
149.建筑
149.1.基本编译和测试
149.2.文献资料
149.3.使用代码
149.3.1.使用m2eclipse导入eclipse
149.3.2.不使用m2eclipse导入eclipse
150.贡献
150.1.签署贡献者许可协议
150.2.行为守则
150.3.规范和内务守则
150.4.Checkstyle
150.4.1.Checkstyle配置
150.5.IDE设置
150.5.1.Intellij IDEA
十八.Spring Cloud GCP
151.引言
152.依赖性管理
153.入门
153.1.Spring Initializr
153.1.1.GCP支持
153.1.2.GCP讯息
153.1.3.GCP储存
153.2.代码示例
153.3.代码挑战
153.4.入门指南
154. Spring Cloud GCP核心
154.1.专案编号
154.2.证书
154.2.1.范围
154.3.环境
154.4.Spring Initializr
155.Google Cloud Pub / Sub
155.1.发布/订阅操作和模板
155.1.1.发布到主题
JSON支持
155.1.2.订阅订阅
155.1.3.从订阅中提取消息
155.2.发布/订阅管理
155.2.1.创建一个话题
155.2.2.删除主题
155.2.3.列出主题
155.2.4.创建订阅
155.2.5.删除订阅
155.2.6.列表订阅
155.3.组态
155.4.示例
156. Spring资源
156.1.谷歌云存储
156.1.1.设置内容类型
156.2.组态
156.3.示例
157. Spring JDBC
157.1.先决条件
157.2.Spring Boot Google Cloud SQL入门
157.2.1.DataSource创建流程
157.2.2.故障排除技巧
连接问题
c.g.cloud.sql.core.SslSocketFactory : Re-throwing cached exception due to attempt to refresh instance information too soon after error这样的错误
PostgreSQL:java.net.SocketException: already connected问题
157.3.示例
158. Spring Integration
158.1.Cloud Pub / Sub的通道适配器
158.1.1.入站通道适配器
158.1.2.出站通道适配器
158.1.3.标头映射
158.2.示例
158.3.Google Cloud Storage的通道适配器
158.3.1.入站通道适配器
158.3.2.入站流媒体通道适配器
158.3.3.出站通道适配器
158.4.示例
159. Spring Cloud Stream
159.1.总览
159.2.组态
159.2.1.生产者目标配置
159.2.2.消费者目的地配置
159.3.示例
160. Spring Cloud Sleuth
160.1.追踪
160.2.Spring Boot Stackdriver Trace入门
160.3.覆盖自动配置
160.4.与记录集成
160.5.示例
161. Stackdriver记录
161.1.Web MVC拦截器
161.2.登录支持
161.2.1.通过API记录
161.2.2.通过控制台登录
161.3.示例
162. Spring Cloud Config
162.1.组态
162.2.快速开始
162.3.在运行时刷新配置
162.4.示例
163. Spring Data Cloud Spanner
163.1.组态
163.1.1.Cloud Spanner设置
163.1.2.Repository设置
163.1.3.自动配置
163.2.对象映射
163.2.1.建设者
163.2.2.
表名的SpEL表达式
163.2.3.主键
163.2.4.
163.2.5.嵌入式对象
163.2.6.人际关系
163.2.7.支持的类型
163.2.8.清单
163.2.9.结构清单
163.2.10.自定义类型
163.2.11.结构数组列的自定义转换器
163.3.扳手操作和模板
163.3.1.SQL查询
163.3.2.
163.3.3.高级阅读
过时的阅读
从二级索引读取
读取偏移量和限制
排序
部分阅读
查询与读取的选项摘要
163.3.4.写入/更新
插入
更新资料
增补
部分更新
163.3.5.DML
163.3.6.交易次数
读/写事务
只读交易
带有@Transactional批注的声明式事务
163.3.7.DML语句
163.4.Repositories
163.4.1.CRUD Repository
163.4.2.分页和排序Repository
163.4.3.扳手Repository
163.5.查询方法
163.5.1.按约定查询方法
163.5.2.自定义SQL / DML查询方法
具有命名查询属性的查询方法
带注释的查询方法
163.5.3.投影
163.5.4.REST Repositories
163.6.数据库和Schema管理员
163.7.示例
164. Spring Data Cloud Datastore
164.1.组态
164.1.1.Cloud Datastore设置
164.1.2.Repository设置
164.1.3.自动配置
164.2.对象映射
164.2.1.建设者
164.2.2.
164.2.3.按键
164.2.4.领域
164.2.5.支持的类型
164.2.6.自定义类型
164.2.7.集合和数组
164.2.8.自定义转换器的集合
164.3.人际关系
164.3.1.嵌入式实体
地图
164.3.2.祖辈关系
164.3.3.关键参考关系
164.4.数据存储操作和模板
164.4.1.GQL查询
164.4.2.按编号查找
指标
读取偏移量,限制和排序
部分阅读
164.4.3.写入/更新
部分更新
164.4.4.交易次数
带有@Transactional批注的声明式事务
164.4.5.对地图的读写支持
164.5.Repositories
164.5.1.按约定查询方法
164.5.2.自定义GQL查询方法
带注释的查询方法
具有命名查询属性的查询方法
164.5.3.交易次数
164.5.4.投影
164.5.5.REST Repositories
164.6.示例
165. Redis的Cloud Memorystore
165.1.Spring缓存
166.云身份识别代理(IAP)身份验证
166.1.组态
166.2.示例
167.Google Cloud Vision
167.1.云视觉模板
167.2.检测图像标签示例
167.3.示例
168. Cloud Foundry
169. Kotlin支持
169.1.先决条件
170.示例
十九.附录:配置纲要Properties

Spring Cloud为开发人员提供了用于快速构建分布式系统中某些常见模式的工具(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线)。分布式系统的协调产生了样板模式,并且使用Spring云开发人员可以快速支持实现这些模式的服务和应用程序。它们可以在任何分布式环境中正常工作,包括开发人员自己的笔记本电脑,裸机数据中心和受管理的平台,例如Cloud Foundry。

版本:Greenwich.SR5

1.特点

Spring Cloud专注于为典型的用例和扩展机制提供良好的开箱即用体验,以涵盖其他情况。

  • 分布式/版本化配置
  • 服务注册和发现
  • 路由
  • 服务到服务的呼叫
  • 负载均衡
  • 断路器
  • 分布式消息传递

第一部分:云原生应用程序

Cloud Native是一种应用程序开发风格,可鼓励在持续交付和价值驱动型开发领域轻松采用最佳实践。一个相关的学科是构建12要素应用程序,其中开发实践与交付和运营目标保持一致,例如,通过使用声明性编程,管理和监视。Spring Cloud通过多种特定方式促进了这些发展方式。起点是一组功能,分布式系统中的所有组件都需要轻松访问这些功能。

其中Spring Cloud建立在Spring Boot上,涵盖了许多这些功能Spring Cloud作为两个库提供了更多功能:Spring Cloud上下文和Spring Cloud Commons。Spring Cloud上下文为Spring Cloud应用程序的ApplicationContext提供了实用程序和特殊服务(引导上下文,加密,刷新作用域和环境端点)。Spring Cloud Commons是在不同的Spring Cloud实现中使用的一组抽象和通用类(例如Spring Cloud Netflix和Spring Cloud Consul)。

如果由于“密钥大小非法”而导致异常,并且使用Sun的JDK,则需要安装Java密码术扩展(JCE)无限强度管辖权策略文件。有关更多信息,请参见以下链接:

将文件解压缩到您使用的JRE / JDK x64 / x86版本的JDK / jre / lib / security文件夹中。

[注意]注意

Spring Cloud是根据非限制性Apache 2.0许可证发行的。如果您想为文档的这一部分做出贡献或发现错误,可以在github上找到源代码和项目跟踪工具

2. Spring Cloud上下文:应用程序上下文服务

Spring Boot对于如何使用Spring来构建应用程序有自己的看法。例如,它具有用于公共配置文件的常规位置,并具有用于公共管理和监视任务的端点。Spring Cloud以此为基础,并添加了一些功能,可能系统中的所有组件都将使用或偶尔需要这些功能。

2.1 Bootstrap应用程序上下文

Spring Cloud应用程序通过创建bootstrap上下文来运行,该上下文是主应用程序的父上下文。它负责从外部源加载配置属性,并负责解密本地外部配置文件中的属性。这两个上下文共享一个Environment,它是任何Spring应用程序的外部属性的来源。默认情况下,引导程序属性(不是bootstrap.properties,而是引导程序阶段加载的属性)具有较高的优先级,因此它们不能被本地配置覆盖。

引导上下文使用与主应用程序上下文不同的约定来定位外部配置。可以使用bootstrap.yml来代替application.yml(或.properties),而将引导程序和外部环境的外部配置很好地分开。以下清单显示了一个示例:

bootstrap.yml。 

spring:
  application:
    name: foo
  cloud:
    config:
      uri: ${SPRING_CONFIG_URI:http://localhost:8888}

如果您的应用程序需要来自服务器的任何特定于应用程序的配置,则最好设置spring.application.name(在bootstrap.ymlapplication.yml中)。为了将属性spring.application.name用作应用程序的上下文ID,必须在bootstrap.[properties | yml]中进行设置。

如果要检索特定的配置文件配置,还应该在bootstrap.[properties | yml]中设置spring.profiles.active

您可以通过设置spring.cloud.bootstrap.enabled=false来完全禁用引导过程(例如,在系统属性中)。

2.2应用程序上下文层次结构

如果从SpringApplicationSpringApplicationBuilder构建应用程序上下文,那么Bootstrap上下文将作为父级添加到该上下文。Spring的一个功能是子上下文从其父级继承属性源和配置文件,因此与构建没有Spring Cloud Config的相同上下文相比,应用程序上下文包含其他属性源。其他属性来源是:

  • bootstrap:如果在Bootstrap上下文中找到任何PropertySourceLocators并且具有非空属性,则会以高优先级显示可选的CompositePropertySource一个示例是Spring Cloud Config服务器中的属性。有关如何自定义此属性源内容的说明,请参见第2.6节“自定义Bootstrap Property源”
  • applicationConfig:[classpath:bootstrap.yml](以及相关文件,如果Spring配置文件处于活动状态):如果您拥有bootstrap.yml(或.properties),则这些属性用于配置Bootstrap上下文。然后,当它们的父级被设置时,它们被添加到子级上下文。它们的优先级低于application.yml(或.properties)以及创建Spring Boot应用程序过程中正常添加到子级的任何其他属性源的优先级。有关如何自定义这些属性源内容的说明,请参见第2.3节“更改引导程序Properties”的位置

由于属性源的排序规则,bootstrap条目优先。但是,请注意,这些不包含来自bootstrap.yml的任何数据,该数据的优先级非常低,但可用于设置默认值。

您可以通过设置创建的任何ApplicationContext的父上下文来扩展上下文层次结构,例如,使用其自己的界面或使用SpringApplicationBuilder便捷方法(parent()child()sibling())。引导上下文是您自己创建的最高级祖先的父级。层次结构中的每个上下文都有其自己的bootstrap(可能为空)属性源,以避免无意间将价值从父辈提升到子孙后代。如果有配置服务器,则层次结构中的每个上下文原则上也可以具有不同的spring.application.name,因此也具有不同的远程属性源。正常的Spring应用程序上下文行为规则适用于属性解析:子上下文的属性按名称以及属性源名称覆盖父级属性。(如果子项具有与父项同名的属性源,则子项中不包括来自父项的值)。

请注意,SpringApplicationBuilder可让您在整个层次结构中共享Environment,但这不是默认设置。因此,同级上下文尤其不需要具有相同的配置文件或属性源,即使它们可能与其父级共享相同的值。

2.3更改引导程序Properties的位置

可以通过设置spring.cloud.bootstrap.name(默认值:bootstrap),spring.cloud.bootstrap.location(默认值:空)或spring.cloud.bootstrap.additional-location(默认值:空)来指定bootstrap.yml(或.properties)位置。 —例如,在系统属性中。这些属性的行为类似于具有相同名称的spring.config.*变体。使用spring.cloud.bootstrap.location将替换默认位置,并且仅使用指定的位置。要将位置添加到默认位置列表中,可以使用spring.cloud.bootstrap.additional-location实际上,它们是通过在引导程序Environment中设置这些属性来设置引导程序ApplicationContext的。如果存在有效的配置文件(通过spring.profiles.active或通过您正在构建的上下文中的Environment API),该配置文件中的属性也会被加载,这与常规Spring Boot应用程序中的加载情况相同-例如,从bootstrap-development.properties中获取development个人资料。

2.4覆盖远程Properties的值

通过引导上下文添加到应用程序中的属性源通常是远程的(例如,来自Spring Cloud Config Server)。默认情况下,不能在本地覆盖它们。如果要让您的应用程序使用其自己的系统属性或配置文件覆盖远程属性,则远程属性源必须通过设置spring.cloud.config.allowOverride=true来授予其权限(在本地设置无效)。设置该标志后,将使用两个更细粒度的设置来控制远程属性相对于系统属性和应用程序本地配置的位置:

  • spring.cloud.config.overrideNone=true:从任何本地属性源覆盖。
  • spring.cloud.config.overrideSystemProperties=false:只有系统属性,命令行参数和环境变量(而不是本地配置文件)才应覆盖远程设置。

2.5自定义Bootstrap配置

通过将项添加到名为org.springframework.cloud.bootstrap.BootstrapConfiguration的项下的/META-INF/spring.factories中,可以将引导上下文设置为执行您喜欢的任何操作。它包含用于创建上下文的Spring @Configuration类的逗号分隔列表。您可以在此处创建要用于主应用程序上下文进行自动装配的任何beans。@Beans类型为ApplicationContextInitializer的特殊合同。如果要控制启动顺序,则可以用@Order批注标记类(默认顺序为last)。

[警告]警告

当添加自定义BootstrapConfiguration,小心你添加类不是@ComponentScanned错误地进入你的应用程序上下文,这里可能并不需要它们。为引导配置类使用单独的程序包名称,并确保@ComponentScan或带注释的配置类@SpringBootApplication尚未包含该名称。

引导过程结束时,将初始化程序注入到主要的SpringApplication实例中(这是正常的Spring Boot启动顺序,无论它是作为独立应用程序运行还是部署在应用程序服务器中)。首先,从spring.factories中找到的类创建引导上下文。然后,在启动之前,将类型为ApplicationContextInitializer的所有@Beans添加到主SpringApplication

2.6自定义引导程序Property源

引导过程添加的外部配置的默认属性来源是Spring Cloud Config服务器,但是您可以通过将类型PropertySourceLocator的beans添加到引导上下文(通过spring.factories)来添加其他来源。例如,您可以从其他服务器或数据库插入其他属性。

例如,请考虑以下定制定位器:

@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {

    @Override
    public PropertySource<?> locate(Environment environment) {
        return new MapPropertySource("customProperty",
                Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
    }

}

传入的Environment是即将创建的ApplicationContext的那个,换句话说,就是我们为其提供其他属性源的那个。它已经有其正常的Spring Boot提供的属性源,因此您可以使用这些属性来定位特定于此Environment的属性源(例如,通过在spring.application.name上键入它,这与默认设置相同)。 Spring Cloud Config服务器属性源定位符)。

如果您创建一个包含此类的jar,然后添加包含以下内容的META-INF/spring.factories,则customProperty PropertySource会出现在任何在其类路径中包含该jar的应用程序中:

org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator

2.7日志配置

如果要使用Spring Boot来配置日志设置,则应将此配置放在`bootstrap。[yml | 属性](如果您希望将其应用于所有事件)。

[注意]注意

为了使Spring Cloud正确初始化日志记录配置,您不能使用自定义前缀。例如,初始化记录系统时,Spring Cloud无法识别使用custom.loggin.logpath

2.8环境变化

应用程序侦听EnvironmentChangeEvent并以几种标准方式对更改做出反应(用户可以通过常规方式将其他ApplicationListeners作为@Beans添加)。观察到EnvironmentChangeEvent时,它会列出已更改的键值,并且应用程序将这些键值用于:

  • 重新绑定上下文中的任何@ConfigurationProperties beans
  • logging.level.*中的所有属性设置记录器级别

请注意,默认情况下,Config Client不轮询Environment中的更改。通常,我们不建议您使用这种方法来检测更改(尽管您可以使用@Scheduled注释对其进行设置)。如果您具有横向扩展的客户端应用程序,则最好向所有实例广播EnvironmentChangeEvent,而不是让它们轮询更改(例如,使用Spring Cloud Bus)。

只要您可以实际更改Environment并发布事件,EnvironmentChangeEvent就涵盖了一大类刷新用例。请注意,这些API是公共的,并且是核心Spring的一部分)。您可以通过访问/configprops端点(正常的Spring Boot Actuator功能)来验证更改是否绑定到@ConfigurationProperties beans。例如,DataSource可以在运行时更改其maxPoolSize(由Spring Boot创建的默认DataSource@ConfigurationProperties bean)并动态地增加容量。重新绑定@ConfigurationProperties并不涵盖另一类用例,在这种情况下,您需要对刷新有更多的控制,并且需要对整个ApplicationContext进行原子更改。为了解决这些问题,我们有@RefreshScope

2.9刷新范围

进行配置更改时,标记为@RefreshScope的Spring @Bean将得到特殊处理。此功能解决了状态beans的问题,该状态仅在初始化时才注入配置。例如,如果通过Environment更改数据库URL时DataSource具有打开的连接,则您可能希望这些连接的持有者能够完成他们正在做的事情。然后,下次某物从池中借用一个连接时,它将获得一个具有新URL的连接。

有时,甚至可能必须将@RefreshScope批注应用到只能初始化一次的某些beans上。如果bean是“不可变的”,则必须用@RefreshScope注释bean或在属性键spring.cloud.refresh.extra-refreshable下指定类名。

[重要]重要

如果您自己创建一个DataSource bean,而实现是一个HikariDataSource,则返回最特定的类型,在这种情况下为HikariDataSource否则,您将需要设置spring.cloud.refresh.extra-refreshable=javax.sql.DataSource

刷新作用域beans是惰性代理,它们在使用时(即在调用方法时)进行初始化,并且作用域充当初始化值的缓存。若要强制bean在下一个方法调用上重新初始化,必须使它的缓存条目无效。

RefreshScope在上下文中是bean,并具有公用的refreshAll()方法,可通过清除目标缓存来刷新作用域中的所有beans。/refresh端点公开了此功能(通过HTTP或JMX)。要按名称刷新单个bean,还有一个refresh(String)方法。

要公开/refresh端点,您需要在应用程序中添加以下配置:

management:
  endpoints:
    web:
      exposure:
        include: refresh
[注意]注意

@RefreshScope@Configuration类上(在技术上)有效,但是可能会导致令人惊讶的行为。例如,这并不意味着该类中定义的所有@Beans本身都在@RefreshScope中。具体而言,除非刷新本身在@RefreshScope中,否则依赖那些beans的任何内容都不能依赖于刷新启动时对其进行更新。在这种情况下,将在刷新时重建它,并重新注入其依赖项。此时,它们将从刷新的@Configuration重新初始化。

2.10加密和解密

Spring Cloud具有Environment预处理器,用于在本地解密属性值。它遵循与Config Server相同的规则,并且通过encrypt.*具有相同的外部配置。因此,您可以使用{cipher}*形式的加密值,并且只要存在有效密钥,就可以在主应用程序上下文获得Environment设置之前对它们进行解密。要在应用程序中使用加密功能,您需要在类路径中包含Spring Security RSA(Maven坐标:“ org.springframework.security:spring-security-rsa”),并且还需要JVM中的全功能JCE扩展。

如果由于“密钥大小非法”而导致异常,并且使用Sun的JDK,则需要安装Java密码术扩展(JCE)无限强度管辖权策略文件。有关更多信息,请参见以下链接:

将文件解压缩到您使用的JRE / JDK x64 / x86版本的JDK / jre / lib / security文件夹中。

2.11端点

对于Spring Boot Actuator应用程序,可以使用一些其他管理端点。您可以使用:

  • POST/actuator/env以更新Environment并重新绑定@ConfigurationProperties和日志级别。
  • /actuator/refresh重新加载引导上下文并刷新@RefreshScope beans。
  • /actuator/restart关闭ApplicationContext并重新启动(默认情况下禁用)。
  • /actuator/pause/actuator/resume用于调用Lifecycle方法(ApplicationContext中的stop()start())。
[注意]注意

如果禁用/actuator/restart端点,则/actuator/pause/actuator/resume端点也将被禁用,因为它们只是/actuator/restart的特例。

3. Spring Cloud Commons:通用抽象

服务发现,负载平衡和断路器之类的模式将它们带到一个通用的抽象层,可以由所有Spring Cloud客户端使用,而与实现无关(例如,使用Eureka或Consul进行的发现) )。

3.1 @EnableDiscoveryClient

Spring Cloud Commons提供了@EnableDiscoveryClient批注。这将寻找META-INF/spring.factoriesDiscoveryClient接口的实现。Discovery Client的实现在org.springframework.cloud.client.discovery.EnableDiscoveryClient键下将配置类添加到spring.factoriesDiscoveryClient实现的示例包括Spring Cloud Netflix EurekaSpring Cloud Consul发现Spring Cloud Zookeeper发现

默认情况下,DiscoveryClient的实现会自动将本地Spring Boot服务器注册到远程发现服务器。可以通过在@EnableDiscoveryClient中设置autoRegister=false来禁用此行为。

[注意]注意

不再需要@EnableDiscoveryClient您可以在类路径上放置DiscoveryClient实现,以使Spring Boot应用程序向服务发现服务器注册。

3.1.1健康指标

公用创建了Spring Boot HealthIndicatorDiscoveryClient实现可以通过实现DiscoveryHealthIndicator来参与。要禁用复合HealthIndicator,请设置spring.cloud.discovery.client.composite-indicator.enabled=false基于DiscoveryClient的通用HealthIndicator是自动配置的(DiscoveryClientHealthIndicator)。要禁用它,请设置spring.cloud.discovery.client.health-indicator.enabled=false要禁用DiscoveryClientHealthIndicator的描述字段,请设置spring.cloud.discovery.client.health-indicator.include-description=false否则,它可能会像已卷起的HealthIndicator中的description一样冒泡。

3.1.2订购DiscoveryClient实例

DiscoveryClient接口扩展了Ordered当使用多个发现客户端时,这很有用,因为它允许您定义返回的发现客户端的顺序,类似于如何订购由Spring应用程序加载的beans。默认情况下,任何DiscoveryClient的顺序都设置为0如果要为自定义DiscoveryClient实现设置不同的顺序,则只需覆盖getOrder()方法,以便它返回适合您的设置的值。除此之外,您可以使用属性来设置Spring Cloud提供的DiscoveryClient实现的顺序,其中包括ConsulDiscoveryClientEurekaDiscoveryClientZookeeperDiscoveryClient为此,您只需要将spring.cloud.{clientIdentifier}.discovery.order(对于Eureka,则为eureka.client.order)属性设置为所需的值。

3.2服务注册

Commons现在提供一个ServiceRegistry接口,该接口提供诸如register(Registration)deregister(Registration)之类的方法,这些方法使您可以提供自定义的注册服务。Registration是标记界面。

以下示例显示了正在使用的ServiceRegistry

@Configuration
@EnableDiscoveryClient(autoRegister=false)
public class MyConfiguration {
    private ServiceRegistry registry;

    public MyConfiguration(ServiceRegistry registry) {
        this.registry = registry;
    }

    // called through some external process, such as an event or a custom actuator endpoint
    public void register() {
        Registration registration = constructRegistration();
        this.registry.register(registration);
    }
}

每个ServiceRegistry实现都有自己的Registry实现。

  • ZookeeperRegistrationZookeeperServiceRegistry一起使用
  • EurekaRegistrationEurekaServiceRegistry一起使用
  • ConsulRegistrationConsulServiceRegistry一起使用

如果您使用的是ServiceRegistry接口,则将需要为使用的ServiceRegistry实现传递正确的Registry实现。

3.2.1 ServiceRegistry自动注册

默认情况下,ServiceRegistry实现会自动注册正在运行的服务。要禁用该行为,可以设置:* @EnableDiscoveryClient(autoRegister=false)以永久禁用自动注册。* spring.cloud.service-registry.auto-registration.enabled=false通过配置禁用行为。

ServiceRegistry自动注册Events

服务自动注册时将触发两个事件。注册服务之前会触发名为InstancePreRegisteredEvent的第一个事件。注册服务后,将触发名为InstanceRegisteredEvent的第二个事件。您可以注册ApplicationListener,以收听和响应这些事件。

[注意]注意

如果将spring.cloud.service-registry.auto-registration.enabled设置为false,则不会触发这些事件。

3.2.2服务注册表执行器端点

Spring Cloud Commons提供了一个/service-registry执行器端点。该端点依赖于Spring应用程序上下文中的Registration bean。使用GET调用/service-registry会返回Registration的状态。对具有JSON正文的同一终结点使用POST会将当前Registration的状态更改为新值。JSON正文必须包含带有首选值的status字段。请参阅更新状态时用于允许值的ServiceRegistry实现的文档以及为状态返回的值。例如,Eureka的受支持状态为UPDOWNOUT_OF_SERVICEUNKNOWN

3.3 Spring RestTemplate作为负载均衡器客户端

RestTemplate可以自动配置为在后台使用负载均衡器客户端。要创建负载均衡的RestTemplate,请创建RestTemplate @Bean并使用@LoadBalanced限定符,如以下示例所示:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

public class MyClass {
    @Autowired
    private RestTemplate restTemplate;

    public String doOtherStuff() {
        String results = restTemplate.getForObject("http://stores/stores", String.class);
        return results;
    }
}
[警告]警告

RestTemplate bean不再通过自动配置创建。各个应用程序必须创建它。

URI需要使用虚拟主机名(即服务名,而不是主机名)。Ribbon客户端用于创建完整的物理地址。有关如何设置RestTemplate的详细信息,请参见RibbonAutoConfiguration

[重要]重要

为了使用负载均衡的RestTemplate,您需要在类路径中具有负载均衡器实现。推荐的实现是BlockingLoadBalancerClient-添加org.springframework.cloud:spring-cloud-loadbalancer以便使用它。RibbonLoadBalancerClient也可以使用,但是目前正在维护中,我们不建议将其添加到新项目中。

[警告]警告

如果要使用BlockingLoadBalancerClient,请确保项目类路径中没有RibbonLoadBalancerClient,因为向后兼容的原因,默认情况下将使用它。

3.4 Spring WebClient作为负载均衡器客户端

WebClient可以自动配置为使用负载均衡器客户端。要创建负载均衡的WebClient,请创建WebClient.Builder @Bean并使用@LoadBalanced限定符,如以下示例所示:

@Configuration
public class MyConfiguration {

	@Bean
	@LoadBalanced
	public WebClient.Builder loadBalancedWebClientBuilder() {
		return WebClient.builder();
	}
}

public class MyClass {
    @Autowired
    private WebClient.Builder webClientBuilder;

    public Mono<String> doOtherStuff() {
        return webClientBuilder.build().get().uri("http://stores/stores")
        				.retrieve().bodyToMono(String.class);
    }
}

URI需要使用虚拟主机名(即服务名,而不是主机名)。Ribbon客户端用于创建完整的物理地址。

[重要]重要

如果要使用@LoadBalanced WebClient.Builder,则需要在类路径中有一个loadbalancer实现。建议您将org.springframework.cloud:spring-cloud-loadbalancer依赖项添加到项目中。然后,将在下面使用ReactiveLoadBalancer或者,此功能也可以在spring-cloud-starter-netflix-ribbon上使用,但是该请求将由后台的非响应LoadBalancerClient处理。此外,spring-cloud-starter-netflix-ribbon已经处于维护模式,因此我们不建议您将其添加到新项目中。

[提示]提示

在下面使用的ReactorLoadBalancer支持缓存。如果检测到cacheManager,将使用ServiceInstanceSupplier的缓存版本。如果没有,我们将从发现服务中检索实例,而不进行缓存。如果您使用ReactiveLoadBalancer建议您在项目中启用缓存

3.4.1重试失败的请求

可以配置负载均衡的RestTemplate以重试失败的请求。默认情况下,禁用此逻辑。您可以通过在应用程序的类路径中添加Spring重试启用它负载平衡的RestTemplate遵循与重试失败的请求有关的某些Ribbon配置值。您可以使用client.ribbon.MaxAutoRetriesclient.ribbon.MaxAutoRetriesNextServerclient.ribbon.OkToRetryOnAllOperations属性。如果要通过对类路径使用Spring重试来禁用重试逻辑,则可以设置spring.cloud.loadbalancer.retry.enabled=false有关这些属性的作用的说明,请参见Ribbon文档

如果要在重试中实现BackOffPolicy,则需要创建LoadBalancedRetryFactory类型的bean并覆盖createBackOffPolicy方法:

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryFactory retryFactory() {
        return new LoadBalancedRetryFactory() {
            @Override
            public BackOffPolicy createBackOffPolicy(String service) {
        		return new ExponentialBackOffPolicy();
        	}
        };
    }
}
[注意]注意

前面示例中的client应替换为您的Ribbon客户名称。

如果要向重试功能中添加一个或多个RetryListener实现,则需要创建类型为LoadBalancedRetryListenerFactory的bean,并返回要用于给定服务的RetryListener数组,如以下示例所示:

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryListenerFactory retryListenerFactory() {
        return new LoadBalancedRetryListenerFactory() {
            @Override
            public RetryListener[] createRetryListeners(String service) {
                return new RetryListener[]{new RetryListener() {
                    @Override
                    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                        //TODO Do you business...
                        return true;
                    }

                    @Override
                     public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                        //TODO Do you business...
                    }

                    @Override
                    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                        //TODO Do you business...
                    }
                }};
            }
        };
    }
}

3.5多个RestTemplate对象

如果您想要一个RestTemplate而不是负载均衡的,请创建一个RestTemplate bean并注入它。要访问负载均衡的RestTemplate,请在创建@Bean时使用@LoadBalanced限定符,如以下示例所示:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate loadBalanced() {
        return new RestTemplate();
    }

    @Primary
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

public class MyClass {
@Autowired
private RestTemplate restTemplate;

    @Autowired
    @LoadBalanced
    private RestTemplate loadBalanced;

    public String doOtherStuff() {
        return loadBalanced.getForObject("http://stores/stores", String.class);
    }

    public String doStuff() {
        return restTemplate.getForObject("https://example.com", String.class);
    }
}
[重要]重要

注意,在前面的示例中,在普通的RestTemplate声明上使用了@Primary批注,以消除不合格的@Autowired注入的歧义。

[提示]提示

如果看到诸如java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89之类的错误,请尝试注入RestOperations或设置spring.aop.proxyTargetClass=true

3.6多个WebClient对象

如果要使WebClient负载不均衡,请创建一个WebClient bean并注入它。要访问负载均衡的WebClient,请在创建@Bean时使用@LoadBalanced限定符,如以下示例所示:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    WebClient.Builder loadBalanced() {
        return WebClient.builder();
    }

    @Primary
    @Bean
    WebClient.Builder webClient() {
        return WebClient.builder();
    }
}

public class MyClass {
    @Autowired
    private WebClient.Builder webClientBuilder;

    @Autowired
    @LoadBalanced
    private WebClient.Builder loadBalanced;

    public Mono<String> doOtherStuff() {
        return loadBalanced.build().get().uri("http://stores/stores")
        				.retrieve().bodyToMono(String.class);
    }

    public Mono<String> doStuff() {
        return webClientBuilder.build().get().uri("http://example.com")
        				.retrieve().bodyToMono(String.class);
    }
}

3.7 Spring WebFlux WebClient作为负载均衡器客户端

3.7.1 Spring具有响应式负载均衡器的WebFlux WebClient

可以将WebClient配置为使用ReactiveLoadBalancer如果将org.springframework.cloud:spring-cloud-loadbalancer添加到项目中,并且spring-webflux在类路径中,则会自动配置ReactorLoadBalancerExchangeFilterFunction以下示例说明如何配置WebClient以在后台使用无功负载均衡器:

public class MyClass {
    @Autowired
    private ReactorLoadBalancerExchangeFilterFunction lbFunction;

    public Mono<String> doOtherStuff() {
        return WebClient.builder().baseUrl("http://stores")
            .filter(lbFunction)
            .build()
            .get()
            .uri("/stores")
            .retrieve()
            .bodyToMono(String.class);
    }
}

URI需要使用虚拟主机名(即服务名,而不是主机名)。ReactorLoadBalancerClient用于创建完整的物理地址。

3.7.2 Spring WebFlux WebClient,带有非反应式负载均衡器客户端

如果您的项目中没有org.springframework.cloud:spring-cloud-loadbalancer,但是确实有spring-cloud-starter-netflix-ribbon,则仍可以将WebClientLoadBalancerClient结合使用。如果spring-webflux在类路径中,将自动配置LoadBalancerExchangeFilterFunction但是请注意,这是在后台使用非反应性客户端。以下示例显示如何配置WebClient以使用负载均衡器:

public class MyClass {
    @Autowired
    private LoadBalancerExchangeFilterFunction lbFunction;

    public Mono<String> doOtherStuff() {
        return WebClient.builder().baseUrl("http://stores")
            .filter(lbFunction)
            .build()
            .get()
            .uri("/stores")
            .retrieve()
            .bodyToMono(String.class);
    }
}

URI需要使用虚拟主机名(即服务名,而不是主机名)。LoadBalancerClient用于创建完整的物理地址。

警告:现在不建议使用此方法。我们建议您将WebFlux与电抗性负载平衡器一起 使用。

3.7.3传递自己的Load-Balancer客户端配置

您还可以使用@LoadBalancerClient批注传递您自己的负载平衡器客户端配置,并传递负载平衡器客户端的名称和配置类,如下所示:

@Configuration
@LoadBalancerClient(value = "stores", configuration = StoresLoadBalancerClientConfiguration.class)
public class MyConfiguration {

	@Bean
	@LoadBalanced
	public WebClient.Builder loadBalancedWebClientBuilder() {
		return WebClient.builder();
	}
}

也可以通过@LoadBalancerClients注释将多个配置(对于一个以上的负载均衡器客户端)一起传递,如下所示:

@Configuration
@LoadBalancerClients({@LoadBalancerClient(value = "stores", configuration = StoresLoadBalancerClientConfiguration.class), @LoadBalancerClient(value = "customers", configuration = CustomersLoadBalancerClientConfiguration.class)})
public class MyConfiguration {

	@Bean
	@LoadBalanced
	public WebClient.Builder loadBalancedWebClientBuilder() {
		return WebClient.builder();
	}
}

3.8忽略网络接口

有时,忽略某些命名的网络接口很有用,以便可以将它们从服务发现注册中排除(例如,在Docker容器中运行时)。可以设置正则表达式列表,以使所需的网络接口被忽略。以下配置将忽略docker0接口以及所有以veth开头的接口:

application.yml。 

spring:
  cloud:
    inetutils:
      ignoredInterfaces:
        - docker0
        - veth.*

您还可以通过使用正则表达式列表来强制仅使用指定的网络地址,如以下示例所示:

bootstrap.yml。 

spring:
  cloud:
    inetutils:
      preferredNetworks:
        - 192.168
        - 10.0

您也可以只使用站点本地地址,如以下示例所示:.application.yml

spring:
  cloud:
    inetutils:
      useOnlySiteLocalInterfaces: true

有关构成站点本地地址的详细信息,请参见Inet4Address.html.isSiteLocalAddress()

3.9 HTTP客户端工厂

Spring Cloud Commons提供了beans用于创建Apache HTTP客户端(ApacheHttpClientFactory)和OK HTTP客户端(OkHttpClientFactory)。仅当OK HTTP jar位于类路径上时,才创建OkHttpClientFactory bean。此外,Spring Cloud Commons提供了beans用于创建两个客户端使用的连接管理器:ApacheHttpClientConnectionManagerFactory用于Apache HTTP客户端,OkHttpClientConnectionPoolFactory用于OK HTTP客户端。如果您想自定义在下游项目中如何创建HTTP客户端,则可以提供自己的beans实现。另外,如果您提供类型为HttpClientBuilderOkHttpClient.Builder的bean,则默认工厂将使用这些构建器作为返回到下游项目的构建器的基础。您还可以通过将spring.cloud.httpclientfactories.apache.enabledspring.cloud.httpclientfactories.ok.enabled设置为false来禁用这些beans的创建。

3.10启用的功能

Spring Cloud Commons提供了一个/features执行器端点。该端点返回类路径上可用的功能以及是否已启用它们。返回的信息包括功能类型,名称,版本和供应商。

3.10.1特征类型

“功能”有两种类型:抽象和命名。

抽象功能是定义接口或抽象类并创建实现的功能,例如DiscoveryClientLoadBalancerClientLockService抽象类或接口用于在上下文中找到该类型的bean。显示的版本为bean.getClass().getPackage().getImplementationVersion()

命名功能是没有实现的特定类的功能,例如“ Circuit Breaker”,“ API Gateway”,“ Spring Cloud Bus”等。这些功能需要一个名称和一个bean类型。

3.10.2声明功能

任何模块都可以声明任意数量的HasFeature beans,如以下示例所示:

@Bean
public HasFeatures commonsFeatures() {
  return HasFeatures.abstractFeatures(DiscoveryClient.class, LoadBalancerClient.class);
}

@Bean
public HasFeatures consulFeatures() {
  return HasFeatures.namedFeatures(
    new NamedFeature("Spring Cloud Bus", ConsulBusAutoConfiguration.class),
    new NamedFeature("Circuit Breaker", HystrixCommandAspect.class));
}

@Bean
HasFeatures localFeatures() {
  return HasFeatures.builder()
      .abstractFeature(Foo.class)
      .namedFeature(new NamedFeature("Bar Feature", Bar.class))
      .abstractFeature(Baz.class)
      .build();
}

这些beans中的每一个都应放入受到适当保护的@Configuration中。

3.11 Spring Cloud兼容性验证

由于某些用户在设置Spring Cloud应用程序时遇到问题,我们决定添加兼容性验证机制。如果您当前的设置与Spring Cloud要求不兼容,则会断开,并附上一份报告,说明出了什么问题。

目前,我们验证哪个版本的Spring Boot已添加到您的类路径中。

报告范例

***************************
APPLICATION FAILED TO START
***************************

Description:

Your project setup is incompatible with our requirements due to following reasons:

- Spring Boot [2.1.0.RELEASE] is not compatible with this Spring Cloud release train


Action:

Consider applying the following actions:

- Change Spring Boot version to one of the following versions [1.2.x, 1.3.x] .
You can find the latest Spring Boot versions here [https://spring.io/projects/spring-boot#learn].
If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [https://spring.io/projects/spring-cloud#overview] and check the [Release Trains] section.

为了禁用此功能,请将spring.cloud.compatibility-verifier.enabled设置为false如果要覆盖兼容的Spring Boot版本,只需用兼容的Spring Boot版本的逗号分隔列表设置spring.cloud.compatibility-verifier.compatible-boot-versions属性。

第二部分 Spring Cloud Config

Greenwich SR5

Spring Cloud Config为分布式系统中的外部化配置提供服务器端和客户端支持。使用Config Server,您可以在中心位置管理所有环境中应用程序的外部属性。客户端和服务器上的概念都与Spring EnvironmentPropertySource抽象映射相同,因此它们非常适合Spring应用程序,但可以与以任何语言运行的任何应用程序一起使用。在应用程序从开发人员到测试人员再到生产人员的整个部署过程中,您可以管理这些环境之间的配置,并确保应用程序具有它们迁移时所需的一切。服务器存储后端的默认实现使用git,因此它轻松支持带标签的配置环境版本,并且可以通过各种工具来访问这些内容来管理内容。添加替代实现并将其插入Spring配置很容易。

4.快速入门

此快速入门介绍了如何使用Spring Cloud Config服务器的服务器和客户端。

首先,启动服务器,如下所示:

$ cd spring-cloud-config-server
$ ../mvnw spring-boot:run

该服务器是Spring Boot应用程序,因此,如果愿意,可以从IDE运行它(主类是ConfigServerApplication)。

接下来尝试一个客户端,如下所示:

$ curl localhost:8888/foo/development
{"name":"foo","label":"master","propertySources":[
  {"name":"https://github.com/scratches/config-repo/foo-development.properties","source":{"bar":"spam"}},
  {"name":"https://github.com/scratches/config-repo/foo.properties","source":{"foo":"bar"}}
]}

定位属性源的默认策略是克隆git存储库(位于spring.cloud.config.server.git.uri),并使用它来初始化小型SpringApplication小型应用程序的Environment用于枚举属性源并将其发布在JSON端点上。

HTTP服务具有以下形式的资源:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

applicationSpringApplication中作为spring.config.name注入(在常规Spring Boot应用中通常是application),profile是有效配置文件(或逗号分隔)属性列表),而label是可选的git标签(默认为master。)

Spring Cloud Config服务器从各种来源获取远程客户端的配置。以下示例从git存储库(必须提供)中获取配置,如以下示例所示:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/spring-cloud-samples/config-repo

其他来源包括任何与JDBC兼容的数据库,Subversion,Hashicorp Vault,Credhub和本地文件系统。

4.1客户端使用

要在应用程序中使用这些功能,您可以将其构建为依赖于spring-cloud-config-client的Spring Boot应用程序(例如,请参阅config-client或示例应用程序的测试用例)。添加依赖项最方便的方法是使用Spring Boot启动器org.springframework.cloud:spring-cloud-starter-config还有一个Maven用户的父pom和BOM(spring-cloud-starter-parent),以及一个Gradle和Spring CLI用户的Spring IO版本管理属性文件。以下示例显示了典型的Maven配置:

pom.xml。 

   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>{spring-boot-docs-version}</version>
       <relativePath /> <!-- lookup parent from repository -->
   </parent>

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>{spring-cloud-version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-config</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>

<build>
	<plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
	</plugins>
</build>

   <!-- repositories also needed for snapshots and milestones -->

现在,您可以创建一个标准的Spring Boot应用程序,例如以下HTTP服务器:

@SpringBootApplication
@RestController
public class Application {

    @RequestMapping("/")
    public String home() {
        return "Hello World!";
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

当此HTTP服务器运行时,它将从端口8888上的默认本地配置服务器(如果正在运行)中获取外部配置。要修改启动行为,可以使用bootstrap.properties(类似于application.properties,但用于应用程序上下文的引导阶段),如以下示例所示:

spring.cloud.config.uri: http://myconfigserver.com

默认情况下,如果未设置应用程序名称,将使用application要修改名称,可以将以下属性添加到bootstrap.properties文件中:

spring.application.name: myapp
[注意]注意

设置属性${spring.application.name}时,请勿在您的应用名称前加上保留字application-,以防止解析正确的属性源时出现问题。

引导程序属性在/env端点中显示为高优先级属性源,如以下示例所示。

$ curl localhost:8080/env
{
  "profiles":[],
  "configService:https://github.com/spring-cloud-samples/config-repo/bar.properties":{"foo":"bar"},
  "servletContextInitParams":{},
  "systemProperties":{...},
  ...
}

名为``configService:<URL of remote repository>/<file name>的属性源包含值为barfoo属性,并且优先级最高。

[注意]注意

属性源名称中的URL是git存储库,而不是配置服务器URL。

5. Spring Cloud Config服务器

Spring Cloud Config服务器为外部配置(名称/值对或等效的YAML内容)提供了一个基于HTTP资源的API。通过使用@EnableConfigServer批注,服务器可嵌入到Spring Boot应用程序中。因此,以下应用程序是配置服务器:

ConfigServer.java。 

@SpringBootApplication
@EnableConfigServer
public class ConfigServer {
  public static void main(String[] args) {
    SpringApplication.run(ConfigServer.class, args);
  }
}

像所有Spring Boot应用程序一样,它默认在端口8080上运行,但是您可以通过各种方式将其切换到更传统的端口8888。最简单的方法也是设置默认配置存储库,方法是使用spring.config.name=configserver(在Config Server jar中有configserver.yml)启动它。另一种方法是使用您自己的application.properties,如以下示例所示:

application.properties。 

server.port: 8888
spring.cloud.config.server.git.uri: file://${user.home}/config-repo

其中${user.home}/config-repo是包含YAML和属性文件的git存储库。

[注意]注意

在Windows上,如果文件URL带有驱动器前缀(例如,file:///${user.home}/config-repo)是绝对的,则在文件URL中需要一个额外的“ /”。

[提示]提示

以下清单显示了在前面的示例中创建git存储库的方法:

$ cd $HOME
$ mkdir config-repo
$ cd config-repo
$ git init .
$ echo info.foo: bar > application.properties
$ git add -A .
$ git commit -m "Add application.properties"
[警告]警告

将本地文件系统用于git存储库仅用于测试。您应该使用服务器在生产环境中托管配置存储库。

[警告]警告

如果仅将文本文件保留在其中,则配置存储库的初始克隆可以快速有效。如果存储二进制文件(尤其是大文件),则可能会在首次配置请求时遇到延迟,或者在服务器中遇到内存不足错误。

5.1环境Repository

您应该在哪里存储配置服务器的配置数据?控制此行为的策略是服务Environment对象的EnvironmentRepositoryEnvironment是Spring Environment的域的浅表副本(包括propertySources作为主要特征)。Environment资源由三个变量参数化:

  • {application},它映射到客户端的spring.application.name
  • {profile},它映射到客户端上的spring.profiles.active(以逗号分隔的列表)。
  • {label},这是服务器端功能,标记了一组“版本化”的配置文件。

Repository实现通常类似于Spring Boot应用程序,从等于{application}参数的spring.config.name和等于{profiles}参数的spring.profiles.active加载配置文件。配置文件的优先规则也与常规Spring Boot应用程序中的规则相同:活动配置文件的优先级高于默认设置,并且,如果有多个配置文件,则最后一个优先(与向Map添加条目类似)。

以下示例客户端应用程序具有此引导程序配置:

bootstrap.yml。 

spring:
  application:
    name: foo
  profiles:
    active: dev,mysql

(与Spring Boot应用程序一样,这些属性也可以由环境变量或命令行参数设置)。

如果存储库基于文件,则服务器从application.yml(在所有客户端之间共享)和foo.yml(以foo.yml优先)创建一个Environment如果YAML文件中包含指向Spring配置文件的文档,则将以更高的优先级应用这些文件(按列出的配置文件的顺序)。如果存在特定于配置文件的YAML(或属性)文件,这些文件也将以比默认文件更高的优先级应用。较高的优先级会转换为Environment中较早列出的PropertySource(这些相同的规则适用于独立的Spring Boot应用程序。)

您可以将spring.cloud.config.server.accept-empty设置为false,以便在未找到应用程序的情况下Server返回HTTP 404状态。默认情况下,此标志设置为true。

5.1.1 Git后端

EnvironmentRepository的默认实现使用Git后端,这对于管理升级和物理环境以及审核更改非常方便。要更改存储库的位置,可以在Config Server中设置spring.cloud.config.server.git.uri配置属性(例如,在application.yml中)。如果您使用file:前缀进行设置,则它应在本地存储库中运行,以便无需服务器即可快速轻松地开始使用。但是,在那种情况下,服务器直接在本地存储库上运行而无需克隆它(如果它不是裸露的,这并不重要,因为Config Server从不对“远程”存储库进行更改)。要扩展Config Server并使其高度可用,您需要使服务器的所有实例都指向同一存储库,因此仅共享文件系统可以工作。即使在那种情况下,最好对共享文件系统存储库使用ssh:协议,以便服务器可以克隆它并将本地工作副本用作缓存。

此存储库实现将HTTP资源的{label}参数映射到git标签(提交ID,分支名称或标记)。如果git分支或标记名称包含斜杠(/),则应使用特殊字符串(_)在HTTP URL中指定标签(以避免与其他URL路径产生歧义)。例如,如果标签为foo/bar,则替换斜杠将产生以下标签:foo(_)bar特殊字符串(_)的包含内容也可以应用于{application}参数。如果您使用命令行客户端(例如curl),请注意URL中的括号-您应使用单引号('')将其从外壳中移出。

跳过SSL证书验证

可以通过将git.skipSslValidation属性设置为true(默认值为false)来禁用配置服务器对Git服务器的SSL证书的验证。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://example.com/my/repo
          skipSslValidation: true

设置HTTP连接超时

您可以配置配置服务器将等待获取HTTP连接的时间(以秒为单位)。使用git.timeout属性。

spring:
  cloud:
    config:
      server:
        git:
          uri: https://example.com/my/repo
          timeout: 4

Git URI中的占位符

Spring Cloud Config服务器支持带有{application}{profile}(如果需要的话还有{label})占位符的git存储库URL,但是请记住该标签始终用作git标签。因此,您可以使用类似于以下的结构来支持每个应用程序一个存储库策略:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/myorg/{application}

您也可以使用类似的模式({profile}来支持每个配置文件一个存储库策略。

此外,在{application}参数中使用特殊字符串“(_)”可以启用对多个组织的支持,如以下示例所示:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/{application}

其中在请求时以以下格式提供{application}organization(_)application

模式匹配和多个Repositories

Spring Cloud Config还通过在应用程序和概要文件名称上进行模式匹配来支持更复杂的需求。模式格式是以逗号分隔的{application}/{profile}名称列表,带有通配符(请注意,以通配符开头的模式可能需要加引号),如以下示例所示:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/spring-cloud-samples/config-repo
          repos:
            simple: https://github.com/simple/config-repo
            special:
              pattern: special*/dev*,*special*/dev*
              uri: https://github.com/special/config-repo
            local:
              pattern: local*
              uri: file:/home/configsvc/config-repo

如果{application}/{profile}与任何模式都不匹配,它将使用在spring.cloud.config.server.git.uri下定义的默认URI。在上面的示例中,对于简单存储库,模式为simple/*(在所有配置文件中仅匹配一个名为simple的应用程序)。本地库匹配,在所有配置文件(该/*后缀会自动添加到没有档案资料匹配的任何模式)local开头的所有应用程序名称。

[注意]注意

单行中所使用的短切简单的例子可以只用于如果唯一的属性被设置为URI。如果您需要设置其他任何内容(凭证,模式等),则需要使用完整表格。

回购中的pattern属性实际上是一个数组,因此您可以使用YAML数组(或属性文件中的[0][1]等后缀)绑定到多个模式。如果要运行具有多个配置文件的应用程序,则可能需要这样做,如以下示例所示:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/spring-cloud-samples/config-repo
          repos:
            development:
              pattern:
                - '*/development'
                - '*/staging'
              uri: https://github.com/development/config-repo
            staging:
              pattern:
                - '*/qa'
                - '*/production'
              uri: https://github.com/staging/config-repo
[注意]注意

Spring Cloud猜测一个包含不以*结尾的配置文件的模式表示您实际上要匹配以该模式开头的配置文件列表(因此*/staging["*/staging", "*/staging,*"]的快捷方式,等等)。例如,这很普遍,例如,您需要在本地开发配置文件中运行应用程序,而又需要在远程配置文件中运行应用程序

每个存储库还可以选择将配置文件存储在子目录中,用于搜索这些目录的模式可以指定为searchPaths以下示例在顶层显示了一个配置文件:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/spring-cloud-samples/config-repo
          searchPaths: foo,bar*

在前面的示例中,服务器在顶层和foo/子目录中以及名称以bar开头的任何子目录中搜索配置文件。

默认情况下,首次请求配置时,服务器会克隆远程存储库。可以将服务器配置为在启动时克隆存储库,如以下顶级示例所示:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://git/common/config-repo.git
          repos:
            team-a:
                pattern: team-a-*
                cloneOnStart: true
                uri: https://git/team-a/config-repo.git
            team-b:
                pattern: team-b-*
                cloneOnStart: false
                uri: https://git/team-b/config-repo.git
            team-c:
                pattern: team-c-*
                uri: https://git/team-a/config-repo.git

在前面的示例中,服务器在接受任何请求之前会在启动时克隆team-a的config-repo。在请求从存储库进行配置之前,不会克隆所有其他存储库。

[注意]注意

设置要在Config Server启动时克隆的存储库有助于在Config Server启动时快速识别配置错误的配置源(例如无效的存储库URI)。在未为配置源启用cloneOnStart的情况下,Config Server可能会以配置错误或无效的配置源成功启动,并且直到应用程序从该配置源请求配置时才检测到错误。

认证方式

要在远程存储库上使用HTTP基本认证,请分别添加usernamepassword属性(不在URL中),如以下示例所示:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/spring-cloud-samples/config-repo
          username: trolley
          password: strongpassword

如果您不使用HTTPS和用户凭据,则在将密钥存储在默认目录(~/.ssh)中且URI指向SSH位置(例如[email protected]:configuration/cloud-configuration)时,SSH也应立即可用。重要的是,~/.ssh/known_hosts文件中应包含Git服务器的条目,并且其格式应为ssh-rsa不支持其他格式(例如ecdsa-sha2-nistp256)。为避免意外,您应确保Git服务器的known_hosts文件中仅存在一个条目,并且该条目与您提供给配置服务器的URL匹配。如果在URL中使用主机名,则要在known_hosts文件中完全使用该主机名(而不是IP)。使用JGit访问存储库,因此您找到的任何文档都应该适用。HTTPS代理设置可以在~/.git/config中设置,也可以(使用与其他JVM进程相同的方式)使用系统属性(-Dhttps.proxyHost-Dhttps.proxyPort)进行设置。

[提示]提示

如果您不知道~/.git目录在哪里,请使用git config --global来操纵设置(例如,git config --global http.sslVerify false)。

使用AWS CodeCommit进行身份验证

Spring Cloud Config服务器还支持AWS CodeCommit身份验证。从命令行使用Git时,AWS CodeCommit使用身份验证帮助程序。该帮助程序未与JGit库一起使用,因此,如果Git URI与AWS CodeCommit模式匹配,则会为AWS CodeCommit创建一个JGit CredentialProvider。AWS CodeCommit URI遵循以下模式://git-codecommit.${AWS_REGION}.amazonaws.com/${repopath}。

如果您提供带有AWS CodeCommit URI的用户名和密码,则它们必须是提供对存储库访问权限AWS accessKeyId和secretAccessKey如果您未指定用户名和密码,则使用AWS Default Credential Provider链检索accessKeyId和secretAccessKey

如果您的Git URI与CodeCommit URI模式(如前所示)匹配,则必须在用户名和密码或默认凭据提供商链支持的位置之一中提供有效的AWS凭据。AWS EC2实例可以将IAM角色用于EC2实例

[注意]注意

aws-java-sdk-core jar是可选的依赖项。如果aws-java-sdk-core jar不在您的类路径中,则无论git服务器URI如何,都不会创建AWS Code Commit凭证提供程序。

使用属性进行Git SSH配置

默认情况下,当使用SSH URI连接到Git存储库时,Spring Cloud Config服务器使用的JGit库使用SSH配置文件,例如~/.ssh/known_hosts/etc/ssh/ssh_config在Cloud Foundry之类的云环境中,本地文件系统可能是临时的,或者不容易访问。在这种情况下,可以使用Java属性设置SSH配置。为了激活基于属性的SSH配置,必须将spring.cloud.config.server.git.ignoreLocalSshSettings属性设置为true,如以下示例所示:

  spring:
    cloud:
      config:
        server:
          git:
            uri: git@gitserver.com:team/repo1.git
            ignoreLocalSshSettings: true
            hostKey: someHostKey
            hostKeyAlgorithm: ssh-rsa
            privateKey: |
                         -----BEGIN RSA PRIVATE KEY-----
                         MIIEpgIBAAKCAQEAx4UbaDzY5xjW6hc9jwN0mX33XpTDVW9WqHp5AKaRbtAC3DqX
                         IXFMPgw3K45jxRb93f8tv9vL3rD9CUG1Gv4FM+o7ds7FRES5RTjv2RT/JVNJCoqF
                         ol8+ngLqRZCyBtQN7zYByWMRirPGoDUqdPYrj2yq+ObBBNhg5N+hOwKjjpzdj2Ud
                         1l7R+wxIqmJo1IYyy16xS8WsjyQuyC0lL456qkd5BDZ0Ag8j2X9H9D5220Ln7s9i
                         oezTipXipS7p7Jekf3Ywx6abJwOmB0rX79dV4qiNcGgzATnG1PkXxqt76VhcGa0W
                         DDVHEEYGbSQ6hIGSh0I7BQun0aLRZojfE3gqHQIDAQABAoIBAQCZmGrk8BK6tXCd
                         fY6yTiKxFzwb38IQP0ojIUWNrq0+9Xt+NsypviLHkXfXXCKKU4zUHeIGVRq5MN9b
                         BO56/RrcQHHOoJdUWuOV2qMqJvPUtC0CpGkD+valhfD75MxoXU7s3FK7yjxy3rsG
                         EmfA6tHV8/4a5umo5TqSd2YTm5B19AhRqiuUVI1wTB41DjULUGiMYrnYrhzQlVvj
                         5MjnKTlYu3V8PoYDfv1GmxPPh6vlpafXEeEYN8VB97e5x3DGHjZ5UrurAmTLTdO8
                         +AahyoKsIY612TkkQthJlt7FJAwnCGMgY6podzzvzICLFmmTXYiZ/28I4BX/mOSe
                         pZVnfRixAoGBAO6Uiwt40/PKs53mCEWngslSCsh9oGAaLTf/XdvMns5VmuyyAyKG
                         ti8Ol5wqBMi4GIUzjbgUvSUt+IowIrG3f5tN85wpjQ1UGVcpTnl5Qo9xaS1PFScQ
                         xrtWZ9eNj2TsIAMp/svJsyGG3OibxfnuAIpSXNQiJPwRlW3irzpGgVx/AoGBANYW
                         dnhshUcEHMJi3aXwR12OTDnaLoanVGLwLnkqLSYUZA7ZegpKq90UAuBdcEfgdpyi
                         PhKpeaeIiAaNnFo8m9aoTKr+7I6/uMTlwrVnfrsVTZv3orxjwQV20YIBCVRKD1uX
                         VhE0ozPZxwwKSPAFocpyWpGHGreGF1AIYBE9UBtjAoGBAI8bfPgJpyFyMiGBjO6z
                         FwlJc/xlFqDusrcHL7abW5qq0L4v3R+FrJw3ZYufzLTVcKfdj6GelwJJO+8wBm+R
                         gTKYJItEhT48duLIfTDyIpHGVm9+I1MGhh5zKuCqIhxIYr9jHloBB7kRm0rPvYY4
                         VAykcNgyDvtAVODP+4m6JvhjAoGBALbtTqErKN47V0+JJpapLnF0KxGrqeGIjIRV
                         cYA6V4WYGr7NeIfesecfOC356PyhgPfpcVyEztwlvwTKb3RzIT1TZN8fH4YBr6Ee
                         KTbTjefRFhVUjQqnucAvfGi29f+9oE3Ei9f7wA+H35ocF6JvTYUsHNMIO/3gZ38N
                         CPjyCMa9AoGBAMhsITNe3QcbsXAbdUR00dDsIFVROzyFJ2m40i4KCRM35bC/BIBs
                         q0TY3we+ERB40U8Z2BvU61QuwaunJ2+uGadHo58VSVdggqAo0BSkH58innKKt96J
                         69pcVH/4rmLbXdcmNYGm6iu+MlPQk4BUZknHSmVHIFdJ0EPupVaQ8RHT
                         -----END RSA PRIVATE KEY-----

下表描述了SSH配置属性。

表5.1。SSH配置Properties

Property名称备注

ignoreLocalSshSettings

如果为true,请使用基于属性的SSH配置而不是基于文件的SSH配置。必须设置为spring.cloud.config.server.git.ignoreLocalSshSettings而不是在存储库定义中。

私钥

有效的SSH私钥。如果ignoreLocalSshSettings为true并且Git URI为SSH格式,则必须设置。

hostKey

有效的SSH主机密钥。如果还设置了hostKeyAlgorithm,则必须设置。

hostKeyAlgorithm

ssh-dss, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or ecdsa-sha2-nistp521之一。如果还设置了hostKey,则必须设置。

strictHostKeyChecking

truefalse如果为false,请忽略主机密钥错误。

knownHostsFile

自定义.known_hosts文件的位置。

preferredAuthentications

覆盖服务器身份验证方法顺序。如果服务器在publickey方法之前进行了键盘交互式身份验证,则应该可以避免登录提示。


Git搜索路径中的占位符

Spring Cloud Config服务器还支持带有{application}{profile}(如果需要的话还有{label})占位符的搜索路径,如以下示例所示:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/spring-cloud-samples/config-repo
          searchPaths: '{application}'

上面的清单导致在存储库中搜索与目录(以及顶层)同名的文件。通配符在带有占位符的搜索路径中也有效(搜索中包括任何匹配的目录)。

强制拉动Git Repositories

如前所述,Spring Cloud Config服务器会复制远程git存储库,以防本地副本变脏(例如,操作系统进程更改了文件夹内容),使得Spring Cloud Config服务器无法从远程更新本地副本。资料库。

要解决此问题,有一个force-pull属性,如果本地副本脏了,则可以使Spring Cloud Config服务器从远程存储库强制拉出,如以下示例所示:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/spring-cloud-samples/config-repo
          force-pull: true

如果您有多个存储库配置,则可以为每个存储库配置force-pull属性,如以下示例所示:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://git/common/config-repo.git
          force-pull: true
          repos:
            team-a:
                pattern: team-a-*
                uri: https://git/team-a/config-repo.git
                force-pull: true
            team-b:
                pattern: team-b-*
                uri: https://git/team-b/config-repo.git
                force-pull: true
            team-c:
                pattern: team-c-*
                uri: https://git/team-a/config-repo.git
[注意]注意

force-pull属性的默认值为false

删除Git Repositories中未跟踪的分支

由于Spring Cloud Config服务器在检出分支到本地存储库(例如,通过标签获取属性)后具有远程git存储库的克隆,因此它将永久保留该分支,直到下一个服务器重新启动(这将创建新的本地存储库)。因此,可能会删除远程分支,但仍可获取其本地副本。而且,如果Spring Cloud Config服务器客户端服务以--spring.cloud.config.label=deletedRemoteBranch,master开头,它将从deletedRemoteBranch本地分支获取属性,而不是从master获取属性。

为了使本地存储库分支保持整洁并保持远程状态-可以设置deleteUntrackedBranches属性。这将使Spring Cloud Config服务器从本地存储库中强制删除未跟踪的分支。例:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/spring-cloud-samples/config-repo
          deleteUntrackedBranches: true
[注意]注意

deleteUntrackedBranches属性的默认值为false

Git刷新率

您可以使用spring.cloud.config.server.git.refreshRate控制配置服务器多久从Git后端获取更新的配置数据。以秒为单位指定此属性的值。默认情况下,该值为0,这意味着配置服务器将在每次请求时从Git存储库中获取更新的配置。

5.1.2版本控制后端文件系统使用

[警告]警告

使用基于VCS的后端(git,svn),文件被检出或克隆到本地文件系统。默认情况下,它们以config-repo-为前缀放在系统临时目录中。例如,在Linux上,它可能是/tmp/config-repo-<randomid>一些操作系统通常会清除临时目录。这可能导致意外行为,例如缺少属性。为避免此问题,请通过将spring.cloud.config.server.git.basedirspring.cloud.config.server.svn.basedir设置为不在系统临时结构中的目录来更改Config Server使用的目录。

5.1.3文件系统后端

Config Server中还有一个本机配置文件,该配置文件不使用Git,而是从本地类路径或文件系统(您要使用spring.cloud.config.server.native.searchLocations指向的任何静态URL)加载配置文件。要使用本机配置文件,请使用spring.profiles.active=native启动Config Server。

[注意]注意

请记住对文件资源使用file:前缀(默认情况下,没有前缀的通常是类路径)。与任何Spring Boot配置一样,您可以嵌入${}样式的环境占位符,但是请记住,Windows中的绝对路径需要额外的/(例如,file:///${user.home}/config-repo)。

[警告]警告

searchLocations的默认值与本地Spring Boot应用程序(即[classpath:/, classpath:/config, file:./, file:./config])相同。这不会将application.properties从服务器公开给所有客户端,因为服务器中存在的所有属性源在被发送到客户端之前都已被删除。

[提示]提示

文件系统后端非常适合快速入门和测试。要在生产环境中使用它,您需要确保文件系统可靠并且可以在Config Server的所有实例之间共享。

搜索位置可以包含{application}{profile}{label}的占位符。这样,您可以隔离路径中的目录并选择一种对您有意义的策略(例如,每个应用程序的子目录或每个配置文件的子目录)。

如果在搜索位置中不使用占位符,则此存储库还将HTTP资源的{label}参数附加到搜索路径上的后缀,因此将从每个搜索位置与该名称相同的子目录加载属性文件。标签(在Spring环境中,带有标签的属性优先)。因此,没有占位符的默认行为与添加以/{label}/结尾的搜索位置相同。例如,file:/tmp/configfile:/tmp/config,file:/tmp/config/{label}相同。可以通过设置spring.cloud.config.server.native.addLabelLocations=false来禁用此行为。

5.1.4 Vault后端

Spring Cloud Config服务器还支持Vault作为后端。

有关Vault的更多信息,请参见Vault快速入门指南

要使配置服务器能够使用Vault后端,您可以使用vault配置文件运行配置服务器。例如,在配置服务器的application.properties中,您可以添加spring.profiles.active=vault

默认情况下,配置服务器假定您的Vault服务器在http://127.0.0.1:8200下运行。它还假定后端的名称为secret,密钥为application所有这些默认值都可以在配置服务器的application.properties中进行配置。下表描述了可配置的Vault属性:

名称默认值

host

127.0.0.1

port

8200

scheme

http

backend

secret

defaultKey

application

profileSeparator

,

kvVersion

1

skipSslValidation

false

timeout

5

namespace

null

[重要]重要

上表中的所有属性必须以spring.cloud.config.server.vault为前缀,或放置在复合配置的正确Vault部分中。

所有可配置的属性都可以在org.springframework.cloud.config.server.environment.VaultEnvironmentProperties中找到。

Vault 0.10.0引入了版本化的键值后端(k / v后端版本2),该后端公开了与早期版本不同的API,现在它需要在安装路径和实际上下文路径之间使用data/并包装data对象中的秘密。设置kvVersion=2将考虑到这一点。

(可选)支持Vault企业版X-Vault-Namespace标头。要将其发送到Vault,请设置namespace属性。

在配置服务器运行时,您可以向服务器发出HTTP请求以从Vault后端检索值。为此,您需要Vault服务器的令牌。

首先,将一些数据放入您的Vault中,如以下示例所示:

$ vault kv put secret/application foo=bar baz=bam
$ vault kv put secret/myapp foo=myappsbar

其次,向配置服务器发出HTTP请求以检索值,如以下示例所示:

$ curl -X "GET" "http://localhost:8888/myapp/default" -H "X-Config-Token: yourtoken"

您应该看到类似于以下内容的响应:

{
   "name":"myapp",
   "profiles":[
      "default"
   ],
   "label":null,
   "version":null,
   "state":null,
   "propertySources":[
      {
         "name":"vault:myapp",
         "source":{
            "foo":"myappsbar"
         }
      },
      {
         "name":"vault:application",
         "source":{
            "baz":"bam",
            "foo":"bar"
         }
      }
   ]
}

多个Properties来源

使用Vault时,可以为您的应用程序提供多个属性源。例如,假设您已将数据写入Vault中的以下路径:

secret/myApp,dev
secret/myApp
secret/application,dev
secret/application

写入secret/application的Properties对使用Config Server的所有应用程序均可名称为myApp的应用程序将具有写入secret/myAppsecret/application的所有属性。myApp启用了dev配置文件时,写入上述所有路径的属性将可用,列表中第一个路径中的属性优先于其他属性。

5.1.5通过代理访问后端

配置服务器可以通过HTTP或HTTPS代理访问Git或Vault后端。通过proxy.httpproxy.https下的设置,可以为Git或Vault控制此行为。这些设置是针对每个存储库的,因此,如果您使用组合环境存储库,则必须分别为组合中的每个后端配置代理设置。如果使用的网络需要HTTP和HTTPS URL分别使用代理服务器,则可以为单个后端配置HTTP和HTTPS代理设置。

下表描述了HTTP和HTTPS代理的代理配置属性。所有这些属性都必须以proxy.httpproxy.https作为前缀。

表5.2。代理配置Properties

Property名称备注

主办

代理的主机。

港口

用于访问代理的端口。

nonProxyHosts

配置服务器应在代理外部访问的所有主机。如果同时为proxy.http.nonProxyHostsproxy.https.nonProxyHosts提供了值,则将使用proxy.http值。

用户名

用来验证代理的用户名。如果同时为proxy.http.usernameproxy.https.username提供了值,则将使用proxy.http值。

密码

用来验证代理的密码。如果同时为proxy.http.passwordproxy.https.password提供了值,则将使用proxy.http值。


以下配置使用HTTPS代理访问Git存储库。

spring:
  profiles:
    active: git
  cloud:
    config:
      server:
        git:
          uri: https://github.com/spring-cloud-samples/config-repo
          proxy:
            https:
              host: my-proxy.host.io
              password: myproxypassword
              port: '3128'
              username: myproxyusername
              nonProxyHosts: example.com

5.1.6与所有应用程序共享配置

所有应用程序之间的共享配置根据您采用的方法而异,如以下主题所述:

基于文件的Repositories

使用基于文件(git,svn和本机)的存储库,所有客户端应用程序之间共享文件名称为application*application.propertiesapplication.ymlapplication-*.properties等)的资源。您可以使用具有这些文件名的资源来配置全局默认值,并在必要时使它们被应用程序特定的文件覆盖。

#_property_overrides [属性覆盖]功能也可以用于设置全局默认值,允许使用占位符应用程序在本地覆盖它们。

[提示]提示

使用本机配置文件(本地文件系统后端),您应该使用不属于服务器自身配置的显式搜索位置。否则,默认搜索位置中的application*资源将被删除,因为它们是服务器的一部分。

Vault服务器

将Vault用作后端时,可以通过将配置放在secret/application中来与所有应用程序共享配置。例如,如果您运行以下Vault命令,则所有使用配置服务器的应用程序都将具有可用的属性foobaz

$ vault write secret/application foo=bar baz=bam

CredHub服务器

将CredHub用作后端时,可以通过将配置放在/application/中或将其放在应用程序的default配置文件中来与所有应用程序共享配置。例如,如果您运行以下CredHub命令,则使用配置服务器的所有应用程序将具有对它们可用的属性shared.color1shared.color2

credhub set --name "/application/profile/master/shared" --type=json
value: {"shared.color1": "blue", "shared.color": "red"}
credhub set --name "/my-app/default/master/more-shared" --type=json
value: {"shared.word1": "hello", "shared.word2": "world"}

5.1.7 JDBC后端

Spring Cloud Config服务器支持JDBC(关系数据库)作为配置属性的后端。您可以通过向类路径中添加spring-jdbc并使用jdbc配置文件或添加类型为JdbcEnvironmentRepository的bean来启用此功能。如果您在类路径上包括正确的依赖项(有关更多详细信息,请参见用户指南),Spring Boot将配置数据源。

数据库需要有一个名为PROPERTIES的表,该表具有名为APPLICATIONPROFILELABEL的列(通常具有Environment的含义),以及KEYVALUE,用于Properties样式的键和值对。Java中所有字段的类型均为String,因此您可以根据需要将它们设置为VARCHARProperty值的行为与来自名为{application}-{profile}.properties的Spring Boot属性文件的值的行为相同,包括所有加密和解密,它们将用作后处理步骤(也就是说,在存储库中直接执行)。

5.1.8 CredHub后端

Spring Cloud Config服务器支持CredHub作为配置属性的后端。您可以通过向Spring CredHub添加依赖项来启用此功能

pom.xml。 

<dependencies>
	<dependency>
		<groupId>org.springframework.credhub</groupId>
		<artifactId>spring-credhub-starter</artifactId>
	</dependency>
</dependencies>

以下配置使用双向TLS访问CredHub:

spring:
  profiles:
    active: credhub
  cloud:
    config:
      server:
        credhub:
          url: https://credhub:8844

属性应存储为JSON,例如:

credhub set --name "/demo-app/default/master/toggles" --type=json
value: {"toggle.button": "blue", "toggle.link": "red"}
credhub set --name "/demo-app/default/master/abs" --type=json
value: {"marketing.enabled": true, "external.enabled": false}

名称为spring.cloud.config.name=demo-app的所有客户端应用程序将具有以下属性:

{
    toggle.button: "blue",
    toggle.link: "red",
    marketing.enabled: true,
    external.enabled: false
}
[注意]注意

如果未指定配置文件,将使用default,而未指定标签时,将使用master作为默认值。注意:添加到application的值将由所有应用程序共享。

OAuth 2.0

您可以使用UAA作为提供程序通过OAuth 2.0进行身份验证

pom.xml。 

<dependencies>
	<dependency>
		<groupId>org.springframework.security</groupId>
		<artifactId>spring-security-config</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.security</groupId>
		<artifactId>spring-security-oauth2-client</artifactId>
	</dependency>
</dependencies>

以下配置使用OAuth 2.0和UAA访问CredHub:

spring:
  profiles:
    active: credhub
  cloud:
    config:
      server:
        credhub:
          url: https://credhub:8844
          oauth2:
            registration-id: credhub-client
  security:
    oauth2:
      client:
        registration:
          credhub-client:
            provider: uaa
            client-id: credhub_config_server
            client-secret: asecret
            authorization-grant-type: client_credentials
        provider:
          uaa:
            token-uri: https://uaa:8443/oauth/token
[注意]注意

使用的UAA客户ID的范围应为credhub.read

5.1.9复合环境Repositories

在某些情况下,您可能希望从多个环境存储库中提取配置数据。为此,您可以在配置服务器的应用程序属性或YAML文件中启用composite配置文件。例如,如果要从Subversion存储库以及两个Git存储库中提取配置数据,则可以为配置服务器设置以下属性:

spring:
  profiles:
    active: composite
  cloud:
    config:
      server:
        composite:
        -
          type: svn
          uri: file:///path/to/svn/repo
        -
          type: git
          uri: file:///path/to/rex/git/repo
        -
          type: git
          uri: file:///path/to/walter/git/repo

使用此配置,优先级由composite键下的存储库列出顺序确定。在上面的示例中,首先列出了Subversion存储库,因此在Subversion存储库中找到的值将覆盖在一个Git存储库中为同一属性找到的值。rex Git存储库中找到的值将在walter Git存储库中为相同属性找到的值之前使用。

如果只想从每种不同类型的存储库中提取配置数据,则可以在配置服务器的应用程序属性或YAML文件中启用相应的配置文件,而不启用composite配置文件。例如,如果要从单个Git存储库和单个HashiCorp Vault服务器中提取配置数据,则可以为配置服务器设置以下属性:

spring:
  profiles:
    active: git, vault
  cloud:
    config:
      server:
        git:
          uri: file:///path/to/git/repo
          order: 2
        vault:
          host: 127.0.0.1
          port: 8200
          order: 1

使用此配置,可以通过order属性确定优先级。您可以使用order属性为所有存储库指定优先级顺序。order属性的数值越低,优先级越高。存储库的优先级顺序有助于解决包含相同属性值的存储库之间的任何潜在冲突。

[注意]注意

如果您的复合环境包括上一个示例中的Vault服务器,则在对配置服务器的每个请求中都必须包含Vault令牌。请参阅Vault后端

[注意]注意

从环境存储库中检索值时,任何类型的故障都会导致整个组合环境的故障。

[注意]注意

使用复合环境时,所有存储库都包含相同的标签很重要。如果您的环境与前面的示例中的环境类似,并且您请求带有master标签的配置数据,但是Subversion存储库不包含名为master的分支,则整个请求将失败。

定制复合环境Repositories

除了使用Spring Cloud中的一个环境存储库之外,您还可以提供自己的EnvironmentRepository bean作为复合环境的一部分。为此,您的bean必须实现EnvironmentRepository接口。如果要在复合环境中控制自定义EnvironmentRepository的优先级,则还应该实现Ordered接口并覆盖getOrdered方法。如果未实现Ordered接口,则EnvironmentRepository的优先级最低。

5.1.10 Property覆盖

Config Server具有替代功能,使操作员可以为所有应用程序提供配置属性。应用程序使用常规的Spring Boot钩子不会意外更改重写的属性。要声明覆盖,请将名称/值对的映射添加到spring.cloud.config.server.overrides,如以下示例所示:

spring:
  cloud:
    config:
      server:
        overrides:
          foo: bar

前面的示例使作为配置客户端的所有应用程序读取foo=bar,而与它们自己的配置无关。

[注意]注意

配置系统不能强制应用程序以任何特定方式使用配置数据。因此,覆盖无法执行。但是,它们确实为Spring Cloud Config客户端提供了有用的默认行为。

[提示]提示

通常,可以使用反斜杠(\)来转义${,以逃避(并在客户端上解析)具有${}的Spring环境占位符。例如,除非应用程序提供自己的app.foo,否则\${app.foo:bar}解析为bar

[注意]注意

在YAML中,您不需要转义反斜杠本身。但是,在属性文件中,在服务器上配置替代时,确实需要转义反斜杠。

您可以通过在远程存储库中设置spring.cloud.config.overrideNone=true标志(默认为false),使客户端中所有替代的优先级更像默认值,让应用程序在环境变量或系统属性中提供自己的值。

5.2健康指标

Config Server带有运行状况指示器,用于检查配置的EnvironmentRepository是否正常工作。默认情况下,它会向EnvironmentRepository询问名为app的应用程序,default配置文件以及EnvironmentRepository实现提供的默认标签。

您可以配置运行状况指示器以检查更多应用程序以及自定义配置文件和自定义标签,如以下示例所示:

spring:
  cloud:
    config:
      server:
        health:
          repositories:
            myservice:
              label: mylabel
            myservice-dev:
              name: myservice
              profiles: development

您可以通过设置spring.cloud.config.server.health.enabled=false禁用运行状况指示器。

5.3安全性

您可以用对您有意义的任何方式来保护Config Server(从物理网络安全到OAuth2承载令牌),因为Spring Security和Spring Boot为许多安全措施提供了支持。

要使用默认的Spring Boot配置的HTTP基本安全性,请在类路径上包含Spring Security(例如,通过spring-boot-starter-security)。缺省值为user用户名和随机生成的密码。随机密码在实践中没有用,因此我们建议您配置密码(通过设置spring.security.user.password)并对其进行加密(有关如何操作的说明,请参见下文)。

5.4加密和解密

[重要]重要

要使用加密和解密功能,您需要在JVM中安装完整功能的JCE(默认情况下不包括)。您可以从Oracle 下载Java密码学扩展(JCE)无限强度辖区策略文件并按照安装说明进行操作(本质上,您需要用下载的JRE lib / security目录替换这两个策略文件)。

如果远程属性源包含加密的内容(值以{cipher}开头),则在通过HTTP发送给客户端之前,将对它们进行解密。此设置的主要优点是,当属性值处于静止 ”状态时(例如,在git存储库中),不需要使用纯文本格式如果无法解密某个值,则将其从属性源中删除,并使用相同的密钥添加一个附加属性,但附加前缀为invalid和一个表示不适用的值(通常为<n/a>)。这很大程度上是为了防止将密文用作密码并意外泄漏。

如果为配置客户端应用程序设置了远程配置存储库,则它可能包含与以下内容类似的application.yml

application.yml。 

spring:
  datasource:
    username: dbuser
    password: '{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ'

.properties文件中的加密值不能用引号引起来。否则,该值不会解密。以下示例显示了有效的值:

application.properties。 

spring.datasource.username: dbuser
spring.datasource.password: {cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ

您可以安全地将此纯文本推送到共享的git存储库,并且秘密密码仍然受到保护。

服务器还公开/encrypt/decrypt端点(假设这些端点是安全的,并且只能由授权代理访问)。如果您编辑远程配置文件,则可以使用Config Server通过POST到/encrypt端点来加密值,如以下示例所示:

$ curl localhost:8888/encrypt -d mysecret
682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
[注意]注意

如果您加密的值中包含需要URL编码的字符,则应对curl使用--data-urlencode选项以确保它们正确编码。

[提示]提示

确保不要在加密值中包含任何curl命令统计信息。将值输出到文件可以帮助避免此问题。

也可以通过/decrypt使用反向操作(前提是服务器配置了对称密钥或完整密钥对),如以下示例所示:

$ curl localhost:8888/decrypt -d 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
mysecret
[提示]提示

如果使用curl进行测试,请使用--data-urlencode(而不是-d)或设置显式的Content-Type: text/plain以确保在有特殊字符时,curl可以正确编码数据(“ +”特别棘手)。

在将加密的值放入YAML或属性文件之前,以及将其提交并将其推送到远程(可能不安全)存储之前,请获取加密的值并添加{cipher}前缀。

/encrypt/decrypt端点也都接受/*/{application}/{profiles}形式的路径,当客户端调用主应用程序时,可用于按应用程序(名称)和配置文件控制密码。环境资源。

[注意]注意

要以这种精细的方式控制密码,您还必须提供类型为TextEncryptorLocator@Bean,以按名称和配置文件创建不同的加密器。默认情况下不提供(所有加密使用相同的密钥)。

spring命令行客户端(安装了Spring Cloud CLI扩展名)也可以用于加密和解密,如以下示例所示:

$ spring encrypt mysecret --key foo
682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
$ spring decrypt --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
mysecret

要使用文件中的密钥(例如用于加密的RSA公钥),请在密钥值前添加“ @”并提供文件路径,如以下示例所示:

$ spring encrypt mysecret --key @${HOME}/.ssh/id_rsa.pub
AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+...
[注意]注意

--key参数是强制性的(尽管前缀为--)。

5.5密钥管理

Config Server可以使用对称(共享)密钥或非对称密钥(RSA密钥对)。非对称选择在安全性方面优越,但使用对称密钥通常更方便,因为它是在bootstrap.properties中配置的单个属性值。

要配置对称密钥,您需要将encrypt.key设置为秘密字符串(或使用ENCRYPT_KEY环境变量将其保留在纯文本配置文件之外)。

[注意]注意

您无法使用encrypt.key配置非对称密钥。

要配置非对称密钥,请使用密钥库(例如,由JDK附带的keytool实用程序创建的密钥库)。密钥库属性为encrypt.keyStore.*,其中*等于

Property描述

encrypt.keyStore.location

Contains a Resource location

encrypt.keyStore.password

Holds the password that unlocks the keystore

encrypt.keyStore.alias

Identifies which key in the store to use

encrypt.keyStore.type

The type of KeyStore to create. Defaults to jks.

加密是使用公钥完成的,解密需要私钥。因此,原则上,如果只想加密(并准备用私钥在本地解密值),则只能在服务器中配置公钥。实际上,您可能不希望在本地进行解密,因为它会将密钥管理过程分布在所有客户端上,而不是将其集中在服务器上。另一方面,如果您的配置服务器相对不安全并且只有少数客户端需要加密的属性,那么它可能是一个有用的选项。

5.6创建密钥库进行测试

要创建用于测试的密钥库,可以使用类似于以下内容的命令:

$ keytool -genkeypair -alias mytestkey -keyalg RSA \
  -dname "CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US" \
  -keypass changeme -keystore server.jks -storepass letmein
[注意]注意

使用JDK 11或更高版本时,使用上述命令时可能会收到以下警告。在这种情况下,您可能需要确保keypassstorepass值匹配。

Warning:  Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified -keypass value.

server.jks文件放入类路径中(例如),然后在您的bootstrap.yml中,为Config Server创建以下设置:

encrypt:
  keyStore:
    location: classpath:/server.jks
    password: letmein
    alias: mytestkey
    secret: changeme

5.7使用多个按键和按键旋转

除了加密属性值中的{cipher}前缀外,Config Server在(Base64编码的)密文开始之前查找零个或多个{name:value}前缀。密钥被传递到TextEncryptorLocator,后者可以执行为密码找到TextEncryptor所需的任何逻辑。如果已配置密钥库(encrypt.keystore.location),则默认定位器将查找具有key前缀提供的别名的密钥,其密文类似于以下内容:

foo:
  bar: `{cipher}{key:testkey}...`

定位器查找名为“ testkey”的键。也可以通过在前缀中使用{secret:…​}值来提供机密。但是,如果未提供,则默认为使用密钥库密码(这是在构建密钥库且未指定密钥时得到的密码)。如果确实提供了机密,则还应该使用自定义SecretLocator对机密进行加密。

当密钥仅用于加密几个字节的配置数据时(也就是说,它们未在其他地方使用),从密码的角度讲,几乎不需要旋转密钥。但是,您有时可能需要更改密钥(例如,在发生安全漏洞时)。在这种情况下,所有客户端都需要更改其源配置文件(例如,在git中),并在所有密码中使用新的{key:…​}前缀。请注意,客户端需要首先检查Config Server密钥库中的密钥别名是否可用。

[提示]提示

如果要让Config Server处理所有加密以及解密,则还可以将{name:value}前缀添加为发布到/encrypt端点的纯文本。

5.8服务加密的Properties

有时,您希望客户端在本地解密配置,而不是在服务器中进行解密。在这种情况下,如果提供encrypt.*配置来查找密钥,则仍然可以拥有/encrypt/decrypt端点,但是需要通过放置{10 /来显式关闭对传出属性的解密。 bootstrap.[yml|properties]中的}。如果您不关心端点,则在不配置键或启用标志的情况下都可以使用。

6.提供其他格式

来自环境端点的默认JSON格式非常适合Spring应用程序使用,因为它直接映射到Environment抽象。如果愿意,可以通过在资源路径中添加后缀(“ .yml”,“。yaml”或“ .properties”)来使用与YAML或Java属性相同的数据。对于不关心JSON终结点的结构或它们提供的额外元数据的应用程序来说,这可能很有用(例如,不使用Spring的应用程序可能会受益于此方法的简单性)。

YAML和属性表示形式还有一个附加标志(提供为名为resolvePlaceholders的布尔查询参数),用于指示应在输出中解析源文档中的占位符(以标准Spring ${…​}格式)在渲染之前,如果可能的话。对于不了解Spring占位符约定的消费者来说,这是一个有用的功能。

[注意]注意

使用YAML或属性格式存在一些限制,主要是与元数据的丢失有关。例如,JSON被构造为属性源的有序列表,其名称与该源相关。即使值的来源有多个来源,YAML和属性形式也会合并到一个映射中,并且原始来源文件的名称也会丢失。同样,YAML表示也不一定是后备存储库中YAML源的忠实表示。它由一系列平面属性来源构成,并且必须对密钥的形式进行假设。

7.提供纯文本

除了使用Environment抽象(或YAML或属性格式的抽象表示之一)之外,您的应用程序可能需要根据其环境量身定制的通用纯文本配置文件。Config Server通过位于/{application}/{profile}/{label}/{path}的附加终结点提供了这些终结点,其中applicationprofilelabel与常规环境终结点具有相同的含义,但是path是指向以下环境的路径文件名(例如log.xml)。该端点的源文件与环境端点的定位方式相同。属性和YAML文件使用相同的搜索路径。但是,不是汇总所有匹配资源,而是仅返回第一个要匹配的资源。

找到资源后,可通过对提供的应用程序名称,配置文件和标签使用有效的Environment来解析常规格式(${…​})的占位符。通过这种方式,资源端点与环境端点紧密集成在一起。考虑以下用于GIT或SVN存储库的示例:

application.yml
nginx.conf

nginx.conf如下所示:

server {
    listen              80;
    server_name         ${nginx.server.name};
}

application.yml像这样:

nginx:
  server:
    name: example.com
---
spring:
  profiles: development
nginx:
  server:
    name: develop.com

/foo/default/master/nginx.conf资源可能如下:

server {
    listen              80;
    server_name         example.com;
}

/foo/development/master/nginx.conf像这样:

server {
    listen              80;
    server_name         develop.com;
}
[注意]注意

与用于环境配置的源文件一样,profile用于解析文件名。因此,如果要使用特定于配置文件的文件,可以通过名为logback-development.xml(优先于logback.xml)的文件来解析/*/development/*/logback.xml

[注意]注意

如果不想提供label并让服务器使用默认标签,则可以提供useDefaultLabel请求参数。因此,default配置文件的前面的示例可以是/foo/default/nginx.conf?useDefaultLabel

8.嵌入配置服务器

Config Server最好作为独立应用程序运行。但是,如果需要,可以将其嵌入另一个应用程序。为此,请使用@EnableConfigServer批注。在这种情况下,名为spring.cloud.config.server.bootstrap的可选属性会很有用。它是一个标志,用于指示服务器是否应从其自己的远程存储库中进行配置。默认情况下,该标志为关闭状态,因为它会延迟启动。但是,当嵌入到另一个应用程序中时,以与其他任何应用程序相同的方式进行初始化是有意义的。spring.cloud.config.server.bootstrap设置为true时,还必须使用复合环境存储库配置例如

spring:
  application:
    name: configserver
  profiles:
    active: composite
  cloud:
    config:
      server:
        composite:
          - type: native
            search-locations: ${HOME}/Desktop/config
        bootstrap: true
[注意]注意

如果使用引导标志,则配置服务器需要在bootstrap.yml中配置其名称和存储库URI。

要更改服务器端点的位置,可以(可选)设置spring.cloud.config.server.prefix(例如,/config)以在前缀下提供资源。前缀应以/开头,但不能以/结尾。它应用于Config Server中的@RequestMappings(即,在Spring Boot server.servletPathserver.contextPath前缀之下)。

如果要直接从后端存储库(而不是从配置服务器)读取应用程序的配置,则基本上需要没有端点的嵌入式配置服务器。您可以不使用@EnableConfigServer注释(设置为spring.cloud.config.server.bootstrap=true)来完全关闭端点。

9.推送通知和Spring Cloud Bus

许多源代码存储库提供程序(例如Github,Gitlab,Gitea,Gitee,Gogs或Bitbucket)都通过Webhook通知您存储库中的更改。您可以通过提供者的用户界面将Webhook配置为URL和您感兴趣的一组事件。例如,Github使用POST到Webhook,其JSON主体包含提交列表和设置为push的标头(X-Github-Event)。如果在spring-cloud-config-monitor库上添加依赖项并在Config Server中激活Spring Cloud Bus,则会启用/monitor端点。

激活Webhook后,配置服务器将发送一个针对它认为可能已更改的应用程序的RefreshRemoteApplicationEvent变化检测可以被策略化。但是,默认情况下,它会查找与应用程序名称匹配的文件中的更改(例如,foo.properties面向foo应用程序,而application.properties面向所有应用程序)。当您要覆盖此行为时,使用的策略是PropertyPathNotificationExtractor,该策略接受请求标头和正文作为参数,并返回已更改文件路径的列表。

默认配置可以与Github,Gitlab,Gitea,Gitee,Gogs或Bitbucket一起使用。除了来自Github,Gitlab,Gitee或Bitbucket的JSON通知之外,您还可以通过使用path={application}模式的形式编码的正文参数POST到/monitor来触发更改通知。这样做会向匹配{application}模式(可以包含通配符)的应用程序广播。

[注意]注意

仅当在配置服务器和客户端应用程序中都激活了spring-cloud-bus时,才发送RefreshRemoteApplicationEvent

[注意]注意

默认配置还检测本地git存储库中的文件系统更改。在这种情况下,不使用Webhook。但是,一旦您编辑配置文件,就会广播刷新。

10. Spring Cloud Config客户

Spring Boot应用程序可以立即利用Spring Config Server(或应用程序开发人员提供的其他外部属性源)。它还选择了与Environment更改事件相关的一些其他有用功能。

10.1配置第一个引导程序

在类路径上具有Spring Cloud Config客户端的任何应用程序的默认行为如下:配置客户端启动时,它将绑定到配置服务器(通过spring.cloud.config.uri引导程序配置属性)并初始化Spring Environment(带有远程资源来源)。

此行为的最终结果是,所有要使用Config Server的客户端应用程序都需要一个bootstrap.yml(或环境变量),其服务器地址设置为spring.cloud.config.uri(默认为“ http:// localhost” :8888“)。

10.2发现第一引导程序

如果使用DiscoveryClient实现,例如Spring Cloud Netflix和Eureka Service Discovery或Spring Cloud Consul,则可以让Config Server向Discovery Service注册。但是,在默认的Config First模式下,客户端无法利用注册。

如果您更喜欢使用DiscoveryClient来查找配置服务器,则可以通过设置spring.cloud.config.discovery.enabled=true(默认值为false)来进行。这样做的最终结果是,所有客户端应用程序都需要具有适当发现配置的bootstrap.yml(或环境变量)。例如,对于Spring Cloud Netflix,您需要定义Eureka服务器地址(例如,在eureka.client.serviceUrl.defaultZone中)。使用此选项的价格是启动时需要进行额外的网络往返,以查找服务注册。好处是,只要发现服务是固定点,配置服务器就可以更改其坐标。默认服务ID是configserver,但是您可以通过设置spring.cloud.config.discovery.serviceId在客户端上(以及在服务器上,以一种通常的服务方式,例如通过设置spring.application.name)来更改该ID。

发现客户端实现均支持某种元数据映射(例如,对于Eureka,我们有eureka.instance.metadataMap)。Config Server的某些其他属性可能需要在其服务注册元数据中进行配置,以便客户端可以正确连接。如果Config Server受HTTP Basic保护,则可以将凭据配置为userpassword另外,如果Config Server具有上下文路径,则可以设置configPath例如,以下YAML文件适用于作为Eureka客户端的Config Server:

bootstrap.yml。 

eureka:
  instance:
    ...
    metadataMap:
      user: osufhalskjrtl
      password: lviuhlszvaorhvlo5847
      configPath: /config

10.3配置客户端快速失败

在某些情况下,如果服务无法连接到Config Server,您可能希望启动失败。如果这是期望的行为,请设置引导程序配置属性spring.cloud.config.fail-fast=true,以使客户端因Exception而停止。

10.4配置客户端重试

如果您希望配置服务器在您的应用程序启动时偶尔会不可用,则可以使其在失败后继续尝试。首先,您需要设置spring.cloud.config.fail-fast=true然后,您需要将spring-retryspring-boot-starter-aop添加到类路径中。默认行为是重试六次,初始回退间隔为1000ms,随后的回退的指数乘数为1.1。您可以通过设置spring.cloud.config.retry.*配置属性来配置这些属性(和其他属性)。

[提示]提示

要完全控制重试行为,请添加ID为configServerRetryInterceptor@Bean类型的@BeanSpring重试有一个RetryInterceptorBuilder,它支持创建一个。

10.5查找远程配置资源

Config Service提供来自/{application}/{profile}/{label}的属性源,其中客户端应用程序中的默认绑定如下:

  • “名称” = ${spring.application.name}
  • “个人资料” = ${spring.profiles.active}(实际上是Environment.getActiveProfiles()
  • “ label” =“大师”
[注意]注意

设置属性${spring.application.name}时,请勿在您的应用名称前加上保留字application-,以防止解析正确的属性源时出现问题。

您可以通过设置spring.cloud.config.*(其中*nameprofilelabel)来覆盖所有参数。label对于回滚到以前的配置版本很有用。使用默认的Config Server实现,它可以是git标签,分支名称或提交ID。标签也可以以逗号分隔的列表形式提供。在这种情况下,列表中的项目将一一尝试直到成功为止。在要素分支上工作时,此行为可能很有用。例如,您可能想使配置标签与分支对齐,但使其成为可选(在这种情况下,请使用spring.cloud.config.label=myfeature,develop)。

10.6为配置服务器指定多个地址

为确保在部署了Config Server的多个实例时并希望不时有一个或多个实例不可用时的高可用性,可以指定多个URL(作为spring.cloud.config.uri属性下的逗号分隔列表)或您的所有实例都在服务注册表中注册,例如Eureka(如果使用Discovery-First Bootstrap模式)。请注意,只有在未运行Config Server时(即,应用程序退出时)或发生连接超时时,这样做才能确保高可用性。例如,如果Config Server返回500(内部服务器错误)响应,或者Config Client从Config Server收到401(由于凭据错误或其他原因),则Config Client不会尝试从其他URL提取属性。此类错误表示用户问题,而不是可用性问题。

如果您在Config Server上使用HTTP基本安全性,则仅当将凭据嵌入在spring.cloud.config.uri属性下指定的每个URL中时,当前才有可能支持per-Config Server身份验证凭据。如果使用任何其他类型的安全性机制,则(当前)不能支持每台配置服务器的身份验证和授权。

10.7配置超时

如果要配置超时阈值:

  • 可以使用属性spring.cloud.config.request-read-timeout配置读取超时。
  • 可以使用属性spring.cloud.config.request-connect-timeout配置连接超时。

10.8安全性

如果在服务器上使用HTTP基本安全性,则客户端需要知道密码(如果不是默认用户名,则需要用户名)。您可以通过配置服务器URI或通过单独的用户名和密码属性来指定用户名和密码,如以下示例所示:

bootstrap.yml。 

spring:
  cloud:
    config:
     uri: https://user:[email protected]

以下示例显示了传递相同信息的另一种方法:

bootstrap.yml。 

spring:
  cloud:
    config:
     uri: https://myconfig.mycompany.com
     username: user
     password: secret

spring.cloud.config.passwordspring.cloud.config.username值会覆盖URI中提供的任何内容。

如果您在Cloud Foundry上部署应用程序,则提供密码的最佳方法是通过服务凭据(例如URI中的密码),因为它不需要在配置文件中。以下示例在本地工作,并且适用于名为configserver的Cloud Foundry上的用户提供的服务:

bootstrap.yml。 

spring:
  cloud:
    config:
     uri: ${vcap.services.configserver.credentials.uri:http://user:[email protected]:8888}

如果您使用另一种形式的安全性,则可能需要向ConfigServicePropertySourceLocator提供一个RestTemplate(例如,通过在引导上下文中进行抓取并将其注入)。ConfigServicePropertySourceLocator 提供一个{848 /}(例如,通过在引导上下文中进行抓取并将其注入)。

10.8.1健康指标

Config Client提供Spring Boot运行状况指示器,该指示器尝试从Config Server加载配置。可以通过设置health.config.enabled=false禁用运行状况指示器。由于性能原因,响应也被缓存。默认的生存时间为5分钟。要更改该值,请设置health.config.time-to-live属性(以毫秒为单位)。

10.8.2提供自定义的RestTemplate

在某些情况下,您可能需要自定义来自客户端对配置服务器的请求。通常,这样做涉及传递特殊的Authorization标头以验证对服务器的请求。提供自定义RestTemplate

  1. 创建一个新的配置bean,实现为PropertySourceLocator,如以下示例所示:

CustomConfigServiceBootstrapConfiguration.java。 

@Configuration
public class CustomConfigServiceBootstrapConfiguration {
    @Bean
    public ConfigServicePropertySourceLocator configServicePropertySourceLocator() {
        ConfigClientProperties clientProperties = configClientProperties();
       ConfigServicePropertySourceLocator configServicePropertySourceLocator =  new ConfigServicePropertySourceLocator(clientProperties);
        configServicePropertySourceLocator.setRestTemplate(customRestTemplate(clientProperties));
        return configServicePropertySourceLocator;
    }
}

  1. resources/META-INF中,创建一个名为spring.factories的文件并指定您的自定义配置,如以下示例所示:

spring.factories.  

org.springframework.cloud.bootstrap.BootstrapConfiguration = com.my.config.client.CustomConfigServiceBootstrapConfiguration

10.8.3 Vault

当使用Vault作为配置服务器的后端时,客户端需要为服务器提供令牌以从Vault中检索值。可以通过在bootstrap.yml中设置spring.cloud.config.token在客户端中提供此令牌,如以下示例所示:

bootstrap.yml。 

spring:
  cloud:
    config:
      token: YourVaultToken

10.9 Vault中的嵌套键

Vault支持将键嵌套在Vault中存储的值中的功能,如以下示例所示:

echo -n '{"appA": {"secret": "appAsecret"}, "bar": "baz"}' | vault write secret/myapp -

此命令将JSON对象写入您的Vault。要访问Spring中的这些值,您将使用传统的dot(.)批注,如以下示例所示

@Value("${appA.secret}")
String name = "World";

前面的代码会将name变量的值设置为appAsecret

第三部分 Spring Cloud Netflix

Greenwich SR5

该项目通过自动配置并绑定到Spring环境和其他Spring编程模型习惯用法,为Spring Boot应用提供了Netflix OSS集成。使用一些简单的批注,您可以快速启用和配置应用程序内部的通用模式,并使用经过测试的Netflix组件构建大型分布式系统。提供的模式包括服务发现(Eureka),断路器(Hystrix),智能路由(Zuul)和客户端负载平衡(Ribbon)。

11.服务发现:Eureka个客户端

服务发现是基于微服务的体系结构的主要宗旨之一。尝试手动配置每个客户端或某种形式的约定可能很困难并且很脆弱。Eureka是Netflix Service Discovery服务器和客户端。可以将服务器配置和部署为高可用性,每个服务器将有关已注册服务的状态复制到其他服务器。

11.1如何包括Eureka客户

要将Eureka客户端包括在您的项目中,请使用启动器,其组ID为org.springframework.cloud,工件ID为​​spring-cloud-starter-netflix-eureka-client有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面

11.2向Eureka注册

当客户端向Eureka注册时,它会提供有关其自身的元数据-例如主机,端口,运行状况指示器URL,主页和其他详细信息。Eureka从属于服务的每个实例接收心跳消息。如果心跳在可配置的时间表上进行故障转移,则通常会将实例从注册表中删除。

以下示例显示了最小的Eureka客户端应用程序:

@SpringBootApplication
@RestController
public class Application {

    @RequestMapping("/")
    public String home() {
        return "Hello world";
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

}

请注意,前面的示例显示了普通的Spring Boot应用程序。通过在类路径上使用spring-cloud-starter-netflix-eureka-client,您的应用程序将自动在Eureka服务器中注册。如下例所示,需要进行配置才能找到Eureka服务器:

application.yml。 

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

在前面的示例中,defaultZone是一个魔术字符串后备值,它为任何不表达首选项的客户端提供服务URL(换句话说,这是一个有用的默认值)。

[警告]警告

defaultZone属性区分大小写,并且需要使用驼峰式大小写,因为serviceUrl属性是Map<String, String>因此,defaultZone属性不遵循default-zone的常规Spring Boot蛇形惯例。

默认应用程序名称(即服务ID),虚拟主机和非安全端口(从Environment获取)分别为${spring.application.name}${spring.application.name}${server.port}

在类路径上具有spring-cloud-starter-netflix-eureka-client可使应用程序同时进入Eureka 实例(即,它自己注册)和客户端(它可以查询注册表以定位其他服务)。实例行为由eureka.instance.*配置键驱动,但是如果确保您的应用程序具有spring.application.name的值(这是Eureka服务ID或VIP的默认值),则默认值很好。

有关配置选项的更多详细信息,请参见EurekaInstanceConfigBeanEurekaClientConfigBean

要禁用Eureka Discovery Client,可以将eureka.client.enabled设置为falsespring.cloud.discovery.enabled设置为false时,Eureka Discovery Client也将被禁用。

11.3通过Eureka服务器进行身份验证

如果其中一个eureka.client.serviceUrl.defaultZone URL内嵌了凭据,则HTTP基本身份验证会自动添加到您的eureka客户端(卷曲样式,如下:http://user:[email protected]:8761/eureka)。对于更复杂的需求,您可以创建类型为DiscoveryClientOptionalArgs@Bean并将ClientFilter实例注入其中,所有这些实例都应用于从客户端到服务器的调用。

[注意]注意

由于Eureka中的限制,无法支持每服务器的基本身份验证凭据,因此仅使用找到的第一组凭据。

11.4状态页和运行状况指示器

Eureka实例的状态页和运行状况指示器分别默认为/info/health,这是Spring Boot Actuator应用程序中有用端点的默认位置。即使您使用非默认上下文路径或Servlet路径(例如server.servletPath=/custom),也需要更改这些内容,即使对于Actuator应用程序也是如此。下面的示例显示两个设置的默认值:

application.yml。 

eureka:
  instance:
    statusPageUrlPath: ${server.servletPath}/info
    healthCheckUrlPath: ${server.servletPath}/health

这些链接显示在客户端使用的元数据中,并在某些情况下用于确定是否将请求发送到您的应用程序,因此,如果请求准确,将很有帮助。

[注意]注意

在Dalston中,还需要在更改该管理上下文路径时设置状态和运行状况检查URL。从Edgware开始就删除了此要求。

11.5注册安全的应用程序

如果您希望通过HTTPS与您的应用进行联系,则可以在EurekaInstanceConfig中设置两个标志:

  • eureka.instance.[nonSecurePortEnabled]=[false]
  • eureka.instance.[securePortEnabled]=[true]

这样做会使Eureka发布实例信息,该实例信息显示出对安全通信的明确偏好。对于以这种方式配置的服务,Spring Cloud DiscoveryClient始终返回以https开头的URI。同样,以这种方式配置服务时,Eureka(本机)实例信息具有安全的运行状况检查URL。

由于Eureka在内部工作的方式,它仍然会为状态和主页发布非安全URL,除非您也明确地覆盖了它们。您可以使用占位符来配置eureka实例URL,如以下示例所示:

application.yml。 

eureka:
  instance:
    statusPageUrl: https://${eureka.hostname}/info
    healthCheckUrl: https://${eureka.hostname}/health
    homePageUrl: https://${eureka.hostname}/

(请注意,${eureka.hostname}是本机占位符,仅在Eureka的更高版本中可用。您也可以使用Spring占位符来实现相同的目的,例如,使用${eureka.instance.hostName}。)

[注意]注意

如果您的应用程序在代理后面运行,并且SSL终止在代理中(例如,如果您在Cloud Foundry或其他平台中作为服务运行),则需要确保拦截代理的转发标头并由应用处理。如果嵌入在Spring Boot应用程序中的Tomcat容器具有针对'X-Forwarded-\ *'标头的显式配置,则此操作自动发生。应用程序提供的指向自身的链接错误(错误的主机,端口或协议)表明此配置错误。

11.6 Eureka的健康检查

默认情况下,Eureka使用客户端心跳来确定客户端是否启动。除非另有说明,否则发现客户端不会根据Spring Boot Actuator传播应用程序的当前运行状况检查状态。因此,在成功注册之后,Eureka始终宣布该应用程序处于“启动”状态。可以通过启用Eureka运行状况检查来更改此行为,这将导致应用程序状态传播到Eureka。结果,所有其他应用程序都不会将流量发送到处于“ UP”状态以外的其他状态的应用程序。以下示例显示如何为客户端启用运行状况检查:

application.yml。 

eureka:
  client:
    healthcheck:
      enabled: true

[警告]警告

eureka.client.healthcheck.enabled=true仅应在application.yml中设置。bootstrap.yml中设置该值会导致不良的副作用,例如在Eureka中以UNKNOWN状态注册。

如果您需要对运行状况检查进行更多控制,请考虑实施自己的com.netflix.appinfo.HealthCheckHandler

11.7 Eureka实例和客户端的元数据

值得花费一些时间来了解Eureka元数据的工作方式,因此您可以在平台上使用有意义的方式来使用它。有用于信息的标准元数据,例如主机名,IP地址,端口号,状态页和运行状况检查。这些都发布在服务注册表中,并由客户端用于以直接方式联系服务。可以将其他元数据添加到eureka.instance.metadataMap中的实例注册中,并且可以在远程客户端中访问此元数据。通常,除非使客户端知道元数据的含义,否则其他元数据不会更改客户端的行为。有几种特殊情况,在本文档的后面部分进行介绍,其中Spring Cloud已经为元数据映射分配了含义。

11.7.1在Cloud Foundry上使用Eureka

Cloud Foundry具有全局路由器,因此同一应用程序的所有实例都具有相同的主机名(其他具有类似体系结构的PaaS解决方案具有相同的排列)。这不一定是使用Eureka的障碍。但是,如果您使用路由器(建议或什至是强制性的,具体取决于平台的设置方式),则需要显式设置主机名和端口号(安全或不安全),以便它们使用路由器。您可能还希望使用实例元数据,以便可以区分客户端上的实例(例如,在自定义负载平衡器中)。默认情况下,eureka.instance.instanceIdvcap.application.instance_id,如以下示例所示:

application.yml。 

eureka:
  instance:
    hostname: ${vcap.application.uris[0]}
    nonSecurePort: 80

根据在Cloud Foundry实例中设置安全规则的方式,您可能可以注册并使用主机VM的IP地址进行直接的服务到服务的调用。Pivotal Web服务(PWS尚不提供此功能

11.7.2在AWS上使用Eureka

如果计划将应用程序部署到AWS云,则必须将Eureka实例配置为可感知AWS。您可以通过如下自定义EurekaInstanceConfigBean来实现:

@Bean
@Profile("!default")
public EurekaInstanceConfigBean eurekaInstanceConfig(InetUtils inetUtils) {
  EurekaInstanceConfigBean b = new EurekaInstanceConfigBean(inetUtils);
  AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild("eureka");
  b.setDataCenterInfo(info);
  return b;
}

11.7.3更改Eureka实例ID

一个普通的Netflix Eureka实例注册的ID等于其主机名(即,每个主机仅提供一项服务)。Spring Cloud Eureka提供了明智的默认值,其定义如下:

${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}}

一个示例是myhost:myappname:8080

通过使用Spring Cloud,可以通过在eureka.instance.instanceId中提供唯一标识符来覆盖此值,如以下示例所示:

application.yml。 

eureka:
  instance:
    instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}

通过前面示例中显示的元数据和在本地主机上部署的多个服务实例,在其中插入随机值以使实例唯一。在Cloud Foundry中,vcap.application.instance_id是在Spring Boot应用程序中自动填充的,因此不需要随机值。

11.8使用EurekaClient

一旦拥有作为发现客户端的应用程序,就可以使用它从Eureka服务器发现服务实例一种方法是使用本地com.netflix.discovery.EurekaClient(而不是Spring Cloud DiscoveryClient),如以下示例所示:

@Autowired
private EurekaClient discoveryClient;

public String serviceUrl() {
    InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);
    return instance.getHomePageUrl();
}
[提示]提示

请勿在@PostConstruct方法或@Scheduled方法(或可能尚未启动ApplicationContext的任何地方)中使用EurekaClient它是通过SmartLifecycle(带有phase=0)进行初始化的,因此最早可以依靠它的是处于更高阶段的另一个SmartLifecycle

11.8.1没有Jersey的EurekaClient

默认情况下,EurekaClient使用Jersey进行HTTP通信。如果希望避免来自Jersey的依赖关系,可以将其从依赖关系中排除。Spring Cloud基于Spring RestTemplate自动配置传输客户端。以下示例显示Jersey被排除在外:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-core</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.jersey.contribs</groupId>
            <artifactId>jersey-apache-client4</artifactId>
        </exclusion>
    </exclusions>
</dependency>

11.9替代本机Netflix EurekaClient

您无需使用原始Netflix EurekaClient而且,通常在某种包装器后面使用它会更方便。Spring Cloud 通过逻辑Eureka服务标识符(VIP)而非物理URL 支持Feign(REST客户端生成器)和Spring RestTemplate要使用固定的物理服务器列表配置Ribbon,可以将<client>.ribbon.listOfServers设置为以逗号分隔的物理地址(或主机名)列表,其中<client>是客户端的ID。

您还可以使用org.springframework.cloud.client.discovery.DiscoveryClient,它为发现客户端提供一个简单的API(非Netflix专用),如以下示例所示:

@Autowired
private DiscoveryClient discoveryClient;

public String serviceUrl() {
    List<ServiceInstance> list = discoveryClient.getInstances("STORES");
    if (list != null && list.size() > 0 ) {
        return list.get(0).getUri();
    }
    return null;
}

11.10为什么注册服务这么慢?

成为实例还涉及到注册表的定期心跳(通过客户端的serviceUrl),默认持续时间为30秒。直到实例,服务器和客户端在其本地缓存中都具有相同的元数据后,客户端才能发现该服务(因此可能需要3个心跳)。您可以通过设置eureka.instance.leaseRenewalIntervalInSeconds来更改周期。将其设置为小于30的值可以加快使客户端连接到其他服务的过程。在生产中,最好使用默认值,因为服务器中的内部计算对租约续订期进行了假设。

11.11区域

如果您已将Eureka客户端部署到多个区域,则您可能希望这些客户端在尝试使用其他区域中的服务之前先使用同一区域中的服务。要进行设置,您需要正确配置Eureka客户端。

首先,您需要确保已将Eureka服务器部署到每个区域,并且它们彼此对等。有关 更多信息,请参见区域和区域部分

接下来,您需要告诉Eureka服务位于哪个区域。您可以使用metadataMap属性来做到这一点。例如,如果将service 1部署到zone 1zone 2上,则需要在service 1中设置以下Eureka属性:

1区服务1

eureka.instance.metadataMap.zone = zone1
eureka.client.preferSameZoneEureka = true

2区服务1

eureka.instance.metadataMap.zone = zone2
eureka.client.preferSameZoneEureka = true

11.12刷新Eureka个客户端

默认情况下,EurekaClient bean是可刷新的,这意味着Eureka客户端属性可以更改和刷新。发生刷新时,客户端将从Eureka服务器中注销,并且可能会在短暂的时间内不提供给定服务的所有实例。消除这种情况的一种方法是禁用刷新Eureka客户端的功能。为此,请设置eureka.client.refresh.enable=false

12.服务发现:Eureka服务器

本节介绍如何设置Eureka服务器。

12.1如何包括Eureka服务器

要将Eureka服务器包含在您的项目中,请使用启动器,其组ID为org.springframework.cloud,工件ID为​​spring-cloud-starter-netflix-eureka-server有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面

[注意]注意

如果您的项目已经使用Thymeleaf作为模板引擎,则Eureka服务器的Freemarker模板可能无法正确加载。在这种情况下,必须手动配置模板加载器:

application.yml。 

spring:
  freemarker:
    template-loader-path: classpath:/templates/
    prefer-file-system-access: false

12.2如何运行Eureka服务器

以下示例显示了最小的Eureka服务器:

@SpringBootApplication
@EnableEurekaServer
public class Application {

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

}

该服务器具有一个主页,其中包含UI和HTTP API端点,用于/eureka/*下的常规Eureka功能。

以下链接提供了一些Eureka背景知识:磁通电容器google小组讨论

[提示]提示

由于Gradle的依赖性解析规则以及缺少父bom功能,取决于spring-cloud-starter-netflix-eureka-server可能会导致应用程序启动失败。要解决此问题,请添加Spring Boot Gradle插件并按如下所示导入Spring Cloud Starter父Bom:

build.gradle。 

buildscript {
  dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-docs-version}")
  }
}

apply plugin: "spring-boot"

dependencyManagement {
  imports {
    mavenBom "org.springframework.cloud:spring-cloud-dependencies:{spring-cloud-version}"
  }
}

12.3高可用性,区域和区域

Eureka服务器没有后端存储,但是注册表中的所有服务实例都必须发送心跳信号以使其注册保持最新(因此可以在内存中完成)。客户端还具有Eureka注册的内存缓存(因此,对于每个对服务的请求,它们都不必转到注册表)。

默认情况下,每个Eureka服务器也是Eureka客户端,并且需要(至少一个)服务URL来定位对等方。如果您不提供该服务,则该服务将运行并运行,但是它将使您的日志充满关于无法向对等方注册的噪音。

另请参阅下面的内容,了解客户端在Ribbon中对区域和区域的支持。

12.4独立模式

只要存在某种监视器或弹性运行时(例如Cloud Foundry),两个缓存(客户端和服务器)和心跳的组合就可以使独立的Eureka服务器对故障具有相当的恢复能力。在独立模式下,您可能希望关闭客户端行为,以使其不会继续尝试并无法到达其对等对象。下面的示例演示如何关闭客户端行为:

application.yml(独立Eureka服务器)。 

server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

请注意,serviceUrl指向与本地实例相同的主机。

12.5同行意识

通过运行多个实例并要求它们彼此注册,可以使Eureka更具弹性和可用性。实际上,这是默认行为,因此要使其正常工作,您需要做的就是向对等体添加有效的serviceUrl,如以下示例所示:

application.yml(两个对等感知Eureka服务器)。 

---
spring:
  profiles: peer1
eureka:
  instance:
    hostname: peer1
  client:
    serviceUrl:
      defaultZone: http://peer2/eureka/

---
spring:
  profiles: peer2
eureka:
  instance:
    hostname: peer2
  client:
    serviceUrl:
      defaultZone: http://peer1/eureka/

在前面的示例中,我们有一个YAML文件,该文件可以通过在不同的Spring配置文件中运行,在两个主机(peer1peer2)上运行同一服务器。您可以通过操纵/etc/hosts解析主机名来使用此配置来测试单个主机上的对等感知(在生产环境中这样做没有太大价值)。实际上,如果您在知道其主机名的计算机上运行,​​则不需要eureka.instance.hostname(默认情况下,使用java.net.InetAddress进行查找)。

您可以将多个对等方添加到系统,并且只要它们都通过至少一个边缘相互连接,它们就可以在彼此之间同步注册。如果对等方在物理上是分开的(在一个数据中心内部或在多个数据中心之间),则该系统原则上可以解决裂脑型故障。您可以将多个对等方添加到系统中,并且只要它们都直接相互连接,它们就可以在彼此之间同步注册。

application.yml(三个对等感知Eureka服务器)。 

eureka:
  client:
    serviceUrl:
      defaultZone: http://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/

---
spring:
  profiles: peer1
eureka:
  instance:
    hostname: peer1

---
spring:
  profiles: peer2
eureka:
  instance:
    hostname: peer2

---
spring:
  profiles: peer3
eureka:
  instance:
    hostname: peer3

12.6何时首选IP地址

在某些情况下,Eureka最好公布服务的IP地址而不是主机名。eureka.instance.preferIpAddress设置为true,并且当应用程序向eureka注册时,它将使用其IP地址而不是其主机名。

[提示]提示

如果Java无法确定主机名,则IP地址将发送到Eureka。设置主机名的唯一明确方法是设置eureka.instance.hostname属性。您可以在运行时使用环境变量(例如,eureka.instance.hostname=${HOST_NAME})设置主机名。

12.7保护Eureka服务器

您只需通过spring-boot-starter-security将Spring Security添加到服务器的类路径中即可保护Eureka服务器。默认情况下,当Spring Security在类路径上时,它将要求在每次向应用程序发送请求时都发送有效的CSRF令牌。Eureka客户通常不会拥有有效的跨站点请求伪造(CSRF)令牌,您需要为/eureka/**端点禁用此要求。例如:

@EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().ignoringAntMatchers("/eureka/**");
        super.configure(http);
    }
}

有关CSRF的更多信息,请参见Spring Security文档

可以在Spring Cloud示例存储库中找到Eureka演示服务器

12.8 JDK 11支持

Eureka服务器所依赖的JAXB模块已在JDK 11中删除。如果要在运行Eureka服务器时使用JDK 11,则必须在POM或Gradle文件中包括这些依赖项。

<dependency>
	<groupId>org.glassfish.jaxb</groupId>
	<artifactId>jaxb-runtime</artifactId>
</dependency>

13.断路器:Hystrix个客户

Netflix创建了一个名为Hystrix的库,该库实现了断路器模式在微服务架构中,通常有多个服务调用层,如以下示例所示:

图13.1。微服务图

Hystrix

较低级别的服务中的服务故障可能会导致级联故障,直至用户。metrics.rollingStats.timeInMilliseconds定义的滚动窗口中,当对特定服务的调用超过circuitBreaker.requestVolumeThreshold(默认:20个请求)并且失败百分比大于circuitBreaker.errorThresholdPercentage(默认:> 50%)时(默认:10秒) ),则电路断开并且无法进行呼叫。在错误和断路的情况下,开发人员可以提供备用功能。

图13.2 Hystrix后备可防止级联故障

HystrixFallback

开路可停止级联故障,并让不堪重负的服务时间得以恢复。后备可以是另一个受Hystrix保护的呼叫,静态数据或合理的空值。可以将回退链接在一起,以便第一个回退进行其他业务调用,然后回退到静态数据。

13.1如何包括Hystrix

要将Hystrix包含在您的项目中,请使用起始者,其组ID为org.springframework.cloud,工件ID为​​spring-cloud-starter-netflix-hystrix有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面

以下示例显示了具有Hystrix断路器的最小Eureka服务器:

@SpringBootApplication
@EnableCircuitBreaker
public class Application {

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(true).run(args);
    }

}

@Component
public class StoreIntegration {

    @HystrixCommand(fallbackMethod = "defaultStores")
    public Object getStores(Map<String, Object> parameters) {
        //do stuff that might fail
    }

    public Object defaultStores(Map<String, Object> parameters) {
        return /* something useful */;
    }
}

@HystrixCommand由一个名为javanica的Netflix contrib库提供Spring Cloud将带有注释的Spring beans自动包装在与Hystrix断路器连接的代理中。断路器计算何时断开和闭合电路,以及在发生故障时应采取的措施。

要配置@HystrixCommand,可以将commandProperties属性与@HystrixProperty批注一起使用。有关 更多详细信息,请参见 此处有关 可用属性的详细信息,请参见Hystrix Wiki

13.2传播安全上下文或使用Spring范围

如果要将某些线程本地上下文传播到@HystrixCommand中,则默认声明无效,因为默认声明在线程池中执行命令(如果超时)。通过要求Hystrix使用不同的隔离策略,可以通过配置或直接在批注中切换Hystrix来使用与调用方相同的线程下面的示例演示了如何在注释中设置线程:

@HystrixCommand(fallbackMethod = "stubMyService",
    commandProperties = {
      @HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
    }
)
...

如果使用@SessionScope@RequestScope,则同样适用。如果遇到运行时异常,提示它找不到范围内的上下文,则需要使用同一线程。

您还可以选择将hystrix.shareSecurityContext属性设置为true这样做会自动配置一个Hystrix并发策略插件挂钩,以将SecurityContext从您的主线程转移到Hystrix命令所使用的那个线程。Hystrix不允许注册多个Hystrix并发策略,因此可以通过将自己的HystrixConcurrencyStrategy声明为Spring bean来使用扩展机制。Spring Cloud在Spring上下文中寻找您的实现,并将其包装在自己的插件中。

13.3健康指标

连接的断路器的状态也显示在调用应用程序的/health端点中,如以下示例所示:

{
    "hystrix": {
        "openCircuitBreakers": [
            "StoreIntegration::getStoresByLocationLink"
        ],
        "status": "CIRCUIT_OPEN"
    },
    "status": "UP"
}

13.4 Hystrix指标流

要启用Hystrix指标流,请包括对spring-boot-starter-actuator的依赖性并设置management.endpoints.web.exposure.include: hystrix.stream这样做将/actuator/hystrix.stream作为管理端点公开,如以下示例所示:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

14.断路器:Hystrix仪表板

Hystrix的主要好处之一是它收集的有关每个HystrixCommand的一组度量。Hystrix仪表板以有效的方式显示每个断路器的运行状况。

图14.1 Hystrix资讯主页

Hystrix

15. Hystrix超时和Ribbon客户

当使用包裹Ribbon客户端的Hystrix命令时,您要确保Hystrix超时配置为比配置的Ribbon超时更长,包括可能进行的任何重试。例如,如果您的Ribbon连接超时是一秒钟,并且Ribbon客户端可能重试了3次请求,则Hystrix超时应该稍微超过3秒。

15.1如何包括Hystrix信息中心

要将Hystrix仪表板包含在您的项目中,请使用启动器,其组ID为org.springframework.cloud,工件ID为​​spring-cloud-starter-netflix-hystrix-dashboard有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面

要运行Hystrix信息中心,请用@EnableHystrixDashboard注释Spring Boot主类。然后访问/hystrix,并将仪表板指向Hystrix客户端应用程序中单个实例的/hystrix.stream端点。

[注意]注意

连接到使用HTTPS的/hystrix.stream端点时,JVM必须信任服务器使用的证书。如果证书不受信任,则必须将证书导入JVM,以使Hystrix仪表板成功连接到流终结点。

15.2 Turbine

从系统的整体运行状况来看,查看单个实例的Hystrix数据不是很有用。Turbine是一个应用程序,它将所有相关的/hystrix.stream端点聚合到一个组合的/turbine.stream中,以便在Hystrix仪表板中使用。个别实例通过Eureka定位。运行Turbine需要使用@EnableTurbine注释对您的主类进行注释(例如,通过使用spring-cloud-starter-netflix-turbine设置类路径)。Turbine 1 Wiki中记录的所有配置属性均适用。唯一的区别是turbine.instanceUrlSuffix不需要预先添加的端口,因为除非turbine.instanceInsertPort=false,否则它将自动处理。

[注意]注意

默认情况下,Turbine通过在Eureka中查找其hostNameport条目,然后将/hystrix.stream附加到已注册实例上来查找/hystrix.stream端点。如果实例的元数据包含management.port,则使用它代替/hystrix.stream端点的port值。默认情况下,名为management.port的元数据条目等于management.port配置属性。可以使用以下配置覆盖它:

eureka:
  instance:
    metadata-map:
      management.port: ${management.port:8081}

turbine.appConfig配置密钥是Eureka serviceId的列表,涡轮使用它们来查找实例。然后,在Hystrix仪表板中使用涡轮流,其URL类似于以下内容:

https://my.turbine.server:8080/turbine.stream?cluster=CLUSTERNAME

如果名称为default,则可以省略cluster参数。cluster参数必须与turbine.aggregator.clusterConfig中的条目匹配。从Eureka返回的值是大写的。因此,如果存在一个向Eureka注册的名为customers的应用程序,则以下示例可用:

turbine:
  aggregator:
    clusterConfig: CUSTOMERS
  appConfig: customers

如果您需要自定义Turbine应该使用哪些集群名称(因为您不想在turbine.aggregator.clusterConfig配置中存储集群名称),请提供类型为TurbineClustersProvider的bean。

clusterName可以通过turbine.clusterNameExpression中的SPEL表达式进行自定义,其中根目录为InstanceInfo的实例。默认值为appName,这意味着Eureka serviceId成为群集密钥(即,客户的InstanceInfoappNameCUSTOMERS)。一个不同的示例是turbine.clusterNameExpression=aSGName,它从AWS ASG名称获取集群名称。以下清单显示了另一个示例:

turbine:
  aggregator:
    clusterConfig: SYSTEM,USER
  appConfig: customers,stores,ui,admin
  clusterNameExpression: metadata['cluster']

在前面的示例中,来自四个服务的群集名称是从它们的元数据映射中拉出的,并且期望其值包括SYSTEMUSER

要将默认群集用于所有应用程序,您需要一个字符串文字表达式(如果在YAML中,也要使用单引号和双引号进行转义):

turbine:
  appConfig: customers,stores
  clusterNameExpression: "'default'"

Spring Cloud提供了spring-cloud-starter-netflix-turbine,它具有运行Turbine服务器所需的所有依赖关系。要添加Turbine,请创建一个Spring Boot应用程序并使用@EnableTurbine对其进行注释。

[注意]注意

默认情况下,Spring Cloud允许Turbine使用主机和端口以允许每个主机,每个集群多个进程。如果你想建立在本地Netflix的行为Turbine,以使每台主机的多个进程,每簇(关键实例ID是主机名),集合turbine.combineHostPort=false

15.2.1集群端点

在某些情况下,其他应用程序了解在Turbine中配置了哪些custers可能会很有用。为此,您可以使用/clusters端点,该端点将返回所有已配置集群的JSON数组。

GET /集群。 

[
  {
    "name": "RACES",
    "link": "http://localhost:8383/turbine.stream?cluster=RACES"
  },
  {
    "name": "WEB",
    "link": "http://localhost:8383/turbine.stream?cluster=WEB"
  }
]

可以通过将turbine.endpoints.clusters.enabled设置为false来禁用此端点。

15.3 Turbine Stream

在某些环境中(例如在PaaS设置中),从所有分布式Hystrix命令中提取指标的经典Turbine模型不起作用。在这种情况下,您可能想让Hystrix命令将指标推送到Turbine。Spring Cloud通过消息传递实现了这一点。要在客户端上执行此操作,请向spring-cloud-netflix-hystrix-stream和您选择的spring-cloud-starter-stream-*添加一个依赖项。有关代理以及如何配置客户端凭据的详细信息,请参见Spring Cloud Stream文档对于本地代理,它应该开箱即用。

在服务器端,创建一个Spring Boot应用程序,并用@EnableTurbineStream对其进行注释。Turbine Stream服务器需要使用Spring Webflux,因此,spring-boot-starter-webflux必须包含在您的项目中。spring-cloud-starter-netflix-turbine-stream添加到您的应用程序时,默认包含spring-boot-starter-webflux

然后,您可以将Hystrix仪表板指向Turbine Stream服务器,而不是单独的Hystrix流。如果Turbine Stream在myhost的端口8989上运行,则将http://myhost:8989放在Hystrix仪表板的流输入字段中。电路以其各自的serviceId为前缀,后跟一个点(.),然后是电路名称。

Spring Cloud提供了spring-cloud-starter-netflix-turbine-stream,其中包含使Turbine Stream服务器运行所需的所有依赖项。然后,您可以添加您选择的流绑定程序,例如spring-cloud-starter-stream-rabbit

Turbine Stream服务器还支持cluster参数。与Turbine服务器不同,Turbine Stream使用eureka serviceIds作为群集名称,并且这些名称不可配置。

如果Turbine Stream服务器在my.turbine.server的端口8989上运行,并且您的环境中有两个eureka serviceId customersproducts,则以下URL将在Turbine Stream服务器上可用。default和空群集名称将提供Turbine Stream服务器接收的所有度量。

https://my.turbine.sever:8989/turbine.stream?cluster=customers
https://my.turbine.sever:8989/turbine.stream?cluster=products
https://my.turbine.sever:8989/turbine.stream?cluster=default
https://my.turbine.sever:8989/turbine.stream

因此,您可以将eureka serviceIds用作Turbine仪表板(或任何兼容的仪表板)的群集名称。您无需为Turbine Stream服务器配置任何属性,例如turbine.appConfigturbine.clusterNameExpressionturbine.aggregator.clusterConfig

[注意]注意

Turbine Stream服务器使用Spring Cloud Stream从配置的输入通道中收集所有度量。这意味着它不会从每个实例中主动收集Hystrix指标。它仅可以提供每个实例已经收集到输入通道中的度量。

16.客户端负载平衡器:Ribbon

Ribbon是一种客户端负载平衡器,可让您对HTTP和TCP客户端的行为进行大量控制。Feign已使用Ribbon,因此,如果使用@FeignClient,则本节也适用。

Ribbon中的中心概念是指定客户的概念。每个负载均衡器都是组件的一部分,这些组件可以一起工作以按需联系远程服务器,并且该组件具有您作为应用程序开发人员提供的名称(例如,使用@FeignClient批注)。根据需要,Spring Cloud通过使用RibbonClientConfiguration为每个命名的客户端创建一个新的集合作为ApplicationContext其中包含ILoadBalancerRestClientServerListFilter

16.1如何包括Ribbon

要将Ribbon包含在您的项目中,请使用起始者,其组ID为org.springframework.cloud,工件ID为​​spring-cloud-starter-netflix-ribbon有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面

16.2自定义Ribbon客户端

您可以使用<client>.ribbon.*中的外部属性来配置Ribbon客户端的某些位,这与本地使用Netflix API相似,不同之处在于可以使用Spring Boot配置文件。可以将本机选项检查为CommonClientConfigKey(功能区核心的一部分)中的静态字段

Spring Cloud还允许您通过使用@RibbonClient声明其他配置(在RibbonClientConfiguration之上)来完全控制客户端,如以下示例所示:

@Configuration
@RibbonClient(name = "custom", configuration = CustomConfiguration.class)
public class TestConfiguration {
}

在这种情况下,客户端由RibbonClientConfiguration中已有的组件以及CustomConfiguration中的任何组件组成(其中后者通常会覆盖前者)。

[警告]警告

CustomConfiguration类必须是@Configuration类,但请注意,对于主应用程序上下文,它不在@ComponentScan中。否则,它由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免将其包括在内(例如,可以将其放在单独的,不重叠的程序包中,或指定要在@ComponentScan)。

下表显示了Spring Cloud Netflix默认为Ribbon提供的beans:

Bean类型Bean名称班级名称

IClientConfig

ribbonClientConfig

DefaultClientConfigImpl

IRule

ribbonRule

ZoneAvoidanceRule

IPing

ribbonPing

DummyPing

ServerList<Server>

ribbonServerList

ConfigurationBasedServerList

ServerListFilter<Server>

ribbonServerListFilter

ZonePreferenceServerListFilter

ILoadBalancer

ribbonLoadBalancer

ZoneAwareLoadBalancer

ServerListUpdater

ribbonServerListUpdater

PollingServerListUpdater

创建其中一种类型的bean并将其放置在@RibbonClient配置中(例如上述FooConfiguration),您可以覆盖所描述的每个beans,如以下示例所示:

@Configuration
protected static class FooConfiguration {

	@Bean
	public ZonePreferenceServerListFilter serverListFilter() {
		ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
		filter.setZone("myTestZone");
		return filter;
	}

	@Bean
	public IPing ribbonPing() {
		return new PingUrl();
	}

}

上一示例中的include语句将NoOpPing替换为PingUrl,并提供了自定义serverListFilter

16.3为所有Ribbon客户端自定义默认值

通过使用@RibbonClients批注并注册默认配置,可以为所有Ribbon客户端提供默认配置,如以下示例所示:

@RibbonClients(defaultConfiguration = DefaultRibbonConfig.class)
public class RibbonClientDefaultConfigurationTestsConfig {

	public static class BazServiceList extends ConfigurationBasedServerList {

		public BazServiceList(IClientConfig config) {
			super.initWithNiwsConfig(config);
		}

	}

}

@Configuration
class DefaultRibbonConfig {

	@Bean
	public IRule ribbonRule() {
		return new BestAvailableRule();
	}

	@Bean
	public IPing ribbonPing() {
		return new PingUrl();
	}

	@Bean
	public ServerList<Server> ribbonServerList(IClientConfig config) {
		return new RibbonClientDefaultConfigurationTestsConfig.BazServiceList(config);
	}

	@Bean
	public ServerListSubsetFilter serverListFilter() {
		ServerListSubsetFilter filter = new ServerListSubsetFilter();
		return filter;
	}

}

16.4通过设置Properties自定义Ribbon客户端

从版本1.2.0开始,Spring Cloud Netflix现在支持通过将属性设置为与Ribbon文档兼容来自定义Ribbon客户端

这使您可以在启动时在不同环境中更改行为。

以下列表显示了受支持的属性>:

  • <clientName>.ribbon.NFLoadBalancerClassName:应实施ILoadBalancer
  • <clientName>.ribbon.NFLoadBalancerRuleClassName:应实施IRule
  • <clientName>.ribbon.NFLoadBalancerPingClassName:应实施IPing
  • <clientName>.ribbon.NIWSServerListClassName:应实施ServerList
  • <clientName>.ribbon.NIWSServerListFilterClassName:应实施ServerListFilter
[注意]注意

这些属性中定义的类优先于使用@RibbonClient(configuration=MyRibbonConfig.class)定义的beans和Spring Cloud Netflix提供的默认值。

要为名为users的服务名称设置IRule,可以设置以下属性:

application.yml。 

users:
  ribbon:
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule

有关Ribbon提供的实现,请参见Ribbon文档

16.5将Ribbon与Eureka一起使用

当Eureka与Ribbon结合使用时(也就是说,两者都在类路径上),ribbonServerList被扩展名DiscoveryEnabledNIWSServerList覆盖,这将填充{71中的服务器列表/}。它还用NIWSDiscoveryPing替换了IPing接口,该接口委托给Eureka确定服务器是否启动。默认安装的ServerListDomainExtractingServerList其目的是不使用AWS AMI元数据(这就是Netflix所依赖的)使元数据可用于负载均衡器。默认情况下,服务器列表是使用实例元数据中提供的zone信息构建的(因此,在远程客户端上,设置为eureka.instance.metadataMap.zone)。如果缺少该字段,并且设置了approximateZoneFromHostname标志,则它可以使用服务器主机名中的域名作为该区域的代理。一旦区域信息可用,就可以在ServerListFilter中使用它。默认情况下,它用于在与客户端相同的区域中定位服务器,因为默认值为ZonePreferenceServerListFilter默认情况下,以与远程实例相同的方式(即通过eureka.instance.metadataMap.zone)确定客户端的区域。

[注意]注意

设置客户端区域的传统archaius方法是通过名为“ @zone”的配置属性。如果可用,Spring Cloud优先于所有其他设置使用该设置(请注意,该键必须在YAML配置中用引号引起来)。

[注意]注意

如果没有其他区域数据源,则根据客户端配置(而不是实例配置)进行猜测。我们取eureka.client.availabilityZones(这是从区域名称到区域列表的映射),然后为实例自己的区域拉出第一个区域(即eureka.client.region,其默认值为“ us-east-1” ”,以与本机Netflix兼容)。

16.6示例:如何在没有Eureka的情况下使用Ribbon

Eureka是一种抽象发现远程服务器的便捷方法,因此您不必在客户端中对它们的URL进行硬编码。但是,如果您不想使用Eureka,则Ribbon和Feign也可以使用。假设您为“商店”声明了@RibbonClient,并且Eureka未被使用(甚至不在类路径上)。Ribbon客户端默认为配置的服务器列表。您可以提供以下配置:

application.yml。 

stores:
  ribbon:
    listOfServers: example.com,google.com

16.7示例:在Ribbon中禁用Eureka

ribbon.eureka.enabled属性设置为false会显式禁用Ribbon中的Eureka,如以下示例所示:

application.yml。 

ribbon:
  eureka:
   enabled: false

16.8直接使用Ribbon API

您也可以直接使用LoadBalancerClient,如以下示例所示:

public class MyClass {
    @Autowired
    private LoadBalancerClient loadBalancer;

    public void doStuff() {
        ServiceInstance instance = loadBalancer.choose("stores");
        URI storesUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
        // ... do something with the URI
    }
}

16.9 Ribbon配置的缓存

每个Ribbon命名的客户端都有一个相应的子应用程序上下文,Spring Cloud维护该上下文。该应用程序上下文在对命名客户端的第一个请求上延迟加载。通过指定Ribbon客户端的名称,可以更改此延迟加载行为,以代替在启动时急于加载这些子应用程序上下文,如以下示例所示:

application.yml。 

ribbon:
  eager-load:
    enabled: true
    clients: client1, client2, client3

16.10如何配置Hystrix线程池

如果将zuul.ribbonIsolationStrategy更改为THREAD,则Hystrix的线程隔离策略将用于所有路由。在这种情况下,HystrixThreadPoolKey默认设置为RibbonCommand这意味着所有路由的HystrixCommands在相同的Hystrix线程池中执行。可以使用以下配置更改此行为:

application.yml。 

zuul:
  threadPool:
    useSeparateThreadPools: true

前面的示例导致在每个路由的Hystrix线程池中执行HystrixCommands。

在这种情况下,默认HystrixThreadPoolKey与每个路由的服务ID相同。要将前缀添加到HystrixThreadPoolKey,请将zuul.threadPool.threadPoolKeyPrefix设置为要添加的值,如以下示例所示:

application.yml。 

zuul:
  threadPool:
    useSeparateThreadPools: true
    threadPoolKeyPrefix: zuulgw

16.11如何提供Ribbon的IRule的密钥

如果您需要提供自己的IRule实现来处理诸如canary测试之类的特殊路由要求,请将一些信息传递给IRulechoose方法。

com.netflix.loadbalancer.IRule.java。 

public interface IRule{
    public Server choose(Object key);
         :

您可以提供一些信息,供您的IRule实现用来选择目标服务器,如以下示例所示:

RequestContext.getCurrentContext()
              .set(FilterConstants.LOAD_BALANCER_KEY, "canary-test");

如果您使用密钥FilterConstants.LOAD_BALANCER_KEY将任何对象放入RequestContext中,则该对象将传递到IRule实现的choose方法中。上例中显示的代码必须在执行RibbonRoutingFilter之前执行。Zuul的前置过滤器是执行此操作的最佳位置。您可以通过预过滤器中的RequestContext访问HTTP标头和查询参数,因此可以用来确定传递到Ribbon的LOAD_BALANCER_KEY如果没有在RequestContext中用LOAD_BALANCER_KEY放置任何值,则将空值作为choose方法的参数传递。

17.外部配置:Archaius

Archaius是Netflix客户端配置库。它是所有Netflix OSS组件用于配置的库。Archaius是Apache Commons Configuration项目的扩展它允许通过轮询源以进行更改或通过将源将更改推送到客户端来更新配置。Archaius使用Dynamic <Type> Property类作为属性的句柄,如以下示例所示:

Archaius示例。 

class ArchaiusTest {
    DynamicStringProperty myprop = DynamicPropertyFactory
            .getInstance()
            .getStringProperty("my.prop");

    void doSomething() {
        OtherClass.someMethod(myprop.get());
    }
}

Archaius具有自己的一组配置文件和加载优先级。Spring应用程序通常不应该直接使用Archaius,但是仍然需要本地配置Netflix工具。Spring Cloud具有Spring环境桥,因此Archaius可以从Spring环境读取属性。该桥允许Spring Boot项目使用常规配置工具链,同时允许它们按记录的方式配置Netflix工具(大部分情况下)。

18.路由器和过滤器:Zuul

路由是微服务架构不可或缺的一部分。例如,/可能被映射到您的web应用程序,/api/users被映射到用户服务,/api/shop被映射到商店服务。 Zuul是Netflix的基于JVM的路由器和服务器端负载平衡器。

Netflix将Zuul用于以下用途

  • 认证方式
  • 见解
  • 压力测试
  • 金丝雀测试
  • 动态路由
  • 服务迁移
  • 减载
  • 安全
  • 静态响应处理
  • 主动/主动流量管理

Zuul的规则引擎可使用几乎所有JVM语言编写规则和过滤器,并内置对Java和Groovy的支持。

[注意]注意

配置属性zuul.max.host.connections已被两个新属性zuul.host.maxTotalConnectionszuul.host.maxPerRouteConnections取代,它们分别默认为200和20。

[注意]注意

所有路由的默认Hystrix隔离模式(ExecutionIsolationStrategy)为SEMAPHORE如果首选该隔离模式,则可以将zuul.ribbonIsolationStrategy更改为THREAD

18.1如何包含Zuul

要将Zuul包含在您的项目中,请使用组ID为org.springframework.cloud和工件ID为​​spring-cloud-starter-netflix-zuul的启动程序。有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面

18.2嵌入式Zuul反向代理

Spring Cloud已创建嵌入式Zuul代理,以简化UI应用程序要对一个或多个后端服务进行代理调用的常见用例的开发。此功能对于用户界面代理所需的后端服务很有用,从而避免了为所有后端独立管理CORS和身份验证问题的需求。

要启用它,请用@EnableZuulProxy注释Spring Boot主类。这样做会导致将本地呼叫转发到适当的服务。按照约定,ID为users的服务从位于/users的代理接收请求(前缀被去除)。代理使用Ribbon来定位要通过发现转发到的实例。所有请求均在hystrix命令中执行,因此失败以Hystrix指标显示。一旦电路断开,代理就不会尝试与服务联系。

[注意]注意

Zuul入门程序不包含发现客户端,因此,对于基于服务ID的路由,您还需要在类路径上提供其中之一(Eureka是一种选择)。

要跳过自动添加服务的过程,请将zuul.ignored-services设置为服务ID模式的列表。如果服务与被忽略但已包含在显式配置的路由映射中的模式匹配,则将其忽略,如以下示例所示:

application.yml。 

 zuul:
  ignoredServices: '*'
  routes:
    users: /myusers/**

在前面的示例中, users ,所有服务均被忽略

要增加或更改代理路由,可以添加外部配置,如下所示:

application.yml。 

 zuul:
  routes:
    users: /myusers/**

前面的示例意味着对/myusers的HTTP调用被转发到users服务(例如,/myusers/101被转发到/101)。

要对路由进行更细粒度的控制,可以分别指定路径和serviceId,如下所示:

application.yml。 

 zuul:
  routes:
    users:
      path: /myusers/**
      serviceId: users_service

前面的示例意味着对/myusers的HTTP调用将转发到users_service服务。路由必须具有可以指定为蚂蚁样式模式的path,因此/myusers/*仅匹配一个级别,而/myusers/**则分层匹配。

后端的位置可以指定为serviceId(用于发现服务)或url(用于物理位置),如以下示例所示:

application.yml。 

 zuul:
  routes:
    users:
      path: /myusers/**
      url: https://example.com/users_service

这些简单的url路由不会作为HystrixCommand来执行,也不会使用Ribbon对多个URL进行负载均衡。为了实现这些目标,可以使用静态服务器列表指定一个serviceId,如下所示:

application.yml。 

zuul:
  routes:
    echo:
      path: /myusers/**
      serviceId: myusers-service
      stripPrefix: true

hystrix:
  command:
    myusers-service:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: ...

myusers-service:
  ribbon:
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    listOfServers: https://example1.com,http://example2.com
    ConnectTimeout: 1000
    ReadTimeout: 3000
    MaxTotalHttpConnections: 500
    MaxConnectionsPerHost: 100

另一种方法是指定服务路由并为serviceId配置Ribbon客户端(这样做需要在Ribbon中禁用Eureka支持- 有关更多信息请参见上文),如下所示例:

application.yml。 

zuul:
  routes:
    users:
      path: /myusers/**
      serviceId: users

ribbon:
  eureka:
    enabled: false

users:
  ribbon:
    listOfServers: example.com,google.com

您可以使用regexmapperserviceId和路由之间提供约定。它使用正则表达式命名组从serviceId中提取变量,并将其注入到路由模式中,如以下示例所示:

ApplicationConfiguration.java。 

@Bean
public PatternServiceRouteMapper serviceRouteMapper() {
    return new PatternServiceRouteMapper(
        "(?<name>^.+)-(?<version>v.+$)",
        "${version}/${name}");
}

前面的示例意味着myusers-v1中的serviceId被映射到路由/v1/myusers/**可以接受任何正则表达式,但是所有命名组必须同时存在于servicePatternroutePattern中。如果servicePatternserviceId不匹配,则使用默认行为。在前面的示例中,myusers中的serviceId被映射到“ / myusers / **”路由(未检测到版本)。默认情况下,此功能是禁用的,仅适用于发现的服务。

要为所有映射添加前缀,请将zuul.prefix设置为一个值,例如/api默认情况下,在转发请求之前,将从请求中删除代理前缀(您可以使用zuul.stripPrefix=false将此行为关闭)。您还可以关闭从单个路由中剥离特定于服务的前缀,如以下示例所示:

application.yml。 

 zuul:
  routes:
    users:
      path: /myusers/**
      stripPrefix: false

[注意]注意

zuul.stripPrefix仅适用于zuul.prefix中设置的前缀。它对给定路由的path中定义的前缀没有任何影响。

在前面的示例中,对/myusers/101的请求被转发到users服务上的/myusers/101

zuul.routes条目实际上绑定到类型为ZuulProperties的对象。如果查看该对象的属性,则可以看到它也有一个retryable标志。将该标志设置为true,以使Ribbon客户端自动重试失败的请求。当您需要修改使用Ribbon客户端配置的重试操作的参数时,也可以将该标志设置为true

默认情况下,X-Forwarded-Host标头被添加到转发的请求中。要关闭它,请设置zuul.addProxyHeaders = false默认情况下,前缀路径被剥离,并且后端请求使用X-Forwarded-Prefix标头(在前面显示的示例中为/myusers)。

如果设置默认路由(/),则带有@EnableZuulProxy的应用程序可以充当独立服务器。例如,zuul.route.home: /会将所有流量(“ / **”)路由到“ home”服务。

如果需要更细粒度的忽略,则可以指定要忽略的特定模式。这些模式在路线定位过程开始时进行评估,这意味着模式中应包含前缀以保证匹配。被忽略的模式跨越所有服务,并取代任何其他路由规范。以下示例显示了如何创建忽略的模式:

application.yml。 

 zuul:
  ignoredPatterns: /**/admin/**
  routes:
    users: /myusers/**

前面的示例意味着所有呼叫(例如/myusers/101)都被转发到users服务上的/101但是,包括/admin/在内的呼叫无法解决。

[警告]警告

如果您需要保留路由的顺序,则需要使用YAML文件,因为使用属性文件时顺序会丢失。以下示例显示了这样的YAML文件:

application.yml。 

 zuul:
  routes:
    users:
      path: /myusers/**
    legacy:
      path: /**

如果要使用属性文件,则legacy路径可能最终位于users路径的前面,从而导致users路径不可访问。

18.3 Zuul HTTP客户端

Zuul使用的默认HTTP客户端现在由Apache HTTP客户端支持,而不是已弃用的Ribbon RestClient要使用RestClientokhttp3.OkHttpClient,请分别设置ribbon.restclient.enabled=trueribbon.okhttp.enabled=true如果要自定义Apache HTTP客户端或OK HTTP客户端,请提供类型为ClosableHttpClientOkHttpClient的bean。

18.4 Cookie和敏感标题

您可以在同一系统中的服务之间共享标头,但您可能不希望敏感标头泄漏到下游到外部服务器中。您可以在路由配置中指定忽略的标头列表。Cookies发挥着特殊的作用,因为它们在浏览器中具有定义明确的语义,并且始终将它们视为敏感内容。如果代理的使用者是浏览器,那么下游服务的cookie也会给用户带来麻烦,因为它们都混杂在一起(所有下游服务看起来都来自同一位置)。

如果您对服务的设计很谨慎(例如,如果只有一个下游服务设置cookie),则可以让它们从后端一直流到调用者。另外,如果您的代理设置了cookie,并且您的所有后端服务都在同一系统中,则很自然地简单地共享它们(例如,使用Spring Session将它们链接到某些共享状态)。除此之外,由下游服务设置的任何cookie可能对调用者都无用,因此建议您将(至少)Set-CookieCookie设置为敏感的标头,用于那些没有使用的路由您网域的一部分。即使对于属于您网域的路由,在让Cookie在它们和代理之间流动之前,也应仔细考虑其含义。

可以将敏感头配置为每个路由的逗号分隔列表,如以下示例所示:

application.yml。 

 zuul:
  routes:
    users:
      path: /myusers/**
      sensitiveHeaders: Cookie,Set-Cookie,Authorization
      url: https://downstream

[注意]注意

这是sensitiveHeaders的默认值,因此除非您希望其与众不同,否则无需进行设置。这是Spring Cloud Netflix 1.1中的新功能(在1.0中,用户无法控制标题,并且所有cookie都双向流动)。

sensitiveHeaders是黑名单,默认值不为空。因此,要使Zuul发送所有标头(ignored除外),必须将其显式设置为空列表。如果要将Cookie或授权标头传递到后端,则必须这样做。以下示例显示如何使用sensitiveHeaders

application.yml。 

 zuul:
  routes:
    users:
      path: /myusers/**
      sensitiveHeaders:
      url: https://downstream

您还可以通过设置zuul.sensitiveHeaders来设置敏感标题。如果在路由上设置了sensitiveHeaders,它将覆盖全局sensitiveHeaders设置。

18.5忽略标题

除了路由敏感的标头之外,您还可以为与下游服务交互期间应丢弃的值(请求和响应)设置一个名为zuul.ignoredHeaders的全局值。默认情况下,如果Spring Security不在类路径中,则它们为空。否则,它们将初始化为Spring Security指定的一组众所周知的安全性标头(例如,涉及缓存)。在这种情况下的假设是,下游服务也可以添加这些标头,但是我们需要来自代理的值。要在类路径上有Spring Security时不丢弃这些众所周知的安全标头,可以将zuul.ignoreSecurityHeaders设置为false如果您在Spring Security中禁用了HTTP安全响应标头,并希望由下游服务提供值,则这样做很有用。

18.6管理端点

默认情况下,如果将@EnableZuulProxy与Spring Boot Actuator结合使用,则将启用两个附加端点:

  • 路线
  • 筛选器

18.6.1路由端点

/routes处的路由端点的GET返回已映射路由的列表:

GET /路线。 

{
  /stores/**: "http://localhost:8081"
}

可以通过将?format=details查询字符串添加到/routes来请求其他路由详细信息。这样做会产生以下输出:

获取/ routes / details。 

{
  "/stores/**": {
    "id": "stores",
    "fullPath": "/stores/**",
    "location": "http://localhost:8081",
    "path": "/**",
    "prefix": "/stores",
    "retryable": false,
    "customSensitiveHeaders": false,
    "prefixStripped": true
  }
}

POST/routes强制刷新现有路由(例如,当服务目录中发生更改时)。您可以通过将endpoints.routes.enabled设置为false来禁用此端点。

[注意]注意

路由应该自动响应服务目录中的更改,但是从POST/routes是强制更改立即进行的一种方法。

18.6.2过滤器端点

过滤器端点/filtersGET按类型返回Zuul过滤器的映射。对于地图中的每种过滤器类型,您将获得该类型的所有过滤器的列表以及它们的详细信息。

18.7扼杀模式和局部Forwards

迁移现有应用程序或API时,常见的模式是勒死旧的端点,并用不同的实现方式慢慢替换它们。Zuul代理是一个有用的工具,因为您可以使用它来处理来自旧端点的客户端的所有流量,但可以将一些请求重定向到新请求。

以下示例显示扼杀方案的配置详细信息

application.yml。 

 zuul:
  routes:
    first:
      path: /first/**
      url: https://first.example.com
    second:
      path: /second/**
      url: forward:/second
    third:
      path: /third/**
      url: forward:/3rd
    legacy:
      path: /**
      url: https://legacy.example.com

在前面的示例中,我们扼杀了legacy应用程序,该应用程序映射到与其他模式之一不匹配的所有请求。/first/**中的路径已使用外部URL提取到新服务中。/second/**中的路径被转发,以便可以在本地处理(例如,使用普通Spring @RequestMapping)。/third/**中的路径也被转发,但是前缀不同(/third/foo被转发到/3rd/foo)。

[注意]注意

被忽略的模式不会被完全忽略,它们不会由代理处理(因此它们也可以在本地有效转发)。

18.8通过Zuul上传文件

如果使用@EnableZuulProxy,则可以使用代理路径上载文件,只要文件很小,它就可以正常工作。对于大文件,有一个替代路径可以绕过“ / zuul / *”中的Spring DispatcherServlet(以避免进行多部分处理)。换句话说,如果您拥有zuul.routes.customers=/customers/**,则可以将POST大文件复制到/zuul/customers/*Servlet路径通过zuul.servletPath外部化。如果代理路由将您带到Ribbon负载均衡器,则超大文件也需要提高超时设置,如以下示例所示:

application.yml。 

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
ribbon:
  ConnectTimeout: 3000
  ReadTimeout: 60000

请注意,要使流技术处理大文件,您需要在请求中使用分块编码(某些浏览器默认不这样做),如以下示例所示:

$ curl -v -H "Transfer-Encoding: chunked" \
    -F "[email protected]" localhost:9999/zuul/simple/file

18.9查询字符串编码

在处理传入请求时,查询参数将被解码,以便可以在Zuul过滤器中进行修改。然后将它们重新编码,在路由过滤器中重建后端请求。例如,如果结果是使用Javascript的encodeURIComponent()方法编码的,则结果可能不同于原始输入。虽然这在大多数情况下不会引起问题,但某些web服务器可能对复杂查询字符串的编码很挑剔。

要强制对查询字符串进行原始编码,可以将特殊标志传递给ZuulProperties,以便使用HttpServletRequest::getQueryString方法按原样使用查询字符串,如以下示例所示:

application.yml。 

 zuul:
  forceOriginalQueryStringEncoding: true

[注意]注意

该特殊标志仅适用于SimpleHostRoutingFilter另外,您松开了使用RequestContext.getCurrentContext().setRequestQueryParams(someOverriddenParameters)轻松覆盖查询参数的功能,因为现在直接在原始HttpServletRequest上获取了查询字符串。

18.10请求URI编码

在处理传入请求时,在将请求URI与路由匹配之前,先对其进行解码。然后在路由过滤器中重建后端请求时,将对请求URI进行重新编码。如果您的URI包含编码的“ /”字符,则可能导致某些意外行为。

要使用原始请求URI,可以将特殊标志传递给'ZuulProperties',以便使用HttpServletRequest::getRequestURI方法按原样使用URI,如以下示例所示:

application.yml。 

 zuul:
  decodeUrl: false

[注意]注意

如果使用requestURI RequestContext属性覆盖请求URI,并且此标志设置为false,则将不对在请求上下文中设置的URL进行编码。确保URL已被编码是您的责任。

18.11普通嵌入式Zuul

如果使用@EnableZuulServer(而不是@EnableZuulProxy),则也可以运行Zuul服务器而不进行代理或有选择地打开代理平台的某些部分。您添加到类型为ZuulFilter的应用程序中的所有beans都会自动安装(与@EnableZuulProxy一样),但是不会自动添加任何代理过滤器。

在这种情况下,仍然可以通过配置“ zuul.routes。*”来指定进入Zuul服务器的路由,但是没有服务发现也没有代理。因此,“ serviceId”和“ url”设置将被忽略。以下示例将“ / api / **”中的所有路径映射到Zuul过滤器链:

application.yml。 

 zuul:
  routes:
    api: /api/**

18.12禁用Zuul过滤器

Spring Cloud的Zuul带有多个ZuulFilter beans,默认情况下在代理和服务器模式下都启用。有关可以启用的过滤器列表,请参见Zuul过滤器包如果要禁用一个,请设置zuul.<SimpleClassName>.<filterType>.disable=true按照惯例,filters之后的软件包是Zuul过滤器类型。例如,要禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter,请设置zuul.SendResponseFilter.post.disable=true

18.13为路线提供Hystrix后备

当Zuul中给定路由的电路跳闸时,可以通过创建类型为FallbackProvider的bean提供回退响应。在此bean中,您需要指定回退的路由ID,并提供一个ClientHttpResponse作为回退的路由。以下示例显示了一个相对简单的FallbackProvider实现:

class MyFallbackProvider implements FallbackProvider {

    @Override
    public String getRoute() {
        return "customers";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private ClientHttpResponse response(final HttpStatus status) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return status;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return status.value();
            }

            @Override
            public String getStatusText() throws IOException {
                return status.getReasonPhrase();
            }

            @Override
            public void close() {
            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("fallback".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

以下示例显示了上一个示例的路由配置可能如何显示:

zuul:
  routes:
    customers: /customers/**

如果您想为所有路由提供默认后备,则可以创建类型为FallbackProvider的bean,并让getRoute方法返回*null,如以下示例:

class MyFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable throwable) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("fallback".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

18.14 Zuul超时

如果要为通过Zuul代理的请求配置套接字超时和读取超时,则根据您的配置,有两个选项:

  • 如果Zuul使用服务发现,则需要使用ribbon.ReadTimeoutribbon.SocketTimeout Ribbon属性配置这些超时。

如果通过指定URL配置了Zuul路由,则需要使用zuul.host.connect-timeout-milliszuul.host.socket-timeout-millis

18.15重写Location标头

如果Zuul在web应用程序的前面,则当web应用程序通过HTTP状态代码3XX重定向时,您可能需要重新编写Location标头。否则,浏览器将重定向到web应用程序的URL,而不是Zuul URL。您可以配置LocationRewriteFilter Zuul过滤器,将Location标头重写为Zuul的URL。它还添加回去的全局前缀和特定于路由的前缀。以下示例通过使用Spring配置文件添加过滤器:

import org.springframework.cloud.netflix.zuul.filters.post.LocationRewriteFilter;
...

@Configuration
@EnableZuulProxy
public class ZuulConfig {
    @Bean
    public LocationRewriteFilter locationRewriteFilter() {
        return new LocationRewriteFilter();
    }
}
[警告]警告

小心使用此过滤器。筛选器作用于所有3XX响应代码的Location头,这可能并不适用于所有情况,例如将用户重定向到外部URL时。

18.16启用跨源请求

默认情况下,Zuul将所有跨源请求(CORS)路由到服务。如果您希望Zuul处理这些请求,可以通过提供自定义WebMvcConfigurer bean来完成:

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/path-1/**")
                    .allowedOrigins("https://allowed-origin.com")
                    .allowedMethods("GET", "POST");
        }
    };
}

在上面的示例中,我们允许https://allowed-origin.com中的GETPOST方法将跨域请求发送到以path-1开头的端点。您可以使用/**映射将CORS配置应用于特定的路径模式或整个应用程序的全局路径。您可以通过此配置来自定义属性:allowedOriginsallowedMethodsallowedHeadersexposedHeadersallowCredentialsmaxAge

18.17指标

Zuul将在执行器指标终结点下提供指标,以解决路由请求时可能发生的任何故障。可以通过点击/actuator/metrics来查看这些指标。指标的名称格式为ZUUL::EXCEPTION:errorCause:statusCode

18.18 Zuul开发人员指南

有关Zuul的工作原理的一般概述,请参见Zuul Wiki

18.18.1 Zuul Servlet

Zuul被实现为Servlet。对于一般情况,Zuul已嵌入Spring调度机制中。这使Spring MVC可以控制路由。在这种情况下,Zuul缓冲请求。如果需要通过Zuul而不缓冲请求(例如,用于大文件上传),则Servlet也将安装在Spring Dispatcher之外。缺省情况下,该servlet的地址为/zuul可以使用zuul.servlet-path属性更改此路径。

18.18.2 Zuul RequestContext

要在过滤器之间传递信息,Zuul使用RequestContext其数据保存在每个请求专用的ThreadLocal中。有关在何处路由请求,错误以及实际的HttpServletRequestHttpServletResponse的信息存储在此处。RequestContext扩展了ConcurrentHashMap,因此任何内容都可以存储在上下文中。FilterConstants包含Spring Cloud Netflix安装的过滤器使用的密钥(稍后会详细介绍)。

18.18.3 @EnableZuulProxy@EnableZuulServer

Spring Cloud Netflix安装了许多过滤器,具体取决于启用了Zuul的注释。@EnableZuulProxy@EnableZuulServer的超集。换句话说,@EnableZuulProxy包含@EnableZuulServer安装的所有筛选器。代理 ”中的其他过滤器启用路由功能。如果您想使用空白 Zuul,则应使用@EnableZuulServer

18.18.4 @EnableZuulServer过滤器

@EnableZuulServer创建一个SimpleRouteLocator,该文件从Spring Boot配置文件中加载路由定义。

已安装以下过滤器(按常规方式Spring Beans):

  • 前置过滤器:

    • ServletDetectionFilter:检测请求是否通过Spring分派器进行。设置键为FilterConstants.IS_DISPATCHER_SERVLET_REQUEST_KEY的布尔值。
    • FormBodyWrapperFilter:解析表单数据并为下游请求重新编码。
    • DebugFilter:如果设置了debug请求参数,则将RequestContext.setDebugRouting()RequestContext.setDebugRequest()设置为true*路由过滤器:
    • SendForwardFilter:使用Servlet RequestDispatcher的Forwards请求。转发位置存储在RequestContext属性FilterConstants.FORWARD_TO_KEY中。这对于转发到当前应用程序中的端点很有用。
  • 帖子过滤器:

    • SendResponseFilter:将代理请求的响应写入当前响应。
  • 错误过滤器:

    • SendErrorFilter:如果RequestContext.getThrowable()不为空,则Forwards至/error(默认)。您可以通过设置error.path属性来更改默认转发路径(/error)。

18.18.5 @EnableZuulProxy过滤器

创建一个DiscoveryClientRouteLocator,它从DiscoveryClient(例如Eureka)以及属性中加载路由定义。DiscoveryClient为每个serviceId创建一条路由。添加新服务后,将刷新路由。

除了前面描述的过滤器之外,还安装了以下过滤器(常规Spring Beans):

  • 前置过滤器:

    • PreDecorationFilter:根据提供的RouteLocator确定路线和路线。它还为下游请求设置了各种与代理相关的标头。
  • 路线过滤器:

    • RibbonRoutingFilter:使用Ribbon,Hystrix和可插拔的HTTP客户端发送请求。RequestContext属性FilterConstants.SERVICE_ID_KEY中可以找到服务ID。此过滤器可以使用不同的HTTP客户端:

      • Apache HttpClient:默认客户端。
      • Squareup OkHttpClient v3:通过在类路径上放置com.squareup.okhttp3:okhttp库并设置ribbon.okhttp.enabled=true来启用。
      • Netflix Ribbon HTTP客户端:通过设置ribbon.restclient.enabled=true启用。该客户端具有局限性,包括不支持PATCH方法,但是还具有内置的重试功能。
    • SimpleHostRoutingFilter:通过Apache HttpClient将请求发送到预定的URL。可在RequestContext.getRouteHost()中找到URL。

18.18.6自定义Zuul过滤器示例

下面的大多数“如何编写”示例都包含在示例Zuul过滤器项目中。在该存储库中也有一些处理请求或响应正文的示例。

本节包括以下示例:

如何编写前置过滤器

前置过滤器可在RequestContext中设置数据,以便在下游的过滤器中使用。主要用例是设置路由过滤器所需的信息。以下示例显示了Zuul前置过滤器:

public class QueryParamPreFilter extends ZuulFilter {
	@Override
	public int filterOrder() {
		return PRE_DECORATION_FILTER_ORDER - 1; // run before PreDecoration
	}

	@Override
	public String filterType() {
		return PRE_TYPE;
	}

	@Override
	public boolean shouldFilter() {
		RequestContext ctx = RequestContext.getCurrentContext();
		return !ctx.containsKey(FORWARD_TO_KEY) // a filter has already forwarded
				&& !ctx.containsKey(SERVICE_ID_KEY); // a filter has already determined serviceId
	}
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		if (request.getParameter("sample") != null) {
		    // put the serviceId in `RequestContext`
    		ctx.put(SERVICE_ID_KEY, request.getParameter("foo"));
    	}
        return null;
    }
}

前面的过滤器从sample请求参数中填充SERVICE_ID_KEY实际上,您不应该执行这种直接映射。而是应从sample的值中查找服务ID。

现在已填充SERVICE_ID_KEYPreDecorationFilter将不运行,而RibbonRoutingFilter将运行。

[提示]提示

如果要路由到完整URL,请致电ctx.setRouteHost(url)

要修改路由过滤器转发到的路径,请设置REQUEST_URI_KEY

如何编写路由过滤器

路由过滤器在预过滤器之后运行,并向其他服务发出请求。这里的许多工作是将请求和响应数据与客户端所需的模型相互转换。以下示例显示了Zuul路由过滤器:

public class OkHttpRoutingFilter extends ZuulFilter {
	@Autowired
	private ProxyRequestHelper helper;

	@Override
	public String filterType() {
		return ROUTE_TYPE;
	}

	@Override
	public int filterOrder() {
		return SIMPLE_HOST_ROUTING_FILTER_ORDER - 1;
	}

	@Override
	public boolean shouldFilter() {
		return RequestContext.getCurrentContext().getRouteHost() != null
				&& RequestContext.getCurrentContext().sendZuulResponse();
	}

    @Override
    public Object run() {
		OkHttpClient httpClient = new OkHttpClient.Builder()
				// customize
				.build();

		RequestContext context = RequestContext.getCurrentContext();
		HttpServletRequest request = context.getRequest();

		String method = request.getMethod();

		String uri = this.helper.buildZuulRequestURI(request);

		Headers.Builder headers = new Headers.Builder();
		Enumeration<String> headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String name = headerNames.nextElement();
			Enumeration<String> values = request.getHeaders(name);

			while (values.hasMoreElements()) {
				String value = values.nextElement();
				headers.add(name, value);
			}
		}

		InputStream inputStream = request.getInputStream();

		RequestBody requestBody = null;
		if (inputStream != null && HttpMethod.permitsRequestBody(method)) {
			MediaType mediaType = null;
			if (headers.get("Content-Type") != null) {
				mediaType = MediaType.parse(headers.get("Content-Type"));
			}
			requestBody = RequestBody.create(mediaType, StreamUtils.copyToByteArray(inputStream));
		}

		Request.Builder builder = new Request.Builder()
				.headers(headers.build())
				.url(uri)
				.method(method, requestBody);

		Response response = httpClient.newCall(builder.build()).execute();

		LinkedMultiValueMap<String, String> responseHeaders = new LinkedMultiValueMap<>();

		for (Map.Entry<String, List<String>> entry : response.headers().toMultimap().entrySet()) {
			responseHeaders.put(entry.getKey(), entry.getValue());
		}

		this.helper.setResponse(response.code(), response.body().byteStream(),
				responseHeaders);
		context.setRouteHost(null); // prevent SimpleHostRoutingFilter from running
		return null;
    }
}

前面的过滤器将Servlet请求信息转换为OkHttp3请求信息,执行HTTP请求,并将OkHttp3响应信息转换为Servlet响应。

如何编写帖子过滤器

后置过滤器通常操纵响应。以下过滤器将随机UUID添加为X-Sample标头:

public class AddResponseHeaderFilter extends ZuulFilter {
	@Override
	public String filterType() {
		return POST_TYPE;
	}

	@Override
	public int filterOrder() {
		return SEND_RESPONSE_FILTER_ORDER - 1;
	}

	@Override
	public boolean shouldFilter() {
		return true;
	}

	@Override
	public Object run() {
		RequestContext context = RequestContext.getCurrentContext();
    	HttpServletResponse servletResponse = context.getResponse();
		servletResponse.addHeader("X-Sample", UUID.randomUUID().toString());
		return null;
	}
}
[注意]注意

其他操作,例如转换响应主体,则更加复杂且计算量大。

18.18.7 Zuul错误的工作方式

如果在Zuul过滤器生命周期的任何部分抛出异常,则将执行错误过滤器。仅当RequestContext.getThrowable()不是null时才运行SendErrorFilter然后,它在请求中设置特定的javax.servlet.error.*属性,并将请求转发到Spring Boot错误页面。

18.18.8 Zuul急切的应用程序上下文加载

Zuul内部使用Ribbon来调用远程URL。默认情况下,Ribbon客户端在第一次调用时被Spring Cloud延迟加载。可以使用以下配置为Zuul更改此行为,这会导致在应用程序启动时急于加载与子Ribbon相关的应用程序上下文。以下示例显示了如何启用即时加载:

application.yml。 

zuul:
  ribbon:
    eager-load:
      enabled: true

19. Polyglot支持Sidecar

您是否要使用非JVM语言来利用Eureka,Ribbon和Config Server?Spring Cloud Netflix Sidecar的灵感来自Netflix Prana它包括一个HTTP API,用于获取给定服务的所有实例(按主机和端口)。您也可以通过嵌入式Zuul代理来代理服务调用,该代理从Eureka获取其路由条目。可以直接通过主机查找或通过Zuul代理访问Spring Cloud Config服务器。非JVM应用程序应实施运行状况检查,以便Sidecar可以向Eureka报告应用程序是启动还是关闭。

要在项目中包含Sidecar,请使用组ID为org.springframework.cloud且工件ID为​​spring-cloud-netflix-sidecar的依赖项。

要启用Sidecar,请使用@EnableSidecar创建一个Spring Boot应用程序。该注释包括@EnableCircuitBreaker@EnableDiscoveryClient@EnableZuulProxy在与非JVM应用程序相同的主机上运行结果应用程序。

要配置侧车,请将sidecar.portsidecar.health-uri添加到application.ymlsidecar.port属性是非JVM应用程序侦听的端口。这样Sidecar可以正确地向Eureka注册应用程序。sidecar.secure-port-enabled选项提供了一种启用流量安全端口的方法。sidecar.health-uri是在非JVM应用程序上可访问的URI,它模仿Spring Boot运行状况指示器。它应该返回类似于以下内容的JSON文档:

health-uri-document。 

{
  "status":"UP"
}

以下application.yml示例显示了Sidecar应用程序的示例配置:

application.yml。 

server:
  port: 5678
spring:
  application:
    name: sidecar

sidecar:
  port: 8000
  health-uri: http://localhost:8000/health.json

DiscoveryClient.getInstances()方法的API为/hosts/{serviceId}以下针对/hosts/customers的示例响应在不同的主机上返回两个实例:

/ hosts / customers。 

[
    {
        "host": "myhost",
        "port": 9000,
        "uri": "http://myhost:9000",
        "serviceId": "CUSTOMERS",
        "secure": false
    },
    {
        "host": "myhost2",
        "port": 9000,
        "uri": "http://myhost2:9000",
        "serviceId": "CUSTOMERS",
        "secure": false
    }
]

非JVM应用程序(如果Sidecar位于端口5678上)可通过http://localhost:5678/hosts/{serviceId}访问此API。

Zuul代理会自动将Eureka中已知的每个服务的路由添加到/<serviceId>,因此可以在/customers中使用客户服务。非JVM应用程序可以在http://localhost:5678/customers上访问客户服务(假设Sidecar正在侦听5678端口)。

如果Config Server已向Eureka注册,则非JVM应用程序可以通过Zuul代理对其进行访问。如果ConfigServer的serviceIdconfigserver并且Sidecar在端口5678上,则可以在http:// localhost:5678 / configserver上对其进行访问

非JVM应用程序可以利用Config Server返回YAML文档的功能。例如,调用https://sidecar.local.spring.io:5678/configserver/default-master.yml 可能会导致YAML文档类似于以下内容:

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  password: password
info:
  description: Spring Cloud Samples
  url: https://github.com/spring-cloud-samples

要在使用HTTP时使运行状况检查请求接受所有证书,请将sidecar.accept-all-ssl-certificates设置为`true。

20.重试失败的请求

Spring Cloud Netflix提供了多种发出HTTP请求的方式。您可以使用负载均衡的RestTemplate,Ribbon或Feign。无论您如何选择创建HTTP请求,始终都有一个请求失败的机会。当请求失败时,您可能希望自动重试该请求。为此,在使用Sping Cloud Netflix时,您需要在应用程序的类路径中包含Spring重试如果存在Spring重试,则负载平衡的RestTemplates,Feign和Zuul会自动重试任何失败的请求(假设您的配置允许这样做)。

20.1退避政策

默认情况下,重试请求时不使用任何退避策略。如果要配置退避策略,则需要创建类型为LoadBalancedRetryFactory的bean并为给定服务覆盖createBackOffPolicy方法,如以下示例所示:

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryFactory retryFactory() {
        return new LoadBalancedRetryFactory() {
            @Override
            public BackOffPolicy createBackOffPolicy(String service) {
                return new ExponentialBackOffPolicy();
            }
        };
    }
}

20.2配置

将Ribbon与Spring重试一起使用时,可以通过配置某些Ribbon属性来控制重试功能。为此,请设置client.ribbon.MaxAutoRetriesclient.ribbon.MaxAutoRetriesNextServerclient.ribbon.OkToRetryOnAllOperations属性。有关这些属性的作用的说明,请参见Ribbon文档

[警告]警告

启用client.ribbon.OkToRetryOnAllOperations包括重试POST请求,由于请求正文的缓冲,这可能会对服务器资源产生影响。

此外,当响应中返回某些状态代码时,您可能想重试请求。您可以通过设置clientName.ribbon.retryableStatusCodes属性来列出希望Ribbon客户端重试的响应代码,如以下示例所示:

clientName:
  ribbon:
    retryableStatusCodes: 404,502

您也可以创建类型为LoadBalancedRetryPolicy的bean,并实现retryableStatusCode方法以根据状态码重试请求。

20.2.1 Zuul

您可以通过将zuul.retryable设置为false来关闭Zuul的重试功能。您还可以通过将zuul.routes.routename.retryable设置为false来逐个路由禁用重试功能。

21. HTTP客户端

Spring Cloud Netflix会自动为您创建Ribbon,Feign和Zuul使用的HTTP客户端。但是,您也可以根据需要提供自定义的HTTP客户端。为此,如果使用的是Apache Http Cient,则可以创建类型为ClosableHttpClient的bean,如果使用的是OK HTTP,则可以创建类型为OkHttpClient的bean。

[注意]注意

创建自己的HTTP客户端时,您还负责为这些客户端实施正确的连接管理策略。这样做不当会导致资源管理问题。

22.维护模式下的模块

将模块置于维护模式意味着Spring Cloud团队将不再向模块添加新功能。我们将修复阻止程序错误和安全性问题,还将考虑并审查社区的一些小请求。

自Greenwich 发布列车全面上市以来,我们打算继续为这些模块提供至少一年的支持。

以下Spring Cloud Netflix模块和相应的启动器将进入维护模式:

  • spring-cloud-netflix-archaius
  • spring-cloud-netflix-hystrix-contract
  • spring-cloud-netflix-hystrix-dashboard
  • spring-cloud-netflix-hystrix-stream
  • spring-cloud-netflix-hystrix
  • spring-cloud-netflix-ribbon
  • spring-cloud-netflix-turbine-stream
  • spring-cloud-netflix-turbine
  • spring-cloud-netflix-zuul
[注意]注意

这不包括Eureka或并发限制模块。

第四部分 Spring Cloud OpenFeign

Greenwich SR5

该项目通过自动配置并绑定到Spring环境和其他Spring编程模型习惯用法,为Spring Boot应用提供了OpenFeign集成。

23.声明式REST客户端:Feign

Feign是声明性的web服务客户端。它使编写web服务客户端更加容易。要使用Feign,请创建一个接口并对其进行注释。它具有可插入的注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插拔编码器和解码器。Spring Cloud添加了对Spring MVC注释的支持,并支持使用Spring Web中默认使用的同一HttpMessageConvertersSpring Cloud集成了Ribbon和Eureka以在使用Feign时提供负载平衡的http客户端。

23.1如何包括Feign

要将Feign包含在您的项目中,请将启动器与组org.springframework.cloud和工件ID spring-cloud-starter-openfeign一起使用。有关 使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面

示例spring boot应用

@SpringBootApplication
@EnableFeignClients
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

StoreClient.java。 

@FeignClient("stores")
public interface StoreClient {
    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    List<Store> getStores();

    @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
    Store update(@PathVariable("storeId") Long storeId, Store store);
}

@FeignClient批注中,字符串值(上面的“ stores”)是一个任意的客户端名称,用于创建Ribbon负载均衡器(请参见下面的Ribbon support的详细信息)。您还可以使用url属性(绝对值或仅是主机名)来指定URL。在应用程序上下文中,bean的名称是接口的标准名称。要指定自己的别名值,可以使用@FeignClient批注的qualifier值。

上面的Ribbon客户端将希望发现“商店”服务的物理地址。如果您的应用程序是Eureka客户端,则它将在Eureka服务注册表中解析该服务。如果您不想使用Eureka,则可以简单地在外部配置中配置服务器列表(例如,参见 上文)。

23.2覆盖Feign默认值

Spring Cloud的Feign支持中的中心概念是指定客户的概念。每个虚拟客户端都是组件的一部分,这些组件可以一起工作以按需联系远程服务器,并且该组件的名称是您使用@FeignClient批注将其指定为应用程序开发人员的。Spring Cloud根据需要使用FeignClientsConfiguration为每个命名客户端创建一个新的合奏作为ApplicationContext其中包含feign.Decoderfeign.Encoderfeign.Contract通过使用@FeignClient批注的contextId属性,可以覆盖该集合的名称。

Spring Cloud使您可以通过使用@FeignClient声明其他配置(在FeignClientsConfiguration之上)来完全控制假客户端。例:

@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
    //..
}

在这种情况下,客户端由FeignClientsConfiguration中已有的组件以及FooConfiguration中的任何组件组成(其中后者将覆盖前者)。

[注意]注意

FooConfiguration不需要用@Configuration进行注释。但是,如果是的话,请注意将其从任何可能包含此配置的@ComponentScan中排除,因为它将成为feign.Decoderfeign.Encoderfeign.Contract等的默认来源,指定时。可以通过将其与任何@ComponentScan@SpringBootApplication放在单独的,不重叠的包中来避免这种情况,也可以在@ComponentScan中将其明确排除在外。

[注意]注意

现在不推荐使用serviceId属性,而推荐使用name属性。

[注意]注意

除了更改ApplicationContext集合的名称之外,还使用@FeignClient批注的contextId属性,它会覆盖客户端名称的别名,并将其用作配置名称的一部分bean为该客户端创建的。

[警告]警告

以前,使用url属性不需要name属性。现在需要使用name

nameurl属性中支持占位符。

@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
    //..
}

Spring Cloud Netflix默认提供以下beans伪装(BeanType beanName:ClassName):

  • Decoder feignDecoder:ResponseEntityDecoder(包装SpringDecoder
  • Encoder feignEncoder:SpringEncoder
  • Logger feignLogger:Slf4jLogger
  • Contract feignContract:SpringMvcContract
  • Feign.Builder feignBuilder:HystrixFeign.Builder
  • Client feignClient:如果启用了Ribbon,则它是LoadBalancerFeignClient,否则使用默认的伪装客户端。

可以通过分别将feign.okhttp.enabledfeign.httpclient.enabled设置为true并将其放在类路径中来使用OkHttpClient和ApacheHttpClient虚拟客户端。您可以自定义HTTP客户端,方法是在使用Apache时提供ClosableHttpClient的bean,在使用OK HTTP时提供OkHttpClient

Spring Cloud Netflix 默认情况下不会为伪装提供以下beans,但仍会从应用程序上下文中查找以下类型的beans以创建伪装客户端:

  • Logger.Level
  • Retryer
  • ErrorDecoder
  • Request.Options
  • Collection<RequestInterceptor>
  • SetterFactory

创建其中一种类型的bean并将其放置在@FeignClient配置中(例如上述FooConfiguration),您可以覆盖上述的每个beans。例:

@Configuration
public class FooConfiguration {
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }

    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("user", "password");
    }
}

这将SpringMvcContract替换为feign.Contract.Default,并将RequestInterceptor添加到RequestInterceptor的集合中。

@FeignClient也可以使用配置属性进行配置。

application.yml

feign:
  client:
    config:
      feignName:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        errorDecoder: com.example.SimpleErrorDecoder
        retryer: com.example.SimpleRetryer
        requestInterceptors:
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        decode404: false
        encoder: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        contract: com.example.SimpleContract

可以按照与上述类似的方式在@EnableFeignClients属性defaultConfiguration中指定默认配置。不同之处在于此配置将适用于所有伪客户端。

如果您希望使用配置属性来配置所有@FeignClient,则可以使用default虚拟名称创建配置属性。

application.yml

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic

如果我们同时创建@Configuration bean和配置属性,则配置属性将获胜。它将覆盖@Configuration值。但是,如果要将优先级更改为@Configuration,可以将feign.client.default-to-properties更改为false

[注意]注意

如果您需要在RequestInterceptor`s you will need to either set the thread isolation strategy for Hystrix to `SEMAPHORE中使用ThreadLocal绑定变量,或者在Feign中禁用Hystrix。

application.yml

# To disable Hystrix in Feign
feign:
  hystrix:
    enabled: false

# To set thread isolation to SEMAPHORE
hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: SEMAPHORE

如果我们要创建多个具有相同名称或URL的伪装客户端,以便它们指向同一台服务器,但每个客户端使用不同的自定义配置,则必须使用@FeignClientcontextId属性,以避免这些配置beans的名称冲突。

@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {
    //..
}
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
public interface BarClient {
    //..
}

23.3手动创建Feign客户

在某些情况下,可能有必要使用上述方法无法实现的方式自定义Feign客户。在这种情况下,您可以使用Feign Builder API创建客户端 下面是一个示例,该示例创建两个具有相同接口的Feign客户端,但为每个客户端配置一个单独的请求拦截器。

@Import(FeignClientsConfiguration.class)
class FooController {

	private FooClient fooClient;

	private FooClient adminClient;

    	@Autowired
	public FooController(Decoder decoder, Encoder encoder, Client client, Contract contract) {
		this.fooClient = Feign.builder().client(client)
				.encoder(encoder)
				.decoder(decoder)
				.contract(contract)
				.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
				.target(FooClient.class, "http://PROD-SVC");

		this.adminClient = Feign.builder().client(client)
				.encoder(encoder)
				.decoder(decoder)
				.contract(contract)
				.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
				.target(FooClient.class, "http://PROD-SVC");
    }
}
[注意]注意

在上面的示例中,FeignClientsConfiguration.class是Spring Cloud Netflix提供的默认配置。

[注意]注意

PROD-SVC是客户将向其请求的服务的名称。

[注意]注意

Feign Contract对象定义在接口上有效的注释和值。自动连线的Contract bean提供对SpringMVC注释的支持,而不是默认的Feign本机注释。

23.4 Feign Hystrix支持

如果Hystrix在类路径上并且在feign.hystrix.enabled=true上,则Feign将使用断路器包装所有方法。还可以返回com.netflix.hystrix.HystrixCommand这使您可以使用反应性模式(通过调用.toObservable().observe()或异步使用(通过调用.queue())。

要基于每个客户端禁用Hystrix支持,请创建具有{prototype“范围的普通Feign.Builder,例如:

@Configuration
public class FooConfiguration {
    	@Bean
	@Scope("prototype")
	public Feign.Builder feignBuilder() {
		return Feign.builder();
	}
}
[警告]警告

在Spring Cloud Dalston发行版之前,如果Hystrix在类路径Feign上,则默认情况下会将所有方法包装在断路器中。Spring Cloud Dalston中对此默认行为进行了更改,以支持选择加入方法。

23.5 Feign Hystrix后备

Hystrix支持回退的概念:当它们的电路断开或出现错误时执行的默认代码路径。要为给定的@FeignClient启用回退,请将fallback属性设置为实现回退的类名称。您还需要将实现声明为Spring bean。

@FeignClient(name = "hello", fallback = HystrixClientFallback.class)
protected interface HystrixClient {
    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    Hello iFailSometimes();
}

static class HystrixClientFallback implements HystrixClient {
    @Override
    public Hello iFailSometimes() {
        return new Hello("fallback");
    }
}

如果需要访问引起后备触发器的原因,则可以使用@FeignClient中的fallbackFactory属性。

@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)
protected interface HystrixClient {
	@RequestMapping(method = RequestMethod.GET, value = "/hello")
	Hello iFailSometimes();
}

@Component
static class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {
	@Override
	public HystrixClient create(Throwable cause) {
		return new HystrixClient() {
			@Override
			public Hello iFailSometimes() {
				return new Hello("fallback; reason was: " + cause.getMessage());
			}
		};
	}
}
[警告]警告

Feign中的后备实现以及Hystrix后备如何工作存在局限性。返回com.netflix.hystrix.HystrixCommandrx.Observable的方法当前不支持后备。

23.6 Feign和@Primary

当将Feign与后退Hystrix一起使用时,ApplicationContext中有多个相同类型的beans。这将导致@Autowired无法正常工作,因为没有一个bean或标记为主要的一个。要解决此问题,Spring Cloud Netflix将所有Feign实例标记为@Primary,因此Spring Framework将知道要插入哪个bean。在某些情况下,这可能不是理想的。要关闭此行为,请将@FeignClientprimary属性设置为false。

@FeignClient(name = "hello", primary = false)
public interface HelloClient {
	// methods here
}

23.7 Feign继承支持

Feign通过单继承接口支持样板API。这允许将常用操作分组为方便的基本接口。

UserService.java。 

public interface UserService {

    @RequestMapping(method = RequestMethod.GET, value ="/users/{id}")
    User getUser(@PathVariable("id") long id);
}

UserResource.java。 

@RestController
public class UserResource implements UserService {

}

UserClient.java。 

package project.user;

@FeignClient("users")
public interface UserClient extends UserService {

}

[注意]注意

通常不建议在服务器和客户端之间共享接口。它引入了紧密耦合,并且实际上也不能与当前形式的Spring MVC一起使用(方法参数映射不被继承)。

23.8 Feign请求/响应压缩

您可以考虑为Feign请求启用请求或响应GZIP压缩。您可以通过启用以下属性之一来做到这一点:

feign.compression.request.enabled=true
feign.compression.response.enabled=true

Feign请求压缩为您提供的设置类似于您为web服务器设置的设置:

feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048

这些属性使您可以选择压缩媒体类型和最小请求阈值长度。

23.9 Feign日志记录

为每个创建的Feign客户端创建一个记录器。默认情况下,记录器的名称是用于创建Feign客户端的接口的全类名称。Feign日志记录仅响应DEBUG级别。

application.yml。 

logging.level.project.user.UserClient: DEBUG

您可以为每个客户端配置的Logger.Level对象告诉Feign要记录多少。选择是:

  • NONE,无日志记录(DEFAULT)。
  • BASIC,仅记录请求方法和URL以及响应状态代码和执行时间。
  • HEADERS,记录基本信息以及请求和响应头。
  • FULL,记录请求和响应的标题,正文和元数据。

例如,以下内容会将Logger.Level设置为FULL

@Configuration
public class FooConfiguration {
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

23.10 Feign @QueryMap支持

OpenFeign @QueryMap批注支持将POJO用作GET参数映射。不幸的是,默认的OpenFeign QueryMap注释与Spring不兼容,因为它缺少value属性。

Spring Cloud OpenFeign提供等效的@SpringQueryMap批注,该批注用于将POJO或Map参数注释为查询参数映射。

例如,Params类定义参数param1param2

// Params.java
public class Params {
    private String param1;
    private String param2;

    // [Getters and setters omitted for brevity]
}

以下伪装客户端通过使用@SpringQueryMap批注来使用Params类:

@FeignClient("demo")
public class DemoTemplate {

    @GetMapping(path = "/demo")
    String demoEndpoint(@SpringQueryMap Params params);
}

23.11故障排除

23.11.1早期初始化错误

根据您使用Feign客户端的方式,启动应用程序时可能会看到初始化错误。要变通解决此问题,您可以在自动接线客户端时使用ObjectProvider

@Autowired
ObjectProvider<TestFeginClient> testFeginClient;

第五部分。Spring Cloud Stream

24. Spring的数据集成之旅简史

Spring的数据集成之旅始于Spring Integration通过其编程模型,它为开发人员提供了一致的开发经验,以构建可以包含企业集成模式以与外部系统(例如数据库,消息代理等)连接的应用程序

快进到云时代,微服务已在企业环境中变得突出。Spring Boot改变了开发人员构建应用程序的方式。借助Spring的编程模型和Spring Boot处理的运行时职责,无缝开发了基于生产,生产级Spring的独立微服务。

为了将其扩展到数据集成工作负载,Spring Integration和Spring Boot被放到一个新项目中。Spring Cloud Stream出生了。

使用Spring Cloud Stream,开发人员可以:*隔离地构建,测试,迭代和部署以数据为中心的应用程序。*应用现代微服务架构模式,包括通过消息传递进行组合。*以事件为中心的思维将应用程序职责分离。事件可以表示及时发生的事件,下游消费者应用程序可以在不知道事件起源或生产者身份的情况下做出反应。*将业务逻辑移植到消息代理(例如RabbitMQ,Apache Kafka,Amazon Kinesis)上。*通过使用项目Reactor的Flux和Kafka Streams API,可以在基于通道的应用程序和基于非通道的应用程序绑定方案之间进行互操作,以支持无状态和有状态的计算。*依靠框架对常见用例的自动内容类型支持。可以扩展到不同的数据转换类型。

25.快速入门

您可以按照以下三步指南在不到5分钟的时间内尝试Spring Cloud Stream。

我们向您展示如何创建一个Spring Cloud Stream应用程序,该应用程序接收来自您选择的消息传递中间件的消息(稍后会详细介绍),并将接收到的消息记录到控制台。我们称之为LoggingConsumer尽管不是很实用,但是它很好地介绍了一些主要概念和抽象,使您更容易理解本用户指南的其余部分。

三个步骤如下:

25.1使用Spring Initializr创建示例应用程序

要开始使用,请访问Spring Initializr从那里,您可以生成我们的LoggingConsumer应用程序。为此:

  1. 在“ 依赖关系”部分,开始输入stream云流选项出现时,选择它。
  2. 开始输入“ kafka”或“兔子”。
  3. 选择KafkaRabbitMQ

    基本上,您选择应用程序绑定到的消息传递中间件。我们建议您使用已经安装的那种,或者对安装和运行感到更自在。另外,从“启动程序”屏幕上可以看到,还有一些其他选项可以选择。例如,您可以选择Gradle作为构建工具,而不是Maven(默认设置)。

  4. 工件字段中,输入“ logging-consumer”。

    Artifact字段的值成为应用程序名称。如果您选择RabbitMQ作为中间件,则Spring Initializr现在应该如下所示:

    流初始化
  5. 单击生成项目按钮。

    这样做会将生成的项目的压缩版本下载到硬盘上。

  6. 将文件解压缩到要用作项目目录的文件夹中。
[提示]提示

我们鼓励您探索Spring Initializr中可用的许多可能性。它使您可以创建许多不同种类的Spring应用程序。

25.2将项目导入IDE

现在,您可以将项目导入到IDE中。请记住,取决于IDE,您可能需要遵循特定的导入过程。例如,根据项目的生成方式(Maven或Gradle),您可能需要遵循特定的导入过程(例如,在Eclipse或STS中,您需要使用File→Import→Maven→现有的Maven项目)。

导入后,该项目必须没有任何错误。另外,src/main/java应该包含com.example.loggingconsumer.LoggingConsumerApplication

从技术上讲,此时,您可以运行应用程序的主类。它已经是有效的Spring Boot应用程序。但是,它没有任何作用,因此我们想添加一些代码。

25.3添加消息处理程序,构建并运行

修改com.example.loggingconsumer.LoggingConsumerApplication类,如下所示:

@SpringBootApplication
@EnableBinding(Sink.class)
public class LoggingConsumerApplication {

	public static void main(String[] args) {
		SpringApplication.run(LoggingConsumerApplication.class, args);
	}

	@StreamListener(Sink.INPUT)
	public void handle(Person 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;
		}
	}
}

从前面的清单中可以看到:

  • 我们已经使用@EnableBinding(Sink.class)启用了Sink绑定(输入无输出)。这样做会向框架发出信号,以启动对消息传递中间件的绑定,在该消息传递中间件自动创建绑定到Sink.INPUT通道的目的地(即队列,主题和其他)。
  • 我们添加了一个handler方法来接收类型为Person的传入消息。这样做可以使您看到框架的核心功能之一:它尝试自动将传入的消息有效负载转换为类型Person

您现在有了一个功能齐全的Spring Cloud Stream应用程序,该应用程序确实侦听消息。为了简单起见,我们从这里开始,假设您在第一步中选择了RabbitMQ 假设已经安装并运行了RabbitMQ,则可以通过在IDE中运行其main方法来启动应用程序。

您应该看到以下输出:

	--- [ 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/[email protected] . .
	. . .
	--- [ 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发送消息。anonymous.CbMIwdkJSBO1ZoPDOtHtCg部分代表组名并已生成,因此在您的环境中它一定是不同的。对于更可预测的内容,可以通过设置spring.cloud.stream.bindings.input.group=hello(或您喜欢的任何名称)来使用显式组名。

消息的内容应为Person类的JSON表示形式,如下所示:

{"name":"Sam Spade"}

然后,在控制台中,您应该看到:

Received: Sam Spade

您还可以将应用程序生成并打包到引导jar中(使用./mvnw clean install),并使用java -jar命令运行生成的JAR。

现在,您有了一个正在运行的(尽管非常基础的)Spring Cloud Stream应用程序。

26. 2.0中有什么新功能?

Spring Cloud Stream引入了许多新功能,增强功能和更改。以下各节概述了最值得注意的部分:

26.1新功能和组件

  • 轮询使用者:引入轮询使用者,使应用程序可以控制消息处理速率。请参见第29.3.5,‘使用轮询消费者 ’ ”的更多细节。您也可以阅读此博客文章以获取更多详细信息。
  • 千分尺支持:度量标准已切换为使用千分尺MeterRegistry也以bean的形式提供,以便自定义应用程序可以将其自动连线以捕获自定义指标。有关更多详细信息请参见第37章,度量标准发射器
  • 新的执行器绑定控件:新的执行器绑定控件使您可以可视化并控制绑定的生命周期。有关更多详细信息,请参见第30.6节“绑定可视化和控件”
  • 可配置的RetryTemplate:除了提供配置RetryTemplate的属性外,我们现在还允许您提供自己的模板,有效地覆盖了框架提供的模板。要使用它,请在您的应用程序中将其配置为@Bean

26.2显着增强

此版本包括以下显着增强:

26.2.1执行器和Web依赖项现在都是可选的

如果既不需要执行器也不需要web依赖项,那么此更改将减少已部署应用程序的占用空间。通过手动添加以下依赖项之一,它还使您可以在反应式和常规web范式之间切换。

以下清单显示了如何添加常规的web框架:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
</dependency>

以下清单显示了如何添加反应式web框架:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

下表显示了如何添加执行器依赖性:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

26.2.2内容类型协商的改进

Verion 2.0的核心主题之一是围绕内容类型协商和消息转换的改进(在一致性和性能方面)。以下摘要概述了该领域的显着变化和改进。有关更多详细信息请参见第32章,内容类型协商部分。此外,此博客文章还包含更多详细信息。

  • 现在,所有消息转换MessageConverter对象处理。
  • 我们引入了@StreamMessageConverter批注以提供自定义MessageConverter对象。
  • 我们引入了默认的Content Type作为application/json,在迁移1.3应用程序或以混合模式(即1.3生产者→2.0消费者)进行操作时,需要考虑该默认值。
  • 在无法确定所提供的MessageHandler的参数类型的情况下,带有文本有效载荷且contentTypetext/…​…​/json的消息不再转换为Message<String>public void handle(Message<?> message)public void handle(Object payload))。此外,强参数类型可能不足以正确地转换消息,因此contentType标头可能被某些MessageConverters用作补充。

26.3显着弃用

从2.0版开始,不推荐使用以下项目:

26.3.1 Java序列化(Java本机和Kryo​​)

JavaSerializationMessageConverterKryoMessageConverter暂时保留。但是,我们计划将来将它们移出核心软件包和支持。弃用此文件的主要原因是要标记基于类型,特定于语言的序列化可能在分布式环境中引起的问题,在该环境中,生产者和使用者可能依赖于不同的JVM版本或具有不同版本的支持库(即Kryo)。我们还想提请注意这样一个事实,即消费者和生产者甚至可能都不是基于Java的,因此多语言风格的序列化(即JSON)更适合。

26.3.2不推荐使用的类和方法

以下是显着弃用的快速摘要。有关更多详细信息,请参见相应的{spring-cloud-stream-javadoc-current} [javadoc]。

  • SharedChannelRegistry.使用SharedBindingTargetRegistry
  • Bindings.符合条件的Beans已通过其类型唯一标识,例如,提供了SourceProcessor或自定义绑定:
public interface Sample {
	String OUTPUT = "sampleOutput";

	@Output(Sample.OUTPUT)
	MessageChannel output();
}
  • HeaderMode.raw.使用noneheadersembeddedHeaders
  • ProducerProperties.partitionKeyExtractorClass赞成partitionKeyExtractorName,而ProducerProperties.partitionSelectorClass赞成partitionSelectorName此更改可确保Spring配置和管理两个组件,并以Spring友好的方式对其进行引用。
  • BinderAwareRouterBeanPostProcessor.在保留该组件的同时,它不再是BeanPostProcessor,并且将来会重命名。
  • BinderProperties.setEnvironment(Properties environment).使用BinderProperties.setEnvironment(Map<String, Object> environment)

本节将详细介绍如何使用Spring Cloud Stream。它涵盖了诸如创建和运行流应用程序之类的主题。

27.简介Spring Cloud Stream

Spring Cloud Stream是用于构建消息驱动的微服务应用程序的框架。Spring Cloud Stream在Spring Boot的基础上创建了独立的生产级Spring应用程序,并使用Spring Integration提供了到消息代理的连接。它提供了来自多家供应商的中间件的合理配置,并介绍了持久性发布-订阅语义,使用者组和分区的概念。

您可以在应用程序中添加@EnableBinding批注,以立即连接到消息代理,还可以在方法中添加@StreamListener,以使其接收流处理的事件。以下示例显示了接收外部消息的接收器应用程序:

@SpringBootApplication
@EnableBinding(Sink.class)
public class VoteRecordingSinkApplication {

  public static void main(String[] args) {
    SpringApplication.run(VoteRecordingSinkApplication.class, args);
  }

  @StreamListener(Sink.INPUT)
  public void processVote(Vote vote) {
      votingService.recordVote(vote);
  }
}

@EnableBinding批注将一个或多个接口作为参数(在这种情况下,该参数是单个Sink接口)。接口声明输入和输出通道。Spring Cloud Stream提供了SourceSinkProcessor接口。您也可以定义自己的接口。

以下清单显示了Sink接口的定义:

public interface Sink {
  String INPUT = "input";

  @Input(Sink.INPUT)
  SubscribableChannel input();
}

@Input注释标识一个输入通道,接收到的消息通过该输入通道进入应用程序。@Output注释标识一个输出通道,已发布的消息通过该输出通道离开应用程序。@Input@Output批注可以使用频道名称作为参数。如果未提供名称,则使用带注释的方法的名称。

Spring Cloud Stream为您创建接口的实现。您可以通过自动装配在应用程序中使用它,如以下示例所示(来自测试用例):

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = VoteRecordingSinkApplication.class)
@WebAppConfiguration
@DirtiesContext
public class StreamApplicationTests {

  @Autowired
  private Sink sink;

  @Test
  public void contextLoads() {
    assertNotNull(this.sink.input());
  }
}

28.主要概念

Spring Cloud Stream提供了许多抽象和原语,简化了消息驱动的微服务应用程序的编写。本节概述以下内容:

28.1应用模型

Spring Cloud Stream应用程序由与中间件无关的内核组成。该应用程序通过Spring Cloud Stream注入到其中的输入和输出通道与外界进行通信。通道通过特定于中间件的Binder实现与外部代理连接。

图28.1 Spring Cloud Stream申请

带粘合剂的SCSt

28.1.1 Fat JAR

可以从IDE以独立模式运行Spring Cloud Stream应用程序以进行测试。要在生产环境中运行Spring Cloud Stream应用程序,您可以使用为Maven或Gradle提供的标准Spring Boot工具来创建可执行(或fat)JAR。有关更多详细信息,请参见Spring Boot参考指南

28.2 Binder抽象

Spring Cloud Stream为KafkaRabbit MQ提供了Binder实现Spring Cloud Stream还包括一个TestSupportBinder,它使通道保持不变,因此测试可以与通道直接交互并可靠地断言所接收的内容。您也可以使用可扩展的API编写自己的Binder。

Spring Cloud Stream使用Spring Boot进行配置,而Binder抽象使Spring Cloud Stream应用程序可以灵活地连接中间件。例如,部署者可以在运行时动态选择通道连接到的目的地(例如Kafka主题或RabbitMQ交换)。可以通过外部配置属性以及Spring Boot支持的任何形式(包括应用程序参数,环境变量以及application.ymlapplication.properties文件)提供这种配置。第27章“ 介绍Spring Cloud Stream”的接收器示例中,将spring.cloud.stream.bindings.input.destination应用程序属性设置为raw-sensor-data会使其从raw-sensor-data Kafka主题或绑定到该队列的队列中读取raw-sensor-data RabbitMQ交换。

Spring Cloud Stream自动检测并使用在类路径上找到的活页夹。您可以使用具有相同代码的不同类型的中间件。为此,在构建时包括一个不同的活页夹。对于更复杂的用例,您还可以在应用程序中打包多个活页夹,并在运行时选择活页夹(甚至为不同的通道使用不同的活页夹)。

28.3持久的发布-订阅支持

应用程序之间的通信遵循发布-订阅模型,其中数据通过共享主题进行广播。在下图中可以看到,该图显示了一组交互的Spring Cloud Stream应用程序的典型部署。

图28.2 Spring Cloud Stream发布-订阅

SCSt传感器

传感器报告给HTTP端点的数据将发送到名为raw-sensor-data的公共目标。从目的地开始,它由计算时间窗平均值的微服务应用程序和另一个将原始数据提取到HDFS(Hadoop分布式文件系统)的微服务应用程序独立处理。为了处理数据,两个应用程序都在运行时将主题声明为其输入。

发布-订阅通信模型降低了生产者和使用者的复杂性,并允许在不中断现有流程的情况下将新应用添加到拓扑中。例如,在平均计算应用程序的下游,您可以添加一个应用程序,该应用程序计算用于显示和监视的最高温度值。然后,您可以添加另一个解释相同平均值流以进行故障检测的应用程序。通过共享主题而不是点对点队列进行所有通信可以减少微服务之间的耦合。

尽管发布-订阅消息传递的概念并不是新概念,但是Spring Cloud Stream采取了额外的步骤,使其成为其应用程序模型的明智选择。通过使用本机中间件支持,Spring Cloud Stream还简化了跨不同平台的发布-订阅模型的使用。

28.4消费群体

尽管发布-订阅模型使通过共享主题轻松连接应用程序变得很重要,但是通过创建给定应用程序的多个实例进行扩展的能力同样重要。这样做时,会将应用程序的不同实例置于竞争的消费者关系中,在该消费者关系中,仅其中一个实例可以处理给定消息。

Spring Cloud Stream通过消费者群体的概念对这种行为进行建模。(Spring Cloud Stream消费者组类似于Kafka消费者组并受其启发。)每个消费者绑定都可以使用spring.cloud.stream.bindings.<channelName>.group属性来指定组名。对于下图所示的消费者,此属性将设置为spring.cloud.stream.bindings.<channelName>.group=hdfsWritespring.cloud.stream.bindings.<channelName>.group=average

图28.3 Spring Cloud Stream消费群体

SCSt小组

订阅给定目标的所有组都将收到已发布数据的副本,但是每个组中只有一个成员从该目标接收给定消息。默认情况下,未指定组时,Spring Cloud Stream会将应用程序分配给与所有其他使用者组具有发布-订阅关系的匿名且独立的单成员使用者组。

28.5消费者类型

支持两种类型的使用者:

  • 消息驱动(有时称为异步)
  • 轮询(有时称为同步)

在2.0版之前,仅支持异步使用者。消息一旦可用,就会被传递,并且有线程可以处理它。

当您希望控制消息的处理速率时,可能需要使用同步使用者。

28.5.1耐久性

与公认的Spring Cloud Stream应用程序模型一致,消费者组订阅是持久的。也就是说,活页夹实现可确保组订阅是持久的,并且一旦创建了至少一个组订阅,该组将接收消息,即使在组中所有应用程序停止时发送消息也是如此。

[注意]注意

匿名订阅本质上是非持久的。对于某些活页夹实现(例如RabbitMQ),可能具有非持久的组订阅。

通常,在将应用程序绑定到给定目标时,最好始终指定使用者组。扩展Spring Cloud Stream应用程序时,必须为其每个输入绑定指定使用者组。这样做可以防止应用程序的实例接收重复的消息(除非需要这种行为,这是不寻常的)。

28.6分区支持

Spring Cloud Stream支持在给定应用程序的多个实例之间分区数据。在分区方案中,物理通信介质(例如代理主题)被视为结构化为多个分区。一个或多个生产者应用程序实例将数据发送到多个消费者应用程序实例,并确保由共同特征标识的数据由同一消费者实例处理。

Spring Cloud Stream提供了用于以统一方式实现分区处理用例的通用抽象。因此,无论代理本身是否自然地被分区(例如,Kafka)(例如,RabbitMQ),都可以使用分区。

图28.4。Spring Cloud Stream分区

SCSt分区

分区是有状态处理中的关键概念,对于确保所有相关数据都一起处理,分区是至关重要的(出于性能或一致性方面的考虑)。例如,在带时间窗的平均计算示例中,重要的是,来自任何给定传感器的所有测量都应由同一应用实例处理。

[注意]注意

要设置分区处理方案,必须同时配置数据产生端和数据消耗端。

29.编程模型

要了解编程模型,您应该熟悉以下核心概念:

  • 目标Binders:负责与外部消息传递系统集成的组件。
  • 目标绑定:在外部消息传递系统和应用程序之间提供的消息的生产者使用者(由目标Binders创建)之间的桥梁
  • 消息:生产者和消费者用来与目的地Binders(以及因此通过外部消息系统进行的其他应用程序)通信的规范数据结构。
SCSt概述

29.1目的地Binders

目标Binders是Spring Cloud Stream的扩展组件,负责提供必要的配置和实现以促进与外部消息传递系统的集成。这种集成负责连接,委派和与生产者和消费者之间的消息路由,数据类型转换,用户代码调用等等。

Binders承担了许多样板工作,否则这些工作就落在了您的肩上。但是,要实现这一点,活页夹仍然需要用户提供的一些简单但需要的指令集形式的帮助,通常以某种类型的配置形式出现。

尽管讨论所有可用的绑定器和绑定配置选项(本手册的其余部分都涉及它们)不在本节的讨论范围之内,但 目标绑定确实需要特别注意。下一节将详细讨论。

29.2目标绑定

如前所述,目标绑定提供了外部消息传递系统与应用程序提供的生产者消费者之间的桥梁

将@EnableBinding批注应用于应用程序的配置类之一可定义目标绑定。@EnableBinding注释本身使用@Configuration进行元注释,并触发Spring Cloud Stream基础结构的配置。

下面的示例显示了一个功能完整且运行正常的Spring Cloud Stream应用程序,该应用程序从INPUT目标接收的消息净荷为String类型(请参见第32章,内容类型协商部分),并将其记录到控制台,并将其转换为大写字母后将其发送到OUTPUT目标。

@SpringBootApplication
@EnableBinding(Processor.class)
public class MyApplication {

	public static void main(String[] args) {
		SpringApplication.run(MyApplication.class, args);
	}

	@StreamListener(Processor.INPUT)
	@SendTo(Processor.OUTPUT)
	public String handle(String value) {
		System.out.println("Received: " + value);
		return value.toUpperCase();
	}
}

如您所见,@EnableBinding批注可以将一个或多个接口类作为参数。这些参数称为绑定,它们包含表示可绑定组件的方法这些组件通常是基于通道的活页夹(例如Rabbit,Kafka等)的消息通道(请参见Spring消息传递)。但是,其他类型的绑定可以为相应技术的本机功能提供支持。例如,Kafka Streams绑定器(以前称为KStream)允许直接绑定到Kafka Streams(有关更多详细信息,请参见Kafka Streams)。

Spring Cloud Stream已经为典型的消息交换合同提供了绑定接口,其中包括:

  • 接收器:通过提供消费消息的目的地来标识消息消费者的合同。
  • 源:通过提供将生成的消息发送到的目的地,来标识消息生产者的合同。
  • 处理器:通过公开两个允许使用和产生消息的目的地,封装了接收器和源协定。
public interface Sink {

  String INPUT = "input";

  @Input(Sink.INPUT)
  SubscribableChannel input();
}
public interface Source {

  String OUTPUT = "output";

  @Output(Source.OUTPUT)
  MessageChannel output();
}
public interface Processor extends Source, Sink {}

尽管前面的示例满足了大多数情况,但是您也可以通过定义自己的绑定接口并使用@Input@Output批注来标识实际的可绑定组件,从而定义自己的合同

例如:

public interface Barista {

    @Input
    SubscribableChannel orders();

    @Output
    MessageChannel hotDrinks();

    @Output
    MessageChannel coldDrinks();
}

将上一个示例中显示的接口用作@EnableBinding的参数将分别触发三个绑定通道的创建,分别命名为ordershotDrinkscoldDrinks

您可以根据需要提供任意数量的绑定接口,作为@EnableBinding批注的参数,如以下示例所示:

@EnableBinding(value = { Orders.class, Payment.class })

在Spring Cloud Stream中,可绑定的MessageChannel组件是Spring消息传递MessageChannel(用于出站)及其扩展名SubscribableChannel(用于入站)。

可轮询的目标绑定

尽管前面描述的绑定支持基于事件的消息使用,但是有时您需要更多控制,例如使用率。

从2.0版开始,您现在可以绑定可轮询的使用者:

以下示例显示了如何绑定可轮询的使用者:

public interface PolledBarista {

    @Input
    PollableMessageSource orders();
	. . .
}

在这种情况下,PollableMessageSource的实现绑定到orders“通道”。有关更多详细信息请参见第29.3.5节“使用轮询的使用者”

自定义频道名称

通过使用@Input@Output批注,可以为该通道指定自定义的通道名称,如以下示例所示:

public interface Barista {
    @Input("inboundOrders")
    SubscribableChannel orders();
}

在前面的示例中,创建的绑定通道被命名为inboundOrders

通常,您不需要直接访问各个通道或绑定(除非通过@EnableBinding注释对其进行配置)。但是,您有时可能会遇到诸如测试或其他极端情况的情况。

除了为每个绑定生成通道并将其注册为Spring beans外,对于每个绑定接口,Spring Cloud Stream还会生成一个实现该接口的bean。这意味着您可以通过在应用程序中自动接线来访问表示绑定或各个通道的接口,如以下两个示例所示:

自动接线绑定界面

@Autowire
private Source source

public void sayHello(String name) {
    source.output().send(MessageBuilder.withPayload(name).build());
}

自动连线个别频道

@Autowire
private MessageChannel output;

public void sayHello(String name) {
    output.send(MessageBuilder.withPayload(name).build());
}

对于自定义通道名称或在需要特别命名通道的多通道方案中,您也可以使用标准Spring的@Qualifier批注。

下面的示例演示如何以这种方式使用@Qualifier批注:

@Autowire
@Qualifier("myChannel")
private MessageChannel output;

29.3产生和消耗消息

您可以使用Spring Integration注释或Spring Cloud Stream本机注释编写Spring Cloud Stream应用程序。

29.3.1 Spring Integration支持

Spring Cloud Stream建立在Enterprise Integration Patterns定义的概念和模式的基础之上,并依靠其内部实现依赖于Spring项目组合Spring Integration框架中已经建立且流行的Enterprise Integration Patterns实现

因此,它支持Spring Integration已经建立的基础,语义和配置选项是很自然的。

例如,您可以将Source的输出通道附加到MessageSource并使用熟悉的@InboundChannelAdapter注释,如下所示:

@EnableBinding(Source.class)
public class TimerSource {

  @Bean
  @InboundChannelAdapter(value = Source.OUTPUT, poller = @Poller(fixedDelay = "10", maxMessagesPerPoll = "1"))
  public MessageSource<String> timerMessageSource() {
    return () -> new GenericMessage<>("Hello Spring Cloud Stream");
  }
}

同样,可以在提供处理器绑定合同的消息处理程序方法的实现时使用@Transformer或@ServiceActivator ,如以下示例所示:

@EnableBinding(Processor.class)
public class TransformProcessor {
  @Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
  public Object transform(String message) {
    return message.toUpperCase();
  }
}
[注意]注意

尽管这可能会略过一些,但重要的是要了解,当您使用@StreamListener注释从同一绑定进行消费时,将使用pub-sub模型。@StreamListener注释的每种方法都收到其自己的消息副本,并且每种方法都有其自己的使用者组。但是,如果通过使用Spring Integration批注之一(例如@Aggregator@Transformer@ServiceActivator)从同一绑定中消费,则这些消费在竞争模型中。没有为每个订阅创建单独的消费者组。

29.3.2使用@StreamListener注释

作为对Spring Integration支持的补充,Spring Cloud Stream提供了自己的@StreamListener注释,其模仿其他Spring消息注释(@MessageMapping@JmsListener@RabbitListener等)并提供便利,例如基于内容的路由等。

@EnableBinding(Sink.class)
public class VoteHandler {

  @Autowired
  VotingService votingService;

  @StreamListener(Sink.INPUT)
  public void handle(Vote vote) {
    votingService.record(vote);
  }
}

与其他Spring消息传递方法一样,方法参数可以用@Payload@Headers@Header进行注释。

对于返回数据的方法,必须使用@SendTo批注为该方法返回的数据指定输出绑定目标,如以下示例所示:

@EnableBinding(Processor.class)
public class TransformProcessor {

  @Autowired
  VotingService votingService;

  @StreamListener(Processor.INPUT)
  @SendTo(Processor.OUTPUT)
  public VoteResult handle(Vote vote) {
    return votingService.record(vote);
  }
}

29.3.3使用@StreamListener进行基于内容的路由

Spring Cloud Stream支持根据条件将消息调度到用@StreamListener注释的多个处理程序方法。

为了有资格支持条件分派,一种方法必须满足以下条件:

  • 它不能返回值。
  • 它必须是单独的消息处理方法(不支持反应性API方法)。

该条件由注释的condition参数中的SpEL表达式指定,并针对每条消息进行评估。所有与条件匹配的处理程序都在同一线程中调用,并且不必假设调用的顺序。

在具有分配条件的@StreamListener的以下示例中,所有带有标头type且具有值bogey的消息都被分配到receiveBogey方法,所有带有标头{11的消息值bacall的/}发送到receiveBacall方法。

@EnableBinding(Sink.class)
@EnableAutoConfiguration
public static class TestPojoWithAnnotatedArguments {

    @StreamListener(target = Sink.INPUT, condition = "headers['type']=='bogey'")
    public void receiveBogey(@Payload BogeyPojo bogeyPojo) {
       // handle the message
    }

    @StreamListener(target = Sink.INPUT, condition = "headers['type']=='bacall'")
    public void receiveBacall(@Payload BacallPojo bacallPojo) {
       // handle the message
    }
}

condition上下文中的内容类型协商

了解使用@StreamListener参数condition的基于内容的路由背后的一些机制很重要,尤其是在整个消息类型的上下文中。如果在继续之前熟悉第32章,内容类型协商这也可能会有所帮助

请考虑以下情形:

@EnableBinding(Sink.class)
@EnableAutoConfiguration
public static class CatsAndDogs {

    @StreamListener(target = Sink.INPUT, condition = "payload.class.simpleName=='Dog'")
    public void bark(Dog dog) {
       // handle the message
    }

    @StreamListener(target = Sink.INPUT, condition = "payload.class.simpleName=='Cat'")
    public void purr(Cat cat) {
       // handle the message
    }
}

前面的代码是完全有效的。它可以毫无问题地进行编译和部署,但是永远不会产生您期望的结果。

这是因为您正在测试的东西在您期望的状态下尚不存在。这是因为消息的有效负载尚未从有线格式(byte[])转换为所需的类型。换句话说,它尚未经过第32章,内容类型协商中描述的类型转换过程

因此,除非使用SPeL表达式评估原始数据(例如,字节数组中第一个字节的值),否则请使用基于消息标头的表达式(例如condition = "headers['type']=='dog'")。

[注意]注意

目前,仅基于通道的绑定程序(不支持响应编程)支持通过@StreamListener条件进行分派。

29.3.4 Spring Cloud功能支持

从Spring Cloud Stream v2.1开始,定义流处理程序源的另一种方法是使用对Spring Cloud函数的内置支持,其中可以将它们表示为java.util.function.[Supplier/Function/Consumer]类型的beans。

若要指定要绑定到绑定公开的外部目标的功能bean,必须提供spring.cloud.stream.function.definition属性。

这是Processor应用程序将消息处理程序公开为java.util.function.Function的示例

@SpringBootApplication
@EnableBinding(Processor.class)
public class MyFunctionBootApp {

	public static void main(String[] args) {
		SpringApplication.run(MyFunctionBootApp.class, "--spring.cloud.stream.function.definition=toUpperCase");
	}

	@Bean
	public Function<String, String> toUpperCase() {
		return s -> s.toUpperCase();
	}
}

在上面的代码中,我们仅定义了类型为java.util.function.Function的bean(称为toUpperCase)并将其标识为bean,用作消息处理程序,其“输入”和“输出”必须绑定到外部目标由处理器绑定公开。

以下是支持源,处理器和接收器的简单功能应用程序的示例。

这是定义为java.util.function.Supplier的Source应用程序的示例

@SpringBootApplication
@EnableBinding(Source.class)
public static class SourceFromSupplier {
	public static void main(String[] args) {
		SpringApplication.run(SourceFromSupplier.class, "--spring.cloud.stream.function.definition=date");
	}
	@Bean
	public Supplier<Date> date() {
		return () -> new Date(12345L);
	}
}

这是定义为java.util.function.Function的Processor应用程序的示例

@SpringBootApplication
@EnableBinding(Processor.class)
public static class ProcessorFromFunction {
	public static void main(String[] args) {
		SpringApplication.run(ProcessorFromFunction.class, "--spring.cloud.stream.function.definition=toUpperCase");
	}
	@Bean
	public Function<String, String> toUpperCase() {
		return s -> s.toUpperCase();
	}
}

这是一个定义为java.util.function.Consumer的接收器应用程序的示例

@EnableAutoConfiguration
@EnableBinding(Sink.class)
public static class SinkFromConsumer {
	public static void main(String[] args) {
		SpringApplication.run(SinkFromConsumer.class, "--spring.cloud.stream.function.definition=sink");
	}
	@Bean
	public Consumer<String> sink() {
		return System.out::println;
	}
}

功能组成

使用此编程模型,您还可以从功能组合中受益,在该功能组合中,您可以从一组简单的函数中动态组成复杂的处理程序。作为示例,我们将以下函数bean添加到上面定义的应用程序中

@Bean
public Function<String, String> wrapInQuotes() {
	return s -> "\"" + s + "\"";
}

并修改spring.cloud.stream.function.definition属性以反映您打算从'toUpperCase'和'wrapInQuotes'编写新函数的意图。为此,可以使用Spring Cloud函数使用|(管道)符号。因此,完成我们的示例,我们的属性现在将如下所示:

—spring.cloud.stream.function.definition=toUpperCase|wrapInQuotes

29.3.5使用轮询的使用者

总览

使用轮询的使用者时,您可以按需轮询PollableMessageSource考虑以下受调查消费者的示例:

public interface PolledConsumer {

    @Input
    PollableMessageSource destIn();

    @Output
    MessageChannel destOut();

}

给定上一个示例中的受调查消费者,您可以按以下方式使用它:

@Bean
public ApplicationRunner poller(PollableMessageSource destIn, MessageChannel destOut) {
    return args -> {
        while (someCondition()) {
            try {
                if (!destIn.poll(m -> {
                    String newPayload = ((String) m.getPayload()).toUpperCase();
                    destOut.send(new GenericMessage<>(newPayload));
                })) {
                    Thread.sleep(1000);
                }
            }
            catch (Exception e) {
                // handle failure
            }
        }
    };
}

PollableMessageSource.poll()方法采用一个MessageHandler参数(通常为lambda表达式,如此处所示)。如果收到并成功处理了消息,它将返回true

与消息驱动的使用者一样,如果MessageHandler引发异常,消息将发布到错误通道,如???”中所述。

通常,poll()方法会在MessageHandler退出时确认该消息。如果该方法异常退出,则该消息将被拒绝(不重新排队),但请参阅“处理错误”一节您可以通过对确认负责来覆盖该行为,如以下示例所示:

@Bean
public ApplicationRunner poller(PollableMessageSource dest1In, MessageChannel dest2Out) {
    return args -> {
        while (someCondition()) {
            if (!dest1In.poll(m -> {
                StaticMessageHeaderAccessor.getAcknowledgmentCallback(m).noAutoAck();
                // e.g. hand off to another thread which can perform the ack
                // or acknowledge(Status.REQUEUE)

            })) {
                Thread.sleep(1000);
            }
        }
    };
}
[重要]重要

您必须在某一时刻ack(或nack)消息,以避免资源泄漏。

[重要]重要

某些消息传递系统(例如Apache Kafka)在日志中维护简单的偏移量。如果传递失败,并用StaticMessageHeaderAccessor.getAcknowledgmentCallback(m).acknowledge(Status.REQUEUE);重新排队,则重新传递任何以后成功确认的消息。

还有一个重载的poll方法,其定义如下:

poll(MessageHandler handler, ParameterizedTypeReference<?> type)

type是一个转换提示,它允许转换传入的消息有效负载,如以下示例所示:

boolean result = pollableSource.poll(received -> {
			Map<String, Foo> payload = (Map<String, Foo>) received.getPayload();
            ...

		}, new ParameterizedTypeReference<Map<String, Foo>>() {});

处理错误

默认情况下,为可轮询源配置了一个错误通道。如果回调引发异常,则将ErrorMessage发送到错误通道(<destination>.<group>.errors);此错误通道也桥接到全局Spring Integration errorChannel

您可以使用@ServiceActivator订阅任何一个错误通道来处理错误。如果没有订阅,则将仅记录错误并确认消息成功。如果错误通道服务激活器引发异常,则该消息将被拒绝(默认情况下),并且不会重新发送。如果服务激活器抛出RequeueCurrentMessageException,则该消息将在代理处重新排队,并在随后的轮询中再次检索。

如果侦听器直接抛出RequeueCurrentMessageException,则如上所述,该消息将重新排队,并且不会发送到错误通道。

29.4错误处理

错误会发生,Spring Cloud Stream提供了几种灵活的机制来处理它们。错误处理有两种形式:

  • 应用程序:错误处理是在应用程序(自定义错误处理程序)中完成的。
  • 系统:将错误处理委托给绑定程序(重新排队,DL和其他)。注意,这些技术取决于绑定程序的实现和底层消息传递中间件的功能。

Spring Cloud Stream使用Spring重试库来促进成功的消息处理。有关更多详细信息请参见第29.4.3节“重试模板”但是,当所有方法均失败时,消息处理程序引发的异常将传播回绑定程序。那时,活页夹调用自定义错误处理程序或将错误传达回消息传递系统(重新排队,DLQ等)。

29.4.1应用程序错误处理

有两种类型的应用程序级错误处理。可以在每个绑定订阅中处理错误,或者全局处理程序可以处理所有绑定订阅错误。让我们查看详细信息。

图29.1 具有自定义和全局错误处理程序的Spring Cloud Stream接收器应用程序

自定义与全局错误通道

对于每个输入绑定,Spring Cloud Stream创建具有以下语义<destinationName>.errors的专用错误通道。

[注意]注意

<destinationName>由绑定的名称(例如input)和组的名称(例如myGroup)组成。

考虑以下:

spring.cloud.stream.bindings.input.group=myGroup
@StreamListener(Sink.INPUT) // destination name 'input.myGroup'
public void handle(Person value) {
	throw new RuntimeException("BOOM!");
}

@ServiceActivator(inputChannel = Processor.INPUT + ".myGroup.errors") //channel name 'input.myGroup.errors'
public void error(Message<?> message) {
	System.out.println("Handling ERROR: " + message);
}

在前面的示例中,目标名称为input.myGroup,专用错误通道名称为input.myGroup.errors

[注意]注意

@StreamListener批注的使用专门用于定义桥接内部通道和外部目标的绑定。假设目标特定错误通道没有关联的外部目标,则该通道是Spring Integration(SI)的特权。这意味着必须使用SI处理程序注释之一(即@ ServiceActivator,@ Transformer等)定义用于此类目标的处理程序。

[注意]注意

如果未指定group,则使用匿名组(类似于input.anonymous.2K37rb06Q6m2r51-SPIDDQ),这不适用于错误处理方案,因为在创建目标之前,您不知道它将是什么。

另外,如果您绑定到现有目的地,例如:

spring.cloud.stream.bindings.input.destination=myFooDestination
spring.cloud.stream.bindings.input.group=myGroup

完整的目标名称为myFooDestination.myGroup,然后专用错误通道名称为myFooDestination.myGroup.errors

回到例子...

预订名为input的通道的handle(..)方法会引发异常。给定错误通道input.myGroup.errors的订阅者,所有错误消息均由该订阅者处理。

如果您有多个绑定,则可能需要一个错误处理程序。Spring Cloud Stream 通过将每个单独的错误通道桥接到名为errorChannel的通道来自动提供对全局错误通道支持,从而允许单个订阅者处理所有错误,如以下示例所示:

@StreamListener("errorChannel")
public void error(Message<?> message) {
	System.out.println("Handling ERROR: " + message);
}

如果错误处理逻辑相同,则与哪个处理程序产生错误无关,这可能是一个方便的选择。

29.4.2系统错误处理

系统级错误处理意味着将错误传递回消息传递系统,并且鉴于并非每个消息传递系统都相同,因此各个粘合剂的功能可能有所不同。

也就是说,在本节中,我们解释了系统级错误处理背后的一般思想,并以Rabbit活页夹为例。注意:Kafka活页夹提供了类似的支持,尽管某些配置属性确实有所不同。另外,有关更多详细信息和配置选项,请参见各个活页夹的文档。

如果未配置内部错误处理程序,则错误将传播到绑定程序,而绑定程序随后会将这些错误传播回消息传递系统。根据消息传递系统的功能,此类系统可能会丢弃该消息,重新排队该消息以进行重新处理或将失败的消息发送给DLQRabbit和Kafka都支持这些概念。但是,其他联编程序可能没有,因此请参阅您单独的联编程序的文档,以获取有关受支持的系统级错误处理选项的详细信息。

删除失败的消息

默认情况下,如果未提供其他系统级配置,则消息传递系统将丢弃失败的消息。尽管在某些情况下可以接受,但在大多数情况下是不可接受的,我们需要一些恢复机制来避免消息丢失。

DLQ-死信队列

DLQ允许将失败的消息发送到特殊目标:-Dead Letter Queue

配置后,失败的消息将发送到此目标,以进行后续的重新处理或审核与对帐。

例如,继续前面的示例,并使用Rabbit活页夹设置DLQ,您需要设置以下属性:

spring.cloud.stream.rabbit.bindings.input.consumer.auto-bind-dlq=true

请记住,在以上属性中,input对应于输入目标绑定的名称。consumer指示它是消费者属性,auto-bind-dlq指示绑定程序为input目标配置DLQ,这将导致名为input.myGroup.dlq的附加Rabbit队列。

配置完成后,所有失败的消息都会通过错误消息路由到此队列,类似于以下内容:

delivery_mode:	1
headers:
x-death:
count:	1
reason:	rejected
queue:	input.hello
time:	1522328151
exchange:
routing-keys:	input.myGroup
Payload {"name”:"Bob"}

从上面可以看到,原始消息会保留下来以供进一步操作。

但是,您可能已经注意到的一件事是,有关消息处理的原始问题的信息有限。例如,您看不到与原始错误相对应的堆栈跟踪。要获取有关原始错误的更多相关信息,您必须设置一个附加属性:

spring.cloud.stream.rabbit.bindings.input.consumer.republish-to-dlq=true

这样做会强制内部错误处理程序在将错误消息发布到DLQ之前拦截该错误消息并向其添加其他信息。配置完成后,您会看到错误消息包含与原始错误有关的更多信息,如下所示:

delivery_mode:	2
headers:
x-original-exchange:
x-exception-message:	has an error
x-original-routingKey:	input.myGroup
x-exception-stacktrace:	org.springframework.messaging.MessageHandlingException: nested exception is
      org.springframework.messaging.MessagingException: has an error, failedMessage=GenericMessage [payload=byte[15],
      headers={amqp_receivedDeliveryMode=NON_PERSISTENT, amqp_receivedRoutingKey=input.hello, amqp_deliveryTag=1,
      deliveryAttempt=3, amqp_consumerQueue=input.hello, amqp_redelivered=false, id=a15231e6-3f80-677b-5ad7-d4b1e61e486e,
      amqp_consumerTag=amq.ctag-skBFapilvtZhDsn0k3ZmQg, contentType=application/json, timestamp=1522327846136}]
      at org.spring...integ...han...MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:107)
      at. . . . .
Payload {"name”:"Bob"}

这有效地结合了应用程序级和系统级的错误处理,以进一步协助下游故障排除机制。

重新排队失败的消息

如前所述,当前支持的活页夹(Rabbit和Kafka)依靠RetryTemplate来促进成功的消息处理。有关详细信息请参见第29.4.3节“重试模板”但是,对于max-attempts属性设置为1的情况,将禁用消息的内部重新处理。此时,您可以通过指示消息传递系统重新排队失败的消息来促进消息的重新处理(重试)。重新排队后,失败的消息将被发送回原始处理程序,从而创建一个重试循环。

如果错误的性质与某些资源的偶发性但短期不可用有关,则此选项可能是可行的。

为此,必须设置以下属性:

spring.cloud.stream.bindings.input.consumer.max-attempts=1
spring.cloud.stream.rabbit.bindings.input.consumer.requeue-rejected=true

在前面的示例中,max-attempts设置为1,实际上禁用了内部重试,而requeue-rejected重新排队拒绝消息的缩写)被设置为true设置后,失败的消息将重新提交给同一处理程序并连续循环,直到处理程序抛出AmqpRejectAndDontRequeueException为止,从本质上讲,您可以在处理程序本身内构建自己的重试逻辑。

29.4.3重试模板

RetryTemplateSpring重试库的一部分。尽管涵盖RetryTemplate的所有功能超出了本文档的范围,但我们将提及以下与RetryTemplate特别相关的使用者属性:

maxAttempts

处理消息的尝试次数。

默认值:3。

backOffInitialInterval

重试时的退避初始间隔。

默认值1000毫秒。

backOffMaxInterval

最大退避间隔。

默认值10000毫秒。

backOffMultiplier

退避乘数。

默认为2.0。

defaultRetryable

retryableExceptions中未列出的由侦听器引发的异常是否可以重试。

默认值:true

retryableExceptions

键中Throwable类名称的映射,值中布尔值的映射。指定将要重试的那些异常(和子类)。另请参见defaultRetriable示例:spring.cloud.stream.bindings.input.consumer.retryable-exceptions.java.lang.IllegalStateException=false

默认值:空。

尽管上述设置足以满足大多数自定义要求,但它们可能无法满足某些复杂的要求,此时,您可能希望提供自己的RetryTemplate实例。为此,在应用程序配置中将其配置为bean。应用程序提供的实例将覆盖框架提供的实例。另外,为避免冲突,必须将绑定程序要使用的RetryTemplate实例限定为@StreamRetryTemplate例如,

@StreamRetryTemplate
public RetryTemplate myRetryTemplate() {
    return new RetryTemplate();
}

从上面的示例中可以看到,由于@StreamRetryTemplate是合格的@Bean,因此无需使用@Bean对其进行注释。

29.5反应式编程支持

Spring Cloud Stream还支持使用反应式API,将传入和传出的数据作为连续的数据流进行处理。可通过spring-cloud-stream-reactive获得对反应式API的支持,需要将其显式添加到您的项目中。

具有响应式API的编程模型是声明性的。您可以使用描述从入站数据流到出站数据流的功能转换的运算符,而不是指定每个消息的处理方式。

目前Spring Cloud Stream仅支持Reactor API将来,我们打算支持基于反应式流的更通用的模型。

反应式编程模型还使用@StreamListener注释来设置反应式处理程序。区别在于:

  • @StreamListener批注不能指定输入或输出,因为它们作为参数提供并从方法返回值。
  • 该方法的参数必须用@Input@Output注释,分别指示传入和传出数据流连接到哪个输入或输出。
  • 该方法的返回值(如果有)用@Output注释,指示应该将数据发送到的输入。
[注意]注意

响应式编程支持需要Java 1.8。

[注意]注意

从Spring Cloud Stream 1.1.1起(从发行版Brooklyn.SR2开始),反应式编程支持要求使用Reactor 3.0.4.RELEASE及更高版本。不支持更早的Reactor版本(包括3.0.1.RELEASE,3.0.2.RELEASE和3.0.3.RELEASE)。spring-cloud-stream-reactive可传递地检索正确的版本,但是项目结构可以将io.projectreactor:reactor-core的版本管理为早期版本,尤其是在使用Maven时。使用Spring Initializr和Spring Boot 1.x生成的项目就是这种情况,该项目将Reactor版本覆盖为2.0.8.RELEASE在这种情况下,必须确保发布了正确版本的工件。您可以通过在项目中添加对版本为3.0.4.RELEASE或更高版本的io.projectreactor:reactor-core的直接依赖来实现。

[注意]注意

当前,术语反应式的使用是指正在使用的反应式API,而不是指执行模型是反应式的(也就是说,绑定的端点仍然使用“推”式而非“拉式”模型)。尽管通过使用Reactor提供了一些反压支持,但在将来的发行版中,我们确实打算通过对连接的中间件使用本机反应性客户端来完全支持反应性管道。

29.5.1基于Reactor的处理程序

基于Reactor的处理程序可以具有以下参数类型:

  • 对于带有@Input注释的参数,它支持Reactor Flux类型。入站Flux的参数化遵循与处理单个消息时相同的规则:可以是整个Message,可以是Message有效负载的POJO或由于以下原因而产生的POJO:基于Message内容类型标头的转换。提供了多个输入。
  • 对于带有Output注释的参数,它支持FluxSender类型,该类型将方法生成的Flux与输出连接起来。一般而言,仅在该方法可以具有多个输出时才建议将输出指定为参数。

基于Reactor的处理程序支持Flux的返回类型。在这种情况下,必须用@Output进行注释。当单个输出Flux可用时,建议使用该方法的返回值。

以下示例显示了基于Reactor的Processor

@EnableBinding(Processor.class)
@EnableAutoConfiguration
public static class UppercaseTransformer {

  @StreamListener
  @Output(Processor.OUTPUT)
  public Flux<String> receive(@Input(Processor.INPUT) Flux<String> input) {
    return input.map(s -> s.toUpperCase());
  }
}

使用输出参数的同一处理器看起来像以下示例:

@EnableBinding(Processor.class)
@EnableAutoConfiguration
public static class UppercaseTransformer {

  @StreamListener
  public void receive(@Input(Processor.INPUT) Flux<String> input,
     @Output(Processor.OUTPUT) FluxSender output) {
     output.send(input.map(s -> s.toUpperCase()));
  }
}

29.5.2反应源

Spring Cloud Stream反应性支持还提供了通过@StreamEmitter注释创建反应性源的功能。通过使用@StreamEmitter批注,可以将常规源转换为被动源。@StreamEmitter是方法级别的注释,用于将方法标记为用@EnableBinding声明的输出的发射器。您不能将@Input批注与@StreamEmitter一起使用,因为标有该批注的方法不会监听任何输入。而是用标记为@StreamEmitter的方法生成输出。遵循@StreamListener中使用的相同编程模型,@StreamEmitter还允许灵活地使用@Output批注,具体取决于方法是否具有任何参数,返回类型和其他考虑因素。

本节的其余部分包含使用各种样式的@StreamEmitter批注的示例。

以下示例每毫秒发出一次Hello, World消息,并发布到Reactor Flux中:

@EnableBinding(Source.class)
@EnableAutoConfiguration
public static class HelloWorldEmitter {

  @StreamEmitter
  @Output(Source.OUTPUT)
  public Flux<String> emit() {
    return Flux.intervalMillis(1)
            .map(l -> "Hello World");
  }
}

在前面的示例中,Flux中的结果消息被发送到Source的输出通道。

下一个示例是@StreamEmmitter的另一种形式,它发送Reactor Flux以下方法代替返回Flux,而是使用FluxSender从源代码中以编程方式发送Flux

@EnableBinding(Source.class)
@EnableAutoConfiguration
public static class HelloWorldEmitter {

  @StreamEmitter
  @Output(Source.OUTPUT)
  public void emit(FluxSender output) {
    output.send(Flux.intervalMillis(1)
            .map(l -> "Hello World"));
  }
}

下一个示例在功能和样式上与上述代码段完全相同。但是,它没有在方法上使用显式的@Output注释,而是在方法参数上使用了注释。

@EnableBinding(Source.class)
@EnableAutoConfiguration
public static class HelloWorldEmitter {

  @StreamEmitter
  public void emit(@Output(Source.OUTPUT) FluxSender output) {
    output.send(Flux.intervalMillis(1)
            .map(l -> "Hello World"));
  }
}

本节的最后一个示例是使用Reactive Streams Publisher API并利用Spring Integration Java DSL中对它的支持来编写反应源的另一种方式以下示例中的Publisher仍在幕后使用Reactor Flux,但是,从应用程序角度看,这对用户是透明的,并且对于Spring Integration仅需要响应流和Java DSL:

@EnableBinding(Source.class)
@EnableAutoConfiguration
public static class HelloWorldEmitter {

  @StreamEmitter
  @Output(Source.OUTPUT)
  @Bean
  public Publisher<Message<String>> emit() {
    return IntegrationFlows.from(() ->
                new GenericMessage<>("Hello World"),
        e -> e.poller(p -> p.fixedDelay(1)))
        .toReactivePublisher();
  }
}

30. Binders

Spring Cloud Stream提供了Binder抽象,用于连接到外部中间件上的物理目标。本节提供有关Binder SPI背后的主要概念,其主要组件以及特定于实现的详细信息。

30.1生产者和消费者

下图显示了生产者和消费者的一般关系:

图30.1。生产者和消费者

生产者消费者

生产者是将消息发送到通道的任何组件。可以将该通道绑定到具有该代理的Binder实现的外部消息代理。调用bindProducer()方法时,第一个参数是代理内目标的名称,第二个参数是生产者向其发送消息的本地通道实例,第三个参数包含属性(例如分区键表达式) ),以在为该通道创建的适配器中使用。

使用者是从通道接收消息的任何组件。与生产者一样,消费者的渠道可以绑定到外部消息代理。调用bindConsumer()方法时,第一个参数是目标名称,第二个参数提供逻辑消费者组的名称。由给定目标的使用者绑定表示的每个组都接收生产者发送到该目标的每个消息的副本(也就是说,它遵循常规的发布-订阅语义)。如果有多个使用相同组名绑定的使用者实例,那么消息将在这些使用者实例之间进行负载平衡,以便由生产者发送的每条消息仅在每个组内的单个使用者实例中被使用(也就是说,它遵循常规排队语义)。

30.2 Binder SPI

Binder SPI由许多接口,现成的实用程序类和发现策略组成,这些策略提供了用于连接到外部中间件的可插拔机制。

SPI的关键是Binder接口,这是将输入和输出连接到外部中间件的策略。以下清单显示了Binder接口的定义:

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);
}

该接口已参数化,提供了许多扩展点:

  • 输入和输出绑定目标。从1.0版开始,仅支持MessageChannel,但将来打算将其用作扩展点。
  • 扩展的使用者和生产者属性,允许特定的Binder实现添加可以以类型安全的方式支持的补充属性。

典型的活页夹实现包括以下内容:

  • 实现Binder接口的类;
  • 一个Spring @Configuration类,它创建类型为Binder的bean和中间件连接基础结构。
  • 在类路径上找到一个META-INF/spring.binders文件,其中包含一个或多个绑定程序定义,如以下示例所示:

    kafka:\
    org.springframework.cloud.stream.binder.kafka.config.KafkaBinderConfiguration

30.3 Binder检测

Spring Cloud Stream依赖于Binder SPI的实现来执行将通道连接到消息代理的任务。每个Binder实现通常都连接到一种消息传递系统。

30.3.1类路径检测

默认情况下,Spring Cloud Stream依靠Spring Boot的自动配置来配置绑定过程。如果在类路径上找到单个Binder实现,则Spring Cloud Stream将自动使用它。例如,旨在仅绑定到RabbitMQ的Spring Cloud Stream项目可以添加以下依赖项:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

有关其他绑定程序依赖项的特定Maven坐标,请参阅该绑定程序实现的文档。

类路径上的30.4倍数Binders

当类路径上存在多个绑定程序时,应用程序必须指示将哪个绑定程序用于每个通道绑定。每个活页夹配置都包含一个META-INF/spring.binders文件,它是一个简单的属性文件,如以下示例所示:

rabbit:\
org.springframework.cloud.stream.binder.rabbit.config.RabbitServiceAutoConfiguration

其他提供的活页夹实现(例如Kafka)也存在类似的文件,并且期望自定义活页夹实现也将提供它们。关键字表示绑定程序实现的标识名,而该值是逗号分隔的配置类列表,每个配置类都包含一个且仅一个bean类型为org.springframework.cloud.stream.binder.Binder的定义。

可以使用spring.cloud.stream.defaultBinder属性(例如,spring.cloud.stream.defaultBinder=rabbit)在全局上执行Binder选择,也可以通过在每个通道绑定上配置活页夹来分别进行Binder选择。例如,从Kafka读取并写入RabbitMQ的处理器应用程序(具有分别名为inputoutput的通道用于读取和写入)可以指定以下配置:

spring.cloud.stream.bindings.input.binder=kafka
spring.cloud.stream.bindings.output.binder=rabbit

30.5连接到多个系统

默认情况下,活页夹共享应用程序的Spring Boot自动配置,以便创建在类路径上找到的每个活页夹的一个实例。如果您的应用程序应连接到多个相同类型的代理,则可以指定多个绑定程序配置,每个配置具有不同的环境设置。

[注意]注意

启用显式绑定程序配置将完全禁用默认的绑定程序配置过程。如果这样做,则配置中必须包括所有正在使用的活页夹。打算透明使用Spring Cloud Stream的框架可以创建可以按名称引用的活页夹配置,但它们不会影响默认的活页夹配置。为此,活页夹配置可以将其defaultCandidate标志设置为false(例如spring.cloud.stream.binders.<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>

30.6绑定可视化和控制

从2.0版开始,Spring Cloud Stream支持通过Actuator端点进行绑定的可视化和控制。

从2.0版执行器开始,并且web是可选的,您必须首先添加web依赖项之一,然后手动添加执行器依赖项。以下示例说明如何为Web框架添加依赖项:

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
</dependency>

以下示例显示如何为WebFlux框架添加依赖项:

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

您可以添加执行器依赖项,如下所示:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
[注意]注意

要在Cloud Foundry中运行Spring Cloud Stream 2.0应用程序,必须将spring-boot-starter-webspring-boot-starter-actuator添加到类路径中。否则,由于运行状况检查失败,该应用程序将无法启动。

您还必须通过设置以下属性来启用bindings执行器端点:--management.endpoints.web.exposure.include=bindings

一旦满足这些先决条件。应用程序启动时,您应该在日志中看到以下内容:

: Mapped "{[/actuator/bindings/{name}],methods=[POST]. . .
: Mapped "{[/actuator/bindings],methods=[GET]. . .
: Mapped "{[/actuator/bindings/{name}],methods=[GET]. . .

要显示当前绑定,请访问以下URL:http://<host>:<port>/actuator/bindings

或者,要查看单个绑定,请访问类似于以下内容的URL之一:http://<host>:<port>/actuator/bindings/myBindingName

您还可以通过发布到相同的URL来停止,开始,暂停和恢复单个绑定,同时提供一个state作为JSON的参数,如以下示例所示:

curl -d'{“ state”:“ STOPPED”}'-H“内容类型:应用程序/ json” -X POST http:// <主机>:<port> / actuator / bindings / myBindingName curl -d'{ “ state”:“ STARTED”}'-H“内容类型:application / json” -X POST http:// <host>:<port> / actuator / bindings / myBindingName curl -d'{“ state”:“ PAUSED“}'-H”内容类型:application / json“ -X POST http:// <host>:<port> / actuator / bindings / myBindingName curl -d'{” state“:” RESUMED“}'- H“内容类型:应用程序/ json” -X POST http:// <主机>:<端口> / actuator / bindings / myBindingName

[注意]注意

PAUSEDRESUMED仅在相应的活页夹及其基础技术支持时起作用。否则,您会在日志中看到警告消息。当前,只有Kafka活页夹支持PAUSEDRESUMED状态。

30.7 Binder配置Properties

定制活页夹配置时,以下属性可用。这些属性通过org.springframework.cloud.stream.config.BinderProperties公开

它们必须以spring.cloud.stream.binders.<configurationName>为前缀。

类型

资料夹类型。它通常引用在类路径上找到的绑定器之一-特别是META-INF/spring.binders文件中的键。

默认情况下,它具有与配置名称相同的值。

继承环境

配置是否继承应用程序本身的环境。

默认值:true

环境

根可用于定制活页夹环境的一组属性。设置此属性后,在其中创建活页夹的上下文不是应用程序上下文的子级。该设置允许在粘合剂组分和应用组分之间完全分离。

默认值:empty

defaultCandidate

活页夹配置是被视为默认活页夹的候选者还是仅在明确引用时才可以使用。此设置允许添加活页夹配置,而不会干扰默认处理。

默认值:true

31.配置选项

Spring Cloud Stream支持常规配置选项以及绑定和活页夹的配置。一些活页夹使附加的绑定属性支持特定于中间件的功能。

可以通过Spring Boot支持的任何机制向Spring Cloud Stream应用程序提供配置选项。这包括应用程序参数,环境变量以及YAML或.properties文件。

31.1绑定服务Properties

这些属性通过org.springframework.cloud.stream.config.BindingServiceProperties公开

spring.cloud.stream.instanceCount

应用程序已部署实例的数量。必须在生产者端进行分区设置。使用RabbitMQ时必须在用户端设置,如果使用autoRebalanceEnabled=false,则必须在Kafka时设置。

默认值:1

spring.cloud.stream.instanceIndex
应用程序的实例索引:从0instanceCount - 1的数字。用于通过RabbitMQ和Kafka(如果是autoRebalanceEnabled=false)进行分区。在Cloud Foundry中自动设置以匹配应用程序的实例索引。
spring.cloud.stream.dynamic目的地

可以动态绑定的目的地列表(例如,在动态路由方案中)。如果设置,则只能绑定列出的目的地。

默认值:空(将任何目的地绑定)。

spring.cloud.stream.defaultBinder

如果配置了多个联编程序,则使用的默认联编程序。请参见Classpath上的多个Binders

默认值:空。

spring.cloud.stream.overrideCloudConnectors

仅当cloud配置文件处于活动状态并且应用程序提供了Spring Cloud Connectors时,此属性才适用。如果属性为false(默认值),则绑定器检测到合适的绑定服务(例如,RabbitMQ绑定器在Cloud Foundry中绑定的RabbitMQ服务)并将其用于创建连接(通常通过Spring Cloud Connectors)。设置为true时,此属性指示绑定程序完全忽略绑定的服务,并依赖Spring Boot属性(例如,依赖于环境中为RabbitMQ绑定程序提供的spring.rabbitmq.*属性) 。连接到多个系统时,此属性的典型用法是嵌套在自定义环境

默认值:false

spring.cloud.stream.bindingRetryInterval

例如,活页夹不支持后期绑定和代理(例如Apache Kafka)关闭时,重试绑定创建之间的间隔(以秒为单位)。将该值设置为零可将此类情况视为致命情况,从而阻止应用程序启动。

默认值:30

31.2绑定Properties

绑定属性是使用spring.cloud.stream.bindings.<channelName>.<property>=<value>格式提供的。<channelName>代表正在配置的通道的名称(例如,对于Source,为output)。

为避免重复,Spring Cloud Stream支持所有通道的设置值,格式为spring.cloud.stream.default.<property>=<value>

在避免重复使用扩展绑定属性时,应使用此格式-spring.cloud.stream.<binder-type>.default.<producer|consumer>.<property>=<value>

在下面的内容中,我们指出了省略了spring.cloud.stream.bindings.<channelName>.前缀的位置,仅着眼于属性名称,但要了解前缀是在运行时包含的。

31.2.1通用绑定Properties

这些属性通过org.springframework.cloud.stream.config.BindingProperties公开

以下绑定属性可用于输入和输出绑定,并且必须以spring.cloud.stream.bindings.<channelName>.为前缀(例如,spring.cloud.stream.bindings.input.destination=ticktock)。

可以使用前缀spring.cloud.stream.default设置默认值(例如“ spring.cloud.stream.default.contentType = application / json”)。

目的地
绑定的中间件上的通道的目标目的地(例如,RabbitMQ交换或Kafka主题)。如果将通道绑定为使用者,则可以将其绑定到多个目标,并且目标名称可以指定为逗号分隔的String值。如果未设置,则使用通道名称。此属性的默认值不能被覆盖。

渠道的消费群体。仅适用于入站绑定。请参阅消费者组

默认值:null(指示匿名使用者)。

内容类型

频道的内容类型。请参见第32章,内容类型协商

默认值:application/json

黏合剂

此绑定使用的粘合剂。有关详细信息,请参见第30.4节“类路径上的多个Binders ”)

默认值:null(如果存在默认活页夹,则使用默认活页夹)。

31.2.2消费者Properties

这些属性通过org.springframework.cloud.stream.binder.ConsumerProperties公开

以下绑定属性仅可用于输入绑定,并且必须以spring.cloud.stream.bindings.<channelName>.consumer.为前缀(例如,spring.cloud.stream.bindings.input.consumer.concurrency=3)。

可以使用spring.cloud.stream.default.consumer前缀(例如,spring.cloud.stream.default.consumer.headerMode=none)设置默认值。

并发

入站使用者的并发。

默认值:1

分区的

消费者是否从分区生产者那里接收数据。

默认值:false

headerMode

设置为none时,禁用输入的标头解析。仅对本身不支持消息头并且需要消息头嵌入的消息中间件有效。当不支持本机头时,使用来自非Spring Cloud Stream应用程序的数据时,此选项很有用。设置为headers时,它使用中间件的本机头机制。设置为embeddedHeaders时,它将标头嵌入到消息有效负载中。

默认值:取决于活页夹的实现。

maxAttempts

如果处理失败,则尝试处理消息的次数(包括第一次)。设置为1以禁用重试。

默认值:3

backOffInitialInterval

重试时的退避初始间隔。

默认值:1000

backOffMaxInterval

最大退避间隔。

默认值:10000

backOffMultiplier

退避乘数。

默认值:2.0

defaultRetryable

retryableExceptions中未列出的由侦听器引发的异常是否可以重试。

默认值:true

instanceIndex

设置为大于零的值时,它允许自定义此使用者的实例索引(如果与spring.cloud.stream.instanceIndex不同)。设置为负值时,默认为spring.cloud.stream.instanceIndex有关更多信息请参见第34.2节“实例索引和实例计数”

默认值:-1

instanceCount

设置为大于零的值时,它允许自定义此使用者的实例计数(如果与spring.cloud.stream.instanceCount不同)。设置为负值时,默认为spring.cloud.stream.instanceCount有关更多信息请参见第34.2节“实例索引和实例计数”

默认值:-1

retryableExceptions

键中Throwable类名称的映射,值中布尔值的映射。指定将要重试的那些异常(和子类)。另请参见defaultRetriable示例:spring.cloud.stream.bindings.input.consumer.retryable-exceptions.java.lang.IllegalStateException=false

默认值:空。

useNativeDecoding

设置为true时,入站消息将直接由客户端库反序列化,该库必须进行相应配置(例如,设置适当的Kafka生产者值反序列化器)。使用此配置时,入站消息解组不是基于绑定的contentType使用本机解码时,生产者负责使用适当的编码器(例如,Kafka生产者值序列化程序)对出站消息进行序列化。同样,当使用本机编码和解码时,headerMode=embeddedHeaders属性将被忽略,并且标头不会嵌入消息中。请参见生产者属性useNativeEncoding

默认值:false

31.2.3生产者Properties

这些属性通过org.springframework.cloud.stream.binder.ProducerProperties公开

以下绑定属性仅可用于输出绑定,并且必须以spring.cloud.stream.bindings.<channelName>.producer.为前缀(例如,spring.cloud.stream.bindings.input.producer.partitionKeyExpression=payload.id)。

可以使用前缀spring.cloud.stream.default.producer(例如,spring.cloud.stream.default.producer.partitionKeyExpression=payload.id)设置默认值。

partitionKeyExpression

一个SpEL表达式,用于确定如何对出站数据进行分区。如果已设置或设置了partitionKeyExtractorClass,则会对该通道上的出站数据进行分区。partitionCount必须设置为大于1的值才能生效。partitionKeyExtractorClass互斥。请参见第28.6节“分区支持”

默认值:null。

partitionKeyExtractorClass

PartitionKeyExtractorStrategy实现。如果已设置,或者已设置partitionKeyExpression,则会对该通道上的出站数据进行分区。partitionCount必须设置为大于1的值才能生效。partitionKeyExpression互斥。请参见第28.6节“分区支持”

默认值:null

partitionSelectorClass

PartitionSelectorStrategy实现。partitionSelectorExpression互斥。如果两者均未设置,则将该分区选择为hashCode(key) % partitionCount,其中key通过partitionKeyExpressionpartitionKeyExtractorClass计算。

默认值:null

partitionSelectorExpression

用于自定义分区选择的SpEL表达式。partitionSelectorClass互斥。如果两者均未设置,则将分区选择为hashCode(key) % partitionCount,其中key通过partitionKeyExpressionpartitionKeyExtractorClass计算。

默认值:null

partitionCount

数据的目标分区数(如果启用了分区)。如果生产者已分区,则必须将其设置为大于1的值。在Kafka上,它被解释为提示。取其较大者,并使用目标主题的分区数。

默认值:1

requiredGroups
生产者必须确保将消息传递到的组的逗号分隔列表,即使它们是在创建消息之后开始的(例如,通过在RabbitMQ中预先创建持久队列)。
headerMode

设置为none时,它将禁用在输出中嵌入标头。它仅对本身不支持消息头并且需要消息头嵌入的消息中间件有效。当不支持本机头时,为非Spring Cloud Stream应用程序生成数据时,此选项很有用。设置为headers时,它使用中间件的本机头机制。设置为embeddedHeaders时,它将标头嵌入到消息有效负载中。

默认值:取决于活页夹的实现。

useNativeEncoding

设置为true时,出站消息将直接由客户端库进行序列化,该库必须进行相应配置(例如,设置适当的Kafka生产者值序列化程序)。使用此配置时,出站消息编组不是基于绑定的contentType使用本机编码时,使用方负责使用适当的解码器(例如,Kafka使用方值反序列化器)对入站消息进行反序列化。此外,当使用本机编码和解码时,headerMode=embeddedHeaders属性将被忽略,并且标头不会嵌入消息中。请参阅消费者属性useNativeDecoding

默认值:false

errorChannelEnabled

设置为true时,如果活页夹支持异步发送结果,则发送失败将发送到目标的错误通道。参见??? 以获取更多信息。

默认值:false

31.3使用动态绑定的目的地

除了使用@EnableBinding定义的通道外,Spring Cloud Stream还允许应用程序将消息发送到动态绑定的目的地。例如,当需要在运行时确定目标目的地时,这很有用。应用程序可以通过使用@EnableBinding注释自动注册的BinderAwareChannelResolver bean来实现。

“ spring.cloud.stream.dynamicDestinations”属性可用于将动态目标名称限制为已知集合(白名单)。如果未设置此属性,则可以动态绑定任何目标。

BinderAwareChannelResolver可以直接使用,如以下使用路径变量来确定目标通道的REST控制器示例所示:

@EnableBinding
@Controller
public class SourceWithDynamicDestination {

    @Autowired
    private BinderAwareChannelResolver resolver;

    @RequestMapping(path = "/{target}", method = POST, consumes = "*/*")
    @ResponseStatus(HttpStatus.ACCEPTED)
    public void handleRequest(@RequestBody String body, @PathVariable("target") target,
           @RequestHeader(HttpHeaders.CONTENT_TYPE) Object contentType) {
        sendMessage(body, target, contentType);
    }

    private void sendMessage(String body, String target, Object contentType) {
        resolver.resolveDestination(target).send(MessageBuilder.createMessage(body,
                new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType))));
    }
}

现在考虑当我们在默认端口(8080)上启动应用程序并使用CURL发出以下请求时会发生什么:

curl -H "Content-Type: application/json" -X POST -d "customer-1" http://localhost:8080/customers

curl -H "Content-Type: application/json" -X POST -d "order-1" http://localhost:8080/orders

在经纪人中创建目的地“客户”和“订单”(交换为Rabbit或在主题为“ Kafka”中),名称为“客户”和“订单”,数据为发布到适当的目的地。

BinderAwareChannelResolver是通用的Spring Integration DestinationResolver,并且可以注入到其他组件中,例如,在路由器中使用基于传入的target字段的SpEL表达式的路由器JSON消息。以下示例包含一个读取SpEL表达式的路由器:

@EnableBinding
@Controller
public class SourceWithDynamicDestination {

    @Autowired
    private BinderAwareChannelResolver resolver;


    @RequestMapping(path = "/", method = POST, consumes = "application/json")
    @ResponseStatus(HttpStatus.ACCEPTED)
    public void handleRequest(@RequestBody String body, @RequestHeader(HttpHeaders.CONTENT_TYPE) Object contentType) {
        sendMessage(body, contentType);
    }

    private void sendMessage(Object body, Object contentType) {
        routerChannel().send(MessageBuilder.createMessage(body,
                new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType))));
    }

    @Bean(name = "routerChannel")
    public MessageChannel routerChannel() {
        return new DirectChannel();
    }

    @Bean
    @ServiceActivator(inputChannel = "routerChannel")
    public ExpressionEvaluatingRouter router() {
        ExpressionEvaluatingRouter router =
            new ExpressionEvaluatingRouter(new SpelExpressionParser().parseExpression("payload.target"));
        router.setDefaultOutputChannelName("default-output");
        router.setChannelResolver(resolver);
        return router;
    }
}

路由器接收器应用程序使用此技术的按需创建的目的地。

如果预先知道通道名称,则可以像其他任何目的地一样配置生产者属性。或者,如果您注册NewBindingCallback<> bean,则会在创建绑定之前调用它。回调采用绑定程序使用的扩展生产者属性的通用类型。它有一种方法:

void configure(String channelName, MessageChannel channel, ProducerProperties producerProperties,
        T extendedProducerProperties);

下面的示例显示如何使用RabbitMQ活页夹:

@Bean
public NewBindingCallback<RabbitProducerProperties> dynamicConfigurer() {
    return (name, channel, props, extended) -> {
        props.setRequiredGroups("bindThisQueue");
        extended.setQueueNameGroupOnly(true);
        extended.setAutoBindDlq(true);
        extended.setDeadLetterQueueName("myDLQ");
    };
}
[注意]注意

如果需要支持具有多个活页夹类型的动态目标,请对通用类型使用Object,并根据需要强制转换extended参数。

32.内容类型协商

数据转换是任何消息驱动的微服务体系结构的核心功能之一。假设在Spring Cloud Stream中,此类数据表示为Spring Message,则在到达消息之前,可能必须将消息转换为所需的形状或大小。这是必需的,原因有两个:

  1. 转换传入消息的内容以匹配应用程序提供的处理程序的签名。
  2. 将外发邮件的内容转换为有线格式。

有线格式通常为byte[](对于Kafka和Rabbit活页夹而言是正确的),但是它由活页夹实现方式控制。

在Spring Cloud Stream中,消息转换是通过org.springframework.messaging.converter.MessageConverter完成的。

[注意]注意

作为后续细节的补充,您可能还需要阅读以下博客文章

32.1力学

为了更好地理解内容类型协商的机制和必要性,我们以下面的消息处理程序为例,看一个非常简单的用例:

@StreamListener(Processor.INPUT)
@SendTo(Processor.OUTPUT)
public String handle(Person person) {..}
[注意]注意

为简单起见,我们假设这是应用程序中唯一的处理程序(我们假设没有内部管道)。

前面示例中所示的处理程序期望将Person对象作为参数,并产生String类型作为输出。为了使框架成功将传入的Message作为参数传递给此处理程序,它必须以某种方式将Message类型的有效负载从有线格式转换为Person类型。换句话说,框架必须找到并应用适当的MessageConverter为此,该框架需要用户提供一些指导。处理程序方法本身的签名(Person类型)已经提供了这些指令之一。因此,从理论上讲,这应该是(并且在某些情况下是足够的)。但是,对于大多数用例而言,为了选择适当的MessageConverter,框架需要额外的信息。缺少的部分是contentType

Spring Cloud Stream提供了三种机制来定义contentType(按优先顺序):

  1. 标题contentType可以通过消息本身进行通信。通过提供contentType标头,您可以声明用于查找和应用适当的MessageConverter的内容类型。
  2. 绑定:可以通过设置spring.cloud.stream.bindings.input.content-type属性为每个目标绑定设置contentType

    [注意]注意

    属性名称中的input段与目的地的实际名称相对应(在本例中为“输入”)。通过这种方法,您可以在每个绑定的基础上声明用于查找和应用适当的MessageConverter的内容类型。

  3. 默认值:如果Message标头或绑定中不存在contentType,则使用默认的application/json内容类型来查找和应用适当的MessageConverter

如前所述,前面的列表还演示了平局时的优先顺序。例如,标头提供的内容类型优先于任何其他内容类型。对于按绑定设置的内容类型也是如此,这实际上使您可以覆盖默认内容类型。但是,它也提供了明智的默认值(由社区反馈确定)。

application/json设置为默认值的另一个原因是由分布式微服务体系结构驱动的互操作性要求,在该体系结构中,生产者和使用者不仅可以在不同的JVM中运行,而且还可以在不同的非JVM平台上运行。

当非无效处理程序方法返回时,如果返回值已经是Message,则该Message成为有效负载。但是,当返回值不是Message时,将使用返回值作为有效负载构造新的Message,同时从输入Message继承标头减去由SpringIntegrationProperties.messageHandlerNotPropagatedHeaders定义或过滤的标头。默认情况下,仅设置一个标头:contentType这意味着新的Message没有设置contentType头,从而确保contentType可以演进。您始终可以选择不从处理程序方法返回Message的位置,在该方法中可以注入所需的任何标头。

如果存在内部管道,则通过相同的转换过程将Message发送到下一个处理程序。但是,如果没有内部管道或您已经到达内部管道的末尾,则Message将发送回输出目的地。

32.1.1内容类型与参数类型

如前所述,为了使框架选择适当的MessageConverter,它需要参数类型以及(可选)内容类型信息。选择适当的MessageConverter的逻辑驻留在参数解析器(HandlerMethodArgumentResolvers)中,该解析器在调用用户定义的处理程序方法之前(即当框架知道实际的参数类型时)触发。如果参数类型与当前有效负载的类型不匹配,则框架将委派给预先配置的MessageConverters的堆栈,以查看其中是否有一个可以转换有效负载。如您所见,MessageConverter的Object fromMessage(Message<?> message, Class<?> targetClass);操作将targetClass作为其参数之一。该框架还确保提供的Message始终包含一个contentType头。当没有contentType标头时,它会插入按绑定的contentType标头或默认的contentType标头。contentType参数类型的组合是框架确定消息是否可以转换为目标类型的机制。如果找不到合适的MessageConverter,则会引发异常,您可以通过添加自定义MessageConverter来处理该异常(请参见第32.3节,“用户定义的消息转换器”)。

但是,如果有效载荷类型与处理程序方法声明的目标类型匹配,该怎么办?在这种情况下,没有任何要转换的内容,并且有效载荷未经修改地传递。尽管这听起来很简单且合乎逻辑,但请记住以Message<?>Object作为参数的处理程序方法。通过将目标类型声明为Object(在Java中为instanceof,是所有内容),实际上就放弃了转换过程。

[注意]注意

不要期望仅根据contentTypeMessage转换为其他类型。请记住,contentType是目标类型的补充。如果需要,您可以提供一个提示,MessageConverter可能会也可能不会考虑。

32.1.2消息转换器

MessageConverters定义两种方法:

Object fromMessage(Message<?> message, Class<?> targetClass);

Message<?> toMessage(Object payload, @Nullable MessageHeaders headers);

重要的是要了解这些方法的约定及其用法,尤其是在Spring Cloud Stream的上下文中。

fromMessage方法将传入的Message转换为参数类型。Message的有效载荷可以是任何类型,而MessageConverter的实际实现要支持多种类型。例如,某些JSON转换器可能支持有效负载类型为byte[]String等。当应用程序包含内部管道(即输入→handler1→handler2→....→输出)并且上游处理程序的输出结果为Message时,这可能不是初始连接格式,这一点很重要。

但是,toMessage方法的合同更为严格,必须始终将Message转换为有线格式:byte[]

因此,出于所有意图和目的(尤其是在实现自己的转换器时),您将这两种方法视为具有以下签名:

Object fromMessage(Message<?> message, Class<?> targetClass);

Message<byte[]> toMessage(Object payload, @Nullable MessageHeaders headers);

32.2提供的MessageConverters

如前所述,该框架已经提供了MessageConverters堆栈来处理最常见的用例。以下列表按优先级描述了提供的MessageConverters(使用了第一个有效的MessageConverter):

  1. ApplicationJsonMessageMarshallingConverterorg.springframework.messaging.converter.MappingJackson2MessageConverter的变体。对于contentTypeapplication/json(默认)的情况,支持将Message的有效负载转换为POJO或从POJO转换为PO195。
  2. TupleJsonMessageConverter:已弃用支持将Message的有效负载转换为org.springframework.tuple.Tuple或从org.springframework.tuple.Tuple转换。
  3. ByteArrayMessageConverter:在contentTypeapplication/octet-stream的情况下,支持将Message的有效载荷从byte[]转换为byte[]它本质上是一个传递,主要是为了向后兼容而存在。
  4. ObjectStringMessageConverter:当contentTypetext/plain时,支持将任何类型转换为String它调用Object的toString()方法,或者,如果有效载荷为byte[],则调用新的String(byte[])
  5. JavaSerializationMessageConverter:已弃用contentTypeapplication/x-java-serialized-object时,支持基于Java序列化的转换。
  6. KryoMessageConverter:已弃用contentTypeapplication/x-java-object时,支持基于Kryo序列化的转换。
  7. JsonUnmarshallingConverter:类似于ApplicationJsonMessageMarshallingConvertercontentTypeapplication/x-java-object时,它支持任何类型的转换。它期望将实际类型信息作为属性嵌入在contentType中(例如,application/x-java-object;type=foo.bar.Cat)。

当找不到合适的转换器时,框架将引发异常。发生这种情况时,应检查代码和配置,并确保您没有错过任何内容(即,确保使用绑定或标头提供了contentType)。但是,很可能您发现了一些不常见的情况(例如自定义contentType),并且提供的MessageConverters的当前堆栈不知道如何进行转换。在这种情况下,您可以添加自定义MessageConverter请参见第32.3节“用户定义的消息转换器”

32.3用户定义的消息转换器

Spring Cloud Stream公开了定义和注册其他MessageConverters的机制。要使用它,请实现org.springframework.messaging.converter.MessageConverter,将其配置为@Bean,并用@StreamMessageConverter进行注释。然后将其附加到MessageConverter的现有堆栈中。

[注意]注意

了解自定义MessageConverter实现已添加到现有堆栈的开头很重要。因此,自定义MessageConverter实现优先于现有实现,您可以覆盖它们并添加到现有转换器中。

下面的示例说明如何创建消息转换器bean以支持称为application/bar的新内容类型:

@EnableBinding(Sink.class)
@SpringBootApplication
public static class SinkApplication {

    ...

    @Bean
    @StreamMessageConverter
    public MessageConverter customMessageConverter() {
        return new MyCustomMessageConverter();
    }
}

public class MyCustomMessageConverter extends AbstractMessageConverter {

    public MyCustomMessageConverter() {
        super(new MimeType("application", "bar"));
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return (Bar.class.equals(clazz));
    }

    @Override
    protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
        Object payload = message.getPayload();
        return (payload instanceof Bar ? payload : new Bar((byte[]) payload));
    }
}

Spring Cloud Stream还为基于Avro的转换器和模式演变提供支持。有关详细信息请参见第33章,Schema Evolution支持

33. Schema进化支持

Spring Cloud Stream为模式演化提供了支持,因此数据可以随着时间的推移而演化,并且仍然可以与较新的生产者和消费者以及反之亦然。大多数序列化模型,尤其是旨在跨不同平台和语言进行移植的模型,都依赖于一种描述如何在二进制有效负载中序列化数据的模式。为了序列化数据然后解释它,发送方和接收方都必须有权访问描述二进制格式的模式。在某些情况下,可以从序列化时的有效负载类型或反序列化时的目标类型推断模式。但是,许多应用程序可以从访问描述二进制数据格式的显式架构中受益。通过模式注册表,您可以以文本格式(通常为JSON)存储模式信息,并使该信息可用于需要它以二进制格式接收和发送数据的各种应用程序。模式可引用为一个元组,该元组包括:

  • 主题,是架构的逻辑名称
  • 模式版本
  • 模式格式,描述数据的二进制格式

以下各节详细介绍了架构演变过程中涉及的各种组件。

33.1 Schema注册客户端

与模式注册表服务器进行交互的客户端抽象是SchemaRegistryClient接口,该接口具有以下结构:

public interface SchemaRegistryClient {

    SchemaRegistrationResponse register(String subject, String format, String schema);

    String fetch(SchemaReference schemaReference);

    String fetch(Integer id);

}

Spring Cloud Stream提供了开箱即用的实现,可以与其自己的模式服务器进行交互,也可以与Confluent Schema注册中心进行交互。

可以使用@EnableSchemaRegistryClient来配置Spring Cloud Stream模式注册表的客户端,如下所示:

  @EnableBinding(Sink.class)
  @SpringBootApplication
  @EnableSchemaRegistryClient
  public static class AvroSinkApplication {
    ...
  }
[注意]注意

默认转换器经过优化,不仅可以缓存来自远程服务器的模式,还可以缓存parse()toString()方法,这是非常昂贵的。因此,它使用了不缓存响应的DefaultSchemaRegistryClient如果要更改默认行为,则可以直接在代码上使用客户端,并将其覆盖为所需的结果。为此,您必须将属性spring.cloud.stream.schemaRegistryClient.cached=true添加到应用程序属性中。

33.1.1 Schema注册客户端Properties

Schema Registry Client支持以下属性:

spring.cloud.stream.schemaRegistryClient.endpoint
模式服务器的位置。进行设置时,请使用完整的URL,包括协议(httphttps),端口和上下文路径。
默认
http://localhost:8990/
spring.cloud.stream.schemaRegistryClient.cached
客户端是否应缓存架构服务器响应。由于缓存发生在消息转换器中,因此通常设置为false使用架构注册表客户端的客户端应将此设置为true
默认
true

33.2 Avro Schema注册客户端消息转换器

对于已在应用程序上下文中注册了SchemaRegistryClient bean的应用程序,Spring Cloud Stream自动为架构管理配置Apache Avro消息转换器。由于接收消息的应用程序可以轻松访问可以与自己的读取器模式进行协调的写入器模式,因此这简化了模式的演变。

对于出站消息,如果通道的内容类型设置为application/*+avro,则激活MessageConverter,如下例所示:

spring.cloud.stream.bindings.output.contentType=application/*+avro

在出站转换期间,消息转换器尝试使用SchemaRegistryClient推断每个出站消息的模式(基于其类型)并将其注册到主题(基于有效负载类型)。如果已经找到相同的模式,则将检索对其的引用。如果不是,则注册架构,并提供新的版本号。通过使用以下方案,将消息与contentType头一起发送:application/[prefix].[subject].v[version]+avro,其中prefix是可配置的,而subject是从有效负载类型推导出来的。

例如,类型为User的消息可能作为二进制有效载荷发送,其内容类型为application/vnd.user.v2+avro,其中user是主题,2是版本号。

接收消息时,转换器从传入消息的标头中推断模式引用,并尝试检索它。该模式在反序列化过程中用作编写器模式。

33.2.1 Avro Schema注册表消息转换器Properties

如果通过设置spring.cloud.stream.bindings.output.contentType=application/*+avro启用了基于Avro的架构注册表客户端,则可以通过设置以下属性来自定义注册行为。

spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled

如果您希望转换器使用反射来从POJO推断Schema,请启用。

默认值:false

spring.cloud.stream.schema.avro.readerSchema
Avro通过查看写入器模式(原始有效负载)和读取器模式(您的应用程序有效负载)来比较模式版本。有关更多信息,请参见Avro文档如果设置,它将覆盖在模式服务器上的所有查找,并将本地模式用作读取器模式。默认值:null
spring.cloud.stream.schema.avro.schema位置

在Schema服务器中注册此属性中列出的所有.avsc文件。

默认值:empty

spring.cloud.stream.schema.avro.prefix

Content-Type标头上要使用的前缀。

默认值:vnd

33.3 Apache Avro消息转换器

Spring Cloud Stream通过其spring-cloud-stream-schema模块为基于模式的消息转换器提供支持。当前,基于模式的消息转换器开箱即用的唯一序列化格式是Apache Avro,将来的版本中将添加更多格式。

spring-cloud-stream-schema模块包含可用于Apache Avro序列化的两种消息转换器:

  • 转换器使用序列化或反序列化对象的类信息或启动时具有已知位置的模式。
  • 使用架构注册表的转换器。它们在运行时定位架构,并随着域对象的发展动态注册新架构。

33.4支持Schema的转换器

AvroSchemaMessageConverter支持通过使用预定义的架构或通过使用类中可用的架构信息(反射地或包含在SpecificRecord中)来对消息进行序列化和反序列化。如果提供自定义转换器,则不会创建默认的AvroSchemaMessageConverter bean。以下示例显示了一个自定义转换器:

要使用自定义转换器,您只需将其添加到应用程序上下文中,就可以选择指定一个或多个与其关联的MimeTypes默认MimeTypeapplication/avro

如果转换的目标类型是GenericRecord,则必须设置架构。

以下示例显示如何通过在没有预定义架构的情况下注册Apache Avro MessageConverter在接收器应用程序中配置转换器。在此示例中,请注意,哑剧类型值为avro/bytes,而不是默认的application/avro

@EnableBinding(Sink.class)
@SpringBootApplication
public static class SinkApplication {

  ...

  @Bean
  public MessageConverter userMessageConverter() {
      return new AvroSchemaMessageConverter(MimeType.valueOf("avro/bytes"));
  }
}

相反,以下应用程序使用预定义的架构(在类路径上找到)注册一个转换器:

@EnableBinding(Sink.class)
@SpringBootApplication
public static class SinkApplication {

  ...

  @Bean
  public MessageConverter userMessageConverter() {
      AvroSchemaMessageConverter converter = new AvroSchemaMessageConverter(MimeType.valueOf("avro/bytes"));
      converter.setSchemaLocation(new ClassPathResource("schemas/User.avro"));
      return converter;
  }
}

33.5 Schema注册表服务器

Spring Cloud Stream提供了模式注册表服务器实现。要使用它,可以将spring-cloud-stream-schema-server工件添加到项目中,并使用@EnableSchemaRegistryServer批注,该批注将架构注册表服务器REST控制器添加到您的应用程序。该注释旨在与Spring Boot web应用程序一起使用,并且服务器的侦听端口由server.port属性控制。spring.cloud.stream.schema.server.path属性可用于控制模式服务器的根路径(尤其是当它嵌入在其他应用程序中时)。spring.cloud.stream.schema.server.allowSchemaDeletion布尔属性可以删除模式。默认情况下,这是禁用的。

架构注册表服务器使用关系数据库来存储架构。默认情况下,它使用嵌入式数据库。您可以使用Spring Boot SQL数据库和JDBC配置选项来自定义模式存储

以下示例显示了一个启用架构注册表的Spring Boot应用程序:

@SpringBootApplication
@EnableSchemaRegistryServer
public class SchemaRegistryServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SchemaRegistryServerApplication.class, args);
    }
}

33.5.1 Schema Registry Server API

Schema Registry Server API包含以下操作:

注册新的Schema

要注册新模式,请向/端点发送一个POST请求。

/接受具有以下字段的JSON有效负载:

  • subject:架构主题
  • format:架构格式
  • definition:架构定义

它的响应是JSON中的架构对象,具有以下字段:

  • id:架构ID
  • subject:架构主题
  • format:架构格式
  • version:架构版本
  • definition:模式定义

通过主题,格式和版本检索现有的Schema

要按主题,格式和版本检索现有架构,请向/{subject}/{format}/{version}端点发送GET请求。

它的响应是JSON中的架构对象,具有以下字段:

  • id:架构ID
  • subject:架构主题
  • format:架构格式
  • version:架构版本
  • definition:模式定义

通过主题和格式检索现有的Schema

要按主题和格式检索现有架构,请向/subject/format端点发送一个GET请求。

它的响应是JSON中每个模式对象的模式列表,其中包含以下字段:

  • id:模式ID
  • subject:架构主题
  • format:架构格式
  • version:架构版本
  • definition:架构定义

通过ID检索现有的Schema

要通过其ID检索架构,请向/schemas/{id}端点发送一个GET请求。

它的响应是JSON中的架构对象,具有以下字段:

  • id:架构ID
  • subject:架构主题
  • format:架构格式
  • version:架构版本
  • definition:模式定义

按主题,格式和版本删除Schema

要删除由其主题,格式和版本标识的模式,请向/{subject}/{format}/{version}端点发送一个DELETE请求。

按ID删除Schema

要通过其ID删除模式,请向/schemas/{id}端点发送一个DELETE请求。

按主题删除Schema

DELETE /{subject}

按主题删除现有架构。

[注意]注意

本说明仅适用于Spring Cloud Stream 1.1.0.RELEASE的用户。Spring Cloud Stream 1.1.0.RELEASE使用表名schema来存储Schema对象。Schema是许多数据库实现中的关键字。为了避免将来发生任何冲突,从1.1.1.RELEASE开始,我们为存储表选择了名称SCHEMA_REPOSITORY任何升级的Spring Cloud Stream 1.1.0.RELEASE用户都应在升级之前将其现有模式迁移到新表中。

33.5.2使用Confluent的Schema注册表

默认配置将创建DefaultSchemaRegistryClient bean。如果要使用Confluent模式注册表,则需要创建类型为ConfluentSchemaRegistryClient的bean,该类型将替代框架默认配置的类型。下面的示例说明如何创建这样的bean:

@Bean
public SchemaRegistryClient schemaRegistryClient(@Value("${spring.cloud.stream.schemaRegistryClient.endpoint}") String endpoint){
  ConfluentSchemaRegistryClient client = new ConfluentSchemaRegistryClient();
  client.setEndpoint(endpoint);
  return client;
}
[注意]注意

ConfluentSchemaRegistryClient已针对Confluent平台4.0.0版进行了测试。

33.6 Schema注册和解决

为了更好地了解Spring Cloud Stream如何注册和解析新模式及其对Avro模式比较功能的使用,我们提供了两个单独的小节:

33.6.1 Schema注册流程(序列化)

注册过程的第一部分是从通过通道发送的有效负载中提取模式。诸如SpecificRecordGenericRecord之类的Avro类型已经包含一个架构,可以从实例中立即检索该架构。对于POJO,如果将spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled属性设置为true(默认值),则将推断模式。

图33.1 Schema作家解析过程

模式解析

获得一个模式,转换器从远程服务器加载其元数据(版本)。首先,它查询本地缓存。如果未找到结果,它将把数据提交给服务器,服务器将提供版本信息。转换器始终缓存结果,以避免为每个需要序列化的新消息查询Schema服务器的开销。

图33.2 Schema注册流程

注册

使用架构版本信息,转换器将消息的contentType标头设置为携带版本信息,例如:application/vnd.user.v1+avro

33.6.2 Schema解析过程(反序列化)

当读取包含版本信息的消息(即contentType标头,其格式类似于第33.6.1节“ Schema注册过程(序列化)”)所述)时,转换器将查询Schema服务器以获取消息的编写器架构。一旦找到了传入消息的正确架构,它将检索阅读器架构,并使用Avro的架构解析支持将其读入阅读器定义(设置默认值和所有缺少的属性)。

图33.3 Schema阅读解决流程

模式阅读

[注意]注意

您应该了解编写者架构(编写消息的应用程序)和阅读者架构(接收应用程序)之间的区别。我们建议花点时间阅读Avro术语并了解其过程。Spring Cloud Stream始终获取编写程序架构以确定如何读取消息。如果要使Avro的模式演变支持正常工作,则需要确保为应用程序正确设置了readerSchema

34.应用程序间通信

Spring Cloud Stream启用应用程序之间的通信。应用程序间通信是一个涉及多个问题的复杂问题,如以下主题所述:

34.1连接多个应用程序实例

尽管Spring Cloud Stream使单个Spring Boot应用程序易于连接到消息传递系统,但Spring Cloud Stream的典型方案是创建多应用程序管道,微服务应用程序在该管道中相互发送数据。您可以通过关联相邻应用程序的输入和输出目标来实现此方案

假设设计要求Time Source应用程序将数据发送到Log Sink应用程序。您可以将名为ticktock的公共目标用于两个应用程序中的绑定。

时间源(通道名称为output)将设置以下属性:

spring.cloud.stream.bindings.output.destination=ticktock

日志接收器(通道名称为input)将设置以下属性:

spring.cloud.stream.bindings.input.destination=ticktock

34.2实例索引和实例计数

在扩展Spring Cloud Stream应用程序时,每个实例可以接收有关同一应用程序还存在多少其他实例以及它自己的实例索引是什么的信息。Spring Cloud Stream通过spring.cloud.stream.instanceCountspring.cloud.stream.instanceIndex属性进行此操作。例如,如果存在HDFS接收器应用程序的三个实例,则所有三个实例的spring.cloud.stream.instanceCount都设置为3,并且各个应用程序的spring.cloud.stream.instanceIndex都设置为01,和2

通过Spring Cloud Data Flow部署Spring Cloud Stream应用程序时,将自动配置这些属性;否则,将自动配置这些属性。分别启动Spring Cloud Stream应用程序时,必须正确设置这些属性。默认情况下,spring.cloud.stream.instanceCount1,而spring.cloud.stream.instanceIndex0

在按比例放大的方案中,这两个属性的正确配置通常对于解决分区行为很重要(请参见下文),并且某些绑定程序(例如,Kafka绑定程序)始终需要这两个属性,以便确保在多个使用者实例之间正确分割数据。

34.3分区

Spring Cloud Stream中的分区包括两个任务:

34.3.1配置用于分区的输出绑定

您可以通过设置其partitionKeyExpressionpartitionKeyExtractorName属性及其partitionCount属性中的一个或仅一个,来配置输出绑定以发送分区数据。

例如,以下是有效的典型配置:

spring.cloud.stream.bindings.output.producer.partitionKeyExpression=payload.id
spring.cloud.stream.bindings.output.producer.partitionCount=5

基于该示例配置,通过使用以下逻辑将数据发送到目标分区。

根据partitionKeyExpression为发送到分区输出通道的每条消息计算分区键的值。partitionKeyExpression是一个SpEL表达式,该表达式根据出站消息进行评估以提取分区键。

如果SpEL表达式不足以满足您的需要,则可以通过提供org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy的实现并将其配置为bean(通过使用@Bean注释)来计算分区键值。 。如果在应用程序上下文中有多个org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy类型的bean,则可以通过使用partitionKeyExtractorName属性指定其名称来进一步过滤它,如以下示例所示:

--spring.cloud.stream.bindings.output.producer.partitionKeyExtractorName=customPartitionKeyExtractor
--spring.cloud.stream.bindings.output.producer.partitionCount=5
. . .
@Bean
public CustomPartitionKeyExtractorClass customPartitionKeyExtractor() {
    return new CustomPartitionKeyExtractorClass();
}
[注意]注意

在Spring Cloud Stream的早期版本中,您可以通过设置spring.cloud.stream.bindings.output.producer.partitionKeyExtractorClass属性来指定org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy的实现。从2.0版开始,不推荐使用此属性,并且在将来的版本中将不再支持该属性。

一旦计算出消息密钥,分区选择过程就会将目标分区确定为0partitionCount - 1之间的值。适用于大多数情况的默认计算基于以下公式:key.hashCode() % partitionCount这可以在绑定上进行自定义,方法是将SpEL表达式设置为针对'key'进行评估(通过partitionSelectorExpression属性),也可以将org.springframework.cloud.stream.binder.PartitionSelectorStrategy的实现配置为bean (通过使用@ Bean批注)。PartitionKeyExtractorStrategy类似,当应用程序上下文中有多个这种类型的bean可用时,您可以使用spring.cloud.stream.bindings.output.producer.partitionSelectorName属性进一步过滤它,如以下示例所示:

--spring.cloud.stream.bindings.output.producer.partitionSelectorName=customPartitionSelector
. . .
@Bean
public CustomPartitionSelectorClass customPartitionSelector() {
    return new CustomPartitionSelectorClass();
}
[注意]注意

在Spring Cloud Stream的早期版本中,您可以通过设置spring.cloud.stream.bindings.output.producer.partitionSelectorClass属性来指定org.springframework.cloud.stream.binder.PartitionSelectorStrategy的实现。从2.0版开始,不推荐使用此属性,并且在将来的版本中将不再支持该属性。

34.3.2配置用于分区的输入绑定

通过设置应用程序本身的partitioned属性以及应用程序本身的instanceIndexinstanceCount属性,将输入绑定(通道名称为input)配置为接收分区数据。下面的例子:

spring.cloud.stream.bindings.input.consumer.partitioned=true
spring.cloud.stream.instanceIndex=3
spring.cloud.stream.instanceCount=5

instanceCount值表示应在其之间分区数据的应用程序实例的总数。instanceIndex在多个实例中必须是唯一的值,其值应介于0instanceCount - 1之间。实例索引可帮助每个应用程序实例识别从中接收数据的唯一分区。活页夹要求使用不支持本地分区的技术。例如,对于RabbitMQ,每个分区都有一个队列,该队列名称包含实例索引。对于Kafka,如果autoRebalanceEnabledtrue(默认值),则Kafka负责在实例之间分配分区,并且不需要这些属性。如果autoRebalanceEnabled设置为false,则绑定器将使用instanceCountinstanceIndex来确定实例所预订的分区(您必须拥有与实例数量一样多的分区) 。活页夹分配分区而不是Kafka。如果您希望特定分区的消息始终发送到同一实例,这可能很有用。当活页夹配置需要它们时,重要的是正确设置两个值,以确保使用所有数据,并且应用程序实例接收互斥的数据集。

尽管在单独情况下使用多个实例进行分区数据处理可能会很复杂,但Spring Cloud Dataflow可以通过正确填充输入和输出值并让您依赖运行时来显着简化流程基础架构,以提供有关实例索引和实例计数的信息。

35.测试

Spring Cloud Stream提供了在不连接消息传递系统的情况下测试您的微服务应用程序的支持。您可以使用spring-cloud-stream-test-support库提供的TestSupportBinder库来完成此操作,该库可以作为测试依赖项添加到应用程序中,如以下示例所示:

   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-stream-test-support</artifactId>
       <scope>test</scope>
   </dependency>
[注意]注意

TestSupportBinder使用Spring Boot自动配置机制来取代在类路径上找到的其他绑定器。因此,在添加活页夹作为依赖项时,必须确保使用了test范围。

TestSupportBinder使您可以与绑定的频道进行交互,并检查应用程序发送和接收的所有消息。

对于出站消息通道,TestSupportBinder注册一个订户,并将应用程序发出的消息保留在MessageCollector中。在测试期间可以检索它们,并针对它们进行断言。

您还可以将消息发送到入站消息通道,以便使用者应用程序可以使用消息。以下示例显示了如何在处理器上测试输入和输出通道:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ExampleTest {

  @Autowired
  private Processor processor;

  @Autowired
  private MessageCollector messageCollector;

  @Test
  @SuppressWarnings("unchecked")
  public void testWiring() {
    Message<String> message = new GenericMessage<>("hello");
    processor.input().send(message);
    Message<String> received = (Message<String>) messageCollector.forChannel(processor.output()).poll();
    assertThat(received.getPayload(), equalTo("hello world"));
  }


  @SpringBootApplication
  @EnableBinding(Processor.class)
  public static class MyProcessor {

    @Autowired
    private Processor channels;

    @Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
    public String transform(String in) {
      return in + " world";
    }
  }
}

在前面的示例中,我们创建了一个具有输入通道和输出通道的应用程序,两者均通过Processor接口绑定。绑定的接口被注入到测试中,以便我们可以访问两个通道。我们在输入通道上发送一条消息,然后使用Spring Cloud Stream的测试支持提供的MessageCollector捕获消息已作为结果发送到输出通道。收到消息后,我们可以验证组件是否正常运行。

35.1禁用测试Binder自动配置

测试绑定程序背后的目的是取代类路径上的所有其他绑定程序,以使其易于测试您的应用程序而无需更改生产依赖性。在某些情况下(例如,集成测试),使用实际的生产绑定程序是有用的,并且这需要禁用测试绑定程序自动配置。为此,可以使用Spring Boot自动配置排除机制之一来排除org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration类,如以下示例所示:

    @SpringBootApplication(exclude = TestSupportBinderAutoConfiguration.class)
    @EnableBinding(Processor.class)
    public static class MyProcessor {

        @Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
        public String transform(String in) {
            return in + " world";
        }
    }

禁用自动配置后,测试绑定程序将在类路径上可用,并且其defaultCandidate属性设置为false,以使其不会干扰常规用户配置。可以使用名称test来引用它,如以下示例所示:

spring.cloud.stream.defaultBinder=test

36.健康指标

Spring Cloud Stream为活页夹提供了健康指标。它以名称binders注册,可以通过设置management.health.binders.enabled属性来启用或禁用。

默认情况下,management.health.binders.enabled设置为falsemanagement.health.binders.enabled设置为true会启用运行状况指示器,使您可以访问/health端点以检索绑定程序运行状况指示器。

健康指标是特定于活页夹的,某些活页夹实现不一定提供健康指标。

37.指标发射器

Spring Boot Actuator为Micrometer提供依赖项管理和自动配置,Micrometer是一种支持众多监视系统的应用程序度量外观

Spring Cloud Stream提供了将任何可用的基于千分尺的度量标准发送到绑定目标的支持,从而允许从流应用程序定期收集度量标准数据,而无需依赖于轮询各个端点。

通过定义spring.cloud.stream.bindings.applicationMetrics.destination属性来激活“度量标准发射器”,该属性指定当前绑定程序用于发布度量标准消息的绑定目标的名称。

例如:

spring.cloud.stream.bindings.applicationMetrics.destination=myMetricDestination

前面的示例指示绑定程序绑定到myMetricDestination(即,Rabbit交换,Kafka主题等)。

以下属性可用于自定义指标的发射:

spring.cloud.stream.metrics.key

发出的度量标准的名称。每个应用程序的唯一值。

默认值:${spring.application.name:${vcap.application.name:${spring.config.name:application}}}

spring.cloud.stream.metrics.properties

允许白名单应用程序属性添加到度量有效负载

默认值:null。

spring.cloud.stream.metrics.meter-filter

控制要捕获的“仪表”的模式。例如,指定spring.integration.*将捕获名称以spring.integration.开头的仪表的度量标准信息

默认值:捕获所有“仪表”。

spring.cloud.stream.metrics.schedule-interval

控制发布度量标准数据的速率的时间间隔。

默认值:1分钟

考虑以下:

java -jar time-source.jar \
    --spring.cloud.stream.bindings.applicationMetrics.destination=someMetrics \
    --spring.cloud.stream.metrics.properties=spring.application** \
    --spring.cloud.stream.metrics.meter-filter=spring.integration.*

下面的示例显示由于上述命令而发布到绑定目标的数据的有效负载:

{
	"name": "application",
	"createdTime": "2018-03-23T14:48:12.700Z",
	"properties": {
	},
	"metrics": [
		{
			"id": {
				"name": "spring.integration.send",
				"tags": [
					{
						"key": "exception",
						"value": "none"
					},
					{
						"key": "name",
						"value": "input"
					},
					{
						"key": "result",
						"value": "success"
					},
					{
						"key": "type",
						"value": "channel"
					}
				],
				"type": "TIMER",
				"description": "Send processing time",
				"baseUnit": "milliseconds"
			},
			"timestamp": "2018-03-23T14:48:12.697Z",
			"sum": 130.340546,
			"count": 6,
			"mean": 21.72342433333333,
			"upper": 116.176299,
			"total": 130.340546
		}
	]
}
[注意]注意

鉴于在迁移到Micrometer后Metric消息的格式略有变化,发布的消息还将STREAM_CLOUD_STREAM_VERSION标头设置为2.x,以帮助区分Metric消息和旧版本的Spring Cloud Stream 。

38.示例

有关Spring Cloud Stream示例,请参见GitHub上spring-cloud-stream-samples存储库。

38.1在CloudFoundry上部署流应用程序

在CloudFoundry上,通常通过称为VCAP_SERVICES的特殊环境变量来公开服务

配置资料夹连接时,可以使用环境变量中的值,如数据流Cloud Foundry服务器文档中所述。

第六部分 Binder实现

39. Apache Kafka Binder

39.1使用

要使用Apache Kafka活页夹,您需要将spring-cloud-stream-binder-kafka作为依赖项添加到Spring Cloud Stream应用程序中,如以下Maven的示例所示:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>

另外,您也可以使用Spring Cloud Stream Kafka入门程序,如以下Maven示例所示:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>

39.2 Apache Kafka Binder概述

下图显示了Apache Kafka活页夹的工作方式的简化图:

图39.1。Kafka Binder

卡夫卡粘合剂

Apache Kafka Binder实现将每个目的地映射到Apache Kafka主题。消费者组直接映射到相同的Apache Kafka概念。分区也直接映射到Apache Kafka分区。

活页夹当前使用Apache Kafka kafka-clients 1.0.0 jar,并且设计为与至少该版本的代理一起使用。该客户端可以与较早的代理进行通信(请参见Kafka文档),但是某些功能可能不可用。例如,对于低于0.11.xx的版本,不支持本机头。另外,0.11.xx不支持autoAddPartitions属性。

39.3配置选项

本节包含Apache Kafka活页夹使用的配置选项。

有关与活页夹有关的常见配置选项和属性,请参阅核心文档

39.3.1 Kafka Binder Properties

spring.cloud.stream.kafka.binder.brokers

Kafka活页夹所连接的代理列表。

默认值:localhost

spring.cloud.stream.kafka.binder.defaultBrokerPort

brokers允许指定带有或不带有端口信息的主机(例如,host1,host2:port2)。当代理列表中未配置任何端口时,这将设置默认端口。

默认值:9092

spring.cloud.stream.kafka.binder.configuration

客户端属性(生产者和消费者)的键/值映射传递给绑定程序创建的所有客户端。由于生产者和消费者都使用了这些属性,因此应将使用限制为通用属性,例如安全性设置。Properties在这里取代引导中设置的所有属性。

默认值:空地图。

spring.cloud.stream.kafka.binder.consumerProperties

任意Kafka客户端使用者属性的键/值映射。这里的Properties取代了启动时和上面的configuration属性中设置的所有属性。

默认值:空地图。

spring.cloud.stream.kafka.binder.headers

活页夹传输的自定义标头列表。仅当与kafka-clients版本<0.11.0.0的旧版应用程序(⇐1.3.x)通信时才需要。较新的版本本机支持标头。

默认值:空。

spring.cloud.stream.kafka.binder.healthTimeout

等待获取分区信息的时间,以秒为单位。如果此计时器到期,运行状况将报告为已关闭。

默认值:10

spring.cloud.stream.kafka.binder.requiredAcks

代理程序上所需的确认数。有关生产者acks属性的信息,请参见Kafka文档。

默认值:1

spring.cloud.stream.kafka.binder.minPartitionCount

仅在设置了autoCreateTopicsautoAddPartitions时有效。活页夹在生成或使用数据的主题上配置的全局最小分区数。可以通过生产者的partitionCount设置或生产者的instanceCount * concurrency设置的值(如果任一个较大)来代替它。

默认值:1

spring.cloud.stream.kafka.binder.producer属性

任意Kafka客户端生产者属性的键/值映射。这里的Properties取代了启动时和上面的configuration属性中设置的所有属性。

默认值:空地图。

spring.cloud.stream.kafka.binder.replicationFactor

如果autoCreateTopics有效,则自动创建的主题的复制因子。可以在每个绑定上覆盖。

默认值:1

spring.cloud.stream.kafka.binder.autoCreateTopics

如果设置为true,则活页夹将自动创建新主题。如果设置为false,则活页夹依赖于已配置的主题。在后一种情况下,如果主题不存在,则活页夹无法启动。

[注意]注意

此设置与代理的auto.topic.create.enable设置无关,并且不影响它。如果服务器设置为自动创建主题,则可以使用默认代理设置将它们作为元数据检索请求的一部分进行创建。

默认值:true

spring.cloud.stream.kafka.binder.autoAddPartitions

如果设置为true,则活页夹将根据需要创建新分区。如果设置为false,则活页夹依赖于已配置的主题的分区大小。如果目标主题的分区数小于预期值,则活页夹无法启动。

默认值:false

spring.cloud.stream.kafka.binder.transaction.transactionIdPrefix

在活页夹中启用事务。请参阅Kafka文档中的transaction.idspring-kafka文档中的Transactions启用事务后,将忽略各个producer属性,并且所有生产者都将使用spring.cloud.stream.kafka.binder.transaction.producer.*属性。

默认值null(无交易)

spring.cloud.stream.kafka.binder.transaction.producer。*

交易绑定中生产者的全球生产者属性。请参见spring.cloud.stream.kafka.binder.transaction.transactionIdPrefix第39.3.3节“ Kafka生产者Properties”以及所有活页夹支持的常规生产者属性。

默认值:请参见各个生产者属性。

spring.cloud.stream.kafka.binder.headerMapperBeanName

KafkaHeaderMapper的bean名称,用于将spring-messaging标头映射到Kafka标头和从Kafka标头映射。例如,如果您希望自定义在标头中使用JSON反序列化的DefaultKafkaHeaderMapper中的受信任软件包,请使用此方法。

默认值:无。

39.3.2 Kafka消费者Properties

以下属性仅适用于Kafka使用者,并且必须以spring.cloud.stream.kafka.bindings.<channelName>.consumer.为前缀。

管理员配置

供应主题时使用的Kafka主题属性中的Map(例如,spring.cloud.stream.kafka.bindings.input.consumer.admin.configuration.message.format.version=0.9.0.0

默认值:无。

管理员副本分配

副本分配的Map <Integer,List <Integer >>,键为分区,值为分配。在配置新主题时使用。请参阅kafka-clients jar中的NewTopic Javadocs。

默认值:无。

管理员复制因子

设置主题时要使用的复制因子。覆盖活页夹范围的设置。忽略是否​​存在replicas-assignments

默认值:无(使用资料夹范围的默认值1)。

autoRebalanceEnabled

当为true时,主题分区将在使用者组的成员之间自动重新平衡。false时,将为每个使用者分配基于spring.cloud.stream.instanceCountspring.cloud.stream.instanceIndex的固定分区集合。这要求在每个启动的实例上同时设置spring.cloud.stream.instanceCountspring.cloud.stream.instanceIndex属性。在这种情况下,spring.cloud.stream.instanceCount属性的值通常必须大于1。

默认值:true

ackEachRecord

autoCommitOffsettrue时,此设置指示在处理每条记录后是否提交偏移量。默认情况下,在处理consumer.poll()返回的记录批次中的所有记录之后,将提交偏移量。可以使用max.poll.records Kafka属性控制轮询返回的记录数,该属性是通过使用者configuration属性设置的。将此设置为true可能会导致性能下降,但是这样做会减少发生故障时重新传送记录的可能性。另外,请参见活页夹requiredAcks属性,该属性还影响落实偏移量的性能。

默认值:false

autoCommitOffset

处理消息后是否自动提交偏移量。如果设置为false,则入站消息中将出现带有类型为org.springframework.kafka.support.Acknowledgment头的键kafka_acknowledgment的头。应用程序可以使用此标头来确认消息。有关详细信息,请参见示例部分。当此属性设置为false时,Kafka活页夹将ack模式设置为org.springframework.kafka.listener.AbstractMessageListenerContainer.AckMode.MANUAL,应用程序负责确认记录。另请参阅ackEachRecord

默认值:true

autoCommitOnError

仅在autoCommitOffset设置为true时有效。如果设置为false,它将抑制导致错误的消息的自动提交,仅对成功的消息进行提交。如果持续出现故障,它允许流从上次成功处理的消息自动重播。如果设置为true,它将始终自动提交(如果启用了自动提交)。如果未设置(默认值),则其有效值与enableDlq相同,如果将错误消息发送到DLQ,则自动提交错误消息,否则不提交。

默认值:未设置。

resetOffsets

是否将使用者的偏移量重置为startOffset提供的值。

默认值:false

startOffset

新组的起始偏移量。允许的值:earliestlatest如果为消费者“绑定”显式设置了消费者组(通过spring.cloud.stream.bindings.<channelName>.group),则“ startOffset”设置为earliest否则,将anonymous消费者组的值设置为latest另请参见resetOffsets(在此列表的前面)。

默认值:null(等于earliest)。

enableDlq

设置为true时,它将为使用者启用DLQ行为。默认情况下,导致错误的消息将转发到名为error.<destination>.<group>的主题。可以通过设置dlqName属性来配置DLQ主题名称。对于错误数量相对较小并且重放整个原始主题可能太麻烦的情况,这为更常见的Kafka重播方案提供了一个替代选项。有关更多信息请参见第39.6节“ Dead-Letter主题处理”处理。从2.0版开始,发送到DLQ主题的消息将通过以下标头得到增强:x-original-topicx-exception-messagex-exception-stacktracebyte[]destinationIsPatterntrue时不允许使用。

默认值:false

组态

使用包含通用Kafka使用者属性的键/值对进行映射。

默认值:空地图。

dlqName

接收错误消息的DLQ主题的名称。

默认值:null(如果未指定,则导致错误的消息将转发到名为error.<destination>.<group>的主题)。

dlqProducerProperties

使用此功能,可以设置特定于DLQ的生产者属性。通过kafka生产者属性可用的所有属性都可以通过此属性设置。

默认:默认Kafka生产者属性。

标头

指示入站通道适配器填充哪些标准头。允许的值:noneidtimestampboth如果使用本机反序列化并且第一个组件接收消息需要id(例如配置为使用JDBC消息存储的聚合器),则很有用。

默认值:none

converterBeanName

实现RecordMessageConverter的bean的名称。在入站通道适配器中用于替换默认的MessagingMessageConverter

默认值:null

idleEventInterval

事件之间的间隔(以毫秒为单位),指示最近未接收到任何消息。使用ApplicationListener<ListenerContainerIdleEvent>接收这些事件。有关用法示例,请参见“示例:暂停和恢复使用者”一节。

默认值:30000

destinationIsPattern

如果为true,则将目的地视为正则表达式Pattern,用于由代理匹配主题名称。设置为true时,不设置主题,并且不允许enableDlq,因为绑定者在设置阶段不知道主题名称。请注意,检测与模式匹配的新主题所花费的时间由消费者属性metadata.max.age.ms控制,该属性(在撰写本文时)默认为300,000ms(5分钟)。可以使用上面的configuration属性进行配置。

默认值:false

39.3.3 Kafka生产者Properties

以下属性仅适用于Kafka生产者,并且必须以spring.cloud.stream.kafka.bindings.<channelName>.producer.为前缀。

管理员配置

预置新主题时使用的Kafka主题属性中的Map(例如,spring.cloud.stream.kafka.bindings.input.consumer.admin.configuration.message.format.version=0.9.0.0

默认值:无。

管理员副本分配

副本分配的Map <Integer,List <Integer >>,键为分区,值为分配。在配置新主题时使用。请参见kafka-clients jar中的NewTopic javadocs。

默认值:无。

管理员复制因子

设置新主题时要使用的复制因子。覆盖活页夹范围的设置。如果存在replicas-assignments,则忽略。

默认值:无(使用资料夹范围的默认值1)。

缓冲区大小

Kafka生产者在发送之前尝试分批处理的数据量的上限(以字节为单位)。

默认值:16384

同步

生产者是否同步。

默认值:false

batchTimeout

生产者在发送消息之前等待允许更多消息在同一批中累积的时间。(通常,生产者根本不等待,仅发送在上一次发送过程中累积的所有消息。)非零值可能会增加吞吐量,但会增加延迟。

默认值:0

messageKeyExpression

根据用于填充产生的Kafka消息的密钥的传出消息(例如,headers['myKey'])评估的SpEL表达式。有效负载无法使用,因为在评估此表达式时,有效负载已经采用byte[]的形式。

默认值:none

headerPatterns

以逗号分隔的简单模式列表,以匹配要映射到ProducerRecord中的Kafka Headers的Spring消息头。模式可以以通配符(星号)开头或结尾。可以使用前缀!来否定模式。比赛在第一个比赛(正数或负数)之后停止。例如,!ask,as*将传递ash,但不会传递askidtimestamp从未映射。

默认值:*(所有标头-idtimestamp除外)

组态

使用包含通用Kafka生产者属性的键/值对进行映射。

默认值:空地图。

[注意]注意

Kafka活页夹使用生产者的partitionCount设置作为提示来创建具有给定分区数的主题(与minPartitionCount结合使用,两者中的最大值是所使用的值)。同时为活页夹配置minPartitionCount和为应用程序配置partitionCount时要小心,因为使用了较大的值。如果已经存在一个分区数较小的主题,并且禁用了autoAddPartitions(默认设置),则绑定器无法启动。如果已经存在一个分区数较小的主题,并且启用了autoAddPartitions,则会添加新的分区。如果已经存在的主题的分区数量大于最大数量(minPartitionCountpartitionCount),则使用现有分区计数。

39.3.4使用示例

在本节中,我们将说明针对特定方案使用前面的属性。

示例:将autoCommitOffset设置为false并依靠手动进行

此示例说明了如何在用户应用程序中手动确认偏移。

本示例要求将spring.cloud.stream.kafka.bindings.input.consumer.autoCommitOffset设置为false在您的示例中使用相应的输入通道名称。

@SpringBootApplication
@EnableBinding(Sink.class)
public class ManuallyAcknowdledgingConsumer {

 public static void main(String[] args) {
     SpringApplication.run(ManuallyAcknowdledgingConsumer.class, args);
 }

 @StreamListener(Sink.INPUT)
 public void process(Message<?> message) {
     Acknowledgment acknowledgment = message.getHeaders().get(KafkaHeaders.ACKNOWLEDGMENT, Acknowledgment.class);
     if (acknowledgment != null) {
         System.out.println("Acknowledgment provided");
         acknowledgment.acknowledge();
     }
 }
}

示例:安全配置

Apache Kafka 0.9支持客户端和代理之间的安全连接。要利用此功能,请遵循Apache Kafka文档中的准则以及Confluent文档中的Kafka 0.9 安全准则使用spring.cloud.stream.kafka.binder.configuration选项为活页夹创建的所有客户端设置安全性属性。

例如,要将security.protocol设置为SASL_SSL,请设置以下属性:

spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_SSL

可以以类似方式设置所有其他安全属性。

使用Kerberos时,请遵循参考文档中的说明来创建和引用JAAS配置。

Spring Cloud Stream支持通过使用JAAS配置文件并使用Spring Boot属性将JAAS配置信息传递到应用程序。

使用JAAS配置文件

可以使用系统属性为Spring Cloud Stream应用程序设置JAAS和(可选)krb5文件位置。以下示例显示如何通过使用JAAS配置文件使用SASL和Kerberos启动Spring Cloud Stream应用程序:

 java -Djava.security.auth.login.config=/path.to/kafka_client_jaas.conf -jar log.jar \
   --spring.cloud.stream.kafka.binder.brokers=secure.server:9092 \
   --spring.cloud.stream.bindings.input.destination=stream.ticktock \
   --spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_PLAINTEXT
使用Spring Boot Properties

作为使用JAAS配置文件的替代方法,Spring Cloud Stream提供了一种通过使用Spring Boot属性为Spring Cloud Stream应用程序设置JAAS配置的机制。

以下属性可用于配置Kafka客户端的登录上下文:

spring.cloud.stream.kafka.binder.jaas.loginModule

登录模块名称。正常情况下无需设置。

默认值:com.sun.security.auth.module.Krb5LoginModule

spring.cloud.stream.kafka.binder.jaas.controlFlag

登录模块的控制标志。

默认值:required

spring.cloud.stream.kafka.binder.jaas.options

使用包含登录模块选项的键/值对进行映射。

默认值:空地图。

以下示例显示如何使用Spring Boot配置属性使用SASL和Kerberos启动Spring Cloud Stream应用程序:

 java --spring.cloud.stream.kafka.binder.brokers=secure.server:9092 \
   --spring.cloud.stream.bindings.input.destination=stream.ticktock \
   --spring.cloud.stream.kafka.binder.autoCreateTopics=false \
   --spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_PLAINTEXT \
   --spring.cloud.stream.kafka.binder.jaas.options.useKeyTab=true \
   --spring.cloud.stream.kafka.binder.jaas.options.storeKey=true \
   --spring.cloud.stream.kafka.binder.jaas.options.keyTab=/etc/security/keytabs/kafka_client.keytab \
   --spring.cloud.stream.kafka.binder.jaas.options.principal=kafka-client-1@EXAMPLE.COM

前面的示例表示以下JAAS文件的等效项:

KafkaClient {
    com.sun.security.auth.module.Krb5LoginModule required
    useKeyTab=true
    storeKey=true
    keyTab="/etc/security/keytabs/kafka_client.keytab"
    principal="[email protected]";
};

如果所需的主题已经存在于代理上或将由管理员创建,则可以关闭自动创建,仅需要发送客户端JAAS属性。

[注意]注意

请勿在同一应用程序中混合使用JAAS配置文件和Spring Boot属性。如果-Djava.security.auth.login.config系统属性已经存在,则Spring Cloud Stream将忽略Spring Boot属性。

[注意]注意

autoCreateTopicsautoAddPartitions与Kerberos一起使用时要小心。通常,应用程序可能使用在Kafka和Zookeeper中没有管理权限的主体。因此,依靠Spring Cloud Stream创建/修改主题可能会失败。在安全的环境中,强烈建议您使用Kafka工具创建主题并以管理方式管理ACL。

示例:暂停和恢复使用方

如果希望暂停使用但不引起分区重新平衡,则可以暂停并恢复使用方。通过将Consumer作为参数添加到@StreamListener中,可以简化此操作。要恢复,需要为ListenerContainerIdleEvent实例使用ApplicationListener事件的发布频率由idleEventInterval属性控制。由于使用者不是线程安全的,因此必须在调用线程上调用这些方法。

以下简单的应用程序显示了如何暂停和恢复:

@SpringBootApplication
@EnableBinding(Sink.class)
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

	@StreamListener(Sink.INPUT)
	public void in(String in, @Header(KafkaHeaders.CONSUMER) Consumer<?, ?> consumer) {
		System.out.println(in);
		consumer.pause(Collections.singleton(new TopicPartition("myTopic", 0)));
	}

	@Bean
	public ApplicationListener<ListenerContainerIdleEvent> idleListener() {
		return event -> {
			System.out.println(event);
			if (event.getConsumer().paused().size() > 0) {
				event.getConsumer().resume(event.getConsumer().paused());
			}
		};
	}

}

39.4错误通道

从版本1.3开始,绑定程序无条件地将异常发送到每个使用者目标的错误通道,并且还可以配置为将异步生产者发送失败消息发送到错误通道。有关更多信息请参见第29.4节“错误处理”

发送失败的ErrorMessage的有效载荷是具有以下属性的KafkaSendFailureException

  • failedMessage:发送失败的Spring消息Message<?>
  • record:从failedMessage创建的原始ProducerRecord

没有对生产者异常的自动处理(例如发送到Dead-Letter队列)。您可以使用自己的Spring Integration流使用这些异常。

39.5 Kafka指标

Kafka活页夹模块公开以下指标:

spring.cloud.stream.binder.kafka.offset:此度量标准指示给定的消费者组尚未从给定的活页夹主题中消费多少消息。提供的指标基于Mircometer指标库。度量标准包含消费者组信息,主题以及与主题上的最新偏移量有关的承诺偏移量的实际滞后时间。该指标对于向PaaS平台提供自动缩放反馈特别有用。

39.6 Dead-Letter主题处理

因为您无法预期用户将如何处置死信,所以该框架没有提供任何标准机制来处理它们。如果死信的原因是暂时的,则您可能希望将消息路由回原始主题。但是,如果问题是永久性问题,则可能导致无限循环。本主题中的示例Spring Boot应用程序是如何将这些消息路由回原始主题的示例,但是在尝试了三遍之后,将其移至停车场主题。该应用程序是另一个从死信主题读取的spring-cloud-stream应用程序。5秒钟未收到任何消息时,它将终止。

这些示例假定原始目的地为so8400out,而使用者组为so8400

有两种策略可供考虑:

  • 考虑仅在主应用程序未运行时才运行重新路由。否则,瞬态错误的重试会很快用完。
  • 或者,使用两阶段方法:使用此应用程序将路由到第三个主题,将另一个应用程序从那里路由回到主主题。

以下代码清单显示了示例应用程序:

application.properties。 

spring.cloud.stream.bindings.input.group=so8400replay
spring.cloud.stream.bindings.input.destination=error.so8400out.so8400

spring.cloud.stream.bindings.output.destination=so8400out
spring.cloud.stream.bindings.output.producer.partitioned=true

spring.cloud.stream.bindings.parkingLot.destination=so8400in.parkingLot
spring.cloud.stream.bindings.parkingLot.producer.partitioned=true

spring.cloud.stream.kafka.binder.configuration.auto.offset.reset=earliest

spring.cloud.stream.kafka.binder.headers=x-retries

应用。 

@SpringBootApplication
@EnableBinding(TwoOutputProcessor.class)
public class ReRouteDlqKApplication implements CommandLineRunner {

    private static final String X_RETRIES_HEADER = "x-retries";

    public static void main(String[] args) {
        SpringApplication.run(ReRouteDlqKApplication.class, args).close();
    }

    private final AtomicInteger processed = new AtomicInteger();

    @Autowired
    private MessageChannel parkingLot;

    @StreamListener(Processor.INPUT)
    @SendTo(Processor.OUTPUT)
    public Message<?> reRoute(Message<?> failed) {
        processed.incrementAndGet();
        Integer retries = failed.getHeaders().get(X_RETRIES_HEADER, Integer.class);
        if (retries == null) {
            System.out.println("First retry for " + failed);
            return MessageBuilder.fromMessage(failed)
                    .setHeader(X_RETRIES_HEADER, new Integer(1))
                    .setHeader(BinderHeaders.PARTITION_OVERRIDE,
                            failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
                    .build();
        }
        else if (retries.intValue() < 3) {
            System.out.println("Another retry for " + failed);
            return MessageBuilder.fromMessage(failed)
                    .setHeader(X_RETRIES_HEADER, new Integer(retries.intValue() + 1))
                    .setHeader(BinderHeaders.PARTITION_OVERRIDE,
                            failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
                    .build();
        }
        else {
            System.out.println("Retries exhausted for " + failed);
            parkingLot.send(MessageBuilder.fromMessage(failed)
                    .setHeader(BinderHeaders.PARTITION_OVERRIDE,
                            failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
                    .build());
        }
        return null;
    }

    @Override
    public void run(String... args) throws Exception {
        while (true) {
            int count = this.processed.get();
            Thread.sleep(5000);
            if (count == this.processed.get()) {
                System.out.println("Idle, terminating");
                return;
            }
        }
    }

    public interface TwoOutputProcessor extends Processor {

        @Output("parkingLot")
        MessageChannel parkingLot();

    }

}

39.7使用Kafka Binder进行分区

Apache Kafka本机支持主题分区。

有时,将数据发送到特定的分区是有好处的-例如,当您要严格订购消息处理时(特定客户的所有消息应转到同一分区)。

以下示例显示了如何配置生产方和消费者方:

@SpringBootApplication
@EnableBinding(Source.class)
public class KafkaPartitionProducerApplication {

    private static final Random RANDOM = new Random(System.currentTimeMillis());

    private static final String[] data = new String[] {
            "foo1", "bar1", "qux1",
            "foo2", "bar2", "qux2",
            "foo3", "bar3", "qux3",
            "foo4", "bar4", "qux4",
            };

    public static void main(String[] args) {
        new SpringApplicationBuilder(KafkaPartitionProducerApplication.class)
            .web(false)
            .run(args);
    }

    @InboundChannelAdapter(channel = Source.OUTPUT, poller = @Poller(fixedRate = "5000"))
    public Message<?> generate() {
        String value = data[RANDOM.nextInt(data.length)];
        System.out.println("Sending: " + value);
        return MessageBuilder.withPayload(value)
                .setHeader("partitionKey", value)
                .build();
    }

}

application.yml。 

spring:
  cloud:
    stream:
      bindings:
        output:
          destination: partitioned.topic
          producer:
            partitioned: true
            partition-key-expression: headers['partitionKey']
            partition-count: 12

[重要]重要

必须为该主题提供足够的分区,以实现所有消费者组所需的并发性。上面的配置最多支持12个使用者实例(如果concurrency为2,则为6;如果并发值为3,则为4,依此类推)。通常最好过量供应分区,以使将来的使用者或并发性增加。

[注意]注意

前面的配置使用默认分区(key.hashCode() % partitionCount)。根据键值,这可能会或可能不会提供适当的平衡算法。您可以使用partitionSelectorExpressionpartitionSelectorClass属性覆盖此默认设置。

由于分区是由Kafka本地处理的,因此在使用者端不需要特殊配置。Kafka在实例之间分配分区。

以下Spring Boot应用程序侦听Kafka流,并打印(到控制台)每条消息去往的分区ID:

@SpringBootApplication
@EnableBinding(Sink.class)
public class KafkaPartitionConsumerApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(KafkaPartitionConsumerApplication.class)
            .web(false)
            .run(args);
    }

    @StreamListener(Sink.INPUT)
    public void listen(@Payload String in, @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) {
        System.out.println(in + " received from partition " + partition);
    }

}

application.yml。 

spring:
  cloud:
    stream:
      bindings:
        input:
          destination: partitioned.topic
          group: myGroup

您可以根据需要添加实例。Kafka重新平衡分区分配。如果实例计数(或instance count * concurrency)超过了分区数,则某些使用者处于空闲状态。

40. Apache Kafka流Binder

40.1使用

要使用Kafka Streams活页夹,只需使用以下Maven坐标将其添加到Spring Cloud Stream应用程序中:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream-binder-kafka-streams</artifactId>
</dependency>

40.2 Kafka流Binder概述

Spring Cloud Stream的Apache Kafka支持还包括为Apache Kafka流绑定显式设计的绑定器实现。通过这种本机集成,Spring Cloud Stream“处理器”应用程序可以直接在核心业务逻辑中使用 Apache Kafka Streams API。

Kafka Streams活页夹实现基于Spring Kafka 项目中Kafka Streams提供的基础

Kafka Streams绑定器为Kafka Streams中的三种主要类型(KStream,KTable和GlobalKTable)提供了绑定功能。

作为本机集成的一部分, Kafka Streams API提供的高级Streams DSL可用于业务逻辑。

还提供处理器API支持的早期版本

如早期所述,Spring Cloud Stream中的Kafka流支持仅在处理器模型中严格可用。可以应用一种模型,在该模型中,可以从入站主题读取消息,进行业务处理,然后可以将转换后的消息写入出站主题。它也可以用于无出站目的地的处理器应用程序中。

40.2.1流DSL

此应用程序消耗来自Kafka主题(例如words)的数据,在5秒的时间窗口内为每个唯一单词计算单词计数,并将计算结果发送到下游主题(例如counts)进行进一步处理。

@SpringBootApplication
@EnableBinding(KStreamProcessor.class)
public class WordCountProcessorApplication {

	@StreamListener("input")
	@SendTo("output")
	public KStream<?, WordCount> process(KStream<?, String> input) {
		return input
                .flatMapValues(value -> Arrays.asList(value.toLowerCase().split("\\W+")))
                .groupBy((key, value) -> value)
                .windowedBy(TimeWindows.of(5000))
                .count(Materialized.as("WordCounts-multi"))
                .toStream()
                .map((key, value) -> new KeyValue<>(null, new WordCount(key.key(), value, new Date(key.window().start()), new Date(key.window().end()))));
    }

	public static void main(String[] args) {
		SpringApplication.run(WordCountProcessorApplication.class, args);
	}

一旦构建为uber-jar(例如wordcount-processor.jar),您就可以像下面一样运行上面的示例。

java -jar wordcount-processor.jar  --spring.cloud.stream.bindings.input.destination=words --spring.cloud.stream.bindings.output.destination=counts

此应用程序将使用来自Kafka主题words的消息,并将计算的结果发布到输出主题counts

Spring Cloud Stream将确保来自传入和传出主题的消息都自动绑定为KStream对象。作为开发人员,您可以专注于代码的业务方面,即编写处理器中所需的逻辑。框架自动处理Kafka Streams基础结构所需的Streams DSL特定配置的设置。

40.3配置选项

本节包含Kafka Streams绑定程序使用的配置选项。

有关与活页夹有关的常见配置选项和属性,请参阅核心文档

40.3.1 Kafka流Properties

以下属性在活页夹级别可用,并且必须以spring.cloud.stream.kafka.streams.binder.文字作为前缀。

组态
使用包含与Apache Kafka Streams API有关的属性的键/值对进行映射。此属性必须以spring.cloud.stream.kafka.streams.binder.为前缀。以下是使用此属性的一些示例。
spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde=org.apache.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde=org.apache.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.kafka.streams.binder.configuration.commit.interval.ms=1000

有关可能用于流配置的所有属性的更多信息,请参见Apache Kafka Streams文档中的StreamsConfig JavaDocs。

经纪人

经纪人网址

默认值:localhost

zkNodes

Zookeeper网址

默认值:localhost

serdeError

反序列化错误处理程序类型。可能的值为-logAndContinuelogAndFailsendToDlq

默认值:logAndFail

applicationId

在绑定程序级别全局设置Kafka Streams应用程序的application.id的简便方法。如果应用程序包含多个StreamListener方法,则应在每个输入绑定的绑定级别上设置application.id。

默认值:none

以下属性适用于Kafka Streams生产者,并且必须以spring.cloud.stream.kafka.streams.bindings.<binding name>.producer.字面量为前缀。为方便起见,如果存在多个输出绑定并且它们都需要一个公共值,则可以使用前缀spring.cloud.stream.kafka.streams.default.producer.进行配置。

钥匙串

要使用的密钥序列

默认值:none

valueSerde

使用价值服务

默认值:none

useNativeEncoding

标志以启用本机编码

默认值:false

以下属性适用于Kafka Streams使用者,并且必须以spring.cloud.stream.kafka.streams.bindings.<binding name>.consumer.`literal. For convenience, if there multiple input bindings and they all require a common value, that can be configured by using the prefix `spring.cloud.stream.kafka.streams.default.consumer.为前缀。

applicationId

设置每个输入绑定的application.id。

默认值:none

钥匙串

要使用的密钥序列

默认值:none

valueSerde

使用价值服务

默认值:none

物化为

使用传入的KTable类型实现状态存储

默认值:none

useNativeDecoding

标志以启用本机解码

默认值:false

dlqName

DLQ主题名称。

默认值:none

40.3.2 TimeWindow属性:

窗口化是流处理应用程序中的重要概念。以下属性可用于配置时间窗口计算。

spring.cloud.stream.kafka.streams.timeWindow.length

赋予此属性后,您可以将TimeWindows bean自动连接到应用程序中。该值以毫秒为单位。

默认值:none

spring.cloud.stream.kafka.streams.timeWindow.advanceBy

值以毫秒为单位。

默认值:none

40.4多个输入绑定

对于需要多个传入KStream对象或KStream和KTable对象的组合的用例,Kafka Streams绑定程序提供了多个绑定支持。

让我们来看看它的作用。

40.4.1多个输入绑定作为接收器

@EnableBinding(KStreamKTableBinding.class)
.....
.....
@StreamListener
public void process(@Input("inputStream") KStream<String, PlayEvent> playEvents,
                    @Input("inputTable") KTable<Long, Song> songTable) {
                    ....
                    ....
}

interface KStreamKTableBinding {

    @Input("inputStream")
    KStream<?, ?> inputStream();

    @Input("inputTable")
    KTable<?, ?> inputTable();
}

在上面的示例中,应用程序被编写为接收器,即没有输出绑定,并且应用程序必须决定有关下游处理的内容。当您以这种方式编写应用程序时,您可能希望向下游发送信息或将其存储在状态存储中(有关可查询状态存储,请参见下文)。

对于传入的KTable,如果要将计算具体化为状态存储,则必须通过以下属性将其表示。

spring.cloud.stream.kafka.streams.bindings.inputTable.consumer.materializedAs: all-songs

上面的示例显示了将KTable用作输入绑定。绑定器还支持GlobalKTable的输入绑定。当您必须确保应用程序的所有实例都可以访问主题中的数据更新时,GlobalKTable绑定非常有用。KTable和GlobalKTable绑定仅在输入上可用。Binder支持KStream的输入和输出绑定。

40.4.2作为处理器的多个输入绑定

@EnableBinding(KStreamKTableBinding.class)
....
....

@StreamListener
@SendTo("output")
public KStream<String, Long> process(@Input("input") KStream<String, Long> userClicksStream,
                                     @Input("inputTable") KTable<String, String> userRegionsTable) {
....
....
}

interface KStreamKTableBinding extends KafkaStreamsProcessor {

    @Input("inputX")
    KTable<?, ?> inputTable();
}

40.5多个输出绑定(又名分支)

Kafka流允许根据某些谓词将出站数据分为多个主题。Kafka Streams绑定程序提供对此功能的支持,而不会损害最终用户应用程序中通过StreamListener公开的编程模型。

您可以按照上面在字数示例中展示的常用方法编写应用程序。但是,使用分支功能时,您需要做一些事情。首先,您需要确保您的返回类型为KStream[],而不是常规的KStream其次,您需要使用SendTo批注,该批注按顺序包含输出绑定(请参见下面的示例)。对于这些输出绑定中的每一个,您都需要配置目标,内容类型等,并符合标准Spring Cloud Stream的要求。

这是一个例子:

@EnableBinding(KStreamProcessorWithBranches.class)
@EnableAutoConfiguration
public static class WordCountProcessorApplication {

    @Autowired
    private TimeWindows timeWindows;

    @StreamListener("input")
    @SendTo({"output1","output2","output3})
    public KStream<?, WordCount>[] process(KStream<Object, String> input) {

			Predicate<Object, WordCount> isEnglish = (k, v) -> v.word.equals("english");
			Predicate<Object, WordCount> isFrench =  (k, v) -> v.word.equals("french");
			Predicate<Object, WordCount> isSpanish = (k, v) -> v.word.equals("spanish");

			return input
					.flatMapValues(value -> Arrays.asList(value.toLowerCase().split("\\W+")))
					.groupBy((key, value) -> value)
					.windowedBy(timeWindows)
					.count(Materialized.as("WordCounts-1"))
					.toStream()
					.map((key, value) -> new KeyValue<>(null, new WordCount(key.key(), value, new Date(key.window().start()), new Date(key.window().end()))))
					.branch(isEnglish, isFrench, isSpanish);
    }

    interface KStreamProcessorWithBranches {

    		@Input("input")
    		KStream<?, ?> input();

    		@Output("output1")
    		KStream<?, ?> output1();

    		@Output("output2")
    		KStream<?, ?> output2();

    		@Output("output3")
    		KStream<?, ?> output3();
    	}
}

Properties: