《Spring Cloud学习笔记:Nacos配置管理 OpenFeign LoadBalancer Getway》

  • 基于Feign的声明式远程调用(代码更优雅),用它来去代替我们之前的RestTemplate方式的远程调用

1. Nacos配置管理

  • Nacos除了可以做注册中心,同样也可以做配置管理来使用。 
  • 利用Nacos实现统一配置管理以及配置的热更新:解决微服务的配置文件重复和配置热更新问题

1.1 统一配置管理

  • 配置的热更新:服务不用重启配置就可以生效。 

当微服务部署的实例越来越多时,逐个修改微服务配置就会效率低下,而且很容易出错,所以我们需要一种统一配置管理方案,可以集中管理所有实例的配置。

而Naocs除了可以做注册中心,同样也可以做配置管理来使用:

  • Nacos一方面可以将配置集中管理,另一方面可以在配置变更时,及时通知微服务,实现配置的热更新。 

在nacos中添加配置文件:把配置交给Nacos去实现配置的统一管理

如何在nacos中管理配置呢?

然后在弹出的表单中,填写配置信息:

注意:
  • 项目的核心配置,需要热更新的配置才有放到nacos管理的必要基本不会变更的一些配置还是保存在微服务本地比较好。  

从微服务拉取配置

微服务要拉取nacos中管理的配置,并且与本地的application.yml配置合并,才能完成项目启动:

  • 但是Nacos地址是配置在application.yml当中,我们先读取Nacos中的配置文件之前,要先获取Nacos的地址,那如何得知Nacos的地址呢? 

因此Spring引入了一种新的配置文件:bootstrap.yml文件,会在application.yml之前被读取,流程如下: 

  • bootstrap.yml的配置文件的优先级会比application.yml配置文件的优先级要高很多。
  • 与Nacos地址和配置文件相关的所有信息都应该放在bootstrap.yml当中。

1. 引入nacos-config依赖:引入Nacos的配置管理依赖

  • 在服务消费者中的pom.xml中引入依赖 
<!--nacos配置管理依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

2. 在resource目录中添加一个bootstrap.yml文件,这个文件是引导文件,它的优先级高于                application.yml

配的就是Data ID: 

spring:
  application:
    name: userservice # 配置服务名称
  profiles:
    active: dev #开发环境,这里是dev 
  cloud:
    nacos:
      server-addr: localhost:8848 # 配置Nacos地址
      config:
        file-extension: yaml # 文件后缀名

3. 读取nacos配置

在user-service中的UserController中添加业务逻辑,读取pattern.dateformat配置:

针对于本例用来格式化时间

1.2 配置热更新

  • 我们最终的目的,是修改nacos中的配置后,微服务无需重启即可让配置生效,也就是配置热更新。  

要实现配置热更新,可以使用两种方式:需要通过下面两种配置实现

方式一:在@Value注入的变量所在类上添加注解@RefreshScope

方式二:使用@ConfigurationProperties注解代替@Value注解  

  • 需要自定义配置类

1.3 多环境配置共享

当nacos、服务本地同时出现相同属性时,多种配置的优先级有高低之分:

  • 本地配置的优先级是最低的,而线上配置也就是Nacos中的配置是更高一点儿的。
  • 当前环境的配置肯定要比多环境共享配置的优先级更高。 

1.4 搭建Nacos集群

  • 注意:Nacos生产环境下一定要部署为集群状态~!
  • SLB指的是负载均衡器

2. OpenFeign远程调用

  • http客户端Feign

RestTemplate方式调用存在的问题:

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

Feign的介绍

  • Feign是一个伪声明式的HTTP客户端,官方地址:GitHub - OpenFeign/feign: Feign makes writing java http clients easier 

其作用就是帮助我们优雅的实现HTTP请求的发送~!

OpenFeign的介绍

