数据结构与算法笔记:实战篇 - 剖析微服务接口鉴权限流背后的数据结构和算法

概述

微服务是最近几年才兴起的概念。简单点将,就是把复杂的大应用,解耦成几个小的应用 。这样做的好处有很多。比如,这样有利于团队组织架构的拆分,比较团队越大协作的难度越大;再比如,每个应用都可以独立运行,独立扩容,独立上线,各个应用之间互不影响。不用像原来那样,一个小功能的上限,整个大应用都要重新发布。

不过,有利就有弊。大应用拆分成微服务之后,服务之间的调用关系变得更复杂,平台的整体负载熵升高,出错的概率、debug 问题的难度都高了好几个数量级。所以,为了解决这些问题,服务治理便成了微服务的一个技术重点。

所谓服务治理,简单点讲,就是管理微服务,保证平台整体正常、平稳地运行。服务治理涉及的内容比较多,比如鉴权、限流、降级、熔断、监控告警等等。这些服务治理功能的实现,底层依赖大类的数据结构和算法。本章,就拿其中的鉴权和限流这两个功能,来带你看看,它们的实现过程都要用到哪些数据结构和算法。


鉴权背景介绍

以防你之前可能对微服务不了解,所以我对鉴权的背景做了简化。

假设我们有一个微服务叫用户服务(User Service)。它提供很多用户相关的接口,比如获取用户信息、注册、登录等等,给公司内部的其他应用使用。但是,并不是公司内部所有应用,都可以访问这个用户服务,也并不是每个有权限访问的应用,都可以访问用户的所有接口。

给你举个例子。下图只有 A、B、C、D 四个应用可以访问用户服务,并且,每个应用只能访问用户服务的部分接口。

在这里插入图片描述

要求实现鉴权功能,我们需要事先将应用对接口的访问权限规则设置好。当某个应用访问其中一个接口时,我们就可以拿应用的请求 URL,在规则中进行匹配。如果匹配成功,就说明允许访问;如果没有可以匹配的规则,那就说明这个应用没有这个接口的访问权限,我们就拒绝服务。

如何实现快速鉴权?

接口的格式有很多,有类似 Dubbo 这样的 RPC 接口,也有类似 Spring Cloud 这样的 HTTP 接口。不同的鉴权实现方式是类似的,这里主要拿 HTTP 接口给你讲解。

鉴权的原理比较简单。那具体到实现层面,我们应该用什么样的数据结构来存储规则呢?用户请求在规则中快速匹配,又该用什么样的算法呢?

实际上,不同的规则和匹配模式,对应的数据结构和匹配算法也是不一样的。所以,关于这个问题,我们继续细化为三个更加详细的需求给你讲解。

1.如何实现精确匹配规则?

先来看最简单的一种匹配模式。只有当请求 URL 跟规则中配置的某个接口精确匹配时,这个请求才会被接受、处理。

在这里插入图片描述

不同的应用对应不同的规则集合。我们可以采用散列表来存储这种对应关系。每个应用对应的规则集合,该如何存储和匹配呢?

针对这种匹配模式,我们可以将每个应用对应的权限规则,存储在一个字符串数组中。当用户请求到来时,我们拿用户的请求 URL,在这个字符串数组中逐一匹配,匹配的算法就是我们之前学过的字符串匹配算法(比如 KMP、BM、BF 等)。

规则不会经常变动,所以,为了加快匹配速度,我们可以按字符串的大小给规则排序,把它组织称有序数组这种数据结构。当要查找某个 URL 能够匹配其中某条规则时,我们可以采用二分查找算法,在有序数组中进行匹配。

而二分查找算法的时间复杂度是 O ( l o g n ) O(logn) O(logn)(n 表示规则的个数),这比起时间复杂度是 O ( n ) O(n) O(n) 的顺序遍历快了很多。对于规则中接口长度比较长,并且鉴权功能调用量非常大的情况,这种优化方法带来的性能提升还是非常可观的。

