微服务之网关

1、什么是微服务网关?

微服务网关是一种用于管理和调度微服务的工具或服务,它在微服务架构中扮演着关键角色。以下是关于微服务网关的清晰概述:

概念定义:

    • 微服务网关是微服务架构中的前端门户,它提供了一个统一的入口点,方便客户端访问和调用多个微服务。
    • 客户端无需直接与每个微服务进行交互,而是通过微服务网关进行请求和响应的转发。

主要作用:

    • 统一入口:为所有微服务提供唯一的入口点,简化客户端与服务的交互,同时保障后台服务的安全性。
    • 鉴权校验:能够识别每个进来的请求,并根据其权限进行校验,阻止不符合要求的请求通过。
    • 动态路由:根据需要,将请求动态地路由到不同的后端集群中,实现服务的灵活调度。
    • 降低耦合度:通过在网关层做映射,将客户端与服务解耦,使服务可以独立发展,减少两者之间的依赖。
    • 提供附加功能:如熔断、限流、请求聚合、请求过滤、认证授权、监控等。

关键特性:

    • 路由转发:接收客户端的请求,并根据配置或规则将请求转发到相应的微服务。
    • 过滤器功能:在服务网关中可以完成一系列的横切功能,如权限校验、限流、监控等。
    • 负载均衡:根据配置的策略,将请求分发到多个微服务实例上,确保系统的负载均衡和性能优化。

技术实现:

    • 微服务网关的实现可以采用多种技术栈和框架,如Spring Cloud Gateway、Netflix Zuul等。
    • 这些框架提供了丰富的功能和灵活的配置选项,以满足不同场景下的需求。

过滤器和网关的区别

过滤器用于拦截单个服务

网关拦截整个的微服务

2、zuul

1)zuul是什么?

Zuul 是 netflix 开源的一个 API Gateway 服务器, 本质上是一个 web servlet 应用。

Zuul 的核心是一系列的 filters, 其作用可以类比 Servlet 框架的 Filter,或者AOP。

Zuul 可以通过加载动态过滤机制,从而实现以下各项功能:

验证与安全保障:识别面向各类资源的验证要求并拒绝那些与要求不符的请求。

审查与监控:在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。

动态路由:以动态方式根据需要将请求路由至不同后端集群处。

压力测试:逐渐增加指向集群的负载流量,从而计算性能水平。

负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求。

静态响应处理:在边缘位置直接建立部分响应,从而避免其流入内部集群。

多区域弹性:跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。

2)zuul网关的实现

结合Nacos实现Zuul网关,创建SpringBoot项目,创建provider和consumer以及zuul-proxy三个子模块

provider导入nacos注册中心依赖

<!--        eureka实现注册依赖-->
<!--        <dependency>-->
<!--            <groupId>org.springframework.cloud</groupId>-->
<!--            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!--            <version>2.0.0.RELEASE</version>-->
<!--        </dependency>-->
        
<!--        Nacos注册依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>0.2.2.RELEASE</version>
        </dependency>

provider配置注册文件

server:
  port: 8081
spring:
  application:
    name: service-provider
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
#eureka:
#  client:
#    serviceUrl:
#      defaultZone: http://127.0.0.1:8080/eureka/
#spring:
#  application:
#    name: service-provider

provider启动类添加注解

//@EnableEurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class ProviderApplication {

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

consumer通过feign实现远程服务调用,导入依赖

<!--        <dependency>-->
<!--            <groupId>org.springframework.cloud</groupId>-->
<!--            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!--            <version>2.0.0.RELEASE</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>0.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>

consumer配置注册文件

server:
  port: 8082  #服务端口
#eureka:
#  client:
#    serviceUrl: #注册中心注册地址
#      defaultZone: http://127.0.0.1:8080/eureka/
#spring:
#  application:
#    name: service-consumer #服务名称

spring:
  application:
    name: service-consumer
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

consumer的客户端接口的创建和使用

@FeignClient(name = "service-provider")
public interface HelloFeignClient {

    @RequestMapping( "/sayHi")
    String sayHi();
}

============================================================================
@RestController
public class HelloController {

