SpringCloud(15)之SpringCloud Gateway

一、Spring Cloud Gateway介绍

        

        Spring Cloud Gateway 是Spring Cloud团队的一个全新项目,基于Spring 5.0SpringBoot2.0、 Project Reactor 等技术开发的网关。旨在为微服务架构提供一种简单有效统一的API路由管理方式。

        Spring Cloud Gateway 作为SpringCloud生态系统中的网关,目标是替代Netflix ZuulGateway不仅 提供统一路由方式,并且基于Filter链的方式提供网关的基本功能。例如:安全,监控/指标,和限流。

        本节我们主要是来学一下SpringCloud Gateway的工作原理以及编写路由方式、拦截器介绍以及用实现对应的限流算法;

二、SpringCloud Gateway的工作原理

        我们在学习Gateway之前,先弄清楚Gateway的工作原理,后面使用它的各个功能时,就知道该如何使 用了,工作流程图如下:

        

         gateway的执行流程如下:

1:Gateway的客户端回向 Spring Cloud Gateway发起请求,请求首先会被HttpwebHandlerAdapter 进行提取组装成网关的上下文,然后网关的上下文会传递到DispatcherHandler。

2:DispatcherHandler 是所有请求的分发处理器, DispatcherHandler 主要负责分发请求对应的处理器,比如将请求分发到对应RoutePredicateHandlerMapping ( 路 由 断 言 处 理 器 映 射 器 ) 。

3: 路由断言处理映射器主要用于路由的查找,以及找到路由后返回对应的FilteringwebHandler。

4:FilteringwebHandler主 要 负 责 组 装Filter链表并调 用Filter 执 行 一 系 列Filter 处理,然后把请求 转到后端对应的代理服务处理,处理完毕后,将Response返回到Gateway客户端 。在Filter链 中 , 通 过 虚 线 分 割Filter  的原因是,过滤器可以在转发请求之前处理或者接收到被代理服务的 返回结果之后处理。所有的Pre 类 型 的Filter  执行完毕之后,才会转发请求到被代理的服务处理。被代理
的服务把所有请求完毕之后,才会执行Post类型的过滤器 。

2.2Gateway的动态路由

         Gateway 路由配置分为基于配置的由设置和基于代码动态路由配置,静态路由是指在application.yml 把路由信息配置好了,而动态路由则是从数据库中加载而来,我们接下来把这2种路由操作都实现一次。

2.2.1 业务说明

        如上图所示,我们要实现一下功能:

1:

       /order  开始的请求,都路由到 hailtaxi-order      

2:

       /driver       开始的请求,都路由到 hailtaxi-driver   

3:

       /pay开始的请求,都路由到 hailtaxi-pay   

2.2.2 基于配置的路由设置

         

如上图所示,正是gateway静态路由配置,这种方式的缺点就是硬编码写死的路由配置:

1:用户所有请求以/order 开始的请求,都路由到 hailtaxi-order   服务
2:用户所有请求以/driver 开始的请求,都路由到 hailtaxi-driver   服务
3:用户所有请求以/pay开始的请求,都路由到 hailtaxi-pay  服务

配置代码如下所示:

server:
  port: 8001
