当前位置: 首页 > news >正文

面试场景题系列:设计限流器

首先看看使用API限流器的好处。

•预防由拒绝服务攻击(Denial of Service,DoS)引起的资源耗尽问题。大型科技公司发布的所有API几乎都强制执行某种形式的限流操作。例如,推特限制每个用户每3小时最多发300条推文。谷歌文档API的默认限制是每个用户每60秒最多发出300个读请求。限流器通过有意或者无意地拦截超额的请求来预防DoS攻击。

•降低成本。限制过量的请求意味着需要的服务器更少,并且可以把更多的资源分配给优先级更高的API。限流器对于使用付费的第三方API的公司来说极为重要。比如,对于外部API,如检查信用值、请求付款、获取健康记录等,你需要按照请求次数付费。限制这些请求的数量对于降低成本很重要。

•预防服务器过载。为了降低服务器负载,可以使用限流器来过滤机器人或者用户不当操作所造成的过量请求。

1.限流器场景需求设定:

•能准确限制过量的请求。

•低延时,限流器不能拖慢HTTP响应时间。

•尽量占用较少的内存。

•这是一个分布式限流器,可以在多个服务器或者进程之间共享。

•需要处理异常。当用户的请求被拦截时,给用户展示明确的异常信息。

•高容错性。如果限流器出现任何问题(比如某个缓存服务器宕机),不能影响整个系统。

2.顶层设计

我们从简单的情形入手,采用基本的客户—服务器通信模式。

2.1在哪里实现限流器

直观地说,既可以在客户端也可以在服务器端实现限流器。

•在客户端实现。一般而言,客户端不是一个强制实行限流的可靠位置,因为客户端请求很容易被恶意伪造。此外,我们也可能无法控制客户端的实现。

•在服务器端实现。图-1展示了一个服务器端的限流器。

图-1

除了在客户端和服务器端实现,还有一种方案。我们可以创建一个中间件作为限流器,用它来限制对API的请求,而不是把限流器放在API服务器上,如图-2所示。

图-2

我们用图-3作为例子来讲解限流器在我们的设计中是怎样工作的。假设我们的API服务器允许客户端每秒发送两次请求,而客户端在1秒之内向服务器发送了3个请求,那么头两个请求被转发到API服务器上,限流器中间件会拦截第3个请求并返回HTTP状态码429,表示用户发送的请求太多。

图-3

云微服务已经非常流行,限流器通常在一个叫作API网关的组件中实现。API网关是一个完全托管的服务,支持流量限制、SSL终止、身份验证、IP地址白名单、静态内容服务等功能。现在,我们只需要知道API网关是一个支持流量限制的中间件即可。

在设计限流器的时候,我们要问自己一个很重要的问题:这个限流器应该在哪里实现?是在服务器端还是在网关中实现?这个问题没有唯一的答案,而是取决于你公司现在的技术栈、工程资源、优先级、目标等因素。以下是一些一般性的指导原则。

•评估公司现在的技术栈,比如编程语言、缓存服务等。确保你现在用的编程语言能高效地在服务器端实现流量限制。

•找到适合业务需求的流量限制算法。如果把所有功能或模块都放在服务器端实现,你就可以自由地选择算法。如果使用第三方网关,你的算法选择就有可能受到限制。

•如果你已经使用了微服务架构,并且在设计中包含了API网关以实现身份验证、IP地址白名单等功能,那么你可以把限流器加在API网关上。

•创建自己的限流器是很花时间的。如果你没有足够的工程资源去实现限流器,使用商业版的API网关是更好的选择。

2.2 流量限制算法

流量限制可以通过不同的算法来实现,每种算法都有不同的优点和缺点。本文虽然并不重点讨论算法,但是大概了解一下它们有助于为我们的场景选择最适合的算法或者算法组合。下面是流行算法的列表。

•代币桶算法(Token Bucket)。

•漏桶算法(Leaking Bucket)。

•固定窗口计数器算法(Fixed Window Counter)。