OpenFeign是一个声明式的HTTP客户端,是Spring Cloud在Eureka公司开源的Feign基础上改造而来,官方地址:https://github.com/OpenFeign/feign 

其作用就是基于SpringMVC的常见注解,帮我们优雅的实现http请求的发送~!

OpenFeign快速入门 

OpenFeign已经被Spring Cloud自动装配,实现起来非常简答:

  1. 引入依赖,包括OpenFeign和负载均衡组件Spring Cloud Load Balancer 
        <!--OpenFeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--负载均衡器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

旧版本中用的才是Ribbon,新版本中用的都是loadbalancer。 

联想补充:Spring Cloud怎么实现服务的负载均衡?

  • Spring Cloud 2020.0.0之前的版本使用的是spring-cloud-netflix-ribbon,一开始都是使用Ribbon作为负载均衡组件的,不过现在的Spring Cloud 2020.0.0 及后续新版本已经弃用Ribbon了而是使用Spring Cloud Load Balancer模块作为负载均衡组件,用来替代Ribbon,不过这个也不是独立的模块,而是spring-cloud-commons中的一个子模块。 

Spring Cloud LoadBalancer支持哪些负载均衡策略?

Spring Cloud LoadBalancer提供了自己负载均衡的抽象接口ReactiveLoadBalancer,并且提供了两种策略实现:

  • RoundRobinLoadBalancer(轮循)
  • RandomLoadBalancer(随机)

目前相比Ribbon来说负载均衡策略还是比较简单的。

2. 在启动类通过添加@EnableFeignClients注解,启用OpenFeign功能

编写OpenFeign客户端

cart-service中,定义一个新的接口,编写Feign客户端:

其中代码如下:

package com.hmall.cart.client;

import com.hmall.cart.domain.dto.ItemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@FeignClient("item-service")
public interface ItemClient {

    @GetMapping("/items")
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}

这里只需要声明接口,无需实现方法。接口中的几个关键信息:

使用FeignClient 
  • feign替我们完成了服务拉取、负载均衡、发送http请求的所有工作,而且,这里我们不再需要RestTemplate了,还省去了RestTemplate的注册。 

OpenFeign连接池

Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现包括:

  • HttpURLConnection:默认实现,不支持连接池 => 每一次都需要重新创建连接,因此效率极低

  • Apache HttpClient :支持连接池

  • OKHttp:支持连接池

因此我们通常会使用带有连接池的客户端来代替默认的HttpURLConnection。比如,我们使用OK Http:

引入依赖: 

<!--OK http 的依赖 -->
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-okhttp</artifactId>
</dependency>

开启连接池:

  • 在application.yml配置文件中开启Feign的连接池功能
feign:
  okhttp:
    enabled: true # 开启OKHttp功能

重启服务,连接池就生效了。 

所谓最近实践,就是使用过程中总结的经验,最好的一种使用方式。   

日志配置

OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:

  • NONE不记录任何日志信息,这是默认值。

  • BASIC(推荐)仅记录请求的方法,URL以及响应状态码和执行时间

  • HEADERS在BASIC的基础上,额外记录了请求和响应的头信息

  • FULL记录所有请求和响应的明细,包括头信息、请求体、元数据。

Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。

要自定义日志级别需要声明一个类型为Logger.Level的Bean,在其中定义日志级别:

  • 也可以基于Java代码来修改日志级别,先声明一个类,然后声明一个Logger.Level的对象

public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 日志级别为BASIC
    }
}

如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:

@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class) 

如果是局部生效,则把它放到对应的@FeignClient这个注解中:

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

3. Getway服务网关 

  • 统一网关Getway

由于每个微服务都有不同的地址或端口,入口不同,在与前端做联调时会发现:

  • 请求不同数据时要访问不同的入口,需要维护多个入口地址,麻烦
  • 前端无法调用Nacos,无法实时更新服务列表

