概述
- Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件。
- 方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
- 依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置。
- 就可以将 Spring Cloud 应用接入阿里分布式应用解决方案,通过阿里中间件来迅速搭建分布式应用系统。
版本介绍
组件关系
依赖关系
Nacos概述
什么是Nacos
- Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集。
- 帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
关键特性
搭建NacosServer
- 官网下载解压启动
- 打开浏览器, 在浏览器当中输入: http://localhost:8848/nacos/
- 账号:nacos
- 密码:nacos
服务注册与发现
工程准备
1.创建父工程 spring-cloud-alibaba-parent ,在父工程当中导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.3.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.创建两个子工程 user 与 goods 工程
我创建一个user为例,goods就自己创建了
- 依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- controller
@RestController
public class UserController {
@RequestMapping("/getUser")
public ResponseResult getUser() {
return ResponseResult.success("My User");
}
}
- util
public class ResponseResult extends HashMap {
public static String SUCCESS_CODE = "200";
public static String ERROR_CODE = "500";
public static String DATA_KEY = "data";
public static String MSG_KEY = "msg";
private ResponseResult() {
}
public ResponseResult set(String key, Object object) {
super.put(key, object);
return this;
}
private static ResponseResult newResponseResult() {
return new ResponseResult();
}
public static ResponseResult success() {
return ResponseResult.newResponseResult()
.set("code", ResponseResult.SUCCESS_CODE)
.set(ResponseResult.MSG_KEY, "操作成功");
}
public static ResponseResult success(String msg) {
return ResponseResult.newResponseResult()
.set("code", ResponseResult.SUCCESS_CODE)
.set(ResponseResult.MSG_KEY, msg);
}
public static ResponseResult success(String msg, Object object) {
return ResponseResult.newResponseResult()
.set("code", ResponseResult.SUCCESS_CODE)
.set(ResponseResult.MSG_KEY, msg)
.set(ResponseResult.DATA_KEY, object);
}
public ResponseResult data(Object obj) {
return this.set("data", obj);
}
public static ResponseResult error() {
return ResponseResult.newResponseResult()
.set(ResponseResult.MSG_KEY, "操作失败")
.set("code", ResponseResult.ERROR_CODE);
}
public static ResponseResult error(String msg) {
return ResponseResult.newResponseResult()
.set(ResponseResult.MSG_KEY, msg)
.set("code", ResponseResult.ERROR_CODE);
}
public static ResponseResult error(String msg, Object object) {
return ResponseResult.newResponseResult()
.set(ResponseResult.MSG_KEY, msg)
.set(ResponseResult.DATA_KEY, object)
.set("code", ResponseResult.ERROR_CODE);
}
}
- 启动类
@SpringBootApplication
public class AppUser {
public static void main(String[] args) {
SpringApplication.run(AppUser.class, args);
}
}
- 配置文件 application.yml
logging:
level:
cn.ddossec: debug # 日志级别
server:
port: 5000 # 端口号
spring:
application:
name: consumption-user # 服务名称
- 在user工程当中添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
-
在启动类上添加注解 @EnableDiscoveryClient
-
在配置文件添加添加配置
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos服务的地址 不要加http
-
启动工程后, 在nacos当中查询服务列表
-
使用相同方式 把goods服务注册到nacos上
-
在 user 工程中通过服务发现调用goods工程
-
在启动类配置 RestTemplate
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
- 服务发现调用
@RestController
public class UserController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("/getUser")
public ResponseResult getUser() {
List<ServiceInstance> instances = discoveryClient.getInstances("provide-goods");
ServiceInstance serviceInstance = instances.get(0);
System.out.println(serviceInstance);
String url = serviceInstance.getUri() + "/getGoods".toString();
return ResponseResult.success("调用成功",restTemplate.getForObject(url, Object.class));
}
}
nacos集群
1.编译源码, 支持mysql8数据库
-
1.下载nacos源码
-
2.下载后, 修改源码进行编译,把mysql数据库改为mysql8
修改pom文件
-
修改 MysqlHealthCheckProcessor 导入的包为mysql8的驱动包
-
修改数据库连接配置为mysql8的配置
db.url.0=jdbc:mysql://11.162.196.161:3306/diamond_devtest?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.url.1=jdbc:mysql://11.163.152.91:3306/diamond_devtest?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
- 进入到源码根目录,使用mvn进行打包
mvn -Prelease-nacos clean install -U -Dmaven.skip.test=true
- 打包后, 找到nacos-1.1.4\distribution\target\nacos-server-1.1.4\nacos\conf
2.在数据库当中创建 nacos_config,导入nacos中sql数据
3.复制3份nacos工程,分别配置信息
- 设置端口号:8849、8850、8851
- 示例
-
分别设置每一个工程的端口号
-
设置数据库连接信息
指定数据源为
Mysqlspring.datasource.platform=mysql
数据库实例数量
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=1234
-
分别设置每一个工程的 application.properties 文件当中进行设置
-
集群信息
-
复制一份 cluster.conf.example 文件改为 cluster.conf 配置以下信息
192.168.43.95:8849
192.168.43.95:8850
192.168.43.95:8851
- 192.168.43.95:是本机IP
- 每一份工程复制一份过去就行
4.分别进入到每一份工程bin目录下,修改 startup.cmd
cluster
- 修改完毕后,开始分别启动每一个
5.在nginx当中配置反向代理
upstream nacos {
server 169.254.108.9:8849;
server 169.254.108.9:8850;
server 169.254.108.9:8851;
}
location /nacos {
proxy_pass http://nacos/nacos;
}
6.启动 nginx 地址栏访问: http://localhost/nacos/
7.将服务注册到Nacos当中,直接写上nginx的地址
集成Ribbon
使用Ribbon
1.不需要添加依赖
2.在 RestTemplate 上添加注解 @LoadBalanced
3.在调用时, 就可以使用服务名称来进行调用 , 和之前的使用方式 是一样的
@RequestMapping("/getGoods")
public ResponseResult getGoods() {
String url = "http://provide-goods/getGoods";
return ResponseResult.success("调用成功",restTemplate.getForObject(url, Object.class));
}
默认使用是 轮询 策略
自定义负载均衡策略
配置方式与之前一样
配置懒加载
ribbon:
eager-load:
enabled: true
clients: provide-goods #多个服务使用逗号隔开
Nacos集成Ribbon,支持权重的负载均衡算法
1.自定义权重算法
public class NacosWeightRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
// 获取请求微服务的名称
String name = loadBalancer.getName();
// 获取nacos服务发现相关名称
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
// nacos client自动通过基于权重的负载均衡算法,选择实例
try {
Instance instance = namingService.selectOneHealthyInstance(name);
return new NacosServer(instance);
} catch (NacosException e) {
e.printStackTrace();
}
return null;
}
}
2.配置使用自定义的算法
3.在nacos服务中心当中配置服务的权重
集成Feign
使用步骤
在客户端引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在启动类上面加上注解 @EnableFeignClients
创建服务 FeignClient
创建一个 service 文件夹,在该文件下创建一个接口
在接口加上添加 @FeignClient() 注解,参数就是你的微服务名字
示例
@FeignClient(name = "provide-goods")
public interface GoodsFeignClient {
@RequestMapping("/getGoods")
public Object getGoods();
}
控制器调用
单个参数请求
goods工程
@RequestMapping("/getGoodsWithID/{id}")
public ResponseResult getGoodsWithID(@PathVariable Integer id){
return ResponseResult.success("goods___id="+id);
}
Feign客户端
@RequestMapping("/getGoodsWithID/{id}")
public ResponseResult getGoodsWithID(@PathVariable Integer id);
user工程调用
多个参数请求
goods工程
Feign客户端
user工程调用
Sentinel
什么是 Sentinel
Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。
使用 Sentinel
-
下载 sentinel 控制台服务器
-
在 jar 目录当中打开控制台,输入 java -jar sentinel-dashboard-1.7.0.jar
- 可以修改端口号:
java -Dserver.port=8088 -Dcsp.sentinel.dashboard.server=localhost:8088 -jar sentinel-dashboard-1.7.0.jar
在浏览器当中输入:localhost:8080
用户名和密码为 sentinel
- 添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 添加配置
sentinel:
transport:
dashboard: localhost:8080
-
在浏览器当中进行访问:http://localhost:5000/getGoodsWithId/1
-
访问完成后,再次查看控制台
功能介绍
-
实时监控
1、 实时监控aip的执行通过/拒绝和对应响应时长 -
簇点链路
1、曾经被访问过的路径
2、流控
-
资源名称,访问的路径,唯一名称
-
针对来源
1、可以针对调用者进行限流
2、假设两个微服务a和b调用,可以对a和b单独的设置限流规则
3、default为不区别来源 -
阈值类型
1、QPS:当调用api的QPS达到阈值时,就去做限流
2、线程数:当调用api的线程数达到一定的域值时就去做限流
3、是否集群
4、流控模式
4.1、直接:当违反定义的api规则时, 直接快速失败
示例
一秒种超过一次时, 就会触发限流
4.2、关联
当关联的资源达到阈值时, 限流自己
示例
http://localhost:5000/getGoodsWithObj?name=leader_tblog&id=18 访问时, 就会被限流
查询接口调用过快, 如果影响修改接口,或者修改接口影响查询接口, 根据业务需求,来进行关联限流
4.3、链路
指定资源从入口资源进来的流量,如果达到阈值,就开启限流
4.4、流控效果
- 快速失败
直接失败,抛出异常信息 - Warm Up
1、根据 codeFactor 从设置的阈值除以codeFactor,经过预热时长,才到达设置的QPS阈值
2、假设设置的阈值为90 那么就会90/3(默认) 30 作为最初的阈值 ,预热指定的时长 才去达到90的值限流 - 排队等待
1、匀速排队,让请求以均匀的速度通过,阈值类型必须设置成QPS,否则无效
2、超过了定义的时长, 就会抛出异常
代码配置
引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.7.0</version>
</dependency>
- 定义资源名称
private static final String GET_GOODS_KEY = "getMyGoods";
- 定义限流规则
@RequestMapping("/initFlowRules")
public void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//设置资源名称
rule.setResource(GET_GOODS_KEY);
//设置针对来源
rule.setLimitApp("default");
//设置阈值类型
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置阈值
rule.setCount(1);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
- 应用到被限流的接口上
@RequestMapping("/getMyGoods")
public String getMyGoods(){
try (Entry entry = SphU.entry(GET_GOODS_KEY)) {
// 被保护的逻辑
return "getMyGoods";
} catch (BlockException ex) {
// 处理被流控的逻辑
return "被限流了";
}
}
自动执行流控代码
使用 springboot 自带的 ApplicationRunner 接口
使用方式,实现 ApplicationRunner 接口,在 run 方法当中执行流控代码
示例
@Component
public class SentinelRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
initFlowRules();
}
private static final String GET_GOODS_KEY = "getMyGoods";
public void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//设置资源名称
rule.setResource(GET_GOODS_KEY);
//设置针对来源
rule.setLimitApp("default");
//设置阈值类型
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置阈值
rule.setCount(1);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}
使用注解改进代码
- Sentinel 支持通过 @SentinelResource 注解定义资源并配置 blockHandler 和 fallback 函数来进行限流之后的处理
- blockHandler 函数会在原方法被限流/降级/系统保护的时候调用
- fallback 函数会针对所有类型的异常
示例
private static final String GET_GOODS_KEY = "getMyGoods";
@SentinelResource(value = GET_GOODS_KEY, blockHandler = "blockHandlerMethod")
@RequestMapping("/getMyGoods")
public String getMyGoods() {
return "getMyGoods";
}
public String blockHandlerMethod(BlockException e) {
e.printStackTrace();
return "被限流了----";
}
降级规则
降级策略
RT
平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 5 个请求,
对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位)
那么在接下的时间窗口之内,对这个方法的调用都会自动地熔断
异常比例
当资源的每秒请求量 >= 5,并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后
资源进入降级状态
异常次数
当资源近 1 分钟的异常数目超过阈值之后会进行熔断。
注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。
热点规则
指定索引参数,如果在时间窗口内超过了指定的阈值,就会触发熔断
- 参数例外项
1、可以对指定的参数再进行设置限流
2、参数级别
对指定的参数限流,甚至对指定参数的值进行限流
注意事项
参数必须是基本数据类型或String类型
系统规则
和其他的规则不太一样,它是针对应用来设的
前面设置的都是争对某一个资源。某一个方法来设置的
阈值类型
load: load是负载,只有在linux机器上才会生效。根据当前系统的负载来决定是不是触发保护。
RT: 这个应用上所有的流量的平均的响应时间,所有服务的平均响应时间超过一个值,那么我就停止接收新的请求。
线程数: 所有服务访问的线程数加起来。
入口qps: 所有服务的qps加起来达到一个值。
cpu使用率: cpu的使用率超过一个百分比
发生以上这些情况的时候把你整个应用给你断掉。所有服务都不提供了
授权规则
使用授权时要先指定来源,对指定来源的内容进行限流
指定来源方式
@Component
public class RequestOrigin implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
//从请求参数中获取origin的参数并返回
String origin = httpServletRequest.getParameter("origin");
//如果获取不到origin 就抛出异常
if (StringUtils.isBlank(origin)){
throw new IllegalArgumentException("必须指定origin");
}
return origin;
}
}
整合Feign
开启 feign 与 Sentinel 集成
feign:
sentinel:
enabled: true
整合方式与 Hystrix 一样
规则持久化
使用 Nacos 配置中心实现规则持久化
- 在 nacos 配置中心当中添加配置 json 规则
[
{
"resource": "getMyGoods2",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
- 在工程当中添加依赖
<!--添加此依赖,项目一启动时, 就去到nacos当中读取配置中心-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.7.0</version>
</dependency>
在配置文件当中添加 sentinel 读取 nacos 地址
eager: true
datasource:
ds:
nacos:
server-addr: localhost:8849
group-id: DEFAULT_GROUP
rule-type: flow
data-id: my-goods-sentinel
data-type: json
Spring-Cloud-GateWay
概述
什么是 Spring-Cloud-GateWay
Spring Cloud Gateway 基于 Spring Boot 2, 是 Spring Cloud 的 全新 项目, 该项 目 提供 了 一个 构建 在 Spring 生态 之上 的 API 网关
Spring Cloud Gateway 旨在 提供 一种 简单 而 有效 的 途径 来 转发 请求, 并为 它们 提供 横 切 关注 点, 例如: 安全性、 监控/ 指标 和 弹性
优点
性能比较高, 是第一代网关 zuul 的 1.6 倍
功能强大, 内置了很多功能,转发/监控/限流
容易扩展
缺点
实现依赖 Netty 和 WebFlux,不是传统的 Servet 模型
不能将其部署在 Tomcat,Jetty 等 Servelt 容器当中,只能打成 Jar 包执行
需要 springboot2.0 及以上版本才支持
词汇
Route路由: 路由网关的基本构建块。 它由ID,目标URI,谓词集合和过滤器集合定义。 如果聚合谓词为真,则匹配路由
Predicate谓词: 对请求的内容进行匹配,条件筛选
Filter过滤器: 可以对请求进行过滤,在发送下游请求之前或之后修改请求和响应
快速入门
创建 gateWay 服务
添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
<version>0.9.4.RELEASE</version>
</dependency>
</dependencies>
创建 启动类
@SpringBootApplication
public class AppGateWay {
public static void main(String[] args) {
SpringApplication.run(AppGateWay.class,args);
}
}
添加 配置文件
logging:
pattern:
console: "%d{HH:mm:ss.SSS} %clr(%5p) %clr(-){faint} %clr(%-80.80logger{79}){cyan} %clr(:) %m%n"
server:
port: 9000
spring:
application:
name: api-gateway
cloud:
gateway:
routes: #当请求满足要求时,转发到指定的服务当中
- id: goods_route #唯一标识,默认是uuid
uri: http://localhost:5000/ #请求要转发的地址
order: 1 #路由优先级 越小, 优先级越高
predicates: #谓词,断言(条件判断 转发请求要满足什么条件)
- Path=/consumer-user/** #当路径中有指定内容时, 才会转发到指定uri
filters: #过滤器,拦截请求做一些额外处理
- StripPrefix=1 #去掉添加的path内容
在浏览器 当中访问
http://localhost:9000/consumer-user/getGoodsWithId/1
GateWay整合Nacos
- 在配置文件中添加 Nacos 的依赖, 把 gatWay 注册到 Nacos 当中
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 在配置文件当中添加 nacos 的地址
cloud:
nacos:
discovery:
server-addr: localhost:80 #nacos服务的地址 不要加http
- 在启动类上添加注解
@EnableDiscoveryClient - 配置 gateWay 从 nacos 中获取服务信息
discovery:
locator:
enabled: true #从nacos当中获取服务信息
- 信息 uri 为服务地址
lb: //服务名称
lb: 为负载均衡调用
lb: //consumer-user
谓词
什么是谓词 ( 断言 )
用于对请求进行判断 ,只有断言都返回值,才会真正的执行路由
满足条件,才能进行过滤,转发
内置断言工厂
基于 Datetime
AfterRoutePredicateFactory
判断请求日期是否晚于指定的日期
参数: 传递一个日期
示例
- After=2020-12-30T23:59:59.789+08:00[Asia/Shanghai]
BeforeRoutePredicateFactory
判断请求日期是否早于指定日期
参数: 传递一个日期
BetweenRoutePredicateFactory
判断请求日期是否在指定日期之间
参数: 传递两个日期
基于远程地址
RemoteAddrRoutePredicateFactory
判断请求主机地址是否在指定地址段时
参数: IP地址段
示例
- RemoteAddr=192.168.1.1/24
基于 Cookie 的断言
CookieRoutePredicateFactory
判断请求 cookie 是否具有给定名称且值与正则表达式匹配
参数: cookie 名称 , 正则表达式
示例
- Cookie=myCookName,[^gao]
基于 header
HeaderRoutePredicateFactory
判断请求 Header 是否具有给定名称且值与正则表达式匹配
参数: 标题名称 , 正则表达式
示例
- Header=Request-Id,\d+
基于 Host
HostRoutePredicateFactory
判断请求的 Host 是否满足匹配条件
参数: 主机名模式
示例
- Host=**.testhost.org
基于 Methods
MethodRoutePredicateFactory
判断请求类型是否为指定的类型
参数: 请求类型
示例
- Method=GET
基于 Query 请求参数
QueryRouterPredicateFactory
判断请求参数是否具有给定名称且值与正则表达式匹配
参数: 请求参数名,正则表达式
示例
- Query=name,test.
基于权重的断言工厂
WeightRoutePredicateFactory
对于同一组内容的路由,按权重进行转发
参数 : 组名,权重
示例
两个对于 / weight/** 路径转发的路由定义,这两个路由是同一个权重分组,且 weight_ route1 权重为 1, weight_ route2 权重为 9
对于 10 个访问 /weight/** 路径的请求来说,将会有 9 个路由到 weight_ route2,1 个路由到 weight_ route1
自定义谓词工厂
要求
名字必须是 配置+RoutePredicateFactory
必须继承 AbstractRoutePredicateFactory
方法介绍
List shortcutFieldOrder()
读取配置文件当中的参数,赋值到配置类中的属性上
要求和配置类当中的属性名称一致
Predicate apply
断言逻辑
class Config 内部类
接收配置文件当中对应的参数
示例
package cn.ddossec.factory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
@Component
class AppTypeRoutePredicateFactory extends AbstractRoutePredicateFactory<AppTypeRoutePredicateFactory.Config> {
//构造器
public AppTypeRoutePredicateFactory() {
super(AppTypeRoutePredicateFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("appType");
}
@Override
public Predicate<ServerWebExchange> apply(AppTypeRoutePredicateFactory.Config config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
//接收参数
String app = serverWebExchange.getRequest().getQueryParams().getFirst("app");
if (StringUtils.isNotEmpty(app)) {
if (config.getAppType().equals(app))
return true;
else
return false;
}
return false;
}
};
}
public static class Config {
private String appType;
public Config(String appType) {
this.appType = appType;
}
public String getAppType() {
return appType;
}
public void setAppType(String appType) {
this.appType = appType;
}
}
}
过滤器
什么是过滤器
GatewayFilter 网关过滤器用于拦截并链式处理 web 请求,可以实现横切的与应用无关的需求,比如:安全、访问超时的设置等
在请求传递过程当中,对请求和响应进行一些额外的处理
生命周期
pre
在 pre 类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等
post
在 post 类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等
分类
局部过滤器
只作用在某 一个路由上
全局过滤器
作用在全部路由上
内置局部过滤器
-
AddRequestHeader
为原始请求添加 Header
Header 名称及值 -
AddRequestParameter
为原始请求添加请求参数
参数名及值 -
AddResponseHeader
为原始响应添加Header
Header名称及值 -
DedupeResponseHeader
剔除响应头中重复的值
需要去重的Header名称及重复策略 -
Hystrix
为路由引入 Hystrix 的断路器保护
HystrixCommand 名称 -
FallBackHeader
为fallbackUri的请求头中添加具体的异常信息
Header的名称 -
PrefixPath
为原始请求添加前缀
前缀路径 -
PreserveHostHeader
为请求添加一个PreservHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
没有参数 -
RequestRateLimiter
用于对请求限流 -
RedirectTo
将原始请求重定向到指定的URL
http状态码及重定向的url -
RemoveHopByHopHeadersFilter
为原始请求删除IETF组织规定的一系列Header
默认就会启用 -
RewritePath
重写原始请求路径
原始路径正则表达式及重写后路径的正则表达式 -
RewriteResponseHeader
重写原始请求中的某个Header
Header名称,值的正则表达式,重写后的值 -
SaveSession
在请求转发之前,强制执行webSession::save操作 -
secureHeaders
为原始响应添加一系列安全作用的响应头 -
SetStatus
修改响应的状态码
HTTP状态码,可以是数字也可以是字符串 -
StripPrefix
用于截断原始请求的路径
使用数字表示要截断的路径的数量 -
Retry
针对不同的响应进行重试 -
RequestSize
设置允许接收最大请求包的大小
请求包大小,单位为字节 -
ModifyRequestBody
在转发请求之前修改原始请求体内容
修改后的请求体内容 -
ModifyResponseBody
修改原始响应体的内容
修改后的响应体内容
自定义局部过滤器
与自定义谓词方式大致一样
@Component
public class LogStatusGatewayFilterFactory extends AbstractGatewayFilterFactory<LogStatusGatewayFilterFactory.Config> {
public LogStatusGatewayFilterFactory() {
super(LogStatusGatewayFilterFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("cacheStatus", "consoleStatus");
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (config.getCacheStatus())
System.out.println("缓存状态");
if (config.getConsoleStatus())
System.out.println("控制台状态");
return chain.filter(exchange);
}
};
}
@Validated
public static class Config {
private Boolean cacheStatus;
private Boolean consoleStatus;
public Boolean getCacheStatus() {
return cacheStatus;
}
public void setCacheStatus(Boolean cacheStatus) {
this.cacheStatus = cacheStatus;
}
public Boolean getConsoleStatus() {
return consoleStatus;
}
public void setConsoleStatus(Boolean consoleStatus) {
this.consoleStatus = consoleStatus;
}
}
}
全局内置过滤器
@Component
public class TokenClobalFilter implements GlobalFilter, Ordered {
/**
* 过滤器执行的顺序
* @return
*/
@Override
public int getOrder() {
return 0;
}
/**
* 执行过滤器逻辑
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
if(!"admin".equals(token)){
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}
工作流程
示例图
介绍
DispatcherHandler
所有请求的调度器,负载请求分发
RoutePredicateHandlerMapping
路由谓语匹配器,用于路由的查找,以及找到路由后返回对应的 WebHandler
DispatcherHandler 会依次遍历 HandlerMapping 集合进行处理
FilteringWebHandler
使用Filter链表处理请求的 WebHandler
RoutePredicateHandlerMapping 找到路由后返回对应的 FilteringWebHandler 对请求进行处理。
FilteringWebHandler 负责组装Filter链表并调用链表处理请求。
Nacos配置中心
概述
Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。
使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理你 Spring Cloud 应用的外部属性配置。
快速入门
- 客户端使用方式
添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
编写 bootstrap.yml 配置文件
spring:
profiles:
active: dev #${spring.profile.active}
application:
name: consumer-user #${prefix}
cloud:
nacos:
config:
server-addr: localhost:80
file-extension: yaml #${file-extension}
- 在 Nacos 管理中心添加配置
动态刷新
@Value("${user.name}")
private String username;
@RequestMapping("/getUserName")
public String getUserName(){
return username;
}
在控制器当中添加注解 @RefreshScope
命名空间与分组
namespace 命名空间
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。
Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
在没有明确指定 ${spring.cloud.nacos.config.namespace} 配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。
Group 分组
将不同的微服务划分到不同的分组当中
DataID
具体的配置文件地址