【Spring Cloud 八】Spring Cloud Gateway网关

gateway网关

  • 系列博客
  • 背景
  • 一、什么是Spring Cloud Gateway
  • 二、为什么要使用Spring Cloud Gateway
  • 三、 Spring Cloud Gateway 三大核心概念
    • 4.1 Route(路由)
    • 4.2 Predicate(断言)
    • 4.3 Filter(过滤)
  • 五、Spring Cloud Gateway是如何工作的
  • 四、 如何使用Spring Cloud Gateway进行服务路由
    • 4.1搭建服务A
      • pom文件
      • yml配置文件
      • 启动类
      • controller类
    • 4.2 搭建服务B
      • pom文件
      • yml配置文件
      • 启动类
      • controller类
    • 4.3搭建Spring Cloud Gateway服务
      • pom文件
      • yml配置文件
      • 启动类
    • 4.4启动项目
    • 4.5动态路由
  • 五、gateway过滤器
    • 5.1自定义全局过滤器
  • 六、使用gateway做token校验
    • 6.1实现的大致流程示意图:![](https://img-blog.csdnimg.cn/3001fa8b45324a61a95e0c06263528a7.png)
    • 6.2实现代码
      • gateway服务
        • pom.xml文件
        • TokenCheckFilter类
      • login-service服务
        • pom.xml文件
        • LoginController类
      • 访问测试
  • 总结
  • 升华

系列博客

【Spring Cloud一】微服务基本知识
【Spring Cloud 三】Eureka服务注册与服务发现
【Spring Cloud 四】Ribbon负载均衡
【Spring Cloud 五】OpenFeign服务调用
【Spring Cloud 六】Hystrix熔断
【Spring Cloud 七】Sleuth+Zipkin 链路追踪

背景

在项目中是使用了Gateway做统一的请求的入口,以及统一的跨域处理以及统一的token校验。但是这些工作都是之前的同事来做的,正好在新项目中也需要使用其进行统一的token校验。本着对Gateway更精进一步所以博主就对Gateway进行了较为全面的学习了解,包括动态路由、自定义过滤器、token校验和续活。

一、什么是Spring Cloud Gateway

Spring Cloud Gateway提供一种简单有效的方法来路由到对应的API上,并可以提供一些额外的功能,安全性、监控、度量、负载等等。

我们可以这样理解,Spring Cloud Gateway将该项目中所有服务对外提供的API聚集起来,并向外提供唯一的入口,同时提供了一些额外的功能,安全性、监控、度量、负载等等。

没使用Spring Cloud Gateway 之前的示意图
在这里插入图片描述
使用Spring Cloud Gateway之后的示意图:
在这里插入图片描述

二、为什么要使用Spring Cloud Gateway

  1. 统一入口并解除客户端与服务端的耦合:在微服务架构中,每个微服务都有自己入口,在没有它的时候客户端需要大量微服务的地址,会照成复杂的客户端代码。并且会暴露服务端的细节,如果服务端发生变化,可能会影响到客户端代码,导致维护困难。
  2. 动态路由和负载均衡:Spring Cloud GateWay 提供动态路由来应对当;网关来能够在多个服务实例之间进行负载均衡,提供整个系统的高可用。
  3. 请求过滤:在微服务架构中,可能需要对请求进行鉴权、身份验证一个中心化的地方来处理这些请求过滤和处理逻辑可以减少重复的代码和逻辑。
  4. 反应式和高性能:由于微服务架构的复杂性,需要一个能够处理高并发请求的解决方案,以确保系统在高负载情况下保持稳定。Spring Cloud Gateway是基于webFlux框架实现的,而webFlux框架底层使用了高性能Reactor模式通信框架的Netty。

三、 Spring Cloud Gateway 三大核心概念

4.1 Route(路由)

路由是由一个ID、一个目的URI、一组断言、一组Filter组成。
如果路由断言为真,那么说明这个路由被匹配上了。

4.2 Predicate(断言)

是一个java8函数断言。输入类型是一个Spring Framewordk ServerWebExchange。可以让你匹配HTTP上的任何请求。比如请求头和参数。

4.3 Filter(过滤)

是Spring WebFilter的实例,Spring Cloud Gateway中的Filter分为两种,分贝是Gateway Filter和Global Filter(一个是针对某一个路由的filter,例如对某一个接口做限流;一个是针对全局的filter,例如token校验,ip黑名单)。过滤器Filter将会对请求和响应进行修改处理。

五、Spring Cloud Gateway是如何工作的

Spring 官网
在这里插入图片描述
客户端向Spring Cloud Gateway发出请求。如果网关处理器映射器确定请求与路由匹配,则会将其发送到网关web处理器。它通过特定的过滤器链来运行请求。过滤器被虚线分割的原因是过滤器可以在发送代理请求之前和之后运行对应的逻辑。

四、 如何使用Spring Cloud Gateway进行服务路由

示例项目示意图:
在这里插入图片描述
备注:Eureka的搭建可以参考这篇博客:【Spring Cloud 三】Eureka服务注册与服务发现

这里之所以使用Eureka是为了之后做动态路由和负载均衡。

4.1搭建服务A

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.12.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.wangwei</groupId>
	<artifactId>login-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>login-service</name>
	<description>login-service</description>
	<properties>
		<java.version>8</java.version>
		<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
	</properties>
	<dependencies>

<!--		<dependency>-->
<!--			<groupId>org.springframework.boot</groupId>-->
<!--			<artifactId>spring-boot-starter-data-redis</artifactId>-->
<!--		</dependency>-->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

yml配置文件

server:
  port: 8081

spring:
  application:
    name: login-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
    register-with-eureka: true #设置为fasle 不往eureka-server注册,默认为true
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答


  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔


启动类

@SpringBootApplication
@EnableEurekaClient
public class LoginServiceApplication {

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

}

controller类

@RestController
public class LoginController {
    @GetMapping("doLogin")
    public String doLogin(){
       return "登陆成功";
    }
}

4.2 搭建服务B

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.wangwei</groupId>
    <artifactId>teacher-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>teacher-service</name>
    <description>teacher-service</description>
    <properties>
        <java.version>8</java.version>
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

yml配置文件

server:
  port: 8082

spring:
  application:
    name: teacher-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
    register-with-eureka: true #设置为fasle 不往eureka-server注册,默认为true
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答


  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔

启动类

@SpringBootApplication
@EnableEurekaClient
public class TeacherServiceApplication {

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

}

controller类

@RestController
public class TeacherController {
    @GetMapping("teach")
    public String teach(){
        return "教书学习";
    }
}

4.3搭建Spring Cloud Gateway服务

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.12.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.wangwei</groupId>
	<artifactId>gateway-server</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>gateway-server</name>
	<description>gateway-server</description>
	<properties>
		<java.version>8</java.version>
		<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
	</properties>
	<dependencies>
<!--		<dependency>-->
<!--			<groupId>org.springframework.boot</groupId>-->
<!--			<artifactId>spring-boot-starter-data-redis</artifactId>-->
<!--		</dependency>-->
<!--		<dependency>-->
<!--			<groupId>org.springframework.boot</groupId>-->
<!--			<artifactId>spring-boot-starter-data-redis-reactive</artifactId>-->
<!--		</dependency>-->

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-gateway</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

yml配置文件

server:
  port: 81 #?????80


spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      enabled: true
      routes:
        - id: login-service-route # 路由的id  保持唯一
          uri: http://localhost:8081 #uri统一资源标识符  url 统一资源定位符
          #uri: lb://login-service #??lb负载均衡
          predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
            - Path=/doLogin # 匹配规则 只要你Path配置上了/doLogin 就往uri转发并将路径带上
        - id: teacher-service-route
          url:  http://localhost:8082
          predicates:
            - Path=/teach



eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
    register-with-eureka: true #设置为fasle 不往eureka-server注册,默认为true
    fetch-registry: true #应用是否拉取服务列表到本地
    registry-fetch-interval-seconds: 10 #为了缓解服务列表的脏读问题,时间越短脏读越少 性能相应的消耗回答


  instance: #实例的配置
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    hostname: localhost #主机名称或者服务ip
    prefer-ip-address: true #以ip的形式显示具体的服务信息
    lease-renewal-interval-in-seconds: 10 #服务实例的续约时间间隔



启动类

@SpringBootApplication
@EnableEurekaClient
public class GatewayServerApplication {

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

}

4.4启动项目

依次启动Eureka服务,gateway服务和两个服务A和服务B。

进行调用服务A和服务B:
如下图所示通过gateway的ip+端口+路径调用到对应的服务A中和服务B中。

在这里插入图片描述

在这里插入图片描述

4.5动态路由

在微服务中通常一个服务的实例有多个,那我们网关如何做负载均衡呢?
gateway帮我们做了很多东西,只要我们集成注册中心(nacos、Eureka、zoomkeeper)并添加对应的配置gateway就可以自动帮我们进行负载均衡。

方式一:
添加对应的配置信息,来开启动态路由

    gateway:
      enabled: true
      routes:
        - id: login-service-route # 路由的id  保持唯一
          uri: http://localhost:8081 #uri统一资源标识符  url 统一资源定位符
          #uri: lb://login-service #??lb负载均衡
          predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
            - Path=/doLogin # 匹配规则 只要你Path配置上了/doLogin 就往uri转发并将路径带上
        - id: teacher-service-route
          uri:  http://localhost:8082
          predicates:
            - Path=/teach
      discovery:
        locator:
          enabled: true #开启动态路由  开启通过应用名称找到服务的功能
          lower-case-service-id: true  # 开启服务名称小写

请求服务时需要带上对应的服务名称
在这里插入图片描述

方式二:
添加对应的配置:uri: lb://login-service #lb负载均衡

    gateway:
      enabled: true
      routes:
        - id: login-service-route # 路由的id  保持唯一
          #uri: http://localhost:8081 #uri统一资源标识符  url 统一资源定位符
          uri: lb://login-service #lb负载均衡
          predicates: # 断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
            - Path=/doLogin # 匹配规则 只要你Path配置上了/doLogin 就往uri转发并将路径带上
        - id: teacher-service-route
          uri:  http://localhost:8082
          predicates:
            - Path=/teach

在这里插入图片描述

推荐使用方式一进行统一的配置负载均衡。

五、gateway过滤器

过滤器按照作用范围可以分为两种,Gateway Filter和Global Filter。

Gateway Filter:网关过滤器需要通过 spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过 spring.cloud.default-filters 配置在全局,作用在所有路由上。

Global Filter:全局过滤器,不需要配置路由,系统初始化作用在所有路由上。

全局过滤器一般用于:统计请求次数、限流、token校验、ip黑名单拦截等。

5.1自定义全局过滤器

@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
    /**
     * 过滤的方法
     * 职责链模式
     * 网关里面有使用 mybatis的二级缓存有变种职责链模式
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //针对请求的过滤 拿到请求 header url 参数。。。
        ServerHttpRequest request = exchange.getRequest();
        //HttpServletRequest 这个是web里面的
        //ServerHttpRequest webFlux里面 响应式里面的
        String path=request.getURI().getPath();
        System.out.println(path);
        HttpHeaders headers=request.getHeaders();
        System.out.println(headers);
        String name = request.getMethod().name();
        String hostString = request.getHeaders().getHost().getHostString();
        System.out.println(hostString);

        //响应相关数据
        ServerHttpResponse response = exchange.getResponse();
        //微服务 肯定是前后端分离的 一般前后端通过数据传输是json格式
        //{"code":200,"msg":"ok"}
        //设置编码 响应头

        response.getHeaders().set("content-type","application/json;charset=utf-8");
        //组装业务返回值
        HashMap<String ,Object> map=new HashMap<>(4);
        map.put("code", HttpStatus.UNAUTHORIZED.value());
        map.put("msg","你未授权");
        ObjectMapper objectMapper=new ObjectMapper();
        //把一个map转换为字节
        byte[] bytes = new byte[0];
        try {
            bytes = objectMapper.writeValueAsBytes(map);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        //通过buffer工厂将字节数组包装成一个数据报
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(wrap));
        //放行,到下一个过滤器
        //return chain.filter(exchange);
    }

    /**
     * 制定顺序的方法
     * 越小越先执行
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

请求接口进行访问,可以发现当前请求已经被拦截下来。

在这里插入图片描述

六、使用gateway做token校验

6.1实现的大致流程示意图:

6.2实现代码

gateway服务

pom.xml文件

在pom文件中新增redis的依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

注意博主是在本机安装的redis所以不需要在服务中添加对应的redis配置。

TokenCheckFilter类

新建TokenCheckFilter类并实现全局过滤器和Ordered

package com.wangwei.gatewayserver.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;


@Component
public class TokenCheckFilter implements GlobalFilter, Ordered {
    /**
     * 指定好放行的路径,白名单
     */
    public static final List<String> ALLOW_URL= Arrays.asList("/doLogin","/myUrl");

    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * 和前端约定好 一般放在请求头类里面一般key为 Authorization value bearer token
     * 1.拿到请求url
     * 2.判断放行
     * 3.拿到请求头
     * 4.拿到token
     * 5.校验
     * 6.放行/拦截
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        if (ALLOW_URL.contains(path)) {
            return chain.filter(exchange);
        }
        //检查
        List<String> authorization = request.getHeaders().get("Authorization");
        if (!CollectionUtils.isEmpty(authorization)){
            String token = authorization.get(0);
            if(StringUtils.hasText(token)){
                //约定好的有前缀的bearer token
                String realToken = token.replaceFirst("bearer ", "");
                if (StringUtils.hasText(realToken)&&redisTemplate.hasKey(realToken)){
                    return chain.filter(exchange);
                }
            }
        }
        //拦截
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().set("content-type","application/json;charset=utf-8");
        //组装业务返回值
        HashMap<String ,Object> map=new HashMap<>(4);
        map.put("code", HttpStatus.UNAUTHORIZED);
        map.put("msg","未授权");
        ObjectMapper objectMapper=new ObjectMapper();
        //把一个map转换为字节
        byte[] bytes = new byte[0];
        try {
            bytes = objectMapper.writeValueAsBytes(map);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        //通过buffer工厂将字节数组包装成一个数据报
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        return response.writeWith(Mono.just(wrap));

    }

    @Override
    public int getOrder() {
        return 0;
    }
}

