SpringCloud Alibaba 笔记
Nacos注册中心
下载nacos 最新稳定版本 https://github.com/alibaba/nacos/releases
-
目前最新文档的版本是2.1.0
-
下载完成后修改默认集群模式
-
修改为standalone单机模式
-
启动nacos,访问页面http://localhost:8848/nacos/index.html
网关
执行过程
1.Gateway的客户端回向Spring Cloud Gateway发起请求,请求首先会被HttpWebHandlerAdapter进行提取组装成网关的上下文
2.网关的上下文会传递到DispatcherHandler。DispatcherHandler是所有请求的分发处理器,DispatcherHandler主要负责分发请求对应的处理器,
3.将请求分发到对应RoutePredicateHandlerMapping(路由断言处理器映射器)。路由断言处理映射器主要用于路由的查找,以及找到路由后返回对应的FilteringWebHandler。FilteringWebHandler主要负责组装Filter链表并调用Filter执行一系列Filter处理,然后把请求转到后端对应的代理服务处理
4.处理完毕后,将Response返回到Gateway客户端
配置
- id:路由的ID 唯一 默认UUID
- uri:匹配后提供服务的路由地址,使用服务名实现负载均衡
- predicates:断言: 路由转发满足的条件
- filters:在路由前对请求的地址进行额外的其他操作
server:
port: 9030
spring:
application:
name: gateway
cloud:
nacos:
discovery:
# 服务注册
server-addr: localhost:8848
config:
# 暂不使用配置中心
enabled: false
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
routes: # 路由数组: 即请求满足什么条件跳转到哪些服务
- id: service # 路由的ID 没有固定规则但要求唯一,建议配合服务名
order: 1 # 用于多个route的排序,值越小,优先级越高
uri: lb://service-01 # 匹配后提供服务的路由地址,使用服务名实现负载均衡
#uri: http://localhost:9001/ #匹配后提供服务的路由地址
predicates: # 断言: 路由转发满足的条件
- Path=/yung/** # 当请求路径满足Path时,才进行转发
filters:
- StripPrefix=1 # 在请求转发之前,去除最前面1层路径,这里去除掉yung
断言
- 指定Cookie正则匹配指定值
predicates:
- Cookie=cookie,china
- 指定Header正则匹配指定值,内容必须是数字
predicates:
- Header=X-Request-Id,\d+
# - Header=X-Request-Id
- 请求Host匹配指定值
predicates:
- Host=**.somehost.org,**.anotherhost.org
- 请求Method匹配指定请求方式
predicates:
- Method=GET,POST
- 请求包含某参数
predicates:
- Query=green
#请求包含某参数并且参数值匹配正则表达式
# - Query=red, gree.
- Path:路径相匹配的进行路由
predicates:
- Path=/system/**
- 权重
predicates:
- Weight=group1, 2
- 时间
predicates:
- After=2021-02-23T14:20:00.000+08:00[Asia/Shanghai]
#- Before=2021-02-23T14:20:00.000+08:00[Asia/Shanghai]
#- Between=2021-02-23T14:20:00.000+08:00[Asia/Shanghai]
自定义断言
- 继承`AbstractRoutePredicateFactory<ExtCheckRoutePredicateFactory.Config>`` 且 类名以XXXRoutePredicateFactory结尾
@Component
public class ExtCheckRoutePredicateFactory extends AbstractRoutePredicateFactory<ExtCheckRoutePredicateFactory.Config> {
public ExtCheckRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
String url=serverWebExchange.getRequest().getURI().toString();
if(url.endsWith(".html")){
return true;
}
return false;
}
};
}
public static class Config{
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
}
}
配置
gateway:
routes:
- id: abc
uri: http://localhost:8080
predicates:
- name: ExtCheck
过滤器
局部过滤器
局部过滤器:针对某一个或一个分组的路由上
routes:
filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
- StripPrefix=1 # 转发之前去掉1层路径
- SetStatus=250 # 修改原始响应的状态码
过滤器工厂 | 作用 | 参数 |
---|---|---|
AddRequestHeader | 为原始请求添加Header | Header的名称及值 |
AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
AddResponseHeader | 为原始响应添加Header | Header的名称及值 |
DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名称及去重策略 |
Hystrix | 为路由引入Hystrix的断路器保护 | Hystrixcommand的名称 |
FallbackHeaders | 为fallbackUri的请求头中添加具体的异常信息 | Header的名称 |
PrefixPath | 为原始请求路径添加前缀 | 前缀路径 |
PreserveHostHeader | 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host | 无 |
RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
RedirectTo | 将原始请求重定向到指定的URL | http状态码及重定向的url |
RemoveHopByHopHeadersFilter | 为原始请求删除IETF组织规定的一系列Header | Header名称 |
RemoveResponseHeader | 为原始请求删除某个Header | Header的名称 |
RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式 |
RewriteResponseHeader | 重写原始响应中的某个Header | Header名称,值的正则表达式,重写后的值 |
SaveSession | 在转发请求之前,强制执行websession::save操作 | 无 |
secureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 |
SetPath | 修改原始的请求路径 | 修改后的路径 |
SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 |
SetStatus | 修改原始响应的状态码 | HTTP状态码,可以是数字,也可以是字符串 |
StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 |
Retry | 针对不同的响应进行重试 | retries、statuses、methods、 series |
RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返413Payload Too Large | 请求包大小,单位为字节,默认值为5M |
ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容 |
ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 |
全局过滤器
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,可以实现登录状态判断、权限校验、请求限流等
自定义过滤器
- 实现GlobalFilter接口
public interface GlobalFilter {
/**
* 处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
*
* @param exchange 请求上下文,里面可以获取Request、Response等信息
* @param chain 用来把请求委托给下一个过滤器
* @return {@code Mono<Void>} 返回标示当前过滤器业务结束
*/
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
- Order接口实现优先级
每一个过滤器都必须指定一个int类型的order值,order值越小,过滤器优先级越高,执行顺序越靠前
默认优先级是按照声明顺序从1递增
@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {
}
实现一个认证全局过滤器
@Component
@Slf4j
public class GatewayUserFilter implements GlobalFilter, Ordered {
private String jwtToken = AmsConstants.JWT_TOKEN;
@Override
public Mono< Void > filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String token = request.getQueryParams().getFirst("token");
if (!token.equals("admin")) {
// 认证失败
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 放行
return chain.filter(responseBuild);
}
}
/**
* 加载过滤器的顺序,数字越小,优先级越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
OpenFeign
openFeign是SpringCloud在Feign的基础上支持了Spring MVC的注解,并通过动态代理的方式产生实现类来做负载均衡并进行调用其他服务
使用
-
使用注解@FeignClient注册一个消费者接口,如
@FeignClient(name = "service-01")
-
消费者启动类添加
@EnableFeignClients
超时时间
- 默认超时时间为
1s
,修改配置文件:
# 设置Feign客户端超时时间(OpenFeign默认支持Ribbon)
ribbon:
# 建立连接所用时间
ConnectTimeout: 5000
# 建立连接后从服务器读取资源可用时间
ReadTimeout: 5000
日志
- 输出Feign日志,添加配置
# 开启Feign调用日志
feign:
client:
config:
default:
loggerLevel: DEBUG
Sentinel
服务熔断
某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的相应信息。当检测当该节点微服务调用响应正常后恢复调用链路
- fallback
- blockHandler
- Feign的支持
使用fallback,当异常时快速返回熔断信息
服务降级
降级一般是从整体负荷考虑,当某个服务熔断之后,服务器将不再被调用,客户端可自己准备一个本地的fallback回调,返回一个缺省值
Seata 分布式事务(AT)
提供了 AT、TCC、SAGA 和 XA 分布式事务模式
文档:http://seata.io/zh-cn/docs/overview/what-is-seata.html
- AT
- 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
- 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
- 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。
- TCC (不依赖与本地事务)
- 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
- 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
- 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
- Saga
长事务解决方案,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
- XA
利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种 事务模式
Seata Server
- 下载
https://github.com/seata/seata/releases - 启动 server
使用nacos + mysql
修改注册配置,registry.conf
修改模式为mysql,file.conf
修改服务组
启动seata-server.bat
seata Client
- 添加依赖
<!-- 分布式事务 seata -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<!-- 剔除自带版本并使用0.9.0版的seata-->
<exclusion>
<artifactId>seata-all</artifactId>
<groupId>io.seata</groupId>
</exclusion>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
- 配置文件
spring:
application:
name: service-02
cloud:
alibaba:
# 自定义事务组要与seata-server配置一致
seata:
tx-service-group: xyg_tx_group
nacos:
discovery:
server-addr: localhost:8848
config:
# 暂不使用配置中心
enabled: false
-
复制 registry.conf、file.conf 到资源文件夹
-
添加@GlobalTransactional注解
@GlobalTransactional(name = "xyg_tx_group", rollbackFor = Exception.class)
public String get() {
servcie01.get();
// 服务发送异常时,回滚所有
servcie02.get();
return Ruturn.SUCCESS;
}
- 测试回滚
curl http://localhost:9030/yung02/service/get
seata server日志
项目搭建
项目地址
https://gitee.com/xiongyungang/springcloud-demo
1. 版本说明
https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明
3. 父级工程依赖
<dependencies>
<!--spring boot 2.3.12-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud Hoxton.SR12-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring cloud alibaba 2.2.8.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
2.GateWay 网关
- pom文件
<dependencies>
<!-- 服务注册到nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 网关gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
- 配置文件bootstrap.yml
server:
port: 9030
spring:
application:
name: gateway
cloud:
nacos:
discovery:
# 服务注册
server-addr: localhost:8848
config:
# 暂不使用配置中心
enabled: false
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
routes: # 路由数组: 即请求满足什么条件跳转到哪些服务
- id: service # 路由的ID 没有固定规则但要求唯一,建议配合服务名
uri: lb://service-01 # 匹配后提供服务的路由地址,使用服务名实现负载均衡
#uri: http://localhost:9001/ #匹配后提供服务的路由地址
predicates: # 断言: 路由转发满足的条件
- Path=/yung/** # 当请求路径满足Path时,才进行转发
filters:
- StripPrefix=1 # 在请求转发之前,去除最前面1层路径,这里去除掉yung
3.服务模块
- pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud-demo</artifactId>
<groupId>com.yung</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
- 配置文件
server:
port: 9002
spring:
application:
name: service-01
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
# 暂不使用配置中心
enabled: false
- 访问接口
package com.yung.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/service")
public class Controller {
@Value("${spring.application.name}")
private String applicationName;
@Value("${server.port}")
private String port;
@RequestMapping(value = "/get", method = RequestMethod.GET)
public String get() {
return String.format("application: %s, port: %s ", applicationName, port);
}
}
测试网关
启动2台service服务,端口分别为9001、9002,访问网关地址:
curl http://localhost:9030/service/get
application: service-01, port: 9001
curl http://localhost:9030/service/get
application: service-01, port: 9002
服务间调用 openFeign实现
- 新增service-02服务,监听9002端口
server:
port: 9002
spring:
application:
name: service-02
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
# 暂不使用配置中心
enabled: false
# 设置Feign客户端超时时间(OpenFeign默认支持Ribbon)
ribbon:
# 建立连接所用时间
ConnectTimeout: 5000
# 建立连接后从服务器读取资源可用时间
ReadTimeout: 5000
# 开启Feign调用日志
feign:
client:
config:
default:
loggerLevel: DEBUG
- service-02 添加openFeign依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 服务间的调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
- service-02 启动类添加@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients // 增加注解
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
- service-02 注册FeignClient,用于调用其他服务
@FeignClient(name = "service-01")
public interface Servcie01 {
// 通过FeignClient调用 lb:service-01//service/get
@RequestMapping(value = "/service/get", method = RequestMethod.GET)
String get();
}
- 修改网关,添加service-02的路由
server:
port: 9030
spring:
application:
name: gateway
cloud:
nacos:
discovery:
# 服务注册
server-addr: localhost:8848
config:
# 暂不使用配置中心
enabled: false
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
routes: # 路由数组: 即请求满足什么条件跳转到哪些服务
- id: service-01 # 路由的ID 没有固定规则但要求唯一,建议配合服务名
uri: lb://service-01 # 匹配后提供服务的路由地址,使用服务名实现负载均衡
#uri: http://localhost:9001/ #匹配后提供服务的路由地址
predicates: # 断言: 路由转发满足的条件
- Path=/yung01/** # 当请求路径满足Path时,才进行转发
filters:
- StripPrefix=1 # 在请求转发之前,去除最前面1层路径,这里去除掉yung
- id: service-02
uri: lb://service-02
predicates:
- Path=/yung02/**
filters:
- StripPrefix=1
- service-02调用service-01
curl http://localhost:9030/yung02/service/get
application: service-01, port: 9001
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