    @Autowired
    private HelloFeignClient helloFeignClient;

    @RequestMapping("/sayHi")
    public String sayHi() {
        return helloFeignClient.sayHi();
    }
}

consumer启动类上添加注解

//@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerFeignApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerFeignApplication.class, args);
    }

}

PS:如果使用Eureka实现zuul网关,打开上述注释掉的,还需要添加一个子模块eureka-server,用于创建Eureka服务器

导入依赖eureka服务依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    <version>2.0.0.RELEASE</version>
</dependency>

配置文件

server:
  port: 8080 #服务注册中心端口
eureka:
  instance:
    hostname: 127.0.0.1 #服务注册中心IP地址
  client:
    registerWithEureka: false #是否向服务注册中心注册自己
    fetchRegistry: false #是否检索服务
    serviceUrl: #服务注册中心链接,指定服务注册中心的位置
      defaultZone: http://127.0.0.1:8080/eureka/
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

启动类上添加注解:

@EnableEurekaServer

zuul-proxy导入依赖

<!--        <dependency>-->
<!--            <groupId>org.springframework.cloud</groupId>-->
<!--            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!--            <version>2.0.0.RELEASE</version>-->
<!--        </dependency>-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>0.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>

zuul-proxy配置文件

server:
  port: 8085
#eureka:
#  client:
#    serviceUrl:
#      defaultZone: http://127.0.0.1:8080/eureka/
#spring:
#  application:
#    name: zuul-proxy
spring:
  application:
    name: zuul-proxy
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
zuul:
  routes: 
    service-provider:
      path: /provider/**
    service-consumer:
      path: /consumer/**

zuul-proxy启动类添加注解

//@EnableEurekaClient
@EnableDiscoveryClient
@EnableZuulProxy
@SpringBootApplication
public class ZuulProxyApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulProxyApplication.class, args);
    }

}

测试访问

启动后访问 http://localhost:8085/provider/sayHi 和 http://localhost:8085/consumer/sayHi

默认路由规则

Zuul 和 Eureka 结合使用可以实现路由自动配置,不需要在配置文件添加配置,自动配置的路由以服务名称为匹配路径,输入 http://localhost:8085/service-provider/sayHi 和 http://localhost:8085/service-consumer/sayHi 即可访问:

配置访问前缀

application.yml 中添加 prefix 配置:

zuul:
  routes: #配置服务路由
    service-provider:
      path: /provider/**
    service-consumer:
      path: /consumer/**
  prefix: /proxy  #网关路由前缀

由于添加了前缀 /proxy,访问时需带上前缀:

http://localhost:8085/proxy/service-provider/sayHi 或者 http://localhost:8085/proxy/provider/sayHi

3、SpringCloud Gateway

1) Gateway介绍

低层使用的是Spring Boot和Spring Webflux提供的Netty底层环境

Gateway 基于异步非阻塞模型性能较高

动态路由,能够匹配任何请求属性

可以对路由指定 Predicate 断言,和 Filter 过滤器

集成Hystrix断路器功能

集成 Spring Cloud 服务发现功能

支持请求限流功能,支持路径重写功能

2)gateway特征

基于Spring Framework 5, Project Reactor, Spring Boot 2.0构建

能够自由设置任何请求属性的路由

路由可以自由设置断言(Predicates)和过滤器(Filter)

可集成熔断器

流量限速

路径重写(rewrite)

3)gateway流程

1. 客户端发送请求给gateway

2. 请求首先被HttpWebHandlerAdapter进行提取组装成网关上下文,

3. 然后网关的上下文会传递到DispatcherHandler进行请求处理(DispatcherHandler是所有请求的处理分发器)

4. DispatcherHandler负责将所有的请求分发给对应的处理器

5. 比如分发给RoutePredicateHandlerMapping(路由断言处理映射器),路由断言映射器主要作用是进行路由查找,以及找到路由以后返回给对应的FilterWebHandler

6. FilterWebHandler主要负责组装Filter链,并调用Filter执行一系列的Filter操作

7. 然后再把请求转到后端对应的代理服务处理

8. 处理完毕之后将Response返回到Gateway客户端

4)核心介绍

① Filter过滤器

使用过滤器,可以在请求被路由之前或者之后对请求进行修改

过滤器按照请求和响应可以分为两种:Pre类型和Post类型

Pre类型:在请求转发到微服务之前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出、协议转换等操作

Post类型:微服务处理完请求后,返回响应给网关,网关可以再次进行处理,例如可以修改响应内容、响应头、日志输出、流量监控等

按照过滤器Filter作用范围划分

GlobaFilter:全局过滤器,应用在所有路由上的过滤器

GatewayFilter:局部过滤器,应用在单个路由或一组路由上的过滤器

过滤器内置类型

Gateway 内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,比如添加剔除响应头,添加去除参数等,官网上有38种,以下列举常用的几种

routes:
        ###路由id
        - id: member
          #### 基于lb负载均衡形式转发
          uri: lb://producer
          filters:
            - StripPrefix=1
            - CheckAuth=fox,男
#            - AddRequestHeader=X-Request-color, blue   #添加请求头
#            - AddRequestParameter=color, blue     #添加请求参数,127.0.0.1:8088/member/testgateway3
#            - RedirectTo=302, https://www.baidu.com/        #重定向操作,127.0.0.1:8088/member/3 跳转到百度页面
#            - PrefixPath=/prepath   #为匹配的路由统一添加前缀,需要在对应微服务需要配置context‐path
#http://127.0.0.1:8088/member/getMember  跳转到http://127.0.0.1:8083/prepath/getMember 访问
          ###匹配规则
          predicates:
            - Path=/member/**

producer中的测试代码,以及前缀添加

@Slf4j
@RestController
public class TestController {
    @GetMapping("/testgateway1")
    public String testGateway1(HttpServletRequest request) throws Exception {
        log.info("gateWay获取请求头X‐Request ‐color:"+request.getHeader("X-Request-color"));
        return "success";
    }
    @GetMapping("/testgateway2")
    public String testGateway2(@RequestHeader("X-Request-color") String color) throws Exception {
        log.info("gateWay获取请求头X‐Request ‐color:"+color);
        return "success";
    }
    @GetMapping("/testgateway3")
    public String testGateway3(@RequestParam("color") String color) throws Exception {
        log.info("gateWay获取请求参数color:"+color);
        return "success";
    }
}


===================================
server:
  port: 8083
  servlet:
    context-path: /prepath
自定义过滤器

继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理。

编写自定义Filter的步骤通常包括以下几点:

创建一个继承AbstractGatewayFilterFactory的Filter类。

在Filter类中重写apply方法并编写自定义逻辑。

配置Filter类中所需的参数(可选)。

在Spring Cloud Gateway的路由配置中添加Filter。

当然,需要根据实际需求进行调整,例如定义不同类型、不同粒度的Filter等等。

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class CheckAuthGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory{

    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            log.info("调用CheckAuthGatewayFilterFactory===" + config.getName() + ":" + config.getValue());
            return chain.filter(exchange);
        };
    }
}

② Route(路由)

构建网关的基础模块,由ID,目标URL,过滤器等组成

③ Predicate(断言)

开发人员可以匹配HTTP请求中的内容(请求头和请求参数),如果请求断言则进行路由

Spring Cloud Gateway

作用: 当请求gateway的时候,  使用断言对请求进行匹配, 如果匹配成功就路由转发, 如果匹配失败就返回404

类型:内置,自定义

断言的类型

基于Datetime类型的断言工厂

此类型的断言根据时间做判断,主要有三个:

AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期  

BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期

BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内 ZonedDateTime.now()

predicates:
  - Before=2025-01-20T17:42:47.789-07:00[Asia/Shanghai]

基于远程地址的断言工厂

RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中

predicates:
  - RemoteAddr=192.168.1.1/24

基于Cookie的断言工厂

CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求 cookie是否具有给定名称且值与正则表达式匹配。

predicates:
  - Cookie=ch, \d+
#\d+ 用于匹配一个或多个数字字符。

基于Header的断言工厂

HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。  判断请求Header是否具有给定名称且值与正则表达式匹配。

predicates:
  - Header=X-Request-Id, \d+

基于Host的断言工厂

HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。

predicates:
  - Host=**.myhost.com

基于Method请求方法的断言工厂

MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。

predicates:
  - Method=GET

基于Path请求路径的断言工厂

PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。

predicates:
  - Path=/member/**

基于Query请求参数的断言工厂

QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。

predicates:
  - Query=a, b     #a=b

基于路由权重的断言工厂

WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发

This route would forward ~80% of traffic to weighthigh.org and ~20% of traffic to weighlow.org

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2
自定义断言

自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。

1、 必须spring组件 bean

2. 类必须加上 RoutePredicateFactory作为结尾

3. 必须继承AbstractRoutePredicateFactory

4. 必须声明静态内部类   声明属性来接收 配置文件中对应的断言的信息

5. 需要结合shortcutFieldOrder进行绑定

6.通过apply进行逻辑判断  true就是匹配成功  false匹配失败

注意:命名需要以 RoutePredicateFactory 结尾

自定义断言类

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;

@Component
@Slf4j
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {
    public CheckAuthRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                log.info("调用CheckAuthRoutePredicateFactory" + config.getName());
                if(config.getName().equals("zhangsan")){
                    return true;
                }
                return false;
            }
        };
    }
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("name");
    }
    public static class Config {
        private String name;
        public String getName() {
            return name;
        }
        public void setName(String name) {
           this.name = name;
        }
    }
}

yml文件配置

predicates:
#自定义CheckAuth断言工厂
  - CheckAuth=zhangsan

④ 核心思想

路由转发+执行过滤器链

当用户发送请求达到Gateway之后,会通过一些匹配条件,定位到真正的服务节点,并且在这个过程的转发前后,进行一些细粒度控制,其中Predicate(断言)是我们的匹配条件,Filter是一个拦截器,有了这两点,再加上URL,就可以实现一个具体的路由。

⑤ 断言和过滤器的不同

1、功能不同:断言用于匹配请求的某些条件,例如请求路径、请求头、请求参数等,它决定了请求是否应该被路由到特定的目标服务。而过滤器用于对请求进行处理和转换,例如添加请求头、修改请求体、记录日志、实现认证授权等。

2、工作原理不同:断言通常用于路由选择的阶段,在接收请求时进行匹配,如果匹配成功,则将请求路由到相应的目标服务。而过滤器则是在请求路由之前或之后应用的,用于对请求进行预处理或后处理,它不会影响路由的选择。

3、使用场景不同:断言通常用于根据请求的某些属性进行路由选择,因此它更贴近网关的底层实现。而过滤器则更加灵活,可用于实现业务逻辑,例如认证授权、请求重试、流量控制等。

5)Gateway实现

① Gateway实现外部地址转发

依赖

<!-- Spring Cloud Gateway -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

配置文件

server:
  port: 80
###服务网关名称
spring:
  application:
    name: boyatop-gateway
  cloud:
    gateway:
#      discovery:
#        locator:
#          ####开启以服务id去注册中心上获取转发地址
#          enabled: false
        ###路由策略
      routes:
        ###路由id
        - id: boyatop
          ####转发http://www.boyatop.com/
          uri: http://www.baidu.com/
          ###匹配规则
          predicates:
            - Path=/boyatop/**
#访问127.0.0.1:80/boyatop  跳转到http:www.baidu.com

② Gateway整合Nacos实现服务转发

依赖

<!-- Spring Cloud Gateway -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>2.0.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>0.2.2.RELEASE</version>
</dependency>

配置文件

server:
  port: 8088
####服务网关名称
spring:
  application:
    name: boyatop-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          ####开启以服务id去注册中心上获取转发地址
          enabled: true
        ###路由策略
      routes:
        ###路由id
        - id: member
          #### 基于lb负载均衡形式转发
          uri: lb://producer
          filters:
            - StripPrefix=1
          ###匹配规则
          predicates:
            - Path=/member/**
#访问127.0.0.1:8088/member/getMember  跳转到127.0.0.1:8083/getMember   对应其注册中心id为producer

produer依赖

<!--        nacos注册中心依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>0.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

producer配置文件

spring:
  application:
    name: producer
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
server:
  port: 8083

producer的controller

@RestController
public class ProducerController {
    @Value("${server.port}")
    private String port;

    @RequestMapping("/getMember")
    public String getMember() {
        return "The port of producer is "+port;
    }
}

4、Zull和Gateway的区别

Zuul网关属于netfix公司开源的产品属于第一代微服务网关

Gateway属于SpringCloud自研发的第二代微服务网关

相比来说SpringCloudGateway性能比Zuul性能要好:

注意:Zuul基于Servlet实现的,阻塞式的Api, 不支持长连接。

SpringCloudGateway基于Spring5构建,能够实现响应式非阻塞式的Api,支持长连接,能够更好的整合Spring体系的产品。

5、Nginx和网关的区别

相同点:都是可以实现对api接口的拦截,负载均衡、反向代理、请求过滤等,可以实现和网关一样的效果。

不同点:

Nginx采用C语言编写的

微服务都是自己语言编写的 比如Gateway就是java写的。

毕竟Gateway属于Java语言编写的, 能够更好对微服务实现扩展功能,相比Nginx如果想实现扩展功能需要结合Nginx+Lua语言等。

6、URL和URI

URL:就是访问网址时的全路径。URL=协议://IP或域名:端口URI  

注意:端口和URI之间没有/的。因为/也是URI。

  例如:https://www.baidu.com/ab/index.html

        http://www.baidu.com:8080/ab/index.html

以上的统称为URL

URI:除了  协议://IP或域名:端口,后面的部分统称为URI

例如:https://www.baidu.com/ab/index.html                 URI是/ab/index.html  

        http://www.baidu.com:8080/ab/index.html        URI是/ab/index.html

7、如何基于Gateway拦截请求参数

在基于网关(如Spring Cloud Gateway)拦截请求参数时,你通常需要使用过滤器(Filter)来实现这一功能。Spring Cloud Gateway 提供了一个强大的过滤器机制,允许你在请求和响应的生命周期中执行自定义逻辑。

以下是一个基于Spring Cloud Gateway拦截请求参数的基本步骤和示例:

1. 创建自定义过滤器:

你需要创建一个实现了 GlobalFilter 接口或继承 AbstractGatewayFilterFactory 的类。对于简单的拦截逻辑,GlobalFilter 可能就足够了,但如果你需要更复杂的配置或动态行为,那么 AbstractGatewayFilterFactory 可能是更好的选择。

2. 在过滤器中拦截请求:

在过滤器的 filter 方法中,你可以获取 ServerWebExchange 对象,它代表了当前的请求-响应交互。通过这个对象,你可以访问请求参数、请求头、请求体等。

3. 检查请求参数:

使用 ServerHttpRequest 对象的 getQueryParams() 方法来获取请求参数。然后,你可以检查这些参数是否满足你的条件。

4. 执行自定义逻辑:

基于请求参数的值,你可以执行各种操作,如修改请求、记录日志、拒绝请求等。

5. 继续请求处理:

最后,确保通过调用 chain.filter(exchange) 将请求传递给过滤器链中的下一个过滤器。

以下是一个简单的示例,展示了如何创建一个检查特定请求参数的过滤器:

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;  
  
import java.util.function.Predicate;  
  
@Component  
public class CustomParameterFilter implements GlobalFilter, Ordered {  
  
    private static final String PARAMETER_NAME = "myParam";  
    private static final Predicate<String> PARAMETER_VALIDATOR = s -> s != null && !s.isEmpty();  
  
    @Override  
    public int getOrder() {  
        // 设置过滤器的顺序  
        return -1;  
    }  
  
    @Override  
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {  
        // 获取请求参数  
        String myParam = exchange.getRequest().getQueryParams().getFirst(PARAMETER_NAME);  
  
        // 验证参数  
        if (!PARAMETER_VALIDATOR.test(myParam)) {  
            // 参数无效,可以记录日志、返回错误响应等  
            // 这里为了示例简单,我们直接返回一个错误响应  
            exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);  
            return exchange.getResponse().setComplete();  
        }  
  
        // 参数有效,继续处理请求  
        return chain.filter(exchange);  
    }  
}

8、Gateway解决跨域问题

① 配置文件解决跨域

采用的是CORS方案

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 # 这次跨域检测的有效期

② 配置类解决跨域

 
@Configuration
public class GulimallCorsConfiguration {
    /**
     * 添加跨域过滤器
     * @return
     */
    @Bean
    public CorsWebFilter corsWebFilter(){
        //基于url跨域,选择reactive包下的
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 跨域配置信息
        CorsConfiguration configuration = new CorsConfiguration();
        // 允许跨域的头
        configuration.addAllowedHeader("*");
        // 允许跨域的请求方式
        configuration.addAllowedMethod("*");
        // 允许跨域的请求来源
        configuration.addAllowedOrigin("*");
        // 是否允许携带cookie跨域
        configuration.setAllowCredentials(true);
        // 任意url都要进行跨域配置
        source.registerCorsConfiguration("/**", configuration);
        return new CorsWebFilter(source);
    }
}

