栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringCloud精简入门(注册中心Eureka、负载均衡、熔断器、远程调用、网关)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringCloud精简入门(注册中心Eureka、负载均衡、熔断器、远程调用、网关)

文章目录

死亡三连问

SpringCloud 是啥?微服务 是啥?为啥 需要微服务? 一些概念性的东西

SpringCloud的主要应用过程 开始操作

完整的文件结构搭建基础框架Eureka注册中心Ribbon负载均衡Hystrix 熔断器Feign远程调用

Feign的熔断器 GateWay网关

路由前缀过滤器

死亡三连问 SpringCloud 是啥?

官方一点的说法:SpringCloud是在SpringBoot基础上构建的,用于快速构建分布式系统的通用模式的工具集(注意了:重点在于它只是一个工具),因此我把它简单的理解为构建微服务的一个工具SpringCloud主要就是将各家较为成熟的服务框架组合起来,再通过SpringBoot风格进行封装,屏蔽掉了原先复杂的配置和实现原理,最终让开发者可以简单方便且易维护的开发的分布式系统开发工具包那么SpringCloud和云到底有啥关系呢,其实由于使用SpringCloud开发的应用程序非常适合在Docker或Pass上部署,所以由SpringCloud构建的程序又叫做云原生应用,可以简单的理解为面向云环境的软件架构 微服务 是啥?

微服务,见名知义,就是较小的服务单元,与微服务相对的单体架构单体架构 单体架构就是将所有的业务场景(表现层、业务逻辑层、数据访问层)都放在一个工程中,部署在一台服务器上那么相对单体架构的微服务架构就是将单一程序开发成一个微服务每个微服务运行在自己的进程中,并使用轻量级的机制通信,通常是HTTP RESTFUL API(也可以采用消息队列来通信)。这些服务围绕着业务能力来划分,并通过自动化部署机制来独立部署(如Jenkins),降低出错频率这些服务可以使用不同的编程语言,不同的数据库,以保证最低限度的集中式管理(例如Eureka,Zookeeper)。 为啥 需要微服务?

一般呢,小的公司是用不到微服务的,怎么样?是不是这个技术瞬间高大上了起来微服务架构的出现 当然是由于单体架构不给力导致的在业务的逻辑越来越复杂的时候,单体架构的代码可读性和可维护性就会越来越低,面对海量用户时,单体架构系统的并发能力也会随之下降那么就需要使用微服务架构将一个复杂业务拆分成多个小业务,将复杂问题不断拆分成一个个简单的小问题微服务是分布式系统,业务与业务之间完全解耦,也就是说一个业务出现了问题,不会影响其他的业务,可以显著提高生产力同时当我们需要增加业务时,可以根据业务再拆分,具有极强的横向扩展能力,面对高并发的场景可以将服务集群化部署,加强系统负载能力因为现实中,一个工程可能由多个服务单元组成,但是多个服务可能使用不同的技术或语言实现,微服务可以让这种融合更自由 一些概念性的东西 SpringCloud的主要应用过程

首先展示一下源自网络的springCloud组件架构图:

结合这张图来说一下springCloud的应用过程:

所有的请求(移动端、客户端有一个算一个)都统一通过网关服务(Zuul Proxy)来访问内部服务网关接收到请求后,从注册中心(Eureka)获取可用的微服务,所有的微服务都需要在注册中心进行注册其中Ribbon主要用于负载均衡,它可以帮助判断并分发具体的请求到后端的具体应用服务实例,起到了分发压力的作用微服务(services)之间通过Feign进行通信处理业务,保持服务的一致性Hystrix负责处理服务超时熔断Turbine监控 服务间的调用和熔断的相关指标

从上面这张图中也可以看出SpringCloud主要的组件,解释一下这些组件都具体是做什么用的:

