近年来,微服务是备受关注的概念。有人主张微服务是一种革命性的技术创新,也有人认为微服务并没有什么新鲜的,只不过是对SOA(面向服务架构)的优化重塑。我不想争论这些,因为无论是支持还是反对,都无法阻挡微服务在敏捷开发和复杂的企业级应用开发中的优势。
通过微服务,我们在应用开发和部署方面取得了显著的进步。将应用开发或者重构成微服务以分离服务,通过 API 以明确的方式来相互“对话”。例如,每个微服务都是自包含(self-contained),各自维护自己的数据存储(这非常有意义),可以独立更新其服务。
使用基于微服务的方式使得应用程序开发变得更快更容易管理,它只需要较少的人力就能实现更多的功能,可以更快更容易地部署。把应用程序设计成一套微服务,更加容易在多台具有负载均衡的服务器上运行,使其能够轻松应对需求高峰、由于时间推移而平稳增长的需求和由于硬件或者软件问题导致的宕机事故。
微服务的最大进步在于改变了我们的工作方式。敏捷软件开发技术、应用迁移云端、DevOps 文化、持续集成与持续部署(CI/CD)和容器应用都使用了微服务来革新应用开发与交付。
我们先来看微服务的发展史。
从最原始的单体应用开始:
假设你要开发一个类似滴滴的打车应用。在单体应用的架构下,你可能这样设计
在架构核心,设计了乘客管理,司机管理。行程管理,支付,消息通知等核心模块。
围绕核心模块,我们再设计各种接口适配器,如同数据库对接,同移动端的api接口,web页面,对其他外部组织对接接口等等。
然后我们就可以打成一个包,进行部署。
在项目的早期,这没有太大问题。我们还可以通过负载均衡器在做多个实例的负载均衡。
然而,成功的应用有一个趋势,随着时间推移而变得越来越臃肿。
首先,在业务快速发展的阶段,开发人员每天有大量业务变更需要开发,在996已经不能保证完成工作的情况下,要开发人员保证应用架构的干净是强人所难。
在业务代码变得足够复杂之后,团队中很快就没有人能完全理清所有业务,只能负责自己的一小块。
最终,正确修复bug和开发新功能都变得越来越困难,只能不断的打补丁。最终单体应用会变成一个无人可以理解的超大型乱码。
最后的结果是:你不要尝试去重构,就让他这样跑着吧。
同时伴随而来的是,单体应用的部署时间越来越长。一个单体应用对服务器的要求也越来越高。
单体应用的另一个问题是可靠性。因为所有模块都运行在同一进程中。任何模块的一个 bug,比如内存泄漏,可能会拖垮整个进程。此外,由于应用程序的所有实例都是相同的,该错误将影响到整个应用的可用性。
总结:
所以,一个成功的应用最终会变成只有少数人能维护的巨大单体。使用着陈旧的技术,很难找到合格的新开发人员。
部署困难,重启耗时极长,可靠性也得不到保障。无法重构,或者重构的代价极大,必须同时重构整个单体。
对单体应用问题的探索,诞生了微服务架构。我们将核心业务的几大业务模块,拆分为独立的迷你应用。他们都有自己独立的核心逻辑和对外相互对接的接口。
每个迷你应用都对外暴露REST服务API。各后端服务可以相互调用。一些 REST API 也暴露给移动端应用使用。然而,前端应用不能直接访问后端服务。前端对后端的服务调用要通过API网关。API 网关统一负责负载均衡、缓存、访问控制、API 计量和监控。
微服务架构模式明显影响到了应用程序与数据库之间的关系,在单体应用中,所有业务共享一个数据库。然而现在,微服务建议其每一个服务都有自己的数据库。这样做的缺点是可能导致部分数据冗余。但是,从微服务架构的角度去理解,业务A的数据库本就不该存业务B的数据,所有的关于业务B的数据,从应该由业务B的微服务对外提供。
另外,在这种架构下,我们可以为业务选择合适的数据库。比如对某些业务,我们需要选择支持高效地理位置查询的数据库。
微服务的优点:
1,拆分了复杂的单体应用。降低了代码的服务度,规定了微服务的业务边界。使业务代码能够容易的开发和修改
2,微服务架构使每个业务模块有一个团队专门负责。技术团队可以根据业务的需要做出更合适的技术选型。同时因为单个微服务代码体量的减小,使代码重构成为可能。
3,部署更快捷和方便。
优点司空见惯,我们其实更应该关注下微服务的缺点
微服务的缺点:
1,整体复杂度更高。微服务根本上说是一个分布式系统。开发者需要选择和实现基于消息或者 RPC 的进程间通信机制。虽然这个有很多框架可供选择,并不需要从头实现。但是整体上的代码复杂度是提高了。
2,事务。如上面所说,微服务架构上每个业务有自己的数据库。以前在单体应用中很好解决的事务问题,现在变得很困难。在基于微服务的应用程序中,需要更新不同服务所用的数据库。通常不会选择分布式事务,不仅仅是因为 CAP 定理。他们根本不支持如今高度可扩展的 NoSQL 数据库和消息代理。最后不得不使用基于最终一致性的方法,这对于开发人员来说更具挑战性。
3,测试微服务应用程序也很复杂。例如,使用 Spring Boot,我只需要编写一个测试类来启动一个单体 web 应用程序并测试其 REST API。相比之下,一个类似的测试类对于微服务来说需要启动该服务及其所依赖的所有服务,或者至少要做服务mock,虽然这不是一件高深的事情,但不要低估了这多出来的工作量和复杂度。