③ 过滤器方式解决跨域

package com.example.springcloudgateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class CrossOriginFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = response.getHeaders();
        //允许进行跨域请求的资源
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, PUT,OPTIONS, DELETE, PATCH");
        //允许请求携带认证信息(如cookies, HTTP认证及客户端SSL证明等)
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
        //允许的头信息。但注意这里使用了*,这意味着允许任何头信息。
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");
        //浏览器端通过XMLHttpRequest的getResponseHeader()方法能够访问到的服务器端的响应头信息
        headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
        return chain.filter(exchange);
    }
}

9、Gateway与WebFlux与Netty三者之间有什么关系

SpringCloudGateway使用了WebFlux作为web层框架,WebFlux又使用了Reactor Netty作为底层的网络编程框架,Reactor Netty又是基于Netty的异步事件驱动的网络应用框架

SpringCloudGateway:提供了路由,过滤,限流,熔断等API网关的功能,可以对微服务进行统一的管理和保护

WebFlux:提供了响应式编程的支持,可以处理异步和流式的数据,提供了基于注解和函数式的两种编程模型

Reactor Netty:提供了非阻塞和反压支持的TCP, HTTP, 和UDP客户端和服务器,可以充分利用CPU资源,避免频繁的线程切换和阻塞,提高并发处理能力