2.如何实现前缀匹配

再来看一下稍微复杂的匹配模式。只要某条规则可以匹配请求 URL 的前缀,我们就说这条规则能够跟这个请求 URL 匹配。

在这里插入图片描述

不同的应用对应不同的规则集合。我们采用散列表来存储这种对应关系。下面着重讲解下,每个应用的规则集合,最适合用什么样的数据结构来存储。

在 Trie 树章节中我们讲到,Trie 树非常适合用来做前缀匹配。所以,针对这个需求,我们可以将每个用户的规则集合,组织成 Trie 树这种数据结构。

不过,Trie 树中的每个节点不是存储单个字符,而是存储接口被 “/” 分割后的子目录(比如 “/user/name” 被分割成 “user” “name” 两个子目录)。因为规则并不会经常变动,所以在 Trie 树中,我们可以把每个节点的子节点们,组织成有序数组这种数据结构。在匹配的过程中,可以利用二分查找算法,决定从一个节点应该跳到哪一个子节点。

在这里插入图片描述

3.如何实现模糊匹配规则?

如果我们的规则更加复杂,规则中包含通配符,比如 “**” 表示匹配任意多个子目录, “*” 表示匹配任意一个子目录。只要用户请求 URL 可以跟某条规则模糊匹配,我们就说这条规则适用于这个请求。

在这里插入图片描述

不同的应用对应不同的规则集合。我们还是采用散列表来存储这种对应关系。下面看下,每个用户对应的规则集合,该用什么结构来存储?针对这种包含通配符的模糊匹配,我们又该使用什么算法来实现呢?还记得我们在回溯算法章节讲的正则表达式的例子吗?我们可以借助正则表达式那个里子的解决思路,来解决这个问题。我们采用回溯算法,拿请求 URL 跟每条规则逐一进行模糊匹配。如何用回溯算法进行模糊匹配,这部分就不重复讲了。

不过,这个解决思路的时间复杂度是非常高的。我们需要拿每一个规则,跟请求 URL 匹配一遍。那有没有办法可以继续优化一下呢?

实际上,我们可以结合实际情况,挖掘出这样一个隐形的条件,那就是,并不是每条规则都包含通配符,包含通配符的只是少数。于是我们可以把不包含通配符的规则和包含通配符的规则分开处理。

我们把不包含通配符的规则,组织成有序数组或者 Trie 树(具体组织成什么结构,视具体需求而定,是精确匹配,就组织成有序数组,是前缀匹配,就组织成 Trie 树),而这一部分的匹配就会非常高效。剩下的是少数包含通配符的规则,我们只要把它们简单存储在一个数组中就可以了。尽管匹配起来会比较慢,但是毕竟这种规则比较少,所以这种方法也是可以接受的。

当接收到一个请求 URL 之后,我们就可以先在不包含通配符的有序数组或者 Trie 树中查找。如果能够匹配,就不需要继续继续在通配符规则中匹配了;如果不匹配,就继续在通配符规则中查找匹配。

限流背景介绍

讲完了鉴权的思路,我们再来看一下限流。

所谓限流,顾名思义,就是对接口调用的频率进行限制。比如每秒中不能超过 100 次调用,超过之后,我们就拒绝服务。限流的原则听起来非常简单,但它在很多场景中,发挥着重要的作用。比如秒杀、大促、618 等场景中,限流已经成为了保证系统平稳运行的一种标配的技术解决方案。

按照不同的限流粒度,限流可以分为很多种类型。比如给每个接口限制不同的访问频率,或者给所有接口限制总的访问频率,又或者更细力度地限制某个应用对某个接口的访问频率等等。

不同粒度的限流功能的实现思路都差不多,所以,本章主要针对限制所有接口总的访问频率这样一个限流需求来讲解。其他粒度限流需求的实现思路,你可以自己思考。