•滑动窗口日志算法(Sliding Window Log)。

•滑动窗口计数器算法(Sliding Window Counter)。

代币桶算法

代币桶算法被广泛用于限制流量。它很简单、容易理解,并被互联网公司广泛使用。亚马逊[插图]和Stripe都使用此算法来对它们的API请求限流。代币桶算法的工作原理如下:

•代币桶是一个有预定义容量的容器。代币按照预定的速率被放入桶中。一旦桶被装满,就不再往里面添加代币。如图-4所示,代币桶的容量是4个代币。重新注入装置每秒将两个代币放入桶中。如果桶满了,多出来的代币就会溢出。

图-4

•每个请求消耗一个代币。当一个请求到达时,我们检查桶里有没有足够的代币。图-5解释了它是如何工作的。

◆ 如果有足够的代币,每次请求到达时,我们就取出一个代币,然后这个请求就可以通过。

◆ 如果没有足够的代币,这个请求将被丢弃。

图-5

图-6描述了代币消耗、重新注入和流量限制的工作原理。在这个例子中,代币桶的容量是4个代币,重新注入代币的速度是每分钟4个。

图-6

代币桶算法有两个参数。

•桶大小:桶内最多允许有多少个代币。

•重新注入代币的速度:每秒放进桶里的代币数量。

我们需要多少个桶呢?说不准,这取决于流量限制规则。下面是一些例子。

•通常,不同的API端点需要使用不同的桶。比如,如果允许用户每秒发1篇帖子,每天添加150个好友,每秒点赞5篇帖子,那么每个用户就需要3个桶。

•如果我们需要基于IP地址来对请求限流,则每个IP地址需要一个桶。

•如果系统最多允许每秒发送10000个请求,那么所有请求理应共享一个全局桶。

代币桶算法有不少优点。

•算法容易实现。

•内存的使用效率高。

•允许在很短时间内出现突发流量。只要还有代币,请求就可以通过。

代币桶算法也有缺点。尽管该算法只需要两个参数,但是要把这两个参数调校好,可能很具有挑战性。

漏桶算法

漏桶算法跟代币桶算法很相似,只不过它对请求是按照固定速率处理的。漏桶算法通常采用先进先出(First-In-First-Out,FIFO)队列来实现。该算法的工作原理如下所述。

•当一个请求到达时,系统先检查桶是否已满。如果没有,就将请求添加到队列中。

•否则,丢弃请求。•定期从队列中取出请求并进行处理。

图-7解释了该算法是如何工作的。

图-7

漏桶算法有如下两个参数。

•桶大小:它等于队列大小。队列中保存了要按固定速率处理的请求。

•出栈速度:它定义了每秒可以处理多少个请求,通常以秒为时间单位。Shopify是一个电商公司,它使用漏桶算法来进行流量限制。

漏桶算法的优点:

•因为队列大小是有限的,所以内存的使用更高效。

•因为对请求是按固定速率来处理的,所以漏桶算法很适合要求出栈速度稳定的场景。

漏桶算法的缺点:

•突发流量会使队列中积压大量旧的请求,如果这些请求不能被及时处理,最新的请求会被限流。

•该算法有两个参数,要调校好它们可能不那么容易。

固定窗口计数器算法

固定窗口计数器算法的工作原理如下所述。

•将时间轴分成固定大小的时间窗口,并给每个时间窗口分配一个计数器。

•每到达一个请求,计数器的值都会加1。

•一旦计数器到达预先设定的阈值,新请求就会被丢弃,直到开始一个新的时间窗口。

我们用一个具体的例子来看看它到底是怎么工作的。在图-8中,时间单位是秒,系统允许每秒最多通过3个请求。在每个1秒的时间窗口中,如果收到超过3个请求,超出的请求会如图4-8所示的那样被丢弃。

图-8


