SpringCloud微服务框架复习笔记

SpringCloud微服务框架复习笔记

什么是微服务架构?
微服务是一种软件开发技术,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够独立地部署到生产环境、类生产环境等。——Wikipedia

我们将在开发中将业务的所有功能集中在一个项目开发,打成一个包部署的架构方式成为单体架构,架构简单,部署成本低是它的优点。单体架构是一些简单的演示练习项目或低并发需求的个人项目中最常采用的架构方式。但其缺点是耦合度高,不便于大型项目的维护。

单体架构

分布式架构:根据业务功能进行拆分,每个业务模块作为独立项目开发,称为一个服务;优点:降低耦合,有利于服务升级扩展;缺点:架构复杂,难度大。

分布式架构

微服务是一种良好的分布式架构方案,它有以下特征

  • 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
  • 面向服务:微服务对外暴露业务接口
  • 自治:团队独立、技术独立、数据独立、部署独立
  • 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题

微服务架构

认识微服务架构

一个成熟的微服务架构解决方案将包含以下相关技术栈

微服务知识

  1. 注册中心
    微服务架构把单一应用程序划分成一组小的服务,每个服务运行在自己的进程中,服务之间通过网络调用进行通信。注册中心是微服务架构中的一个核心组件,它负责维护服务实例的注册和发现
    微服务架构中的服务实例是动态的,服务实例可能会经常添加、删除和修改。注册中心能够帮助服务消费者快速发现服务提供者,并且还能够监控服务提供者的健康状态

  2. 配置中心
    每个服务都有自己的配置文件,而一个上线的项目可能会有成百上千的服务,这些配置文件,我们不可能一个一个去修改,这个时候需要一个配置中心,它主要用来统一管理整个服务集群成千上百的配置,我们要变更某些配置,只要找到配置中心就可以了,它就会去找到对应的配置,实现配置的热更新。

  3. 网关
    所有的请求进来,并不能直接就去访问对应的服务,而是要通过一个网关服务,由它来路由到对应的服务

  4. 分布式缓存
    数据库层,由于数据库即使是集群部署,也很难抗住高并发,往往还需要一个缓存集群,把数据库的数据搬到内存中以提高访问效率。请求先查询缓存,缓存未命中的时候再去查询数据库。

  5. 分布式搜索
    对于一些复杂的数据的搜索、统计、分析,我们还需要使用搜索集群

  6. 消息队列
    在分布式架构中,还需要消息队列,因为一个业务往往会调用多个服务,但我们不能等到所有的服务都执行完成再返回响应数据,因为这样操作就类似串行执行,响应速度有所下降。这个时候可以使用消息队列,让服务发送消息通知其他服务去执行指定的任务,而自己则可以结束运行,提高响应速度。

  7. 分布式日志服务
    主要用来统计成百上千的服务的运行日志,方便系统出问题时的定位。

  8. 系统监控链路追踪
    可以实时监控整个服务集群的所有节点的运行状态

目前主流的微服务开发框架中,Dubbo、SpringCloud、SpringCloudAlibaba 在国内外市场拥有较高的占有率。下方是框架技术的对比图:

微服务技术对比

其中 SpringCloud 是国内使用最广泛的微服务框架技术。其官网为:https://spring.io/projects/spring-cloud。 SpringCloud 集成了各种微服务功能组件,并基于 SpringBoot 实现了这些组件的自动装配

一、微服务的注册中心

微服务技术依据各个服务功能分成不同的模块,而一项业务需要多个服务模块共同完成,这便少不了各模块接口间的互相调用。下面是不同微服务模块远程调用的一个实例:

微服务远程调用

首先浏览器发送请求到订单模块,订单模块在到数据库查询信息;这时候订单模块再发送请求到用户模块,用户模块在到数据库查询信息。

基于 RestTemplate 实现的服务器远程调用:

  1. 配置类(也可直接再启动类注入)中注册 RestTemplate。
/**
* 注入RestTemplate发送http请求获取数据
*
* @return new RestTemplate()
*/
@Bean
RestTemplate restTemplate() {
    return new RestTemplate();
}
  1. RestTemplate 服务远程调用
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 通过id查询订单
     *
     * @param orderId 订单编号
     * @return json
     */
    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.使用 RestTemplate 远程调用服务器接口
        String url = "http://localhost:8081/user/" + order.getUserId();
        User user = restTemplate.getForObject(url, User.class);
        // 3.封装数据
        order.setUser(user);
        // 4.返回 order 对象
        return order;
    }
}

