微服务API网关Spring Cloud Gateway实战

概述

  • 微服务网关是为了给不同的微服务提供统一的前置功能;
  • 网关服务可以配置集群,以承载更多的流量;
  • 负载均衡与网关互相成就,一般使用负载均衡(例如 nginx)作为总入口,然后将流量分发到多个网关实现复杂功能;
  • 网关核心组件包括路由(分组)、断言(匹配路径)、过滤器(处理逻辑);

什么是微服务网关

在微服务架构中,通常一个系统会被拆分为多个微服务,面对这么多微服务客户端应该如何去调用呢?如果根据每个微服务的地址发起调用,存在如下问题:

  • 客户端多次请求不同的微服务,会增加客户端代码和配置的复杂性,维护成本比价高
  • 认证复杂,每个微服务可能存在不同的认证方式,客户端去调用,要去适配不同的认证
  • 存在跨域的请求,调用链有一定的相对复杂性(防火墙 / 浏览器不友好的协议)
  • 难以重构,随着项目的迭代,可能需要重新划分微服务

为了解决上面的问题,微服务引入了 API 网关的概念,API 网关为微服务架构的系统提供简单、有效且统一的 API 路由管理,作为系统的统一入口,提供内部服务的路由中转,给客户端提供统一的服务,可以实现一些和业务没有耦合的公用逻辑,主要功能包含认证、鉴权、路由转发、安全策略、防刷、流量控制、监控日志等。
image.png

什么是 Spring Cloud Gateway

官网文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/

Spring Cloud Gateway 是 Spring Cloud 官方推出的第二代网关框架,定位于取代 Netflix Zuul。Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等。
Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。它不能在传统的 servlet 容器中工作,也不能构建成 war 包。

网关组件对比:
image.png

核心概念

  • 路由(route)

路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成。

  • 断言(predicates))

Java8 中的断言函数,SpringCloud Gateway 中的断言函数类型是 Spring5.0 框架中的 ServerWebExchange。断言函数允许开发者去定义匹配 Http request 中的任何信息,比如请求头和参数等。如果断言为真,则说明请求的 URL 和配置的路由匹配。

  • 过滤器(Filter)

SpringCloud Gateway 中的 filter 分为 Gateway FilIer 和 Global Filter。Filter 可以对请求和响应进行处理。

工作原理

Spring Cloud Gateway 的工作原理跟 Zuul 的差不多,最大的区别就是 Gateway 的 Filter 只有 pre 和 post 两种。

官网图示:
image.png
客户端向 Spring Cloud Gateway 发出请求,如果请求与网关程序定义的路由匹配,则该请求就会被发送到网关 Web 处理程序,此时处理程序运行特定的请求过滤器链。
过滤器之间用虚线分开的原因是过滤器可能会在发送代理请求的前后执行逻辑。所有 pre 过滤器逻辑先执行,然后执行代理请求。代理请求完成后,执行 post 过滤器逻辑。

Spring Cloud Gateway 实战

微服务快速接入 Spring Cloud Gateway

1)引入依赖

<!-- gateway网关 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<!-- nacos服务注册与发现 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>

注意:gateway 会和 spring-webmvc 的依赖冲突,需要排除 spring-webmvc。

2)编写 yml 配置文件

