前段时间我大发雷霆,因为有人曲解了REST在微服务中的作用,而且没能正确使用它。有人认为无法基于HTTP进行异步交互,所以基于HTTP的单体系统无法被分解成微服务。大多数人认为HTTP就是REST,这是件让人感到悲哀的事情。在我们的行业里,这些人都是很有经验的,本该知道这两者其实是不一样的。如果你也分不清它们之间的区别,那么请先看看Rest Cookbook或者Roy博士的文章。
好吧,似乎有点跑题了,让我们回到之前说的问题上:我之所以大发雷霆,是希望把大家引导到正确的方向上,同时给大家提供一些建议。
REST和HTTP
首先,并不是说构建微服务一定要基于HTTP。关于这个,可以看看InfoQ上早前发表的一些相关文章,或者回顾过去7年我们在WildFly/EAP及其它项目上所做的事情。
HTTP不是唯一的选择,它有它的不足,不仅仅因为它是基于文本的协议,在性能上它也比不过更成熟的IIOP。这个问题在Narayana项目中就暴露出来了,就算是二进制版本的HTTP/2也不行。
并不是说HTTP在性能方面存在不足才劝你要抛弃它的,我们还有其它的选择。传统的消息中间件,如A-MQ,它支持很多种模式,包括代理和非代理模式,还支持AMQP和MQTT协议,让异构系统之间的交互变得更通畅。
当然,我并不是说一定不要使用HTTP构建微服务。但是当我们跟很多分布式系统打交道的时候,不得不考虑其它很多方面的问题,比如可靠性,性能,耦合度等等。不是非用HTTP不可,不过JMS也一样。我知道,基于HTTP构建的的服务容易测试,只需要一个浏览器。不过也可以考虑其它的工具,比如Arquillian。
异步调用
现在我们来谈谈异步HTTP。有些人说HTTP可以实现异步调用,这个有可能吗?当然有可能,下面我会给出一些参考。
首先,我们拿一些知名的项目来作为例子,比如Vert.X和Undertow,它们都基于HTTP实现异步消息交互。这两个都是很受欢迎的项目,很多人使用它们构建了很多大型应用。
其次,你可以不相信我所说的,但是你可以去翻看InfoQ上关于这个主题的文章。这样的文章有很多,甚至可以追溯到10年前。如果要我推荐关于这个主题的书,我会推荐我的一个老朋友也是前同事Jim Webber,或者另外一个老朋友也是现任同事Bill Burke的书。
在使用HTTP的时候,响应码对我们来说很重要。对于200,403,404这样的响应码我们已经很熟悉了,但HTTP还有一些东西使得实现异步交互变为可能,比如202。在HTTP协议规范里是这样描述202的:
请求已被接受,但是还没有处理完毕。请求最终有可能不会被处理,因为系统可能不允许处理该请求。对于这种情况,没办法通过异步操作来重新发送一个状态码。202响应不保证处理结果,它只是允许服务端接收请求,而不需要让客户端一直等待处理结果。它的响应里应该包括一些内容,比如当前请求的处理状态,或者让客户端知道去哪里可以得到处理状态,或者告诉客户端请求处理完毕的大致时间。
很显然,我加亮的地方指的就是“异步处理”。这个响应码肯定不是大家经常看到的,而且就算用到了,也因为被浏览器隐藏,所以仍然看不到。重点是,HTTP支持异步调用。作为开发者,我们也一定能利用这点。
如果你在考虑使用标准框架来处理异步HTTP,你大概会想到JAX-RS。网上有很多相关的资料,Bill Burke的那本书也不错。他甚至写了JAX-RS2.0,值得一看。Bill所在的标准制定小组在他们新版本协议规范里还特意增加了支持回调的异步客户端API。而且我们不要忘了Bill早在2009年作的关于早期协议版本的演示。
它是真的异步吗?
如果你读到这里,应该很清楚,基于HTTP的异步处理完全是有可能的。不过我还是想指出它的一些瑕疵,就像有些文章说的那样。
当人们说到异步交互,一般指的是以下两种类型。一种是请求被同步发送到服务端,服务端返回一个确认响应,告诉客户端请求最终会被处理并返回结果。这种方式可以提高并发性。另外一种是"触发然后忘记"模式,服务端不会返回任何确认给客户端。
所幸的是,当大多数人提到第二种方式时,其实他们心里想的是第一种,因为他们直接把确认响应给忽略掉了。这两者的区别其实很关键,在真实的异步系统中,不可能只通过时间的长短来判断一个端点到底是崩溃了还只是响应慢。这个在做系统架构决策的时候很重要。
权衡利弊
Fischer,Lynch和Patterson这三个人在1985年发表了一篇论文,获得Dijkstra最具影响力论文奖。他们在论文中证明了FLP理论,即无法通过时间长短来判断一个端点是否已崩溃。大多数人认为这在同步系统中是有可能的,但是在异步系统中是不可能的,就算整个系统中只有一个糟糕的处理器。
你也许会问为什么这个如此重要。很显然,如果你处在一个真实的异步交互场景中,需要知道哪些调用可以直接得到结果,而哪些不会。这不是在纸上谈兵,论文里已经说得很清楚了,所以在开发的时候要小心这方面的问题。
因此,所有关于事务的协议,包括Narayama里的那些,都应该是同步的。
还有个事情需要注意,有些人把Brewer的CAP理论同FLP混为一谈。尽管CAP和FLP都跟分布式系统的行为有关,但它们之间有一些明显的区别:
CAP认为在异步网络里无法实现同时满足以下三点的支持读写的存储系统。
- 可用性——每个请求最终都会收到响应
- 一致性——服务端为每个请求返回正确的响应
- 分区容错——允许消息丢失
从某种程度上说,CAP听起来有点像FLP,但其实不是。如果你真的感兴趣,可以看看Ken Birman的论文。
FLP允许网络里有节点崩溃,但不允许有消息丢失。
上面的信息已经超出了一个开发者本该知道的范围,但多知道一些关于分布式系统的陷阱总是有好处的。还有就是,我们一定要小心,有时候人们说到CAP或者FLP,并不代表他们真的理解背后的原理。而事实是,CAP经常被人误解和滥用。
有疑问加站长微信联系(非本文作者)