如何实现精准限流?

最简单的限流算法叫固定时间窗口限流算法。这种算法是如何工作的呢?首先我们需要先选定一个时间起点,之后,每当有接口请求到来,我们就将计数器加一。如果在当前时间窗口内,根据限流规则(比如每秒最大允许 100 次访问请求),出现累加访问次数超过限流值的情况时,我们就拒绝后续的访问请求。当进入下一个时间窗口之后,计数器就清零重新计数。

在这里插入图片描述

这种基于时间窗口的限流算法的缺点是,限流规则过于粗略,无法应对时间窗口临界时间内的突发流量。这是怎么回事呢?我举个例子给你解释下。

假设我们的限流规则是,每秒不能超过 100 次接口请求。第一个 1s 时间窗口内,100 次接口请求都集中在最后 10ms 内。在第二个 1s 的时间窗口内,100 次请求都集中在最开始的 10ms 内。虽然两个时间窗口内流量都符合限流要求,但在两个时间窗口临界的 20ms 内,会集中有 200 次接口请求。固定时间窗口限流算法并不能对这种情况做限制,所以,集中在这 20ms 内的 200 次请求就有可能压垮系统。

在这里插入图片描述

为了解决这个问题,我们可以对固定时间窗口限流宣发稍加改造。我们可以限制任意时间窗口(比如 1s)内,接口请求数都不能超过某个阈值(比如 100 次)。因此,相对于固定时间窗口限流算法,这个算法叫滑动时间窗口限流算法

流量经过滑动时间窗口限流算法整形之后,可以保证任意一个 1s 的时间窗口内,都不会超过最大允许的限流值,从流量曲线上来看会更加平滑。那具体到实现层面,我们该如何做呢?

假设限流规则是,在任意 1s 内,接口的请求次数不能大于 k 次。我们就维护一个大小为 k+1 的循环队列,用来记录 1s 内到来的请求。注意,这里的循环队列的大小等于限流次数加一,因为循环队列存储数据时,会浪费一个存储单元。

当有新的请求到来时,我们将与这个新请求的时间间隔超过 1s 的请求,从队列中删除。然后,我们再来看循环队列中是否有空闲位置。如果有,则把新请求存储在队列尾部(tail 指针所指的位置);如果没有,则说明这 1 秒内的请求次数已经超过了限流值 k,所以这个请求被拒绝服务。

为了方便你理解,我举了一个例子。在这个例子中,我们假设限流的规则是,任意 1 秒内,接口的请求次数都不能大于 6 次。

在这里插入图片描述

即便滑动时间窗口限流算法可以保证任意时间窗口内,接口请求次数不会超过最大限流值,但是仍然不能防止,在西事件力度上访问过于集中的问题。

比如刚刚举的例子,第一个 1s 的时间窗口内,100 次请求都集中在最后 10ms,也就是说,基于时间窗口的限流算法,不管是固定时间窗口还是滑动时间窗口,只能在选定的时间粒度上限流,对选定时间粒度内的更加细粒度的访问频率不做限制。

实际上,针对这个问题,还有很多更加平滑的限流算法,比如令牌桶算法、漏桶算法等。如果感兴趣,你可以自己去研究一下。

总结

本章,讲解了跟微服务相关的接口鉴权和限流功能的实现思路。

关于鉴权,我们讲了三种不同的规则匹配模式。不管是哪种匹配模式,我们都可以用散列表来存储不同应用对应的不同规则集合。对于每个应用的规则集合的存储,三种匹配模式使用不同的数据结构。

  • 对于第一种精确匹配模式,我们利用有序数组来存储每个应用的规则集合,并通过二分查找和字符串匹配算法,来匹配请求 URL 与规则。
  • 对于第二种前缀匹配模式,我们利用 Trie 树来存储每个应用的规则集合。
  • 对于第三种模糊匹配模式,我们利用普通的数组来存储包含通配符的规则,通过回溯算法,来进行请求 URL 与规则的匹配。