单体架构时我们只需要完成一次用户登录、身份校验,就可以在所有业务中获取到用户信息,而微服务拆分后,每个微服务都独立部署,这就存在一些问题:

  • 如果没有网关做校验,微服务直接摆在那里,允许任何人都可以来请求访问,这是不安全的
  • 如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去调用,这样无疑增加了代码编写的复杂性
  • 服务地址过多,而且将来可能变化,前端不知道该请求谁?
  • 认证复杂,每个服务都需要独立认证:每个服务都可能需要登录用户信息,难道每个微服务都需要编写JWT登录校验、用户信息获取的功能吗?
  • 当微服务之间调用时,该如何传递用户信息?

可以通过API网关技术来解决上述问题。

网关概述 

网关的核心功能特性 

网关就是网络的关口,是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,负责前端请求的路由 => 服务路由(告诉你在几楼几号这叫做路由)、转发(你找不着带你过去这叫做转发)以及用户登录时的身份校验(身份认证和权限校验,做过滤拦截)(检查你户口本)

数据在网络间传输,从一个网络传输到另一网络时就需要经过网关来做数据的路由和转发以及数据安全的校验。

网关的作用:

网关是所有微服务的统一入口 

  1. 对用户请求做身份认证、权限校验
  2. 将用户请求路由到微服务,并实现负载均衡
  3. 对用户请求做限流 

  • 网关路由:解决前端请求入口的问题
  • 网关鉴权:解决统一登录校验和用户信息获取的问题

 更通俗的来讲,网关就像是以前园区传达室的大爷。

  • 外面的人要想进入园区,必须经过大爷的认可,如果你是不怀好意的人,肯定被直接拦截。

  • 外面的人要传话或送信,要找大爷。大爷帮你带给目标人。

现在,服务网关就起到同样的作用,前端请求不能直接访问微服务,而是要请求网关:

  • 网关可以做安全控制,也就是做登录身份校验,网关作为微服务入口,需要校验用户是否有请求资格(比如是否登录),校验通过才放行(接着解析JWT令牌),如果校验不通过则需要进行拦截
  • 一切请丢都必须先经过网关,但网关不处理业务,通过网关认证后,网关再根据前端的请求判断应该访问哪个微服务,这个判断的过程就是请求路由,然后再将请求转发过去,转发到具体的微服务,当路由的目标服务有多个时,还需要做负载均衡
  • 网关它也是一个微服务,网关也是需要将自己的信息注册到注册中心上,并且网关会去注册中心去拉取所有的服务地址
  • 有了网关以后,我们微服务的地址再也不需要暴露给前端了,要暴露给前端的仅仅是网关地址,这对于微服务来讲也是一种保护,而且,对前端来讲,由于它只知道网关地址,因此整个微服务对它来讲是隐藏的,是一个黑盒的,也就是说在前端看来,后端跟原来的单体架构其实是没什么区别的,这样它的开发体验也是一致的
  • 一切请求,一定要先到网关(前端直接请求网关),即可再到微服务~!
  • 限流:当请求流量过高时,在网关中按照下游的微服务能够接受的速度来放行请求,避免服务压力过大

如果微服务有做集群,网关还要进行负载均衡: 

在Spring Cloud当中,提供了两种网关的实现方案:

  • Netfilx Zuul:它是Netflix出品,基于Servlet的阻塞式编程,早期实现,目前已经淘汰,需要调优才能获得与Spring Cloud Getway类似的性能
  • Spring Cloud Getway:Spring官方出品,基于Spring 5 中提供的的WebFlux技术,完全支持响应式编程,属于响应式编程的体现 => 基于WebFlux的响应式编程,吞吐能力更强,具备更好的性能,无需调优即可获得优异性能
注意:
  • Spring Cloud Alibaba技术栈中并没有提供自己的网关,我们可以采用Spring Cloud Getway来做网关。

Getway快速入门

利用网关实现请求路由

  • 网关是一个独立服务~! 