Netty:提供了一个高性能的网络编程框架,支持多种协议和传输层,提供了零拷贝,内存池,序列化等优化技术

Netty为什么性能高

netty采用了IO多路复用技术,多个IO的阻塞复用到一个select线程阻塞上,能够有效的应对大量的并发请求

基于多路复用Reactor线程模型,将服务端监听线程和IO线程分离,使用NioEventLoop来表示一个不断循环执行处理任务的线程,每个NioEventLoop有一个selector,用于监听绑定在其上的socket链路,可以充分利用CPU资源,减少线程切换与阻塞

netty采用了无锁化串行设计,即消息的处理尽可能在一个线程内完成,期间不进行线程切换,避免了多线程竞争和同步锁的使用。同时通过调整NioEventLoopGroup的线程参数,可以同时启动多个串行化的线程并行运行,提升性能。

netty内部使用了大量的比如volatile,CAS和原子类等兼顾性能的情况下提供并发安全的支持

netty采用了高性能的序列化框架,默认提供了对Google Protobuf的支持,Protobuf序列化后的码流只有Java序列化的1/4左右,节省了网络带宽和CPU资源。

netty采用了零拷贝技术,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝,同时提供了组合Buffer对象,可以聚合多个ByteBuffer对象,避免了内存拷贝,还使用了transferTo方法,可以直接将文件缓冲区的数据发送到目标Channel

