微服务
微服务是一种经过良好架构设计的分布式架构方案,微服务架构特征:
- 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
- 面向服务:微服务对外暴露业务接口
- 自治:团队独立、技术独立、数据独立、部署独立
- 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
单体架构:
- 简单方便,高度耦合、扩展性差,适合小型项目
分布式架构特点:
- 松耦合,扩展性好,但架构复杂,难度大。适合大型互联网项目
微服务:一种良好的分布式架构方案
- 优点:拆分粒度更小、服务更独立、耦合度更低
- 缺点:架构非常复杂,运维、监控、部署难度提高
Dubbo | SpringCloud | SpringCloudAlibaba | |
注册中心 | zookeeper、Redis | Eureka、Consul | Nacos、Eureka |
服务远程调用 | Dubbo 协议 | Feign(http 协议) | Dubbo、Feign |
配置中心 | 无 | SpringCloudConfig | SpringCloudConfig、Nacos |
服务网关 | 无 | SpringCloudGateway、zuul | SpringCloudGateway、Zuul |
服务监控和保护 | dubbo-admin 功能弱 | Hystrix | Sentinel |
springCloud
服务注册发现:Eureka、Nacos、Consul
统一配置管理:Spring Cloud Config、Nacos
服务远程调用:OpenFeign、Dubbo
统一网关路由:Spring Cloud Gateway、zuul
服务链路监控:Zipkin、Sleuth
流控、降级、保护:Hystix、Sentinel
服务拆分及远程调用
1、不同微服务,不要重复开发相同业务
2、微服务数据独立,不要访问其他微服务的数据库
3、微服务可以将自己的业务暴露为接口,供其他微服务调用
- 微服务需要根据业务模块拆分,做到单一职责,不要重复开发相同业务
- 微服务可以将业务暴露为接口,供其他微服务使用
- 不同微服务都应该有自己独立的数据库
远程调用方式分析
从订单向用户发起远程调用
user 服务向外暴露了一个接口 http://localhost:8091/user/{id}
订单模块直接向用户模块发起 http 请求(RestTemplate)
/**
* 创建 RestTemplate 并注入 Spring 容器
*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
service
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用 RestTemplate 发起 http 请求,查询用户
// 2.1 url 路径
String url = "http://localhost:8091/user/" + order.getUserId();
// 2.2 发送 http 请求,实现远程调用
User user = restTemplate.getForObject(url, User.class);
// 3.封装 user 到 order
order.setUser(user);
// 4.返回
return order;
}
消费者与提供者
- 服务提供者:一次业务中,被其他微服务调用的服务。(提供接口给其他微服务)
- 服务消费者:一次业务中,调用其他微服务的服务。(调用其他微服务提供的接口)
一个服务既可以是提供者又可以是消费者(提供者和消费者角色是相对的)
Eureka 注册中心
Eureka 的作用
- 消费者如何获取服务提供者具体的信息
- 服务提供者启动时向 eureka 注册自己的信息
- eureka 保存这些信息
- 消费者根据服务名称向 eureka 拉取提供者信息
- 如果又多个服务提供者,消费者如何选择
- 服务消费者利用负载均衡算法,从服务列表中挑选一个
- 消费者如何感知服务提供者健康状态
- 服务提供者会每隔 30 秒向 EurekaServer 发送心跳请求,报告健康状态
- eureka 会更新记录服务列表信息,心跳不正常会被剔除
- 消费者就可以拉取到最新的信息
在 Eureka 架构中,微服务角色有两类
- EurekaServer:服务端,注册中心
- 记录服务信息
- 心跳监控
- EurekaClient:客户端
- Provider:服务提供者
- 注册自己的信息到 EurekaServer
- 每隔 30 秒向 EurekaServer 发送心跳
- consumer:服务消费者
- 根据服务名称从 EurekaServer 拉取服务列表
- 基于服务列表做负载均衡,选中一个微服务后发起远程调用
搭建 EurekaServer
创建项目 引入 spring-cloud-starter-netflix-eureka-server 依赖
<dependencies>
<!--eureka 服务器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
编写启动类,添加 @EnableEurekaServer 注解
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
编写 yml 文件配置
server:
port: 10086
spring:
application:
name: eurekaserver
eureka:
client:
service-url: # eureka 的地址信息
defaultZone: http://127.0.0.1:10086/eureka
服务注册
- 引入 eureka-client 依赖
- 在 application.yml 中配置 eureka 地址
服务的发现/拉取
服务拉取是基于服务名称获取服务列表,然后对服务列表做负载均衡
1、修改 OrderService 代码,修改访问的 url 路径,用服务名代替 ip、端口:
String url = "http://userservice/user/" + order.getUserId();
2、在 order-service 项目的启动类 OrderApplication 中的 RestTemplate 添加负载均衡注解:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
Riboon 负载均衡
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KnHNXlig-1674732672886)(C:\Users\xiaobai\AppData\Roaming\Typora\typora-user-images\image-20221124163451711.png)]
负载均衡策略
Ribbon 的负载均衡规则是一个叫做 IRule 的接口来定义的,每一个子接口都是一种规则
内置负载均衡规则类 | 规则描述 |
RoundRobinRule | 简单轮询服务列表来选择服务器。它是 Ribbon 默认的负载均衡规则 |
AvailabilityFilteringRule | 对以下两种服务器及进行忽略: (1)在默认情况下,这台服务器如果 3 次链接失败,这台服务器就会被设置为“短路”状态。短路状态将持续 30 秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了 AvailabilityFilteringRule 规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit 属性进行配置。 |
weightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重会英雄服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用 Zone 对服务器进行分类,这个 Zone 可以理解为一个机房、一个机架等。而后再对 Zone 内的多个服务做轮询。 |
BestAvailableRuel | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器 |
RetryRule | 重试机制的选择逻辑 |
通过定义 IRule 实现可以修改负载均衡规则,有两种方式:
1、代码方式:在 order-service 中的 OrderApplication 类中,定义一个新的 IRule
@Bean
public IRule randomRule(){
return new RandomRule();
}
2、配置文件方式:在 order-service 的 application.yml 文件中,添加新的配置也可以修改规则
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
饥饿加载
Ribbon 默认是在采用懒加载,即第一次访问时才会去创建 LoadBanlanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载
ribbon:
eager-load:
enabled: true # 开启饥饿加载
clients: # 指定饥饿加载的服务名称
- userservice
总结
Ribbon 负载均衡规则
- 规则接口是 IRule
- 默认实现是 ZoneAvoidanceRule,根据 zone 选择服务列表,然后轮询
负载均衡自定义方式
- 代码方式:配置灵活,但修改时需要重新打包发布
- 配置方式:直观,方便,无需重新打包发布,但是无法做到全局配置
饥饿加载
- 开启饥饿加载
- 指定饥饿加载的微服务名称
Nacos 注册中心
Nacos 是阿里巴巴的产品,现在是 SpringCloud 中的一个组件。相比 Eureka 功能更加丰富,在国内受欢迎程度较高。
单机模式:
1.首先通过cmd进入到nacos文件夹里面bin目录
2.然后输入命令startup.cmd -m standalone 这里是将nacos以单机模式运行
Nacos的依赖
父工程:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
客户端:
<!-- nacos客户端依赖包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
配置文件
spring:
cloud:
nacos:
server-addr: localhost:8848 # nacos 服务地址
服务集群属性
1、修改 application.yml
spring:
cloud:
nacos:
server-addr: localhost:8848 # nacos 服务地址
discovery:
cluster-name: SX # 配置集群名称,也就是机房位置
Nacos 服务分级存储模型
- 一级是服务,例如 userservice
- 二级是集群,例如山西或山东
- 三级是实例,例如山西机房的某台部署了 userservice 的服务器
根据集群负载均衡
先将 order-service 设置集群名称
设置负载均衡的 IRule 为 NacosRule,这个规则优先会寻找与自己同集群的服务
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
实际部署中会出现这样的场景
- 服务器设备性能有差异,部分实例所在集器性能比较好,另一些较差,我们希望性能好的集器承担更多的用户请求
Nacos 提供了权重配置来控制访问频率,权重越大则访问频率越高
环境隔离 - namespace
Nacos 中服务存储和数据存储的最外层都是一个名为 namespace 的东西,用来做最外层隔离
1、在 Nacos 控制台可以创建 namespace,用来隔离不同环境
2、填写写的命名空间信息(命名空间名 dev、描述 开发环境)
3、xml 配置 namespace(命名空间 ID)
nacos:
server-addr: localhost:8848
discovery:
cluster-name: SX # 配置集群名称,也就是机房位置
namespace: 36cd6f37-dc38-4c0a-b89d-080fe1ed18dd # dev 环境
- namespace 用来做环境隔离
- 每个 namespace 都有唯一 id
- 不同 namespace 下的服务不可见
临时实例和非临时实例ya
服务注册到 Nacos 时,可以选择注册为临时或非临时实例
spring:
cloud:
nacos:
discovery:
ephemeral: false # 设置非临时实例
Nacos 与 eureka
共同点:
- 都支持服务注册和服务拉取
- 都支持服务提供者心跳方式做健康检测
区别:
- Nacos 支持服务端制动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
- 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
- Nacos 支持服务列表变更的消息推送模式,服务列表更新更及时
- Nacos 集群默认采用 AP 方式,当集群中存在非临时实例时,采用 CP 模式;Eureka 采用 AP 方式
Nacos 配置管理
统一配置管理
配置更改热更新
在 nacos 中添加配置信息(配置列表)
在弹出的表单中填写配置信息:
- 配置文件的 id:[服务名称]-[profile]_[后缀名(yaml)]
- 分组,默认
- 格式,支持 yaml 和 properties
- 发布
配置获取步骤:
项目启动 -》(在 bootstrap.yml 中获取 nacos 地址)读取 nacos 中配置文件 -》读取本地配置文件 application.yml -》创建 spring 容器 -》加载 bean
1、引入 Nacos 的配置管理客户端依赖
<!-- nacos 的配置管理依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2、在 resource 中添加 bootstrap.yml 文件,这个文件时引导文件,优先级高于 application.yml
配置 nacos 地址、当前环境、服务名称、文件后缀名,这些决定了程序启动时去 nacos 读取哪个文件
spring:
application:
name: userservice
profiles:
active: dev # 环境
cloud:
nacos:
server-addr: localhost:8848 # nacos 地址
config:
file-extension: yaml # 文件后缀名
3、测试 controller
@Value("${pattern.dateformat}")
private String dateformat;
@GetMapping("now")
public String now(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
}
配置自动刷新
Nacos 中的配置文件变更后,微服务无需重启就可以感知。不过需要通过下面两种配置实现:
- 方式一:在 @Value 注入的变量所在类上添加 @RefreshScope
- 方法二:使用 @ConfigurationProperties 注解
@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PattenProperties {
private String dateformat;
}
多环境配置共享
微服务启动时会从 nacos 读取多个配置文件:
- [spring.application.name]-[spring.prorfiles.active].yaml([服务名]-[spring.prorfiles.active].yaml,环境配置)
- [spring.application.name].yaml ([服务名].yaml,默认配置,多环境共享)
无论 profile 如何变化,[spring.application.name].yaml 这个文件一定会加载,因此多环境共享配置可以写入这个文件
多配置的优先级:
[服务名]-[环境].yaml > [服务名].yaml > 本地配置
Nacos 集群搭建
搭建步骤
- 搭建 M有SQL 集群并初始化数据库表
- 下载解压 nacos
- 修改集群配置(节点信息)、数据库配置
- 分别启动多个 nacos 节点
- nginx 反向代理
http 客户端 Feign
RestTemplate 方法调用存在的问题
- 代码可读性差,编程体验不统一
- 参数复杂 URL 难以维护
Feign
Feign 是一个声明式的 http 客户端
其作用就是帮助我们优雅的实现 http 请求的发送,解决上面提到的问题
定义和使用 Feign 客户端
1、引入依赖
<!-- feign 客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、在启动类添加注解开启 Feign 的功能
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
3、编写 Feign 客户端
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
主要是基于 Spring MVC 的注解来声明远程调用的信息
- 服务名称:userservice
- 请求方式:GET
- 请求路径:/user/{id}
- 请求参数:Long id
- 返回值类型:User
4、用 Feign 客户端代替 RestTemplate
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用 Feign 远程调用
User user = userClient.findById(order.getUserId());
// 3.封装 user 到 order
order.setUser(user);
// 4.返回
return order;
}
自定义 Feign 的配置
Feign 运行自定义配置来覆盖默认配置
类型 | 作用 | 说明 |
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http 远程调用的结果做解析,例如解析 json 字符串为 java 对象 |
feign.codec.Encoder | 请求参数编码 | 请求参数编码,便于通过 http 请求发送 |
feign.Contract | 支持的注解格式 | 默认是 Spring MVC 的注解 |
feign.Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用 Ribbon 的重试 |
一般我们需要配置的就是日志级别
配置 Feign 日志有两种方式
方式一:配置文件方式
- 全局生效:
feign:
client:
config:
default: # 这里用 default 就是全局配置,如果写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
- 局部生效:
feign:
client:
config:
userservice: # 针对 userservice 的配置
loggerLevel: FULL # 日志级别
方式二:java 代码方式
先声明一个 Bean:
public class DefaultFeignConfiguration {
@Bean
public Logger.Level logLevel(){
return Logger.Level.BASIC;
}
}
而后如果是全局配置,则把它放到 @EnableFeignClients 这个注解中:
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
如果是局部配置,则把它放到 @FeignClient 这个注解中:
@FeignClient("userservice", configuration = DefaultFeignConfiguration.class)
Feign 的性能优化
Feign 底层的客户端实现:
- URLConnection:默认实现,不支持连接池
- Apache HttpClient:支持连接池
- OKHttp:支持连接池
因此优化 Feign 的性能主要包括:
- 使用连接池代替默认的 URLConnection
- 日志级别,最好用 basic 或 none
Feign 的性能优化-连接池配置
Feign 添加 HttpClient 的支持:
引入依赖:
<!-- httpClient 的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
配置连接池:
feign:
httpclient:
enabled: true # 支持 httpClient 的开关
max-connections: 200 # 最大连接数
max-connections-per-route: 50 # 单个路径的最大连接数
根据压测调试最大连接数和单个路径的最大连接数的最佳值
Feign 的最佳实践
方式一(继承):给消费者的 FeignClient 和提供者的 controller 定义统一的父接口作为标准。
- 服务紧耦合
- 父几口参数列表中的映射不会被继承
方式二(抽取):将 FeignClient 抽取为独立模块,并且把接口有关的 POJO、默认的 Feign 配置都放到这个模块中,提供给所有消费者使用
实现步骤:
1、创建一个 module,命名为 feign-api,然后引入 feign 的 starter 依赖
2、将 order-service 中编写的 UserClient、User、DefaultFeignConfiguration 都复制到 feign-api 项目中
3、在 order-service 中引入 feign-api 的依赖
4、修改 order-service 中的所有与上述三个组件有关的 import 部分,改成导入 feign-api 中的包
5、重启测试
当定义的 FeignClien 不在 SpringBootApplication 的扫描包范围时,这些 FeignClient 无法使用。有两种方式解决:
方式一:指定 FeignClient 所在包
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
方式二:指定 FeignClient 字节码
@EnableFeignClients(clients = {UserClient.class})
统一网关 Gateway
网关功能:
- 身份认证和权限校验
- 服务路由、负载均衡
- 请求限流
在 Spirng Cloud 中网关的实现包括两种:
- gateway
- zuul
Zuul 时基于 Servlet 的实现,属于阻塞式编程。而 SpringCloud Gateway 则是基于 Spring5 中提供的 WebFlux,属于响应式编程的实现,具有更好的性能。
搭建网关服务
1、创建新的 module,引入 Spring Cloud Gateway 的依赖和 nacos 的服务发现依赖:
<!-- 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>
2、编写路由配置及 nacos 地址
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848 # nacos 地址
gateway:
routes:
- id: user-service # 路由标识,必须唯一
uri: lb://userservice # 路由的目标地址,lb 是 loadBalance 简写 负载均衡,后面跟服务名称
predicates: # 路由断言,判断请求是否符合规则
- Path=/user/** # 路径断言,判断路径是否以/user开头,如果是则符合
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
最好不要复制,容易出错
路由配置包括:
- 路由 id:路由的唯一标识
- 路由目标(uri):路由的目标地址,http 代表固定地址,lb 代表根据服务名负载均衡
- 路由断言(predicates):判断路由的规则
- 路由过滤器(filters):对请求或响应做处理
路由断言工厂 Route Predicate Factory
- 我们在配置文件中写的断言规则只是字符串,这些字符串会被 Predicate Factory 读取并处理,转变为路由判断的条件
- 例如 Path=/user/** 是按照路径匹配,这个规则是由 org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory 类来处理的
- 像这样的断言工厂在 Spring Cloud Gateway 还有十几个
Spring 提供的 11 种基本的 Predicate 工厂:
名称 | 说明 | 示例 |
After | 是某个时间点后的请求 | |
Before | 是某个时间点之前的请求 | |
Between | 是某两个时间点之前的请求 | |
Cookie | 请求必须包含某些 cookie | |
Header | 请求必须包含某些 header | |
Host | 请求必须是访问某个 host(域名) | |
Method | 请求方式必须是指定方式 | |
Path | 请求路径必须符合指定规则 | |
Query | 请求参数必须包含指定参数 | |
RemoteAddr | 请求者的 ip 必须是指定范围 | |
Weight | 权重处理 |
路由过滤器 GatewayFilter
GatewayFilter 是网关种提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理
过滤器工厂 GatewayFilterFactory
Spring 提供了 31 种不同的路由过滤器工厂
名称 | 说明 |
AddRequestHeader | 给当前请求添加一个请求头 |
RemoveRequestHeader | 移除请求中的一个请求头 |
AddResponseHeader | 给响应结果中添加一个响应头 |
RemoveResponseHeader | 从响应结果中移除有一个响应头 |
RequestRateLimiter | 限制请求流量 |
。。。 |
给所有进入 userservice 的请求添加一个请求头:Truth=whz is freaking awesome!
实现方式:在 gateway 中修改 application.yml 文件,给 userservice 的路由添加过滤器:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
filters:
- AddRequestHeader=Truth,whz is freaking aowsome!
如果要对所有的路由都生效,则可以将过滤器工厂写到 default 下
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
default-filters:
- AddRequestHeader=Truth,whz is freaking aowsome!
全局过滤器 GlobalFilter
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与 GatewayFilter 的作用一样。
区别在于 GatewayFilter 通过配置定义,处理逻辑是固定的。而 GlobalFilter 的逻辑需要自己写代码实现。
定义方式是实现 GlobaFilter 接口
定义全局过滤器,拦截并判断用户身份
- 参数中是否有 authorization
- authorization 参数值是否为 admin
如果同时满足则放行,否则拦截
//@Order(-1) //权重 越小越早执行
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1、获取请求参数
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> params = request.getQueryParams();
//2、获取参数中的 authorization 参数
String auth = params.getFirst("authorization");
//3、判断参数值是否等于 admin
if ("admin".equals(auth)) {
//4、是,放行
return chain.filter(exchange);
}
//5、否,拦截
//5.1、设置状态码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//5.2、拦截请求
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -1;
}
}
过滤器执行顺序
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和 DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后一次执行每个过滤器
- 每一个过滤器都必须指定一个 int 类型的 order 值,order 值越小,优先级越高,执行顺序越靠前
- GlobalFilter 通过实现 Ordered 接口,或者添加 @Order 注解来指定 order 值,由我们自己指定
- 路由过滤器和 defaultFilter 的 order 由 Spring 指定,默认是按照声明顺序从 1 递增
- 当过滤器的 order 值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter 的顺序执行(defaultFilter最先,然后是局部的路由过滤器,最后是全局过滤器)
跨域问题处理
跨域:域名不一致就是跨域
- 域名不同
- 域名相同,端口不同
跨域问题:浏览器禁止请求的发起者与服务端发生跨域 ajax 请求,,请求被浏览器拦截的问题
解决方案:CORS
网关处理跨域采用的同样是 CORS 方案,并且只需要简单配置即可实现:
spring:
cloud:
gateway:
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决 options 请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
- "http://www.leyou.com"
allowedMethods: # 允许的跨域 ajax 的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带 cookie
maxAge: 360000 # 这次跨域检测的有效期