这个算法的一个主要问题是,如果在时间窗口的边界上出现流量的爆发,则有可能会导致通过的请求数超出阈值。我们来看下面这个例子。如图-9所示,系统每分钟最多只允许通过5个请求,并且可用配额(阈值)在整分钟时会重置。在2:00:00和2:01:00之间有5个请求,在2:01:00和2:02:00之间又有5个请求。对于2:00:30至2:01:30这1分钟的时间窗口,有10个请求通过,而这是允许通过的请求数量的两倍。

图-9

固定窗口计数器算法的优点:

•内存的使用效率高。

•容易理解。

•在每个单位时间窗口结束时重置请求数阈值,这种设计适用于某些特定场景。

固定窗口计数器算法的缺点是,如果在时间窗口的边界上流量激增,会导致通过的请求数超过设定的阈值。

滑动窗口日志算法

前面讨论过,固定窗口计数器算法有一个重大问题:在时间窗口的边界上,它允许更多的请求通过。滑动窗口日志算法解决了这个问题。它的工作原理如下所述。

•算法记录每个请求的时间戳。时间戳数据通常保存在缓存中,比如Redis中的有序集合。

•当新请求到达时,移除所有过时的时间戳。过时时间戳是指那些早于当前时间窗口起始时间的时间戳。

•将新请求的时间戳添加到日志中。

•如果日志的条数等于或者少于允许的请求数,则请求通过,否则请求被拒绝。我们用一个例子来解释这个算法,如图-10所示。

图-10

在这个例子中,限流器每分钟允许2个请求通过。通常Linux时间戳会被存储在日志中。但是为了使可读性更高,我们在这里使用了人类可读的时间表示。•当一个新请求在1:00:01到达时,日志是空的。因此,该请求被允许通过。

•当一个新请求在1:00:30到达时,1:00:30这个时间戳被添加到日志中。添加后,日志条数变为2,没有超过允许通过的请求数量。因此这个请求也被允许通过。

•当一个新请求在1:00:50到达时,时间戳被插入日志。在插入后,日志条数变为3,大于允许通过的最大请求数。因此该请求被拒绝,但是它的时间戳留在了日志中。

•当一个新请求在1:01:40到达时,所有在[1:00:40,1:01:40)范围内的请求都在最近的时间窗口中,所有早于1:00:40发送的请求都已过时。两个过时的时间戳1:00:01和1:00:30从日志中被删除。删除后,日志的条数变成2;因此,这个请求被允许通过。

滑动窗口日志算法的优点是,其实现的流量限制非常准确,在任何滑动的时间窗口中,请求的数量都不会超过阈值。但是其消耗的内存过多,因为即使一个请求已被拒绝,它的时间戳依然被保存在内存中。

滑动窗口计数器算法

滑动窗口计数器算法是组合了固定窗口计数器算法和滑动窗口日志算法的方法。这个算法有两种不同的实现方法,这里会解释其中的一种。图-11展示了这个算法是怎么工作的。

假设限流器每分钟最多允许通过7个请求,然后前一分钟有5个请求,当前分钟有3个请求。当一个新请求出现在当前分钟的30%的位置时,滑动窗口所允许的请求数量通过下面的公式来计算:

当前窗口的请求数+之前窗口的请求数×滑动窗口和之前窗口的重合率

用这个公式,我们可以算出滑动窗口所允许的请求数量为6.5个(3+5×0.7=6.5)。基于不同的用户场景,对这个数字可以向上或者向下取整。在我们的例子中,将它向下取整为6。

图-11

因为限流器每分钟最多允许通过7个请求,所以现在的请求可以通过。但是,再多接收一个请求就会超过阈值。受篇幅所限,我们不讨论另一种实现方式,感兴趣的读者可以阅读Medium网站上的文章“System Design—Rate limiter and Data Modelling”。滑动窗口计数器算法也不是完美的。它也有优点和缺点。

滑动窗口计数器算法优点:

•它平滑了流量中的波动,因为当前时间窗口内请求的速率是基于前一个时间窗口内请求的平均速率计算出来的。