spring:
  application:
    name: gateway-service
  # 配置nacos注册中心地址
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

    gateway:
      # 设置路由:路由id、路由到微服务的uri、断言
      routes:
        - id: order_route  # 路由ID,全局唯一,建议配置服务名
          uri: lb://order-service  # lb 整合负载均衡器loadbalancer
          predicates:
            - Path=/order/**   # 断言,路径相匹配的进行路由

        - id: user_route   # 路由ID,全局唯一,建议配置服务名
          uri: lb://user-service  # lb 整合负载均衡器loadbalancer
          predicates:
            - Path=/user/**   # 断言,路径相匹配的进行路由

3)测试
[http://localhost:8888/order/findOrderByUserId/1](http://localhost:8888/order/findOrderByUserId/1)

路由断言工厂(Route Predicate Factories)配置

predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地。application.yml 配置文件中写的断言规则只是字符串,这些字符串会被 Predicate Factory 读取并处理,转变为路由判断的条件。

官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

通过网关启动日志,可以查看内置路由断言工厂:
image.png

路径匹配

spring:
  cloud:
    gateway:
      # 设置路由:路由id、路由到微服务的uri、断言
      routes:
      - id: order_route # 路由ID,全局唯一
        uri: lb://order-service # 目标微服务的请求地址和端口
        predicates:
         # 测试:http://localhost:8080/order/findOrderByUserId/1
        - Path=/order/** # 断言,路径相匹配的进行路由

Header 匹配

spring:
  cloud:
    gateway:
      # 设置路由:路由id、路由到微服务的uri、断言
      routes:
      - id: order_route  # 路由ID,全局唯一
        uri: lb://order-service  # 目标微服务的请求地址和端口
        predicates:
          - Path=/order/**   # 断言,路径相匹配的进行路由
         # Header匹配 请求中带有请求头名为 X-Request-Id,其值与 \d+ 正则表达式匹配
         - Header=X-Request-Id, \d+

例如:
image.png

过滤器工厂( GatewayFilter Factories)配置

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

官网:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

图示说明:
image.png

添加请求头

需求:给所有进入 order-service 的请求添加一个请求头:X-Request-color=red。

只需要修改 gateway 服务的 application.yml 文件,添加路由过滤即可:

spring:
  cloud:
    gateway:
      # 设置路由:路由id、路由到微服务的uri、断言
      routes:
      - id: order_route  # 路由ID,全局唯一
        uri: http://localhost:8020  # 目标微服务的请求地址和端口
        # 配置过滤器工厂
        filters:
        - AddRequestHeader=X-Request-color, red  # 添加请求头

测试代码:

@GetMapping("/testgateway")
public String testGateway(HttpServletRequest request) throws Exception {
    log.info("gateWay获取请求头X-Request-color:"
             +request.getHeader("X-Request-color"));
    return "success";
}
@GetMapping("/testgateway2")
public String testGateway(@RequestHeader("X-Request-color") String color) throws Exception {
    log.info("gateWay获取请求头X-Request-color:"+color);
    return "success";
}
// gateWay获取请求头X-Request-color:red
// gateWay获取请求头X-Request-color:red

添加请求参数

spring:
  cloud:
    gateway:
      # 设置路由:路由id、路由到微服务的uri、断言
      routes:
      - id: order_route  # 路由ID,全局唯一
        uri: http://localhost:8020  # 目标微服务的请求地址和端口
        # 配置过滤器工厂
        filters:
        - AddRequestParameter=color, blue  # 添加请求参数

测试代码:

@GetMapping("/testgateway3")
public String testGateway3(@RequestParam("color") String color) throws Exception {
    log.info("gateWay获取请求参数color:"+color);
    return "success";
}
// gateWay获取请求参数color:blue

自定义过滤器工厂

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

@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);
        };
    }
}

配置自定义的过滤器工厂:

spring:
  cloud:
    gateway:
      # 设置路由:路由id、路由到微服务的uri、断言
      routes:
      - id: order_route  # 路由ID,全局唯一
        uri: http://localhost:8020  # 目标微服务的请求地址和端口
        # 配置过滤器工厂
        filters:
        - CheckAuth=fox,男   # 自定义过滤器工厂

全局过滤器(Global Filters)配置

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与 GatewayFilter 的作用一样。

  • GatewayFilter:网关过滤器,需要通过spring.cloud.routes.filters配置在具体的路由下,只作用在当前特定路由上,也可以通过配置spring.cloud.default-filters让它作用于全局路由上。
  • GlobalFilter:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter 包装成 GatewayFilterChain 能够识别的过滤器。

官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters

图示说明:
image.png

ReactiveLoadBalancerClientFilter

ReactiveLoadBalancerClientFilter 会查看 exchange 的属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR的值(一个URI,比如lb://order-service/order/testgateway2?color=blue),如果该值的 scheme 是 lb,比如:lb://myservice ,它将会使用 Spring Cloud 的 LoadBalancerClient 来将 myservice 解析成实际的 host 和 port。
其实就是用来整合负载均衡器 loadbalancer 的。

spring:
  cloud:
    gateway:
      routes:
      - id: order_route
        uri: lb://order-service
        predicates:
        - Path=/order/**

自定义全局过滤器

自定义全局过滤器定义方式是实现 GlobalFilter 接口。每一个过滤器都必须指定一个 int 类型的 order 值,order 值越小,过滤器优先级越高,执行顺序越靠前。GlobalFilter 通过实现 Ordered 接口来指定 order 值。
示例代码:

@Component
@Slf4j
public class CheckAuthFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取token
        String token = exchange.getRequest().getHeaders().getFirst("token");
        if (null == token) {
            log.info("token is null");
            ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().add("Content-Type",
                                      "application/json;charset=UTF-8");
            // 401 用户没有访问权限
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            byte[] bytes = HttpStatus.UNAUTHORIZED.getReasonPhrase().getBytes();
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            // 请求结束,不继续向下请求
            return response.writeWith(Mono.just(buffer));
        }
        // TODO 校验token进行身份认证
        log.info("校验token");
        return chain.filter(exchange);
    }

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

Gateway 跨域资源共享配置(CORS Configuration)

在前端领域中,跨域是指浏览器允许向服务器发送跨域请求,从而克服 Ajax 只能同源使用的限制。
同源策略(Same Orgin Policy)是一种约定,它是浏览器核心也最基本的安全功能,它会阻止一个域的 js 脚本和另外一个域的内容进行交互,如果缺少了同源策略,浏览器很容易受到 XSS、CSRF 等攻击。所谓同源(即在同一个域)就是两个页面具有相同的协议(protocol)、主机(host)和端口号(port)。

CORS: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS

如何解决gateway跨域问题?

(1)通过yml配置的方式

官方说明:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration

spring:
  cloud:
    gateway:
    globalcors:
      cors-configurations:
        '[/**]':
          allowedOrigins: "*"
          allowedMethods:
          - GET
          - POST
          - DELETE
          - PUT
          - OPTION

(2)通过java配置的方式

@Configuration
public class CorsConfig {
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}

Gateway 基于 redis+lua 脚本限流

spring cloud 官方提供了 RequestRateLimiter 过滤器工厂,基于 redis+lua 脚本方式采用令牌桶算法实现了限流。

官方说明:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-requestratelimiter-gatewayfilter-factory

请求不被允许时返回状态:HTTP 429 - Too Many Requests

实操:
1)添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

2)修改 application.yml ,添加 redis 配置和 RequestRateLimiter 过滤器工厂配置

spring:
  application:
    name: gateway-service

  data:
    # 配置redis地址
    redis:
      host: localhost
      port: 6379
      database: 0
      timeout: 5000
      lettuce:
        pool:
          max-active: 200
          max-wait: 10000
          max-idle: 100
          min-idle: 10
  # 配置nacos注册中心地址
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

    gateway:
      # 设置路由:路由id、路由到微服务的uri、断言
      routes:
        - id: order_route  # 路由ID,全局唯一,建议配置服务名
          # 测试 http://localhost:8888/order/findOrderByUserId/1
          uri: lb://order-service  # lb 整合负载均衡器ribbon,loadbalancer
          predicates:
            - Path=/order/**   # 断言,路径相匹配的进行路由
          # 配置过滤器工厂
          filters:
            - name: RequestRateLimiter   # 限流过滤器
              args:
                redis-rate-limiter.replenishRate: 1 # 令牌桶每秒填充速率
                redis-rate-limiter.burstCapacity: 2 # 令牌桶的总容量
                key-resolver: "#{@keyResolver}" # 使用SpEL表达式,从Spring容器中获取Bean对象 

3)配置 keyResolver,可以指定限流策略,比如 url 限流,参数限流,ip 限流等等

@Bean
KeyResolver keyResolver() {
    // url限流
    return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
    // 参数限流
    // return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}

Gateway 整合 sentinel 限流

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

  • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
  • 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

sentinel网关流控:https://sentinelguard.io/zh-cn/docs/api-gateway-flow-control.html

Gateway 整合 sentinel 实现网关限流

1)引入依赖

<!-- gateway接入sentinel  -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2)添加 yml 配置,接入 sentinel dashboard,通过 sentinel 控制台配置网关流控规则

server:
  port: 8888
spring:
  application:
    name: gateway-sentinel-demo
  main:
    allow-bean-definition-overriding: true
  # 配置nacos注册中心地址
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

    sentinel:
      transport:
        # 添加sentinel的控制台地址
        dashboard: 127.0.0.1:8080

    gateway:
      # 设置路由:路由id、路由到微服务的uri、断言
      routes:
      - id: order_route  # 路由ID,全局唯一,建议配合服务名
        uri: lb://order-service  # lb 整合负载均衡器loadbalancer
        predicates:
        - Path=/order/**

      - id: user_route
        uri: lb://user-service  #lb 整合负载均衡器loadbalancer
        predicates:
        - Path=/user/**

注意:基于 SpringBoot3 的 Spring Cloud Gateway 和 Sentinel 还存在兼容性问题,等待 Sentinel 官方对最新的 Gateway 适配包更新。

Sentinel 网关流控实现原理

当通过 GatewayRuleManager 加载网关流控规则(GatewayFlowRule)时,无论是否针对请求属性进行限流,Sentinel 底层都会将网关流控规则转化为热点参数规则(ParamFlowRule),存储在 GatewayRuleManager 中,与正常的热点参数规则相隔离。转换时 Sentinel 会根据请求属性配置,为网关流控规则设置参数索引(idx),并同步到生成的热点参数规则中。
外部请求进入 API Gateway 时会经过 Sentinel 实现的 filter,其中会依次进行路由/API分组匹配、请求属性解析和参数组装。Sentinel 会根据配置的网关流控规则来解析请求属性,并依照参数索引顺序组装参数数组,最终传入SphU.entry(res, args) 中。Sentinel API Gateway Adapter Common 模块向 Slot Chain 中添加了一个 GatewayFlowSlot,专门用来做网关规则的检查。GatewayFlowSlot 会从 GatewayRuleManager 中提取生成的热点参数规则,根据传入的参数依次进行规则检查。若某条规则不针对请求属性,则会在参数最后一个位置置入预设的常量,达到普通流控的效果。
图示:
image.png

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

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

相关文章

504. 七进制数

这篇文章会收录到 : 算法通关第十三关-青铜挑战数学基础问题-CSDN博客 七进制数 描述 : 给定一个整数 num&#xff0c;将其转化为 7 进制&#xff0c;并以字符串形式输出。 题目 : LeetCode 504. 七进制数 : 504. 七进制数 分析 : 我们先通过二进制想一下7进制数的变化特…

C++二分查找算法:包含每个查询的最小区间

题目 给你一个二维整数数组 intervals &#xff0c;其中 intervals[i] [lefti, righti] 表示第 i 个区间开始于 lefti 、结束于 righti&#xff08;包含两侧取值&#xff0c;闭区间&#xff09;。区间的 长度 定义为区间中包含的整数数目&#xff0c;更正式地表达是 righti -…

<蓝桥杯软件赛>零基础备赛20周--第8周第1讲--十大排序

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周&#xff08;读者可以按…

【C语言】把歌词里的播放时间跟歌词提取出来

一&#xff0c;介绍 给到一个字符串&#xff0c;里面包含了时间&#xff08;唱该歌词的时间以及该歌词&#xff09;例如“[02:16.33][04:11.44][05:11.44]我想大声宣布对你依依不舍”&#xff0c;如何把两者都给打印出来呢&#xff1f;下面给出解释 二&#xff0c;代码 #incl…

【Openstack Train安装】四、MariaDB/RabbitMQ 安装

本章介绍了MariaDB/RabbitMQ的安装步骤&#xff0c;MariaDB/RabbitMQ仅需要在控制节点安装。 在安装MariaDB/RabbitMQ前&#xff0c;请确保您按照以下教程进行了相关配置&#xff1a; 【Openstack Train安装】一、虚拟机创建 【Openstack Train安装】二、NTP安装 【Opensta…

UI自动化测试的正确姿势 —— Airtest设备连接API详解第一篇

一、背景 Airtest作为一款优秀的自动化测试工具&#xff0c;有着强大的API功能&#xff0c;处理日常自动化测试过程中需要的各类操作。今天就给大家逐一介绍关于设备连接和常用API部分&#xff0c;结合自动化测试中的各类需求&#xff0c;看看如何通过使用Airtest来快速实现。…

记录创建粒子的轻量级JavaScript库——particles.js(可用于登录等背景显示)

文章目录 前言一、下载particles.js二、引入particles.js并使用三、配置数据说明如有启发&#xff0c;可点赞收藏哟~ 前言 本文记录使用创建粒子的轻量级JavaScript库 particles.js 可用于登录等背景显示 一、下载particles.js 先下载particles.js库&#xff0c;放在项目libs…

【古月居《ros入门21讲》学习笔记】08_发布者Publisher的编程实现

目录 说明&#xff1a; 1. 话题模型 图示 说明 2. 实现过程&#xff08;C&#xff09; 创建功能包 创建发布者代码&#xff08;C&#xff09; 配置发布者代码编译规则 编译并运行 编译 运行 3. 实现过程&#xff08;Python&#xff09; 创建发布者代码&#xff08;…

7Docker搭建es和kibana

一、安装es 1.拉取镜像 sudo docker pull elasticsearch:7.12.0 elasticsearch:7.12.0:我安装的版本是7.12.0&#xff0c;可以根据实际的情况安装 创建docker容器挂在的目录&#xff1a; sudo mkdir -p /opt/elasticsearch/config sudo mkdir -p /opt/elasticsearch/data s…

算法通关村第一关—青铜挑战—用Java基本实现各种链表操作

文章目录 第一关—链表【青铜挑战】1.1 单链表的概念1.2 链表的相关概念1.3 创建链表 - Java实现1.4 链表的增删改查1.4.1 遍历单链表 - 求单链表长度1.4.2 链表插入 - 三种位置插入&#xff08;1&#xff09;在链表的表头插入&#xff08;2&#xff09;在链表的中间插入&#…

Testlink 1.9.20+phpstudy_pro安装遇到的问题

phpstudy_pro启动了Apache2.4.39和Mysql5.7.26,php的版本是7.3.4zai。 安装Testlink 1.9.19时没有数据库的问题&#xff0c;安装Testlink 1.9.20时遇到了数据库问题&#xff0c;如下图所示&#xff1a; 网上搜索“Failed!Mysql Database cannnot be used”&#xff0c;给出的…

香港身份、香港永居身份、香港护照区别,三种证件之间是什么关系?

香港身份、香港永居身份、香港护照区别&#xff0c;三种证件之间是什么关系&#xff1f; 在港“通常性”住满7年之后&#xff0c;可以申请永居身份&#xff01; 香港身份&#xff1a;也可以称之为临时身份&#xff0c;无论通过香港优才计划、高才通计划、专才计划或者留学拿身份…

ANN人工神经网络:从基础认知到现实理解

什么是神经网络&#xff1f; 神经网络的再认知 前面我们了解过&#xff0c;人工神经网络&#xff08;Artificial Neural Network&#xff0c;ANN&#xff09;是人类为了模仿人大脑的神经网络结构创建出来的一种计算机系统结构。但如果仔细深入到神经网络当中&#xff0c;会慢…

Deep Learning(wu--84)

文章目录 2偏差和方差正则化梯度消失\爆炸权重初始化导数计算梯度检验OptimizationMini-Batch 梯度下降法指数加权平均偏差修正RMSpropAdam学习率衰减局部最优问题 调参BNsoftmax framework 2 偏差和方差 唔&#xff0c;这部分在机器学习里讲的更好点 训练集误差大&#xff…

修改YOLOv5的模型结构第三弹

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 文章目录 任务任务拆解 开始修改C2模块修改yolo.py修改模型配置文件 模型训练 上次已…

点击元素以外的事件监听

在项目中&#xff0c;我们经常会遇到需要监听目标元素以外的区域被点击或鼠标移入移出等需求。 例如下面我们有一个表格里面嵌套表单的组件 我希望点击n行的时候&#xff0c;n行的元素变成表单元素进行输入或者选择&#xff0c; 当我点击其他其他区域n行又会恢复成数据展示…

web和微信小程序设置placeholder样式

文章目录 一、场景二、web2.1、概念2.2、用法2.3、样式 三、小程序四、最后 一、场景 在页面布局时经常会用到input输入框&#xff0c;有时为了提示用户输入正确的信息&#xff0c;需要用placeholder属性加以说明。 二、web 2.1、概念 placeholder 是HTML5 中新增的一个属性…

拼图 游戏

运行出的游戏界面如下&#xff1a;按住A不松开&#xff0c;显示完整图片&#xff1b;松开A显示随机打乱的图片 User类 package domain;/*** ClassName: User* Author: Kox* Data: 2023/2/2* Sketch:*/ public class User {private String username;private String password;p…

C# WPF上位机开发(倒计时软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 生活当中&#xff0c;我们经常会遇到倒计时的场景&#xff0c;比如体育运动的时候、考试的时候等等。正好最近我们学习了c# wpf开发&#xff0c;完…

Azure Machine Learning - 创建Azure AI搜索索引

目录 一、先决条件检查空间 二、创建和加载索引启动向导连接到 数据源跳过认知技能配置配置索引配置索引器 三、监视索引器进度四、检查搜索索引结果五、添加或更改字段六、使用搜索浏览器查询七、运行更多示例查询八、清理资源 在本文中&#xff0c;你将使用导入数据向导和由虚…