spring:
  application:
    name: hailtaxi-gateway
  main:
    allow-bean-definition-overriding: true
  cloud:
    #Consul配置
    consul:
      host: localhost
      port: 8500
      discovery:
        #注册到Consul中的服务名字
        service-name: ${spring.application.name}
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - PUT
      routes:
      - id: hailtaxi-driver
        uri: lb://hailtaxi-driver
        predicates:
        - Path=/**
        filters:
          - name: RequestRateLimiter #请求数限流 名字不能随便写 ,使用默认的facatory
            args:
              key-resolver: "#{@ipKeyResolver}"
              redis-rate-limiter.replenishRate: 1
              redis-rate-limiter.burstCapacity: 1
          #- StripPrefix=1
        #- Cookie=username,itheima
        #- Header=token,^(?!\d+$)[\da-zA-Z]+$
        #- Method=GET
      #默认全局过滤器
      default-filters:
        # 往响应过滤器中加入信息
        - AddResponseHeader=X-Response-Default-MyName,itheima

         参数配置说明:

  • -id:这是唯一的标记,不能出现重复;
  • uri:路由的地址,可以是lb:ip:端口,也是可以lb://${spring.application.name},其中的lb表示支持负载均衡;
  • predicates:这个表示是一个断言,其实就是一个判断条件,后面会介绍一共有那些断言判断;
  • -path=/driver/**:这是路由条件,表示以/driver开头的所有请求路径到路由到driver服务去;
  • filters:这是配置的是局部过滤器,表示只对当前的路由配置生效,后面会介绍到具体的拦截器;
  • -StripPrefix=1:去掉第一个路径,这种一般是用于将请求路径进行包装,不暴露真实路径出去,然后在网关层还原真实路径,一般是用作放爬虫的;

2.2.3 基于代码的动态路由配置

        我们同样实现上面的功能,    于代码方式实现。所有路由规则我们可以从数据库中读取并加载到 程序中。基于代码的路由配置我们只需要创建RouteLocator   并添加路由配置即可,代码如下:

    /***
     * 路由配置
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r -> r.path("/driver/**").uri("lb://hailtaxi-driver"))
                .route(r -> r.path("/order/**").uri("lb://hailtaxi-order"))
                //使用GatewayFilter过滤器
                .route(r -> r.path("/pay/**").uri("lb://hailtaxi-pay").filter(new PayFilter()))
                .build();
    }

        在真实场景中,基于配置文件的方式更直观、简介,但代码的路由配置是更强大,可以实现很丰富的功 能,可以把路由规则存在数据库中,每次直接从数据库中加载规则,这样的好处是可以动态刷新路由规 则,通常应用于权限系统动态配置新系统。

2.3.4 Gateway-Predicate/Filter

        上面路由匹配规则中我们都用了-Path 方式,其实就是路径匹配方式,除了路径匹配方式, Gateway还支持很多丰富的匹配方式,我们对这些方式分别进行讲解。

        具体的详见官网:Spring Cloud Gateway

 

        routes下面的属性含义如下:

id : 我们自定义的路由 ID , 保持唯一
uri  : 目标服务地址

predicates:  路由条件, Predicate   接受一个输入参数,返回一个布尔值结果。该属性包含多种默认方
法来将 Predicate  组合成其他复杂的逻辑(比如:与,或,非)

         Predicate  来源于Java8,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方 法来将 Predicate  组合成其他复杂的逻辑(比如:与,或,非)。

        在 Spring   Cloud   Gateway Spring    Predicate  的特性实现了各种路由匹配规则,通过 Header 请求参数等不同的条件来作为条件匹配到对应的路由。

        下面的一张图(来自网络)总结了 Spring   Cloud 内置的几种 Predicate   实现:

 

        我们在这里讲解几个断言匹配 方式。 

Cookie
        

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

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

        这里表示请求携带了cookie为chocolate的数据,并且值为ch.p,就允许通过。 

Header 匹配:
        

         Header 匹配 和 Cookie 匹配 一样,也是接收两个参数,一个 header 中属性名称和一个正则表达式, 这个属性值和正则表达式匹配则执行。配置如下:

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

        这里表示,请求头必要要有一个X-Request-Id,并且是数字的才能通过;

请求方式匹配:
        

         通过请求的方式是 POSTGETPUTDELETE 等进行路由。配置如下:

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

        表示只有请求方式是GET跟POST的才允许通过;

三、Gateway过滤器

        Spring Cloud Gateway根据作用范围划分为GatewayFilterGlobalFilter,二者区别如下:

  • GatewayFilter : 需要通过 spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或 通过spring.cloud.default-filters 配置在全局,作用在所有路由上;
  • GlobalFilter : 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成 GatewayFilterChain 可识别的过滤器,它为请求业务以及路由的 URI 转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路 由上。

         

        过滤器作为Gateway 的重要功能。常用于请求鉴权、服务调用时长统计、修改请求或响应 header 、限 流、去除路径等等。
        更多过滤器的使用大家可以参考官网: Spring Cloud Gateway

         3.1 过滤器的分类

默认过滤器:出厂自带,实现好了拿来就用,不需要实现
    全局默认过滤器
    局部默认过滤器
自定义过滤器:根据需求自己实现,实现后需配置,然后才能用哦。
    全局过滤器:作用在所有路由上。
    局部过滤器:配置在具体路由下,只作用在当前路由上。

        3.2过滤器的使用 

        1)默认过滤器配置 

        默认过滤器有两种:全局默认过滤器和局部默认过滤器;

        全局过滤器:对输出的响应头设置属性;

                对输出的响应设置其头部属性名称为X-Request-Foo,值为hello

         修改配置文件,配置如下:
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://example.org
        filters:
        - AddRequestHeader=X-Request-Foo, Hello

 局部过滤器:通过局部默认过滤器,修改请求路径。局部过滤器在这里介绍两种:添加路径前缀、去除 路径前缀。

2)前缀处理

       在项目中做开发对接接口的时候,我们很多时候需要统一API路径,比如统一以 /api 开始的请求调用 hailtaxi-driver 服务,但真实服务接口地址又没有 /api 路径,我们可以使用Gateway的过滤器处理 请求路径。

        在gateway中可以通过配置路由的过滤器StripPrefix实现映射路径中的前缀处理,我们来使用一下该过 滤器,再进一步做说明。

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://example.org
        filters:
        - StripPrefix=1
        有时候为了简化用户请求地址,比如用户请求 http://localhost:8001/info/1 我们想统一路由到
http://localhost:18081/driver/info/1 ,可以使用 PrefixPath 过滤器增加前缀。
spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://example.org
        filters:
        - PrefixPath=/api

3.3自定义全局过滤器

         自定义过滤器也有两类:全局自定义过滤器,和局部自定义过滤器。全局过滤器在项目中很多地方都有可能会使用,因此我们来学习下如何定义全局过滤器,并使用全局过 滤器。

        全局过滤器需要实现:GlobalFilter,Ordered接口

GlobalFilter:过滤器拦截处理方法
Ordered:过滤器也有多个,这里主要定义过滤器执行顺序,里面有个方法getOrder()会返回过滤器执行
顺序,返回值越小,越靠前执行

         我们创建全局过滤器并完成常见业务用户权限校验。

        基本逻辑:如果请求中有Token参数,则认为请求有效放行,如果没有则拦截提示授权无效。

@Component
public class RouterFilter implements GlobalFilter,Ordered {

    /***
     * 路由拦截
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("GlobalFilter拦截器执行");
        //获取请求参数
        String token = exchange.getRequest().getQueryParams().getFirst("token");

        //如果token为空,则表示没有登录
        if(StringUtils.isEmpty(token)){
            //没登录,状态设置403
            exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
            //结束请求
            return exchange.getResponse().setComplete();
        }
        //放行
        return chain.filter(exchange);
    }

    /***
     * 拦截器顺序
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

         此时请求,我们不携带token参数,效果如下:

 3.4 自定义局部过滤器

        局部过滤器一般是作用在某一个路由上,需要实例化创建才能使用,局部过滤器需要实现接口GatewayFilter,代码如下:

public class PayFilter implements GatewayFilter, Ordered {

    /***
     * 过滤器执行拦截
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("GatewayFilter拦截器执行-----PayFilter");
        return chain.filter(exchange);
    }

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


}

然后我们再配置动态路由的时候加上这个局部过滤器即可:

    /***
     * 路由配置
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r -> r.path("/driver/**").uri("lb://hailtaxi-driver"))
                .route(r -> r.path("/order/**").uri("lb://hailtaxi-order"))
                //使用GatewayFilter过滤器
                .route(r -> r.path("/pay/**").uri("lb://hailtaxi-pay").filter(new PayFilter()))
                .build();
    }

 这里需要注意一点的是,如果我们是在配置文件中使用我们自定义的局部过滤器就不能实现GatewayFilter了就要继承AbstractGatewayFilterFactory,代码如下:

/**
 * 记得这里的名字后缀必须是GatewayFilterFactory
 */
