Spring Cloud Gateway 常见过滤器的基本使用

目录

1. 过滤器的作用

2. Spring Cloud Gateway 过滤器的类型

2.1 内置过滤器

2.1.1 AddResponseHeader

2.1.2 AddRequestHeader

2.1.3 PrefixPath

2.1.4 RequestRateLimiter

2.1.5 Retry

2.2 自定义过滤器


1. 过滤器的作用

过滤器通常用于拦截、处理或修改数据流和事件流,在数据流中执行特定的操作或转换。

过滤器主要在以下几个方面发挥作用:

  1. 功能扩展和定制:过滤器允许您自定义和扩展网关的功能,以满足特定需求,如请求和响应的修改、路由规则的动态配置等。

  2. 数据校验和过滤:通过过滤器,您可以检查、验证和过滤传入或传出的数据,确保请求和响应的合法性和一致性。

  3. 安全保护:过滤器可以用于实施安全策略,如认证、授权、防止攻击等,以增强网关的安全性。

  4. 性能优化:通过过滤器,您可以对请求和响应进行性能优化,如缓存、压缩、请求路由的智能选择,以提高网关的性能。

  5. 统一处理:过滤器允许您在网关层面执行共享的处理逻辑,如日志记录、监控、审计等,以确保整个微服务体系的一致性和可维护性。

  6. 逻辑复用:通过过滤器,您可以将一些常见的操作抽象出来,以实现逻辑的复用,减少重复代码和维护工作。

2. Spring Cloud Gateway 过滤器的类型

Spring Cloud Gateway 过滤器可以分为两大类:

1. 内置过滤器

  • 局部的内置过滤器
  • 全局的内置过滤器

2. 自定义过滤器

2.1 内置过滤器

内置过滤器常见的有以下几种:

  1. AddResponseHeader
  2. AddRequestHeader
  3. AddRequestParameter(和 AddRequestHeader 相似)
  4. PrefixPath
  5. RequestRateLimiter
  6. Retry

Spring Cloud Gateway 过滤器常见有这么几种,实际上它有30多种,可以借助官方文档加以了解:Spring Cloud Gateway

过滤器又分为前置过滤器后置过滤器

在目标方法返回之前执行的过滤器就叫做前置过滤器(AddRequestXXX),在目标方法返回之后执行的过滤器就叫做后置过滤器。(AddResponseXXX)

前置工作:准备 user-service 和 order-service 两个模块,并且配置好 naocs 连接信息。

① user-service:创建一个 controller

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired  // 获取动态端口
    private ServletWebServerApplicationContext context;

    @RequestMapping("/getname")
    public String getName() {
        return context.getWebServer().getPort() +
                "--UserService:name=java-"+
                new Random().nextInt(100);
    }
}
server.port=0
spring.application.name=user-service-gateway
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos

② order-service:创建一个 controller

@RestController
@RequestMapping("/order")
public class OrderController {

    @RequestMapping("/getcount")
    public int getCount() {
        return new Random().nextInt(1000);
    }
}
server.port=0
spring.application.name=order-service-gateway
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos

2.1.1 AddResponseHeader