搭建网关服务

  1. 创建新的Module,创建Spring Boot工程Getway,引入Spring Cloud Getway的起步依赖Nacos的服务发现依赖
        <!--网关Getway依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--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-starter-loadbalancer</artifactId>
        </dependency>
       
     <!-- Spring Boot的编译打包插件-->
     <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

2. 编写启动类SpringBootApplication

3. 编写基础配置和路由规则(配置路由规则:spring cloud getway routes)

  • id是路由的唯一标识,每一个路由规则都应该有自己的ID,确保它唯一不重复。
server:
  port: 10010 # 网关端口
spring:
  application:
    name: gateway # 服务名称
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由规则id,自定义(一般情况下与微服务名称保持一致),只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址(不推荐)
          uri: lb://userservice # 路由的目标地址(路由目标微服务) lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,判断请求是否符合规则,符合规则才路由到目标也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求

4. 启动网关服务进行测试

路由属性总结 

网关路由对应的Java类是RouteDefinition,其中常见的属性,即可以配置的内容包括:

  • 路由id:路由的唯一标识
  • 路由目标(uri):路由的目标地址(路由目的地),支持lb和http两种:http代表固定地址,lb代表根据服务名负载均衡
  • 路由断言(predicates):判断请求是否符合路由的规则,判断请求是否符合要求,符合则转发到路由目的地
  • 路由过滤器(filters):对请求或响应做特殊处理

路由断言工厂 - Route Predicate Factory

读取并解析用户配置定义的断言规则 

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