•对内存的使用很高效。滑动窗口计数器算法的缺点是,它只对不那么严格的回溯窗口起作用。该算法只是对真实流量速率进行了近似估计,因为它假设前一个窗口中的请求是均匀分布的。尽管如此,这个问题可能并没有看起来那么严重。根据Cloudflare的实验,在4亿个请求中,只有0.003%的请求被错误地允许通过或被限流。

2.3 高层级架构

流量限制算法的基本理念是简单的。从高层级来看,我们需要一个计数器来记录有多少请求是由同一个用户、同一个IP地址等发来的。如果计数器的值超出设定的阈值,请求就不允许通过。

那么,我们应该在哪里存储计数器呢?使用数据库并不是一个好主意,因为硬盘的访问速度很慢。内存上的缓存速度快且支持基于时间的过期策略,因此可以选它。比如,Redis就是一个很受欢迎的选项。Redis是一个内存存储系统,它提供了两个命令:INCR和EXPIRE。

•INCR:把存储的计数器值加1。

•EXPIRE:为计数器设置一个超时时间。如果超时时间到期,计数器会被自动删除。

图-12展示了限流器的高层级架构,它按如下方式工作。

•客户端将请求发送给限流器中间件。

•限流器中间件在Redis对应的桶中获取计数器并检查其值是否达到了阈值。

◆ 如果达到阈值,请求将被拒绝。

◆ 如果没有,请求将被发送给API服务器。同时,系统增加计数器的值并把它保存到Redis中。

图-12

3.深入设计

图-12所示的高层级设计并没有回答下面的问题:

•流量限制规则是如何创建的?这些规则存储在哪里?

•如何处理被限流的请求?

在本节中,我们先回答关于流量限制规则的问题,然后讨论处理被限流请求的策略,最后讨论如何在分布式系统中进行流量限制。我们会给出一个详细的设计并探讨性能优化和监控方面的问题。

3.1 流量限制规则

Lyft开源了它的流量限制组件。我们来看两个使用这个组件编写的流量限制规则的例子。在下面的例子中,系统设置了每天最多允许有5条营销消息。

下面是另一个例子。这个规则是在1分钟之内客户端登录不允许超过5次。这些规则一般都写在配置文件中并保存在硬盘上。

3.2 超过流量的限制

如果一个请求被限流,API会给客户端返回HTTP响应码429(请求过多)。根据应用场景,我们有可能会把超过阈值的请求放入队列,之后再处理。比如,一些订单请求因为系统过载被限流了,我们可以保存这些订单以便稍后处理。

限流器返回的HTTP头

客户端如何知道请求有没有被限流呢?客户端如何在请求被拦截之前知道允许通过的请求数还剩多少?答案藏在HTTP头里。限流器会返回下面的HTTP头给客户端。

•X-Ratelimit-Remaining:在当前时间窗口内剩余的允许通过的请求数量。

•X-Ratelimit-Limit:客户端在每个时间窗口内可以发送多少个请求。

•X-Ratelimit-Retry-After:在被限流之后,需要等待多少秒才能继续发送请求而不被拦截。当用户发送的请求过多时,限流器将向客户端返回HTTP响应码429(表示请求太多)和X-Ratelimit-Retry-After响应头。

3.3 详细设计

图-13

•流量限制规则存储在硬盘上。工作进程(Worker)经常从硬盘中获取规则并将其存储到缓存中。

•当客户端向服务器发送请求时,请求会首先被发给限流器中间件。

•限流器中间件从缓存中加载规则。它从Redis缓存中获取计数器和上一次请求的时间戳。基于响应,限流器中间件做出以下决定:

◆ 如果请求没有被限流,就将其转发给API服务器。

◆ 如果请求被限流,限流器会向客户端返回429响应码(请求太多)来报错。同时,请求要么被丢弃,要么被转发到队列中。

3.4 分布式系统中的限流器

在单服务器环境中创建一个限流器并不难。但是,要将限流器系统扩展,以支持多个服务器和并发线程,那就是另一回事了。其中存在两个挑战:

•竞争条件(Race Condition)。

•同步问题。

