spring-cloud服务远程调用(Eureka、Nacos、OpenFeign)

比如对于一个订单系统,订单信息里面有产品的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;
    }
}

不需要额外的配置,全局生效

相关推荐
a***97682 小时前
从MySQL迁移到PostgreSQL的完整指南
数据库·mysql·postgresql
c***72742 小时前
【Redis系列】RedisTemplate的使用与注意事项
数据库·redis·缓存
q***48412 小时前
【Mysql】:如何恢复误删的数据?
数据库·mysql
*翊墨*2 小时前
达梦数据库Linux安装
linux·数据库·excel
h***38182 小时前
SQL 注入漏洞原理以及修复方法
网络·数据库·sql
青春:一叶知秋3 小时前
【Redis存储】渐进式遍历和数据库管理
数据库·redis·缓存
不会c嘎嘎3 小时前
MySQL -- 基本查询
数据库·mysql
p***h6433 小时前
从无到有:MongoDB事务的演进之路
数据库·mongodb
E***U9454 小时前
MySQL自然语言处理应用
数据库·mysql