Spring Gateway网关服务分析

news2024/7/4 4:28:56
  1. 关键原理解析

基本原理

Spring Cloud Route核心可以概括为Gateway过滤器框架和Route定义和解析两块内容。

DefaultFilter、GatewayFilter、GlobalFilter

三种过滤器的区别及执行顺序

SpringCloud Gateway中的提到的过滤器包括三种类型:

DefaultFilter:默认过滤器,接口GatewayFilter的实现类,配置后所有路由都执行的过滤器。

GatewayFilter: 网关过滤器,接口GatewayFilter的实现类,特定路由配置的过滤器。

GlobalFilter:全局过滤器,接口GlobalFilter的实现类。全局过滤器,无需配置。

三种过滤器在网关路由处理过程中其实都是统一加载在同一个过滤器链中,按照Order对三种过滤器进行排序,按照order值从小到大的顺序执行,如果order值相同,则按照加载顺序DefaultFilter->GatewayFilter->GlobalFilter。

DefaultFilter和GatewayFilter 在请求过来路由查找过程中加载,GlobalFilter则在FilteringWebHandler处理请求时加载。

DefaultFilter和gatewayFilter的转载过程

装载过程在RouteDefinitionRouteLocator中实现,该类负责根据路由定义加载路由信息。

private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {

        List<GatewayFilter> filters = new ArrayList();

        if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {

            filters.addAll(this.loadGatewayFilters("defaultFilters", this.gatewayProperties.getDefaultFilters()));

        }

        if (!routeDefinition.getFilters().isEmpty()) {

            filters.addAll(this.loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));

        }

        AnnotationAwareOrderComparator.sort(filters);

        return filters;

}

//loadGatewayFilters函数中将gatewayFilter进行Order赋值

for(int i = 0; i < filters.size(); ++i) {

    GatewayFilter gatewayFilter = (GatewayFilter)filters.get(i);

    if (gatewayFilter instanceof Ordered) {

        ordered.add(gatewayFilter);

    } else {

        ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));

    }

}

GlobalFilter加载过程

GlobalFilter由FilteringWebHandler在初始化时加载所有对象, 在FilteringWebHandler处理请求时将GlobalFilter和该请求相关的所有Gateway一起进行排序执行。

public Mono<Void> handle(ServerWebExchange exchange) {

    Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);

    List<GatewayFilter> gatewayFilters = route.getFilters();

    List<GatewayFilter> combined = new ArrayList(this.globalFilters);

combined.addAll(gatewayFilters);

//gatewayFilter和globalFilter加入统一集合进行排序

    AnnotationAwareOrderComparator.sort(combined);

    if (logger.isDebugEnabled()) {

        logger.debug("Sorted gatewayFilterFactories: " + combined);

    }

    //调用过滤器链处理请求

    return (new FilteringWebHandler.DefaultGatewayFilterChain(combined)).filter(exchange);

}

WebFilter

org.springframework.web.server.WebFilter 是Spring-web框架定义的过滤器, 是在进入webHandler之前进行调用, 如果在网关项目中实现相关接口将在网关定义的三种Filter之前执行

Filter和@WebFilter

Javax.sevlet.Filter接口和tomcat的@WebFilter注解都是实现Servlet容器的Filter接口,这类型接口作用再Servlet上, 而Spring-web 则是建立在DispatcherServlet之上。Filter是最原始,最早执行的Filter。

路由的获取和解析过程

路由解析

Spring Web请求处理从Dispatcher入口后的两个核心组件是HandlerMapping和HandlerAdapter,其中HandlerMapping负责映射请求URL到对应的HandlerAdapter进行处理。

对于SpringCloud Gateway来说这两个组件的具体实现类是:RouterFunctionMapping和

FilteringWebHandler. RouterFunctionMapping路由解析相关源码如下:

@Override

protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {

     ……

     //查找路由

     return lookupRoute(exchange)

               // .log("route-predicate-handler-mapping", Level.FINER) //name this

               .flatMap((Function<Route, Mono<?>>) r -> {

                    exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);

                    //将路由设置到ServerWebExchange的属性数据中

                    exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);

                     //返回FilteringWebHandler

                    return Mono.just(webHandler);

               }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {

                    ……

               })));

}

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {

     return this.routeLocator.getRoutes()

               // individually filter routes so that filterWhen error delaying is not a

               // problem

               .concatMap(route -> Mono.just(route).filterWhen(r -> {

                    // add the current route we are testing

                    exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());

                     //通过Predicate进行匹配

                    return r.getPredicate().apply(exchange);

               })

              ……

     }