竞争条件

如前所述,限流器大体上是按如下方式工作的:

•从Redis中读取计数器的值。

•检查计数器值加1后是否超过了阈值。

•如果没有,就把计数器的值在Redis中加1。

竞争条件可能会发生在一个高并发的环境中,如图-14所示。

图-14

假设在Redis中计数器的值是3。如果两个请求同时读计数器的值,它们中的任何一个在将值写回Redis之前,都会把计数器的值加1,而不检查其他线程的情况。这样,两个请求(线程)都认为它们拥有正确的计数器值——4,但是正确的计数器值应该是5。

锁是竞争条件最直观的解决方案,但是它会显著地拖慢系统。通常我们使用以下两种策略用来解决这个问题:Lua脚本和Redis的有序集合数据结构。

同步问题

同步是分布式系统中需要考虑的另一个重要因素。为了支持百万量级的用户,一个限流器有可能不足以处理所有的流量。当使用多个限流器时,限流器之间就必须同步。举个例子,如图-15所示,在左图中,客户端1向限流器1发送请求,客户端2向限流器2发送请求。但因为网络层是无状态的,所以客户端也可以把请求发给别的限流器,如图-15的右图所示。如果没有同步,则限流器1将不包含任何关于客户端2的数据,因此限流器1就无法正常工作。

图-15

一个可行的解决方案是使用黏性会话(Sticky Session),允许客户端将请求发往同一个限流器。但是,这个解决方案既不可扩展也不灵活,因此不建议使用。更好的方法是使用中心化的数据存储,比如Redis。该设计如图-16所示。

图-16

3.5 性能优化

性能优化是系统设计面试中常见的主题。我们会针对两个方面来做优化。

第一,对限流器而言,设置多数据中心是至关重要的,因为离数据中心越远,响应延时越高。大多数云服务提供商在全球设置了很多边缘服务器。比如,截至2020年5月20日,Cloudflare有194个在地理上广泛分布的边缘服务器。流量会被自动转发到最近的边缘服务器以降低延时(如图-17所示)。

图-17

3.6 监控

在设置好限流器之后,收集数据来检查限流器是否有效是很重要的。我们主要想确保:

•流量限制算法有效。

•流量限制规则有效。

举个例子,如果流量限制规则太严格,就会导致很多有效请求被丢弃。在这种情况下,我们希望稍微放宽限制。另一个例子是,我们发现,在限时促销这种流量激增的场景下,限流器变得无效了。因此,可能需要换一种流量限制算法来应对突发的流量。这时候,代币桶就是一个合适的替代算法。

4.总结

在本文中,我们讨论了不同的流量限制算法及其优缺点。这些算法包括:

•代币桶算法。

•漏桶算法。

•固定窗口计数器算法。

•滑动窗口日志算法。

•滑动窗口计数器算法。

然后,我们讨论了系统架构、分布式系统中的限流器、性能优化和监控。同任何其他系统设计面试题类似,如果有时间,你还可以谈一谈下面的话题。

•硬流量限制与软流量限制。硬流量限制是指请求数量不能超过阈值。软流量限制是指请求数量可以在短时间内超过阈值。

•在不同层级做流量限制。在本文中,我们只讨论了在应用层(HTTP层,第7层)做流量限制。流量限制也可以用在其他层。例如,你可以使用Iptables[插图](IP层,第3层)并根据IP地址来限流。注意:开放系统互联模型(Open Systems Interconnection,OSI模型)有7层[插图],其中,第1层为物理层,第2层为数据链路层,第3层为网络层,第4层为传输层,第5层为会话层,第6层为表示层,第7层为应用层。

•避免被限流。用以下最佳实践来设计你的客户端:

◆ 使用客户端缓存,避免频繁地调用API。

◆ 理解流量限制,不要在短时间内发送太多请求。

◆ 添加代码以捕获异常或错误,使客户端可以优雅地从异常中恢复。

◆ 在重试逻辑中添加足够的退避时间。

相关文章:

