读后感之《从单体应用到微服务》04
以下文章选自《超神说》
目标:共同学习、共同进步、告别码农,成为受人敬仰的、有态度的程序猿。拒绝不知其所以然的复制粘贴、拒绝人云亦云。用最严谨的态度、最专业的方法、最可靠的知识来源,探究技术内幕,死磕到底!!!
内容简介
原书名字是《Monolith To Microservices》,是大神Sam Newman的新书,目前还没有中文版本。原本是想写一个简短的读后感的,但是写着写着,发现书中的内容真的是太经典了,浅尝辄止的描述完全不能体现本书的价值。于是就改成了用我自己的语言对书中每一章的内容进行了精炼。因此这个读后感也可以作为原书的精简版来看,只不过用的是我自己的语言总结的。也是由于这个原因,这篇文章越写字数越多,最后接近三万字,花费的时间也很多。为了便于阅读,分成4部分来发。
注:本文中的图片截自原书
第五章、不断增加的痛苦
微服务架构所带来的挑战随着服务规模的增加而不断增加。并且往往没有终点,一直都会处于发现问题解决问题的循环当中。文章中给出下图展示了服务规模和主要面临的挑战:
从图中可以看出,当服务数量在2-10个时,可能只有少量的团队来维护,主要面临的挑战是重大变化和处理报表;当服务个数上升到10-50个,可能仍然是少量的团队维护,面临的挑战变成了服务所有权的划分、开发人员的经验和需要运行太多的服务;服务个数继续上升到50个以上后,团队个数更多,开发人员也更多,这时面临的挑战是全局和局部的优化(如何平衡独立性和统一性)、孤儿服务(没有人负责的服务)。同时,随着服务规模的变大,对系统鲁棒性、弹性、监控和排错的需求将变得越来越急迫。
问题:所有权
代码的所有权分为三种:
强所有权:所有的服务都有拥有者。外部的人如果想要修改服务的代码,必须将修改提交给拥有者,由拥有者判断是否采用。
弱所有权:服务有拥有者,但任何人都可以对服务进行修改。修改前可能会告知拥有者。
集体所有权:服务没有拥有者,任何人都能随意修改任何代码。
许多团队开始的时候规模比较小(20人左右),使用集体所有权是有意义的。当人数变多,或者地理位置分散,沟通成本上升,进而无法在全员统一思想。作者建议,大规模的微服务架构并且有超过100个开发人员,建议使用强所有权。每个小团队内部可以使用集体所有权。否则若所有权是可以满足实际需要的。
问题:重大变化
微服务将一个大的系统分割成若干个部分,每个部分都具有独立可部署性,通过提供自身的能力和使用其它服务的能力来实现业务价值。这就要求对服务的变更不能影响其消费者。为了达到这个目标,服务对外提供的能力将作为契约,不仅要保证这个契约返回稳定的结果,还要保证其拥有一致性的行为。在应用微服务架构的早期比较容易出现拆分问题。
解决这个问题的唯一办法是:不要打破协议。下面列出一些规则:
消除意外的重大变化
当你不知道自己对服务接口的调整会对消费者有怎样的影响,这时就会出现意外的重大变化。如果有一个协议的显示描述文件,可以帮助检查协议结构性的损伤。除了结构性的损伤,改变还可能引起语义上的变化。解决的方法就是让改变尽快的被所有开发人员知道。
在变更执行前多考虑几次
尽可能对协议扩展而不是变化。通过添加新的方法、资源、主题、新的功能等,而不是变更原有的接口。尝试向前和向后的兼容性。
给消费者足够的迁移时间
通过提供不同版本的部署包或者在同一个部署包中提供不同版本的接口,确保不会影响使用旧版本的消费者。当部署包版本不多时,可以使用不同版本的部署包。大部分情况应使用在一个运行的部署包中提供多版本的支持。
问题:报表
在单体应用中使用单体数据库实现报表统计功能时,可以使用表连接的操作跨多个表得到想要的数据。有时为了报表的处理不影响数据库的负载,会将数据库中的数据拷贝到一个报表专用的数据库中提供分析,如下图所示:
使用微服务架构以后,单体数据库被拆分成多个服务数据库。这会让报表功能变得复杂起来。解决这个问题通常使用的方法是,将各个独立的服务数据库中的数据提取到一个专用的报表数据库中进行处理,如下图所示:
第四章介绍过的变化数据提取系统可以用于这个场景,也可以自己编写程序定时批量的完成数据提取等。
问题:监控和排错
在单体应用时,如果发现CPU持续的100%,我们可以很快定位到问题所在。但是微服务架构中,服务实例可能成百上千,监控和排错就变得越来越困难。
常用的解决方法如下:
日志聚合:使用日志聚合系统将所有服务实例产生的日志集中采集起来进行分析和告警。建议这个系统应该在一开始就使用起来。
链路追踪:用户的一个请求会涉及到多个服务之间的调用,任何一个环节的问题都会影响最终的响应结果,当请求失败,又或者是超时,如何定位是哪一个服务出现问题,这在排错时尤为重要。因此需要使用链路追踪系统。
生产环境中的测试:系统涉及到众多的微服务应用,每个服务都是独立演化的,服务自身的所有测试都通过并不能说明部署后整个系统可以正常运行。因此需要持续的在生产环境中测试。通过用自动化手段在生产环境中使用虚拟的用户对真实用户场景进行测试。需要注意的是,测试过程一定要保证无副作用。
提升可观测性:利用各种监控手段,对微服务这个分布式系统中的各个节点的物理资源、应用状态等进行全面的集中监控,在发现潜在问题时,使用告警系统及时发出警报。
问题:开发人员的体验
随着服务的不断增多,向java的jvm这种资源密集型的运行时环境通常在开发人员的机器上同时只能运行少量的几个。这就意味着每个开发人员无法在本机同时运行整个系统的代码。当开发的服务需要调用多个其它服务时,应该怎么办?
通常的解决方案是“打桩(stub)”,通过对所需服务进行打桩,在不用运行所需服务程序的前提下来调试程序。还可以使用一些Mock框架提供更进一步的模拟。
另一种解决方法是在远程运行一套完整的系统,通过本地/远程联调的方法进行调试。
问题:运行太多的组件
服务的不断增多以及服务所以来的第三方中间件的不断增多,这让部署变得越来越困难。人工的部署方式不但易错,而且在大规模的微服务系统中是不切实际的。除了部署问题以外,伸缩性和鲁棒性也不能只通过人工来完成。这就需要引入自动化的能力,通过CI/CD和构建DevOps的流程来解决部署问题。另一方面,使用Kubernetes等云原生技术,来保证系统的伸缩性和鲁棒性。
问题:系统测试(端到端的测试)
这个问题类似于开发人员的体验问题,一个端到端的测试可能会跨越很大的服务范围,涉及多个负责服务的团队。解决的方法有:
限制自动功能测试的范围:端到端的测试用例尽量限制在一个团队所负责的服务内。
使用消费者驱动的契约测试(CDC):通过CDC来保证服务的修改不会引起消费者服务的失败。
使用自动发布和逐步交付:使用各类灰度发布来及逆行逐步交付,这样即便出现问题,其影响范围也会可控。
持续优化反馈机制:测试用例的不可能覆盖所有场景。因此需要建立快速的问题反馈机制,当问题发生,在处理问题的同时,也应将这个场景加入到测试用例中进行持续优化。
问题:全局还是局部优化
微服务要求每个团队拥有独立性,但这需要和全局的决策保持一致,这是需要权衡的。比如每个团队都可以自行选择使用什么技术进行服务的实现,如果没有限制,最终会形成混乱和浪费。比如A团队使用Oracle数据库,B团队使用Mysql,A团队花费了很多经历在Oracle的实践上,解决了很多问题,同时B团队在Mysql上做了相同的事情。对企业来说,可能涉及到商用软件授权的购买、A团队人员调动到B团队后的培训以及新人加入时的培训等。
一种解决这个问题的简单方法是,成了一个技术领导小组,由各个团队的技术负责人组成,由CTO、首席架构师或者企业其他高层领导负责。该小组的职责是站在全局的角度对每个技术进行审核。保证企业内个团队的技术是在一个可控范围内发展的。
问题:鲁棒性和弹性
鲁棒性和弹性是不同的概念。鲁棒性强调系统对于已知的风险有应对的能力;弹性则更强调对未知风险的应对。在分布式系统中,任何一个部分都会影响整个系统的鲁棒性和弹性。
下面这段是我自己写的,不是原书的内容,但是和原书表达的含义类似。
要解决这个问题,首先需要承认系统的脆弱性。把每一个服务间的调用都认为是不可靠的,然后在服务内部的业务逻辑上予以应对。
使用服务解耦、熔断、容错等方式可以提升系统的鲁棒性。
而对于弹性,则涉及的因素要更多一些,不是几个模式就能搞定的,需要企业建立一个整体的工作方式,来改进工作当中的实践。通过不断经验积累和思考学习来应对无法预知的问题。
问题:孤儿服务
大规模微服务系统运行一段时间后,可能因为有些服务变化极少和人员变动,导致一些服务的原来的负责团队消失了,而这些服务慢慢的被人们遗忘。这些就被称作是孤儿服务。它们自顾自的运行着,但是一旦出现问题,可能会让所有人莫名其妙。在对孤儿服务进行修改时,甚至可能出现源代码随着原来的团队一起消失了。
书中作者认为,孤儿服务在集中所有权的团队内比较少见。大部分有这类问题的团队都采用建立服务注册中心和统一的代码仓库,将微服务元信息和代码仓库的提交记录进行联动。如果企业中已经存在孤儿服务,应该尽早为它指定所有者。
总结(我的感悟)
微服务并不适合所有场景,并且任何两个微服务架构都不可能完全一致。别人的微服务经验只能拿来借鉴,不能照搬。考虑到微服务所带来的额外成本和分布式问题,在对业务领域知识了解不够、以及产品初期业务不稳定需要快速变化和实现时,并不适合使用微服务架构。微服务架构更适合于系统规模庞大、人员众多、关系复杂的场景。需要强调的是,微服务不是没有成本的,相反,其成本往往要比单体应用要高,随着服务规模的增加,成本和面临的挑战都将随之上升。因此,建议参考《极限编程》和《重构》的方法,在初期使用单体应用只为实现当下所需功能,过程中不断使用代码重构的方式形成模块化的单体应用,随着系统业务的稳定、领域知识的积累、组织人员的增加和系统复杂度的上升,逐步向微服务迁移。使用本书中提到的各种代码拆分和数据拆分的模式,逐步、小步的向微服务迁移。在整个迁移过程需要结合“企业架构”和“ITIL”这类IT服务管理理论,自上而下的规划与管理。微服务架构中需要面临的挑战极多,涉及众多组件和技术,这些都需要逐步的学习和应用。微服务与企业文化和组织架构有着极深的联系,绝不仅仅是一种IT技术,并且,微服务架构一定是围绕业务领域概念进行建模的。任何以技术驱动或者为了微服务而微服务的组织终将面临进退两难的境地,这点我作为一个过来人深有感触。当你没有充分的理由使用微服务架构时,请不要勉强自己。尽量先用微服务以外的方法实现自己的需求。当你已经开始使用微服务,但发现无法驾驭它时,不要觉得难为情,你应该尽快停止。技术永远是为业务服务,技术的发展只有一个目标:让业务发展的更好。能够支撑你现有的业务又为未来的业务预留了扩展空间,同时花费较少的成本,这应该算得上最好的架构,或许它不够时髦、技术老旧、不够绚丽,但对于你来说,它才是最好的!!!
关于作者
简介:10年JAVA开发经验,多年架构师经验。获得国家“系统架构设计师”、“PMP”、“MySQL DBA OCP”、“TOGAF9鉴定级”、“K8s CKA”、“Scrum Master PSM”等证书。擅长Java全栈开发、DevOps、架构设计、领域驱动、研发管理等。
写在最后
如果大家在工作或学习中遇到技术问题,可以通过邮箱或留言联系我,若问题具有代表性,我可以录制专门的视频对问题的成因和解决方案进行讲解。
为什么不是视频了?因为懒。。。(录制视频需要的时间太长,而且不方便后期的快速回顾,因此后面都会优先使用图文)
关注公众号查看更多内容!!!
目标:共同学习、共同进步、告别码农,成为受人敬仰的、有态度的程序猿。拒绝不知其所以然的复制粘贴、拒绝人云亦云。用最严谨的态度、最专业的方法、最可靠的知识来源,探究技术内幕,死磕到底!!!
内容简介
原书名字是《Monolith To Microservices》,是大神Sam Newman的新书,目前还没有中文版本。原本是想写一个简短的读后感的,但是写着写着,发现书中的内容真的是太经典了,浅尝辄止的描述完全不能体现本书的价值。于是就改成了用我自己的语言对书中每一章的内容进行了精炼。因此这个读后感也可以作为原书的精简版来看,只不过用的是我自己的语言总结的。也是由于这个原因,这篇文章越写字数越多,最后接近三万字,花费的时间也很多。为了便于阅读,分成4部分来发。
注:本文中的图片截自原书
第五章、不断增加的痛苦
微服务架构所带来的挑战随着服务规模的增加而不断增加。并且往往没有终点,一直都会处于发现问题解决问题的循环当中。文章中给出下图展示了服务规模和主要面临的挑战:
从图中可以看出,当服务数量在2-10个时,可能只有少量的团队来维护,主要面临的挑战是重大变化和处理报表;当服务个数上升到10-50个,可能仍然是少量的团队维护,面临的挑战变成了服务所有权的划分、开发人员的经验和需要运行太多的服务;服务个数继续上升到50个以上后,团队个数更多,开发人员也更多,这时面临的挑战是全局和局部的优化(如何平衡独立性和统一性)、孤儿服务(没有人负责的服务)。同时,随着服务规模的变大,对系统鲁棒性、弹性、监控和排错的需求将变得越来越急迫。
问题:所有权
代码的所有权分为三种:
强所有权:所有的服务都有拥有者。外部的人如果想要修改服务的代码,必须将修改提交给拥有者,由拥有者判断是否采用。
弱所有权:服务有拥有者,但任何人都可以对服务进行修改。修改前可能会告知拥有者。
集体所有权:服务没有拥有者,任何人都能随意修改任何代码。
许多团队开始的时候规模比较小(20人左右),使用集体所有权是有意义的。当人数变多,或者地理位置分散,沟通成本上升,进而无法在全员统一思想。作者建议,大规模的微服务架构并且有超过100个开发人员,建议使用强所有权。每个小团队内部可以使用集体所有权。否则若所有权是可以满足实际需要的。
问题:重大变化
微服务将一个大的系统分割成若干个部分,每个部分都具有独立可部署性,通过提供自身的能力和使用其它服务的能力来实现业务价值。这就要求对服务的变更不能影响其消费者。为了达到这个目标,服务对外提供的能力将作为契约,不仅要保证这个契约返回稳定的结果,还要保证其拥有一致性的行为。在应用微服务架构的早期比较容易出现拆分问题。
解决这个问题的唯一办法是:不要打破协议。下面列出一些规则:
消除意外的重大变化
当你不知道自己对服务接口的调整会对消费者有怎样的影响,这时就会出现意外的重大变化。如果有一个协议的显示描述文件,可以帮助检查协议结构性的损伤。除了结构性的损伤,改变还可能引起语义上的变化。解决的方法就是让改变尽快的被所有开发人员知道。
在变更执行前多考虑几次
尽可能对协议扩展而不是变化。通过添加新的方法、资源、主题、新的功能等,而不是变更原有的接口。尝试向前和向后的兼容性。
给消费者足够的迁移时间
通过提供不同版本的部署包或者在同一个部署包中提供不同版本的接口,确保不会影响使用旧版本的消费者。当部署包版本不多时,可以使用不同版本的部署包。大部分情况应使用在一个运行的部署包中提供多版本的支持。
问题:报表
在单体应用中使用单体数据库实现报表统计功能时,可以使用表连接的操作跨多个表得到想要的数据。有时为了报表的处理不影响数据库的负载,会将数据库中的数据拷贝到一个报表专用的数据库中提供分析,如下图所示:
使用微服务架构以后,单体数据库被拆分成多个服务数据库。这会让报表功能变得复杂起来。解决这个问题通常使用的方法是,将各个独立的服务数据库中的数据提取到一个专用的报表数据库中进行处理,如下图所示:
第四章介绍过的变化数据提取系统可以用于这个场景,也可以自己编写程序定时批量的完成数据提取等。
问题:监控和排错
在单体应用时,如果发现CPU持续的100%,我们可以很快定位到问题所在。但是微服务架构中,服务实例可能成百上千,监控和排错就变得越来越困难。
常用的解决方法如下:
日志聚合:使用日志聚合系统将所有服务实例产生的日志集中采集起来进行分析和告警。建议这个系统应该在一开始就使用起来。
链路追踪:用户的一个请求会涉及到多个服务之间的调用,任何一个环节的问题都会影响最终的响应结果,当请求失败,又或者是超时,如何定位是哪一个服务出现问题,这在排错时尤为重要。因此需要使用链路追踪系统。
生产环境中的测试:系统涉及到众多的微服务应用,每个服务都是独立演化的,服务自身的所有测试都通过并不能说明部署后整个系统可以正常运行。因此需要持续的在生产环境中测试。通过用自动化手段在生产环境中使用虚拟的用户对真实用户场景进行测试。需要注意的是,测试过程一定要保证无副作用。
提升可观测性:利用各种监控手段,对微服务这个分布式系统中的各个节点的物理资源、应用状态等进行全面的集中监控,在发现潜在问题时,使用告警系统及时发出警报。
问题:开发人员的体验
随着服务的不断增多,向java的jvm这种资源密集型的运行时环境通常在开发人员的机器上同时只能运行少量的几个。这就意味着每个开发人员无法在本机同时运行整个系统的代码。当开发的服务需要调用多个其它服务时,应该怎么办?
通常的解决方案是“打桩(stub)”,通过对所需服务进行打桩,在不用运行所需服务程序的前提下来调试程序。还可以使用一些Mock框架提供更进一步的模拟。
另一种解决方法是在远程运行一套完整的系统,通过本地/远程联调的方法进行调试。
问题:运行太多的组件
服务的不断增多以及服务所以来的第三方中间件的不断增多,这让部署变得越来越困难。人工的部署方式不但易错,而且在大规模的微服务系统中是不切实际的。除了部署问题以外,伸缩性和鲁棒性也不能只通过人工来完成。这就需要引入自动化的能力,通过CI/CD和构建DevOps的流程来解决部署问题。另一方面,使用Kubernetes等云原生技术,来保证系统的伸缩性和鲁棒性。
问题:系统测试(端到端的测试)
这个问题类似于开发人员的体验问题,一个端到端的测试可能会跨越很大的服务范围,涉及多个负责服务的团队。解决的方法有:
限制自动功能测试的范围:端到端的测试用例尽量限制在一个团队所负责的服务内。
使用消费者驱动的契约测试(CDC):通过CDC来保证服务的修改不会引起消费者服务的失败。
使用自动发布和逐步交付:使用各类灰度发布来及逆行逐步交付,这样即便出现问题,其影响范围也会可控。
持续优化反馈机制:测试用例的不可能覆盖所有场景。因此需要建立快速的问题反馈机制,当问题发生,在处理问题的同时,也应将这个场景加入到测试用例中进行持续优化。
问题:全局还是局部优化
微服务要求每个团队拥有独立性,但这需要和全局的决策保持一致,这是需要权衡的。比如每个团队都可以自行选择使用什么技术进行服务的实现,如果没有限制,最终会形成混乱和浪费。比如A团队使用Oracle数据库,B团队使用Mysql,A团队花费了很多经历在Oracle的实践上,解决了很多问题,同时B团队在Mysql上做了相同的事情。对企业来说,可能涉及到商用软件授权的购买、A团队人员调动到B团队后的培训以及新人加入时的培训等。
一种解决这个问题的简单方法是,成了一个技术领导小组,由各个团队的技术负责人组成,由CTO、首席架构师或者企业其他高层领导负责。该小组的职责是站在全局的角度对每个技术进行审核。保证企业内个团队的技术是在一个可控范围内发展的。
问题:鲁棒性和弹性
鲁棒性和弹性是不同的概念。鲁棒性强调系统对于已知的风险有应对的能力;弹性则更强调对未知风险的应对。在分布式系统中,任何一个部分都会影响整个系统的鲁棒性和弹性。
下面这段是我自己写的,不是原书的内容,但是和原书表达的含义类似。
要解决这个问题,首先需要承认系统的脆弱性。把每一个服务间的调用都认为是不可靠的,然后在服务内部的业务逻辑上予以应对。
使用服务解耦、熔断、容错等方式可以提升系统的鲁棒性。
而对于弹性,则涉及的因素要更多一些,不是几个模式就能搞定的,需要企业建立一个整体的工作方式,来改进工作当中的实践。通过不断经验积累和思考学习来应对无法预知的问题。
问题:孤儿服务
大规模微服务系统运行一段时间后,可能因为有些服务变化极少和人员变动,导致一些服务的原来的负责团队消失了,而这些服务慢慢的被人们遗忘。这些就被称作是孤儿服务。它们自顾自的运行着,但是一旦出现问题,可能会让所有人莫名其妙。在对孤儿服务进行修改时,甚至可能出现源代码随着原来的团队一起消失了。
书中作者认为,孤儿服务在集中所有权的团队内比较少见。大部分有这类问题的团队都采用建立服务注册中心和统一的代码仓库,将微服务元信息和代码仓库的提交记录进行联动。如果企业中已经存在孤儿服务,应该尽早为它指定所有者。
总结(我的感悟)
微服务并不适合所有场景,并且任何两个微服务架构都不可能完全一致。别人的微服务经验只能拿来借鉴,不能照搬。考虑到微服务所带来的额外成本和分布式问题,在对业务领域知识了解不够、以及产品初期业务不稳定需要快速变化和实现时,并不适合使用微服务架构。微服务架构更适合于系统规模庞大、人员众多、关系复杂的场景。需要强调的是,微服务不是没有成本的,相反,其成本往往要比单体应用要高,随着服务规模的增加,成本和面临的挑战都将随之上升。因此,建议参考《极限编程》和《重构》的方法,在初期使用单体应用只为实现当下所需功能,过程中不断使用代码重构的方式形成模块化的单体应用,随着系统业务的稳定、领域知识的积累、组织人员的增加和系统复杂度的上升,逐步向微服务迁移。使用本书中提到的各种代码拆分和数据拆分的模式,逐步、小步的向微服务迁移。在整个迁移过程需要结合“企业架构”和“ITIL”这类IT服务管理理论,自上而下的规划与管理。微服务架构中需要面临的挑战极多,涉及众多组件和技术,这些都需要逐步的学习和应用。微服务与企业文化和组织架构有着极深的联系,绝不仅仅是一种IT技术,并且,微服务架构一定是围绕业务领域概念进行建模的。任何以技术驱动或者为了微服务而微服务的组织终将面临进退两难的境地,这点我作为一个过来人深有感触。当你没有充分的理由使用微服务架构时,请不要勉强自己。尽量先用微服务以外的方法实现自己的需求。当你已经开始使用微服务,但发现无法驾驭它时,不要觉得难为情,你应该尽快停止。技术永远是为业务服务,技术的发展只有一个目标:让业务发展的更好。能够支撑你现有的业务又为未来的业务预留了扩展空间,同时花费较少的成本,这应该算得上最好的架构,或许它不够时髦、技术老旧、不够绚丽,但对于你来说,它才是最好的!!!
关于作者
简介:10年JAVA开发经验,多年架构师经验。获得国家“系统架构设计师”、“PMP”、“MySQL DBA OCP”、“TOGAF9鉴定级”、“K8s CKA”、“Scrum Master PSM”等证书。擅长Java全栈开发、DevOps、架构设计、领域驱动、研发管理等。
写在最后
如果大家在工作或学习中遇到技术问题,可以通过邮箱或留言联系我,若问题具有代表性,我可以录制专门的视频对问题的成因和解决方案进行讲解。
为什么不是视频了?因为懒。。。(录制视频需要的时间太长,而且不方便后期的快速回顾,因此后面都会优先使用图文)
关注公众号查看更多内容!!!