@Component
public class PayGatewayFilterFactory extends AbstractGatewayFilterFactory<PayGatewayFilterFactory.Config> {


    public PayGatewayFilterFactory() {
        super(Config.class);
    }
    @Override
    public List<String> shortcutFieldOrder() {
        return Lists.newArrayList("paymethod");
    }
    /**
     * 执行拦截
     * @param config
     * @return
    配置文件中使用如下:
     */
    @Override
    public GatewayFilter apply(PayGatewayFilterFactory.Config config) {
        return (exchange, chain) -> {
            String paymethod = config.getPaymethod();
            //将paymethod添加到请求头中
            exchange.getRequest().mutate().header("paymethod",paymethod);
            return chain.filter(exchange);
        };
    }

    /***
     * 获取指定属性值
     */
    @Data
    public static class Config {
        private String paymethod;
    }
}

        然后使用如下:

    gateway:
      #路由配置
      routes:
        #唯一标识符
        - id: hailtaxi-driver
          uri: lb://hailtaxi-driver
          #路由断言
          predicates:
            - Path=/driver/**
        #唯一标识符
        - id: hailtaxi-order
          uri: lb://hailtaxi-order
          #路由断言
          predicates:
            - Path=/order/**
        #唯一标识符
        - id: hailtaxi-pay
          uri: lb://hailtaxi-pay
          #路由断言
          predicates:
            - Path=/pay/**
            - Pay=aliPay

四、跨域配置

       出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本 的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源 策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另 外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol), 主机(host)和端口号(port)。

        反正就一句话:ip、端口、协议三者存在一个不一致那就是跨域;

在SpringCloud Gateway中配置跨域是非常简单的,如下面配置:

    gateway:
      globalcors:
        corsConfigurations: '[/**]'
        allowedOrigins: "*"
        allowedMethods:
          - GET
          - POST
          - PUT

        这样子配置就能够解决跨域问题,但是在配置文件中配置会如很多局限性,如:但如果涉及到Cookie跨域,上面的配置就不生效了,如果涉及到Cookie跨域,需要创建CorsWebFilter 过滤器,所以一般不会在配置文件中去配置,会在代码中去实现配置,具体如下:

    /**
     * 配置跨域
     * @return
     */
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        // cookie跨域
        config.setAllowCredentials(Boolean.TRUE);
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        // 配置前端js允许访问的自定义响应头
        config.addExposedHeader("Authorization");

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