面试场景题系列:设计限流器

首先看看使用API限流器的好处。 •预防由拒绝服务攻击(Denial of Service,DoS)引起的资源耗尽问题。大型科技公司发布的所有API几乎都强制执行某种形式的限流操作。例如,推特限制每个用户每3小时最多发300条推文。谷歌文档API的默认限制是每个用户每60秒…...

【蓝桥杯——物联网设计与开发】拓展模块3 - 温度传感器模块

目录 一、温度传感器模块 (1)资源介绍 🔅原理图 🔅STS30-DIS-B 🌙引脚分配 🌙通信 🌙时钟拉伸(Clock Stretching) 🌙单次触发模式 🌙温度数据转…...

网狐旗舰版源码搭建概览

简单的列一下: 服务端源码内核源码移动端源码核心移动端源码AI控制工具源码多款子游戏源码前端、管理后台、代理网站源码数据库自建脚本UI工程源码配置工具及二次开发帮助文档 编译环境要求 VS2015 和 Cocos3.10 环境,支持移动端 Android 一键编译&am…...

vue3中使用vuedraggable实现拖拽

包安装方式 yarn add vuedraggablenext npm i -S vuedraggablenext属性说明 如果下面的属性说明未能完全看明&#xff0c;可以看左边的对应的菜单查看详细说明和例子。 完整例子 <template><div class"itxst"><div><draggable:list"s…...

leetcode 7. 整数反转

class Solution { public: int reverse(int x) { long long n0; if(x0) return 0; while(x%100) { xx/10; } while(x!0) { nn*10x%10; xx/10; } if(n<-2147483648||n>2147483647) return 0; return n; } };...

Nginx单向链表 ngx_list_t

目录 基本概述 数据结构 接口描述 具体实现 ngx_list_create ngx_list_init ngx_list_push 使用案例 整理自 nginx 1.9.2 源码 和 《深入理解 Nginx&#xff1a;模块开发与架构解析》 基本概述 Nginx 中的 ngx_list_t 是一个单向链表容器&#xff0c;链表中的每一个节…...

go语言中的字符串详解

目录 字符串的基本特点 1.字符串的不可变性 2.其他基本特点 字符串基本操作 1. 创建字符串 2. 获取字符串长度 3. 字符串拼接 4. 遍历字符串 5. 字符串比较 字符串常用函数 1. 判断子串 2. 查找与索引 3. 字符串替换 4. 分割与连接 5. 修剪字符串 6. 大小写转换…...

Windows脚本清理C盘缓存

方法一&#xff1a;使用power文件.ps1的文件 脚本功能 清理临时文件夹&#xff1a; 当前用户的临时文件夹&#xff08;%Temp%&#xff09;。系统临时文件夹&#xff08;C:\Windows\Temp&#xff09;。 清理 Windows 更新缓存&#xff1a; 删除 Windows 更新下载缓存&#xff0…...

分布式协同 - 分布式事务_2PC 3PC解决方案

文章目录 导图Pre2PC&#xff08;Two-Phase Commit&#xff09;协议准备阶段提交阶段情况 1&#xff1a;只要有一个事务参与者反馈未就绪&#xff08;no ready&#xff09;&#xff0c;事务协调者就会回滚事务情况 2&#xff1a;当所有事务参与者均反馈就绪&#xff08;ready&a…...

永磁同步电机负载估计算法--自适应扩张状态观测器

一、 原理介绍 在线性扩张观测器中&#xff0c;LESO观测器增益ω0 决定了观测器的跟踪速度&#xff0c;ω0 越大&#xff0c;观测器估计精度越高&#xff0c; 抗干扰能力越强&#xff0c;瞬态响应速度加快&#xff0c;过大则会引入高频噪声使系统不稳定。为使观测器在全速域内…...

【HarmonyOS应用开发——ArkTS语言】购物商城的实现【合集】