考虑上述案例,我们将远程调用过程中的双方分别成为服务提供者服务消费者

  • 服务提供者:一次业务中被其它微服务调用的。(提供接口给其它微服务使用)
  • 服务消费者:一次业务中调用其他微服务。(调用其他微服务的接口
  • 提供者与消费者角色其实是相对的,一个服务可以同时是服务提供者和服务消费者。

Ⅰ、Eureka 注册中心

上述服务远程调用存在一些问题:

  • 服务消费者该如何获取服务提供者的地址信息
  • 如果有多个服务提供者,消费者该如何选择
  • 消费者如何得知服务提供者的健康状态

为了解决这些问题,SpringCloud 为我们提供了一个注册中心技术——Eureka

Eureka的作用

Eureka 是 SpringCloud 框架中的一个核心组件,它负责维护服务实例的注册和发现。服务提供者启动时会向 eureka 注册自己的信息,随后消费者则根据服务名称向 eureka 拉取提供者信息。若服务提供者存在多个,服务消费者将利用负载均衡算法,从服务列表中挑选一个。

动手实践

步骤一:搭建 Eureka 服务器

  1. 为项目新建一个模块,引入 eureka-server 依赖:
<!-- eureka服务器 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
  1. 编写启动类,添加 @EnableEurekaServer 注解:
@SpringBootApplication
@EnableEurekaServer//开启Eureka服务器
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}
  1. 添加 application.yml 文件,编写下面的配置
server:
  port: 10086 # 服务端口
spring:
  application:
    name: eurekaserver  # eureka的服务名称
eureka:
  client:
    service-url:  # eureka的地址信息
      defaultZone: http://127.0.0.1:10086/eureka

步骤二:为服务提供者和消费者进行服务注册

  1. 在要注册到 EurekaServer 的项目下引入 eureka-client 依赖:
<!-- eureka客户端依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  1. 在 application.yml 文件,编写注册至eureka的配置信息
Spring:
  application:
    name: userservice  # user的服务名称
eureka:
  client:
    service-url: # eureka的地址信息
      defaultZone: http://127.0.0.1:10086/eureka

步骤三:在服务消费者中实现服务发现(服务拉取)

  1. 在服务消费者的代码的代码中修改访问的url路径,用服务名代替ip、端口:
//定义服务提供者接口的url地址,通过 eureka 实例名称进行服务发现
String url = "http://userservice/user/" + order.getUserId();
  1. 在服务消费者的 RestTemplate 注入处添加 `` 注解,实现负载均衡
@Bean
@LoadBalanced//负载均衡,实现 eureka 注册中心服务发现
RestTemplate restTemplate() {
    return new RestTemplate();
}

启动相关微服务后,访问 Eureka WEB 服务器地址即可进入 Eureka 的管理界面。

Eureka项目演示
Eureka展示

负载均衡原理

SpringCloud 底层其实是利用了一个名为 Ribbon 的组件,来实现负载均衡功能的。

Ribbon负载均衡流程

其基本流程如下:

  • 拦截我们的 RestTemplate 请求,即http://userservice/user/1
  • RibbonLoadBalancerClient 会从请求 url 中获取服务名称,也就是 user-service
  • DynamicServerListLoadBalancer 根据 user-service 到 eureka 拉取服务列表
  • eureka 返回列表,localhost:8081、localhost:8082
  • IRule 利用内置负载均衡规则,从列表中选择一个,例如 localhost:8081
  • RibbonLoadBalancerClient 修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1,发起真实请求

Ribbon 的负载均衡规则是一个叫做 IRule 的接口来定义的,每一个子接口都是一种规则:

IRule子接口
Ribbon负载均衡策略

修改负载均衡规则的两种方式:

  1. 代码方式:在服务消费者中的启动类中,定义一个新的 IRule:
@Bean
public IRule randomRule(){
    return new RandomRule();
}
  1. 配置文件方式:在服务消费者的 application.yml 文件中,添加新的配置也可以修改规则:
userservice: # 给某个微服务配置负载均衡规则,这里是 userservice 服务
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则 

饥饿加载

Ribbon 默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:

ribbon:
  eager-load:
    enabled: true  # 开启饥饿加载
    clients: userservice  # 指定对userservice这个服务饥饿加载

Ⅱ、Nacos注册中心

Nacos 是阿里巴巴的产品,现在是 SpringCloud 中的一个组件。相比 Eureka 功能更加丰富,在国内受欢迎程度较高。

完成下载后,在 bin 目录下打开 cmd 命令行,运行 startup.cmd -m standalone 单机运行Nacos