Netflix Eureka 该组件负责服务的注册与发现,Eureka体系包括:服务注册中心、服务提供者、服务消费者Netflix Hystrix 熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点。比如突然某个服务出现了故障,当请求多时,就会发生严重的阻塞影响整体服务响应。Hystrix会及时发现某个不在状态不稳定服务,将其他服务调过来响应Netflix Zuul 该组件是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul相当于设备和Netflix流应用的Web网站后端所有请求的前门Netflix Archaius 该组件用于配置管理API,包含一系列配置管理API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。原理是每隔60s从配置源读取一次内容,这样修改配置文件后不需要重启服务就可以使修改后的内容生效,前提是使用archaius的API来读取Spring Cloud Config 配置中心,配置管理工具包,可以将配置放在远程服务器,集中化管理集群配置,目前支持本地存储、GIt以及SubVersion。方便以后统一管理、升级装备Spring Cloud Bus 事件、消息总线,用于在集群中传播状态变化,可以与Spring Cloud Config联合实现热部署Spring Cloud for Cloud Foundry 一个开源Paas云平台,支持多种框架、语言、运行时环境、云平台以及应用服务,使开发人员可以在几秒钟内进行应用程序的部署和扩展,无需担心任何基础架构问题Spring Cloud Cluster 提供在分布式系统中的集群所需要的基础功能支持,如选举、集群的状态一致性、全局锁、tokens等常见状态模式的抽象和实现Spring Cloud Consul Consul是一个支持多数据中心分布式高可用的服务发现和配置共享的服务软件,consul是一个服务发现与配置工具,与Docker容器可以无缝集成 开始操作 完整的文件结构

整个项目分为四个模块consumer、provider、euraka、gateway

consumer结构

eureka结构

gateway文件结构

provider文件结构

搭建基础框架

IDEA创建maven项目
更改父级Pom文件
完整的父级Pom文件如下:



    4.0.0

    org.example
    springCloud
    pom
    1.0-SNAPSHOT

    
        UTF-8
        UTF-8
        1.8
        1.8
        1.8
        2.8.0
        2.9.5
        2.9.2
        3.1.0
        5.7.19
        1.1.10
    
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.8.RELEASE
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-test
            test
        
        
            junit
            junit
            4.13.2
            test
        
        
            cn.hutool
            hutool-all
            ${hutool.version}
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                Greenwich.SR6
                pom
                import
            
        
    


主要的部分就是Properties和dependency两部分,本次测试项目中并没有使用到数据库,但实际业务需要使用到,所以记得添加

创建生产者Provider
在项目的父级目录上右键new->module->maven
更改provider的pom文件



    
        springCloud
        org.example
        1.0-SNAPSHOT
    
    4.0.0

    provider

    
        8
        8
    

    
        
            org.projectlombok
            lombok
        
    


添加provider的基础文件

Controller层代码如下

@RestController
@RequestMapping("provider")
public class ProviderController {
    //使用Map模拟数据库
    private Map map;

    {
        map = new HashMap<>();
        map.put(1, new User(1, "Johnny"));
        map.put(2, new User(2, "Timmy"));
    }

    @GetMapping("/findAll")
    public List findAll() {
        System.out.println("Provider::findAll");
        List list = new ArrayList<>();
        for (Map.Entry users : map.entrySet()) {
            list.add(users.getValue());
        }
        return list;
    }

    @GetMapping("/findById/{id}")
    public User findById(@PathVariable("id") Integer id) {
        return map.get(id);
    }
}

pojo层代码如下

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String name;
}

ProviderApplication启动类代码如下

@SpringBootApplication
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class);
    }
}

Provider启动测试
启动provider的启动类,使用postman测试
如下即provider基础编写完成

添加消费者Consumer模块
步骤同Provider
更改consumer的pom文件



    
        springCloud
        org.example
        1.0-SNAPSHOT
    
    4.0.0

    consumer

    
        8
        8
    

    
        
            org.projectlombok
            lombok
        
    


添加Consumer基础代码

Controller层代码如下:

@RestController
@RequestMapping("consumer")
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;
    @GetMapping("/findAll")
    public List findAll() {

        

        //provider的请求url
        System.out.println("Consumer:: findAll");
        String baseUrl = "http://localhost:8081/provider/findAll";
        List list = restTemplate.getForObject(baseUrl, List.class);
        return list;
    }

    @GetMapping("/findById/{id}")
    public User findById(@PathVariable("id") Integer id) {
        String baseUrl = "http://localhost:8081/provider/findById/" + id;
        User user = restTemplate.getForObject(baseUrl, User.class);
        return user;
    }
}

pojo层代码如下
和provider的完全一致

Consumer启动类代码如下

@SpringBootApplication
public class ConsumerApplication {
    //springBoot没有主动注入RestTemplate,需要我们手动注入
    @Autowired
    private RestTemplateBuilder templateBuilder;

    @Bean
    public RestTemplate restTemplate() {
        return templateBuilder.build();
    }
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class);
    }
}