最终由Route定义中的Predicate来对请求进行匹配。 相关的Route Predicate Factories包括路径断言,Cookie断言等各种断言。(可参考官方文档)

路由的加载

路由的加载的核心组件是包括RouteLocator和RouteDefinitionLocator,可以使用Gateway中自带的实现来满足微服务、手工配置、动态配置等应用,也可以通过扩展或操作相关组件实现自己的动态路由定义。下面介绍路由相关的核心定义和源码。

路由信息Route

Route对象是在Gateway处理流程中体现一个具体的路由配置。主要属性如下:

Id:唯一标识

Uri:目的URL

order;排序ORDER

predicate;路由断言配置,用于路由匹配

gatewayFilters:路由过滤器配置

metadata:路由元数据

其中Predicate、gatewayFilter、metadata也是gateway的相关核心概念。

RouteLocator

RouteLocator: Route定位器,getRoutes获取所有的路由。Gateway RouteLocator的默认实现是RouteDefinitionRouteLocator,顾名思义是从RouteDefinition(路由定义)中解析出路由信息,依赖RouteDifinitionLocator类来获取所有路由定义。

public Flux<Route> getRoutes() {

        //依赖RouteDefinitionLcator获取路由定义。

        return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute).map((route) -> {

            ……

            return route;

        });

    }

RouteDefinitionLocator

RouteDefinitionLocator: 路由定义加载器,负责加载网关的RouteDefinition。有以下几种路由定义加载器实现,根据配置和实际需要进行使用。

DiscoveryClientRouteDefinitionLocator

当spring.cloud.gateway.discovery.locator.enabled = true(默认为关闭)时,springcloud gateway通过DiscoveryClientRouteDefinitionLocator 获取所有服务产生基于服务注册中心的路由配置。

关键源码如下:

//通过DiscoveryClient获取的所有服务实例遍历转换为路由定义

return serviceInstances.filter(instances -> !instances.isEmpty()).flatMap(Flux::fromIterable)

          .filter(includePredicate).collectMap(ServiceInstance::getServiceId)

          // remove duplicates

          .flatMapMany(map -> Flux.fromIterable(map.values())).map(instance -> {

                //转换为路由定义

               RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance);

               final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);

                //加载Discovery属性中配置的Predicate,从源码中可以看到配置的Predicate和Filter是所有服务

                //路由共同使用的

               for (PredicateDefinition original : this.properties.getPredicates()) {

                    PredicateDefinition predicate = new PredicateDefinition();

                    predicate.setName(original.getName());

                    for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {

                         String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);

                         predicate.addArg(entry.getKey(), value);

                    }

                    routeDefinition.getPredicates().add(predicate);

               }

                //加载配置的过滤器定义

               for (FilterDefinition original : this.properties.getFilters()) {

                    FilterDefinition filter = new FilterDefinition();

                    filter.setName(original.getName());

                    for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {

                         String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);

                         filter.addArg(entry.getKey(), value);

                    }

                    routeDefinition.getFilters().add(filter);

               }

               return routeDefinition;

          });

spring.cloud.gateway.discovery.locator路径下可以配置过滤器和Predicated:

该配置是针对所有动态服务共有的,相关配置类是(DiscoveryLocatorProperties)

RedisRouteDefinitionRepository/ InMemoryRouteDefinitionRepository

当spring.cloud.gateway.redis-route-definition-repository.enabled=true是实例化RedisRouteDefinitionRepository,当系统没有定义RouteDefinition接口时,实例化InMemoryRouteDefinitionRepository。两个实例实现了RouteDefinitionRepository接口,提供了RouteDefinition的管理能力的示例,可以直接使用或参考实现自身定义的RouteDefinitionRoute实现对RouteDefinition的管理。

InMemoryRouteDefinitionRepository使用synchronizedMap来保存RouteDefinition:

private final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());

RefreshRoutesEvent

利用ApplicationEvent机制,提供在网关中传递路由刷新的事件,Gateway框架中CorsGatewayFilterApplicationListener和RouteDefinitionMetrics对该事件进行了监听。前者对新的路由信息新增全局的cors配置,后者则是对路由定义的一个统计测量

注:ApplicationEvent机制的使用设计到两个接口:ApplicationListener<RefreshRoutesEvent>监听事件接口,ApplicationEventPublisherAware事件发布器注入接口。 通过ApplicationEventPublisherAware获取到ApplicationEventPublisher发发布的相关事件会通知到对应事件的Listener。