spring:
  cloud:
    nacos: # 配置注册中心
      discovery:
        server-addr: localhost:8848
        username: nacos
        password: nacos
    gateway: # 配置网关
      routes:
        - id: userservice
          uri: lb://user-service-gateway   # loadbalancer
          predicates:
            - Path=/user/**
          filters:
            - AddResponseHeader=My-Resp-Header, www.baidu
server:
  port: 10086

在配置过滤器之前,我在网关服务中引入 Nacos 和 LoadBalancer 之后,使用10086 端口去访问userservice服务时,查看对应的响应头:

配置了 AddResponseHeader 过滤器之后,再去访问userservice服务时,查看对应的响应头:

上述的设置,设置的是局部过滤器,如果此时在网关服务中再新增一组 id (如下),此时再去访问order-service服务时,响应头中就不会包含 My-Resp-Header 这个标头了。

spring:
  cloud:
    gateway: # 配置网关
      routes:
        - id: userservice  # 用户服务
          uri: lb://user-service-gateway   # loadbalancer
          predicates:
            - Path=/user/**
          filters:
            - AddResponseHeader=My-Resp-Header, www.baidu
        - id: orderservice  # 订单服务
          uri: lb://order-service-gateway
          predicates:
            - Path=/order/**

此时还想访问 orderservice 服务时,也在响应头中看到对应的标头,要么就把 filters 照搬到下面,但是这样做,代码就不具备维护性了,发生修改的时候,这将会是一个体力活;要么就使用全局过滤器。

全局过滤器的配置

spring:
  cloud:
    gateway: # 配置网关
      routes:
        - id: userservice  # 用户服务
          uri: lb://user-service-gateway   # loadbalancer
          predicates:
            - Path=/user/**
          filters:
            - AddResponseHeader=My-Resp-Header, www.baidu
        - id: orderservice  # 订单服务
          uri: lb://order-service-gateway
          predicates:
            - Path=/order/**
      default-filters:  # 全局过滤器
        - AddResponseHeader=MyApplication-Resp-Header, gateway.org

2.1.2 AddRequestHeader

spring:
  cloud:
    nacos: # 配置注册中心
      discovery:
        server-addr: localhost:8848
        username: nacos
        password: nacos
    gateway: # 配置网关
      routes:
        - id: userservice  # 用户服务
          uri: lb://user-service-gateway   # loadbalancer
          predicates:
            - Path=/user/**
          filters:
            - AddResponseHeader=My-Resp-Header, www.baidu
      default-filters:
        - AddRequestHeader=My-Req-Marking, www.baidu

如何拿到前置过滤器,可以在 userservice 服务里边写一个打印请求头的 controller:

@RequestMapping("/print-header")
public void printHeader(HttpServletRequest request) {
    Enumeration<String> headers = request.getHeaderNames();
    while(headers.hasMoreElements()) {
        String key = headers.nextElement();
        String value = request.getHeader(key);
        System.out.println(key +": " + value);
    }
}

此时运行userservice 和 gateway,去反问 print-header 接口, 查看 userservice 的控制台:

2.1.3 PrefixPath

spring:
  cloud:
    nacos: # 配置注册中心
      discovery:
        server-addr: localhost:8848
        username: nacos
        password: nacos
    gateway: # 配置网关
      routes:
      - id: userservice
        uri: lb://user-service-gateway
        predicates:
        - Path=/user/**
        filters:
        - PrefixPath=/v2

假设此时 userservice 中的 UserController 升级为了 v2 版本,但是我的接口 /user/** 已经 对外公布了,此时还想让 localhost:10086/user/getname 访问到 v2 版本的 controller,肯定是不允许再修改后端的接口了,也不能让前端跟着改,使用 PrefixPath 就可以很好的调整。

第一版代码:

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired  // 获取动态端口
    private ServletWebServerApplicationContext context;

    @RequestMapping("/getname")
    public String getName() {
        return context.getWebServer().getPort() +
                "--UserService:name=java-"+
                new Random().nextInt(100);
    }
}

第二版代码: 

@RequestMapping("/v2/user")
@RestController
public class UserControllerV2 {
    @Autowired  // 获取动态端口
    private ServletWebServerApplicationContext context;

    @RequestMapping("/getname")
    public String getName() {
        return context.getWebServer().getPort() +
                "--V2:UserService:name=java-"+
                new Random().nextInt(10);
    }
}

当我们配置了 PrefixPath 之后,再使用原来的 localhost:10086/user/getname 去访问服务的时候,就可以正常的访问到 v2 版本的 controller 了,无需再去修改前后端接口。

2.1.4 RequestRateLimiter

这是 Spring Cloud Gateway 内置的网关限流过滤器,它使用了令牌桶的限流算法。

令牌桶限流算法:令牌桶限流算法通过固定速率生成令牌放入桶中,桶满则丢弃新令牌。请求到来时消耗令牌进行处理,桶内无令牌则等待或丢弃请求,从而平滑流量,防止网络拥堵。

Spring Cloud  Gateway 当前版本支持和 Redis 一起实现限流功能,Spring Cloud Gateway 选择 Redis 作为限流方案的一个重要支撑是因为 Redis 的一些特性可以很好地满足限流中对于性能、一致性和分布式处理的要求。

  1. 分布式环境:在微服务架构中,服务实例往往是分布式部署的,Redis 由于其天然的分布式特性,能够确保在不同的服务实例之间共享限流的状态,实现全局限流。

  2. 性能:Redis 是一个高性能的内存数据库,它的读写速度非常快,可以达到每秒数十万次的读写请求。这种性能上的优势使得 Redis 成为实现限流中维护和检查速率限制状态的理想选择。

  3. 原子操作:Redis 支持多种原子操作,这对于计数器来说非常重要。例如,使用 INCRDECR 命令递增或递减计数器,可以保证即使在高并发的情况下,计数器的值也是准确的。

  4. 过期策略:Redis 允许为数据设置生存时间(TTL),这对于限流算法中的时间窗口非常有用。例如,在固定时间窗口算法中,可以设置令牌或计数器在特定时间后自动过期。

它的实现步骤总共分为三步:

  1. 添加 Redis 框架依赖
  2. 创建限流规则
  3. 配置限流过滤器

a.添加 Redis 框架依赖

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

注意事项:Redis版本太低,此功能设置会无效,建议使用 Redis 版本 5.x+。

b.创建限流规则

限流规则,既可以针对某一个 IP 做限流,也可以针对 URL 进行限流(所有的 IP 访问 URL 都会限流)

创建一个类,根据IP进行限流:

@Component
public class IpAddressKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().
                getRemoteAddress().getHostName());
    }
}

创建一个类,根据URL进行限流:

@Component
public class UrlKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        // 获取请求的URI路径作为限流的key
        String path = exchange.getRequest().getURI().getPath();
        return Mono.just(path);
    }
}

c.设置限流过滤器

spring:
  cloud:
    nacos: # 配置注册中心
      discovery:
        server-addr: localhost:8848
        username: nacos
        password: nacos
    gateway: # 配置网关
      routes:
      - id: userservice
        uri: lb://user-service-gateway
        predicates:
        - Path=/user/**
        filters:
        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 1  # 每秒请求数
            redis-rate-limiter.burstCapacity: 1  # 最大请求数
            key-resolver: "#{@ipAddressKeyResolver}"  # spEL表达式

  data:
    redis:
      host: 127.0.0.1
      port: 16379
      database: 0

注意,内置的限流过滤器 name 必须等于"RequestRateLimiter" ,其他参数的含义如下:

1. redis-rate-limiter.replenishRate:令牌填充速度(每秒允许请求数)

2. redis-rate-limiter.burstCapacity:令牌桶容量(最大令牌数)

3. key-resolver:根据哪个 key 进行限流,它的值是 spEL 表达式。

这三步完成之后,就表示每个 IP  每秒钟只能访问一次,如果刷新页面刷新的太快,就会出现如下页面:

2.1.5 Retry

在 OpenFeign 里面呢,也有个 Retry 超时重试,而且它还可以自定义重试规则,为什么 Gateway 还要有一个 Retry 呢 ?

OpenFeign里面的重试机制是服务调用层面的,它是用来帮助服务消费者处理调用远程服务时的问题的。而Spring Cloud Gateway的重试机制是在网关层面上,主要用于对所有通过网关的服务调用提供统一的重试策略,以处理上游服务可能出现的不稳定性(网络抖动)。两者虽都提供重试功能,但服务的层次不同,因此它们在微服务架构中各有其作用。

请求重试过滤器配置案例:

spring:
  cloud:
    nacos: # 配置注册中心
      discovery:
        server-addr: localhost:8848
        username: nacos
        password: nacos
    gateway: # 配置网关
      routes:
      - id: userservice
        uri: lb://user-service-gateway
        predicates:
        - Path=/user/**
        filters:
        - name: Retry  # 重试过滤器
          args:
            retries: 3
            statuses: GATEWAY_TIMEOUT
            methods: GET
            series: SERVER_ERROR
            backoff:
              firstBackoff: 10ms  # 第一次重试间隔
              maxBackoff: 50ms  # 最大重试间隔
              factor: 2  # firstBack * (factor ^ n)   # 重试系数
              basedOnPreviousValue: false  # 基于上次重试时间加上重试系数来计算

注意,重试过滤器的 name 必须等于 "Retry" ,因为 "Retry" 就是内置重试过滤器的名字,改为其他框架就无法识别了;其他参数的含义如下:

1. retries:尝试的重试次数。

2. statuses:重试的HTTP状态码。取值参考:HttpStatus (Spring Framework 6.1.2 API)

3. methods:重试的HTTP方法。取值:GET(默认值),HEAD,POST,PUT,PATCH,DELETE,OPTIONS,TRACE。

4. series:要重试的一系列状态码。默认值是 SERVER_ERROR,值是 5,表示 5xx,( 5开头的状态码)。共有 5 个取值:

  • 1xx:INFORMATIONAL
  • 2xx:SUCCESSFUL
  • 3xx:REDIRECTION
  • 4xx:CLIENT_ERROR
  • 5xx:SERVER_ERROR

5. backoff:配置的重试策略。

  • firstBackoff:第一次的重试间隔
  • maxBackoff:最大重试间隔
  • factor:重试系数,firstBackoff *(factor ^ n),n= 1,2,3,4...
  • basedOnPreviousValue:默认关闭,如果设为 true,就表示基于上次的重试时间加上重试系数来计算,例如 firstBackoff 为 10,假设上一次重试是 10 * 2 ^ 1,那么下一次就不是 10 * 2 ^ 2,而是 20 * 2 ^ 2。

【案例演示】

在 userservice 服务中,写一个接口去触发 GATEWAY_TIMEOUT:

@RequestMapping("/504")
public void return504(HttpServletResponse response) {
    System.out.println("------- Do return504 method. ------");
    response.setStatus(504);
}

启动 userservice 模块和 gateway 模块,此时 userservice 服务自动注入到 nacos 中,使用原生服务接口访问:

原生服务接口:192.168.10.83:63129/user/504,触发 504 

此时查看控制台 :打印了一次

再使用 Gateway 网关去访问 userservice 服务, localhost:10086/user/504,触发 504

此时查看控制台:打印了 4 次(清除上面的打印后)

可见重试过滤器确实生效了。

【注意事项】

1. 此处的超时重试和 OpenFeign 里面的超时重试不太一样,这里设置重试 3 次,就是真的重试 3 次,加上触发重试的 1 次,总共就是打印 4 次,而 OpenFeign 里面的超时重试,重试次数的下标是从 1 开始的,所以在 OpenFeign 里面,这样设置,只会重试 2 次,打印 3 次。

2. 上面重试过滤器的配置,像 method、series 这种参数,它都是有默认值的,比如说 series 的默认值是 5 开头的状态码,那么即使我们不设置这个参数,当我们触发 5 开头的异常时,并且没有与其他设置的参数相悖的时候,也会触发超时重试功能。

2.2 自定义过滤器

代码案例:使用 Spring Cloud Gateway 提供的全局过滤器实现统一认证授权。

@Component
public class AuthFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange,   // 执行的事件
                             GatewayFilterChain chain)     // 过滤器链
    {

        // 未登录判断逻辑,当参数中 username=admin && password=admin 继续执行,
        // 否则退出执行

        // 得到 Request 对象 (reactive web)
        ServerHttpRequest request = exchange.getRequest();
        // 得到 Response 对象 (reactive web)
        ServerHttpResponse response = exchange.getResponse();
        String username = request.getQueryParams().getFirst("username");
        String password = request.getQueryParams().getFirst("password");
        if (username != null && username.equals("admin")
                && password != null && password.equals("admin")) {
            // 已经登录,执行下一步
            return chain.filter(exchange);
        } else {
            // 设置无权限 401
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            // 执行完成,不用继续执行后续流程了
            return response.setComplete();
        }
    }
}

在访问相对应服务的时候,可以先不加用户名密码试一次,它会触发 401,再加上 username=admin&password=admin,就可以正常访问到相应服务了。

当有多个过滤器时,我们还可以给过滤器指定它的执行顺序:

假如说,此时有两个过滤器,一个用来验证登录,一个用来验证是否有权限。那么这个时候,需要先验证登录,才会再去验证是否有权限。如何实现?

  1. 实现 Ordered 接口
  2. 重写 getOrder 方法
@Override
public int getOrder() {
    // 此值越小越先执行
    return 1;
}

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

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

相关文章

Redis 快速搭建与使用

文章目录 1. Redis 特性1.1 多种数据类型支持1.2 功能完善1.3 高性能1.4 广泛的编程语言支持1.5 使用简单1.6 活跃性高/版本迭代快1.7 I/O 多路复用模型 2. Redis发展历程3. Redis 安装3.1 源码安装3.1.1 下载源码包3.1.2 解压安装包3.1.3 切换到 Redis 目录3.1.4 编译安装 3.2…

slf4j+logback源码加载流程解析

Logger log LoggerFactory.getLogger(LogbackDemo.class);如上述代码所示&#xff0c;在项目中通常会这样创建一个Logger对象去打印日志。 然后点进去&#xff0c;会走到LoggerFactory的getILoggerFactory方法&#xff0c;如下代码所示。 public static ILoggerFactory getILo…

缓存cache和缓冲buffer的区别

近期被这两个词汇困扰了&#xff0c;感觉有本质的区别&#xff0c;搜了一些资料&#xff0c;整理如下 计算机内部的几个部分图如下 缓存&#xff08;cache&#xff09; https://baike.baidu.com/item/%E7%BC%93%E5%AD%98 提到缓存&#xff08;cache&#xff09;&#xff0c;就…

<PDF-Pics> support

If get any questions,email me caohechunhotmail.com

Channel 使用事项和注意细节

&#xff08;1&#xff09;channel 可以声明为只读&#xff0c;或者只写性质 &#xff08;2&#xff09;channel 只读和只写的最佳实践案例 在默认情况下&#xff0c;管道是双向管道&#xff0c;即可读可写。 var ch chan intfunc main() {//声明为只写管道var chan1 chan<…

系统编程--常用命令

这里写目录标题 常用命令tab补齐获取历史命令快捷键相对路径和绝对路径ls补充详细区分文件对自己自身列-l递归ls which命令 系统目录介绍内容补充上一级目录运行一个可执行文件&#xff08;运行一个程序&#xff09; 一级目录二级目录二级目录二级目录 一级目录二级目录二级目录…

linux基于用户身份对资源访问进行控制的解析及过程

linux中用户分为三类 1.超级用户&#xff08;root&#xff09; 拥有至高无上的权限 2.普通用户 人为创建、权限小&#xff0c;权限受到控制 3.程序用户 运行程序的用户&#xff0c;不是给人使用的&#xff0c;给程序使用的&#xff0c;一般不给登录&#xff01; 组账…

第2课 使用FFmpeg读取rtmp流并用openCV显示视频

本课对应源文件下载链接&#xff1a; https://download.csdn.net/download/XiBuQiuChong/88680079 这节课我们开始利用ffmpeg和opencv来实现一个rtmp播放器。播放器的最基本功能其实就两个:显示画面和播放声音。在实现这两个功能前&#xff0c;我们需要先用ffmpeg连接到rtmp服…

解决IDEA 不能正确识别系统环境变量的问题

问题描述 本人laptop 上的是设置了GOOGLE_APPLICATION_CREDENTIALS 这个环境变量的&#xff0c; 正常java or python 的程序能基于这个环境变量使用 某个gcp service account 去访问GCP的资源 [gatemanmanjaro-x13 ~]$ env | grep -i google GOOGLE_APPLICATION_CREDENTIALS/…

ubuntu 安装apisix -亲测可用

官方未提供在ubuntu系统中安装apisix的方式&#xff0c;似乎只能通过源码方式安装&#xff0c;但是并不推荐&#xff0c;非常容易失败&#xff0c; 具体操作方式如下&#xff1a; ubuntu和Debian其实类似的&#xff0c;可使用DEB方式安装&#xff0c;如下截图 注意&#xff1…

年度总结|存储随笔2023年度最受欢迎文章榜单TOP15-part2

TOP11&#xff1a;PCIe在狂飙&#xff0c;SAS存储之路还有多远&#xff1f; 随着科技的飞速发展&#xff0c;固态硬盘&#xff08;SSD&#xff09;已经成为现代计算机系统中不可或缺的一部分。它以其出色的性能和可靠性&#xff0c;改变了我们对于存储设备的期待。当前业内SSD广…

通用定时器PWM波输出原理

1通用PWM波输出原理 总结&#xff1a;PWM波周期或频率由ARR决定&#xff0c;PWM波占空比由CCRx决定 1通用PWM模式 1.1PWM模式1 PWM模式1&#xff1a; 递增&#xff1a;CNT < CCRx&#xff0c;输出有效电平1 CNT > CCRx&#xff0c;输出无效电平0 递减&#xff1a;CNT …

Python面向对象编程 —— 类和异常处理

​ &#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 1. 类 1.1 类的定义 1.2 类变量和实例变量 1.3 类的继承 2. 异常处理 2.1类型异常 2.…

fiddler菜单汉化补丁

【Fiddler汉化补丁下载地址】 一、手机应用市场下载“头条搜索极速版”app 二、头条搜索极速版中搜索“葫芦娃指南”获取 三、汉化过程完整过程 1、安装Fiddler英文版 2、比如我将Fiddler安装在&#xff1a;D:\Programs\Fiddler 3、将【fiddler汉化】文件夹中的FiddlerT…

2024年【茶艺师(初级)】考试技巧及茶艺师(初级)试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 茶艺师&#xff08;初级&#xff09;考试技巧是安全生产模拟考试一点通生成的&#xff0c;茶艺师&#xff08;初级&#xff09;证模拟考试题库是根据茶艺师&#xff08;初级&#xff09;最新版教材汇编出茶艺师&#…

【基于VirtualBox及openEuler20.03 TLS SP1编译openGauss2.1.0源码】

【openEuler 20.03 TLS编译openGauss2.1.0源码】 一、安装环境二、安装步骤 一、安装环境 项目Value虚拟机virtualbox操作系统openEuler 20.03 TLSopenGauss2.1.0openGauss-third_party2.1.0 二、安装步骤 以下操作需要在root用户下执行 编辑/etc/selinux/config vim /etc/s…

分享一个学习Typescript最全的Github网站

一个专注研究Typescript的网站&#xff0c;&#x1f396;&#x1f396;&#x1f396;在这里你可以全面深入学习Typescript相关知识,通过动画方式讲解TS&#xff0c;还有很多常见问题解答。你还可以挑战相应的题目&#xff0c;快来学习吧 我就懒一点&#xff0c;直接原滋原味的…

跟踪SEO性能

他们说&#xff0c;如果你能衡量一些东西&#xff0c;你就可以改进它。 在SEO中&#xff0c;也不例外。专业的SEO跟踪从排名和转化到丢失链接等的所有内容&#xff0c;以帮助证明SEO的价值。衡量你的工作和持续改进的影响&#xff0c;对于你的SEO成功、客户保留和感知价值至关…

工智能基础知识总结--什么是CNN

什么是CNN 卷积神经网络(Convolutional Neural Networks, CNN)是一类包含卷积计算且具有深度结构的前馈神经网络(Feedforward Neural Networks),是深度学习(deep learning)的代表算法之一。CNN最常用于CV领域,但是在NLP等其他领域也有应用,如用于文本分类的TextCNN。 …

搞知识竞赛活动要做哪些准备工作

举办知识竞赛&#xff0c;大量的精力和时间投入是在筹划准备阶段。诸如竞赛的策划布置、题库的设计建立、参赛人员的复习准备、竞赛器具的购置、赛场的布置安装、对各环节的督促检验等一系列工作&#xff0c;都是在此期间进行和完成的。无论哪一环节出现疏漏偏差&#xff0c;都…