同系列文章:Go 进阶训练营
业务场景各有不同,分享内容时不要太具体到哪个业务场景,应该找到变化中不变的部分。万变不离其宗,先把本质学好。
单体应用
单体应用业务复杂后,应用无法扩展,可靠性低。而且无法针对热点模块单独处理。整体资源利用率低。
微服务和SOA的关系
微服务是SOA的一种实践。
-
小既是美:小服务代码少,易维护、bug少、易测试
-
单一职责:只做一件事,专注才能做好
- common-util包就不符合单一职责,每个包应该干一个事,例如util目录下的string包
-
尽可能早地创建原型:先定义API,建立服务契约,达成服务间沟通的一致性约定,实现和完善可以慢慢做
-
可移植性比效率更重要:服务间的轻量交互协议优先考虑兼容性和移植性,例如grpc支持多种语言
微服务定义
围绕业务功能建设的,服务关注单一业务,由于服务间通信频繁,需要采用轻量级的通信。单独部署维护,可使用不同的编程语言和存储技术。服务组件化后,方便复用,系统架构清晰,高效开发。
特点
- 原子服务
- 独立进程
- 隔离部署
- 去中心化服务治理
微服务的不足
Fred Brooks 在30年前写道,“there are no silver bullets”
运维难,基础设施建设难,复杂度高。
测试难,服务上游多,测试不清楚是缺陷还是上游服务的问题。对应的是沟通协调成本增加,测试环境服务此时是否能更新,更新后需要通知他人
微服务应用是分布式系统,由此会带来固有的复杂性。开发者不得不使用 RPC 或者消息传递,来实现进程间通信;此外,必须要写代码来处理消息传递中速度过慢或者服务不可用等局部失效问题。
分区的数据库架构,同时更新多个业务主体的事务很普遍。这种事务对于单体式应用来说很容易,因为只有一个数据库。在微服务架构应用中,需要更新不同服务所使用的不同的数据库,从而对开发者提出了更高的要求和挑战。
服务模块间的依赖,应用的升级有可能会波及多个服务模块的修改。
微服务化发现的问题
网络瓶颈
以前是单体应用,业务操作大部分是进程内完成。微服务后,一个业务操作涉及到多个服务之间通信,通过rpc完成。一个请求到了应用后,应用和下游的服务进行网络交互会放大100倍。也就是微服务会给网络带来压力。不过部分情况可能是代码层面的问题。以前是循环查库,瓶颈大部分在DB。现在循环rpc,网络问题被放大。解决办法:batch化,提供粗粒度的接口。
串行执行
单体应用中,复杂的业务场景下,涉及到多个业务流程。除非某个流程特别耗时,一般采用串行执行。因为即使可以使用线程池来进行并行处理,但这个优化带来的收益太小,不如想想DB瓶颈咋解决。但在微服务中,每个业务流程可能设计到需要和其他服务通信,这是比较耗时的,所以可以结合实际场景,并行执行。
分布式一致性问题
服务之间的通信没有进程内通信可靠,失败后如何处理,进而暴露出复杂的一致性问题。
分布式事务
DB由一个演进为多个,虽然压力被分担,但也带来了棘手的问题,例如分布式事务。
应用部署与维护
需要部署的应用数量直线上升,日志、监控都需要改造升级,应用管理难度更大。
问题排查
以前应用有问题,ssh连上去看看日志,arthas监控下,或者本地启应用复现问题调试。上了微服务后,前端反应一个问题,是哪个后端服务导致的呢?应该看哪个服务的日志?排查起来要困难许多。
服务组件化
微服务之前,为了复用代码,在垂直应用中,抽取了部分代码,封装为业务组件,通过依赖库的形式提供给上层应用使用。缺点是业务组件更新,需要所有使用该业务组件的上层应用重新构建。在复杂系统中,这是个很耗时的事情,之前做的垂直架构系统,构建一个上层应用需要好几分钟。
微服务化后,将业务组件封装为业务中台服务,场景层通过rpc调用即可。这时业务组件是可以单独维护的,只要不涉及协议层的变更。另外,我们还会封装一部分基础、业务无关、通用、未来很少变更(不涉及业务)的代码为基础组件库。
Go微服务
- kit:基础库
- service:业务代码+kit+三方库
- rpc+mq:轻量通讯
本质上等同于:多个微服务组合完成了一个完整的用户场景(usecase)
按业务组织服务
按业务能力组织服务的意思是服务提供的能力和业务功能对应,比如:订单服务和数据访问服务,前者反应了真实的订单相关业务,后者是一种技术抽象服务不反应真实的业务。所以按微服务架构理念来划分服务时,是不应该存在数据访问服务这样一个服务的。
事实上传统应用设计架构的分层结构正反映了不同角色的沟通结构。所以若要按微服务的方式来构建应用,也需要对应调整团队的组织架构。每个服务背后的小团队的组织是跨功能的,包含实现业务所需的全面的技能。
B站的模式:大前端(移动/Web) =》 网关接入 =》业务服务 =》平台服务 =》基础设施(PaaS/Saas)
开发团队对软件在生产环境的运行负全部责任!
康威定律
设计系统的架构受制于产生这些设计的组织的沟通结构。
即系统设计本质上反映了企业的组织机构。系统各个模块间的接口也反映了企业各个部门之间的信息流动和合作方式。
康威定律源于模块的设计者需要互相之间频繁沟通。而跨部门交流比较难。[2]
埃里克·雷蒙在《新黑客词典》中,称康威定律指出了软件架构与软件团队架构的等价(congruent)。
如果团队、部门、子部门等的组织结构没有紧密反映产品的必要组成或产品组成的关系,那么项目将会遇到麻烦。因此,应该确保组织结构兼容于产品架构。
去中心化
每个服务面临的业务场景不同,可以针对性的选择合适的技术解决方案。但也需要避免过度多样化,结合团队实际情况来选择取舍,要是每个服务都用不同的语言的技术栈来实现,想想维护成本真够高的。
数据去中心化
每个服务独占一个db、cache,避免db发生故障影响全部服务,例如一条慢sql。体现微服务隔离性,提高整体可靠性。
治理去中心化
流量热点,去全局热点
技术去中心化
尽量收敛
基础设施自动化
无自动化不微服务,自动化包括测试和部署。单一进程的传统应用被拆分为一系列的多进程服务后,意味着开发、调试、测试、监控和部署的复杂度都会相应增大,必须要有合适的自动化基础设施来支持微服务架构模式,否则开发、运维成本将大大增加。
- CICD:Gitlab + Gitlab Hooks + k8s
- Testing:测试环境、单元测试、API自动化测试
- 在线运行时:k8s,以及一系列 Prometheus、ELK、Conrtol Panle
Version:0.9 StartHTML:0000000105 EndHTML:0000005226 StartFragment:0000000141 EndFragment:0000005186
可用性 & 兼容性设计
-
著名的 Design For Failure 思想,写代码时考虑鲁棒性。
-
微服务架构采用粗粒度的进程间通信,例如提供批量操作接口
-
引入了额外的复杂性和需要处理的新问题,如网络延迟、消息格式、负载均衡和容错,忽略其中任何一点都属于对“分布式计算的误解”。
- 隔离
- 超时控制
- 负载保护
- 限流
- 降级
- 重试
- 负载均衡
-
一旦采用了微服务架构模式,那么在服务需要变更时我们要特别小心,服务提供者的变更可能引发服务消费者的兼容性破坏,时刻谨记保持服务契约(接口)的兼容性。
Be conservative in what you send, be liberal in what you accept.
- 发送时要保守,接收时要开放。按照伯斯塔尔法则的思想来设计和实现服务时,发送的数据要更保守,意味着最小化的传送必要的信息,接收时更开放意味着要最大限度的容忍冗余数据,保证兼容性。