动态路由配置和配置文件静态配置的关系
  1. RouteDefinitionLocator的Primary Bean是CompositeRouteDefinitionLocator

@Bean

@Primary

public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {

     return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));

}

构建CompositeRouteDefinitionLocator的RouteDefinitionLocator列表可能包括

  1. PropertiesRouteDefinitionLocator: 读取配置文件生成的路由定义
  2. DiscoveryClientRouteDefinitionLocator:

当 spring.cloud.gateway.discovery.locator.enabled = true 时从服务注册中心获取的服务路由定义。

  1. RedisRouteDefinitionRepository:当spring.cloud.gateway.redis-route-definition-repository.enabled = true时。可以通过RedisRouteDefinitionRepository接口操作存储在Redis中的Route定义
  2. InMemoryRouteDefinitionRepository: 内存存储的Route定义。 默认配置中是不会创建该Bean。 但定制开发中可以使用该类进行开发。

CompositeRouteDefinitionLocator汇集所有的RouteDefinitionLocator加载的RouteDefinition。

网关功能实现解析

了解了Gateway的路由定义及工作原理之后,Gateway提供的所有功能都是基于过滤器框架上实现的。

路由跳转

路由跳转是Gateway的最基本功能,同样是通过过滤器框架来实现,涉及到的过滤器如下,一下的过滤器都是GlobalFilter,无需用户配置:

Filter

order

说明

RouteToRequestUrlFilter(GlobalFilter)

10000

路由解析,根据路由定义,将路由信息解析成目的地址

ReactiveLoadBalancerClientFilter

10500

负载均衡,使用了spring-cloud-commons中的spring-cloud-balancer模块

WebsocketRoutingFilter

2147483646

  1. 如果目标是websocket,交给WebSocketService处理,
  2. 没有生成路由并且scheme是forward,交由dispatcherHandler处理(本地处理)
  3. http/https路由使用Netty进行请求发送

ForwardRoutingFilter

2147483647

NettyRoutingFilter

2147483647

负载均衡

最新的网关使用spring-cloud-balancer模块来进行负载均衡,这里先看一下2.0.2版本中的LoadBalancerClientFilter实现,该实现使用RibbonLoadBalancer实现的一个循环使用所有可用实例的负载均衡器。

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //获取RouteToRequestFilter中解析度目标地址

URI url = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);

        String schemePrefix = (String)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);

        //非负载均衡则直接略过

        if (url == null || !"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix)) {

            return chain.filter(exchange);

        } else {

           

            ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);

            log.trace("LoadBalancerClientFilter url before: " + url);

            //负载均衡实现方法choose

            ServiceInstance instance = this.choose(exchange);

            if (instance == null) {

                throw new NotFoundException("Unable to find instance for " + url.getHost());

            } else {

                URI uri = exchange.getRequest().getURI();

                String overrideScheme = null;

                if (schemePrefix != null) {

                    overrideScheme = url.getScheme();

                }

                URI requestUrl =

this.loadBalancer.reconstructURI(

new LoadBalancerClientFilter.DelegatingServiceInstance(instance, overrideScheme), uri);

                log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);

                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);

                return chain.filter(exchange);

            }

        }

    }

protected ServiceInstance choose(ServerWebExchange exchange) {

        return this.loadBalancer.choose(

((URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR)).getHost());

}

最终由以下两个关键接口进行实际的服务器加载和选择:

com.netflix.loadbalancer.IRule: 负责从可用服务中选中服务

com.netflix.loadbalancer.ServerList:加载所有可用服务

ServerList中Eureka集成的实现在:spring-cloud-netflix-eureka-client包中的最终的DiscoveryEnabledNIWSServerList类,最终调用EurekaClient和Eureka集群同步可用服务。

IRule的默认实现是:com.netflix.loadbalancer. RoundRobinRule 核心逻辑:

int nextServerIndex = this.incrementAndGetModulo(serverCount);

server = (Server)allServers.get(nextServerIndex);

限流

由GatewayFilter:RequestRateLimiterGatewayFilterFactory实现。

核心接口:

org.springframework.cloud.gateway.filter.ratelimit.KeyResolver

org.springframework.cloud.gateway.filter.ratelimit. RateLimiter

限流实现:

org.springframework.cloud.gateway.filter.ratelimit. RedisRateLimiter

