微服务简介
# 微服务简介
# 什么是微服务
Martin Fowler 与 James Lewis 共同提出了微服务的概念,定义了微服务是由单一应用程序构成的小服务,自己拥有自己的进程与轻量化处理,服务依业务功能设计,以全自动的方式部署,与其他服务使用 HTTP API 通信。同时服务会使用最小的规模的集中管理 (例如 Docker) 能力,服务可以用不同的编程语言与数据库等组件实现。——微服务的维基百科词条定义
互联网应用架构大致的演进方向为:单体架构 -> 服务化架构 -> 微服务架构。在演化过程中,架构越来越复杂,一个应用被拆分的服务也越来越细。
# 单体应用
互联网早期的技术栈通常为 LAMP(Linux + Apache + MySQL + PHP)或 MVC(Spring + iBatis/Hibernate + Tomcat)。这两种架构都是典型的单体应用架构。其优点是技术栈简单,因此学习上手快,部署也容易。
随着业务越来越复杂,开发团队规模不断扩张,单体应用架构就难以适应开发迭代节奏,主要有以下问题:
- 构建、部署效率低:代码越多,依赖资源越多,则构建、部署的耗费时间自然会越长。即使每次修改一个很小的功能点,也不得不全量构建、全部部署,耗时耗力。
- 团队协作成本高:单体应用的代码往往在一个工程中,而一个工程中的开发人员越多,显然沟通成本越高。
- 可用性差:因为所有的功能开发最后都部署到同一个 WAR 包里,运行在同一个 Tomcat 进程之中,一旦某一功能涉及的代码或者资源有问题,那就会影响整个 WAR 包中部署的功能。
# 微服务
微服务架构有以下 4 个特点:
- 服务拆分粒度更细:根据业务拆分。
- 独立部署:每个服务部署在物理上隔离,互不影响。
- 独立维护:根据组织架构拆分,分团队维护。
- 服务治理:服务数量变多,需要有统一的服务治理平台。
简单来说,微服务就是将庞杂臃肿的单体应用拆分成细粒度的服务,独立部署,并交给各个中小团队来负责开发、测试、上线和运维整个生命周期。
# 何时需要微服务
应用微服务化架构前,要思考几个问题:什么时候进行服务化拆分?如何拆分服务?
当应用复杂度、开发团队膨胀到难以维护时,就该考虑服务化拆分了。
# 拆分服务的思考维度
- 业务维度:业务和数据关系密切的应该拆分为一个微服务,而功能相对比较独立的业务适合单独拆分为一个微服务。
- 功能维度:公共功能聚合为一个服务。标准是是否被多个其他服务调用,且依赖的资源独立不与其他业务耦合。
- 组织架构:根据实际组织架构,天然分为不同的团队,每个团队独立维护若干微服务。
但并不是说功能拆分的越细越好,过度的拆分反而会让服务数量膨胀变得难以管理,因此找到符合自己业务现状和团队人员技术水平的拆分粒度才是可取的。
# 拆分服务的原则
单一职责
高内聚,低耦合
先粗后细,逐渐细化
渐进式迭代
考虑扩展性
# 拆分服务的前置条件
- 服务如何定义?
- 对于单体应用来说,不同功能模块之前相互交互时,通常是以类库的方式来提供各个模块的功能。
- 对于微服务来说,每个服务都运行在各自的进程之中,无论采用哪种通讯协议,是 HTTP 还是 RPC,服务之间的调用都通过接口来约定如何交互。约定内容包括接口名、接口参数以及接口返回值。
- 服务如何发布和订阅?
- 单体应用由于部署在同一个 WAR 包里,接口之间的调用属于进程内的调用。
- 对于微服务来说,服务提供者需要向注册中心发布自己提供的服务(暴露接口信息以及接口地址);服务消费者向注册中心订阅哪些服务可用。
- 服务如何监控?通常对于一个服务,我们最关心的是 QPS(调用量)、AvgTime(平均耗时)以及 P999(99.9% 的请求性能在多少毫秒以内)这些指标。这时候你就需要一种通用的监控方案,能够覆盖业务埋点、数据收集、数据处理,最后到数据展示的全链路功能。
- 服务如何治理?可以想象,拆分为微服务架构后,服务的数量变多了,依赖关系也变复杂了。比如一个服务的性能有问题时,依赖的服务都势必会受到影响。可以设定一个调用性能阈值,如果一段时间内一直超过这个值,那么依赖服务的调用可以直接返回,这就是熔断,也是服务治理最常用的手段之一。
- 故障如何定位?在单体应用拆分为微服务之后,一次用户调用可能依赖多个服务,每个服务又部署在不同的节点上,如果用户调用出现问题,你需要有一种解决方案能够将一次用户请求进行标记,并在多个依赖的服务系统中继续传递,以便串联所有路径,从而进行故障定位。
应用微服务架构,必须要先解决以上问题
# 微服务的基础架构
微服务架构下,服务调用主要依赖下面几个基本组件:
- 服务描述
- 注册中心
- 服务框架
- 服务监控
- 服务追踪
- 服务治理
# 服务描述
服务调用首先要解决的问题就是服务如何对外描述。比如,你对外提供了一个服务,那么这个服务的服务名叫什么?调用这个服务需要提供哪些信息?调用这个服务返回的结果是什么格式的?该如何解析?这些就是服务描述要解决的问题。
常用的服务描述方式包括 RESTful API、XML 配置以及 IDL 文件三种。
其中,RESTful API 方式通常用于 HTTP 协议的服务描述,并且常用 Wiki 或者Swagger (opens new window)来进行管理。下面是一个 RESTful API 方式的服务描述的例子。
XML 配置方式多用作 RPC 协议的服务描述,通过 *.xml 配置文件来定义接口名、参数以及返回值类型等。下面是一个 XML 配置方式的服务描述的例子。
IDL 文件方式通常用作 Thrift 和 gRPC 这类跨语言服务调用框架中,比如 gRPC 就是通过 Protobuf 文件来定义服务的接口名、参数以及返回值的数据结构。
# 注册中心
有了服务的接口描述,下一步要解决的问题就是服务的发布和订阅,就是说你提供了一个服务,如何让外部想调用你的服务的人知道。这个时候就需要一个类似注册中心的角色,服务提供者将自己提供的服务以及地址登记到注册中心,服务消费者则从注册中心查询所需要调用的服务的地址,然后发起请求。
一般来讲,注册中心的工作流程是:
- 服务提供者在启动时,根据服务发布文件中配置的发布信息向注册中心注册自己的服务。
- 服务消费者在启动时,根据消费者配置文件中配置的服务信息向注册中心订阅自己所需要的服务。
- 注册中心返回服务提供者地址列表给服务消费者。
- 当服务提供者发生变化,比如有节点新增或者销毁,注册中心将变更通知给服务消费者。
# 服务框架
服务消费者发起调用需解决以下问题:
- 服务通信采用什么协议?就是说服务提供者和服务消费者之间以什么样的协议进行网络通信,是采用四层 TCP、UDP 协议,还是采用七层 HTTP 协议,还是采用其他协议?
- 数据传输采用什么方式?就是说服务提供者和服务消费者之间的数据传输采用哪种方式,是同步还是异步,是在单连接上传输,还是多路复用。
- 数据压缩采用什么格式?通常数据传输都会对数据进行压缩,来减少网络传输的数据量,从而减少带宽消耗和网络传输时间,比如常见的 JSON 序列化、Java 对象序列化以及 Protobuf 序列化等。
# 服务监控
一旦服务消费者与服务提供者之间能够正常发起服务调用,你就需要对调用情况进行监控,以了解服务是否正常。通常来讲,服务监控主要包括三个流程。
- 数据收集。就是要把每一次服务调用的请求耗时以及成功与否收集起来,并上传到集中的数据处理中心。
- 数据处理。有了每次调用的请求耗时以及成功与否等信息,就可以计算每秒服务请求量、平均耗时以及成功率等指标。
- 数据展示。数据收集起来,经过处理之后,还需要以友好的方式对外展示,才能发挥价值。通常都是将数据展示在 Dashboard 面板上,并且每隔 10s 等间隔自动刷新,用作业务监控和报警等。
# 服务链路追踪
除了需要对服务调用情况进行监控之外,你还需要记录服务调用经过的每一层链路,以便进行问题追踪和故障定位。
服务链路追踪的工作原理大致如下:
- 服务消费者发起调用前,会在本地按照一定的规则生成一个
requestid
,发起调用时,将requestid
当作请求参数的一部分,传递给服务提供者。 - 服务提供者接收到请求后,记录下这次请求的
requestid
,然后处理请求。如果服务提供者继续请求其他服务,会在本地再生成一个自己的requestid
,然后把这两个requestid
都当作请求参数继续往下传递。
以此类推,通过这种层层往下传递的方式,一次请求,无论最后依赖多少次服务调用、经过多少服务节点,都可以通过最开始生成的 requestid
串联所有节点,从而达到服务追踪的目的。
# 服务治理
服务监控能够发现问题,服务追踪能够定位问题所在,而解决问题就得靠服务治理了。服务治理就是通过一系列的手段来保证在各种意外情况下,服务调用仍然能够正常进行。
在生产环境中,你应该经常会遇到下面几种状况。
- 单机故障。通常遇到单机故障,都是靠运维发现并重启服务或者从线上摘除故障节点。然而集群的规模越大,越是容易遇到单机故障,在机器规模超过一百台以上时,靠传统的人肉运维显然难以应对。而服务治理可以通过一定的策略,自动摘除故障节点,不需要人为干预,就能保证单机故障不会影响业务。
- 单 IDC 故障。你应该经常听说某某 App,因为施工挖断光缆导致大批量用户无法使用的严重故障。而服务治理可以通过自动切换故障 IDC 的流量到其他正常 IDC,可以避免因为单 IDC 故障引起的大批量业务受影响。
- 依赖服务不可用。比如你的服务依赖依赖了另一个服务,当另一个服务出现问题时,会拖慢甚至拖垮你的服务。而服务治理可以通过熔断,在依赖服务异常的情况下,一段时期内停止发起调用而直接返回。这样一方面保证了服务消费者能够不被拖垮,另一方面也给服务提供者减少压力,使其能够尽快恢复。
上面是三种最常见的需要引入服务治理的场景,当然还有一些其他服务治理的手段比如自动扩缩容,可以用来解决服务的容量问题。