例如Path=/user/**是按照路径匹配,这个规则是由

"org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory"类来处理的

Spring提供了12种基本的RoutePredicateFactory实现:

名称说明示例
After是某个时间点后的请求- After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before是某个时间点之前的请求- Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between是某两个时间点之前的请求- Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie请求必须包含某些cookie- Cookie=chocolate, ch.p
Header请求必须包含某些header- Header=X-Request-Id, \d+
Host请求必须是访问某个host(域名)- Host=.somehost.org,.anotherhost.org
Method请求方式必须是指定方式- Method=GET,POST
Path请求路径必须符合指定规则- Path=/red/{segment},/blue/**
Query请求参数必须包含指定参数- Query=name, Jack或者- Query=name
RemoteAddr请求者的ip必须是指定范围(对IP地址做限制)- RemoteAddr=192.168.1.1/24
Weight权重处理

我们只需要掌握Path这种路由工程就可以了。  

网关实现登录校验 

  • 单体架构时我们只需要完成一次用户登录、身份校验,就可以在所有业务中获取到用户信息,而微服务拆分后,每个微服务都独立部署,不再共享数据,总不能每个微服务都需要做登录校验,这显然不可取。 

鉴权思路分析

  • 我们的登录是基于JWT来实现的,校验JWT的算法复杂,而且需要用到密钥,如果每个微服务都去做登录校验,这就会存在两大问题:
  1. 每个微服务都需要知道JWT的密钥,不安全
  2. 每个微服务重复编写登录校验代码、权限校验代码,麻烦

既然网关是所有微服务的入口,一切请求都需要先经过网关,我们完全可以把登录校验的工作放到网关去做,这样之前说的问题就都解决了:

  • 只需要在网关和用户服务保存密钥
  • 只需要在网关开发登录校验的功能

此时,登录校验的流程图:

JWT校验它一定要在网关将请求转发到微服务之前去做,但是请求转发是Getway网关内部代码来做,那我们如何在网关转发之前去做登录校验?

网关过滤器 

Getway网关内部源码分析 - 网关请求处理流程

  • 登录校验必须在网关请求转达到微服务之前去做,否则就失去了意义,而网关的请求转发是Gateway内部代码实现的,要想在请求转发之前做登录校验,就必须了解Gateway内部工作的基本原理。
  • 我们知道,网关的底层是没有业务逻辑的,它要做的事情就是基于我们配置的路由规则来去判断前端请求到底应该由哪个微服务来进行处理,然后将请求转发到对应的微服务,而这里对路由规则的判断就是由HandlerMapping的接口来进行处理的,HandlerMapping的默认实现是RoutePredicateHandlerMapping(基于路由断言去做路由规则的匹配)。
  • HandlerMapping就是来做路由匹配的,匹配完了以后就交给下一个接口去处理了,这就是责任链模式。

如图所示:

  1. 客户端请求进入网关后由HandlerMapping对请求做判断,HandlerMapping找到与当前请求匹配的路由规则(Route)并存入上下文,然后将请求交给请求处理器WebHandler去处理。

  2. WebHandler则会加载网关中配置的多个过滤器,加载当前路由下需要执行的过滤器链(Filter Chain),放入到集合并排序,形成过滤器链,然后按照顺序逐一执行这些过滤器(后面称为Filter)。

  3. 图中Filter被虚线分为左右两部分,是因为Filter内部的逻辑分为pre和post两部分,分别会在请求路由到微服务之前和之后被执行  =>  pre顺序执行,post倒序执行。

  4. 只有在所有Filter的pre逻辑都依次顺序执行通过后,请求才会被路由到微服务(如果过滤器的pre逻辑执行失败,则会直接结束,不会再往下去执行了)。

  5. 微服务返回结果后,再倒序执行Filter的post逻辑。

  6. 最终把响应结果返回。

过滤器链当中的一个特殊过滤器:

  • NettyRoutingFilter:该过滤器不用做配置,默认对所有路由都生效的过滤器,而且该过滤器的执行顺序恰好就是在整个过滤器链的最后,而它的作用就是将请求转发到微服务,当微服务返回结果后去做封装,然后保存上下文接着又依次返回给其它过滤器,最终返回给用户。 

上图我们得知:最终请求转发是由一个名为NettyRoutingFilter的过滤器来执行的,而且这个过滤器是整个过滤器链中顺序最靠后的一个,如果能够定义一个过滤器,在其中实现登录校验逻辑,并且将过滤器的执行顺序定义到NettyRoutingFilter之前, 

路由过滤器或网关过滤器 - GaetwayFilter

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

路由过滤器工厂 - GetwayFilterFactory

Spring提供了33种不同的路由过滤器(工厂),每种路由过滤器都有独特的作用,例如:

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

......

在服务的application.yml当中来配置。

默认过滤器 - defaultFilters 

  •  如果要对所有的路由都生效,则可以将过滤器工厂写到defalut下。
spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/**
      default-filters: # 默认过滤项
      - AddRequestHeader=Truth, Itcast is freaking awesome! 

总结 

过滤器的作用是什么?

  1. 对路由的请求或响应做加工处理,比如添加请求头
  2. 配置在路由下的过滤器只对当前路由的请求生效

defaultFilters的作用是什么?

  • 对所有路由都生效的过滤器 

全局过滤器 - GlobalFilter

  • 全局过滤器的作用是拦截处理一切进入网关的请求和微服务响应,与GetwayFilter的作用一样。
  • 区别于路由过滤器或网关过滤器GetwayFilter通过配置来定义,并且每一种路由过滤器的处理逻辑是固定的,如果我们希望拦截请求,做自己的业务逻辑则没办法实现,而GlobalFilter的逻辑需要自己写代码实现。

定义方式是实现GlobalFilter接口:

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

在filter中编写自定义逻辑,可以实现下列功能:

  • 登录状态判断
  • 权限校验
  • 请求限流等 

拦截器是在Servlet之后Controller之前,而过滤器是在Servlet之前。        

package cn.itcast.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 自定义全局过滤器
 * 过滤器的顺序除了可以通过注解来指定,还可以通过Ordered接口来指定 => 责任链模式
 */