List<String> keys = getKeys(id);

List<String> scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", Instant.now().getEpochSecond() + "", "1");

Flux<List<Long>> flux = this.redisTemplate.execute(this.script, keys, scriptArgs);

return flux.onErrorResume((throwable) -> {

    return Flux.just(Arrays.asList(1L, -1L));

}).reduce(new ArrayList(), (longs, l) -> {

    longs.addAll(l);

    return longs;

}).map((results) -> {

    boolean allowed = (Long)results.get(0) == 1L;

    Long tokensLeft = (Long)results.get(1);

    Response response = new Response(allowed, this.getHeaders(routeConfig, tokensLeft));

    return response;

});

核心原理有Redis Lua脚本来进行限流算法。Redis Lua实现Redis事务性。算法逻辑如下: 由tokenkey保存当前token剩余数量, timestamp key可以保存上次刷新时间, 通过时间窗内限流配置,计算可用tokens,当有剩余token时减去本次请求占用的token数量并更新的Redis中

KeyResolver:定制限流标识,从ServerExchange相关信息确定这次限流的唯一标识。每一个标识可以认为是一个唯一的限流器。

Google guava RateLimiter

有关限流功能Google guava提供的限流工具RateLimiter是一个基于令牌桶算法实现的限流器,常用于控制网站的QPS。与Semaphore不同,Semaphore控制的是某一时刻的访问量,RateLimiter控制的是某一时间间隔的访问量。

SmoothBursty和SmoothWarmingUp是定义在SmoothRateLimiter里的两个静态内部类,是SmoothRateLimiter的真正实现类。

RateLimiter的一个重要设计原则——透支未来令牌

如果说令牌池中的令牌数量为x,某个请求需要获取的令牌数量是y,只要x>0,即使y>x,该请求也能立即获取令牌成功。但是当前请求会对下一个请求产生影响,即会透支未来的令牌,使得下一个请求需要等待额外的时间。

2. 实际项目中网关需要承担的任务

1. 集成企业统一登录平台:获取用户信息

2. 用户及权限校验:如角色权限、各类黑白名单。

3. 通用请求过滤处理功能: 如日记、数据脱敏、metric统计等。

4.生成及校验JWT Token

其中第1、2点是基本每个项目都必须实现的,其他相关通用请求处理功能均可以再网关组件实现。

3. 补充 SpringCloud Gateway基本介绍

本章内容主要整理自官方文档,为SpringCloud的基本概念介绍和配置示例,如熟悉可忽略。

基本概念

路由(route)

路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成。如果断言为真,则说明请求的URL和配置的路由匹配。

断言(predicates)

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

过滤器(Filter)

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

Route Predicate

Predicate对应于路由配置中的断言, 示例如下:

路径断言

    指定请求路径匹配列表。

predicates:

        - Path=/red/{segment},/blue/{segment}

请求参数断言

    指定一个参数和可选的正则(没有的话只判断是否存在)

        predicates:

        - Query=green

GatewayFilter 工厂

GatewayFilter工厂对应于配置在路由上的过滤器。如下面例子:

添加请求头The AddRequestHeader GatewayFilter Factory

请求中增加请求头

        filters:

        - AddRequestHeader=X-Request-red, blue

        predicates:

        - Path=/red/{segment}

        filters:

        - AddRequestHeader=X-Request-Red, Blue-{segment}

定义默认过滤器

spring.cloud.gateway.default-filters

spring:

  cloud:

    gateway:

      default-filters:

      - AddResponseHeader=X-Response-Default-Red, Default-Blue

      - PrefixPath=/httpbin

全局过滤器 GlobalFilter

实现:GlobalFilter, Ordered 接口。全局过滤器和gateway filter最终都会汇集到一个过滤器链上按照ordered接口返回的顺序执行,区别在GlobalFilter无需配置。所有请求路由都会经过改类型过滤器。

比如:

负载均衡ReactiveLoadBalancerClientFilter

   对于uri schema为lb://会进行负载均衡。

      routes:

      - id: myRoute

        uri: lb://service

HttpHeadersFilter

HttpHeadersFilter 接口由NettyRoutingFilter调用,接口返回的header将添加到响应头中。

路由元数据

可以使用元数据为每个路由配置附加数据,在处理过程中使用。

spring:

  cloud:

    gateway:

      routes:

      - id: route_with_metadata

        uri: https://example.org

        metadata:

          optionName: "OptionValue"

          compositeObject:

            name: "value"

          iAmNumber: 1

Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);