Consumer代码启动测试
因为同时Provider代码也必须是运行状态,所以需要先给provider改一下运行端口号
provider的application.yml文件中添加
server.port: 8081
这里有个idea小技巧,为了看到所有的模块运行情况,我们需要底栏显示Services

找到本项目的.idea文件夹下面的workspace.xml文件,在文件末尾添加

 
    
  

后重启IDEA使之生效

启动consumer,provider

Eureka注册中心

创建一个子模块eureka
方式同consumer、provider
更改pom文件



    
        springCloud
        org.example
        1.0-SNAPSHOT
    
    4.0.0

    eureka

    
        8
        8
    

    
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-server
        
    


因为Eureka只是注册中心,没有实际业务,所以完整的文件结构如下

启动类EurekaApplication中代码

@SpringBootApplication
@EnableEurekaServer  //声明当前模块是一个Eureka服务端
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class);
    }
}

配置文件

server:
  # 实际业务不要用这个端口号,易被攻击
  port: 8761

#声明注册中心的url
eureka:
  client:
    service-url:
      defaultZone: http://0.0.0.0:8761/eureka
    # 关闭注册自己
    fetch-registry: false
    register-with-eureka: false

Done!! 启动程序,url访问localhost:8761

我们看到Instance currently registered with Eureka中空空如也,这是因为我们没有进行对微服务的注册,重申一遍:所有的微服务都需要注册到注册中心

将provider注册到注册中心
1、在Provider的pom文件中增加eureka client的依赖


            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

2、启动类上加注解

@EnableDiscoveryClient
这里想说一下@EnableDiscoveryClient和@EnableEurekaClient
从表面意思上看,似乎是@EnableEurekaClient更应该用在这里,但实际上这个注解已经是老版了,应用场景单一

并且使用后会在界面显示这样的信息
3、配置文件增加配置

server:
  port: 8081

# 服务名称
spring:
  application:
    name: provider

#eureka配置
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka

启动后显示eureka页面显示已经注册了一个实例

将consumer注册到注册中心
步骤同provider,相同部分不再赘述

consumer中通过注册中心的方式调用provider
ConsumerController改为如下内容

@RestController
@RequestMapping("consumer")
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/findAll")
    public List findAll() {

        
        //通过discoveryClient获得的是一个实例列表,因为通常我们会做负载均衡,所以不止一个实例
        System.out.println("Eureka:: Consumer");
        List providers = discoveryClient.getInstances("provider");
        ServiceInstance serviceInstance = providers.get(0);
        URI uri = serviceInstance.getUri();
        System.out.println(uri);
        String scheme = serviceInstance.getScheme();
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        //拼接url
        String eurekaUrl = scheme + "://" + host + ":" + port + "/provider/findAll";
        System.out.println(eurekaUrl);
        List list = restTemplate.getForObject(eurekaUrl, List.class);




        return list;
    }

    @GetMapping("/findById/{id}")
    public User findById(@PathVariable("id") Integer id) {
        String baseUrl = "http://localhost:8081/provider/findById/" + id;
        User user = restTemplate.getForObject(baseUrl, User.class);
        return user;
    }
}

运行Consumer后使用postman调用findAll接口,控制台显示如下

Ribbon负载均衡

首先我们需要启动两个provider
只需在启动了一个provider后,更改端口号再启动一个provider即可
启动完成后,在注册中心会看到有两个provider
在Consumer的RestTemplate上面添加@LoadBalanced
该注释是开启负载均衡的作用

@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
    //springBoot没有主动注入RestTemplate,需要我们手动注入
    @Autowired
    private RestTemplateBuilder templateBuilder;

    @Bean
    @LoadBalanced  //开启负载均衡 只增加了这里请注意,其余都不变
    public RestTemplate restTemplate() {
        return templateBuilder.build();
    }
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class);
    }
}

在Consumer的Controller层更改访问provider的形式
开启负载均衡之后,可以直接使用实例名称进行访问接口,而无需通过discoveryClient自己手动获取

以下仅显示Conusmer的findAll的更改,其余模块没有更改

@GetMapping("/findAll")
    public List findAll() {
        
        //第一个provider是实例名称,即在provider配置文件中的name
        String balancedUrl = "http://provider/provider/findAll";
        List list = restTemplate.getForObject(balancedUrl, List.class);
        return list;
    }

启动所有微服务后,访问consumer的接口