关于限流,我们讲了两种限流算法,第一种是固定时间窗口限流算法,第二种是滑动时间窗口限流算法。对于滑动时间窗口限流算法,我们用了之前学过的循环队列来实现。比如固定时间窗口限流算法,它对流量的整形效果更佳,流量更加平滑。

从本章的学习中,也可以看出,对于基础架构工程师来说,如果不精通数据结构和算法,我们就很难开发出性能卓越的基础架构、中间件。这其实就体现了数据结构和算法的重要性。

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

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

相关文章

程序算法设计分析

动态规划和分治、贪心相比有什么区别?各自的优缺点? 分治算法特征: 1)规模如果很小,则很容易解决。//一般问题都能满足 2)大问题可以分为若干规模小的相同问题。//前提 3)利用子问题的解&#x…

最靓丽的C++开源通知弹框SnoreToasts自动监听软件及网页通知

SnoreToasts,作为一款轻量级的C开源项目,为开发者提供了一个便捷的方式来在Windows操作系统上展示通知弹框(Toast Notifications)。 特点与优势 轻量级:SnoreToasts采用了简洁的代码设计,避免了不必要的依…

苹果可能与谷歌大模型合作,马斯克xAI下个月推出Grok-2,比尔·盖茨:Scaling Law快要走到尽头

ChatGPT狂飙160天,世界已经不是之前的样子。 更多资源欢迎关注 1、苹果被曝 Gemini 模型今秋或融入苹果智能生态系统 苹果知名爆料人马克古尔曼(Mark Gurman)最新透露,苹果公司将于今年秋季宣布与Alphabet旗下的谷歌的大模型Gem…

电流传感器技术详解,你都懂吗?

在任何过程中,电量的测量对于监控、分析和控制系统都是必不可少的。要执行这些类型的测量,必须使用电流传感器。除非可以测量,否则无法管理物理量。让我们深入了解电流传感器的行为。 电流传感器 电流传感器是将电流信号转换为另一个可分析…

PyTorch计算机视觉实战:目标检测、图像处理与深度学习

本书基于真实数据集,全面系统地阐述现代计算机视觉实用技术、方法和实践,涵盖50多个计算机视觉问题。全书分为四部分:一部分介绍神经网络和PyTorch的基础知识,以及如何使用PyTorch构建并训练神经网络,包括输入数据缩放…

从零到一:全面掌握模板机部署与虚拟机克隆的实训指南【实训Day01】

一、模板机的部署 1.设置好子网ip和子网掩码以及网关 保证有NAT模式 子网IP着重记住前三段:192.168.222.xxx 2.开始建立虚拟机 注意:路径中不要有中文 3.安排8个内核 4.安排2GB内存 创建初步成功,接着 5.开启此虚拟机 6.选择中文 7.日期&…

好展位,抢先订!2024年亚洲(泰国) 国际电力能源展

2024年亚洲(泰国) 国际电力能源展 Powerex Asia & Electric Asia 2024 ▏发电设备 ▏输配电 ▏电网 ▏太阳能光伏 ▏储能 ▏氢能 ▏生物能源 ▏风能 ▏智慧能源 ▏ 能源部官方批复亚洲重点电力及新能源展 展会时间:2024 年 10 月 16-…

R可视化数据必要格式——长格式

一、引言 我们在对数据进行可视化时遇到最头疼、最常见的问题是什么?数据问题。 因为我们往往不会从零自己编程进行可视化,往往是现有模板或积累,而正确的数据格式对应正确的图形包要求,一定会正确出图,所以只有一个问…

鸿蒙开发设备管理:【@ohos.settings (设置数据项名称)】

设置数据项名称 说明: 本模块首批接口从API version 8开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 本模块提供设置数据项的访问功能相关接口的说明及示例。 导入模块 import settings from ohos.settings;settings.getUri…