五、限流

        网关可以做很多的事情,比如,限流,当我们的系统 被频繁的请求的时候,就有可能 将系统压垮,所 以 为了解决这个问题,需要在每一个微服务中做限流操作,但是如果有了网关,那么就可以在网关系统 做限流,因为所有的请求都需要先通过网关系统才能路由到微服务中。

        5.1漏桶算法讲解

        

        这里只是稍微讲解一下啊漏桶算法,更多算法请自行学习:

  1. 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
  2.  根据限流大小,设置按照一定的速率往桶里添加令牌;比如1s只允许一个请求通过,那就一秒中往令牌桶中放一个令牌;
  3. 桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
  4. 请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后, 将令牌直接删除;
  5. 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足 够的限流;

        5.2令牌桶实现案例

        5.2.1引入依赖

        <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>

        同时配置redis:

  redis:
    host: 127.0.0.1
    port: 6379

 5.2.2 定义keyResolver

        在Applicatioin引导类中添加如下代码,KeyResolver用于计算某一个类型的限流的KEY也就是说,可以 通过KeyResolver来指定限流的Key

        这个好比我们做分布式锁一样,你针对那个key做锁;

        我们可以根据IP来限流,比如每个IP每秒钟只能请求一次,在GatewayApplication定义key的获取,获 取客户端IP,将IP作为key,如下代码:

    /***
     * IP限流
     * @return
     */
    @Bean(name="ipKeyResolver")
    public KeyResolver userKeyResolver() {
        return new KeyResolver() {
            @Override
            public Mono<String> resolve(ServerWebExchange exchange) {
                //获取远程客户端IP
                String hostName = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
                System.out.println("hostName:"+hostName);
                return Mono.just(hostName);
            }
        };
    }

 然后再路由配置如下:

参数说明:

  •  redis-rate-limiter.replenishRate:是您希允许用户每秒执行多少请求,而不会丢弃任何请求。放令牌的速率;
  • redis-rate-limiter.burstCapacity:是指令牌桶的容量,允许在一秒钟内完成的最大请求数,请该设置为0将阻止任何请求
    

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

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

