模块化和微服务以及多语言后端开发

2020-07-02 00:00:00 微服 架构 服务 公司 模块化

模块化、微服务和多语言开发并不是在一个层面上的概念,但这几个月来, @圆胖肿 老师总是在我回答下面过分夸大传销他的vert.x和模块化多语言解决方案,就连我一些出国留学的回答都不放过,我真是不胜其烦,又怕知乎上关注我的朋友被他的言论误导,我把我知道的“模块化”和微服务的东西分享给大家。并没有什么放之四海而皆准的设计方案,视自己的应用场景和公司团队实际情况来选用适合自己的技术。

本文正文中关于模块化和微服务的很多观点来自Sander Mak的这篇文章。我自己也结合我的实际经验做了些扩展。这位Sander Mak是Java 9 Modularity图书的作者之一。(Java 9 Modularity这本书的另一个作者Paul Bakker也在近的oracle code one上比较“模块化”和微服务。这也是圆胖肿让我去学习的材料之一。但我只看到他ppt的一页,他那个推也没什么人理,找不到更多资料了)

Modules vs. microserviceswww.oreilly.com

这篇文章的副标题是“Apply modular system design principles while avoiding the operational complexity of microservices.” 作者提出使用模块化的单体服务架构(monolith)和使用微服务(microservice)架构相比,前者可以减少运维复杂度,其实说白了,就是帮你省了运维的钱了。使用微服务对运维成本的增加体现在(包括但不限于):

1,部署的压力。比如本来可能整个公司每周部署单体服务一次。拆分成20个微服务后,每周总共要部署二十次。我们需要为每个服务都创建持续集成CI,部署脚本,监控等。现在很多小公司CI都没有,自动测试覆盖率很低,全靠部署后手动测试。你一周部署这么多次,质量就会没法控制,只会出更多的问题。

2,微服务治理。如果我们不能做到很好的服务发现(service discovery)和负载均衡等,可能你任何的一个重要的微服务做一次滚动升级,都要造成整个服务的降级。所以很多公司在刚刚换到微服务的几个月里,其实整个系统的可用性是降低了的。

3,分布式的追踪和日志。这个我近写了一篇文章讲分布式的追踪。虽然有现成的工具,但你要给所有微服务调用都搞上这么一套,还是挺麻烦的。

北南:从头用go写一个GraphQL服务(3)OpenTracing追踪zhuanlan.zhihu.com

微服务的切分其实并不是难点,很多人都会切分。但是把这些微服务有机的连在一起是个系统工程,需要公司大量的技术投入,又烧钱,又费事。当然,微服务还有其他的问题,这个我写本书都说不完。总而言之,微服务不是银弹,天下也没有免费的午餐。

那为啥这么多人跟你说微服务好,而且google,netflix,twitter这些公司把自己的服务构建在微服务架构上呢?因为这些公司可以接受多花钱多费事,从而解决下面的问题:

  1. 每个组都可以独立工作。每个后端服务都可以独立scale
  2. 每个服务都比较小而且专注于一小块业务逻辑。
  3. 每个服务在自己内部改动或者自己被替换的时候(比如说升级,重新部署等)都不会对公司全局造成影响。

这些优点对google这样的大型互联网公司是非常有意义的,大公司组多,服务也多,就怕互相牵扯,牵一发而动全身,谁都怕自己就改一点点,却造成不可预计的后果。大型互联网公司的单体服务架构到了后来基本一定会进入新功能加不进去,老功能出了问题没法解决的尴尬境地。互联网公司发展到一定程度,都会寻求微服务这样的解决方案,目前来看,微服务这剂苦药虽然不是那么好吃,但是它真能治病。

不过呢,微服务对于一个初创的企业或者小型公司是没有那么大意义的,比如说你公司后端就一个组,二三十号后台工程师,所有业务逻辑都紧凑的内聚到一起,所有的事情都可以在几个人里面说的清楚,你硬拆,只会增加成本。


