比如对于一个订单系统,订单信息里面有产品的id,但是没有详细的产品信息,产品详细信息单独作为一个微服务向外提供接口, 此时订单系统就可以通过产品id发送http请求调用产品详细信息系统获取产品的详细信息。

1.采⽤Spring 提供的RestTemplate
1.定义RestTemplate
java
@Configuration
public class BeanConfig {
@Bean //创建一个spring提供的restTemplate类,进行http请求
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
使用url进行远程服务调用
java
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
//拼接url
String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();
//传入要访问的url和要返回的类型
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
RestTemplate是从Spring3.0开始支持的一个http请求工具,它是一个同步的REST API客户端,提供了常见的REST请求方案的模版
什么是REST
REST:表现层资源状态转移
1.资源:网络上所有的事务都可以抽象为资源,每一个资源都有一个唯一的资源标识符(URL)
2.表现层:资源的表现形式,比如文本作为资源,可以用txt格式表现,也可以通过HTML,XML,JSON等格式表现,甚至以二进制的格式表现。
3.状态转移:访问url,也就是客户端和服务器的交互过程,客户端用到的手段,只能是HTTP协议,这个过程中,可能会涉及到数据状态变化,比如对数据的增删改查,都是状态的转移。
REST是一种设计风格,指资源在网络中以某种表现形式进行状态转移。
REST描述的是在网络中Client和Server的一种交互形式,REST本身不实用,实用的是如何设计RESTful API(REST风格的网络接口)
RestTemplate 是Spring提供, 封装HTTP调⽤, 并强制使⽤RESTful⻛格. 它会处理HTTP连接和关闭, 只需要使⽤者提供资源的地址和参数即可。
缺点:
1.操作方式繁琐,RESTful API通常根据GET、POST、PUT、DELETE来区别对资源的操作动作,但是HTTP Method并不可直接诶见到,需要通过抓包工具才能观察,如果把动作放在URL上反而更加直观,更利于团队的理解和交流。
2.一些浏览器对GET,POST之外的请求支持不太友好,需要额外处理
3.过分强调资源,而实际业务需要可能比较复杂,并不能单纯使用增删改查就能满足需求,强行使用RESTful API 会增加开发难度和成本
2.服务注册/服务发现Eureka
Eureka的主要目的就是解决上面url写死的问题,解决url变动对程序的影响
1.搭建eureka服务
1.创建一个eureka项目
2.引入eureka依赖
java
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
3.配置eureka配置文件
java
server:
port: 10010
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
client:
fetch-registry: false # 表示是否从Eureka Server获取注册信息,默认为true.因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,这里设置为false
register-with-eureka: false # 表示是否将自己注册到Eureka Server,默认为true.由于当前应用就是Eureka Server,故而设置为false.
service-url:
# 设置与Eureka Server的地址,查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4.开启eureka服务
java
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
访问eureka

2.服务注册
1.加入Eureka的依赖
java
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.修改配置信息
java
#Eureka Client
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10010/eureka/
java
spring:
application:
name: product-service
3.启动,测试

3.服务发现
1.加入eureka依赖
java
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.修改配置信息
java
spring:
application:
name: product-service
java
spring:
application:
name: order-service
3.修改远程调用的代码
java
@Slf4j
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
@Autowired //获取服务发现
private DiscoveryClient discoveryClient;
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
//拼接url
// String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();
//从eureka中获取服务列表,通过应该名获取,服务可能会有多个
List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
String uri = instances.get(0).getUri().toString();
String url = uri + "/product/" + orderInfo.getProductId();
log.info("远程调用的url: " + url);
//传入要访问的url和要返回的类型
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
4.启动,测试


3.负载均衡
增加两个订单服务


java
//从eureka中获取服务列表,通过应该名获取,服务可能会有多个
List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
String uri = instances.get(0).getUri().toString();
String url = uri + "/product/" + orderInfo.getProductId();
这里获取到实例列表之后,只使用0号下标的服务实例,就会导致所有服务请求都会打到同一个服务程序上,这样对于分布式系统的集群服务来说就没有意义了。
可以看到每一次访问到的都是9091
1.取余
请求计数器%实例数
用一个计数器记录请求次数然后对实例数进行求余运算。

java
@Slf4j
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
@Autowired //获取服务发现
private DiscoveryClient discoveryClient;
//计数器
private AtomicInteger count = new AtomicInteger(1);
//不拿出来的话,每次获得的instances列表顺序就不固定,取余就没有意义了。
private List<ServiceInstance> instances;
@PostConstruct
public void init() {
instances = discoveryClient.getInstances("product-service");
}
// public OrderInfo selectOrderById(Integer orderId) {
// OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
// //拼接url
//// String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();
// //从eureka中获取服务列表,通过应该名获取,服务可能会有多个
// List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
// String uri = instances.get(0).getUri().toString();
// String url = uri + "/product/" + orderInfo.getProductId();
// log.info("远程调用的url: " + url);
//
// //传入要访问的url和要返回的类型
// ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
// orderInfo.setProductInfo(productInfo);
// return orderInfo;
// }
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
//拼接url
// String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();
int index = count.getAndIncrement() % instances.size();
String uri = instances.get(index).getUri().toString();
String url = uri + "/product/" + orderInfo.getProductId();
log.info("远程调用的url: " + url);
//传入要访问的url和要返回的类型
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
存在的问题:当实例发生变化的时候,程序感知不到
2.LoadBalance
负载均衡:按照一定的规则合理分配负载
SpringCloud LoadBalance
1.添加注解
@Configuration
public class BeanConfig {
@Bean //创建一个spring提供的restTemplate类,进行http请求
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
添加LoadBalanced注解
2.修改远程调用代码,把IP和端口号改成应用名
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
String url = "http://product-service/product/" + orderInfo.getProductId();
log.info("远程调用的url: " + url);
//传入要访问的url和要返回的类型
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
url部分ip加端口号换成被调用者的服务名称
负载均衡策略
1.轮询:轮流执行
2.随机:随机选取执行
其他策略需要自己实现
注册中心的其他实现------Nacos
下载完成之后,修改nacos的配置


修改集群模式为单机模式
启动后:http://127.0.0.1:8848/nacos 访问nacos
使用:
1.引入spring-cloud-alibaba的依赖
<properties>
<spring-cloud-alibaba.version>2022.0.0.0-RC2</spring-cloud-alibaba.version>
</properties>
<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>
2.引入Nacos相关的依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
3.修改配置
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
@Configuration
public class BeanConfig {
@Bean //创建一个spring提供的restTemplate类,进行http请求
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
4.远程调用
5.测试
nacos可以在不改变程序的情况下让服务下线

使用nacos操作服务下线只是不会再访问这个程序,但是程序本身并没有下线,可以单独对这个服务访问
控制流量访问大小:
先在nacos上设置权重,权重占总权重的比例就代表访问流量,但是spring-cloud有自己的负载均衡策略,想要使用nacos自己的负载均衡策略,要给订单服务加上配置
spring:
cloud:
loadbalancer:
nacos:
enabled: true
同集群优先访问,配置集群名称
spring
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
cluster-name: BJ
Nacos负载均衡
1.服务上线/下线
2.配置服务权重(非严格)
3.配置同集群优先访问
Nacos健康检查
客户端主动上报机制:
客户端通过心跳上报方式告知服务器(nacos注册中心)健康状态,默认心跳间隔5秒
nacos会在超过15秒未收到心跳后将实例设置为不健康状态,超过30秒将实例删除
服务器反向探测机制:
nacos主动探知客户端健康状态,默认间隔为20秒
健康检查失败后实例会被标记为不健康,不会被立即删除
客户端默认为临时实例,采用主动上报机制,永久实例采用反向探测机制
配置永久实例
spring:
cloud:
nacos:
discovery:
ephemeral: false # 设置为⾮临时实例
Nacos环境隔离
nacos可以配置不同的环境,来达到相互隔离的效果

在命令空间中创建不同的环境,然后复制命名空间ID进行配置
spring:
cloud:
nacos:
discovery:
namespace: 34edd3c4-6a9d-4a11-af4b-24e8f8207042
Nacos配置中心
配置中⼼就是对配置项进⾏统⼀管理. 通过配置中⼼, 可以集中查看, 修改和删除配置, ⽆需再逐个
修改配置⽂件. 提⾼效率的同时, 也降低了出错的⻛险
1.添加配置

选择yml格式

2.引入相关依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- SpringCloud 2020.*之后版本需要引⼊bootstrap-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
3.修改配置文件
配置bootstrap.properties(或者yml格式)
spring.application.name=product-service
spring.cloud.nacos.config.server-addr=110.41.51.65:10020
获取配置
@RefreshScope
@RestController
public class NacosController {
@Value("${nacos.config}")
private String nacosConfig;
@RequestMapping("/getConfig")
public String getConfig() {
return "从nacos获取配置项nacos.config: " + nacosConfig;
}
}
配置配置空间的命令空间,默认访问的是public空间

复制ID
spring:
application:
name: product-service
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: b6be0faa-ff1f-419e-acbf-e64e1cf9a76b
完成命令空间修改
data id

spring会监听了以下配置项,但是优先级会不一样

Nacos和Eureka的区别
共同点:都支持服务注册和服务拉取
区别:
1.功能
nacos除了服务发现和注册之外,还提供了配置中心,流量管理和DNS服务等功能
2.CAP理论
Eureka遵循AP原则,Nacos可以切换AP和CP模式,默认为AP
Nacos根据配置识别AP或者CP模式,如果注册Nacos的Client的节点是临时节点,那么Nacos对这个Client节点的效果就是AP,反之是CP。AP和CP可以同时混合存在
3.服务发现
Eureka:基于拉模式,Eureka Client会定期从Server拉取服务信息,有缓存,默认每30秒拉取一次
Nacos:基于推送模式,服务列表有变化时实时推送给订阅者,服务端和客户端保持心跳连接
OpenFeign
RestTemplate存在的问题

1.需要拼接URL,灵活性高,但是封装臃肿,URL复杂时,容易出错
2.代码可读性差,风格不统一
微服务之间的通信方式,通常有两种:RPC和HTTP
在spirngcloud中,默认是使用HTTP来进行微服务的通信,最常用的实现形式有两种:
RestTemplate
OpenFeign
OpenFeign的使用
1.引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.通过注解,开启OpenFeign的功能
//开启feign功能
@EnableFeignClients
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
3.编写客户端
//指定调用的服务
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
//指定调用的url
@RequestMapping("/{productId}")
ProductInfo getProductInfo(@PathVariable Integer productId);
}
4.修改远程调用
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductApi productApi;
@Autowired
private RestTemplate restTemplate;
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
//拼接url
// String url = "http://product-service/product/" + orderInfo.getProductId();
// //传入要访问的url和要返回的类型
// ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
ProductInfo productInfo = productApi.getProductInfo(orderInfo.getProductId());
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
5.测试
Feign传递参数
对于普通参数要加@RequestParam注解
//feign通过requestParam来接收参数,不可以省略
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
对于传递对象要加@SpringQueryMap
@RequestMapping("/p3")
public String p3(@SpringQueryMap ProductInfo productInfo);
对于传递json跟springMVC一样加@RequestBody
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo);
OpenFeign的最佳实践
1.继承的方式
将共用的方法单独抽取出来一个接口
public interface ProductInterface {
//指定调用的url
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable Integer productId);
//feign通过requestParam来接收参数,不可以省略
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
@RequestMapping("/p3")
public String p3(@SpringQueryMap ProductInfo productInfo);
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo);
}
其他程序去继承或实现即可
2.抽取的方式
将调用方整个openfeign的接口都抽取出来,打成jar包
//指定调用的服务
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
//指定调用的url
@RequestMapping("/{productId}")
ProductInfo getProductInfo(@PathVariable Integer productId);
//feign通过requestParam来接收参数,不可以省略
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
@RequestMapping("/p3")
public String p3(@SpringQueryMap ProductInfo productInfo);
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo);
}
调用方要加上调用的客户端,让spring能够扫描到
//开启feign功能 ,指定客户端让spring扫描product-api下的包
@EnableFeignClients(clients = {ProductApi.class})
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
统一网关服务------GateWay
网关核心功能
1.权限控制
2.动态路由
3.负载均衡
4.限流
网关开发
1.创建项目
2.引入网关相关的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</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-loadbalancer</artifactId>
</dependency>
3.写启动类
4.配置文件
server:
port: 10030
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: order-service #路由规则id,随便起,不重复即可
uri: lb://order-service/ #路由的uri
predicates:
- Path=/order/**,/feign/** #符合这个规则,则路由到上面的服务
- id: product-service
uri: lb://product-service/
predicates:
- Path=/product/**
5.测试
Predicate
Predicate是Java 8提供的⼀个函数式编程接⼝, 它接收⼀个参数并返回⼀个布尔值, ⽤于条件过滤, 请求参数的校验
比如判断一个字符串是否为空:
public class StringPredicate implements Predicate<String> {
@Override
public boolean test(String s) {
return s == null || s.isEmpty();
}
}
public class PredicateTest {
@Test
public void test1() {
Predicate<String> predicate = new StringPredicate();
System.out.println(predicate.test(""));
System.out.println(predicate.test("aa"));
}
@Test
public void test2() {
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String s) {
return s == null || s.isEmpty();
}
};
System.out.println(predicate.test(""));
System.out.println(predicate.test("aa"));
}
@Test
public void test3() {
Predicate<String> predicate = s -> s == null || s.isEmpty();
System.out.println(predicate.test(""));
System.out.println(predicate.test("aa"));
}
/**
* negate
* 非
*/
@Test
public void test4() {
Predicate<String> predicate = a -> a.equals("aa");
System.out.println(predicate.negate().test(""));
System.out.println(predicate.negate().test("aa"));
}
/**
* or
* 判断字符串是否为aa或bb
*/
@Test
public void test5() {
Predicate<String> predicate1 = a -> "aa".equals(a);
Predicate<String> predicate2 = b -> "bb".equals(b);
System.out.println(predicate1.or(predicate2).test(""));
System.out.println(predicate1.or(predicate2).test("aa"));
}
/**
* and
* 判断字符串不为空,且由数字组成
*/
@Test
public void test6() {
Predicate<String> predicate1 = s -> s != null && !s.isEmpty();
Predicate<String> predicate2 = s -> s != null && s.chars().allMatch(Character::isDigit);
System.out.println(predicate1.and(predicate2).test(""));
System.out.println(predicate1.and(predicate2).test("aa"));
System.out.println(predicate1.and(predicate2).test("1234"));
}
}
Route Predicate Factories
Route Predicate Factories (路由断⾔⼯⼚, 也称为路由谓词⼯⼚, 此处谓词表⽰⼀个函数), 在Spring
Cloud Gateway中, Predicate提供了路由规则的匹配机制.
我们在配置⽂件中写的断⾔规则只是字符串, 这些字符串会被Route Predicate Factory读取并处理,
变为路由判断的条件
Filter
Predicate决定了请求由哪⼀个路由处理, 如果在请求处理前后需要加⼀些逻辑, 这就是Filter(过滤器)的作⽤范围了.
Filter分为两种类型: Pre类型和Post类型.
Pre类型过滤器: 路由处理之前执⾏(请求转发到后端服务之前执⾏), 在Pre 类型过滤器中可以做鉴权, 限流等.
Post类型过滤器: 请求执⾏完成后, 将结果返回给客⼾端之前执⾏.
GatewayFilter: 应⽤到单个路由或者⼀个分组的路由上.
GlobalFilter: 应⽤到所有的路由上, 也就是对所有的请求⽣效
添加filter



