SpringCloud之Gateway

news2024/11/26 17:48:38

(学习笔记)

1、概述

官网:Spring Cloud Gateway

Gateway是在Spring生态系统之上构建的API网关服务,基于Spring6,Spring Boot 3和Project Reactor等技术。它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式,并为它们提供跨领域的关注点,例如:安全性、监控/度量和恢复能力。 

2、体系定位 

Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关;但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关SpringCloud Gateway替代Zuul,那就是SpringCloud Gateway一句话:Gateway是原zuul1.x版的替代

 

3、作用 

反向代理 

鉴权

流量控制

熔断

日志监控

总结: 

Spring Cloud Gateway组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对应的微服务。

Spring Cloud Gateway是加在整个微服务最前沿的防火墙和代理器,隐藏微服务结点IP端口信息,从而加强安全保护。

Spring Cloud Gateway本身也是一个微服务,需要注册进服务注册中心。

4、Gateway三大核心 

4.1、Route(路由) 

路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由

4.2、Predicate(断言)

参考的是Java8的java.util.function.Predicate;开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由

4.3、Filter(过滤)

指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。

4.4、总结

Web前端请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。

predicate就是我们的匹配条件;

filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了

5、工作流程 

客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。

过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(Pre)或之后(Post)执行业务逻辑。

在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等;

在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。

6、入门配置 

(1)创建Module,改pom

 <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

(2)yml

server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}

(3)主启动类(^(* ̄(oo) ̄)^ 不写任务业务代码)

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

 (4)测试

 

7、路由映射 

7.1:测试一

诉求:不想暴露8001端口,希望在8001真正的支付微服务外面套一层9527网关

(1)cloud-payment8001 添加测试接口

@RestController
public class PayGateWayController {

    @Resource
    private PayService payService;

    @GetMapping(value = "/pay/gateway/get/{id}")
    public ResultData<Pay> getById(@PathVariable("id") Integer id) {
        Pay pay = payService.getPayById(id);
        return ResultData.success(pay);
    }

    @GetMapping(value = "/pay/gateway/info")
    public ResultData<String> getGatewayInfo() {
        return ResultData.success("gateway info test:"+ IdUtil.simpleUUID());
    }
}

(2)cloud-gateway9527 新增yml配置

server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

 (3)测试

7.2、测试二

我们启动80订单微服务,它从Consul注册中心通过微服务名称找到8001支付微服务进行调用,

80 → 9527 → 8001

要求访问9527网关后才能访问8001,如果我们此时启动80订单,可以做到吗?

 (1)修改cloud-api-commons 的 PayFeignApi 

@FeignClient(value = "cloud-payment-service")
public interface PayFeignApi {

    /**
     * GateWay进行网关测试案例01
     * @param id
     * @return
     */
    @GetMapping(value = "/pay/gateway/get/{id}")
    public ResultData getById(@PathVariable("id") Integer id);

    /**
     * GateWay进行网关测试案例02
     * @return
     */
    @GetMapping(value = "/pay/gateway/info")
    public ResultData<String> getGatewayInfo();
}

(2)cloud-consumer-feign-order80,新建OrderGateWayController

@RestController
public class OrderGateWayController {
    @Resource
    private PayFeignApi payFeignApi;

    @GetMapping(value = "/feign/pay/gateway/get/{id}")
    public ResultData getById(@PathVariable("id") Integer id) {
        return payFeignApi.getById(id);
    }

    @GetMapping(value = "/feign/pay/gateway/info")
    public ResultData<String> getGatewayInfo() {
        return payFeignApi.getGatewayInfo();
    }
}

(3)测试

cloud-gateway9527 网关开启和关闭都访问通过 

结论:

9527网关是否启动,毫无影响,o(╥﹏╥)o

目前的配置来看,网关被绕开了...... 

7.3、正确做法 

(1)同一家公司自己人,系统内环境,直接找微服务

