基于搭建微服务架构的实践,作者总结出一套适用于现代化Web和云技术的实战经验,并从微服务领域的先行者(如Netflix、Soundcloud、谷歌、亚马逊、Spotify等)身上学到了很多经验。全文很长,建议收藏转发后阅读。
作者丨Vinay Sahni 译者丨大愚若智 编辑丨Cindy 产品复杂度与日俱增。想继续按以前的节奏去演进产品变得越来越困难了,是时候寻找一种更好的方法了。微服务架构承诺可以让团队快速前进,但与此同时也带来了一系列全新的挑战。 微服务架构主要是为了应对复杂度。相对于单一的复杂系统,该架构由多个简单的服务组成,这些服务之间存在复杂的交互,其目标在于确保复杂度能够得到控制。 |确定关键需求 微服务架构自身也会导致复杂度增加。需要运维的系统数量不仅没有减少,反而变得更多。到处散布着日志文件,分布式环境中难以维持一致性,类似的问题还有很多。我们的目标在于实现一种简化复杂度的状态:知道复杂度问题无法避免,但可通过工具和过程加以控制。 明确了需求后,我打算从下列几方面着手。
|平台 服务团队需要能自由构建必要的东西。与此同时为确保一致性并管理愈加复杂的运维工作,还要设立相应标准。这意味着需要让通信、日志、监控和部署等工作实现标准化。平台本身就是一系列标准和工具组合的产物,借助平台将能更容易地创建并运维满足标准要求的服务。 团队该如何与平台交互?通常可能需要大量Web接口进行持续集成、监控、日志,以及文档记录。团队需要用一个控制中心作为这一切的起点。列出所有服务并链接到各种内部工具的简单控制中心就行。理想情况下,控制中心还可以从内部工具中收集数据,并在快速浏览中提供额外的价值。 对于已经在使用团队交流解决方案的组织,一种较为常见的做法是使用自定义的机器人程序将常用任务直接显示在聊天界面中。这种做法在需要触发测试和部署,或需要快速了解某个运行中服务状态的时候较为有用。通过这种做法,聊天记录也可以变成一种针对以往操作进行审计的审计轨迹。 | 服务的本质 平台内部通常运行了很多服务。“很多”取决于具体规模,可能指十多个,数百个,甚至上千个。每个服务均通过相互独立的程序包封装了一定的业务能力。为确保服务能专注于某一具体用途,所构建的服务必须尽可能小;但为了将服务之间的交互减至最低,服务又要足够大。 每个服务都需要独立开发和部署。如果没有对API做出破坏性改动,则无需与其他服务团队进行协调。每个服务实际上都是相关团队自己的产品,有着自己的基准代码和生命周期。
提防共享的库!如果对共享库进行更改需要同时更新所有服务,意味着所有服务间存在强耦合点。在引入共享库之前,一定要充分理解可能产生的后果。 当多个服务直接读写数据库中同一张表时,对这些表做任何改动都需要协调这些相关服务的部署。这一点违背了服务相互独立这一原则。共享的数据存储很容易不经意间造成耦合。每个服务需要有自己的私有数据。 私有数据还能提供另一个优势:根据服务的具体用例选择最适合的数据库技术。 不一定。每个服务需要自己的数据库,但这些数据库可共置在一台共享的数据服务器上。重点在于不应让服务知道其他服务底层数据库的存在。这样即可用一台共享数据服务器先开始开发,以后只要更改配置即可将不同服务的数据库隔离起来。 然而共享的数据服务器也可能造成一些问题。首先会形成单点故障,进而导致一大批服务同时故障,这一点绝不能掉以轻心。其次很可能因为一个服务占用太多资源而无意中对其他服务造成影响。 这个问题很复杂。每个服务应该是一种能提供某些业务能力的自治单位。 服务应当弱耦合在一起,对其他服务的依赖应尽可能低。一个服务与其他服务的任何通信都应通过公开暴露的接口(API、事件等)实现,这些接口需要妥善设计以隐藏内部细节。 服务应具备高内聚力。密切相关的多个功能应尽量包含在同一个服务中,这样可将服务之间的干扰降至最低。 服务应包含单一的界限上下文。界限上下文(Bounded context)可将某一领域的内部细节,包括该领域特定的模块封装在一起。 理想情况下,必须对自己的产品和业务有足够的了解才能确定最自然的服务边界。就算一开始确定的边界是错误的,服务之间的弱耦合也可以让你在未来轻松重构(例如合并、拆分、重组)。 再深入看看界限上下文吧。应该尽量避免创建哑CRUD服务,这类服务只能导致紧密的耦合以及较差的内聚力。领域驱动设计所引入的界限上下文这一概念可以帮助我们确定最合理的服务边界。界限上下文可将某领域的相关内容封装在一起(例如将其封装为服务)。多个界限上下文可通过妥善定义的接口(例如API)进行通信。 虽然一些模型可以完全封装在界限上下文内部,但有些模型可能有跨越不同界限上下文的不同用例(和相关属性)。这种情况下每个界限上下文须具备与模型有关的属性。 这里可以举一个更具体的例子。例如技术服务台解决方案Enchant,该系统的核心模型是工单(Ticket),每个工单代表客户的一个支持请求。工单服务负责管理工单的整个生命周期,包含主要属性。此外还有报表服务负责预先计算并存储每个特定工单的统计信息。每个工单的报表统计信息用两种方式存储:
将统计信息存储在报表中的做法可以更好地满足服务要求:弱耦合、高内聚,每个服务自行负责自己的界限上下文。然而这种方法会增加复杂度。工单的改动需要通知报表服务,为此可以让报表服务订阅由工单服务提供的事件流,这样也可以让服务之间的耦合程度降至最低。 微服务这个词中的微字与物理规模或代码行数没有任何关系,仅代表最小化的复杂度。服务应当足够小,只承担某个单一作用,与此同时服务也要足够大,以便将服务之间的通信量降至最低。 没什么硬性规定限制服务只能是一个进程、一个虚拟机、或一个容器。服务应包含能够以自治方式实现业务能力所需的全部功能,甚至可包含外部服务,例如用于实现持久存储的数据服务器,用于异步工作进程的作业队列,甚至确保一切快速运行所需的缓存。 无状态(Stateless)服务实例不存储与上一个请求有关的任何信息,传入请求可发送至服务的任何实例。这种做法的主要收益在于可简化运维和伸缩工作。可以在后台服务之前增加一个简单的负载均衡器,随后即可根据请求数量的变化轻松地增加或删除实例,同时故障实例的替换过程也更为简单。 话虽如此,很多服务可能依然需要用某种方式存储数据。这些数据可推送至外部服务中,例如以磁盘作为边界的数据库服务器或以内存作为边界的缓存。 无论怎么看,分布式系统的一致性都是个老大难问题。与其对抗这种问题,更好的方法是让分布式系统能够实现最终一致性。在最终一致的系统中,虽然不同服务在特定时间点会对数据产生有分歧的视图,但最终会通过汇聚获得一致视图。 如果能对服务进行妥善建模(例如弱耦合和高内聚),你会发现对大部分用例来说,最终一致性是一种足够好的默认设置。创建最终一致的分布式系统,这种做法在方向上与创建弱耦合系统的目标是一致的。都趋向于进行异步通信,面对下游服务的故障可以获得固有的保护。 举个例子。在Enchant中有一个工单服务(用于管理客户支持请求)和一个报表服务(用于计算工单的统计信息)。报表服务可从工单服务获得一个异步的内容更新馈送源(Feed)。这意味着当任何时候工单服务中产生更新后,报表服务可在几秒后获得相关信息。 在这几秒钟内,两个服务会对底层客户请求产生有差异的视图。对于报表这一具体用例来说,几秒钟的延迟是可接受的。此外这种方式还提供了一个附加的收益,可以保护工单服务不受报表服务故障的影响。 在采用最终一致性的设置后会发现,当请求等待回应而受阻时,并不需要在这过程中将其他所有操作全部完成。任何可以等待(并且是资源或时间密集型)的操作都可以作为作业传递给异步工作进程。这种方式可以:
上文提到过作业运行失败后可重试。自动重试失败作业的做法会遇到一个挑战:可能会无法知道失败的作业在失败前是否完成了自己的工作。为确保相关操作足够简单,必须让作业具备幂等性(Idempotent)。在这个语境下,幂等性意味着多次运行同一个作业不会产生任何消极影响。无论作业运行一次或多次,最终结果必须相同。 服务(及其API)和文档一样重要。对于每个服务,一定要提供清晰易用的使用文档。理想情况下,所有服务的使用文档应放在同一个位置,并确保需要使用时服务团队不需要花费大量时间考虑文档到底在哪里。 需要将已记录端点的变动情况通知给其他所依赖服务的所有者。通知系统必须了解不同服务目前的所有者是谁,并了解有关团队或所有权的全部改动。这些信息可通过平台进行追踪并提供。 无状态服务的优势之一在于可同时为服务运行多个实例。由于每个实例都是无状态的,可由任何实例处理任何请求。这一特性与负载均衡器非常相符,借此可对服务进行更简单的伸缩。同时几乎所有云平台都提供了负载均衡器功能。 传统负载均衡器位于接收端,此时客户端只知道一个目标的存在。该目标接收请求并将其分散至多个(对外隐藏的)内部服务实例。另一种做法是客户端负载均衡器,例如Netflix使用Ribbon构建了这样的实现。 对于客户端负载均衡器,客户端可以知道多个可能的目标,并根据策略选择要使用的目标,例如可首选使用同一数据中心内的目标以降低请求延迟。此外还可配合使用这两种做法:首先从传统负载均衡器着手,随后在需要实现高级路由功能时添加客户端负载均衡器。客户端负载均衡器可包含在客户端库中。 如果要让数据跨越私有网络边界与外部客户端通信,将有更多额外需求需要考虑。数据需要通过某种方式编码,将往返通信的次数降至最低,为确保这些客户端只能访问需要的数据,还要提高安全措施,诸如此类。 聚合服务将负责收集来自其他服务的数据,处理任何特殊的编码或压缩要求,同时由于客户端仅需要与这一个服务通信,这样做还有助于简化安全工作的实现。 在不同的需求下,构建不同的聚合服务会更符合实际,如一个用户场景(公开API、移动客户端、桌面客户端等)一个聚合服务。如果你的需求更简单,那么一个聚合服务也就足够了。 由于聚合服务需要处理来自多个服务的数据,很容易无意中泄露其中包含的业务逻辑并降低服务内聚力。这种情况需要当心!任何与某个服务有关的业务逻辑应该只属于该服务。聚合服务的本意是在外部客户端和内部服务之间充当一个稀薄的粘合层。 答案主要取决于具体的上下文。因此首先需要考虑下列几个问题:
聚合服务的本质使得这种服务依赖(并紧密耦合于)一个或多个其他服务,因此这种服务会受到其他任何相关故障服务的影响,进而导致聚合服务故障。所以必须了解不同的故障场景并制定好应对措施。 需要结合数据所在位置或服务在整个大架构中的角色考虑服务安全需求。可能要对传输中或存储后的数据提供安全保护。可能要在服务边界或私有网络边界实施网络安全机制。很难确保万无一失,下文提供了一些值得考虑的原则:
|服务的交互 微服务架构提倡有许多职责单一的小服务组成,这些服务之间互相交互。然而这就造成了一系列的问题,比如:服务之间如何发现彼此?是否采用统一的协议?如果一个服务无法与其他服务通信会怎样?我会在接下来的内容里讨论部分相关话题。 随着服务数量越来越多,在服务间使用标准化通信方法愈加重要。由于服务不一定使用相同语言编写,通信协议的选择必须不依赖具体语言和平台。此外还要同时考虑同步和异步通信。 首先,传输协议HTTP是同步通信的最佳选择。HTTP客户端几乎已得到所有语言支持,很多云平台都内建了HTTP负载均衡器,该协议本身内建了用于缓存、持久连接、压缩、身份验证以及加密所需的机制。最重要的是,围绕该协议有一个稳健成熟的工具生态体系可供使用:缓存服务器、负载均衡器、优秀的浏览器端调试器,甚至可对请求进行重播的代理。 协议较为繁琐是HTTP的不足之一,它需频繁发送纯文本头字段(Header),并频繁建立和终止连接。相比HTTP生态系统已经带来的巨大价值,我们可以辩解说接受这些不足是一个合理的权衡。 然而,时下已经有了另外一个更好的选项:HTTP/2。通过对头字段进行压缩并用一个持久连接实现多路复用请求(Multiplexing request),该协议有效解决了上述问题,同时维持与老版本客户端的向后兼容性。HTTP目前依然实用,未来一样很好用。 话虽如此,如果已达到一定规模,通过降低内部传输的开销可对底线造成较显著的改善,那么也许更适合用其他传输方式。 对于异步通信,需要实施发布订阅模式。为此有两个主要方法:
一个重量级的消息传递基础设施的存在通常会鼓励将业务逻辑脱离服务本身而进入消息层。这种做法会导致服务的内聚性降低,并增加了额外一层,从而会降低服务内聚力,并增加了额一层,随着使用时间延长可能无意间导致复杂度逐渐提高。与一个服务有关的任何业务逻辑都应属于该服务,并由该服务的团队负责管理。强烈建议坚守智能的服务 哑管道这样的原则,以确保不同团队维持自治力。 这方面有两个主要的竞争者:
JSON是一种稳定并得到广泛应用的序列化格式,浏览器包含对该格式的原生解析能力,浏览器内建调试器也能很好地显示这种内容。唯一不足在于要具备JSON解析器/序列器,好在所有语言都已提供。使用JSON最主要的麻烦在于每条信息会重复包含属性名,导致传输效率低下,但传输协议的压缩功能可缓解这一问题。 在解析和网络传输方面Protocol buffers更高效,并经历了谷歌高负荷环境的考验。取决于消息定义文件,这种格式需针对不同语言具备解析器/序列器生成器。不同语言对该格式的支持不像JSON那么广泛,不过大部分现代化语言均已支持。为了使用这种格式,服务器需要预先与客户端共享消息定义文件。 JSON更易上手更通用,Protocol buffers更精益更快速,但在.proto文件的共享和编译方面会产生些许额外开发负担。这两种格式都是不错的选择,选定一个坚持使用吧。 正如需要自动化的监控和警报机制,确定所有服务有统一的异常定义,也是一个好主意。 对HTTP传输协议来说这一点很简单。服务通常可生成200、300以及400系列的HTTP状态代码。任何500错误代码或超时通常可认定服务出现故障。这些代码也可用于反向代理和负载均衡器,如果这些组件无法与后端实例通信,通常会抛出502(Bad Gateway)或503(Service Unavailable)错误。 好的API必须易用且易理解,可在不暴露底层实现细节的情况下提供完成任务所需的信息,这些信息数量恰恰满足需求,不多不少。同时API的演化只会对现有用户造成最少量影响。API的设计更像是一种艺术而非科学。 由于已选择HTTP作为传输协议,为了释放HTTP的全部潜力,还要将HTTP与REST配合使用。RESTful API提供了资源丰富的端点,可通过GET、POST以及PATCH等动词操作。我之前写的一篇有关RESTful API设计的文章详细介绍了对外API的设计,这篇文章的大部分内容也适用于微服务API的设计。 这样可以让不同服务的API实现一致性并更简洁。借此可通过更易于理解的方式检索或搜索内容,无须寻找修改资源某一特定属性所需的方法,可直接针对资源使用PATCH(部分更新)。这样可减少API上的端点数量,有助于进一步降低复杂度。 由于大部分现代化公开API都是RESTful API,因此有丰富的工具可供使用。例如客户端库、测试自动化工具,以及自省代理(Introspecting proxy)。 在服务实例变化不定的环境中,用硬编码指定IP地址的方式是行不通的,需要通过某种发现机制让服务能相互查找。这意味着对于到底有哪些可用服务必须具备“权威信息来源”。此外还要通过某种方式借助这个权威来源发现服务实例之间的通信,并对其进行均衡。 服务注册表可以作为信息的权威来源。其中包含有关可用服务的信息,以及服务网络位置。考虑到该服务本身的一些关键特质(是一种单一故障点),该服务必须具备极高容错能力。 可通过两种方式将服务注册至服务注册表:
在大架构方面,服务注册表也可充当监控系统或系统可视化工具所用状态信息的来源。 创建可用的注册表只解决了问题的一半,还需要实际使用这种注册表才能让服务以动态的方式相互发现!此时主要有两种方法:
在大部分云平台上,获得最基本服务发现功能最简单的办法是为每个服务添加一条指向负载均衡器的DNS记录。此时负载均衡器的已注册实例清单将成为服务注册表,DNS查询将成为服务发现机制。运行状况异常的实例会自动被负载均衡器移除,并在恢复运行后重新加入。 当有多个服务需要相互协调时,主要可通过两种方法实施复杂工作流:使用集中化编排程序(Orchestrator),或使用去中心化交互。 集中化的编排程序会通过一个进程对多个服务进行协调以完成大规模工作流。服务对工作流本身及所涉及的具体细节完全不知情。编排程序会处理复杂的安排和协调,例如强制规定不同服务的运行顺序,或对某个服务请求失败后重试。为确保编排程序了解执行进展,此时通信通常是同步的。使用编排程序最大的挑战在于要在一个集中位置建立业务逻辑。 去中心化的交互中,更大规模工作流内的每个服务将完全自行负责自己的角色。服务之间通常会相互侦听,尽快完成自己的工作,如果出错则尽快重试,并在执行完毕后送出相关事件。此时通信通常是异步的,业务逻辑依然保留在相关服务中。这种方式的挑战之处在于需要追踪工作流整体的执行进度。 去中心化交互可更好地满足我们的要求:弱耦合,高内聚,每个服务自行负责自己的界限上下文。所有这些特征最终都可提高团队的自治能力。通过服务监控所有相互协调的其他服务所发出的事件,这种方法也可用被动方式对工作流整体的状态进行追踪。 变化是不可避免的,重点在于如何妥善管理这些变化。API的版本控制能力,以及同时对多个版本提供支持的能力,这些都可大幅降低变化对其他服务团队造成的影响。这样大家将有更多时间按自己的计划更新代码。每个API都应该有版本控制机制! 虽然如此,无限期地维持老版本这本身也是一个充满挑战的工作。无论出于什么原因,对老版本的支持只需要维持数周,最多数月。这样其他团队才能获得自己需要的时间,不会进一步拖累你自己的开发速度。 虽然听起来挺好,但其实很糟糕。创建一个全新服务,这本身就会带来不小的开销。要监控的内容更多,可能出错的东西也更多。老版本中发现的Bug很有可能也要在新版中修复。 如果服务的所有版本需要对底层数据获得共享视图,情况将变得更复杂。虽然可以让所有服务与同一个数据库通信,但这又成了一个糟糕的主意!所有服务会与持久存储架构建立非常紧密的耦合。在任何版本中对架构所做的任何改动都会无意导致其他版本服务的中断。最终也许只能使用相互同步的多份基准代码。 所有受支持的版本应共存于同一份基准代码和同一个服务实例中。此时可使用结构版本化(Versioning scheme)确定请求的到底是哪个版本。可行的情况下,老的端点应当更新以将修改后的请求中继至对应新端点。虽然同一个服务中多版本共存的局面不会降低复杂度,但可避免无意中增加复杂度,导致本就复杂的环境变得更复杂。 一个服务如果超负荷运转,那么让它直接快速的失败,要好过拖累其他服务。所有类型的请求需要对不同情况下的使用进行一定的限制。此外还要通过某种方法,按照需要提高对使用情况的限制。这样可确保服务稳定,而负责服务的团队也将有机会对使用量的进一步激增做好规划。 虽然此类限制对不能自动伸缩的服务最重要,但对于可自动伸缩的服务最好也加以限制。你肯定不希望以“惊喜”的方式了解到设计决策中所包含的局限!然而对可自动伸缩的服务进行的限制可略微放宽一些。 为了帮助服务团队获得自助服务管理能力,限制机制的管理界面可包含在服务模板中,或在平台层面上通过集中化服务的方式提供。 请求量突然激增会使得服务对下游服务造成极大压力,这样的压力还会顺着整个链条继续向下传递。连接池有助于在请求量短时间内激增时“抚平”影响。通过合理设置连接池规模,即可即可对在任意时间内向下游发出的请求数量做出限制。可为每个需要通信的服务设置一个独立连接池,借此将下游服务中存在的故障隔离在系统的特定位置。 如果无法从池中获得连接,此时最好能快速失败,而不要无限期堵塞。这个速度决定了其他服务要等你等多久。故障本身对团队来说也是一种预警,并会导致一些很有用的疑问:是否需要扩容了?是否下游服务中断了? 设想一下这样的场景:一个服务接到大量请求开始超负荷并变慢,进而对该服务的所有调用都开始变慢。这种问题会持续对上游造成影响,最终用户界面开始显得迟钝。用户请求得不到预期回应,开始四处乱点期待着能自己解决问题(遗憾的是这种事情经常发生),这种做法只会让问题进一步恶化。这就是连锁故障。很多服务会在出现故障的同时发出警报,相信我,你绝对不想就这种问题获得第一手的亲身体验。 由于有多个服务相互支撑并可能出现故障,此时确定问题的根源成了一个充满挑战的工作。故障是服务本身的内部问题造成的,还是因为某个下游服务?这种场景很适合为下游API的调用使用较短超时值。超时值使得多个服务不会“缓慢地”逐渐进入故障状态,而是可以让一个服务真正发生故障时其他服务能快速故障,并从中判断出问题根源。 因此仅使用默认的30秒超时值还不够好。要将超时值设置为下游服务认为合理的时间。举例来说,如果预计某个服务的响应时间为10-50毫秒,那么超时值只要大于500毫秒就已经不合适了。 服务API会逐渐演化。需要与API的使用方进行协调的变更,其发布速度会远低于无须这种协调的变更。为了将耦合程度降至最低,服务应当能容忍与之通信的服务中所产生的不相关变更。这其实意味着如果服务中加入了字段,或改动/删除了不再使用的字段,不应该导致与该服务通信的其他服务出现故障。 如果所有服务都能容忍不相关变更,就可在无须任何协调的情况下对API进行额外改动。对于比较重大但依然不相关的变更,也只需要使用该服务的团队运行自己的测试工具确认一切都能正常工作即可。 与故障资源进行的任何通信企图都会产生成本。消耗端需要使用资源尝试发起请求,这会用到网络资源,同时也会消耗目标端的资源。 断路开关可防止发起注定会失败的请求。该机制的实现非常简单:如果到某个服务的请求出现较多失败,添加一个标记并停止在接下来一段时间里继续向这个服务发请求。但同时也要定期允许发起一个请求,借此确认该服务是否重新上线,确认上线即可取消这样的标记。 断路开关的逻辑要封装在服务模板所包含的客户端库中。 一个用户发出的请求可能引起多个服务执行操作,因此对某一特定请求的影响范围进行调试可能会很难。此时一种简化该过程的方法是:在服务请求中包含一个关联ID。关联ID是一种唯一标识符,可用于区分每个服务传递给任意下游请求的请求来源。通过与集中化日志机制配合使用,可轻松看到请求在整个基础架构中的前进路径。 该ID可由面向用户的聚合服务,或由任何需要发出请求,但该请求并非传入请求直接导致的意外结果的服务生成。任何足够随机的字符串(例如UUID)都可用作这个用途。 在最终一致的世界里,服务可通过订阅事件馈送源(Feed)的方式与其他服务同步数据。 虽然听起来很简单,但魔鬼往往隐藏在细节里。数据库和事件流通常是两个不同系统,这使得你非常难以用原子级方式同时写入这两个系统,进而难以确保最终一致性。 可以使用本地数据库事务封装数据库操作,同时将其写入事件表。随后事件发布程序会从事件表读取。但并非所有数据库都支持此类事务,事件发布程序可能要从数据库提交日志中读取信息,但并非所有数据库都能暴露此类日志。 分布式系统很难实现一致性。就算以分布式一致性为核心特性的数据库系统也要很多额外操作才能实现。与其打这样一场硬仗,其实也可以考虑使用某种尽可能足够好的同步解决方案,并在事后通过专门的过程找出并修复不一致的地方。 这种方式也能实现最终一致性,只不过“不一致的窗口期”可能会略微长于通过复杂的方式跨越不同系统(数据库和事件流)实现一致性时的窗口期。 就算要跨越多个服务复制某些数据,也应该让一个服务始终成为任何其他数据的单一数据来源。对数据的所有更新需要在这个数据源上进行,同时这个数据源也可在未来用于进行一致性验证时的记录来源。 首先需要复查服务边界是否正确设置。如果服务需要强一致,通常将数据共置在一个服务(以及一个数据库)这样的做法更合理,这样可用更简单方式提供事务保障。 如果确认服务边界设置无误但依然需要强一致,则要检查一下分布式事务,这种机制很难妥善实现,同时可能会在两个服务间产生强耦合。建议将其作为最后的手段。 所有API请求需要进行身份认证。这样服务团队才能更好地分析使用模式,并获得用于管理不同使用模式下对请求进行限制所需的标识符。 这种标识符是服务团队为使用该服务的用户提供的,具备唯一性的API密钥。必须具备某种颁发和撤销此类API密钥的方法。这些方法可内建于服务模板,或通过集中化身份认证服务在平台层面上提供,这样还可让服务团队以自助服务的方式管理自己的密钥。 声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除 |