Pointnet++改进即插即用系列:全网首发GLSA聚合和表示全局和局部空间特征|即插即用,提升特征提取模块性能

简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入GLSA,提升性能。3.专栏持续更新,紧随最新的研究内容。 目录 1.理论介绍 2.修改步骤 2.1 步骤一 2.2 步骤二 2.3 步骤三 1.理论介…

Java中==和equals()的区别

Java中和equals()的区别 1、操作符2、equals()方法3、总结 💖The Begin💖点点关注,收藏不迷路💖 在Java中,和equals()是两个常用的比较操作符和方法,但它们之间的用法和含义却有着本…

GPT-5即将登场:AI赋能下的未来工作与日常生活新图景

随着OpenAI首席技术官米拉穆拉蒂在近期采访中的明确表态,GPT-5的发布已不再是遥不可及的梦想,而是即将在一年半后与我们见面的现实。这一消息无疑在科技界乃至全社会引发了广泛关注和热烈讨论。从GPT-4到GPT-5的飞跃,被形容为从高中生到博士生…

03.C1W2.Sentiment Analysis with Naïve Bayes

目录 Probability and Bayes’ RuleIntroductionProbabilitiesProbability of the intersection Bayes’ RuleConditional ProbabilitiesBayes’ RuleQuiz: Bayes’ Rule Applied Nave Bayes IntroductionNave Bayes for Sentiment Analysis P ( w i ∣ c l a s s ) P(w_i|clas…

【笔记】太久不用redis忘记怎么后台登陆了

!首先启动虚拟机linux的centos7 2.启动finalshell 我的redis启动在根目录用 redis-server redis.conf --启动 systemctl status redis --查看redis状态 是否active redis-cli -h centos的ip地址 -p 你要用的redis端口号(默认为6379) -a 你…

JavaSE阶段面试题(一)

目录 1.int a 1, int b 1, Integer c 1, Integer d 1;四个区别和联系,以及c和d是同一个吗? 2.为什么重写HashCode必须重写euqals,两者之间的关系? 3.创建对象的方式有哪些 4.重写和重载的区别 5.抽象类和接口…

firewalld(6)自定义services、ipset

简介 在前面的文章中我们已经介绍了zone、rich rule 、--direct等功能和基本配置。在前面文章中,我们提到过firewalld内置了很多服务,可以通过firewall-cmd --get-services来查看服务,也可以通过配置文件查看这些服务/var/lib/firewalld/ser…

汽车IVI中控开发入门及进阶(三十三):i.MX linux开发之开发板

前言: 大部分物料/芯片,不管MCU 还是SoC,都会有原厂提供配套开发板,有这样一个使用原型,在遇到问题时或者进行开发时可以使用。 i.MX 8QuadXPlus MEK board: 1、要测试display显示器,可使用i.MX mini SAS将“LVDS1_CH0”端口连接到LVDS到HDMI适配器的cable。 2、要测试…

12. Revit API: Document、Element

12. Revit API: Document、Element 前言 还是先讲一下Document吧,不然Selection不好讲,那涉及到了挺多东西的,比元素(Element)和各类Filter,这些都与Document有关,所以先简单讲一下这个。 一、…

解码AWS EC2:塑造云服务器新标杆的五大核心优势

在云计算领域,亚马逊弹性计算云(Amazon Elastic Compute Cloud, 简称EC2)作为AWS的明星服务,凭借其卓越的性能、灵活性和广泛的生态系统,已经成为企业构建云上基础设施的首选。EC2不仅仅是一个简单的云服务器租用服务&…

【C++】多态详解

💗个人主页💗 ⭐个人专栏——C学习⭐ 💫点击关注🤩一起学习C语言💯💫 目录 一、多态概念 二、多态的定义及实现 1. 多态的构成条件 2. 虚函数 2.1 什么是虚函数 2.2 虚函数的重写 2.3 虚函数重写的两个…