可以看到是拿到了结果,那么我们怎么知道到底是访问了那个Provider呢,虽然实际是不用关心的,但在学习阶段我们最好关心以下
通过打印日志可以看到,刚刚请求的 8082端口的provider
PS:我们还可以通过配置文件更改负载均衡的策略
比如当前默认的是轮询机制,我们可以配置成随机之类的
研究了一下,好像是如果要针对某个服务配置负载均衡策略,就需要在配置文件里增加

#RandomRule 随机
#RoundRobinRule 轮询
#provider是服务名,记得切换称自己的
provider:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

如果设置全局负载均衡策略,就需要在consumer的配置类中返回一个配置
像这样

  @Bean
    public IRule defaultLBStrategy(){
        return new RandomRule();
    }

!!!需要注意的是,两者不能同时用,用其中一个的时候,记得注释另一个

Hystrix 熔断器

插播! 插播! 紧急插播 Hystrix原理
先上手操作,再了解一下原理,还是蛮重要的

Consumer的Pom文件中增加Hystrix的依赖


            org.springframework.cloud
            spring-cloud-starter-netflix-hystrix
        

Conusmer的启动类添加注释开启熔断
因为我们这里只测试一个Provider的熔断,所以记得取消负载均衡的注释

Consumer的配置文件中设置熔断器的配置

# 配置熔断策略:
hystrix:
  command:
    default:
      circuitBreaker:
        # 原理分析中解释配置含义
        # 强制打开熔断器 默认false关闭的。测试配置是否生效
        forceOpen: false
        # 触发熔断错误比例阈值,默认值50%
        errorThresholdPercentage: 50
        # 熔断后休眠时长,默认值5秒
        sleepWindowInMilliseconds: 5000
        # 熔断触发最小请求次数,默认值是20
        requestVolumeThreshold: 10
      execution:
        isolation:
          thread:
            # 熔断超时设置,默认为1秒
            timeoutInMilliseconds: 2000

在Conusmer的Controller层编写降级的FallBack方法

@RestController
@RequestMapping("consumer")
public class ConsumerController {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @HystrixCommand(fallbackMethod = "findAllFallBack")
    @GetMapping("/findAll")
    public List findAll() {

        
        //通过discoveryClient获得的是一个实例列表,因为通常我们会做负载均衡,所以不止一个实例
//        System.out.println("Eureka:: Consumer");
        List providers = discoveryClient.getInstances("provider");
        ServiceInstance serviceInstance = providers.get(0);
        URI uri = serviceInstance.getUri();
        System.out.println(uri);
        String scheme = serviceInstance.getScheme();
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        //拼接url
        String eurekaUrl = scheme + "://" + host + ":" + port + "/provider/findAll";
        System.out.println(eurekaUrl);
        List list = restTemplate.getForObject(eurekaUrl, List.class);

        return list;
    }

    public List findAllFallBack() {
        System.out.println("Ops!! find All Fall Back");
        return null;
    }
}


!! 注意:FallBack方法的返回值必须和FindAll方法一致

在Provider的findAll方法中让线程睡几秒,触发熔断
因为我们配置的触发熔断时间为1秒,所以当请求返回超过1秒时就会触发熔断

熔断效果
在请求时,没有得到正确返回,触发了熔断

Cosumer的运行控制台输出了FallBack方法中的内容

PS:当我们需要所有的请求都共用一个FallBack方法时,可以配置全局熔断器
只需要两步
1、在Controller类(注意这里是类不是方法)上添加注解 @DefaultProperties(defaultFallback = “defaultFallback”)
2、在目标方法上添加注解 @HystrixCommand (注意不需要再次指定fallbackMethod)
ps:暗戳戳的说一句,感觉也没有省事到哪里去

Feign远程调用

老样子,先操作后理解
插播! 插播! Feign的插播

Consumer的pom文件中添加依赖


            org.springframework.cloud
            spring-cloud-starter-openfeign
        

Consumer启动类中添加开启Feign客户端的注释

@EnableFeignClients  //开启feign客户端

Consumer中编写Feign客户端
feign客户端在Consumer里就是一个仿照ProviderController中方法的接口,没有实质的方法实现

@FeignClient(value = "provider",path = "/provider")
public interface ConsumerClient {
    //伪装成ProviderController中的findAll
    @GetMapping("/findAll")
    List findAll() throws Exception;
}

Consumer中的Controller使用Feign发送请求

@RestController
@RequestMapping("consumer")
public class ConsumerController {
    //注入Feign客户端
    @Qualifier("com.dean.feign.ConsumerClient")
    @Autowired
    private ConsumerClient consumerClient;

