springcloud-alibaba
介绍:
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
此外,阿里云同时还提供了 Spring Cloud Alibaba 企业版 微服务解决方案,包括无侵入服务治理(全链路灰度,无损上下线,离群实例摘除等),企业级 Nacos 注册配置中心和企业级云原生网关等众多产品。
参考文档 请查看 WIKI 。
主要功能
- 服务限流降级:默认支持 WebServlet、WebFlux、OpenFeign、RestTemplate、Spring Cloud Gateway、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
- 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成对应 Spring Cloud 版本所支持的负载均衡组件的适配。
- 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
- 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
- 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。
- 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
- 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
- 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
注册中心 nacos
官网:
下载地址:https://github.com/alibaba/nacos/releases/download/2.2.0/nacos-server-2.2.0.zip
启动:
win 环境:
1 | # 需要 java jdk 1.8 + , maven 3.2 + |
登录地址:
1 | http://192.168.205.1:8848/nacos/index.html#/login |
springCloudAlibaba 版本对照表
springCloudAlibaba代码分支 | spring Cloud | spring Boot | jdk |
---|---|---|---|
2022.x | Spring Cloud 2022 | Spring Boot 3.0.x | 最低支持 JDK 17 |
2021.x | Spring Cloud 2021 | Spring Boot 2.6.x | 最低支持 JDK 1.8 |
2020.0 | Spring Cloud 2020 | Spring Boot 2.4.x | 最低支持 JDK 1.8 |
2.2.x | Spring Cloud Hoxton | Spring Boot 2.2.x | 最低支持 JDK 1.8 |
greenwich | Spring Cloud Greenwich | Spring Boot 2.1.x | 最低支持 JDK 1.8 |
finchley | Spring Cloud Finchley | Spring Boot 2.0.x | 最低支持 JDK 1.8 |
1.x | Spring Cloud Edgware | Spring Boot 1.x | 最低支持 JDK 1.7 |
项目准备
- 创建项目
父工程 pom
1 |
|
子工程 pom
1 |
|
给子项目(需要注册的项目)添加 nacos 服务注册依赖
1 | <!--nacos 服务注册与发现 --> |
给子项目(需要注册的项目)开启服务注册/发现
1 |
|
给子项目(需要注册的项目)配置服务端口、服务名称和数据库连接
1 | server: |
子项目编写一个接口:
1 |
|
启动项目后可发现服务
注册中心的实现还有 eureka 关于eureka 请看我另外一篇博客“使用Eureka编写注册中心服务”
负载均衡
ribbon
ribbon 是spring cloud 默认集成的组件
复制 子项目 serviceA 为 serviceB 修改端口 为 8082
新建子项目 client 作为客户端调用方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
SpringApplication.run(Client.class,args);
}
// 配置 @LoadBalanced 注解
public RestTemplate restTemplate() {
return new RestTemplate();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestController {
RestTemplate template;
Object getOrderByOid({ String oid)
// 这里通过服务名调用
return template.getForObject("http://serviceA/test/order/"+oid, Order.class);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16server:
port: 8080
spring:
application:
name: client
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username: root
password: 1qaz!QAZ
profiles:
active: dev
cloud:
nacos:
server-addr: 127.0.0.1:8848 # nacos 服务地址多次调用会发现轮询请求服务名为‘serviceA’的不同的服务实例
详细内容请参考我的另外一篇笔记:客户端负载均衡Ribbon
feign
添加依赖
1 | <dependency> |
feign 客户端代码
1 | package com.wu.feign; |
开启 feign
1 | package com.wu; |
控制器调用
1 | package com.wu.controller; |
关于 feign 的更多内容请看我另外一篇笔记:声明式REST客户端Feign
服务容错-Sentine
官网:https://sentinelguard.io/zh-cn/docs/introduction.html
文档:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
https://github.com/alibaba/Sentinel/tree/master/sentinel-extension/sentinel-datasource-nacos
下载控制台
启动控制台
1 | java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.6.jar |
1 | # 用户名密码都是 |
项目添加依赖:
pom
1 | <dependency> |
application.yml
1 | spring: |
这里的 spring.cloud.sentinel.transport.port
端口配置会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了一个限流规则,会把规则数据 push 给这个 Http Server 接收,Http Server 再将规则注册到 Sentinel 中。
如果这里没有数据那么手动访问下接口看看
可直接在控制台手动设置规则:
规则种类
- 流量控制规则 (FlowRule)
流量规则的定义
重要属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 或线程数模式 | QPS 模式 |
limitApp | 流控针对的调用来源 | default ,代表不区分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流控效果(直接拒绝 / 排队等待 / 慢启动模式),不支持按调用关系限流 | 直接拒绝 |
同一个资源可以同时有多个限流规则。更多详细内容可以参考 流量控制。
- 熔断降级规则 (DegradeRule)
熔断降级规则包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,即规则的作用对象 | |
grade | 熔断策略,支持慢调用比例/异常比例/异常数策略 | 慢调用比例 |
count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 | |
timeWindow | 熔断时长,单位为 s | |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | 5 |
statIntervalMs | 统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) | 1000 ms |
slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
同一个资源可以同时有多个降级规则。更多详情可以参考 熔断降级。
- 系统保护规则 (SystemRule)
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统规则包含下面几个重要的属性:
Field | 说明 | 默认值 |
---|---|---|
highestSystemLoad | load1 触发值,用于触发自适应控制阶段 |
-1 (不生效) |
avgRt | 所有入口流量的平均响应时间 | -1 (不生效) |
maxThread | 入口流量的最大并发数 | -1 (不生效) |
qps | 所有入口资源的 QPS | -1 (不生效) |
highestCpuUsage | 当前系统的 CPU 使用率(0.0-1.0) | -1 (不生效) |
更多详情可以参考 系统自适应保护。
访问控制规则 (AuthorityRule)
很多时候,我们需要根据调用方来限制资源是否通过,这时候可以使用 Sentinel 的访问控制(黑白名单)的功能。黑白名单根据资源的请求来源(
origin
)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。授权规则,即黑白名单规则(
AuthorityRule
)非常简单,主要有以下配置项:resource
:资源名,即限流规则的作用对象limitApp
:对应的黑名单/白名单,不同 origin 用,
分隔,如appA,appB
strategy
:限制模式,AUTHORITY_WHITE
为白名单模式,AUTHORITY_BLACK
为黑名单模式,默认为白名单模式
更多详情可以参考 来源访问控制。
热点规则 (ParamFlowRule)
热点参数规则(
ParamFlowRule
)类似于流量控制规则(FlowRule
):
属性 | 说明 | 默认值 |
---|---|---|
resource | 资源名,必填 | |
count | 限流阈值,必填 | |
grade | 限流模式 | QPS 模式 |
durationInSec | 统计窗口时间长度(单位为秒),1.6.0 版本开始支持 | 1s |
controlBehavior | 流控效果(支持快速失败和匀速排队模式),1.6.0 版本开始支持 | 快速失败 |
maxQueueingTimeMs | 最大排队等待时长(仅在匀速排队模式生效),1.6.0 版本开始支持 | 0ms |
paramIdx | 热点参数的索引,必填,对应 SphU.entry(xxx, args) 中的参数索引位置 |
|
paramFlowItemList | 参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型和字符串类型 |
|
clusterMode | 是否是集群参数流控规则 | false |
clusterConfig | 集群流控相关配置 |
详情可以参考 热点参数限流。
动态规则扩展
简单的说法就是把规则集中管理(存入数据库或配置中心)
扩展常见的实现方式有:
- 拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件,甚至是 VCS 等。这样做的方式是简单,缺点是无法及时获取变更;
- 推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。
Sentinel 目前支持以下数据源扩展:
通过 nacos 实现动态规则
内容:
1 | [ |
RuleConstant.java
1 | public final class RuleConstant { |
springcloud 中配置 datasource
application.yml
1 | spring: |
ds1, ds2, 是 ReadableDataSource
的名字,可随意编写。后面的 file
,zk
,nacos
, apollo
就是对应具体的数据源,它们后面的配置就是这些具体数据源各自的配置。注意数据源的依赖要单独引入(比如 sentinel-datasource-nacos
)。
每种数据源都有两个共同的配置项: data-type
、 converter-class
以及 rule-type
。
data-type
配置项表示 Converter
类型,Spring Cloud Alibaba Sentinel 默认提供两种内置的值,分别是 json
和 xml
(不填默认是json)。 如果不想使用内置的 json
或 xml
这两种 Converter
,可以填写 custom
表示自定义 Converter
,然后再配置 converter-class
配置项,该配置项需要写类的全路径名(比如 spring.cloud.sentinel.datasource.ds1.file.converter-class=com.alibaba.cloud.examples.JsonFlowRuleListConverter
)。
rule-type
配置表示该数据源中的规则属于哪种类型的规则(flow
, degrade
, authority
, system
, param-flow
, gw-flow
, gw-api-group
)。注意网关流控规则 (GatewayFlowRule) 对应 gw-flow
。
启动项目报错:
1 | 2023-02-23 15:41:14.564 ERROR 26896 --- [ main] c.a.c.s.c.SentinelDataSourceHandler : [Sentinel Starter] DataSource ds build error: Error creating bean with name 'ds-sentinel-nacos-datasource': Lookup method resolution failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [com.alibaba.cloud.sentinel.datasource.factorybean.NacosDataSourceFactoryBean] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2] |
添加依赖:
pom
1 | <dependency> |
启动成功后访问 sentinel 控制台(若没有数据 就访问一次接口)
登录 nacos 控制台 编辑规则,添加另外一个接口的规则
发布后在 sentinel 刷新下可看到新的规则
快速访问接口几次即可看到被限制
服务网关 gateway
Spring Cloud 网关 | 中文文档 (gitcode.net)
牛刀小试
创建一个 module
pom
1 | <dependencies> |
1 | server: |
浏览器请求测试:localhost:1000/test-gateway/test/order/101
测试转发2:localhost:1000/gateway/test/system-name
结合 nacos 一起使用
添加依赖:
pom
1 | <!--nacos客户端--> |
启动类添加开启注解:
1 | package com.wu; |
application.yml
1 | server: |
注意 uri 这里 不是用 http 了。
lb 指的是从nacos中按照名称获取微服务,并遵循负载均 衡策略
精简配置版:
1 | server: |
请求测试:localhost:1000/serviceA/test/order/101
解释一下:
注意这里的请求地址。http://localhost:1000/serviceA/test/order/101。其中 http://localhost:1000 是 gateway 服务的地址;/serviceA 是服务名;/test/order/101 是接口地址
gateway 架构
基本概念
路由(Route) 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。主要定义了下面的几个 信息:
id,路由标识符,区别于其他 Route。
uri,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
predicate,断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
fifilter,过滤器用于修改请求和响应信息。
执行流程
客户端向 Spring Cloud网关提出请求。如果网关处理程序映射确定请求与路由匹配,则将请求发送到网关 Web 处理程序。此处理程序通过特定于该请求的筛选链来运行该请求。用虚线划分过滤器的原因是,过滤器可以在发送代理请求之前和之后运行逻辑。所有的“pre”过滤逻辑都会被执行。然后提出代理请求。在提出代理请求之后,将运行“POST”过滤逻辑。
注意:在没有端口的路由中定义的 URI 分别获得 HTTP 和 HTTPS URI 的默认端口号 80 和 443.
断言配置
配置断言(谓词)和过滤器有两种方法:快捷方式和完全展开的参数。下面的大多数示例都使用了快捷方式。
名称和参数名称将以code的形式在每个部分的第一个或两个表示中列出。参数通常按快捷方式配置所需的顺序列出。
下面示例用两个参数定义了Cookie
路由谓词工厂,cookie 名mycookie
和要匹配mycookievalue
的值。
- 快捷方式
快捷方式配置由筛选器名称识别,后面跟着一个等号(=
),后面是用逗号分隔的参数值(,
)。
1 | spring: |
- 展开配置
完全展开的参数看起来更像是带有名称/值对的标准 YAML 配置。通常,会有name
键和args
键。args
键是用于配置谓词或筛选器的键值对的映射。
1 | spring: |
内置断言工厂
After
路由谓词工厂
After
路由谓词工厂接受一个参数,adatetime
(这是一个 JavaZonedDateTime
)。此谓词匹配在指定的 DateTime 之后发生的请求。下面的示例配置一个 after 路由谓词:
1 | predicates: |
Before
路由谓词工厂
Before
路由谓词工厂接受一个参数,adatetime
(这是一个 JavaZonedDateTime
)。此谓词匹配在指定的datetime
之前发生的请求。下面的示例配置一个 before 路由谓词:
1 | predicates: |
Between
路由谓词工厂
Between
路由谓词工厂接受两个参数,datetime1
和datetime2
,它们是 JavaZonedDateTime
对象。此谓词匹配发生在datetime1
之后和datetime2
之前的请求。datetime2
参数必须位于datetime1
之后。下面的示例配置了一个 between 路由谓词:
1 | predicates: |
Cookie
路由谓词工厂
Cookie
路由谓词工厂接受两个参数,cookiename
和regexp
(这是一个 Java 正则表达式)。此谓词匹配具有给定名称且其值与正则表达式匹配的 cookie。下面的示例配置了一个 Cookie 路由谓词工厂:
1 | predicates: |
Header
路由谓词工厂
Header
路由谓词工厂接受两个参数,header
和regexp
(这是一个 Java 正则表达式)。此谓词与具有给定名称的头匹配,其值与正则表达式匹配。下面的示例配置头路由谓词:
1 | predicates: |
Host
路由谓词工厂
Host
路由谓词工厂接受一个参数:主机名列表patterns
。该模式是一种 Ant 样式的模式,以.
作为分隔符。此谓词匹配与模式匹配的Host
头。下面的示例配置了一个主机路由谓词:
1 | predicates: |
官网说明:
URI 模板变量(如
{sub}.myhost.org
)也受到支持。如果请求具有
Host
头,其值为www.somehost.org
或beta.somehost.org
或www.anotherhost.org
,则此路由匹配。这个谓词提取 URI 模板变量(例如
sub
,在前面的示例中定义)作为名称和值的映射,并将其放在ServerWebExchange.getAttributes()
中,并在ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
中定义一个键。然后这些值可供[GatewayFilter
工厂使用](#gateway-route-filters)
Method
路由谓词工厂
Method
路由谓词工厂接受一个methods
参数,该参数是一个或多个参数:要匹配的 HTTP 方法。下面的示例配置了一个方法路由谓词:
1 | predicates: |
Path
路由谓词工厂
Path
路由谓词工厂接受两个参数: Spring PathMatcher``patterns
的列表和一个名为matchTrailingSlash
的可选标志(默认为true
)。下面的示例配置了一个路径路由谓词:
1 | predicates: |
如果请求路径是:例如:/red/1
或/red/1/
或/red/blue
或/blue/green
,则此路由匹配。如果matchTrailingSlash
(尾随斜杠)被设置为false
,那么请求路径/red/1/
将不会被匹配。这个谓词提取 URI 模板变量(例如segment
,在前面的示例中定义)作为名称和值的映射,并将其放在ServerWebExchange.getAttributes()
中,并在ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE
中定义一个键。然后这些值可供[GatewayFilter
工厂]使用(#gateway-route-filters)可以使用一种实用方法(称为get
)来使对这些变量的访问更容易。下面的示例展示了如何使用get
方法:
1 | Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange); |
Query
路由谓词工厂
Query
路由谓词工厂接受两个参数:一个必需的param
和一个可选的regexp
(这是一个 Java 正则表达式)。下面的示例配置一个查询路由谓词:
1 | predicates: |
RemoteAddr
路由谓词工厂
RemoteAddr
路由谓词工厂接受一个sources
的列表(最小大小为 1),这些字符串是 CIDR 表示法(IPv4 或 IPv6)字符串,例如192.168.0.1/16
(其中192.168.0.1
是一个 IP 地址,16
是一个子网掩码)。以下示例配置了一个 RemoteAddr 路由谓词:
1 | predicates: |
默认情况下,RemoteAddr 路由谓词工厂使用来自传入请求的远程地址。如果 Spring Cloud网关位于代理层的后面,这可能与实际的客户端 IP 地址不匹配。
你可以通过设置自定义RemoteAddressResolver
来定制远程地址的解析方式。 Spring Cloud网关带有一个基于X-forward-for 标头 (opens new window),XForwardedRemoteAddressResolver
的非默认远程地址解析器。
XForwardedRemoteAddressResolver
有两种静态构造函数方法,它们采用不同的方法实现安全性:
XForwardedRemoteAddressResolver::trustAll
返回一个RemoteAddressResolver
,它总是使用在X-Forwarded-For
头中找到的第一个 IP 地址。这种方法容易受到欺骗,因为恶意客户端可能会为X-Forwarded-For
设置初始值,该初始值将被解析器接受。XForwardedRemoteAddressResolver::maxTrustedIndex
获取一个索引,该索引与在 Spring Cloud网关前运行的受信任基础设施的数量相关。 Spring 如果云网关例如只能通过 HAProxy 访问,那么应该使用 1 的值。如果在 Spring Cloud网关可访问之前需要两个值得信赖的基础设施跳,那么应该使用 2 的值。
考虑以下标头值:
1 | X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3 |
以下maxTrustedIndex
值产生以下远程地址:
maxTrustedIndex |
结果 |
---|---|
[Integer.MIN_VALUE ,0] |
(无效,IllegalArgumentException 在初始化期间) |
1 | 0.0.0.3 |
2 | 0.0.0.2 |
3 | 0.0.0.1 |
[4, Integer.MAX_VALUE ] |
0.0.0.1 |
下面的示例展示了如何使用 Java 实现相同的配置:
gatewayconfig.java
1 | RemoteAddressResolver resolver = XForwardedRemoteAddressResolver |
Weight
路由谓词工厂
Weight
路由谓词工厂接受两个参数:group
和weight
(一个 INT)。权重是按组计算的。下面的示例配置了权重路由谓词:
1 | spring: |
此路线将把 80% 的流量转发给weighthigh.org (opens new window),并将 20% 的流量转发给weighlow.org (opens new window)。
XForwarded Remote Addr
路由谓词工厂
XForwarded Remote Addr
路由谓词工厂接受一个sources
的列表(最小大小为 1),这些字符串是 CIDR 表示法(IPv4 或 IPv6)字符串,例如192.168.0.1/16
(其中192.168.0.1
是一个 IP 地址,16
是一个子网掩码)。
此路由谓词允许基于X-Forwarded-For
HTTP 报头对请求进行过滤。
这可以用于反向代理,例如负载均衡器或 Web 应用程序防火墙,在这些代理中,只有当请求来自由这些反向代理使用的受信任的 IP 地址列表时,才允许请求。
1 | predicates: |
如果X-Forwarded-For
标头包含192.168.1.10
,则此路由匹配。
内置局部过滤器工厂
- AddRequestHeader 过滤器
1 | filters: |
对于所有匹配的请求,此清单将X-Request-red:blue
头添加到下游请求的头。
- AddRequestParameter 过滤器
1 | filters: |
将为所有匹配的请求将red=blue
添加到下游请求的查询字符串中。
- AddResponseHeader 过滤器
1 | filters: |
将为所有匹配的请求向下游响应的头添加X-Response-Red:Blue
头。
- DedupeResponseHeader 过滤器
1 | filters: |
接受一个name
参数和一个可选的strategy
参数。name
可以包含一个以空格分隔的标题名称列表。
在网关 CORS 逻辑和下游逻辑都添加响应头的情况下,这将删除重复的Access-Control-Allow-Credentials
和Access-Control-Allow-Origin
响应头的值。
DedupeResponseHeader
过滤器还接受一个可选的strategy
参数。可接受的值是RETAIN_FIRST
(默认)、RETAIN_LAST
和RETAIN_UNIQUE
。
- CircuitBreaker 断路器 过滤器
要启用 Spring Cloud电路断路器过滤器,你需要在 Classpath 上放置spring-cloud-starter-circuitbreaker-reactor-resilience4j
。以下示例配置了 Spring Cloud电路断路器GatewayFilter
:
1 | filters: |
Spring Cloud电路断路器过滤器还可以接受可选的fallbackUri
参数。目前,只支持forward:
模式 URI。如果回退被调用,请求将被转发到与 URI 匹配的控制器。下面的示例配置了这种回退:
1 | spring: |
主要的场景是使用fallbackUri
在网关应用程序中定义内部控制器或处理程序。但是,你也可以将请求重新路由到外部应用程序中的控制器或处理程序,如下所示:
1 | spring: |
在此示例中,网关应用程序中没有fallback
端点或处理程序。然而,在另一个应用程序中有一个,注册在[localhost:9994](http://localhost:9994)
下。
在请求被转发到 Fallback 的情况下, Spring Cloud Circuitbreaker 网关过滤器还提供了导致它的Throwable
。它被添加到ServerWebExchange
中,作为ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR
属性,该属性可以在处理网关应用程序内的回退时使用。
- FallbackHeaders 过滤器
FallbackHeaders
工厂允许你在转发到外部应用程序中的fallbackUri
的请求的标题中添加 Spring Cloud Circuitbreaker 执行异常详细信息,如下所示:
1 | spring: |
在此示例中,在运行断路器时发生执行异常之后,请求被转发到在localhost:9994
上运行的应用程序中的fallback
端点或处理程序。带有异常类型、消息和(如果可用的话)根原因异常类型和消息的头将由FallbackHeaders
过滤器添加到该请求中。
有关断路器和网关的更多信息,请参见Spring Cloud CircuitBreaker Factory section。
- MapRequestHeader 过滤器
接受fromHeader
和toHeader
参数。它创建一个新的命名头(toHeader
),并从传入的 HTTP 请求中从现有的命名头(fromHeader
)中提取该值。如果输入标头不存在,则过滤器不会产生任何影响。如果新的命名标头已经存在,那么它的值将被新的值扩充。以下示例配置MapRequestHeader
:
1 | routes: |
这样,当您发送一个请求时,例如:
1 | curl -H "X-Request-Id: 123456" http://localhost:8080/get |
那么转发到 http://example.org 的请求头中会包含:
1 | X-Request-Id: 123456 |
- PrefixPath 过滤器
接受一个prefix
参数。
1 | filters: |
这将在所有匹配请求的路径前缀/mypath
。因此,对/hello
的请求将被发送到/mypath/hello
。
- PreserveHostHeader 过滤器
没有参数。此筛选器设置一个请求属性,路由筛选器将检查该属性以确定是否应发送原始的主机头,而不是由 HTTP 客户机确定的主机头。以下示例配置
1 | routes: |
当请求被转发到 https://example.org 时,会保留原始的 Host 消息头。
- RequestRateLimiter 过滤器
使用RateLimiter
实现来确定是否允许当前请求继续进行。如果不是,则返回HTTP 429 - Too Many Requests
(默认情况下)的状态。
- RedirectTo 过滤器
接受两个参数,status
和url
。status
参数应该是 300 系列重定向 HTTP 代码,例如 301.url
参数应该是一个有效的 URL。这是Location
标头的值。对于相对重定向,应该使用uri: no://op
作为路由定义的 URI。
1 | routes: |
这将发送带有Location:https://acme.org
头的状态 302 来执行重定向。
- RemoveRequestHeader 过滤器
接受一个name
参数。它是要删除的标头的名称。
1 | filters: |
这将在向下游发送X-Request-Foo
头之前删除它。
- RemoveResponseHeader 过滤器
接受一个name
参数。它是要删除的标头的名称。
1 | filters: |
这将在响应返回到网关客户机之前从响应中删除X-Response-Foo
头。
- RemoveRequestParameter 过滤器
接受一个name
参数。它是要删除的查询参数的名称。
1 | filters: |
这将在向下游发送red
参数之前删除该参数。
- RewritePath 过滤器
接受一个路径regexp
参数和一个replacement
参数。这使用 Java 正则表达式以灵活的方式重写请求路径。
1 | routes: |
对于/red/blue
的请求路径,在发出下游请求之前,将路径设置为/blue
。注意,由于 YAML 规范,$
应该替换为$\
。
- RewriteLocationResponseHeader 过滤器
修改Location
响应头的值,通常是为了去掉后台特定的细节。它需要stripVersionMode
、locationHeaderName
、hostValue
和protocolsRegex
参数。
- RewriteResponseHeader 过滤器
接受name
、regexp
和replacement
参数。它使用 Java 正则表达式以一种灵活的方式重写响应头值。
1 | filters: |
对于标头值/42?user=ford&password=omg!what&flag=true
,在发出下游请求后将其设置为/42?user=ford&password=***&flag=true
。
- SaveSession 过滤器
在向下游转发之前强制执行WebSession::save
操作
1 | filters: |
- SecureHeaders 过滤器
作用是给响应的包头中添加一些安全保护的字段信息,例如 X-XSS-Protection、X-Frame-Options 等。这些字段可以防止一些常见的 Web 攻击,如跨站脚本、点击劫持等。
1 | spring: |
- SetPath 过滤器
接受一个路径template
参数。它提供了一种简单的方法,通过允许模板化的路径段来操作请求路径。这使用了 Spring Framework 中的 URI 模板。允许多个匹配段。
1 | filters: |
经过这个路由的请求都会将原始路径替换为 /foo/{segment} ,其中 {segment} 是原始路径中的第一个段。例如,如果原始路径是 /bar/baz ,那么修改后的路径就是 /foo/bar 。
- SetRequestHeader 过滤器
接受name
和value
参数。
1 | filters: |
所有经过这个具有 X-Request-Foo 请求头路由的请求都会修改 X-Request-Foo 的字段值为 Bar 。
- SetResponseHeader 过滤器
接受name
和value
参数。
1 | filters: |
所有经过这个具有 X-Request-Foo 响应头路由的请求都会修改 X-Request-Foo 的字段值为 Bar 。
- SetStatus 过滤器
只接受一个参数,status
。它必须是有效的 Spring HttpStatus
。它可以是整数值404
,也可以是枚举的字符串表示:NOT_FOUND
。
1 | routes: |
这两种情况下,响应的 HTTP 状态都设置为 401.
- StripPrefix 过滤器
接受一个参数,parts
。parts
参数指示在向下游发送请求之前要从请求中剥离的路径中的部件数量。
1 | filters: |
所有经过这个路由的请求都会去掉第一个前缀。例如,如果原始路径是 /api/goods ,那么修改后的路径就是 /goods 。如果要去掉多个前缀,可以修改 parts 的值为相应的数字
- Retry 过滤器
工厂支持以下参数:
retries:应该尝试的重试次数。
statuses:应该重试的 HTTP 状态代码,用org.springframework.http.HttpStatus表示。
methods:应该重试的 HTTP 方法,用org.springframework.http.HttpMethod表示。
series:要重试的一系列状态代码,用org.springframework.http.HttpStatus.Series表示。
exceptions:应该重试的抛出的异常列表。
backoff:为重试配置的指数退避。在回退间隔firstBackoff * (factor ^ n)之后执行重试,其中n是迭代。如果配置了maxBackoff,则应用的最大退避限制为maxBackoff。如果basedOnPreviousValue为真,则按prevBackoff * factor计算退避。
如果启用,以下默认值将配置为Retry过滤器:
retries:三次
series:5XX 系列
methods:get 方法
exceptions:IOException和TimeoutException
backoff:禁用
下面的清单配置了重试GatewayFilter:
1 | routes: |
- RequestSize 过滤器
可以限制请求到达下游服务。过滤器接受maxSize
参数。maxSize
是DataSize
类型,因此值可以定义为一个数字,后面跟着一个可选的DataUnit
后缀,例如“KB”或“MB”。对于字节,默认值为“B”。它是以字节为单位定义的请求的允许大小限制。
1 | routes: |
当请求由于大小而被拒绝时,RequestSize
工厂将响应状态设置为413 Payload Too Large
,并带有一个附加的头errorMessage
。下面的示例显示了这样的errorMessage
:
1 | errorMessage : Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB |
- SetRequestHostHeader 过滤器
可以用指定的 vaue 替换现有的主机报头。过滤器接受host
参数。
1 | routes: |
将主机报头的值替换为example.org
。
- ModifyRequestBody 过滤器
过滤器来修改请求主体,然后再由网关向下游发送。(只能通过java代码来实现)
1 |
|
- ModifyResponseBody
修改响应主体,然后再将其发送回客户机。
1 |
|
- TokenRelay 过滤器
令牌中继是 OAuth2 使用者充当客户端并将传入的令牌转发给传出的资源请求的一种方式。使用者可以是纯客户机(如 SSO 应用程序)或资源服务器。
用法:Spring Cloud 网关 | 中文文档 (gitcode.net)
- CacheRequestBody 过滤器
由于请求体流只能被读取一次,所以需要对请求体流进行缓存。你可以使用CacheRequestBody
过滤器来缓存请求主体,然后再将其发送到下游,并从 exchagne 属性获取主体。
- default-filters 默认过滤器
要添加筛选器并将其应用到所有路由,你可以使用spring.cloud.gateway.default-filters
。此属性接受一个过滤器列表。以下清单定义了一组默认筛选器:
1 | spring: |
链路追踪 Sleuth
概念
SpringCloud Sleuth主要功能就是在分布式系统中提供追踪解决方案。它大量借用了Google Dapper的 设计, 先来了解一下Sleuth中的术语和相关概念。
- Trace:由一组Trace Id相同的Span串联形成一个树状结构。为了实现请求跟踪,当请求到达分布式系统 的入口端点时,只需要服务跟踪框架为该请求创建一个唯一的标识(即TraceId),同时在分布式系统 内部流转的时候,框架始终保持传递该唯一值,直到整个请求的返回。那么我们就可以使用该唯一标识 将所有的请求串联起来,形成一条完整的请求链路。
- Span 代表了一组基本的工作单元。为了统计各处理单元的延迟,当请求到达各个服务组件的时候,也 通过一个唯一标识(SpanId)来标记它的开始、具体过程和结束。通过SpanId的开始和结束时间戳, 就能统计该span的调用时间,除此之外,我们还可以获取如事件的名称。请求信息等元数据。
- Annotation 用它记录一段时间内的事件,内部使用的重要注释:
- cs(Client Send)客户端发出请求,开始一个请求的生命
- sr(Server Received)服务端接受到请求开始进行处理,
- sr-cs = 网络延迟(服务调用的时间)
- ss(Server Send)服务端处理完毕准备发送到客户端,
- ss - sr = 服务器上的请求处理时间 cr(Client Reveived)客户端接受到服务端的响应,请求结束。
- cr - sr = 请求的总时间
入门
引入依赖:
1 | <dependencies> |
请求接口:
日志中的:
1 | [client,6e0f2cee8a3d0e72,6e0f2cee8a3d0e72,false] |
分别代表:微服务名称, traceId, spanid,是否将链路的追踪结果输出到第三方平台
集成ZipKin
介绍:
Zipkin 是 Twitter 的一个开源项目,它基于Google Dapper实现,它致力于收集服务的定时数据,以解 决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。 我们可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的REST API接口来辅助我们查 询跟踪数据以实现对分布式系统的监控程序,从而及时地发现系统中出现的延迟升高问题并找出系统性 能瓶颈的根源。 除了面向开发的 API 接口之外,它也提供了方便的UI组件来帮助我们直观的搜索跟踪信息和分析请求链 路明细,比如:可以查询某段时间内各用户请求的处理时间等。 Zipkin 提供了可插拔数据存储方式:In-Memory、MySql、Cassandra 以及 Elasticsearch。
上图展示了 Zipkin 的基础架构,它主要由 4 个核心组件构成:
Collector:收集器组件,它主要用于处理从外部系统发送过来的跟踪信息,将这些信息转换为 Zipkin内部处理的 Span 格式,以支持后续的存储、分析、展示等功能。
Storage:存储组件,它主要对处理收集器接收到的跟踪信息,默认会将这些信息存储在内存中, 我们也可以修改此存储策略,通过使用其他存储组件将跟踪信息存储到数据库中。
RESTful API:API 组件,它主要用来提供外部访问接口。比如给客户端展示跟踪信息,或是外接 系统访问以实现监控等。
Web UI:UI 组件, 基于API组件实现的上层应用。通过UI组件用户可以方便而有直观地查询和分 析跟踪信息。
Zipkin分为两端,一个是 Zipkin服务端,一个是 Zipkin客户端,客户端也就是微服务的应用。 客户端会 配置服务端的 URL 地址,一旦发生服务间的调用的时候,会被配置在微服务里面的 Sleuth 的监听器监 听,并生成相应的 Trace 和 Span 信息发送给服务端。
下载 jar 包。gitHub 地址:https://github.com/openzipkin/zipkin
启动:
1 | PS D:\Zipkin> java -jar .\zipkin-server-2.21.1-exec.jar |
访问Zipkin服务端:http://localhost:9411/zipkin/
项目作为客户端添加以下配置:
pom:
1 | <dependency> |
application.ym
1 | spring: |
访问接口:http://localhost:8080/client/order/101
后点击查询
ZipKin 数据持久化
存储在 mysql :
创建表:sql
1 | -- 来源:https://github.com/openzipkin/zipkin/blob/master/zipkin-storage/mysql-v1/src/main/resources/mysql.sql |
启动 ZipKin 指定 mysql 数据库参数:
1 | java -jar zipkin-server-2.21.1-exec.jar |
测试:
发送接口请求,再查看数据库。发现数据已存入数据库了
存储在 ElarsticSearch :
es官网:https://www.elastic.co/cn/start
下载解压后 执行/bin/elasticsearch.bat 后浏览器访问:http://localhost:9200/
显示类似如下信息标识启动成功:
1 | { |
启动 ZipKin 加上 es 的存储参数
1 | java -jar zipkin-server-2.21.1-exec.jar --STORAGE_TYPE=elasticsearch --ESHOST=localhost:9200 |
如果想要通使用 ELK 来检索日志,那么可以看我另外一篇文章:spring cloud 集成 Sleuth
消息驱动 RocketMQ
RocketMQ官网:https://rocketmq.apache.org/zh/docs/quickStart/01quickstart
快速开始
1 | #下载包 |
sdk 测试消息收发
项目 pom 文件添加依赖:
1 | <dependency> |
通过mqadmin创建 Topic。
1 | sh bin/mqadmin updatetopic -n localhost:9876 -t TestTopic -c DefaultCluster |
生产者测试代码
1 | package com.wu.example; |
执行 main 方法,控制台提示发送成功!
1 | 18:47:18.933 [main] INFO com.wu.example.ProducerExample - Send message successfully, messageId=01005056C0000863800410C73600000000 |
消费者测试代码:
1 | package com.wu.example; |
执行 main 方法,控制台日志提示 消费成功!
1 | 18:47:52.089 [RocketmqMessageConsumption-0-24] INFO com.wu.example.PushConsumerExample - Consume message successfully, messageId=01005056C0000863800410C73600000000 |
note:
1 | #关闭服务 |
结合spring boot
pom 依赖:
1 | <!-- https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-spring-boot-starter --> |
配置 application.yml 改成自己的(不配置 会导致 RocketMQTemplate 注入失败)
1 | rocketmq: |
发送消息
1 | // Send string |
idea 控制台:
1 | syncSend1 to topic string-topic sendResult=SendResult [sendStatus=SEND_OK, msgId=7F00000107C818B4AAC20957F3640006, offsetMsgId=C0A8CD8900002A9F0000000000001482, messageQueue=MessageQueue [topic=string-topic, brokerName=broker-a, queueId=1], queueOffset=0] |
rocketMQ-dashboard
1 | /** https://rocketmq.apache.org/zh/docs/deploymentOperations/04Dashboard */ |
访问:http://192.168.205.137:6060/#/ (根据自己的启动端口访问,记得关闭防火墙或开发端口)
接收消息:
1 | package com.wu.consumer; |
控制台输出:
发送对象并接受回调 生产者
1 | rocketMQTemplate.sendAndReceive(objectRequestTopic, new Order().setOid(1666L).setUid(434233).setUsername("name-dfg"), new RocketMQLocalRequestCallback<Order>() { |
消费者:
1 | import com.wu.entry.Order; |
更多调用示例代码请参考官方示例:apache/rocketmq-spring: Apache RocketMQ Spring Integration (github.com)
或点这里下载示例代码:https://github.com/apache/rocketmq-spring/archive/refs/heads/master.zip
note:
1 | [root@localhost rocketmq-all-5.1.0-bin-release]# sh bin/mqnamesrv |
短信服务
这里以腾讯云短信为例(为什么不用阿里云,因为腾讯云可以购买100条的小套餐,便宜!)
首先登录腾讯云找到短信(服务)平台:https://console.cloud.tencent.com/smsv2/csms-sign
申请短信签名和模板(前提是需要完成个人认证或企业认证)
代码示例控制台:https://console.cloud.tencent.com/api/explorer?Product=sms&Version=2021-01-11&Action=SendSms
在这里可以测试发送短信,并且能自动生成代码示例。依赖信息也可以在这里找到。参数不知道怎么填的,点击参数旁边的*号可以跳转到对应的指引页面(配置)获取参数
自己用代码在本地运行的时候需要 secretID 和 secretKey 这两个东西在这里可以去创建
本地运行结果:
简单说两句:
短信服务功能很简单,不管是 阿里云,腾讯云,sendCloud 或者其他服务商基本都要经过这么几个步骤
1、注册账号并通过认证(个人认证或企业认证)
2、申请短信签名。即发送短信的主体(一般显示在短信的开头用中括号括起来的比如:【腾讯云】【阿里云】【xx小店】)
3、申请短信模板。(一般这里会区分 验证码类短信或通知类短信)
然后就是参考官方给出的文档了。写的好的 一般只要把代码复制下来然后替换掉自己的参数就可以了。
另外我在本地运行的时候出了小问题:
1 | D:\Java\jdk1.8.0_144\bin\java.exe "-javaagent:D:\Program Files\ideaIU-2019.2win\lib\idea_rt.jar=5418:D:\Program Files\ideaIU-2019.2win\bin" -Dfile.encoding=UTF-8 -classpath "D:\Java\jdk1.8.0_144\jre\lib\charsets.jar;D:\Java\jdk1.8.0_144\jre\lib\deploy.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;D:\Java\jdk1.8.0_144\jre\lib\javaws.jar;D:\Java\jdk1.8.0_144\jre\lib\jce.jar;D:\Java\jdk1.8.0_144\jre\lib\jfr.jar;D:\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;D:\Java\jdk1.8.0_144\jre\lib\jsse.jar;D:\Java\jdk1.8.0_144\jre\lib\management-agent.jar;D:\Java\jdk1.8.0_144\jre\lib\plugin.jar;D:\Java\jdk1.8.0_144\jre\lib\resources.jar;D:\Java\jdk1.8.0_144\jre\lib\rt.jar;D:\IdeaProjects\studyalibaba\client\target\classes;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\boot\spring-boot-starter\2.1.3.RELEASE\spring-boot-starter-2.1.3.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\boot\spring-boot\2.1.3.RELEASE\spring-boot-2.1.3.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\spring-context\5.1.5.RELEASE\spring-context-5.1.5.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\boot\spring-boot-autoconfigure\2.1.3.RELEASE\spring-boot-autoconfigure-2.1.3.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\boot\spring-boot-starter-logging\2.1.3.RELEASE\spring-boot-starter-logging-2.1.3.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\Program Files\apache-maven-3.5.3\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\Program Files\apache-maven-3.5.3\repository\org\apache\logging\log4j\log4j-to-slf4j\2.11.2\log4j-to-slf4j-2.11.2.jar;D:\Program Files\apache-maven-3.5.3\repository\org\apache\logging\log4j\log4j-api\2.11.2\log4j-api-2.11.2.jar;D:\Program Files\apache-maven-3.5.3\repository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;D:\Program Files\apache-maven-3.5.3\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\spring-core\5.1.5.RELEASE\spring-core-5.1.5.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\spring-jcl\5.1.5.RELEASE\spring-jcl-5.1.5.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;D:\Program Files\apache-maven-3.5.3\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;D:\Program Files\apache-maven-3.5.3\repository\javax\xml\bind\jaxb-api\2.3.1\jaxb-api-2.3.1.jar;D:\Program Files\apache-maven-3.5.3\repository\javax\activation\javax.activation-api\1.2.0\javax.activation-api-1.2.0.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\boot\spring-boot-starter-web\2.1.3.RELEASE\spring-boot-starter-web-2.1.3.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\boot\spring-boot-starter-json\2.1.3.RELEASE\spring-boot-starter-json-2.1.3.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\com\fasterxml\jackson\core\jackson-databind\2.9.8\jackson-databind-2.9.8.jar;D:\Program Files\apache-maven-3.5.3\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;D:\Program Files\apache-maven-3.5.3\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.8\jackson-datatype-jdk8-2.9.8.jar;D:\Program Files\apache-maven-3.5.3\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.8\jackson-datatype-jsr310-2.9.8.jar;D:\Program Files\apache-maven-3.5.3\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.8\jackson-module-parameter-names-2.9.8.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\boot\spring-boot-starter-tomcat\2.1.3.RELEASE\spring-boot-starter-tomcat-2.1.3.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.16\tomcat-embed-core-9.0.16.jar;D:\Program Files\apache-maven-3.5.3\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.16\tomcat-embed-el-9.0.16.jar;D:\Program Files\apache-maven-3.5.3\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.16\tomcat-embed-websocket-9.0.16.jar;D:\Program Files\apache-maven-3.5.3\repository\org\hibernate\validator\hibernate-validator\6.0.14.Final\hibernate-validator-6.0.14.Final.jar;D:\Program Files\apache-maven-3.5.3\repository\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;D:\Program Files\apache-maven-3.5.3\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;D:\Program Files\apache-maven-3.5.3\repository\com\fasterxml\classmate\1.4.0\classmate-1.4.0.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\spring-web\5.1.5.RELEASE\spring-web-5.1.5.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\spring-beans\5.1.5.RELEASE\spring-beans-5.1.5.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\spring-webmvc\5.1.5.RELEASE\spring-webmvc-5.1.5.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\spring-aop\5.1.5.RELEASE\spring-aop-5.1.5.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\spring-expression\5.1.5.RELEASE\spring-expression-5.1.5.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\com\alibaba\cloud\spring-cloud-starter-alibaba-nacos-discovery\2.1.4.RELEASE\spring-cloud-starter-alibaba-nacos-discovery-2.1.4.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\com\alibaba\cloud\spring-cloud-alibaba-commons\2.1.4.RELEASE\spring-cloud-alibaba-commons-2.1.4.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\com\alibaba\nacos\nacos-client\1.4.1\nacos-client-1.4.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\alibaba\nacos\nacos-common\1.4.1\nacos-common-1.4.1.jar;D:\Program Files\apache-maven-3.5.3\repository\commons-io\commons-io\2.2\commons-io-2.2.jar;D:\Program Files\apache-maven-3.5.3\repository\org\apache\httpcomponents\httpasyncclient\4.1.4\httpasyncclient-4.1.4.jar;D:\Program Files\apache-maven-3.5.3\repository\org\apache\httpcomponents\httpcore\4.4.11\httpcore-4.4.11.jar;D:\Program Files\apache-maven-3.5.3\repository\org\apache\httpcomponents\httpcore-nio\4.4.11\httpcore-nio-4.4.11.jar;D:\Program Files\apache-maven-3.5.3\repository\com\alibaba\nacos\nacos-api\1.4.1\nacos-api-1.4.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\google\guava\guava\24.1.1-jre\guava-24.1.1-jre.jar;D:\Program Files\apache-maven-3.5.3\repository\com\google\code\findbugs\jsr305\1.3.9\jsr305-1.3.9.jar;D:\Program Files\apache-maven-3.5.3\repository\org\checkerframework\checker-compat-qual\2.0.0\checker-compat-qual-2.0.0.jar;D:\Program Files\apache-maven-3.5.3\repository\com\google\errorprone\error_prone_annotations\2.1.3\error_prone_annotations-2.1.3.jar;D:\Program Files\apache-maven-3.5.3\repository\com\google\j2objc\j2objc-annotations\1.1\j2objc-annotations-1.1.jar;D:\Program Files\apache-maven-3.5.3\repository\org\codehaus\mojo\animal-sniffer-annotations\1.14\animal-sniffer-annotations-1.14.jar;D:\Program Files\apache-maven-3.5.3\repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;D:\Program Files\apache-maven-3.5.3\repository\com\fasterxml\jackson\core\jackson-core\2.9.8\jackson-core-2.9.8.jar;D:\Program Files\apache-maven-3.5.3\repository\io\prometheus\simpleclient\0.5.0\simpleclient-0.5.0.jar;D:\Program Files\apache-maven-3.5.3\repository\com\alibaba\spring\spring-context-support\1.0.10\spring-context-support-1.0.10.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\cloud\spring-cloud-commons\2.1.0.RELEASE\spring-cloud-commons-2.1.0.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\security\spring-security-crypto\5.1.4.RELEASE\spring-security-crypto-5.1.4.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\cloud\spring-cloud-context\2.1.0.RELEASE\spring-cloud-context-2.1.0.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\cloud\spring-cloud-starter-netflix-ribbon\2.1.0.RELEASE\spring-cloud-starter-netflix-ribbon-2.1.0.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\cloud\spring-cloud-starter\2.1.0.RELEASE\spring-cloud-starter-2.1.0.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\security\spring-security-rsa\1.0.7.RELEASE\spring-security-rsa-1.0.7.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\bouncycastle\bcpkix-jdk15on\1.60\bcpkix-jdk15on-1.60.jar;D:\Program Files\apache-maven-3.5.3\repository\org\bouncycastle\bcprov-jdk15on\1.60\bcprov-jdk15on-1.60.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\cloud\spring-cloud-netflix-ribbon\2.1.0.RELEASE\spring-cloud-netflix-ribbon-2.1.0.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\cloud\spring-cloud-netflix-archaius\2.1.0.RELEASE\spring-cloud-netflix-archaius-2.1.0.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\cloud\spring-cloud-starter-netflix-archaius\2.1.0.RELEASE\spring-cloud-starter-netflix-archaius-2.1.0.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\com\netflix\archaius\archaius-core\0.7.6\archaius-core-0.7.6.jar;D:\Program Files\apache-maven-3.5.3\repository\commons-configuration\commons-configuration\1.8\commons-configuration-1.8.jar;D:\Program Files\apache-maven-3.5.3\repository\com\netflix\ribbon\ribbon\2.3.0\ribbon-2.3.0.jar;D:\Program Files\apache-maven-3.5.3\repository\com\netflix\ribbon\ribbon-transport\2.3.0\ribbon-transport-2.3.0.jar;D:\Program Files\apache-maven-3.5.3\repository\io\reactivex\rxnetty-contexts\0.4.9\rxnetty-contexts-0.4.9.jar;D:\Program Files\apache-maven-3.5.3\repository\io\reactivex\rxnetty-servo\0.4.9\rxnetty-servo-0.4.9.jar;D:\Program Files\apache-maven-3.5.3\repository\com\netflix\hystrix\hystrix-core\1.5.18\hystrix-core-1.5.18.jar;D:\Program Files\apache-maven-3.5.3\repository\org\hdrhistogram\HdrHistogram\2.1.9\HdrHistogram-2.1.9.jar;D:\Program Files\apache-maven-3.5.3\repository\javax\inject\javax.inject\1\javax.inject-1.jar;D:\Program Files\apache-maven-3.5.3\repository\io\reactivex\rxnetty\0.4.9\rxnetty-0.4.9.jar;D:\Program Files\apache-maven-3.5.3\repository\com\netflix\ribbon\ribbon-core\2.3.0\ribbon-core-2.3.0.jar;D:\Program Files\apache-maven-3.5.3\repository\commons-lang\commons-lang\2.6\commons-lang-2.6.jar;D:\Program Files\apache-maven-3.5.3\repository\com\netflix\ribbon\ribbon-httpclient\2.3.0\ribbon-httpclient-2.3.0.jar;D:\Program Files\apache-maven-3.5.3\repository\commons-collections\commons-collections\3.2.2\commons-collections-3.2.2.jar;D:\Program Files\apache-maven-3.5.3\repository\org\apache\httpcomponents\httpclient\4.5.7\httpclient-4.5.7.jar;D:\Program Files\apache-maven-3.5.3\repository\com\sun\jersey\jersey-client\1.19.1\jersey-client-1.19.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\sun\jersey\jersey-core\1.19.1\jersey-core-1.19.1.jar;D:\Program Files\apache-maven-3.5.3\repository\javax\ws\rs\jsr311-api\1.1.1\jsr311-api-1.1.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\sun\jersey\contribs\jersey-apache-client4\1.19.1\jersey-apache-client4-1.19.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\netflix\servo\servo-core\0.12.21\servo-core-0.12.21.jar;D:\Program Files\apache-maven-3.5.3\repository\com\netflix\netflix-commons\netflix-commons-util\0.3.0\netflix-commons-util-0.3.0.jar;D:\Program Files\apache-maven-3.5.3\repository\com\netflix\ribbon\ribbon-loadbalancer\2.3.0\ribbon-loadbalancer-2.3.0.jar;D:\Program Files\apache-maven-3.5.3\repository\com\netflix\netflix-commons\netflix-statistics\0.1.1\netflix-statistics-0.1.1.jar;D:\Program Files\apache-maven-3.5.3\repository\io\reactivex\rxjava\1.3.8\rxjava-1.3.8.jar;D:\IdeaProjects\studyalibaba\commen\target\classes;D:\Program Files\apache-maven-3.5.3\repository\com\baomidou\mybatis-plus-boot-starter\3.5.3.1\mybatis-plus-boot-starter-3.5.3.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\baomidou\mybatis-plus\3.5.3.1\mybatis-plus-3.5.3.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\baomidou\mybatis-plus-extension\3.5.3.1\mybatis-plus-extension-3.5.3.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\baomidou\mybatis-plus-core\3.5.3.1\mybatis-plus-core-3.5.3.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\baomidou\mybatis-plus-annotation\3.5.3.1\mybatis-plus-annotation-3.5.3.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\github\jsqlparser\jsqlparser\4.4\jsqlparser-4.4.jar;D:\Program Files\apache-maven-3.5.3\repository\org\mybatis\mybatis\3.5.10\mybatis-3.5.10.jar;D:\Program Files\apache-maven-3.5.3\repository\org\mybatis\mybatis-spring\2.0.7\mybatis-spring-2.0.7.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\boot\spring-boot-starter-jdbc\2.1.3.RELEASE\spring-boot-starter-jdbc-2.1.3.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\com\zaxxer\HikariCP\3.2.0\HikariCP-3.2.0.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\spring-jdbc\5.1.5.RELEASE\spring-jdbc-5.1.5.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\org\springframework\spring-tx\5.1.5.RELEASE\spring-tx-5.1.5.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\mysql\mysql-connector-java\8.0.15\mysql-connector-java-8.0.15.jar;D:\Program Files\apache-maven-3.5.3\repository\com\alibaba\cloud\spring-cloud-starter-alibaba-nacos-config\2.1.4.RELEASE\spring-cloud-starter-alibaba-nacos-config-2.1.4.RELEASE.jar;D:\Program Files\apache-maven-3.5.3\repository\com\tencentcloudapi\tencentcloud-sdk-java-sms\3.1.706\tencentcloud-sdk-java-sms-3.1.706.jar;D:\Program Files\apache-maven-3.5.3\repository\com\tencentcloudapi\tencentcloud-sdk-java-common\3.1.706\tencentcloud-sdk-java-common-3.1.706.jar;D:\Program Files\apache-maven-3.5.3\repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;D:\Program Files\apache-maven-3.5.3\repository\com\squareup\okio\okio\3.2.0\okio-3.2.0.jar;D:\Program Files\apache-maven-3.5.3\repository\com\squareup\okio\okio-jvm\3.2.0\okio-jvm-3.2.0.jar;D:\Program Files\apache-maven-3.5.3\repository\org\jetbrains\kotlin\kotlin-stdlib-jdk8\1.2.71\kotlin-stdlib-jdk8-1.2.71.jar;D:\Program Files\apache-maven-3.5.3\repository\org\jetbrains\kotlin\kotlin-stdlib\1.2.71\kotlin-stdlib-1.2.71.jar;D:\Program Files\apache-maven-3.5.3\repository\org\jetbrains\annotations\13.0\annotations-13.0.jar;D:\Program Files\apache-maven-3.5.3\repository\org\jetbrains\kotlin\kotlin-stdlib-jdk7\1.2.71\kotlin-stdlib-jdk7-1.2.71.jar;D:\Program Files\apache-maven-3.5.3\repository\org\jetbrains\kotlin\kotlin-stdlib-common\1.6.20\kotlin-stdlib-common-1.6.20.jar;D:\Program Files\apache-maven-3.5.3\repository\com\squareup\okhttp3\okhttp\3.8.1\okhttp-3.8.1.jar;D:\Program Files\apache-maven-3.5.3\repository\com\google\code\gson\gson\2.8.5\gson-2.8.5.jar;D:\Program Files\apache-maven-3.5.3\repository\com\squareup\okhttp3\logging-interceptor\3.8.1\logging-interceptor-3.8.1.jar;D:\Program Files\apache-maven-3.5.3\repository\org\ini4j\ini4j\0.5.4\ini4j-0.5.4.jar" com.wu.sendmsg.SendSms |
之后在 pom 文件中加了个 kotlin 依赖就好了
1 | <!-- https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib --> |
配置中心 nacos
配置中心的介绍就不多说了,常用的配置中心有 apollo (阿波罗) , springcloud config , consul 和 nacos 等。关于 apollo 大家可以看我另外一篇文章 “配置中心之Apollo”。这里我们来学习下 nacos 配置中心。
快速体验
配置文件优先级(由高到低): bootstrap.properties -> bootstrap.yml -> application.properties -> application.yml
项目中新建 bootstrap.yml 配置文件
1 | spring: |
在 nacos 新建配置:
1 | server: |
注意 data ID 与 项目中 bootstrap.yml 内容的关系
pom 添加配置:
1 | <dependency> |
启动项目:看到类似以下日志表示成功!
1 | 2023-03-03 14:31:37.200 INFO 24892 --- [ main] com.wu.Client : Started Client in 6.817 seconds (JVM running for 7.813) |
动态获取配置
1 | /** |
配置的继承与聚合
继承:
1 | # nacos 中新建如下配置 |
聚合:
在 bootstrap.yml 中配置
1 | spring: |
聚合可以把不同 group 的配置引入到当前配置。可以配置多个。这种组合只能在 bootstrap.yml 中配置。
命名空间(Namespace)
命名空间可用于进行不同环境的配置隔离。一般一个环境划分到一个命名空间
配置分组(Group)
配置分组用于将不同的服务可以归类到同一分组。一般将一个项目的配置分到一组
配置集(Data ID)
在系统中,一个配置文件通常就是一个配置集。一般微服务的配置就是一个配置集
分布式事务 seata
官网文档:Seata 是什么
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
下载网站:Releases · seata/seata (github.com)
代码来源:seata-samples/springcloud-nacos-seata at master · seata/seata-samples (github.com)
我下的 seate 版本是 1.6.1 :Release v1.6.1 · seata/seata (github.com)
springboot 版本是:2.1.3.RELEASE
springcloud 版本是:Greenwich.RELEASE
spring cloud alibaba 版本是:2.1.4.RELEASE
这里有三个文件说明下:
- nacos-config.txt
这个文件是自己建立的内容如下:
1 | service.vgroupMapping.stock-service-group=default |
- registry.config
这个文件也是自己建立的内容如下:(可从示例代码 resources 下复制过来改下)
1 | registry { |
- config.txt
这个文件原本就有的,里边列出了所有配置项。当我执行初始化脚本发现初始化失败!看到这里边的配置说明我明白了我出现的一些错误是和我没关系的。有些是可以注释掉的
另外执行初始化配置脚本的位置在 /seata/script/config-center/nacos 下面 (如果你是其他的配置中心 那么就在对应的地方)
可通过 git bash 到这里执行
1 | $ sh nacos-config.sh 127.0.0.1 |
初始化配置
1 | wuzhiyong@DESKTOP-SAUA6M1 MINGW64 /d/IdeaProjects/studyalibaba/seata/script/config-center/nacos |
我的配置是 type = nacos 的所以初始化后可以在 nacos 看到很多参数:
开始
启动 seata
1 | # 切换到 /seata/bin 路径 |
启动项目(stock-service,order-service)
注意数据库 url 加上时间参数。(示例代码的配置里是没有加的)
1 | spring.datasource.url=jdbc:mysql://localhost:3306/seata_stock?allowMultiQueries=true&serverTimezone=UTC |
测试
部分代码:
OrderController:
1 |
|
OrderService:
1 |
|
StockController:
1 | /** |
StockService:
1 | /** |
通过请求接口和查询数据库,可发现异常的都回滚了,回滚的数据可从不连续的自增id中得到验证。