netty采用了内存池技术,可以重用ByteBuf对象,内部维护了一个内存池,提升内存的使用效率,降低GC

netty提供了灵活的TCP参数配置能力,可以根据不同的场景合理设置TCP参数,例如SO_RCVBUF, SO_SNDBUF,SO_TCPNODELAY等,这些参数对于性能的影响是非常大的

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

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

相关文章

Android中的消息异步处理机制及实现方案

基本介绍 当我们需要执行复杂的计算逻辑&#xff0c;网络请求等耗时操作时&#xff0c;服务器可能不会立即响应请求&#xff0c;如果不将这类操作放在子线程中运行&#xff0c;就会导致主线程被阻塞住&#xff0c;从而影响用户的使用体验如果想要更新应用程序中的UI控件&#…

JDK17 你的下一个白月光

JDK版本升级的非常快&#xff0c;现在已经到JDK20了。JDK版本虽多&#xff0c;但应用最广泛的还得是JDK8&#xff0c;正所谓“他发任他发&#xff0c;我用Java8”。 但实际情况却不是这样&#xff0c;越来越多的java工程师拥抱 JDK17&#xff0c;于是了解了一下 JDK17新语法&a…

亲测几十款随身wifi,全网最全随身WiFi避坑指南!最值得买的随随身wifi品牌推荐!