目录 &#x1f60b;环境配置&#xff1a;华为HarmonyOS开发者 &#x1f4fa;演示效果&#xff1a; &#x1f4d6;实验步骤及方法&#xff1a; 1. 在src/main/ets文件中创建components文件夹并在其中创建Home.ets和HomeProduct.ets文件。​ 2. 在Home.ets文件中定义 Home 组…...

Python二维码生成器qrcode库

要在Python中生成二维码&#xff0c;你可以使用 qrcode 库。这个库非常方便&#xff0c;允许你生成并保存二维码图像。下面是一个简单的示例&#xff0c;展示了如何使用 qrcode 库生成二维码。 首先&#xff0c;你需要安装 qrcode 库。你可以使用 pip 来安装它&#xff1a; s…...

Python:模拟(包含例题:饮料换购 图像模糊 螺旋矩阵)

模拟题&#xff1a;直接按照题目含义模拟即可&#xff0c;一般不涉及算法 注意&#xff1a; 1.读懂题&#xff1a;理清楚题目流程 2.代码和步骤一一对应&#xff1a;变量名&#xff0c;函数名&#xff0c;函数功能 3.提取重复的部分&#xff0c;写成对应的函数&#xff08;…...

git分支与部署环境的关系以及开发规范

一 某金融机构 1.1 分支分类以及作用 1.master master分支为主分支,用于部署生产环境的分支,无论任何时候都要确保master分支的稳定性;master分支由feature及hotfix分支合并,任何时间都不能直接修改代码。目前用于老仿真和老生产,暂时不动。 2.prod 主分支,是master…...

2024最新鸿蒙开发面试题合集(一)-HarmonyOS NEXT Release(API 12 Release)

1. HarmonyOS应用打包后的文件扩展名是? 打包后的文件扩展名为.hap&#xff08;HarmonyOS Ability Package&#xff09;&#xff0c;这是HarmonyOS应用的标准包格式 2. 页面和自定义组件生命周期有哪些? 页面和自定义组件生命周期说明 有Entry装饰器的component组件的生命…...

【mybatis】详解 # 和 $ 的区别,两者分别适用于哪种场景,使用 $ 不当会造成什么影响

# 和 $ 的区别 在MyBatis中&#xff0c;# 和 $ 是用来处理参数的两种不同方式&#xff0c;它们之间有一些重要的区别&#xff1a; # 符号&#xff1a; # 是用来进行参数占位符的&#xff0c;它会进行 SQL 注入防护。使用 # 时&#xff0c;MyBatis 会将参数值进行预处理&…...

Java面试题,数据结构,图的最短路径算法应用于社交网络分析

图的最短路径算法应用于社交网络分析 在一个大型社交网络中&#xff0c;用户想要找到连接两个特定用户的最短路径。假设你已经有了这个社交网络的数据模型&#xff0c;其中节点代表用户&#xff0c;边代表用户之间的关系。请设计一个解决方案&#xff0c;以找出两个用户之间的…...

Tree数据处理

文章目录 一、Tree数据重置二、Tree拆分成二级数据1、过滤数据2、二级数据 Tree组件的数据处理往往需要使用递归&#xff0c;本文归纳一下常见的数据处理情景&#xff0c;持续更新&#xff1b; 一、Tree数据重置 递归的标志就是寻找子元素的集合字段&#xff0c;一般为children…...

idea配置gitee仓库

idea配置gitee 0、fork开源项目 到自己的仓库&#xff0c;这一步相当于创建了一个自己的git仓库&#xff0c;并复制了别人的开源代码。 注意&#xff1a;如果直接下载别人的开源项目&#xff0c;需要从新配置git仓库信息&#xff0c;因为开源项目一般都设置了git信息。而修改…...

SpringBoot 事务

事务是一组操作的集合, 是一个不可分割的操作.会把所有的操作作为一个整体, 一起向数据库提交或者是撤销操作请求. 所以这组操作要么同时成功, 要么同时失败. 为什么需要事务? 我们在进行程序开发时, 也会有事务的需求. 比如转账操作: 第一步&#xff1a;A 账户 -100 元. …...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...