再来说说多语言后端开发的问题,如同评论区中阿莱兄的说法,这现在感觉也是个趋势了,我们习惯在后端开发中使用不同的开发语言。前段时间我有一个回答在讲一个公司要不要使用不同的编程语言和技术栈来实现微服务。有时候一个公司或者一个服务很可能想使用不同的编程语言和技术栈。目前这种多语言协作的模式在微服务后端架构中有非常多成功的案例。但在单体架构中,多语言协作仅限于近亲语言的结合,比如说java+scala。或者说java的服务用python做个build脚本和CI管理工具。像亲缘关系比较远的,比如php或python直接结合c/c++的方式虽然也广泛存在,但遇到性能问题时,往往都会造成更多的调优困难。

如何看待哔哩哔哩用go语言重写所有的java后台工程?www.zhihu.com

我个人建议尽量不要增加一个后台服务里面的复杂度。有些东西一开始很美,但时间长了就容易产生问题。这个所谓的复杂度当然也包括在一个后台服务里使用多个语言,例如在go中使用cgo,也包括vert.x实现多个语言的verticle。圆胖肿更是建议使用vertx通过graal的多语言支持来把多个语言的实现直接捏在一起。我好几年前还是对vertx这个框架挺有热情的,也投入了不少时间。不过我觉得vert.x虽然提出了还不错的异步工作架构(说实话这种东西我也见的非常多了,akka以及众多akka衍生框架还没死呢,我觉得vert.x在实时数据处理上取代akka的部分应用是不错的),vert.x一定程度上解决了不同组件的解耦和异步调用问题,但我同时对这个框架一直有很多顾虑,见本文后。

一般来说,我不给初创企业或者规模还不大的互联网公司进行微服务架构的布道。比如说我个人正在做的GraphQL框架,其实也是基于组件化的单体服务思想,而不是上来咱们啥都是微服务。对于规模已经开始快速增长的互联网公司,应用微服务是可以逐步进行的,你可以把单体服务中的一小部分先抠出来,比如说你就可以先把用户服务先切出来做成一个独立的微服务。

我建议初创互联网公司尽量使用可以快速开发的单一语言实现自己的单体架构服务,具体选择什么语言看自己的团队情况,看自己产品对运行效率是否敏感。我觉得对于大多数公司,一开始主要关注的应该是开发效率和人员成本,尽量使用简单直接、历史包袱小的语言和框架。比如说团队里都没有人对scala有经验,就别挑战自己非要作死玩scala。而且就算你是使用单体架构服务,你也要模块化设计,几乎所有的现代框架都支持模块化,你不一定非得要java9和vertx才能搞出模块化。在公司草创时期,一个架构师的基本素养直接就体现在模块设计的好坏上。

微服务和模块化/组件化是不矛盾的,微服务本身就是一种烧钱费事的模块化的实现方式,而且在每个微服务中,你仍可以使用模块化的设计。比如说你可以写一个验证URL或者email地址是否合法的组件,它就可以给一个微服务的多处地方使用,甚至被多个微服务共享。但这样的组件好是相对功能稳定而且接口很固定的。这种跨服务的组件也不要太多,要不然以后升级维护也是个麻烦。


--------------------------------------

我个人对vert.x的三点顾虑:

1:在大型集群下,vert.x的eventbus/service proxy是否会成为整个系统的瓶颈?

2:当你一个vert.x instance下有多个verticle的时候,这些verticle使用的资源如何隔离?比如说其中一个verticle造成GC频率过高,其他verticle会受到怎么样的影响?如果我一个vert.x instance里只放一个verticle,那是否会加重eventbus成为瓶颈的可能?

3: 我对js和ruby在jvm上的执行效率和调优难度也有比较大的顾虑。我还没见过有人用js或者ruby在jvm上实现了任何成功的超高并发服务。如果你只用java和scala,那选择就太多了,比如说使用twitter的finatra框架我觉得就更为简洁和高效,scalability能力也更强。

这里有truffler ruby的性能对比,看着比ruby2.4还是快挺多的,感动么?不过这真心不是truffler有多快,是ruby太慢了。

Benchmarking a Go AI in Ruby: CRuby vs. Rubinius vs. JRuby vs. Truffle – a year laterpragtob.wordpress.com

相关文章