前言前端
随着业务的发展,代码量的膨胀和团队成员的增长,传统单体式架构的弊端愈来愈凸显,严重制约了业务的快速创新和敏捷交付。为了解决传统单体架构面临的挑战,前后演进出了SOA服务化架构、RPC框架、分布式服务框架,最后就是当今很是流行的微服务架构。java
微服务化架构并不是银弹,它的实施自己就会面临不少陷阱和挑战,涉及到设计、开发、测试、部署、运行和运维等各个方面,一旦使用不当,则会致使整个微服务架构改造的效果大打折扣,甚至失败。后端
本文从微服务的生命周期全过程,阐述微服务架构的改造如何实施,以及如何避开各类陷阱,提高实施效率。缓存
在实施微服务架构改造以前,咱们的产品线遇到一个很大挑战,就是需求的交付周期愈来愈短,采用的传统MVC单体架构愈来愈难知足特性快速交付和上线的需求。传统的电信项目,团队规模每每都很是大,甚至会跨地域。跨团队、跨地域的分布式协同开发,代码的重用和共享是个难题。网络
例如咱们的支付功能须要新增一个限额保护, 短短十几行代码的一个小需求,评估以后居然须要9个星期才能上线。缘由就是限额保护功能须要同时在9个不一样的功能模块中修改, 新增900多个测试用例用来作全量的回归测试,示例以下:架构
经过对已有的MVC单体架构进行分析,咱们发现主要存在以下几个问题:框架
研发成本高:代码重复率高,需求变动困难,没法知足新业务快速上线和敏捷交付。运维
测试、部署成本高:业务运行在一个进程中,所以系统中任何程序的改变,都须要对整个系统从新测试并部署。异步
可伸缩性差:水平扩展只能基于整个系统进行扩展,没法针对某一个功能模块按需扩展。分布式
可靠性差:某个应用BUG,例如死循环、OOM等,会致使整个进程宕机,影响其它合设的应用。
代码维护成本高:本地代码在不断的迭代和变动,最后造成了一个个垂直的功能孤岛,只有原来的开发者才理解接口调用关系和功能需求,新加入人员或者团队其它人员很难理解和维护这些代码。
依赖关系没法有效管理:服务间依赖关系变得错踪复杂,甚至分不清哪一个应用要在哪一个应用以前启动,架构师都不能完整的描述应用的架构关系。
以上问题的应对策略,就是服务化。
首先,须要对业务进行拆分。当业务量大了之后,特别是当不一样的功能耦合在一块儿的时候,任何一个地方的改动都是很是困难的,必须对业务进行拆分,拆分的策略有两种:
横向拆分。按照不一样的业务域进行拆分,例如订单、商品、库存、号卡资源等。造成独立的业务领域微服务集群。
纵向拆分。把一个业务功能里的不一样模块或者组件进行拆分。例如把公共组件拆分红独立的原子服务,下沉到底层,造成相对独立的原子服务层。这样一纵一横,就能够实现业务的服务化拆分。
其次,要作好微服务的分层:梳理和抽取核心应用、公共应用,做为独立的服务下沉到核心和公共能力层,逐渐造成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。
完成服务的拆分和分层工做以后,就会涉及到分布式的部署和调用。如何透明化、高效的发现服务,须要一个服务注册中心,经过服务化和订阅、发布机制对应用调用关系解耦,支持服务的自动注册和发现。
服务化架构的演进历史
在实施微服务架构以前,咱们一块儿回顾下服务化架构的演进历史。
MVC
MVC架构大部分人都用过,它主要用来解决先后端、界面、控制逻辑和业务逻辑分层问题。比较流行的技术堆栈就是Spring Struts iBatis(Hibernate) Tomcat(JBoss)。
RPC
随着业务特别是互联网的发展,业务规模的扩大,模块化逐步成为一种趋势,此时解决模块之间远程调用的RPC框架应运而生。RPC须要解决模块之间跨进程通讯的问题,不一样的团队开发不一样的模块,经过一个RPC框架实现远程调用,RPC框架帮业务把通讯细节给屏蔽掉,可是RPC框架也有自身的缺点。
RPC自己不负责服务化,例如:服务的自动发现无论、服务的应用和发布无论、服务的运维和治理也无论。没有透明化、服务化的能力,对整个应用层的侵入仍是比较深的。
SOA
SOA服务化架构,企业级资产重用和异构系统间的集成对接,SOA架构的现状:在传统企业IT领域,主要是解决异构系统之间的互通和粗粒度的标准化(WebService)。互联网领域,提供一套高效支撑应用快速开发迭代的服务化架构。例如各个互联网公司自研或者开源的分布式服务框架。
微服务架构
首先看一下微服务架构的定义:微服务(MSA)是一种架构风格,旨在经过将功能分解到各个离散的服务中以实现对解决方案的解耦。它有以下几个特征:
小,且只干一件事情。
独立部署和生命周期管理。
异构性
轻量级通讯,RPC或者Restful。
1.微服务架构的拆分原则
微服务架构的实施过程当中,首先遇到的最大的难题,就是它的拆分原则。
微服务拆分原则:围绕业务功能进行垂直和水平拆分。大小粒度是难点,也是团队争论的焦点。
很差的实践
以代码量做为衡量标准,例如500行之内。
拆分的粒度越小越好,例如以单个资源的操做粒度为划分原则。
建议的原则
功能完整性、职责单一性。
粒度适中,团队可接受。
迭代演进,非一蹴而就。
API的版本兼容性优先考虑。
代码量多少不能做为衡量微服务划分是否合理的原则,由于咱们知道一样一个服务,功能自己的复杂性不一样,代码量也不一样。还有一点须要重点强调,在项目刚开始的时候,不要指望微服务的划分一蹴而就。
微服务架构的演进,应该是一个按部就班的过程。在一个公司、一个项目组,它也须要一个按部就班的演进过程。一开始划很差,没有关系。当演进到一个阶段时,微服务的部署、测试和运维等成本都很是低的时候,这对于你的团队来讲就是一个好的微服务。
2.微服务架构的开发原则
微服务的开发还会面临依赖滞后的问题。例如:A要作一个身份证号码校验,依赖服务提供者B。因为B把身份证号码校验服务的开发优先级排的比较低,没法知足A的交付时间点。A会面临要么等待,要么本身实现一个身份证号码校验功能。
之前单体架构的时候,你们须要什么,每每喜欢本身写什么,这实际上是没有太严重的依赖问题。可是到了微服务时代,微服务是一个团队或者一个小组提供的,这个时候必定没有办法在某一个时刻同时把全部的服务都提供出来,“需求实现滞后”是必然存在的。
一个好的实践策略就是接口先行,语言中立,服务提供者和消费者解耦,并行开发,提高产能。不管有多少个服务,首先须要把接口识别和定义出来,而后双方基于接口进行契约驱动开发,利用Mock服务提供者和消费者,互相解耦,并行开发,实现依赖解耦。
采用契约驱动开发,若是需求不稳定或者常常变化,就会面临一个接口契约频繁变动的问题。对于服务提供者,不能由于担忧接口变动而迟迟不对外提供接口,对于消费者要拥抱变动,而不是抱怨和抵触。要解决这个问题,一种比较好的实践就是管理 技术左右开弓:
容许接口变动,可是对变动的频度要作严格管控。
提供全在线的API文档服务(例如Swagger UI),将离线的API文档转成全在线、互动式的API文档服务。
API变动的主动通知机制,要让全部消费该API的消费者可以及时感知到API的变动。
契约驱动测试,用于对兼容性作回归测试。
3.微服务架构的测试原则
微服务开发完成以后须要对其进行测试。微服务的测试包括单元测试、接口测试、集成测试和行为测试等,其中最重要的就是契约测试:
利用微服务框架提供的Mock机制,能够分别生成模拟消费者的客户端测试桩和提供者的服务端测试桩,双方能够基于Mock测试桩对微服务的接口契约进行测试,双方都不须要等待对方功能代码开发完成,实现了并行开发和测试,提升了微服务的构建效率。基于接口的契约测试还能快速的发现不兼容的接口变动,例如修改字段类型、删除字段等。
4.微服务架构的部署原则
测试完成以后,须要对微服务进行自动化部署。微服务的部署原则:独立部署和生命周期管理、基础设施自动化。须要有一套相似于CI/CD的流水线来作基础设施自动化,具体能够参考Netflix开源的微服务持续交付流水线Spinnaker:
最后一块儿看下微服务的运行容器:微部署能够部署在Dorker容器、PaaS平台(VM)或者物理机上。使用Docker部署微服务会带来不少优先:
一致的环境,线上线下环境一致。
避免对特定云基础设施提供商的依赖。
下降运维团队负担。
高性能接近裸机性能。
多租户。
相比于传统的物理机部署,微服务能够由PaaS平台实现微服务自动化部署和生命周期管理。除了部署和运维自动化,微服务云化以后还能够充分享受到更灵活的资源调度:
云的弹性和敏捷。
云的动态性和资源隔离。
5.微服务架构的治理原则
微服务部署上线以后,最重要的工做就是服务治理。微服务治理原则:线上治理、实时动态生效。
微服务经常使用的治理策略:
流量控制:动态、静态流控制。
服务降级。
超时控制。
优先级调度。
流量迁移。
调用链跟踪和分析。
服务路由。
服务上线审批、下线通知。
SLA策略控制。
微服务治理模型以下所示:
最上层是为服务治理的UI界面,提供在线、配置化的治理界面供运维人员使用。SDK层是提供了微服务治理的各类接口,供服务治理Portal调用。最下面的就是被治理的微服务集群,集群各节点会监听服务治理的操做去作实时刷新。
例如:修改了流控阈值以后,服务治理服务会把新的流控的阈值刷到服务注册中心,服务提供者和消费者监听到阈值变动以后,获取新的阈值并刷新到内存中,实现实时生效。因为目前服务治理策略数据量不是特别大,因此能够将服务治理的数据放到服务注册中心(例如etcd/ZooKeeper),没有必要再单独作一套。
微服务最佳实践
介绍完微服务实施以后,下面咱们一块儿学习下微服务的最佳实践。
服务路由:本地短路策略。关键技术点:优先调用本JVM内部服务提供者,其次是相同主机或者VM的,最后是跨网络调用。经过本地短路,能够避免远程调用的网络开销,下降服务调用时延、提高成功率。原理以下所示:
服务调用方式:同步调用、异步调用、并行调用。一次服务调用,一般就意味着会挂一个服务调用线程。采用异步调用,能够避免线程阻塞,提高系统的吞吐量和可靠性。可是在实际项目中异步调用也有一些缺点,致使使用不是特别普遍:
须要写异步回调逻辑,与传统的接口调用使用方式不一致,开发难度大一些。
一些场景下须要缓存上下文信息,引入可靠性问题。
并行调用适用于多个服务调用没有上下文依赖,逻辑上能够并行处理,相似JDK的Fork/Join, 并行服务调用涉及到同步转异步、异步转同步、结果汇聚等,技术实现难度较大,目前不少服务框架并不支持。采用并行服务调用,能够把传统串行的服务调用优化成并行处理,可以极大的缩短服务调用时延。三种服务调用方式的原理图以下:
微服务故障隔离:线程级、进程级、容器级、VM级、物理机级等。关键技术点:
支持服务部署到不一样线程/线程池中。
核心服务和非核心服务隔离部署。
为了防止线程膨胀,支持共享和独占两种线程池策略。
谈到分布式,就绕不开事务一致性问题:大部分业务能够经过最终一致性来解决,极少部分须要采用强一致性。
具体的策略以下:
最终一致性,能够基于消息中间件实现。
强一致性,使用TCC框架。服务框架自己不会直接提供“分布式事务”,每每根据实际须要迁入分布式事务框架来支持分布式事务。
微服务的性能三要素:
I/O模型,这个一般会选用非堵塞的,Java里面可能用java原生的。
线程调度模型。
序列化方式。
公司内部服务化,对性能要求较高的场景,建议使用异步非阻塞I/O(Netty) 二进制序列化(Thrift压缩二进制等) Reactor线程调度模型。
最后咱们一块儿看下微服务的接口兼容性原则:技术保障、管理协同。
制定并严格执行《微服务前向兼容性规范》,避免发生不兼容修改或者私自修改不通知周边的状况。
接口兼容性技术保障:例如Thrift的IDL,支持新增、修改和删除字段、字段定义位置无关性,码流支持乱序等。
持续交付流水线的每日构建和契约化驱动测试,可以快速识别和发现不兼容。