@Order(-1)  // 该注解是一个顺序注解,设置过滤器先后顺序的,这个值越小,优先级越高
@Component
public class AuthorizationFilter implements GlobalFilter, Ordered {
    /**
     * 自定义全局过滤器,拦截请求并判断用户身份(登录认证过滤器)
     *
     * @param exchange 请求上下文,里面可以获取Request,Response等信息
     * @param chain    用来把请求委托给下一个过滤器
     * @return 返回标识当前过滤器业务结束
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.获取请求参数               .var自动补全左边
//        MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        // 2.获取参数中的authorization参数
        String authorization = queryParams.getFirst("authorization");
        // 3.判断参数值是否等于admin
        if ("admin".equals(authorization)) {
            // 4.是,放行  只有这一个API
            return chain.filter(exchange);
        } else {
            // 5.否,拦截
            // 5.1 禁止访问,设置状态码:HttpStatus是一个枚举类     403:服务端拒绝访问
            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            // 5.2 结束处理
            return exchange.getResponse().setComplete();
        }
    }

    /**
     * 设置过滤器的优先级
     *
     * @return 该值越小, 优先级越高
     */
    @Override
    public int getOrder() {
        return -1;
    }
}

总结 - 实现全局过滤器的步骤:

  1. 实现GlobalFilter接口

  2. 添加注解@Order注解或者实现Ordered接口

  3. 编写处理逻辑 

过滤器一定要有顺序~! 

过滤器链的执行顺序 

请求进入网关会碰到三类过滤器:当前路由的过滤器(局部路由过滤器)、DefaultFilter(全局路由过滤器)、GlobalFilter

请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:

  • 局部路由过滤器和全局路由过滤器它两的本质一样,是同一类,都是路由过滤器GaetwayFilter类,只不过作用范围不同而已~!

GetwayFilterAdapter:过滤器适配器  =>  适配器模式 

在网关当中,所有的GlobalFilter都可以被适配成GetwayFilter,从这个角度来讲,我们可以任务网关中的所有过滤器最终都是GetwayFilter类型,既然是同一种类型,那我们当然可以扔到同一种集合中去做排序。 

过滤器的排序规则是什么呢?网关中过滤器的执行顺序? 

  • 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序就越靠前 
  • GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,值由我们自己指定
  • 路由过滤器和defaultFilter的order值由Spring指定,默认是按照声明顺序(就是在application.yml文件中的声明顺序)从1递增。
  • 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行(看了Spring的Getway源码才知道)。

getFilter()方法是加载路由过滤器和defaultFilter,handle()方法就是去加载GlobalFilter并且对GlobalFilter去做装饰,把它变成GetwayFilter,最后把所有过滤器合并做排序的:

总结:
  • 排序,先看order值,值越小,优先级越高,值一样时,defaultFilter最先,然后是局部的路由过滤器,最后是全局过滤器。 

网关的跨域问题处理

  • 在微服务当中,所有请求都要先经过网关,再到微服务,也就是说,跨域请求不需要在每个微服务里都去处理,仅仅在网关处理就可以了。
  • 但是网关又跟以前的不一样,网关是基于WebFlux实现的,没有Servlet相关的API,因此,我们之前所学的解决方案不一定能够适用。

跨域问题回顾:

  • 域名不一致就是跨域,比如域名不同、域名相同但端口不同

跨域问题:浏览器禁止请求的发起者与微服务发生跨域Ajax请求,请求被浏览器拦截的问题。

解决方案:CORS 

网关处理跨域问题采用的同样是CORS方案(CORS是浏览器去询问服务器你让不让跨域,它有一次询问,这个询问的请求方式是options,默认情况下这种请求方式是会被网关拦截的,所以要改为true,就是让网关不拦截options类型的请求,这样CORS的询问请求就会被正常发出了)只需要在网关Getway服务的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 # 这次跨域检测的有效期,减少每一次对服务器的Ajax请求而造成的访问压力

