其实从业界的讨论来看,微服务本身并没有一个严格的定义。不过,ThoughtWorks 的首席科学家 Martin Flowler 的描述更加通俗易懂:
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务和服务间采用轻量级的通信机制互相沟通(通常是基于 HTTP 的 RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下 文,选择合适的语言、工具对其进行构建。
—— 摘自马丁·福勒先生的博客,微服务的概念最早即来源于此。
微服务架构的特征
1、多微才够微
微服务架构通过对特定业务领域的分析与建模,将复杂的应用分解成小而专一、耦合度低并 且高度自治的一组服务,每个服务都是很小的应用。
微服务的「微」并不是一个真正可衡量、看得见、摸得着的微。这个「微」所表达的,是一种设计思想和指导方针,是需要团队或者组织共同努力找到一个平衡点。所以,微服务到底有多微,是个仁者见仁,智者见智的问题,最重要的是团队觉得合适。但注意,如果达成「团队觉得合适」的结论,至少还应遵循以下两个基本前提:
业务独立性:首先,应保证微服务是具有业务独立性的单元,并不能只是为了微而微;
团队自主性:其次,考虑到团队的沟通及协作成本,一般不建议超过10人。当团队超过10人,在沟通,协作上所耗费的成本会显著增加,这也是大部分敏捷实践里提倡的。
2、单一职责
所谓高内聚,是一个模块内各个元素彼此结合的紧密程度高。低耦合,是指对于一个完整的系统,模块与模块之间,尽可能独立存在。
在面向对象的设计中,更是有放之四海而皆准的「SOLID原则」。SOLID 原则中的 S 表示的是 SRP(Single Responsibility Principle,单一职责原则):即一个对象应该只有一个发生变化的原因,如果一个对象可被多个原因改变,那么就说明这个对象承担了多个职责。
对于每个服务而言,我们希望它处理的业务逻辑能够单一,在服务架构层面遵循单一职责原则。也就是说,微服务架构中的每个服务,都是具有业务逻辑的,符合高内聚、低耦合原则以及单一职责原则的单元,不同的服务通过「管道」的方式灵活组合,从而构建出庞大的系统。
3、轻量级通信
服务之间应通过轻量级的通信机制,实现彼此之间的互通互联,互相协作。所谓轻量级通信机制,通常指语言无关、平台无关的交互方式。
对于轻量级通信格式而言,我们熟悉的 XML 或者 JSON ,它们的解析和使用基本与语言无关、平台无关。对于轻量级通信协议而言,通常基于HTTP,能让服务间的通信变得标准化并且无状态化。 REST(Representational State Transfer,表现层状态转化),是实现服务之间互相协作的轻量级通信机制之一。
对于微服务而言通过使用语言无关、平台无关的轻量级通信机制,使服务与服务之间的协作变得更加标准化,也就意味着在保持服务外部通信机制轻量级的情况下,团队可以选择更适合的语言、工具或者平台来开发服务本身。
4、独立性
独立性是指在应用的交付过程中,开发、测试以及部署的独立。
在传统的单体架构应用中,所有功能都存在于同一个代码库中。当修改了代码库中的某个功能,很容易出现功能之间相互影响的情况。尤其是随着代码量、功能的不断增加,风险也会逐渐增加。
除此之外,当多个特性被不同小组实现完毕,需要经过集成、回归测试,团队才有足够的信心,保证功能相互配合、正常工作并且互不影响。因此,测试过程不是一个独立的过程。
当所有测试验证完毕,单体架构应用将被构建成一个部署包,并标记相应的版本。在部署过程中,单体架构部署包将被部署到生产环境,如果其中某个特性存在缺陷,则有可能导致整个部署过程的失败或回滚。、
单体应用开发、测试、构建、部署流程图如下:
在微服务架构中,每个服务都是一个独立的业务单元,当对某个服务进行改变时,对其他的服务不会产生影响。换句话说,服务和服务之间是独立的。对于每个服务,都有独立的代码库。当对当前服务的代码进行修改后,并不会影响其他服务。从代码库的层面而言,服务与服务是隔离的。
对于每个服务,都有独立的测试机制,并不担心破坏其他功能而需要建立大范围的回归测试。由于构建包是独立的,部署流程也就能够独立,因此服务能够运行在不同的进程中。
微服务开发、测试、构建、部署流程图如下:
5、进程隔离
所有功能运行在同一个进程中,也就意味着,当对应用进行部署时,必须停掉当前正在运行的应用,部署完成后,再重新启动进程,无法做到独立部署。如果当前某应用中包含定时任务的功能,则要考虑在什么时间窗口适合部署,是否先停掉消息队列或者切断与数据源的联系,以防止数据被读入应用程序内存,但还未处理完,应用就被停止而导致的数据不一致性。
为了提高代码的重用以及可维护性,在应用开发中,我们有时也会将重复的代码提取出来,封装成组件(比如 PHP 中的 Composer 扩展包)。在传统的单体架构应用中,当应用程序在运行期时,所有的组件最终也会被加载到同一个进程中运行。
在微服务架构中,应用程序由多个服务组成,每个服务都是一个具有高度自治的独立业务实体。通常情况下,每个服务都能运行在一个独立的操作系统进程中,这就意味着不同的服务能非常容易的被部署到不同的主机上。作为运行微服务的环境,我们希望它能够保持高度自治性和隔离型。如果多个服务运行在同一个服务器节点上,虽然省去了节点的开销,但是增加了部署和扩展的复杂度。
总而言之,微服务架构其实是将单一的应用程序划分成一组小的服务,每个服务都是具有业务属性的独立单元,同时能够被独立开发、独立运行、独立测试以及独立部署。
微服务架构总体设计
看完微服务的定义后,接下来我们一起走进微服务架构,来看看它的总体设计和各个组成部分。在具体介绍之前先来看下微服务架构的整体架构图和一次正常的服务调用的完整流程:
此图是根据笔者所用技术所话,注册中心可以用Nacos、zk、eureka等代替。
首先服务提供者(用户、商品等微服务子模块)按照指定格式的服务接口描述,向注册中心注册服务,声明自己能够提供哪些服务以及服务的地址是什么,完成服务发布。
接下来服务消费者(API 网关层或者相邻的其它微服务模块)请求注册中心,查询所需要调用服务的地址,然后以约定的通信协议向服务提供者发起请求,得到请求结果后再按照约定的协议解析结果。
在服务调用过程中,服务的请求耗时、调用量以及成功率等指标都会被记录下来用作监控,调用经过的链路信息会被记录下来,用于故障定位和问题追踪。在这期间,如果调用失败,可以通过重试等服务治理手段来保证成功率。
所以总结起来,微服务架构下,服务调用主要依赖下面几个基本组件:
注册中心:注册并维护远程服务及服务提供者的地址,供服务消费者发现和调用,为保证可用性,通常基于分布式 kv 存储器来实现;
服务框架:用于实现微服务的 RPC 框架,包含服务接口描述及实现方案、向注册中心发布服务等功能,常见的 RPC 框架包括 Spring Cloud、Dubbo、gRPC、 Thrift、go-micro 等;
服务网关:介于客户端与微服务之间的网关层,可以理解为「门卫」的角色,以确保服务提供者对客户端的透明,这一层可以进行反向路由、安全认证、灰度发布、日志监控等前置动作;
服务监控:对服务消费者与提供者之间的调用情况进行监控和数据展示;
服务追踪:记录对每个请求的微服务调用完整链路,以便进行问题定位和故障分析;
服务治理:服务治理就是通过一系列的手段来保证在各种意外情况下,服务调用仍然能够正常进行,这些手段包括熔断、隔离、限流、降级、负载均衡等。
基础设施:分布式消息队列、日志存储、数据库、缓存、文件服务器、搜索集群等,用以提供服务底层的基础数据服务,可以自建,也可以使用阿里云等公有云提供的服务。
此外,从微服务部署角度来说,我们通常会基于容器编排技术实现微服务的运维。