相关文章

文件上传---->生僻字解析漏洞

现在的现实生活中&#xff0c;存在文件上传的点&#xff0c;基本上都是白名单判断&#xff08;很少黑名单了&#xff09; 对于白名单&#xff0c;我们有截断&#xff0c;图片马&#xff0c;二次渲染&#xff0c;服务器解析漏洞这些&#xff0c;于是今天我就来补充一种在upload…

银河麒麟桌面版操作系统修改主机名

1图形化方式修改 1.1在计算机图标上右键&#xff0c;选择属性 1.2修改 1.2.1点击修改计算机名 选择玩属性后会自动跳转到关于中&#xff0c;在计算机名中点击修改图标本质就是设置里面的系统下的关于&#xff0c;我们右键计算机选择属性就直接跳转过来了 1.2.2修改系统名字 …

【Spring】SpringBoot 日志文件

目 录 一.日志有什么用&#xff1f;二.日志怎么用&#xff1f;三.自定义日志打印四.日志持久化五.日志级别六.更简单的日志输出—lombok 日志的主要掌握内容&#xff1a; 输出自定义日志信息 将日志持久化 通过设置日志的级别来筛选和控制日志的内容 一.日志有什么用&#…

YOLOv8改进 | Conv篇 | 利用YOLOv9的GELAN模块替换C2f结构(附轻量化版本 + 高效涨点版本 + 结构图)

一、本文介绍 本文给大家带来的改进机制是利用2024/02/21号最新发布的YOLOv9其中提出的GELAN模块来改进YOLOv8中的C2f,GELAN融合了CSPNet和ELAN机制同时其中利用到了RepConv在获取更多有效特征的同时在推理时专用单分支结构从而不影响推理速度,同时本文的内容提供了两种版本…

集合框架之List集合

目录 ​编辑 一、什么是UML 二、集合框架 三、List集合 1.特点 2.遍历方式 3.删除 4.优化 四、迭代器原理 五、泛型 六、装拆箱 七、ArrayList、LinkedList和Vector的区别 ArrayList和Vector的区别 LinkedList和Vector的区别 一、什么是UML UML&#xff08;Unif…

Flask——基于python完整实现客户端和服务器后端流式请求及响应

文章目录 本地客户端Flask服务器后端客户端/服务器端流式接收[打字机]效果 看了很多相关博客&#xff0c;但是都没有本地客户端和服务器后端的完整代码示例&#xff0c;有的也只说了如何流式获取后端结果&#xff0c;基本没有讲两端如何同时实现流式输入输出&#xff0c;特此整…

Nginx基础入门

一、Nginx的优势 nginx是一个高性能的HTTP和反向代理服务器&#xff0c;也是一个SMTP&#xff08;邮局&#xff09;服务器。 Nginx的web优势&#xff1a;IO多路复用&#xff0c;时分多路复用&#xff0c;频分多路复用 高并发&#xff0c;IO多路复用&#xff0c;epoll&#xf…

备战蓝桥杯---基础算法刷题1

最近在忙学校官网上的题&#xff0c;就借此记录分享一下有价值的题&#xff1a; 1.注意枚举角度 如果我们就对于不同的k常规的枚举&#xff0c;复杂度直接炸了。 于是我们考虑换一个角度&#xff0c;我们不妨从1开始枚举因子&#xff0c;我们记录下他的倍数的个数sum个&#…

每日五道java面试题之spring篇(二)

目录&#xff1a; 第一题 Spring事务传播机制第二题 Spring事务什么时候会失效?第三题 什么是bean的⾃动装配&#xff0c;有哪些⽅式&#xff1f;第四题 Spring中的Bean创建的⽣命周期有哪些步骤&#xff1f;第五题 Spring中Bean是线程安全的吗&#xff1f; 第一题 Spring事务…

QT中调用python

一.概述 1.Python功能强大&#xff0c;很多Qt或者c/c开发不方便的功能可以由Python编码开发&#xff0c;尤其是一些算法库的应用上&#xff0c;然后Qt调用Python。 2.在Qt调用Python的过程中&#xff0c;必须要安装python环境&#xff0c;并且Qt Creator中编译器与Python的版…