// get all metadata properties

route.getMetadata();

// get a single metadata property

route.getMetadata(someKey);

Http超时配置

全局

connect-timeout必须以毫秒为单位指定。

response-timeout必须指定为 java.time.Duration

spring:

  cloud:

    gateway:

      httpclient:

        connect-timeout: 1000

        response-timeout: 5s

每个路由

通过元数据配置。

        metadata:

          response-timeout: 200

          connect-timeout: 200

Reactor Netty 访问日志

要启用 Reactor Netty 访问日志,请设置

-Dreactor.netty.http.server.accessLogEnabled=true.

    <logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">

        <appender-ref ref="async"/>

</logger>

CORS配置

spring:

  cloud:

    gateway:

      globalcors:

        cors-configurations:

          '[/**]':

            allowedOrigins: "https://docs.spring.io"

            allowedMethods:

            - GET

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

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

相关文章

CAN总线协议的理解以及移植stm32代码并使用

什么是CAN总线协议 是一种异步半双工的通讯协议&#xff0c;只有CAN_High与CAN_Low两条信号线。 有两种连接形式&#xff1a;闭环总线&#xff08;高速&#xff09;和开环总线&#xff08;远距离&#xff09; 他使用的是一种差分信号来传输电信号 所谓差分信号就是两条信号线…

软件测试|黑盒测试方法论-判定表

在因果图分析法中最后会得出一个判定表&#xff0c;可以看出因果图和判定表是有联系的&#xff0c;一般需要结合起来使用。 因果图是一种分析工具&#xff0c;通过分析最终得到判定表&#xff0c;再通过判定表编写测试用例。在一定情况下也可以直接书写判定表&#xff0c;省略…

Stable Diffusion webui 源码调试(一)

Stable Diffusion webui 源码调试&#xff08;一&#xff09; 个人模型主页&#xff1a;LibLibai stable-diffusion-webui 版本&#xff1a;v1.4.1 内容更新随机&#xff0c;看心情调试代码~ 调试txt2img的参数和工作流 文件 /work/stable-diffusion-webui/modules/txt2img…

软件测试|selenium执行js脚本

JavaScript是运行在客户端&#xff08;浏览器&#xff09;和服务器端的脚本语言&#xff0c;允许将静态网页转换为交互式网页。可以通过 Python Selenium WebDriver 执行 JavaScript 语句&#xff0c;在Web页面中进行js交互。那么js能做的事&#xff0c;Selenium应该大部分也能…

地铁机电设备健康管理现状及改善方法

轨道交通和我们的生活息息相关&#xff0c;从火车到地铁再到轻轨&#xff0c;给人们的出行带来了很大的便利。因此&#xff0c;保障轨道交通的的正常运行和安全至关重要&#xff0c;需要运维人员及时排查设备的问题&#xff0c;解决故障&#xff0c;保证轨道交通的安全运行。本…

Cygwin工具制作Redis服务端Window版本

文章目录 前言一、cygwin是什么&#xff1f;二、cygwin安装Redis源码编译 前言 在学习到redis&#xff0c;经常需要用到一个redis服务端&#xff0c;如果有买服务器或者本机可以支持经常开虚拟机&#xff0c;也是可以的&#xff0c;如果不具备这些条件&#xff0c;还是本机win…

Leetcode刷题详解——字母大小写全排列

1. 题目链接&#xff1a;784. 字母大小写全排列 2. 题目描述&#xff1a; 给定一个字符串 s &#xff0c;通过将字符串 s 中的每个字母转变大小写&#xff0c;我们可以获得一个新的字符串。 返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。 示例 1&#xff1a; 输入&…

2023年9月少儿编程 中国电子学会图形化编程等级考试Scratch编程二级真题解析(判断题)

2023年9月scratch编程等级考试二级真题 判断题(共10题,每题2分,共20分) 26、下列两个程序运行效果一样 答案:对 考点分析:考查积木综合使用,重点考查重复执行和坐标积木 两个程序都是在x=0,y=100的时候停止,所以正确 27、甲、乙和丙,一位是山东人,一位是河南人,…

死锁问题概述

文章目录 死锁的概念死锁的定义相似概念&#xff1a;饥饿死锁产生的原因死锁产生的必要条件死锁的预防破坏互斥条件破坏不可剥夺/不可抢占条件破坏请求并保持条件破坏循环等待条件 死锁避免安全性算法 死锁的处理策略死锁的检测死锁的解除 死锁的概念 死锁的定义 多个进程由于…