关于随身wifi我认为我是比较有发言权的&#xff0c;历经三年测评了几十种随身wifi&#xff0c;便宜的贵的&#xff0c;大牌的小厂的&#xff0c;电池款USB款等各种随身wifi。根据测试结果以及通过电商平台搜索、粉丝反馈、社交平台评价等综合测评结果。今天就跟大家分享一下&am…

如何打造电力全域知识中心:知识库融合知识图谱

前言 随着人工智能技术的进步&#xff0c;智能化成为产业转型升级的关键抓手&#xff0c;国家电网在“十四五”发展规划中提出加快公司数字化转型进程、推进能源互联网企业建设的要求。知识管理能力建设作为强化企如何打造电力全域知识中心&#xff1a;知识库融合知识图谱业运…

5G消息 x 文旅 | 一站式智慧文旅解决方案

5G消息 x 文旅 | 一站式智慧文旅解决方案 文旅 x 5G 消息将进一步强化资源整合&#xff0c;满足游客服务需求、企业营销需求、政府管理需求&#xff0c;推进文化旅游项目的智慧化、数字化&#xff0c;增强传播力、竞争力和可持续性。5G 消息的“原生入口”、“超强呈现”、“智…

matlab 路面点云标线提取

目录 一、算法原理二、代码实现三、结果展示四、参考链接本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 算法来自本人自创。实现效果如下图所示,具体实现原理看代码即可。 二、代码实现 clc; cle…