@FeignClient(value = "cloud-payment-service")//自己人内部,自己访问自己,写微服务名字OK
public interface PayFeignApi
{
    /**
     * GateWay
进行网关测试案例01
     * 
@param id
     
@return
     
*/
    
@GetMapping(value = "/pay/gateway/get/{id}")
    public ResultData getById(@PathVariable("id") Integer id);

    /**
     * GateWay
进行网关测试案例02
     * 
@return
     
*/
    
@GetMapping(value = "/pay/gateway/info")
    public ResultData<String> getGatewayInfo();

(2) 不同家公司有外人,系统外访问,先找网关再服务

8、Gateway高级特性 

8.1、Route以微服务名-动态获取服务URI 

 (1)痛点:

(2)解决uri地址写死问题

将url地址改成服务名,9527服务修改后的yml

spring:
  application:
    name: cloud-gateway #以微服务注册进consulnacos服务列表内
  
cloud:
    consul#配置consul地址
      
host: localhost
      port8500
      
discovery:
        prefer-ip-addresstrue
        service-name
: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          
#uri: http://localhost:8001                #匹配后提供服务的路由地址
          
uri: lb://cloud-payment-service          #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              断言,路径相匹配的进行路由

        
id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          
#uri: http://localhost:8001                #匹配后提供服务的路由地址
          
uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              断言,路径相匹配的进行路由 

 8.2、Predicate断言(谓词)

 8.2.1、介绍

官网:Spring Cloud Gateway

IDEA启动控制台打印: 

整体架构概述:

8.2.2、常用的内置Route Predicate 

官网:Spring Cloud Gateway

 Most examples below use the shortcut way

Shortcut Configuration 

Fully Expanded Arguments 

8.2.2.1、The After Route Predicate Factory (用于秒杀、抢购之类的场景)

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - After=2024-09-09T16:07:32.204+08:00[Asia/Shanghai]

如何获得ZonedDateTime

public class ZonedDateTimeDemo{
    public static void main(String[] args){
        ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
              System.out.println(zbj);
    }
}

8.2.2.2、The Before Route Predicate Factory 

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - Before=2024-09-09T16:10:32.204+08:00[Asia/Shanghai]

 8.2.2.3、The Between Route Predicate Factory

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - Between=2024-09-09T16:10:32.204+08:00[Asia/Shanghai], 2024-09-09T16:13:32.204+08:00[Asia/Shanghai]
8.2.2.4、The Cookie Route Predicate Factory 

Cookie Route Predicate需要两个参数,一个是 Cookie name ,一个是正则表达式。
路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - Cookie=chocolate, ch.p

测试

不带cookie

curl http://localhost:9527/pay/gateway/get/1

带cookie

curl http://localhost:9527/pay/gateway/get/1 --cookie "chocolate=ch.p"

 

8.2.2.5、The Header Route Predicate Factory 

两个参数:一个是属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。 

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式

测试

curl http://localhost:9527/pay/gateway/get/1 -H  "X-Request-Id:123456"

 

curl http://localhost:9527/pay/gateway/get/1 -H  "X-Request-Id:abcd"

 

 8.2.2.6、The Host Route Predicate Factory

Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.号作为分隔符。
它通过参数中的主机地址作为匹配规则。 

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - Host=**.test.com

测试

 正确:     curl http://localhost:9527/pay/gateway/get/1 -H  "Host:www.test.com"

正确:     curl http://localhost:9527/pay/gateway/get/1 -H  "Host:java.test.com"

错误:     curl http://localhost:9527/pay/gateway/get/1 -H  "Host:java.test.net"

8.2.2.7、The Method Route Predicate Factory

8.2.2.8、The Path Route Predicate Factory

          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由 

8.2.2.9、The Query Route Predicate Factory 

支持传入两个参数,一个是属性名,一个为属性值,属性值可以是正则表达式。 

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - Query=username, \d+  # 要有参数名username并且值还要是整数才能路由

测试: 

http://localhost:9527/pay/gateway/get/1?username=123

 

http://localhost:9527/pay/gateway/get/1?username=abc 要有参数名username并且值还要是整数才能路由

8.2.2.10、The RemoteAddr Route Predicate Factory 

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - RemoteAddr=192.168.18.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。

测试