login-service服务

pom.xml文件

在pom文件中新增redis的依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

注意博主是在本机安装的redis所以不需要在服务中添加对应的redis配置。

LoginController类

在LoginController类中新增doLogin方法

@Autowired
    public StringRedisTemplate redisTemplate;

    @PostMapping("doLogin")
    public String doLogin(@RequestBody User user){
        //token
        String token= UUID.randomUUID().toString();
        //存起来
        redisTemplate.opsForValue().set(token,user.toString(), Duration.ofSeconds(7200));
        return token;
    }

访问测试

http://localhost:81/doLogin

由于在gateway将该/doLogin放入了白名单,所以该请求不会进行token校验,发送请求成功之后会返回token
在这里插入图片描述
访问http://localhost:81/teach,并在请求头中添加token,最后可以看到请求访问成功。

在这里插入图片描述

如果我们不在请求头中添加对应的token或者token为错误token,那么gateway会将请求进行拦截。

在这里插入图片描述

总结

  1. gateway在微服务中起到了很重要的作用,作为项目请求的统一入口。
  2. gateway也体现了我们软件设计的复用思想,可以统一进行跨域处理,进行token校验。
  3. gateway比较重要的一块是关于他的过滤器,通过实现过滤器的接口,可以自定义过滤器,提供了很强大的可扩展支持