跨域的CORS方案对性能会有一定的损耗,为了减少损耗,我们可以给跨域请求设置有效期,有效期范围内,浏览器将不再发起询问,而是直接放行,从而提高性能。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/270892.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

几种串口扩展电路

一、IIC串口扩展电路 LCT200 是一款可以通过 I2C 接口通讯&#xff0c;拓展 2 路独立串口的通讯芯片&#xff0c;同时也支持通过 2 路串口读写 I2C 接口的数据。LCT200 的封装为 TSSOP-20。 主要功能&#xff1a;⚫ 通过对 I2C 接口读写实现拓展 2 路独立串口功能 ⚫ 通过读写…

SpringBoot Event,事件驱动轻松实现业务解耦

什么是事件驱动 Spring 官方文档AWS Event Driven 简单来说事件驱动是一种行为型设计模式&#xff0c;通过建立一对多的依赖关系&#xff0c;使得当一个对象的状态发生变化时&#xff0c;所有依赖它的对象都能自动接收通知并更新。即将自身耦合的行为进行拆分&#xff0c;使拆…

AI安全综述

1、引言 AI安全这个话题&#xff0c;通常会引伸出来图像识别领域的对抗样本攻击。下面这张把“熊猫”变“猴子”的攻击样例应该都不陌生&#xff0c;包括很多照片/视频过人脸的演示也很多。 对抗样本的研究领域已经具备了一定的成熟性&#xff0c;有一系列的理论来论述对抗样本…

【JavaEE初阶二】 Thread类及常见方法

1. 线程的创建 主要来简单学习一下以下几种方法&#xff1a; 1.1 继承 Thread 类 具体代码见前面的一章节&#xff0c;主体的步骤有以下几部分&#xff1b; 1、继承 Thread 来创建一个自定义线程类MyThread class MyThread2 extends Thread{//重写run方法Overridepublic void …

VScode远程连接服务器,Pycharm专业版下载及远程连接(深度学习远程篇)

Visual Code、PyCharm专业版&#xff0c;本地和远程交互。 远程连接需要用到SSH协议的技术&#xff0c;常用的代码编辑器vscode 和 pycharm都有此类功能。社区版的pycharm是免费的&#xff0c;但是社区版不支持ssh连接服务器&#xff0c;只有专业版才可以&#xff0c;需要破解…

xlua源码分析(四) lua访问C#的值类型

xlua源码分析&#xff08;四&#xff09; lua访问C#的值类型 上一节我们主要探讨了C#是如何使用interface和delegate访问lua层的table和function的&#xff0c;本节我们跟着Examples 05_NoGc&#xff0c;来看看xlua是如何实现lua层无gc访问C#的值类型的。 首先例子中用到的lua…

在线快速获取UDID教程