 8.3、自定义断言,XXXRoutePredicateFactory规则

8.3.1、源代码:

public abstract class AbstractRoutePredicateFactory<C> extends AbstractConfigurable<C> implements RoutePredicateFactory<C> 
{
  public AbstractRoutePredicateFactory(Class<C> configClass) 
  {
    super(configClass);
  }
}
8.3.2、架构概述

8.3.3、模版套路

要么继承AbstractRoutePredicateFactory抽象类

要么实现RoutePredicateFactory接口

开头任意取名,但是必须以RoutePredicateFactory后缀结尾

8.3.4、编写步骤 

(1)新建类名XXX需要以RoutePredicateFactory结尾,并继承AbstractRoutePredicateFactory类

@Component标注不可忘

public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config>
{

(2) 重写apply方法

@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config){
    return null;
}

(3) 新建apply方法所需要的静态内部类MyRoutePredicateFactory.Config

*** 这个Config类就是我们的路由断言规则

@Validated
public static class Config{
    @Setter
    @Getter
    @NotEmpty
    private String userType; //钻、金、银等用户等级
}

 (4)空参构造方法,内部调用super

public MyRoutePredicateFactory(){
    super(MyRoutePredicateFactory.Config.class);
}

(5)再次重写apply方法

@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config)
{
    return new Predicate<ServerWebExchange>()
    {
        @Override
        public boolean test(ServerWebExchange serverWebExchange)
        {
            //检查request的参数里面,userType是否为指定的值,符合配置就通过
            String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");

            if (userType == null) return false;

            //如果说参数存在,就和config的数据进行比较
            if(userType.equals(config.getUserType())) {
                return true;
            }

            return false;
        }
    };
}

 完整代码:

package com.atguigu.cloud.mygateway;

import jakarta.validation.constraints.NotEmpty;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import java.util.function.Predicate;

/**
 * 需求说明,自定义会员等级userType,按照钻/金/银和yml配置的会员等级,以适配是否可以访问
 */
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {

    //这个Config类就是我们的路由断言规则,重要
    @Validated
    public static class Config{
        @NotEmpty
        private String userType; //钻/金/银等级

        public String getUserType() {
            return userType;
        }

        public void setUserType(String userType) {
            this.userType = userType;
        }
    }

    public MyRoutePredicateFactory() {
        super(MyRoutePredicateFactory.Config.class);
    }
    @Override
    public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
                if (userType == null) {
                    return false;
                }
                return userType.equals(config.getUserType());
            }
        };
    }
}
8.3.5、测试一

yml

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - My=diamond

启动出现错误: 

导致原因:为什么Shortcut Configuration不生效? 

解决方案:Fully Expanded Arguments

yml

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - name: My
              args:
                userType: diamond

测试结果

8.3.6、测试二

上述bug分析:

缺少shortcutFieldOrder方法的实现,所以不支持短格式

完整代码

@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {

    //这个Config类就是我们的路由断言规则,重要
    @Validated
    public static class Config{
        @NotEmpty
        private String userType; //钻/金/银等级

        public String getUserType() {
            return userType;
        }

        public void setUserType(String userType) {
            this.userType = userType;
        }
    }

    public MyRoutePredicateFactory() {
        super(MyRoutePredicateFactory.Config.class);
    }
    @Override
    public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
                if (userType == null) {
                    return false;
                }
                return userType.equals(config.getUserType());
            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("userType");
    }
}

yml

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - My=diamond

8.4、Filter过滤

8.4.1、概念 

官网:Spring Cloud Gateway