项目启动

  1. 在父工程的 pom 文件中添加 spring-cloud-alilbaba 的管理依赖:
<!-- Alibaba管理依赖 -->
<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>
  1. 添加 nacos 的客户端依赖
<!-- nacos客户端依赖包 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  1. 修改需要服务注册的项目中的 application.yml 文件,添加 nacos 地址
spring:
  application:
    name: orderservice  # order的服务名称
  cloud:
    nacos:
      server-addr: localhost:8848   # nacos 服务地址

项目和 Nacos 启动后,访问 Nacos 服务地址进入 WEB 管理界面(初始账号密码为:nacos)。

终端启动Nacos
Nacos界面

Nacos服务分级存储模型

Nacos 将分布式服务分为三个等级:服务、集群、实例

Nacos服务分级存储模型

此时我们注意到:服务调用应尽可能选择本地集群的服务,跨集群调用延迟较高;本地集群不可访问时,再去访问其它集群。

Nacos 为服务跨集群调用问题提供了解决方案,即在配置文件中配置服务集群属性

  1. 修改 application.yml,设置如下内容:
application:
    name: orderservice  # 服务名称
  cloud:  # nacos配置
    nacos:
        server-addr: localhost:8848  # 服务地址
        discovery:
            cluster-name: GZ  # 自定义集群名称
  1. 在服务消费者中设置负载均衡策略NacosRule
userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule  # Nacos集群负载均衡策略

二、Nacos 配置中心

当微服务的部署越来越多时,我们微服务配置就会越来越多,如果不想逐个管理起来,我们就可以通过 Nacos 的配置管理服务把它们集中起来管理。
事实上,Nacos 不仅是承担注册中心重任的组件,而且是备受众多企业青睐的配置中心组件。

Ⅰ、配置方法

  1. 在 Nacos 配置管理->配置列表界面点击 "+" 号新建配置;随后按下图完成配置:

Nacos新建配置

  1. 在项目引入 Nacos 的配置管理客户端依赖:
<!-- nacos配置管理依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- nacos配置类依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
  1. 在resource目录添加一个 bootstrap.yml 文件,这个文件是引导文件,优先级高于 application.yml,事实上,我们可以把所有有关 Nacos 的配置都统一配置在此处:
# nacos配置文件
spring:
    application:
        name: userservice  # 实例名称
    profiles:
        active: public  # 环境
    cloud:
        nacos:
            server-addr: localhost:8848  # 项目地址
            config:
                file-extension: yaml  # 配置文件文件后缀
            discovery:
                cluster-name: GZ  # 自定义集群名称
                ephemeral: true  # 是否为临时实例

Nacos 配置管理配置完成后,可以通过格式化时间代码测试一下:

@RestController
@RequestMapping("/user")
@RefreshScope//当使用@Value注解时,启用该注解实现配置文件热部署
public class UserController {

    @Autowired
    private UserService userService;

    //属性注入加载配置文件
    @Value("${pattern.dateformat}")
    private String dateformat;

    /**
     * @Value加载测试userservice-public.yaml配置文件属性
     *
     * @return 格式化后的当前时间
     */
    @GetMapping("/now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
    }
  ......//下略
}

除了 @Value 注解获取配置文件属性外,还可以使用 @ConfigurationProperties 注解获取配置对象,并自动实现配置热部署

@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
    private String dateformat;//userservice-public.yaml属性
}

在使用处注入对象

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    //使用@ConfigurationProperties(prefix = "pattern")注解注入配置文件属性
    @Autowired
    private PatternProperties patternProperties;

    /**
     * 使用@ConfigurationProperties注解获取userservice-public.yaml配置文件
     * 实现配置的热部署
     *
     * @return time
     */
    @GetMapping("/now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()));
    }
  ......//下略
}

Ⅱ、多环境配置共享

