我所理解的微服务,就六个字:“高内聚,低耦合”。
没错,就是这个在软件开发过程中被反复提到的六个字,各类设计模式、架构设计、从入门到放弃等各种书中总会提到,从初级到高级到骨灰级程序员、架构师挂在嘴边的也是这六个字。只不过,在微服务概念之前,这六个字被用在类、模块、组件上,微服务则是将它放在服务上。
注:上面是精简版,下面是完整版,看官自便。
什么是微服务
微服务,2014年被大神马丁·福勒提出(所以2014年被称为微服务元年),在他的博客中提出他对微服务的理解。这个时候,微服务还高高在上,大家不知道如何落地实现。一直到2016年,随着各种技术和工具的落地,很多互联网公司才初步实现微服务,至此,微服务才真正造福(还是祸乱?)于开发界。
那到底什么是微服务?这里引用马丁·福勒原文:
The microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.
概括大意就是:
微服务是一种架构风格:
- 一组小服务
- 每个服务运行在独立进程中
- 服务之间使用轻量级通信
- 服务可独立部署
- 是基于业务能力实现
- 无集中式管理
基本上,符合上面这几条建议的架构风格,就是微服务。不过业界现在也没有统一定义,所以,如果发现以后微服务定义和上面的有偏差,一定是微服务的兼容性、包容性使然。
业界对于微服务还有一句更加剪短的描述:微服务是一组小而自治的服务。
这两个概念互相补充,微服务就是一组小、自治、基于业务的服务,以松散的服务方式,构建可独立部署的模块化应用。
小
说到“小”,有很多种观点,有的以代码行数界定,有的以重构时间界定,我个人认为这两种方式都不好。
首先,来说说以代码行数界定的方式。大家都是业内人士,都清楚行数有时候和编程习惯、开发能力有直接关系。比如,IP地址校验,正则表达式的写法和自己判断编写规则判断,代码行数就完全不一样。再者,代码行数是定2万行还是10万行,还是多少?假设定2万行,那20001行是不是就不符合微服务定义。再者,对于一个项目所依赖的基础类库、应用框架、编码规范,这些定义不同,代码行数也会有偏差。最后,对于动态语言和静态语言,其编译器、运行环境、支撑类库不同,代码行数也不尽相同。所以,用代码行数评判,是完全不可行的。
第二,用重构时间来界定。重构是一个考验对业务和架构理解的行为,功夫深浅不同,重构时间不同,功夫不到,还可能进入重构地狱,重构最后还会流产;功夫到了,可能几天时间就能重构一个异常复杂、异常庞大的系统。所以,用这种看经验、看水平的方式区分服务的微还是不微,也不可行。
我认为,这里说的小,可以类比“单一职责原则”。服务小而专,服务之间耦合低,可以实现业务的高度自治。简单说,就是能够让普通开发人员理解,业务上不掺杂不相干的业务,保持业务原子性。能够做到这样,就是小。
拿电商系统中的商品服务举例,商品包括商品基本信息、价格、库存等一系列功能。这些都属于商品,但如果把这些业务都放在一个服务里,拿这个服务就违反了小这个概念。商品主要是电商中的信息展示、下单、发货场景,展示基本信息、价格、库存,下单校验价格、库存,发货需要商品库存和仓储信息。看似几个功能中商品数据都有重叠,但是又不是完全重叠。比如商品信息展示,基本信息是最重要的,价格第二等重要,库存重要性最低。如果遇到高峰或者压力过大,库存可以降级,在下单时再做校验;如果压力再升,价格也可以不用实时更新变价。所以一个商品就可以拆分为三个服务:基本信息服务、商品价格服务、商品库存服务。这样一拆,职责单一,功能内聚。
可能有人认为把商品放在一个服务多好,接口区分就好了,实现简单。其实就“小”这个概念来说,也不是不可以,但是这会和下面的几条建议有冲突。
自治
每个微服务都是独立的个体,部署在独立进程中,每个服务一个进程。进程彼此独立,也就互相之间没有制约,无论是升级、修改、发布、回滚等一系列操作,都可以在一定情况下忽略其他服务。而且,服务在独立进程中,就可以独立监控,可以比较清晰的知道服务的运行情况,监控服务占用物理资源、运行Metric等就更加简单。这也算是服务独立进程部署的一个附加价值。
服务部署在独立进程中,彼此之间耦合低,通过网络通信时,还需要承诺使用技术无关的轻量级通信协议,最常见的是 HTTP/REST 或者 RPC 通信。很多关于微服务通信的建议中不推荐 RPC,是因为这个很可能会引入技术限制,但是目前的很多 RPC 组件,都提供了多语言支持,而且 RPC 的效率明显高于 HTTP/REST。但是不推荐使用强语言依赖的通信协议,比如Java RMI。同时也不推荐使用使用比较重的协议,比如 WebService。这样做的好处就是服务之间不耦合,可以选择更加适合的语言、工具或平台。有一条比较好的建议是,微服务的服务通信接口不应该随意变动,如果需要变动,需要提供更加兼容的方式,这样能够在一定程度上减少服务修改造成的影响。
每个微服务部署在独立进程中,从另一个维度解释就是,每个服务可以独立部署,在持续集成方面就有个更灵活的时间。部署过程中的提交、静态代码扫描、单元测试等流水线,可以独立或极少的依赖其他服务进行写作测试,也能够应用蓝绿部署、金丝雀部署等各种优秀的部署实践。退一步讲,可以独立的部署,也就可以独立的回滚。
还有一点比较重要,自治能够帮助每个微服务个体可以根据业务需要由选择技术栈,需要关注的是业务本身适合的技术,而不是其他依赖或不兼容的情况。(当然,技术栈不是越多越好,技术栈过多,可能在人员协调和招聘方面会有一定的影响。)
基于业务
微服务团队是围绕业务组织的跨职能团队,产品、设计、研发、测试、架构、运维等各种角色齐全,按照业务能力组织团队,在不同业务深耕,不断优化迭代。
这里不得不提到康威定律:设计系统的组织,其产生的设计等同于组织之内、组织之间的沟通结构。
第一定律:Communication dictates design(组织沟通方式会通过系统设计表达出来)
第二定律:There is never enough time to do something right, but there is always enough time to do it over(时间再多一件事情也不可能做的完美,但总有时间做完一件事情)
第三定律:There is a homomorphism from the linear graph of a system to the linear graph of its design organization(线型系统和线型组织架构间有潜在的异质同态特性)
第四定律: The structures of large systems tend to disintegrate during development, qualitatively more so than with small systems(大的系统组织总是比小系统更倾向于分解)
组件基于业务的团队,还可以是团队成员关注产品本身,而不是只关注与项目。团队成员就是产品的主人,有责任和义务保证产品的快速发展和演进迭代。同时可以制定更加行之有效的奖惩机制,成员有更强的产品成就感、荣誉感。亚马逊有一条建议"you build, you run it",业务团队对生产中产品付全部责任,
微服务自治的特性中提过,基于业务能力,可以更加业务特性选择不同的技术栈,这一点非常重要。
微服务的挑战
IT界有句名言:没有银弹。也就是说,没有完美的解决方案,微服务也是。
微服务是一个重型杀伤性武器,是叶轻眉留给范闲的巴雷特,但是前提是,范闲有子弹,而且会用。微服务有千百种好处,但是这些好处都是有代价的。
系统复杂性
由单体服务拆解为一系列的微服务,也就形成了分布式系统,系统复杂性不可同日而语。
首先是服务性能。相较于进程内的方法调用,微服务之间只能通过进程间通信协议进行沟通,其通信效率依赖于网络和带宽等物理因素。同时,从原来的进程内部方法调用,变成服务之间网络通信,也会对网络和带宽带来压力。两者之间,互为因果,彼此影响。
然后是服务的可靠性,服务增多,服务发生故障的概率不变的情况下,发生故障的次数就会增多,可靠性随之降低。同时,由于第一中情况引入的网络延迟等,服务通信失败的情况更加复杂。比如,网络超时这种异常情况:被调用方已经开始处理请求,而且最终会成功,但是调用方出现网络超时这种错误,如果单纯的认为服务调用异常,本地事务回滚,就可能造成数据的不一致性(也是第二个复杂性)。
最后就是开发、测试、问题定位也变得不可控:
- 对于开发,在单体架构中,业务之间都是方法调用,这个再简单不过,但是在微服务中,就得改为接口调用,而且还要根据通信接口的特性捕捉异常,根据捕捉异常的类型进行处理。而且,在需要事务的场景中,单体架构本地事务就可以解决绝大部分问题,但是在微服务场景中,就需要引入分布式事务,不同的分布式事务实践又需要不同的回滚实现。
- 对于测试,单体架构的测试可以使用简单的测试用例,但是在微服务中,服务之间彼此依赖,一个业务可能涉及多个服务之间的数据,启动多个服务联合测试,会造成人力成本提升;使用Mock接口,就增加了开发工作量,也对测试人员有了更高的要求。
- 对于问题定位,一旦测试过程中或线上出现问题,需要定位问题,找到问题出现的原因,进而解决问题。但是在微服务架构中,因为服务众多,一个错误产生的原因,可能是调用方数据不准确,也可能是被调用方逻辑有问题,甚至是被调用方的下游被调用方出现的异常。
数据一致性
如上面提到的,因为微服务之间调用的方式由方法调用变为服务通信调用,而且数据分而治之,所以没有办法依赖于简单的数据库事务解决,所以数据一致性就是问题。所以会出现两种方式,一种是分布式事务保证数据同步一致性,一种的基于异步消息保证数据最终一致性。无论哪种方式,又会增强第一种挑战:系统复杂性。
运维复杂性
运维的复杂性主要体现在配置、部署、监控等方面。
随着服务的增多,服务参数、数据库、缓存等一些列启动运行和依赖配置随之增多,而且变得复杂,而且不同的环境,还需要不同的配置。
服务增多,部署成本随之增高。在单体架构时代,只有一个应用包。但是在微服务时代,可能需要上百个服务。如果没有流水线这样的持续发布工具,单靠人力,部署将会是运维的噩梦。
在微服务时代,监控是必不可少的。监控包括服务监控、系统监控、指标告警、日志采集等,不单单是需要监控的终端变多,监控的数据量、异常分析都会增多,这也都提高了运维复杂性。
测试复杂性
在系统复杂性中提到过测试变得更加复杂。这个复杂不单单是测试的方式,还有测试的范围。在微服务中,为了覆盖所有的场景,测试可以分单元测试、服务测试、端到端测试。为了简便,服务测试又分了Mock和Stub等不同的流派。
微服务是什么
微服务就是将“高内聚、低耦合”应用到服务中的一种软件研发建议,是一种更适合当下业务快速多变的架构风格,是一组小、自治、基于业务的模块化松散服务。
网友评论