写点什么

深入浅出 REST

2007 年 12 月 25 日

不知你是否意识到,围绕着什么才是实现异构的应用到应用通信的“正确”方式,一场争论正进行的如火如荼:虽然当前主流的方式明显地集中在基于 SOAP、WSDL 和 WS-* 规范的 Web Services 领域,但也有少数人用细小但洪亮的声音主张说更好的方式是 REST,表述性状态转移(REpresentational State Transfer)的简称。在本文中,我不会涉及争论的话题,而是尝试对 REST 和 RESTful HTTP 应用集成做实用性的介绍。以我的经验,有些话题一旦触及就会引来众多的讨论,当涉及到这方面话题的时候,我会深入详细地阐述。

REST 关键原则

大部分对 REST 的介绍是以其正式的定义和背景作为开场的。但这儿且先按下不表,我先提出一个简单扼要的定义:REST 定义了应该如何正确地使用(这和大多数人的实际使用方式有很大不同)Web 标准,例如 HTTP 和 URI。如果你在设计应用程序时能坚持 REST 原则,那就预示着你将会得到一个使用了优质 Web 架构(这将让你受益)的系统。总之,五条关键原则列举如下:

  • 为所有“事物”定义 ID
  • 将所有事物链接在一起
  • 使用标准方法
  • 资源多重表述
  • 无状态通信

下面让我们进一步审视这些原则。

为所有“事物”定义 ID

在这里我使用了“事物”来代替更正式准确的术语“资源”,因为一条如此简单的原则,不应该被淹没在术语当中。思考一下人们构建的系统,通常会找到一系列值得被标识的关键抽象。每个事物都应该是可标识的,都应该拥有一个明显的 ID——在 Web 中,代表 ID 的统一概念是:URI。URI 构成了一个全局命名空间,使用 URI 标识你的关键资源意味着它们获得了一个唯一、全局的 ID。

对事物使用一致的命名规则(naming scheme)最主要的好处就是你不需要提出自己的规则——而是依靠某个已被定义,在全球范围中几乎完美运行,并且能被绝大多数人所理解的规则。想一下你构建的上一个应用(假设它不是采用 RESTful 方式构建的)中的任意一个高级对象(high-level object),那就很有可能看到许多从使用唯一标识中受益的用例。比如,如果你的应用中包含一个对顾客的抽象,那么我可以相当肯定,用户会希望将一个指向某个顾客的链接,能通过电子邮件发送到同事那里,或者加入到浏览器的书签中,甚至写到纸上。更透彻地讲:如果在一个类似于 Amazon.com 的在线商城中,没有用唯一的 ID(一个 URI)标识它的每一件商品,可想而知这将是多么可怕的业务决策。

当面对这个原则时,许多人惊讶于这是否意味着需要直接向外界暴露数据库记录(或者数据库记录 ID)——自从多年以来面向对象的实践告诫我们,要将持久化的信息作为实现细节隐藏起来之后,哪怕是刚有点想法都常会使人惊恐。但是这条原则与隐藏实现细节两者之间并没有任何冲突:通常,值得被 URI 标识的事物——资源——要比数据库记录抽象的多。例如,一个定单资源可以由定单项、地址以及许多其它方面(可能不希望作为单独标识的资源暴露出来)组成。标识所有值得标识的事物,领会这个观念可以进一步引导你创造出在传统的应用程序设计中不常见的资源:一个流程或者流程步骤、一次销售、一次谈判、一份报价请求——这都是应该被标识的事物的示例。同样,这也会导致创建比非 RESTful 设计更多的持久化实体。

下面是一些你可能想到的 URI 的例子:

复制代码
http://example.com/customers/1234
http://example.com/orders/2007/10/776654
http://example.com/products/4554
http://example.com/processes/salary-increase-234

正如我选择了创建便于阅读的 URI——这是个有用的观点,尽管不是 RESTful 设计所必须的——应该能十分容易地推测出 URI 的含义:它们明显地标识着单一“数据项”。但是再往下看:

复制代码
http://example.com/orders/2007/11
http://example.com/products?color=green

首先,这两个 URI 看起来与之前的稍有不同——毕竟,它们不是对一件事物的标识,而是对一类事物集合的标识(假定第一个 URI 标识了所有在 2007 年 11 月份提交的定单,第二个则是绿颜色产品的集合)。但是这些集合自身也是事物(资源),也应该被标识。

注意,使用唯一、全局统一的命名规则的好处,既适用于浏览器中的 Web 应用,也适用于机对机(machine-to-machine,m2m)通信。

来对第一个原则做下总结:使用 URI 标识所有值得标识的事物,特别是应用中提供的所有“高级”资源,无论这些资源代表单一数据项、数据项集合、虚拟亦或实际的对象还是计算结果等。

将所有事物链接在一起

接下来要讨论的原则有一个有点令人害怕的正式描述:“超媒体被当作应用状态引擎(Hypermedia as the engine of application state)”,有时简写为 HATEOAS。(严格地说,这不是我说的。)这个描述的核心是超媒体概念,换句话说:是链接的思想。链接是我们在 HTML 中常见的概念,但是它的用处绝不局限于此(用于人们网络浏览)。考虑一下下面这个虚构的 XML 片段:

复制代码
<order self="http://example.com/customers/1234">
<amount>23</amount>
<product ref="http://example.com/products/4554">
<customer ref="http://example.com/customers/1234">
</customer> </product></order>

如果你观察文档中 product 和 customer 的链接,就可以很容易地想象到,应用程序(已经检索过文档)如何“跟随”链接检索更多的信息。当然,如果使用一个遵守专用命名规范的简单“id”属性作为链接,也是可行的——但是仅限于应用环境之内。使用 URI 表示链接的优雅之处在于,链接可以指向由不同应用、不同服务器甚至位于另一个大陆上的不同公司提供的资源——因为 URI 命名规范是全球标准,构成 Web 的所有资源都可以互联互通。

超媒体原则还有一个更重要的方面——应用“状态”。简而言之,实际上服务器端(如果你愿意,也可以叫服务提供者)为客户端(服务消费者)提供一组链接,使客户端能通过链接将应用从一个状态改变为另一个状态。稍后我们会在另一篇文章中探究这个方面的影响;目前,只需要记住:链接是构成动态应用的非常有效的方式。

对此原则总结如下:任何可能的情况下,使用链接指引可以被标识的事物(资源)。也正是超链接造就了现在的 Web。

使用标准方法

在前两个原则的讨论中暗含着一个假设:接收 URI 的应用程序可以通过 URI 明确地一些有意义的事情。如果你在公共汽车上看到一个 URI,你可以将它输入浏览器的地址栏中并回车——但是你的浏览器如何知道需要对这个 URI 做些什么呢?

