microservice的anti-pattern

本人主要关注的是领域驱动设计(ddd),一直觉得微服务算是另一个分野,没有特别地去关注。直到在油管上看到《领域驱动设计》的作者Eric Evans的这视频。(视频中Evans讲解了通过微服务,终于实现了可靠的Bounded Context的边界。)
microservice的anti-pattern_第1张图片
才开始觉得这两个技术其实有一定关联。而在《building microservies》一书中作者也明确提出Bounded Context是一个很好的概念来帮助划分微服务。
microservice的anti-pattern_第2张图片
工作上其实其他的部门也有过微服务的项目,不过由于当时经验不足(此处略去270个字…)。再加上最近看到的微服务的失败案例,正好分享一下这个话题。

API hell (API无限连击)

这是一个叫Jimmy Bogard的工程师在某个视频里分享的概念。其中的案例是说他为大电脑生产商Bell公司(你懂的)做一个耗时超过1年的网购服务时遇到的问题。
Bell公司的这个项目,工程师可能打了鸡血,一下子搞了200个服务!当然人家Netflix据说是有500多个微服务,与之相比差的还很远。
microservice的anti-pattern_第3张图片
可能Bell公司是看了这张图给了他们启发。不过Jimmy Bogard对这个概念图的解释是这样的
microservice的anti-pattern_第4张图片
有某一个入口的服务gateway,它来调用内部的各种微服务,然后把结果合并返回。
而Bell公司的设计是相面这样的。
microservice的anti-pattern_第5张图片
服务A调用了服务B,结果服务B为了处理A的请求还要去调用服务C,服务C又要调用服务D,服务D可能又要去调服务A。虽然在例图里,大家都调用了4个服务,感觉没什么区别。问题是当A去调B时,A并没有意识到B要去掉其他的服务,A认为只是一个简单的请求,所以当A还要去调用另一个服务E,A根本不认为这是个很大的成本,结果可能就是在调E时,又去调了F,G两个服务。
microservice的anti-pattern_第6张图片
最极端的一个例子,一个请求如果要经过200个服务,每个子请求150ms,结果在理想的情况下,一个从画面过来的请求就要话30秒的时间。显然这是完全不能被接受的。再加上请求还有可能失败。假设一个请求成功率是99.9%,那经过两百次子请求的请求的成功率会是81%左右。也就是5次里有一次画面会报错。其结果就是Bell再发布这个网购服务时,没有能够跑起来。之后财大气粗的他们用了最好的硬件,尝试跑这个微服务时,第一个页面的响应据说用了超过30秒。
Bell公司的微服务项目建立在了如下的前提下。
- 网络是可靠的
- 网络的延迟是可以忽略的
- 带宽是正无限的
当然没有一个前提是成立的。(反之,在设计微服务时必须考虑微服务连不上和延迟问题,好痛苦orz)
另外Bell公司的开发模式也很有趣。将近百人的开发团队,分成各个小组,小组分担几个微服务,发自己的微服务时,把需要依赖的外部微服务全部以mock来实现,直到开发的后期进入集成阶段才发现了这个问题。
因为忽略了调用外部服务的成本,所以产生了数以千计的歌中API调用——API hell。同时想引出下一个点。

微服务没有自主权(autonomy)

微服务的自治权是一个很重要的概念。某种程度上它定义了什么才能被称为一个“服务“。
而Bell公司的项目上他们更重视另一个原则–single source of truth。目的是要避免数据一致性的破坏。
我个人觉得遵不遵循ssot是个比较有争议的话题。因为当你把一个服务的数据拷贝到另一个服务,无疑是增加了服务的复杂度,你必须考虑不同服务内的数据如何保持同步之类的。不过当你禁止服务间数据的拷贝,带来的问题便是各个服务之间会发生数据依赖性。
假设有一个Product服务,Order服务,Delivery服务。产品的信息只允许产品服务拥有。其他的服务要调用产品服务获取产品的信息。个人觉得当假设Order服务需要提供订单列表这个功能。每次显示订单列表都要调用Product服务。这显然称不上自主。
microservice的anti-pattern_第7张图片
如果你对的例子存有疑问,认为具体允不允许数据拷贝要根据实际的需求来决定。另外本人还经历过更悲惨的项目。居然把分层的概念于微服务混同。他们把分成了view service, logic service, data persistence service。
microservice的anti-pattern_第8张图片
当然从自主权方面来讲根本完全不满足。一个单独的view service是没有任何意义,只是一个二传手而已。。。
microservice的anti-pattern_第9张图片
这样的服务分割法,当然也继承了分层设计的优良传统–级联修改。假设Product需要增加一个字段,那我们必须修改3个服务,自主权何其的低啊。。。

不过你可能觉得如果我们不是那么傻,把层级都分成服务,就按业务分成各个服务(就是这节开头的分法),不是也会发生同样的级联修改的问题吗?
microservice的anti-pattern_第10张图片
当你的product的数据构造(比如加了一个字段)变化了,会影响到Order服务。
面对这种情况个人觉得应该先弄清需求。Product服务与Order服务所需要的product是同样的东西吗?关于product,Product与Order关注的点是否相同。这少很多情况下两者关注的东西是不同的。比如假设Product服务需要不仅需要商品的名称,型号,价格,还要关注发售日期,生产厂家等等信息。而Order服务则可能只需要商品的名称,型号,价格。
microservice的anti-pattern_第11张图片
这里借鉴了ddd的Bounded Context这个概念。在不同的Context中,我们有不同的描述产品(product)的模型。在Order服务中,product模型可能只是隶属于order集合(aggregate)的一个value object。
在这个前提下,只要是Order服务不关注的对product的更改,比如说新增了一个产品原料这类的信息,这些更改理论上都不会影响Order服务。
然后再把Order服务需要的product数据都通过某种方式同步给Order服务,结果Order也有product的数据的拷贝。具体数据同步的方式,根据需求的不同,实现也不同。这个例子里,我们甚至可以把product的信息当作orders表的一个字段,然后在Order生成,对orders插入数据时,一起将product的信息也保存。
microservice的anti-pattern_第12张图片
如此情况下,Order服务在实现”显示订单信息”功能时不用再询问Product服务了。
microservice的anti-pattern_第13张图片

总结

这次讲了microservice的两种反面教材。
一种是忽略了通讯的成本和风险。这就好像你是用云服务,然后指望云上的虚拟机用不宕机一样。人家云服务供应商已经明确说明了云上的资源可能是不稳定(把责任规避得一干二净),需要考虑到这个风险设计能够应对它的服务。(还信誓旦旦地出了个cloud design pattern供你参考)
另一种是微服务分割的不合理。ddd的理论在服务分割时会有帮助。
通过这两种方式可以让你的微服务项目迅速变成一个天坑,(希望大家慎用! ^ ^)
另外其实还想谈谈开发团队,组织方面的问题,等到下次再写吧。

你可能感兴趣的:(microservice的anti-pattern)