当你开始使用微服务时会发现,很多基于微服务的架构主要有两个优势:首先它具有较小的粒度,其次它能够在解决问题的方法上给予你更多的选择。那么其他的分解技术是否也有相应的好处呢? 1.4.1 共享库 基本上所有的语言都支持将整个代码库分解成为多个库,这是一种非常标准的分解技术。这些库可以由第三方或者自己的组织提供。 不同的团队和服务可以通过库的形式共享功能。比如说,我可能会创建一系列有用的集合操作类工具,或者一个可以重用的统计库。 团队可以围绕库来进行组织,而库本身可以被重用。但是这种方式存在一些缺点。 首先,你无法选择异构的技术。一般来讲,这些库只能在同一种语言中,或者至少在同一个平台上使用。其次,你会失去独立地对系统某一部分进行扩展的能力。再次,除非你使用的是动态链接库,否则每次当库有更新的时候,都需要重新部署整个进程,以至于无法独立地部署变更。而最糟糕的影响可能是你会缺乏一个比较明显的接缝来建立架构的安全性保护措施,从而无法确保系统的弹性。 共享库确实有其相应的应用场景。有时候你会编写代码来执行一些公共任务,这些代码并不属于任何一个业务领域,并且可以在整个组织中进行重用,很显然这些代码就应该成为可重用的库。但是你还是需要很小心,如果使用共享代码来做服务之间的通信的话,那么它会成为一个耦合点。第4 章会再讨论该问题。 服务之间可以并且应该大量使用第三方库来重用公共代码,但有时候效果不太好。 1.4.2 模块 除了简单的库之外,有些语言提供了自己的模块分解技术。它们允许对模块进行生命周期管理,这样就可以把模块部署到运行的进程中,并且可以在不停止整个进程的前提下对某个模块进行修改。 作为一个与具体技术相关的模块分解技术,OSGI(Open Source Gateway Initiative,开放服务网关协议)值得一提。Java 本身并没有真正的模块概念,至少要到Java 9 才能看到这个特性加入到语言中。OSGI 最初是Eclipse Java IDE 使用的一种安装插件的方式,而现在很多项目都在使用库来对Java 程序进行模块化。 OSGI 的问题在于它非常强调诸如模块生命周期管理之类的事情,但语言本身对此并没有足够的支持,这就迫使模块的作者做更多的工作来对模块进行适当的隔离。在一个进程内也很容易使模块之间过度耦合,从而引起各种各样的问题。我个人对OSGI 的经验是,它带来的复杂度要远远大于它带来的好处,即使对于很优秀的团队来说也是不可避免。业界的其他同事也多有类似的看法。 Erlang 采用了不同的方式,模块的概念内嵌在Erlang 语言的运行时中,因此这种模块化分解的方式是很成熟的。你可以对Erlang 的模块进行停止、重启或者升级等操作,且不会引起任何问题。Erlang 甚至支持同时运行同一个模块的多个版本,从而可以支持更加优雅的模块升级。 Erlang 的模块化能力确实非常惊人,但是即使我们非常幸运地能够使用具有这种能力的平台,还是会存在与使用共享库类似的缺点,即它会大大限制我们采用新技术和独立对服务进行扩展的能力,并且有可能会导致使用过度耦合的集成技术,同时也会缺乏相应的接缝来进行架构的安全性保护。 还有一个值得注意的事情是:尽管在一个单块进程中创建隔离性很好的模块是可能的,但是我很少见到真正有人能做到。这些模块会迅速和其他代码耦合在一起,从而失去意义。而进程边界的存在则能够有效地避免这种情况的发生(至少很难犯错误)。尽管我不认为这是使用进程隔离的主要原因,但是事实上确实很少有人能够在同一个进程内部做到很好的模块隔离。 除了把系统划分为不同的服务之外,你可能也想要在一个进程内部使用模块进行划分,但是仅仅使用模块划分不能解决所有的问题。如果你只使用Erlang,可能会花很长时间才能把Erlang 的模块化做好,但是我怀疑大部分人不会这么做。对于剩下的人来说,模块能够提供的好处与共享库比较类似。