微服务启动时会从nacos读取多个配置文件:

  • [spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml
  • [spring.application.name].yaml,例如:userservice.yaml

无论profile如何变化,[spring.application.name].yaml这个文件一定会加载,因此多环境共享配置可以写入这个文件

且配置文件优先性为:服务名-profile.yaml > 服务名称.yaml > 本地配置

三、Feign 服务远程调用

前文我们通过 RestTemplate 远程调用服务接口,但其存在的下面问题:

  • 代码可读性差,且编程体验不统一
  • 参数复杂时URL难以维护

而 SpringCloud 在服务远程调用方面为我们提供了一个HTTP客户端——FeignFeign 是一个声明式的http客户端,其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。Feign 会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。

Ⅰ、代码实例

  1. 引入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 在服务消费者的启动类添加注解开启 Feign 的功能:
@EnableFeignClients
  1. 编写 Feign 客户端 UserClient:
@FeignClient("userservice")
public interface UserClient {
    /**
     * Feign 接口调用 http://userservice/user/{id}
     *
     * @param id
     * @return User对象
     */
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}
  1. 用 Feign 客户端代替 RestTemplate:
@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserClient userClient;

    /**
     * 通过id查询订单
     *
     * @param orderId 订单编号
     * @return json
     */
    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.使用 Feign 调用远程服务器接口
        User user = userClient.findById(order.getUserId());
        // 3.封装数据
        order.setUser(user);
        // 4.返回
        return order;
    }
}

Ⅱ、Feign 自定义配置

Feign 运行自定义配置来覆盖默认配置,可以修改的配置如下:

自定义Feign的配置

其中修改日志等级或其他配置方式有两种:
方式一:修改配置文件

  1. 全局生效
feign:
  client:
    config:
      default:
        loggerLever: basic  # 日志等级,默认为none
  1. 局部生效
feign:  
  client:
    config: 
      userservice: # 针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

方式二@Bean 注入方式

  1. 先创建一个类,在声明一个 @Bean
public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 日志级别为BASIC
    }
}
  1. 随后确定其作用于局部或全局

如果是全局配置,则把它放到 @EnableFeignClients 注解中:

@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)

如果是局部配置,则把它放到 @FeignClient 注解中:

@FeignClient(value = "userservice", configuration = FeignClientConfiguration.class)

Ⅲ、Feign 性能优化

Feign 底层的客户端实现:

  • URLConnection默认实现,不支持连接池
  • Apache HttpClient:支持连接池
  • OKHttp:支持连接池

因此优化 Feign 的性能主要包括:

  1. 使用连接池代替默认的 URLConnection
  2. 日志级别,最好用 basic 或 none

下面提到了如何将默认连接客户端切换HttpClient

  1. Feign 添加 HttpClient 的支持
<!-- HttpClient依赖 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
  1. 配置连接池
feign:
  client:
    config:
      default:  # default全局的配置
        loggerLevel: BASIC  # 日志级别,BASIC就是基本的请求和响应信息
  httpclient:
    enabled: true  # 开启feign对HttpClient的支持
    max-connections: 200  # 最大的连接数
    max-connections-per-route: 50  # 每个路径的最大连接数

Ⅳ、Feign 最佳实践

方式一(继承):给消费者的 FeignClient 和提供者的 Controller 定义统一的父接口作为标准

Feign最佳实践方式一

方式二(抽取):将 FeignClient 抽取为独立模块,并且把接口有关的 POJO、默认的 Feign 配置都放到这个模块中,提供给所有消费者使用

Feign最佳实践方式二

下面样例使用了抽取方式完成对 feign-api 的实现:

  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. 重启测试

当定义的 FeignClient 不在 SpringBootApplication 的扫描包范围时,这些 FeignClient 无法使用。有两种方式解决:
方式一:指定 FeignClient 所在包

@EnableFeignClients(basePackages = "cn.itcast.feign.clients")

方式二:指定 FeignClient 字节码

@EnableFeignClients(clients = {UserClient.class})

Feign抽取API

四、服务网关 Gateway

在微服务架构中,网关是一种中间层,它在客户端和服务端之间提供了一个单一的入口点,并且可以用于路由请求、鉴权、监控、安全性、负载均衡等多种功能。

网关在微服务架构中起到了身份认证和权限校验、统一管理服务调用关系、服务路由、负载均衡、提供统一的接口、请求限流等重要作用,因此是微服务架构中不可或缺的一环。

为什么需要网关

在 SpringCloud 中网关的实现包括两种:

  • Gateway
  • Zuul

Zuul是基于 Servlet 的实现,属于阻塞式编程。而 SpringCloudGateway 则是基于 Spring5 中提供的 WebFlux,属于响应式编程的实现,具备更好的性能

Ⅰ、搭建网关服务

使用 Gateway 搭建网关服务的一些步骤:

  1. 创建新的 module,引入 SpringCloudGateway 的依赖和 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>
  1. 编写路由配置及 nacos 地址:
server:
    port: 10010