升华

通过学习gateway不光是学习gateway更重要的是学习软件设计思想。

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

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

相关文章

无脑入门pytorch系列(三)—— nn.Linear

本系列教程适用于没有任何pytorch的同学&#xff08;简单的python语法还是要的&#xff09;&#xff0c;从代码的表层出发挖掘代码的深层含义&#xff0c;理解具体的意思和内涵。pytorch的很多函数看着非常简单&#xff0c;但是其中包含了很多内容&#xff0c;不了解其中的意思…

8.1.5:Extreme Optimization Numerical Libraries for .NET

构建金融、工程和科学应用程序。 Extreme Optimization Numerical Libraries for .NET 是通用数学和统计类的集合。它为基于 Microsoft .NET 平台构建的技术和统计计算提供了一个完整的平台。它将数学库、向量和矩阵库以及统计库结合在一个方便的包中。 一般特征 即使对数学不太…

wxPython两个页面跳转简单方案

import wxclass Frame1(wx.Frame):def __init__(self, parent):super().__init__(parent)panel wx.Panel(self)button wx.Button(panel, label"跳转到Frame2")button.Bind(wx.EVT_BUTTON, self.on_button_click)sizer wx.BoxSizer(wx.VERTICAL)sizer.Add(button, …

ArduPilot开源飞控之飞行模式

ArduPilot开源飞控之飞行模式 1. 源由2. 飞行模式-已实现3. 飞行模式-设计3.1 模式初始化(init)3.2 模式退出(exit)3.3 模式任务(run)3.4 模式切换场景3.4.1 上电初始化3.4.2 EKF FAILSAFE触发3.4.3 do_failsafe_action FAILSAFE触发3.4.4 AP_Avoidance_Copter触发3.4.5 Crash触…

水库大坝安全监测MCU,提升大坝管理效率的利器!

水库大坝作为防洪度汛的重要设施&#xff0c;承担着防洪抗旱&#xff0c;节流发电的重要作用。大坝的安全直接关系到水库的安全和人民群众的生命财产安全。但因为水库大坝的隐患不易被察觉&#xff0c;发现时往往为时已晚。因此&#xff0c;必须加强对大坝的安全管理。其安全监…

linux 系统中vi 编辑器和库的制作和使用

目录 1 vim 1.1 vim简单介绍 1.2 vim的三种模式 1.3 vim基本操作 1.3.1命令模式下的操作 1.3.2 切换到文本输入模式 1.3.3 末行模式下的操作 2 gcc编译器 2.1 gcc的工作流程 2.2 gcc常用参数 3 静态库和共享&#xff08;动态&#xff09;库 3.1库的介绍 3.2静态…

blender 发射体粒子

发射体粒子的基础设置 选择需要添加粒子的物体&#xff0c;点击右侧粒子属性&#xff0c;在属性面板中&#xff0c;点击加号&#xff0c;物体表面会出现很多小点点&#xff0c;点击空格键&#xff0c;粒子会自动运动&#xff0c;像下雨一样&#xff1b; bender 粒子系统分为两…

AI 绘画Stable Diffusion 研究(八)sd采样方法详解

大家好&#xff0c;我是风雨无阻。 本文适合人群&#xff1a; 希望了解stable Diffusion WebUI中提供的Sampler究竟有什么不同&#xff0c;想知道如何选用合适采样器以进一步提高出图质量的朋友。 想要进一步了解AI绘图基本原理的朋友。 对stable diffusion AI绘图感兴趣的朋…

2023国赛数学建模思路 - 案例:ID3-决策树分类算法

文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模…

Mybatis的SqlSource SqlNode BoundSql

学习链接 MyBatis SqlSource解析 【Mybatis】Mybatis源码之SqlSource#getBoundSql获取预编译SQL Mybatis中SqlSource解析流程详解 Mybatis TypeHandler解析 图解 Mybatis的SqlSource&SqlNode - processon DynamicSqlSource public class DynamicSqlSource implement…

Java SpringBoot Vue ERP系统

系统介绍 该ERP系统基于SpringBoot框架和SaaS模式&#xff0c;支持多租户&#xff0c;专注进销存财务生产功能。主要模块有零售管理、采购管理、销售管理、仓库管理、财务管理、报表查询、系统管理等。支持预付款、收入支出、仓库调拨、组装拆卸、订单等特色功能。拥有商品库存…

WebGL和OpenGL之间的差异

推荐&#xff1a;使用 NSDT场景编辑器助你快速搭建可二次编辑的3D应用场景 WebGL和OpenGL是与图形处理有关的技术标准&#xff0c;它们在计算机图形中扮演着重要的角色。本文将介绍WebGL和OpenGL的区别&#xff0c;并重点介绍"WebGL"和"OpenGL"的特点。 一…

《算法竞赛·快冲300题》每日一题:“糖果配对”

《算法竞赛快冲300题》将于2024年出版&#xff0c;是《算法竞赛》的辅助练习册。 所有题目放在自建的OJ New Online Judge。 用C/C、Java、Python三种语言给出代码&#xff0c;以中低档题为主&#xff0c;适合入门、进阶。 文章目录 题目描述题解C代码Java代码Python代码 “ 糖…

docker的网络模式

docker0网络 docker容器的 虚拟网关loopback &#xff1a;回环网卡、TCP/IP网卡是否生效virtual bridge&#xff1a;linux 自身继承了一个虚拟化功能&#xff08;kvm架构&#xff09;&#xff0c;是原生架构的一个虚拟化平台&#xff0c;安装了一个虚拟化平台之后就会系统就会自…

excel入门

上下左右移动 enter:换行&#xff0c;向下移动 shiftenter:向上移动 tab:向右移动 shifttab:向左移动 合并居中操作 开始-》合并居中 CtrlM 内容过长盖过了下一个单元格内容 双击列与列之间线 同时修改多行或者多列宽度或者高度 修改单行高度宽度 选中某一行拉取指定高…

电脑提示msvcp140.dll丢失的解决方法,dll组件怎么处理

Windows系统有时在打开游戏或者软件时&#xff0c; 系统会弹窗提示缺少“msvcp140.dll.dll”文件 或者类似错误提示怎么办&#xff1f; 错误背景&#xff1a; msvcp140.dll是Microsoft Visual C Redistributable Package中的一个动态链接库文件&#xff0c;它在运行软件时提…

调整数组使奇数全部都位于偶数前面

题目内容&#xff1a; 输入一个整数数组&#xff0c;实现一个函数&#xff0c; 来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分&#xff0c; 所有偶数位于数组的后半部分。 题目思路&#xff1a; 将奇数部分放在前半部分&#xff0c;偶数部分放在后半部分&am…

【24择校指南】齐鲁工业大学计算机考研考情分析

齐鲁工业大学 考研难度&#xff08;☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、23专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文1140字&#xff0c;预计阅读&#xff1a;3分钟。 2023考情概况 齐鲁工…

VB6编程IEEE浮点算法实践

纯代码实现浮点计算实际上对浮点算法的再实践。IEEE浮点表示法是Modbus RTU协议至今还在用的传送编码&#xff0c;更是WITS 1记录标准的基础。以往实现 MKI、CVI&#xff0c;MKL、CVL&#xff0c;MKS、CVS&#xff0c;MKD、CVD在高级语言里封装了现成的语句&#xff0c;现在Pow…

SCF金融公链新加坡启动会 链结创新驱动未来

新加坡迎来一场引人瞩目的金融科技盛会&#xff0c;SCF金融公链启动会于2023年8月13日盛大举行。这一受瞩目的活动将为金融科技领域注入新的活力&#xff0c;并为广大投资者、合作伙伴以及关注区块链发展的人士提供一个难得的交流平台。 在SCF金融公链启动会上&#xff0c; Wil…