构建旧物回收系统的决策支持系统

内容概要&#xff1a; 在旧物回收系统中&#xff0c;构建一个有效的决策支持系统对于提高管理效率、优化资源配置具有重要意义。本文将探讨如何构建旧物回收系统的决策支持系统&#xff0c;并分析其如何辅助管理者做出更科学的决策。 一、决策支持系统的定义与功能 决策支持…

Opencv数一数有多少个水晶贴纸?

1.目标-数出有多少个贴纸 好久没更新博客了&#xff0c;最近家里小朋友在一张A3纸上贴了很多水晶贴纸&#xff0c;要让我帮他数有多少个&#xff0c;看上去有点多&#xff0c;贴的也比较随意&#xff0c;于是想着使用Opencv来识别一下有多少个。 原图如下&#xff1a; 代码…

校园车辆管理系统的设计与实现

第1章 绪论 1.1 研究背景与意义 随着高等教育的普及和扩张&#xff0c;大学校园已成为一个综合性的小型社会。教学楼、实验室、宿舍、体育设施等构成了庞大且复杂的校园基础设施。在这样的环境下&#xff0c;教师、学生、家长及访客的车辆数量也随之增多&#xff0c;这不仅带来…

python-02

面向对象 Python中把具有相同属性和方法的对象归为一个类。 class ClassName: 语句 class Myclass: # 定义类Myclassdef pp(self): # 定义方法pp()print("这是产品说明书")myclass Myclass() # 实例化类Myclass myclass.pp() # 调用Myclass中的方法pp()打印…

电脑缺失d3dcompiler_47.dll会怎么样,该如何修复呢