第一步 进入UDID 登录咕噜分发官网(https://www.gulufenfa.com) 点击工具箱 进入控制台—【开发者工具】—【UDID】 第二步 获取UDID 进入UDID快速获取页面 因需要历史UDID所以需要登录您在咕噜分发的账号 用苹果手机扫描二维码根据操作UDID 第三步 手机操作教程 手机…

科技云报道:开源才是大模型的未来?

科技云报道原创。 一年前&#xff0c;ChatGPT横空出世&#xff1b;7个多月后&#xff0c;Meta宣布开源LLaMA 2&#xff0c;并且可免费商用。 这一天&#xff0c;也成为大模型发展的分水岭。短时间内&#xff0c;LLaMA 2对一些闭源的大模型厂商造成了致命性的打击。 随后&…

CGAL的三角曲面网格骨架化

1、介绍 马模型的曲线骨架。 骨架是用于分割、形状匹配、重建、虚拟导航等的有效形状抽象。顾名思义&#xff0c;曲线骨架是曲线结构的图&#xff08;1D&#xff09;。对于3D几何体来说&#xff0c;它不是由表面&#xff08;2D&#xff09;组成的中轴。如图所示&#xff0c;形…

VMvare虚拟机之文件夹共享防火墙设置

目录 一.jdk的配置&TomCat的配置 1.1 jdk配置 1.2 tomcat配置 二.文件夹共享功能 2.1 作用 2.2.高级共享和普通共享 三.防火墙设置 3.1 入站规则和出站规则 四.附图-思维导图 一.jdk的配置&TomCat的配置 建立一个共享文件夹&#xff0c;将jdk文件和tomcat文…

嵌入式——RTC内置实时时钟

学习目标 理解原理图RTC设计部分掌握初始化RTC掌握设置时间掌握读取时间学习内容 RTC原理图 RTC结构框图 RTC时钟 开发流程 加载依赖。gd32f4xx_rtc.c,gd32f4xx_pmu.c初始化RTC。时钟配置。获取时钟。RTC初始化 // 电池管理加载 rcu_periph_clock_enable(RCU_PMU); pmu_back…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Progress进度条组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Progress进度条组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Progress组件 进度条也是UI开发最常用的组件之一&#xff0c;进度条组件…

分类预测 | Matlab实现SCSO-SVM基于沙猫群优化算法优化支持向量机的多变量分类预测【23年新算法】

分类预测 | Matlab实现SCSO-SVM基于沙猫群优化算法优化支持向量机的多变量分类预测【23年新算法】 目录 分类预测 | Matlab实现SCSO-SVM基于沙猫群优化算法优化支持向量机的多变量分类预测【23年新算法】分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现SCSO-…

docker部署kafka zookeeper模式集群

单机模式链接&#xff1a;https://blog.csdn.net/wsdhla/article/details/133032238 kraft集群模式链接&#xff1a;部署Kafka_kafka 部署-CSDN博客 zookeeper选举机制举例&#xff1a; 目前有5台服务器&#xff0c;每台服务器均没有数据&#xff0c;它们的编号分别是1,2,3,4,5…

如何通过内网穿透实现远程访问本地Linux SVN服务

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

Nature | 大型语言模型(LLM)能够发现和产生新知识吗?

大型语言模型&#xff08;LLM&#xff09;是基于大量数据进行预训练的超大型深度学习模型。底层转换器是一组神经网络&#xff0c;这些神经网络由具有自注意力功能的编码器和解码器组成。编码器和解码器从一系列文本中提取含义&#xff0c;并理解其中的单词和短语之间的关系。通…

python 通过opencv及face_recognition识别人脸

效果&#xff1a; 使用Python的cv2库和face_recognition库来进行人脸检测和比对的 0是代表一样 认为是同一人。 代码&#xff1a; pip install opencv-python pip install face_recognition# 导入cv2库&#xff0c;用于图像处理 import cv2 # 导入face_recognition库&#…

跨域 - CORS跨域资源共享介绍

目录 1&#xff0c;介绍2&#xff0c;简单请求判定交互规范 3&#xff0c;非简单请求交互规范1&#xff0c;发送预检请求2&#xff0c;预检请求响应3&#xff0c;浏览器发送真实的请求&#xff0c;服务器完成真实的响应。 附带身份凭证 相关内容&#xff1a; 浏览器同源策略和跨…

C# WPF上位机开发(MySql访问)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们学习了数据库sqlite&#xff0c;不过这是一种小型的数据库&#xff0c;作为内部使用还可以。但是&#xff0c;如果要与外面的其他供应商进…

doris数据模型,06-Aggregate(聚合模型)

聚合模型的特点 将表中的列分为Key和Value。 Key是数据的维度列&#xff0c;比如时间&#xff0c;地区等等。key相同时会发生聚合。 Value是数据的指标列&#xff0c;比如点击量&#xff0c;花费等等。 每个指标列还会有自己的聚合函数&#xff0c;如&#xff1a;sum&#xff…