    @GetMapping("/findAll")
    public List findAll() throws Exception {
        
        return consumerClient.findAll();
    }
}

启动后发送请求可正常获得结果
Feign的熔断器

是的,Feign里面也结合了熔断器,让熔断器的实现更加容易
如果需要进一步配置feign的熔断器,需要添加依赖


            io.github.openfeign
            feign-hystrix
            9.7.0
        

然后在配置文件里进行配置

feign:
  hystrix:
    enabled: true

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 15000
  threadpool:
    default:
      coreSize: 40
      maximumSize: 100
      maxQueueSize: 100

如不需要这些高级配置只需要按下列步骤进行即可

Consumer里实现ConsumerClient
我们之前写了一个接口ConsumerClient假装是ProviderController,那么当我们实现它的时候,它就成为了一个编写降级服务的类

@Component
public class ConsumerFallBack implements ConsumerClient {

    //feign Hystrix 服务降级方法
    @Override
    public List findAll() throws Exception {
        System.out.println("feign Hystrix");
        return null;
    }
}

Consumer的配置文件中编写Feign Hystrix的配置
因为Feign内置了熔断器,我们就不再需要熔断器本身的一些配置和注解了,只需要设置Feign的Hystrix配置即可

# feign 熔断器
feign:
  hystrix:
    enabled: true

Consumer的Feign客户端添加FallBack所在的类

@FeignClient(value = "provider",path = "/provider",fallback = ConsumerFallBack.class)
public interface ConsumerClient {
    //伪装成ProviderController中的findAll
    @GetMapping("/findAll")
    List findAll() throws Exception;

}

启动后,可以看到熔断器有效

Consumer的控制台这边可以看到输出了我们降级方法中的内容
GateWay网关

创建一个SpringBoot组件Gateway
方式同创建provider 和consumer
文件结构如下:

Gateway的pom文件中添加依赖
注意这里有个大坑,因为我们之前在父工程的中添加了spring-boot-starter-web的依赖,但是网关使用时不能有这个依赖,所以需要我们从父工程中移除,分别添加到使用到这个依赖的子工程中(在本项目里就是consumer、provider、eureka)
一定要移除,移除完成后在gateway的pom文件中添加如下依赖

        
            org.springframework.cloud
            spring-cloud-starter-gateway
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

编写Gateway启动类GateWayApplication
注意网关也只是个微服务,需要注册到注册中心

@EnableDiscoveryClient
@SpringBootApplication
public class GateWayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GateWayApplication.class);
    }
}

配置Gateway的配置文件Appliaction.yml
网关重在配置

server:
  port: 9000