typescript 分析泛型工具类Partial的实现原理理解索引查询类型

Partial实现原理 在 TypeScript 中&#xff0c;Partial 是一个非常有用的工具类型&#xff0c;它能够将一个对象类型的所有属性变为可选。Partial 的实现原理是通过使用映射类型&#xff08;Mapped Type&#xff09;和 keyof 关键字来实现的。 下面我们来看一下 Partial 的实现…

LeetCode 热题 100 | 二叉树(终)

目录 1 二叉树小结 1.1 模式一 1.2 模式二 2 236. 二叉树的最近公共祖先 3 124. 二叉树中的最大路径和 菜鸟做题&#xff08;返校版&#xff09;&#xff0c;语言是 C 1 二叉树小结 菜鸟碎碎念 通过对二叉树的练习&#xff0c;我对 “递归” 有了一些肤浅的理解。…

RabbitMQ开启MQTT协议支持

1&#xff09;RabbitMQ启用MQTT插件 rootmq:/# rabbitmq-plugins enable rabbitmq_mqtt Enabling plugins on node rabbitmq: rabbitmq_mqtt The following plugins have been configured:rabbitmq_managementrabbitmq_management_agentrabbitmq_mqttrabbitmq_web_dispatch Ap…

正交匹配追踪(Orthogonal Matching Pursuit, OMP)的MATLAB实现

压缩感知&#xff08;Compressed Sensing, CS&#xff09;是一种利用稀疏信号的先验知识&#xff0c;用远少于奈奎斯特采样定理要求的样本数目恢复整个信号的技术。正交匹配追踪&#xff08;Orthogonal Matching Pursuit, OMP&#xff09;是一种常见的贪婪算法&#xff08;Gree…

P8630 [蓝桥杯 2015 国 B] 密文搜索

P8630 [蓝桥杯 2015 国 B] 密文搜索 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P8630 题目分析 基本上是hash的板子&#xff0c;但实际上对于密码串&#xff0c;只要判断主串中任意连续的八个位置是否存在密码串即可&#xff1b;那么我们…

Python 光速入门课程

首先说一下&#xff0c;为啥小编在即PHP和Golang之后&#xff0c;为啥又要整Python&#xff0c;那是因为小编最近又拿起了 " 阿里天池 " 的东西&#xff0c;所以小编又不得不捡起来大概五年前学习的Python&#xff0c;本篇文章主要讲的是最基础版本&#xff0c;所以比…

opencv判断灰化情况

目的 先说说理论&#xff1a; 在图像处理中&#xff0c;用RGB三个分量&#xff08;R&#xff1a;Red&#xff0c;G&#xff1a;Green&#xff0c;B&#xff1a;Blue&#xff09;&#xff0c;即红、绿、蓝三原色来表示真彩色&#xff0c;R分量&#xff0c;G分量&#xff0c;B分…

使用单一ASM-HEMT模型实现从X波段到Ka波段精确的GaN HEMT非线性仿真

来源&#xff1a;Accurate Nonlinear GaN HEMT Simulations from X- to Ka-Band using a Single ASM-HEMT Model 摘要&#xff1a;本文首次研究了ASM-HEMT模型在宽频带范围内的大信号准确性。在10、20和30 GHz的频率下&#xff0c;通过测量和模拟功率扫描进行了比较。在相同的频…

树-王道-复试

树 1.度&#xff1a; 树中孩子节点个数&#xff0c;所有结点的度最大值为 树的度 2.有序树&#xff1a; 逻辑上看&#xff0c;树中结点的各子树从左至右是有次序的&#xff0c;不能互换。 **3.**树的根节点没有前驱&#xff0c;其他节点只有一个前驱 **4.**所有节点可有零个或…

备战蓝桥杯—— 双指针技巧巧答链表4

对于单链表相关的问题&#xff0c;双指针技巧是一种非常广泛且有效的解决方法。以下是一些常见问题以及使用双指针技巧解决&#x1f680;&#x1f680;&#xff1a; 合并两个有序链表&#xff1a; 使用两个指针分别指向两个链表的头部&#xff0c;逐一比较节点的值&#xff0c;…