作用:请求鉴权:异常处理等 

记录接口调用时长统计(大厂面试题)

类型

①全局默认过滤器Global Filters

官网:Spring Cloud Gateway

Gateway出厂默认已有的,直接用即可,主要作用于所有的路由

不需要在配置文件中配置,作用在所有的路由上,实现GlobalFilter接口即可

②单一内置过滤器GatewayFilter

也可以称为网关过滤器,这种过滤器主要是作用于单一路由或者某个路由分组

官网:Spring Cloud Gateway

③自定义过滤器

8.4.2、内置过滤器 

服务提供者cloud-provider-payment8001 添加接口

@RestController
public class PayGateWayController {

    @GetMapping(value = "/pay/gateway/filter")
    public ResultData<String> getGatewayFilter(HttpServletRequest request) {
        String result = "";
        Enumeration<String> headers = request.getHeaderNames();
        while(headers.hasMoreElements()) {
            String headName = headers.nextElement();
            String headValue = request.getHeader(headName);
            System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);
            if(headName.equalsIgnoreCase("X-Request-atguigu1")
                    || headName.equalsIgnoreCase("X-Request-atguigu2")) {
                result = result+headName + "\t " + headValue +" ";
            }
        }
        return ResultData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
    }
}
8.4.2.1、请求头(RequestHeader)相关组 
8.4.2.1.1、The AddRequestHeader GatewayFilter Factory (6.1)

添加请求头

yml

server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - My=diamond

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

        - id: pay_routh3 #pay_routh3
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
          filters:
            - AddRequestHeader=X-Request-atguigu1,atguiguValue1  # 请求头kv,若一头含有多参则重写一行设置
            - AddRequestHeader=X-Request-atguigu2,atguiguValue2

测试

curl http://localhost:9527/pay/gateway/filter

8.4.2.1.2、The RemoveRequestHeader GatewayFilter Factory (6.18) 

删除请求头

请求之前:

 yml

- RemoveRequestHeader=sec-fetch-site # 删除请求头sec-fetch-site

server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - My=diamond

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

        - id: pay_routh3 #pay_routh3
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
          filters:
            - AddRequestHeader=X-Request-atguigu1,atguiguValue1  # 请求头kv,若一头含有多参则重写一行设置
            - AddRequestHeader=X-Request-atguigu2,atguiguValue2
            - RemoveRequestHeader=sec-fetch-site      # 删除请求头sec-fetch-site

测试

 8.4.2.1.3、The SetRequestHeader GatewayFilter Factory(6.29)

修改请求头

修改之前

yml

- SetRequestHeader=sec-fetch-mode, Blue-updatebyzzyy 将请求头sec-fetch-mode对应的值修改为Blue-updatebyzzyy 

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - My=diamond

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

        - id: pay_routh3 #pay_routh3
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
          filters:
            - AddRequestHeader=X-Request-atguigu1,atguiguValue1  # 请求头kv,若一头含有多参则重写一行设置
            - AddRequestHeader=X-Request-atguigu2,atguiguValue2
            - RemoveRequestHeader=sec-fetch-site      # 删除请求头sec-fetch-site
            - SetRequestHeader=sec-fetch-mode, Blue-updatebyzzyy # 将请求头sec-fetch-mode对应的值修改为Blue-updatebyzzyy

测试结果:

8.4.2.2、请求参数(RequestParameter)相关组 

The AddRequestParameter GatewayFilter Factory(6.3)

The RemoveRequestParameter GatewayFilter Factory(6.19)

- AddRequestParameter=customerId,9527001 新增请求参数Parameterv
- RemoveRequestParameter=customerName   
删除url请求参数customerName,你传递过来也是null

server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - My=diamond

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

        - id: pay_routh3 #pay_routh3
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
          filters:
            - AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
            - RemoveRequestParameter=customerName   # 删除url请求参数customerName,你传递过来也是null

服务提供者cloud-provider-payment8001的接口修改