# 服务名称
spring:
  application:
    name: gateway
  # 网关配置
  cloud:
    gateway:
      # 全局配置
      globalcors:
        cors-configurations:
          '[/**]': # 匹配所有请求
            allowedOrigins: "*" #跨域处理 *表示通配 允许所有的域 实际可以填写ip
            allowedMethods: # 支持的方法
              - GET
              - POST
              - PUT
              - DELETE
      # 网关路由配置
      routes:
        - id: provider-router # 路由id 唯一标识
          uri: lb://provider # 路由地址,动态路由  lb是一个网关的协议
          predicates: # 断言
            - Path=/provider/** # 请求url 多个使用逗号 即所有的/provider/**请求会交由uri中写的服务provider来处理

#eureka配置
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka

编写过滤器TokenFilter

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class TokenFliter implements GlobalFilter, Ordered {
    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("There is GateWay");

        return chain.filter(exchange);
    }

    //可能有多个网关,执行顺序由getOrder决定
    @Override
    public int getOrder() {
        return 0;
    }
}

完成后顺序启动Eureka、Provider、GateWay
可以看到注册中心出现了两个服务provider和gateway

本来我们访问provider的findAll方式是这样

也就是访问的是provider服务
但当我们配置了网关以后,可以将provider的真正服务端口号隐藏起来,访问网关服务来访问到findAll

网关控制台这边也可以看到我们通过TokenFliter中的fliter方法打印的字样
路由前缀

网关的配置文件中更改配置(隐藏前缀)
在gateway中可以通过配置路由的过滤器PrefixPath来实现映射路径的前缀添加,可以起到隐藏接口地址的作用,避免接口地址暴露
啥意思呢
就是以我们的provider来看,我们之前请求路径是/provider/findAll,但通过配置之后我们可以隐藏/provider,只访问findAll即可

predicates:
  - Path=/**
filters:
  - PrefixPath=/provider

测试

可以看到通过该配置,即隐藏了实际服务的端口号也隐藏了请求的前缀

网关的配置文件中更改配置(增加前缀)
在gateway中通过配置路由过滤器StripPrefix,实现映射路径中的地址的去除。
通过StripPrefix=1来指定路由中需要去除的前缀个数
例如我们通过访问路径/api/provider/findAll将会被路由解析到/provider/findAll

predicates:
  - Path=/api/**
filters:
  - StripPrefix=1

测试

PS:以上两个也可以都加

          predicates: # 断言
            - Path=/api/** # 
          filters:
            - StripPrefix=1
            - PrefixPath=/provider

但一定要注意StripPrefix和PrefixPath的顺序问题
写成现在这样的意思就是先去掉/api,再添加/provider,所以请求的时候可以直接用/api/findAll请求

过滤器

过滤器是网关的一个重要功能,实现了请求的鉴权,前面路由前缀的功能也是使用过滤器实现的

过滤器分为全局过滤器和局部过滤器
可以同时配置全局过滤器和局部过滤器

全局过滤器(实现GlobalFilter及Ordered)
例如上面所说的TokenFilter

@Component
public class TokenFliter implements GlobalFilter, Ordered {
    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("There is GateWay");

        return chain.filter(exchange);
    }

    //设置过滤器执行顺序,值越小,优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

自定义局部过滤器
有两种实现方式

直接实现GatewayFilter接口,重写里面的filter方法继承AbstractGatewayFilterFactory类,重写apply方法(实际上底层也是重写filter方法)
以下是第二种方式的代码示例

package com.dean.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;


//类名的格式必须为XXXGatewayFilterFactory
@Component
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory {
    public static final String AUTH_NAME = "name";

    //构造函数
    public AuthGatewayFilterFactory() {
        super(Config.class);
    }

    //如果要获得配置的-Auth的值,必须重写这个方法,否则拿不到
    @Override
    public List shortcutFieldOrder() {
        return Arrays.asList(AUTH_NAME);
    }


    //过滤逻辑
    @Override
    public GatewayFilter apply(AuthGatewayFilterFactory.Config config) {
        return (exchange, chain) -> {
            System.out.println("AuthGateWay Filter");
            System.out.println(config.name);
            //可以通过exchange拿到uri,进行拼接或处理  参考PrefixPathGateWayFilterFactory
            return chain.filter(exchange);
        };
    }

    //用于接收配置文件中的参数
    public static class Config {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

主要有几个重点:
1、类名必须是XXXGatewayFilterFactory
2、配置文件里的配置就是这个XXX
3、shortcutFieldOrder()方法必须重写,否则拿不到配置的值

gateway的yaml中路由处进行配置

      # 网关路由配置
      routes:
        #id是个列表 有多个路由配置时就写多个id及下面的配置,以下是完整的一个
        - id: provider-router # 路由id 唯一标识
          uri: lb://provider # 路由地址,动态路由  lb是一个网关的协议
          predicates: # 断言
            - Path=/**
#            - Path=/api/** # 请求url 多个使用逗号 即所有的/provider/**请求会交由uri中写的服务provider来处理
          # 添加filter后,会在predicates上的所有请求上添加/provider
          # 所以实际请求就不需要再写/provider了
          filters:
#            - StripPrefix=1
            - PrefixPath=/provider
            - Auth=name  #过滤器的名称取得是AuthGatewayFilterFactory去掉GatewayFilterFactory的部分

其实只是多了一个我们自己增加的-Auth

通过网关访问接口后控制台的输出

PS:当我们想要配置多个路由及每个路由配置自己的过滤器时
从配置中可以看出route下的-id是个列表的格式

彩蛋:GateWay自带的过滤器
gateway自带的过滤器有几十个,常见的有

过滤器名称说明
AddRequestHeader对匹配上的请求加上Header
AddRequestParameters对匹配上的请求路由
AddResponseHeader对从网关返回的响应添加Header
StripPrefifix对匹配上的请求路径去除前缀
PrefifixPath对匹配上的请求路径添加前缀

例如配置文件中配置

spring:
  application:
    name: gateway
  cloud:
    gateway:
      default-filters:
        - AddResponseHeader=ilove,web

请求后就可以看到响应头添加了ilove=web了

感兴趣的话,可以去官方文档看看

以上。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/735649.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号