什么是代理IP池?如何判断IP代理商的IP池是否真实优质?

代理池充当多个代理服务器的存储库&#xff0c;提供在线安全和匿名层。代理池允许用户抓取数据、访问受限制的内容以及执行其他在线任务&#xff0c;而无需担心被检测或阻止的风险。代理池为各种在线活动&#xff08;例如网页抓取、安全浏览等&#xff09;提高后勤保障。 读完…

<C++> list模拟实现

目录 前言 一、list的使用 1. list的构造函数 2. list iterator的使用 3. list capacity 4. list modifiers 5. list的算法 1. unique​ 2. sort 3. merge 4. remove 5. splice 二、list模拟实现 1. 设置节点类 && list类 2. push_back 3. 迭代器 重载 * 重载前置 …

统计一个只包含大写字母的字符串中顺序对的数量.其中顺序对的定义为前面的字符小后面的字符大.例如在“ABC“中的顺序对为3,因为有AB,AC,BC

哈希法&#xff1a;扫描字符串&#xff0c;将出现的字符次数加1&#xff0c;统计比当前字符字典序小的字母出现的次数&#xff0c;即为顺序串的个数。 int CounSq(const char* arr)//时间复杂度O&#xff08;n&#xff09; {int sig[26] { 0 };int index 0;int sum 0;for (…

【TiDB】TiDB CLuster部署

目录 0 大纲 一 集群部署工具TiUP简介 1 TiUP 简介 2 TiUP使用 3 TiUP使用举例 二 TiDB Cluster安装配置需求 1 生产环境硬件需求 2 操作系统需求 三 TIDB部署 1 软硬件需求以及前置检查​编辑 2 安装TiUP 组件 ​3 集群拓扑文件 4 执行部署命令 &#xff08;1&…

如何使用 NFTScan NFT API 在 zkSync 网络上开发 Web3 应用

zkSync 是由 Matter Labs 创建的&#xff0c;是一个以用户为中心的 zk rollup 平台&#xff0c;它是以太坊的第 2 层扩展解决方案&#xff0c;使用 zk-rollups 作为扩展技术&#xff0c;与 optimistic rollups 一样&#xff0c;zk-rollups 将会汇总以太坊主网上的交易并将交易证…

SSM图书管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 图书管理系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和 数据库&#xff0c;系统主要…

模拟ASP.NET Core MVC设计与实现

前几天有人在我的《ASP.NET Core框架揭秘》读者群跟我留言说&#xff1a;“我最近在看ASP.NET Core MVC的源代码&#xff0c;发现整个系统太复杂&#xff0c;涉及的东西太多&#xff0c;完全找不到方向&#xff0c;你能不能按照《200行代码&#xff0c;7个对象——让你了解ASP.…

Hive 知识点八股文记录 ——(一)特性

Hive通俗的特性 结构化数据文件变为数据库表sql查询功能sql语句转化为MR运行建立在hadoop的数据仓库基础架构使用hadoop的HDFS存储文件实时性较差&#xff08;应用于海量数据&#xff09;存储、计算能力容易拓展&#xff08;源于Hadoop&#xff09; 支持这些特性的架构 CLI&…

Annotation Processor

Annotation Processor Processor处理约定 JavaC编译环境获取当前的源文件及类文件&#xff0c; 建立多轮次的处理过程。每一次轮次的处理结果将作为下一轮的输入。当某一轮处理完成后没有产生新的源文件或类文件&#xff0c;触发最后一轮。Processors 通过getSupportedAnnotat…

“位不配财”?程序员兼职,稳妥挣钱才是王道!

一、配不上 戏称程序员为“码农”&#xff0c;一年到头&#xff0c;像那地里的老黄牛和勤勤恳恳的老农民。 又像极了那工地上的农民工&#xff0c;天天搬砖&#xff0c;苦得嘞。 作为推动时代进步的得力干将&#xff0c;工作量自然是不容小觑。说程序员不加班都没人信&#x…

【前段基础入门之】=>CSS3新特性 文本多列 布局

概述&#xff1a; 作用&#xff1a;专门用于实现类似于报纸的布局。属于是一行文本多列布局 属性/值描述column-count 指定列数&#xff0c;值是数字。column-width指定列宽&#xff0c;值是长度单位columns同时指定列宽和列数&#xff0c;复合属性&#xff1b;值没有数量和顺序…