在计算机使用过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“缺少d3dcompiler47.dll文件”。那么&#xff0c;d3dcompiler47.dll到底是什么&#xff1f;为什么计算机会缺失它&#xff1f;它会对电脑产生什么具体影响&#xff1f;如何解决这个问题&…

⭐Unity 控制任意UI的渐隐渐显

使用脚本之前先给要控制的UI加上CanvasGroup组件 解释: 这个脚本使用协程来逐渐改变CanvasGroup的alpha值&#xff0c;从而实现渐隐和渐显的效果。 Mathf.Lerp函数用于在指定的时间内平滑地从当前透明度过渡到目标透明度。 通过调用FadeIn和FadeOut方法&#xff0c;你可以在任…

SpringBoot 实现 阿里云语音通知(SingleCallByTts)

目录 一、准备工作1.开通 阿里云语音服务2.申请企业资质3.创建语音通知模板&#xff0c;审核通过4.调用API接口---SingleCallByTts5.调试API接口---SingleCallByTts 二、代码实现1.导入依赖 com.aliyun:aliyun-java-sdk-dyvmsapi:3.0.22.创建工具类&#xff0c;用于发送语音通知…

C++面向对象程序设计 - 函数库

C语言程序中各种功能基本上都是由函数来实现的&#xff0c;在C语言的发展过程中建立了功能丰富的函数库&#xff0c;C从C语言继承了些函数功能。如果要用函数库中的函数&#xff0c;就必须在程序文件中包含文件中有关的头文件&#xff0c;在不同的头文件中&#xff0c;包含了不…

RabbitMQ实践——交换器(Exchange)绑定交换器

在《RabbitMQ实践——交换器&#xff08;Exchange&#xff09;和绑定&#xff08;Banding&#xff09;》一文中&#xff0c;我们实验了各种交换器。我们可以把交换器看成消息发布的入口&#xff0c;而消息路由规则则是由“绑定关系”&#xff08;Banding&#xff09;来定义&…

GitLab教程(二):快速上手Git

文章目录 1.将远端代码克隆到本地2.修改本地代码并提交到远程仓库3.Git命令总结git clonegit statusgit addgit commitgit pushgit log 首先&#xff0c;我在Gitlab上创建了一个远程仓库&#xff0c;用于演示使用Gitlab进行版本管理的完整流程&#xff1a; 1.将远端代码克隆到本…

快速构建本地RAG聊天机器人:使用LangFlow和Ollama实现无代码开发

基于LangChain的快速RAG应用原型制作方法 还记得构建智能聊天机器人需要数月编码的日子吗&#xff1f; LangChain这样的框架确实简化了开发流程&#xff0c;但对非程序员来说&#xff0c;数百行代码仍然是一道门槛。 有没有更简单的方法呢&#xff1f; 图片由 Ravi Palwe 在…

数字政协:迈向智慧时代,开启政协工作新篇章

在信息化浪潮席卷全球的今天&#xff0c;数字技术不仅改变了我们的生活方式&#xff0c;也深刻影响着政治生态的变革。其中&#xff0c;“数字政协”的崛起&#xff0c;正是新时代政协工作创新发展的重要标志。那么&#xff0c;什么是数字政协&#xff1f;它又将如何助力政协工…

[图解]建模相关的基础知识-09

1 00:00:01,350 --> 00:00:03,780 首先&#xff0c;我们来看一下什么叫关系 2 00:00:05,370 --> 00:00:08,990 这个关系跟下面说的这些关系 3 00:00:09,000 --> 00:00:10,390 它不是一个东西 4 00:00:11,110 --> 00:00:14,950 比如说&#xff0c;我们UML类图上&…

门控循环单元GRU与长短期记忆网络LSTM

门控循环单元与长短期记忆网络 门控隐状态 问题提出&#xff1a;对于一个序列来说不是每个观察值都是同等重要想只记住相关的观察需要&#xff1a; 能关注的机制&#xff08;更新门&#xff09;能遗忘的机制&#xff08;重置门&#xff09; 第一个词元的影响至关重要。 我们…