@RestController
public class PayGateWayController {

    @GetMapping(value = "/pay/gateway/filter")
    public ResultData<String> getGatewayFilter(HttpServletRequest request) {
        String result = "";
        Enumeration<String> headers = request.getHeaderNames();
        while(headers.hasMoreElements()) {
            String headName = headers.nextElement();
            String headValue = request.getHeader(headName);
            System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);
            if(headName.equalsIgnoreCase("X-Request-atguigu1")
                    || headName.equalsIgnoreCase("X-Request-atguigu2")) {
                result = result+headName + "\t " + headValue +" ";
            }
        }
        System.out.println("=============================================");
        String customerId = request.getParameter("customerId");
        System.out.println("request Parameter customerId: "+customerId);

        String customerName = request.getParameter("customerName");
        System.out.println("request Parameter customerName: "+customerName);
        System.out.println("=============================================");
        return ResultData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
    }
}

 测试1:

http://localhost:9527/pay/gateway/filter

 测试2

http://localhost:9527/pay/gateway/filter?customerId=9999&customerName=z3

8.4.2.3、回应头(ResponseHeader)相关组

The AddResponseHeader GatewayFilter Factory(6.4)

The SetResponseHeader GatewayFilter Factory(6.30) 

The RemoveResponseHeader GatewayFilter Factory(6.20)

- AddResponseHeader=X-Response-atguigu, BlueResponse 新增请求参数X-Response-atguigu并设值为BlueResponse

- SetResponseHeader=Date,2099-11-11 设置回应头Date值为2099-11-11

- RemoveResponseHeader=Content-Type 将默认自带Content-Type回应属性删除

server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - My=diamond

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

        - id: pay_routh3 #pay_routh3
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
          filters:
            - AddResponseHeader=X-Response-atguigu, BlueResponse # 新增请求参数X-Response-atguigu并设值为BlueResponse
            - SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
            - RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除

测试之前,请求url:http://localhost:9527/pay/gateway/filter

添加过滤器测试

8.4.2.4、前缀和路径相关组
8.4.2.4.1、The PrefixPath GatewayFilter Factory (6.14)

自动添加路径前缀

yml