限流算法
1.固定窗口(假设流量限制:每分钟1000次)

可能在59秒的时候突然来了1000次请求,和61秒的时候突然来了1000个请求,此时对于突然流量处理不好
2.滑动窗口

固定时间段进行滑动,但是滑动也需要时间间隔,如果间隔是1分钟,就成了固定窗口,这种算法对于突发流量也处理不好
3.漏桶算法

一开始没有流量,突然有大流量,也只能以固定速率处理
4.令牌桶算法

有请求进行访问的时候,需要从令牌桶里获取令牌才能进行访问
Default Filters
前⾯的filter添加在指定路由下, 所以只对当前路由⽣效, 若需要对全部路由⽣效, 可以使⽤
spring.cloud.gateway.default-filters 这个属性需要⼀个filter的列表
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Red, Default-Blue
- PrefixPath=/httpbin
GlobalFilter
GlobalFilter是Spring Cloud Gateway中的全局过滤器, 它和GatewayFilter的作⽤是相同的.
GlobalFilter 会应⽤到所有的路由请求上, 全局过滤器通常⽤于实现与安全性, 性能监控和⽇志记录等相 关的全局功能.
Spring Cloud Gateway 内置的全局过滤器也有很多, ⽐如:
Gateway Metrics Filter: ⽹关指标, 提供监控指标
Forward Routing Filter: ⽤于本地forword, 请求不转发到下游服务器
LoadBalancer Client Filter: 针对下游服务, 实现负载均衡
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
添加配置:


访问:

自定义GatewayFilter
传递参数类
@Data
public class CustomConfig{
private String name;
}
自定义
@Slf4j
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> implements Ordered {
//需要接收的参数
public CustomGatewayFilterFactory() {
super(CustomConfig.class);
}
@Override
public GatewayFilter apply(CustomConfig config) {
return new GatewayFilter() {
/**
* ServerWebExchange:HTTP请求和响应的契约信息,提供了对HTTP请求和访问,可以获取HTTP请求和响应的信息
* GatewayFilterChain:过滤器链
* Mono Reactor的核心类,数据流的发布者,最多触发一个事件,异步完成任务时发出通知
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//pre类型 执行请求 Post类型
log.info("Pre Filter, config:{}", config);
return chain.filter(exchange).then(Mono.fromRunnable(() ->{
log.info("Post Filter......");
}));//执行请求
//Mono.fromRunnable()创建一个包含Runnable元素的数据流
}
};
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
添加配置

自定义GlobalFilter
@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("Pre GlobalFilter......");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("Post Global Filter");
}));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
不需要额外的配置,全局生效