spring:
    application:
        name: gateway  # 服务名称
    cloud:
        nacos:
            server-addr: localhost:8848  # 给服务注册在nacos
        gateway:  # gateway配置
            routes:  # 网关配置
                -   id: user-service  # 唯一路由id,自定义
                    uri: lb://userservice  # 路由目标地址,使用Nacos负载均衡
                    predicates:  # 路由断言,判断请求是否符合规则
                        -   Path=/user/**  # 路径断言
                -   id: order-service
                    uri: lb://orderservice
                    predicates:
                        -   Path=/order/**

搭建网关服务

Ⅱ、路由断言工厂

网关路由 routes 可以配置的内容包括:

  • 路由id:路由唯一标示
  • uri:路由目的地,支持 lb 和 http 两种
  • predicates路由断言,判断请求是否符合要求,符合则转发到路由目的地
  • filters:路由过滤器,处理请求或响应

我们在配置文件中写的断言规则只是字符串,这些字符串会被 Predicate Factory 读取并处理转变为路由判断的条件

Gateway断言工厂

Ⅲ、路由过滤器

GatewayFilter 是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应处理

以下是 Spring 提供的部分路由过滤器工厂

名称 说明
AddRequestHeader 给当前请求添加一个请求头
RemoveRequestHeader 移除请求中的一个请求头
AddResponseHeader 给响应结果中添加一个响应头
RemoveResponseHeader 从响应结果中移除有一个响应头
RequestRateLimiter 限制请求的流量
... ...

更多的详细路由过滤器请见 Spring Cloud 官方文档https://docs.spring.io/spring-cloud-gateway/docs/3.1.5-SNAPSHOT/reference/html/#gatewayfilter-factories

一个简单的案例:给所有进入 userservice 的请求添加一个请求头:Message=Hello World!

如上文,我们可以在配置文件的 filters 处声明 AddRequestHeader。

spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/** 
        filters:  # 过滤器
        - AddRequestHeader=Massage, Hello World!  # 添加请求头

默认过滤器:如果要对所有的路由都生效,则可以将过滤器工厂写到 default 下。格式如下:

spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/**
      default-filters: # 默认过滤项
      - AddRequestHeader=Massage, Hello World!

Ⅳ、全局过滤器

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与 GatewayFilter 的作用一样。区别在于 GatewayFilte r通过配置定义,处理逻辑是固定的。而 GlobalFilter 的逻辑需要自己写代码实现。定义方式是实现 GlobalFilter 接口

public interface GlobalFilter {
    /**
     *  处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
     *
     * @param exchange 请求上下文,里面可以获取Request、Response等信息
     * @param chain 用来把请求委托给下一个过滤器 
     * @return {@code Mono<Void>} 返回标示当前过滤器业务结束
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

下面是一个入门的案例
需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件

  • 参数中是否有 authorization,
  • authorization 参数值是否为 admin

如果同时满足则放行,否则拦截;下方代码是具体实现:

/**
 * gateway网关模拟登录实现
 */
@Order(-1)// 过滤器优先级,有小越高
@Component
public class AuthorizeFilter implements GlobalFilter {
    /**
     * 登录过滤器实现
     *
     * @param exchange ServerWebExchange
     * @param chain 过滤器链
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取取请求参数
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> params = request.getQueryParams();
        String authorization = params.getFirst("authorization");
        // 判断参数值是否等于 admin
        if ("admin".equals(authorization)) {
            // http://localhost:10010/order/101?authorization=admin 请求成功,放行
            return chain.filter(exchange);
        }
        // 否,拦截
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//设置状态码——未登录
        return exchange.getResponse().setComplete();
    }
}

此时若访问 http://localhost:10010/order/101?authorization=admin 时,过滤器将放行,使得请求成功。否则,将无法通过过滤器。

Ⅴ、跨域问题处理

在 gateway 服务的 application.yml 文件中配置下的内容:

spring:
  cloud:
    gateway:
      # ...略
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求 
              - "http://localhost:8090"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*"  # 允许在请求中携带的头信息
            allowCredentials: true  # 是否允许携带cookie
            maxAge: 360000  # 这次跨域检测的有效期

本复习随笔笔记依据 SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式,系统详解springcloud微服务技术栈课程|黑马程序员Java微服务 课程教学顺序辅助完成。
感谢@黑马程序员讲师的热情与付出,用颇为生动的授课方式让我愉快地入门了 Java 微服务框架。
SpringCloud课程资料链接点我进入

相关文章索引

微服务异步通讯——RabbitMQ消息队列复习笔记
Elasticsearch全文检索引擎复习笔记

posted @ 2022-12-18 18:12  TfiyuenLau  阅读(1534)  评论(0编辑  收藏  举报