它知道如何去处理 URI 的原因在于所有的资源都支持同样的接口,一套同样的方法(只要你乐意,也可以称为操作)集合。在 HTTP 中这被叫做动词(verb),除了两个大家熟知的(GET 和 POST)之外,标准方法集合中还包含 PUT、DELETE、HEAD 和 OPTIONS。这些方法的含义连同行为许诺都一起定义在 HTTP 规范之中。如果你是一名 OO 开发人员,就可以想象到 RESTful HTTP 方案中的所有资源都继承自类似于这样的一个类(采用类 Java、C#的伪语法描述,请注意关键的方法):

复制代码
class Resource {
Resource(URI u);
Response get();
Response post(Request r);
Response put(Request r);
Response delete();
}

由于所有资源使用了同样的接口,你可以依此使用 GET 方法检索一个表述(representation)——也就是对资源的描述。因为规范中定义了 GET 的语义,所以可以肯定当你调用它的时候不需要对后果负责——这就是为什么可以“安全”地调用此方法。GET 方法支持非常高效、成熟的缓存,所以在很多情况下,你甚至不需要向服务器发送请求。还可以肯定的是,GET 方法具有幂等性[译注:指多个相同请求返回相同的结果]——如果你发送了一个 GET 请求没有得到结果,你可能不知道原因是请求未能到达目的地,还是响应在反馈的途中丢失了。幂等性保证了你可以简单地再发送一次请求解决问题。幂等性同样适用于 PUT(基本的含义是“更新资源数据,如果资源不存在的话,则根据此 URI 创建一个新的资源”)和 DELETE(你完全可以一遍又一遍地操作它,直到得出结论——删除不存在的东西没有任何问题)方法。POST 方法,通常表示“创建一个新资源”,也能被用于调用任意过程,因而它既不安全也不具有幂等性。

如果你采用 RESTful 的方式暴露应用功能(如果你乐意,也可以称为服务功能),那这条原则和它的约束同样也适用于你。如果你已经习惯于另外的设计方式,则很难去接受这条原则——毕竟,你很可能认为你的应用包含了超出这些操作表达范围的逻辑。请允许我花费一些时间来让你相信不存在这样的情况。

来看下面这个简单的采购方案例子:

可以看到,例子中定义了两个服务程序(没有包含任何实现细节)。这些服务程序的接口都是为了完成任务(正是我们讨论的 OrderManagement 和 CustomerManagement 服务)而定制的。如果客户端程序试图使用这些服务,那它必须针对这些特定接口进行编码——不可能在这些接口定义之前,使用客户程序去有目的地和接口协作。这些接口定义了服务程序的应用协议(application protocol)。

在 RESTful HTTP 方式中,你将通过组成HTTP 应用协议的通用接口访问服务程序。你可能会想出像这样的方式:

可以看到,服务程序中的特定操作被映射成为标准的 HTTP 方法——为了消除歧义,我创建了一组全新的资源。“这是骗人的把戏”,我听见你叫嚷着。不,这不是欺骗。标识一个顾客的 URI 上的 GET 方法正好相当于 getCustomerDetails 操作。有人用三角形形象化地说明了这一点:

把三个顶点想象为你可以调节的按钮。可以看到在第一种方法中,你拥有许多操作,许多种类的数据以及固定数量的“实例”(本质上和你拥有的服务程序数量一致)。在第二种方法中,你拥有固定数量的操作,许多种类的数据和许多调用固定方法的对象。它的意义在于,证明了通过这两种方式,你基本上可以表示任何你喜欢的事情。

为什么使用标准方法如此重要?从根本上说,它使你的应用成为 Web 的一部分——应用程序为 Web 变成 Internet 上最成功的应用所做的贡献,与它添加到 Web 中的资源数量成比例。采用 RESTful 方式,一个应用可能会向 Web 中添加数以百万计的客户 URI;如果采用 CORBA 技术并维持应用的原有设计方式,那它的贡献大抵只是一个“端点(endpoint)”——就好比一个非常小的门,仅仅允许有钥匙的人进入其中的资源域。

统一接口也使得所有理解 HTTP 应用协议的组件能与你的应用交互。通用客户程序(generic client)就是从中受益的组件的例子,例如 curl、wget、代理、缓存、HTTP 服务器、网关还有 Google、Yahoo!、MSN 等等。

总结如下:为使客户端程序能与你的资源相互协作,资源应该正确地实现默认的应用协议(HTTP),也就是使用标准的 GET、PUT、POST 和 DELETE 方法。

资源多重表述

到目前为止我们一直忽略了一个稍微复杂的问题:客户程序如何知道该怎样处理检索到的数据,比如作为 GET 或者 POST 请求的结果?原因是,HTTP 采取的方式是允许数据处理和操作调用之间关系分离的。换句话说,如果客户程序知道如何处理一种特定的数据格式,那就可以与所有提供这种表述格式的资源交互。让我们再用一个例子来阐明这个观点。利用 HTTP 内容协商(content negotiation),客户程序可以请求一种特定格式的表述:

复制代码
GET /customers/1234 HTTP/1.1
Host: example.com
Accept: application/vnd.mycompany.customer+xml

请求的结果可能是一些由公司专有的 XML 格式表述的客户信息。假设客户程序发送另外一个不同的请求,就如下面这样:

复制代码
GET /customers/1234 HTTP/1.1
Host: example.com
Accept: text/x-vcard

结果则可能是 VCard 格式的客户地址。(在这里我没有展示响应的内容,在其 HTTP Content-type 头中应该包含着关于数据类型的元数据。)这说明为什么理想的情况下,资源表述应该采用标准格式——如果客户程序对 HTTP 应用协议和一组数据格式都有所“了解”,那么它就可以用一种有意义的方式与世界上任意一个 RESTful HTTP 应用交互。不幸的是,我们不可能拿到所有东西的标准格式,但是,或许我们可以想到在公司或者一些合作伙伴中使用标准格式来营造一个小环境。当然以上情况不仅适用于从服务器端到客户端的数据,反之既然——倘若从客户端传来的数据符合应用协议,那么服务器端就可以使用特定的格式处理数据,而不去关心客户端的类型。

在实践中,资源多重表述还有着其它重要的好处:如果你为你的资源提供 HTML 和 XML 两种表述方式,那这些资源不仅可以被你的应用所用,还可以被任意标准 Web 浏览器所用——也就是说,你的应用信息可以被所有会使用 Web 的人获取到。

资源多重表述还有另外一种使用方式:你可以将应用的 Web UI 纳入到 Web API 中——毕竟,API 的设计通常是由 UI 可以提供的功能驱动的,而 UI 也是通过 API 执行动作的。将这两个任务合二为一带来了令人惊讶的好处,这使得使用者和调用程序都能得到更好的 Web 接口。

总结:针对不同的需求提供资源多重表述。

无状态通信

无状态通信是我要讲到的最后一个原则。首先,需要着重强调的是,虽然 REST 包含无状态性(statelessness)的观念,但这并不是说暴露功能的应用不能有状态——
事实上,在大部分情况下这会导致整个做法没有任何用处。REST 要求状态要么被放入资源状态中,要么保存在客户端上。或者换句话说,服务器端不能保持除了单次请求之外的,任何与其通信的客户端的通信状态。这样做的最直接的理由就是可伸缩性—— 如果服务器需要保持客户端状态,那么大量的客户端交互会严重影响服务器的内存可用空间(footprint)。(注意,要做到无状态通信往往需要需要一些重新设计——不能简单地将一些 session 状态绑缚在 URI 上,然后就宣称这个应用是 RESTful。)

但除此以外,其它方面可能显得更为重要:无状态约束使服务器的变化对客户端是不可见的,因为在两次连续的请求中,客户端并不依赖于同一台服务器。一个客户端从某台服务器上收到一份包含链接的文档,当它要做一些处理时,这台服务器宕掉了,可能是硬盘坏掉而被拿去修理,可能是软件需要升级重启——如果这个客户端访问了从这台服务器接收的链接,它不会察觉到后台的服务器已经改变了。

理论上的 REST

我承认:以上我所说的 REST 不是真正的 REST,而且我可能有点过多地热衷于简单化。但因为我想有一个与众不同的开场,所以没有在一开始就介绍其正式的定义和背景。现在就让我们稍微简要地介绍一下这方面的内容。

首先,先前我并没有明确地区分 HTTP、RESTful HTTP 和 REST。要理解这些不同方面之间的关系,我们要先来看看 REST 的历史。

Roy T. Fielding 在他的博士学位论文(实际上你应该访问这个链接——至少对于一篇学术论文来说,它是相当易读的。此论文已被翻译成中文)中定义了术语REST。Roy 曾是许多基本Web 协议的主要设计者,其中包括HTTP 和URIs,并且他在论文中对这些协议提出了很多想法。(这篇论文被誉为“REST 圣经”,这是恰当的——毕竟,是作者发明了这个术语,所以在定义上,他写的任何内容都被认为是权威的。)在论文中,Roy 首先定义一种方法论来谈论架构风格——高级、抽象的模式,来表达架构方法背后的核心理念。每一个架构风格由一系列的约束(constraints)定义形成。架构风格的例子包括“没有风格”(根本没有任何约束)、管道和过滤器(pipe and filter)、客户端/ 服务器、分布式对象以及——你猜到它了——REST。

如果对你来说这些听起来都太抽象了,那就对了——REST 在本质上是一个可以被许多不同技术实现的高层次的风格,而且可以被实例化——通过为它的抽象特性赋上不同的值。比如,REST 中包含资源和统一接口的概念——也就是说,所有资源都应该对这些相同的方法作出反应。但是REST 并没有说明是哪些方法,或者有多少方法。

REST 风格的一个“化身”便是 HTTP(以及一套相关的一套标准,比如 URI),或者稍微抽象一些:Web 架构自身。接着上面的例子,HTTP 使用 HTTP 动词作为 REST 统一接口的“实例”。由于 Fielding 是在 Web 已经(或者至少是大部分)“完善”了之后才定义的 REST 风格,有人可能会争论两者是不是 100% 的匹配。但是无论如何,整体上来说 Web、HTTP 和 URI 仅仅是 REST 风格的一个主要实现。不过,由于 Roy Fielding 即是 REST 论文的作者,又对 Web 架构设计有过深远的影响,两者相似也在情理之中。

最后,我在前面一次又一次地使用着术语“RESTful HTTP”,原因很简单:许多使用 HTTP 的应用因为一些理由并没有遵循 REST 原则,有人会说使用 HTTP 而不遵循 REST 原则就等同于滥用 HTTP。当然这听起来有点狂热——事实上违反 REST 约束的原因通常是,仅仅因为每个约束带来的设计权衡可能不适合于一些特殊情况。但通常,违背 REST 约束的原因可归咎于对其好处认知的缺乏。来看一个明显的反面案例:使用 HTTP GET 调用类似于删除对象的操作,这违反了 REST 的安全约束和一般性常识(客户程序不应为此负责,服务器端开发人员大概不是有意而为之)。但在随后的文章中,我会提及更多这样或那样的对 HTTP 的滥用。

总结

本文试图对 REST(Web 架构)背后的概念提供快速的介绍。RESTful HTTP 暴露功能的方式与 RPC、分布式对象以及 Web Services 是不相同的;要真正理解这些不同是需要一些心态的转变。不管你构建的应用是仅仅想暴露 Web UI 还是想把 API 变成 Web 的一份子,了解下 REST 的原则还是有好处的。

Stefan Tilkov是 InfoQ SOA 社区的首席编辑,并且是位于德国和瑞士的 innoQ公司的共同创始人、首席顾问和 REST 狂热分子首领。

查看英文原文 A Brief Introduction to REST


译者简介:苑永凯,软件设计师,毕业于山东大学。主要关注领域为 Java EE 企业应用、Java EE 中间件技术以及敏捷开发方法实践,微有心得。他的 Blog 为 http://blog.csdn.net/ai92 ,您也可以通过 yuanyk[AT]gmail.com 与他联系。参与 InfoQ 中文站内容建设,请邮件至 china-editorial[at]infoq.com

2007 年 12 月 25 日 22:10197085

评论 3 条评论

发布
用户头像
infoq的水准和csdn根本不在一个档次,看来有点含量的人都跑到这里了
2020 年 06 月 20 日 17:20
回复
用户头像
阅读数说明了一切,没有评论是因为文章水平太高了,有几个人有评论的资格?这又不是csdn上面那种水货文章,漏洞百出,看着评论挺多,其实是骂声一片
2020 年 06 月 20 日 17:18
回复
用户头像
这些文章毫无评论,感觉infoq一潭死水。
2020 年 06 月 08 日 17:19
回复
没有更多了
  • API 风格(上):如何设计 RESTful API?

    今天,我们一起来看下如何设计应用的API风格,我会介绍两种常用API风格中的一种,RESTful API。

    2021 年 6 月 22 日

  • 观点:REST 在 SOA 中适合哪个位置?

    最近,Jean-Jacques Dubray就REST在SOA中适合什么位置发表了自己的看法,他认为,并非所有的REST特性都适合SOA。

  • 是时候将 WADL 加入到 JAX-RS 中了吗?

    在JavaOne2012上有一场关于Java EE未来的专题讨论,参会者很想知道WADL是否会成为JAX-RS标准的一部分。尽管讨论小组尚未同意,但是观众更倾向于支持WADL而不是反对它。这是不是一件好事儿呢?对于成功的REST来说,WADL是否会继续被视为非必需的呢?

  • SOA 吸纳 WOA?

    Dion Hinchcliffe讲述了为何SOA与WOA是互补而不是竞争的关系。根据Dion的说法,采用一种基于WOA的方法,不仅给开发者降低了门槛,而且较传统SOA方法更具优势。Dion认为WOA并不是REST的同义词,反WOA的争论主要归咎于SOA供应商和专家们“保护自己的地盘”。

  • RESTful 世界里的 Cool URI

    假想一下,如果要以最小的集成代价实现一个分布在全世界范围的信息空间,用它来共享机器可识别的数据,会怎么样?这是关于REST的吗?不是的。根据 SWEO的说法,这跟语义网有关。那些Cool URI有助于实现这种方式。所以,去看看RESTful SOA URI是不是也很“酷”可能是值得的。

  • HTTP 有哪些特点?

    只有全方位、多角度了解HTTP,才能实现“扬长避短”,更好地利用HTTP。

    2019 年 6 月 26 日

  • 我们应该重新定义 REST 吗?

    为什么我们应该淘汰“REST API”这个术语

  • 定制媒体类型的扩增符合 RESTful 吗?

    Subbu Allamaraju在博文中重温了REST社区中的久辩不衰的话题——标准媒体类型和定制媒体类型的比较——并试图寻找使用他们的最佳实践。

  • 解答有关 REST 的十点疑惑

    在了解过REST之后,你肯定很想知道这个概念在介绍性的、“Hello, World”级场景以外能派上多大用场。本文,Stefan Tilkov解答了人们——尤其是那些深谙基于SOAP/WSDL的Web服务架构手法的人——开始研究REST时容易产生的关于REST的十点疑惑。

  • 什么时候 POSTing 状态合适?

    在这篇文章中,Tim Bray,审视了Sun Cloud API的第一版公众草稿所收到的反馈。对于其中一些反馈他在文中作出了回应,对于如何对交互进行建模,例如以RESTful的方式创建一个VM集群,进行了探索。

  • WebSockets 与 REST 之争?

    随着WebSockets目前成为W3C的推荐候选,以及一个新JSR将会在JCP中启动,很多人不禁要问WebSockets是如何在REST协议下运作,那将是会怎样的情景?两者彼此是否兼容,或许正如有些人相信的那样,WebSockets是否会将公众的注意力从REST转移过去,从而引领一种新的Web交互风格?甚至已经有人认为,WebSockets将会“破坏web”。

  • 工整与自由的风格之争:SOAP 和 REST

    它们来自两个不同的时代,却同时活跃于当今的互联网,并担当着重量级的角色,影响了一批新技术的诞生。

    2019 年 9 月 18 日

  • 支持 REST 设计的 WCF Web 编程模型

    在MIX07大会上Don Box和Steve Maine的“浏览可编程Web”联合演讲中,他们介绍了即将随Visual Studio Orcas一同发布的WCF Web编程模型。在统一的WCF编程模型下,Web编程模型从而可以支持Web服务REST化设计的功能。

  • HTTP 世界全览(下):与 HTTP 相关的各种协议

    学习HTTP的相关协议,可以让你对HTTP了解得更为全面,为后续的学习扫清障碍。

    2019 年 6 月 5 日

  • 打住!SOA、SOA 2.0、ROA 和 WOA,缩略词也太过头了吧?

    当SOA 2.0风头已过,REST vs SOA vs Web服务的争论也不像之前那么激烈的时候,业界又有一些人开始宣扬面向Web的架构(WOA)。可这和现有的东西有任何区别吗(比如REST)?如果有的话,它是什么,它又是怎么样帮助开发者和部署者的呢?来自Burton Group的Anne Thomas Manes认为这一术语有些过头了,也并没有为现在的争论带来任何的价值。

  • 错误处理(上):如何设计一套科学的错误码?

    今天,我会给你详细介绍下,如何设计一套规范的、科学的错误码。

    2021 年 7 月 6 日

  • HTTP 服务

    2019 年 4 月 12 日

  • Restful Objects 简介

    Restful Objects是关于领域对象模型的超媒体API的公共规范。该规范的1.0.0 版本刚刚发布,并且目前已经出现了两个实现了该规范的开源框架——一个基于Java平台,另一个基于.NET平台。

  • REST 反模式

    在本文中,Stefan Tilkov讲解了一些经常出现在自称“符合REST式设计”的应用中的反模式(比如:全部采用GET或POST,忽视缓存及响应代码,误用cookies,忘记超媒体与MIME类型,以及破坏自描述性等),并给出了避免这些反模式的对策。

发现更多内容

ARTS-WEEK5

一周思进

ARTS 打卡计划

系统架构感想

朱月俊

自由职业的前半年,我是如何度过的?

王磊

Java 程序员 程序员人生 程序员成长

区块链冷链食品追溯系统

CECBC区块链专委会

区块链技术 上链 溯源 浙冷链

谈反应式编程在服务端中的应用,数据库操作优化,提速 Upsert

newbe36524

C# MySQL 数据库 mongodb Reactive

GO语言泛型编程实践

老胡爱分享

go 泛型

Linux系统查询端口命令

打鱼小王子

一个典型的大型互联网应用系统使用了哪些技术方案和手段,主要解决什么问题?

朱月俊

python中对字典与列表组合进行排序

开心太平洋

Python List 排序

重学 Java 设计模式:实战备忘录模式「模拟互联网系统上线过程中,配置文件回滚场景」

小傅哥

Java 设计模式 小傅哥 重构 备忘录模式

原来使用Postman如此简单,API测试之Postman使用全指南

软测小生

接口 Postman 接口测试 API API测试

快来解锁Pepper机器人新技能,够酷Pepper就跟你回家!

阿甜

编程 开发者 App 开发 机器人

消息队列(三)如何保证消息不被重复消费?

奈何花开

Java MQ 消息队列

第四周作业

南宫煌

极客大学架构师训练营

安畅迁移平台的云原生之路

雪雷

Kubernetes DevOps 云原生 CI/CD 迁移

一文带你学会 Blob(含 7 个使用场景)

pingan8787

Java 前端 Web Blob

学习总结 - 第 4 周

饶军

央行数字货币:第三方支付产业新变量

CECBC区块链专委会

数字货币 DCEP 区块链技术

《架构师训练营》第四周命题作业

MyBatis标签trim,你不会以为我是去空格的吧?

Java小咖秀

Java mybatis Java 面试

互联网系统常见问题以及解决方案

而立

极客大学架构师训练营

架构师训练营 - 学习笔记 - 第四周

心在飞

极客大学架构师训练营

《架构师训练营》第四周总结

Prometheus 存储层的演进

伴鱼技术团队

性能优化 系统架构 Prometheus 存储 时序数据库

架构师训练营 - 第四周命题作业

牛牛

极客大学架构师训练营 作业

架构师训练营第四周学习总结

CATTY

架构师训练营作业 -Week4

wyzwlj

极客大学架构师训练营

小师妹学JVM之:JIT中的PrintAssembly

程序那些事

JVM 小师妹 性能调优 JIT 汇编

围绕 Office 365 的那些 CLI

手艺人杨柳

Office 365

计算机操作系统基础(六)---作业管理之进程调度

书旅

Java php 多线程 操作系统 进程

CECBC带你一图看懂区块链

CECBC区块链专委会

CECBC 区块链技术 去中心化

技术为帆,纵横四海- Lazada技术东南亚探索和成长之旅

技术为帆,纵横四海- Lazada技术东南亚探索和成长之旅

深入浅出REST-InfoQ