server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - My=diamond

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

        - id: pay_routh3 #pay_routh3
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            #- Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
            - Path=/gateway/filter/**              # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay
          filters:
            - PrefixPath=/pay # http://localhost:9527/pay/gateway/filter

之前完整正确地址:
http://localhost:9527/pay/gateway/filter
现在完整组合地址:
PrefixPath + Path
实际调用地址:
http://localhost:9527/gateway/filter
相当于说前缀被过滤器统一管理了。

测试

8.4.2.4.2、The SetPath GatewayFilter Factory(6.29)

访问路径修改

yml

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - My=diamond

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

        - id: pay_routh3 #pay_routh3
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            #- Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
            - Path=/XYZ/abc/{segment}           # 断言,为配合SetPath测试,{segment}的内容最后被SetPath取代
          filters:
            - SetPath=/pay/gateway/{segment}  # {segment}表示占位符,你写abc也行但要上下一致

测试

 

8.4.2.4.3、The RedirectTo GatewayFilter Factory(6.16)

重定向到某个页面

yml

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - My=diamond

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service            #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

        - id: pay_routh3 #pay_routh3
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
          filters:
            - RedirectTo=302, http://www.atguigu.com/ # 访问http://localhost:9527/pay/gateway/filter跳转到http://www.atguigu.com/
8.4.2.5、其它 

  配置在此处相当于全局通用,自定义秒变Global

8.4.3、自定义过滤器 
8.4.3.1、自定义全局Filter 

官网:Spring Cloud Gateway

案例需求:统计接口调用耗时情况,如何落地,谈谈设计思路

解决方案:通过自定义全局过滤器搞定上述需求,自定义接口调用耗时统计的全局过滤器 

步骤:

(1)新建类MyGlobalFilter并实现GlobalFilter,Ordered两个接口

@Component //不要忘记
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered{
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
    {
        return null;
    }

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

(2)yml

server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

        - id: pay_routh3 #pay_routh3
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由,默认正确地址

(3)全局过滤器完整代码

@Slf4j
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {

    private static final String BEGIN_VISIT_TIME = "begin_visit_time";//开始访问时间

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //先记录下访问接口的开始时间
        exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
            if (beginVisitTime != null) {
                log.info("访问接口主机: " + exchange.getRequest().getURI().getHost());
                log.info("访问接口端口: " + exchange.getRequest().getURI().getPort());
                log.info("访问接口URL: " + exchange.getRequest().getURI().getPath());
                log.info("访问接口URL参数: " + exchange.getRequest().getURI().getRawQuery());
                log.info("访问接口时长: " + (System.currentTimeMillis() - beginVisitTime) + "ms");
                log.info("我是美丽分割线: ###################################################");
                System.out.println();
            }

        }));
    }

    /**
     * 数字越小优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

测试结果:

 

8.4.3.2、自定义条件Filter  

 自定义单一内置过滤器GatewayFilter

(1)先参考GateWay内置出厂默认的 :SetStatusGatewayFilterFactory、SetPathGatewayFilterFactory、AddResponseHeaderGatewayFilterFactory 等等

(2)自定义网关过滤器规则步骤套路 

①新建类名XXX需要以GatewayFilterFactory结尾并继承AbstractGatewayFilterFactory类

@Component标注不可忘

public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
}

②新建XXXGatewayFilterFactory.Config内部类

@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config>
{
    public static class Config {
        @Setter @Getter
        private String status;
    }
}

 ③重写apply方法

@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config>
{
    @Override
    public GatewayFilter apply(MyGatewayFilterFactory.Config config)
    {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request =  exchange.getRequest();
                System.out.println("进入自定义网关过滤器MyGatewayFilterFactory,status===="+config.getStatus());
                if(request.getQueryParams().containsKey("atguigu")) {
                    return chain.filter(exchange);
                }else {
                    exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }

    public static class Config {
        @Setter @Getter
        private String status;
    }
}

④重写shortcutFieldOrder

@Override
public List<String> shortcutFieldOrder() {
    List<String> list = new ArrayList<String>();
    list.add("status");
    return list;
}

⑤空参构造方法,内部调用super

public MyGatewayFilterFactory() {
    super(MyGatewayFilterFactory.Config.class);
}

⑥完整代码

@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {

    public MyGatewayFilterFactory() {
        super(MyGatewayFilterFactory.Config.class);
    }

    @Override
    public GatewayFilter apply(MyGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

                ServerHttpRequest request = exchange.getRequest();
                System.out.println("进入自定义网关过滤器MyGatewayFilterFactory,status====" + config.getStatus());
                if (request.getQueryParams().containsKey("atguigu")) {
                    return chain.filter(exchange);
                }
                exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
                return exchange.getResponse().setComplete();
            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("status");
    }

    @Setter
    @Getter
    public static class Config {
        private String status;
    }
}

 ⑦yml

参照:

配置

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - After=2023-12-30T23:02:39.079979400+08:00[Asia/Shanghai]

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

        - id: pay_routh3 #pay_routh3
          uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由,默认正确地址
          filters:
            - My=atguigu

测试结果:

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构——(java版)Map与Set

文章目录 二叉搜索树&#xff08;1&#xff09; 二叉搜索树的概念&#xff1a;&#xff08;2&#xff09;二叉搜索树的意义&#xff1a;&#xff08;3&#xff09;二叉搜索树的实现&#xff1a;实现的方法与属性实现二叉搜索树的查询&#xff1a;实现二叉搜索树的插入&#xff…

C++环境配置

目录 1. 前言 2. 正文 2.1 问题 3. 备注 1. 前言 好久不见&#xff0c;最近有些病痛缠身&#xff0c;最近流感有些频发&#xff0c;小生不慎中招&#xff0c;实在是惭愧。当然这不是虚哈&#xff0c;说虚的先出去看个电影再回来。我猜是因为好久没感冒了&#xff0c;所以可…

“最佳行业标杆应用”!亿达科创亮相国际人工智能展

9月8日&#xff0c;2024第五届深圳国际人工智能展会启幕。展会以“智创未来价值链接”为题&#xff0c;汇聚全球人工智能领域的顶尖企业、专家学者及创新成果&#xff0c;亿达科创受邀参展并发表主题演讲&#xff0c;全面展示人工智能技术的最新进展和广泛应用。会上发布“GAIE…

大模型算法岗位面试攻略:100个常见问题详解,高效备战拿下三个offer!

导读 大模型时代很多企业都在开发自己的大模型&#xff0c;这直接刺激了大模型岗位的需求。本文为大家整理了大模型面试相关的知识点&#xff0c;希望对大家面试求职有所帮助。 今天分享大模型面试相关知识点&#xff0c;持续更新。 1. RAG技术体系的总体思路 数据预处理->…

Mysql链接异常 | [08001] Public Key Retrieval is not allowed

Datagrid报错 [08001] Public Key Retrieval is not allowed 这个错误通常是由于 MySQL 8.0 中的新特性导致的。默认情况下&#xff0c;MySQL 8.0 使用 caching_sha2_password 作为认证插件&#xff0c;而你需要在连接 URL 中明确允许公钥检索或者使用老版本的认证方式 mysql…

小型洗衣机什么牌子好又便宜?五款备受好评机型测评,闭眼入

在日常生活中&#xff0c;内衣洗衣机已成为现代家庭必备的重要家电之一。选择一款耐用、质量优秀的内衣洗衣机&#xff0c;不仅可以减少洗衣负担&#xff0c;还能提供高效的洗涤效果。然而&#xff0c;市场上众多内衣洗衣机品牌琳琅满目&#xff0c;让我们往往难以选择。那么&a…

NXOpenC属性操作

1.属性基本介绍 查看属性,文件->属性 使用VS创建项目,找到do_it(),在do_it()里面进行修改 删除属性,使用UF_ATTR_delete(),第一个参数是部件的TAG,第二个参数是属性的类型,第三个属性名字。 下标是属性类型 类型整型UF_ATTR_integer实数类型UF_ATTR_real时间类型U…

strtok函数讲解使用

目录 1.头文件 2.strtok函数介绍 3.解释strtok函数 小心&#xff01;VS2022不可直接接触&#xff0c;否则&#xff01;没这个必要&#xff0c;方源面色淡然一把抓住&#xff01;顷刻炼化&#xff01; 1.头文件 strtok函数的使用需要头文件 #include<string.h> 2.strto…

Chrome 本地调试webrtc 获取IP是xxx.local

浏览器输入 chrome://flags/#enable-webrtc-hide-local-ips-with-mdns并将属性改为disabled修改成功后重启浏览器并刷新网页即可

开放式耳机是什么意思?五款高评分产品极力推荐!

开放式耳机是一种耳机设计&#xff0c;其特点是耳机的耳杯或耳罩不是完全封闭的&#xff0c;允许空气在耳机内部和外部环境之间自由流动。这种设计通常用于头戴式耳机&#xff0c;而不是入耳式耳机。开放式耳机的主要特点包括&#xff1a; 1. 声音传播&#xff1a;开放式耳机允…

Nestjs仿小米商城企业级Nodejs RBAC 微服务项目实战视频教程+Docker Swarm K8s云原生分布式部署教程分享

Nest.js是一个渐进的Node.js框架&#xff0c;可以在TypeScript和JavaScript (ES6、ES7、ES8)之上构建高效、可伸缩的企业级服务器端应用程序。它的核心思想是提供了一个层与层直接的耦合度极小、抽象化极高的一个架构体系。Nest.js目前在行业内具有很高的关注度&#xff0c;所以…

SAP自动付款和自动付款常见错误解决方案

应付账款-自动付款 供应商组(ERP)决定业务伙伴角色 分组决定编号范围;分组也与ERP中的供应商组相映射业务伙伴角色选择BP,分组可以选择BP/VN/CU;业务伙伴角色选择VN,分组只能选择VN 首先创建BP 标识可根据币种进行区别,不设置系统自动排序。其中银行账户22位…

AMD RDNA走到尽头,UDNA合二为一

原文转载修改自&#xff08;更多互联网新闻/搞机小知识&#xff09;&#xff1a; AMD UDNA架构合二为一&#xff0c;取代RDNA和CDNA架构 就在最近&#xff0c;正如我们之前所预料的那样&#xff0c;AMD正式“屈服”了。AMD高级副总裁Jack Huynh直接在IFA2024上宣布&#xff0c…

一、计算机网络的体系结构

1.1 计算机网络的组成 1&#xff09;从组成部分上分为&#xff1a;硬件、软件、协议。硬件是指主机、通信链路、交换设备和通信处理机组曾。软件包括各种实现资源共享的软件以及各种软件工具&#xff08;如网络操作系统、邮件收发程序、FTP程序、聊天软件&#xff09;。 2&…

插装式比例减压阀PPIS04-NG PPRV放大器

比例减压阀/电磁阀操作的方向控制阀能够根据BEUEC比例放大器控制信号控制分别调整/切换先导压力。这自额阀用于控制变量泵的倾斜角度或控制阀的阀芯位置&#xff0c;在作业车辆的电气控制中起着重要的作用。 比例减压阀在行走机械应用和暴露的环境条件实现可靠性、优良的线性度…

blender软件下载地址,blender哪个版本好用

​blender软件下载 不废话&#xff0c;blender软件下载直接点&#xff1a;https://download.blender.org/release/ blender最新稳定版&#xff1a;Blender 4.2.1 LTS 【渲染101云渲染】&#xff1a;如果您希望使用Blender的最新功能&#xff0c;并且愿意接受可能存在的一些小…

《代码整洁之道》-大师眼中的整洁代码是什么样

几个月前写了一篇文章“如何写出难以维护的代码”&#xff0c;从中能大概了解到不好维护的代码是什么样&#xff0c;有哪些坏味道&#xff0c;那肯定有人会反问&#xff0c;难以维护的代码见的太多了&#xff0c;也知道长什么样&#xff0c;但是对于好维护的代码是什么样的比较…

无人机+激光雷达:探索新技术应用场景

无人机与激光雷达技术的结合&#xff0c;为众多领域带来了前所未有的应用可能性和创新解决方案。以下是对无人机激光雷达技术的新应用场景的探索&#xff1a; 一、测绘与地理信息 1. 高分辨率数字表面模型&#xff08;DSM&#xff09;和地形模型&#xff08;DTM&#xff09;&…

关于科研性单位(用电环境)选择工业级插排插座的建议

实验室是科学研究与实验的重要场所&#xff0c;实验环境将直接影响到实验各项数据和结果的可靠性以及实验人员的安全。这类科研建筑内部的设计结构复杂&#xff0c;且有着不同功能类型的区域。根据工作活动的性质来划分不同区域&#xff0c;如&#xff1a;科研通用区&#xff0…

【Unity基础】如何选择脚本编译方式Mono和IL2CPP?

Edit -> Project Settings -> Player 在 Unity 中&#xff0c;Scripting Backend 决定了项目的脚本编译方式&#xff0c;即如何将 C# 代码转换为可执行代码。Unity 提供了两种主要的 Scripting Backend 选项&#xff1a;Mono 和 IL2CPP。它们之间的区别影响了项目的性能、…