说起微服务,大家应该并不陌生,不只是一线大厂,很多中小规模团队也已经将这项技术引入并在实际业务中落地。
那作为一名开发人员,应该如何学习微服务呢?
虽然现在开源的微服务框架有很多,各种编程语言的都有,花上几个小时搭建一套可运行的开发环境也并不是一件难事。但毕竟微服务涉及的组件还是挺多的,相比于单体架构来说,复杂度提升了不少。
不知道你有没有和我一样的困扰,有时候想要深入学习一下,但却不知道从什么地方入手,结果就是对其了解只是浮于表面。
所以我最近看了极客时间的专栏《从 0 开始学微服务》,觉得作为入门还是不错的。
同时,我总结了专栏中的部分内容,并加入一些自己的思考,形成了这篇文章。算是学习微服务的一个大纲,然后再按照这个大纲去深入学习,不断充实这套技术体系。
好了,下面正文开始。
微服务是指开发应用所用的一种架构形式。通过微服务,可将大型应用分解成多个独立的组件,其中每个组件都有各自的责任领域。在处理一个用户请求时,基于微服务的应用可能会调用许多内部微服务来共同生成其响应。
微服务的特点:
想要构建微服务,首先要解决的问题是,服务提供者如何发布一个服务,服务消费者如何引用这个服务。具体来说,就是这个服务的接口名是什么?调用这个服务需要传递哪些参数?接口的返回值是什么类型?以及一些其他接口描述信息。
最常见的服务发布和引用的方式有三种:
这种方式就比较常见了,主要被用作 HTTP 或者 HTTPS 协议的接口定义,即使在非微服务架构体系下,也被广泛采用。而且学习成本低,比较适合用作跨业务平台之间的服务协议。
这种方式下需要在服务提供者和服务消费者之间维持一份对等的 XML 配置文件,来保证服务调用。比如提供者维护 server.xml,消费者维护 client.xml。
在接口变更时,需要同时修改这两个接口描述文件。
IDL 就是接口描述语言(interface description language)的缩写,通过一种中立的方式来描述接口,使得在不同的平台上运行的对象和不同语言编写的程序可以相互通信交流。
也就是说 IDL 主要是用作跨语言平台的服务之间的调用,有两种最常用的 IDL:一个是 Facebook 开源的 Thrift 协议,另一个是 google 开源的 gRPC 协议。
每种方式都有各自的优缺点,具体应该如何选择,需要根据自身的业务特点。
描述方式 |
使用场景 |
缺点 |
RESTful API |
跨语言平台,组织内外皆可 |
使用 HTTP 作为通信协议,相比于 TCP,性能较差 |
XML 配置 |
JAVA 平台,一般用于组织内部 |
不支持跨语言平台 |
IDL 文件 |
跨语言平台,组织内外皆可 |
修改和删除字段不支持向前兼容 |
服务拆分之后,服务的提供者和消费者分别运行在不同的机器上,而且服务众多,有几十个,甚至上百个。这就涉及到一个问题,消费者如何才能找到服务提供者,这也是注册中心需要解决的问题。
注册中心需要实现哪些 API?
除此之外,为了便于管理,注册中心还必须提供一些后台管理的 API,例如:
在拆分为微服务架构前,单体应用只需要管理一套配置;而拆分为微服务后,每一个系统都有自己的配置,并且都各不相同,而且因为服务治理的需要,有些配置还需要能够动态改变,以达到动态降级、切流量、扩缩容等目的,所以如何管理配置十分重要。
配置中心一般包含下面几个功能:
API 网关可以被视为一种充当应用程序服务和不同客户端之间的中间件,可以管理许多事情,例如:
事实上,它是作为一个反向代理工作的,客户端只需要知道系统的网关,应用服务就可以隐藏起来,不直接向其他系统暴露。
如果没有 API 网关,可能需要在每个服务中做一些横切关注点,比如想记录服务的请求和响应。此外,如果应用程序由多个服务组成,客户端需要知道每个服务地址,并且在更改服务地址的情况下,需要更新多个地方。
微服务架构拥有很好的可扩展性,我们能够通过运行更多服务实例来处理更多请求,但问题是,哪个实例应该接收请求,或客户端如何知道哪个服务实例应该处理请求?
这些问题的答案是负载均衡。负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。
常用的负载均衡算法有一下五种:
顾名思义就是从可用的服务节点中,随机挑选一个节点来访问。
实现比较简单,在请求量远超可用服务节点数量的情况下,各个服务节点被访问的概率基本相同,主要应用在各个服务节点的性能差异不大的情况下。
跟随机算法类似,各个服务节点被访问的概率也基本相同,也主要应用在各个服务节点性能差异不大的情况下。
在轮询算法基础上的改进,可以通过给每个节点设置不同的权重来控制访问的概率,因此主要被用在服务节点性能差异比较大的情况。
比如经常会出现一种情况,因为采购时间的不同,新的服务节点的性能往往要高于旧的节点,这个时候可以给新的节点设置更高的权重,让它承担更多的请求,充分发挥新节点的性能优势。
与加权轮询算法预先定义好每个节点的访问权重不同,采用最少活跃连接算法,客户端同服务端节点的连接数是在时刻变化的,理论上连接数越少代表此时服务端节点越空闲,选择最空闲的节点发起请求,能获取更快的响应速度。
尤其在服务端节点性能差异较大,而又不好做到预先定义权重时,采用最少活跃连接算法是比较好的选择。
因为它能够保证同一个客户端的请求始终访问同一个服务节点,所以适合服务端节点处理不同客户端请求差异较大的场景。
比如服务端缓存里保存着客户端的请求结果,如果同一客户端一直访问一个服务节点,那么就可以一直从缓存中获取数据。
不管是单体架构,还是微服务架构,监控都是必不可少的。只不过微服务架构更加复杂,监控起来也就更加不容易。
对于一个微服务来说,必须明确要监控哪些对象、哪些指标,并且还要从不同的维度进行监控,才能掌握微服务的调用情况。
监控对象可以分为四个层次,由上到下可归纳为:
搞清楚要监控的对象之后,需要监控具体哪些指标呢?
一般来说,要从多个维度来对业务进行监控,包括下面几个维度:
在调试单体应用时,非常直观容易。但是在微服务架构上,因为一个请求可能会通过不同的服务,而不同的服务又不在一个地方,这使得调试和跟踪变得困难。
所以服务追踪是分布式系统中必不可少的功能,它能够帮助我们查询一次用户请求在系统中的具体执行路径,以及每一条路径的上下游的详细情况,对于追查问题十分有用。
它的核心理念就是调用链:通过一个全局唯一的 ID 将分布在各个服务节点上的同一次请求串联起来,从而还原原有的调用关系,可以追踪系统问题、分析调用数据并统计各种系统指标。
小结一下,traceId 是用于串联某一次请求在系统中经过的所有路径,spanId 是用于区分系统不同服务之间调用的先后关系,而 annotation 是用于业务自定义一些自己感兴趣的数据,在上传 traceId 和 spanId 这些基本信息之外,添加一些自己感兴趣的信息。
系统故障是避免不了的,虽然微服务架构做了服务拆分,不至于像单体架构那样整体崩溃,但由于其整体复杂度也大大提升,故障处理也更加困难。
顾名思义,限流就是限制流量。通常情况下,系统能够承载的流量根据集群规模的大小是固定的,可以称之为系统的最大容量。
当真实流量超过了系统的最大容量后,就会导致系统响应变慢,服务调用出现大量超时,反映给用户的感觉就是卡顿、无响应。
所以,应该根据系统的最大容量,给系统设置一个阈值,超过这个阈值的请求会被自动抛弃,这样的话可以最大限度地保证系统提供的服务正常。
熔断和限流还不太一样,上面我们可以看到限流是控制请求速率,只要还能承受,那么都会处理,但熔断不是。
在一条调用链上,如果发现某个服务异常,比如响应超时。那么调用者为了避免过多请求导致资源消耗过大,最终引发系统雪崩,会直接返回错误,而不是疯狂调用这个服务。
什么是降级呢?降级就是通过停止系统中的某些功能,来保证系统整体的可用性。
降级可以说是一种被动防御的措施,为什么这么说呢?因为它一般是系统已经出现故障后所采取的一种止损措施。
单体应用拆分成多个微服务后,能够实现快速开发迭代,但随之带来的问题是测试和运维部署成本的提升。
而容器技术正好可以很好的解决这些问题,目前最流行的当属 Docker 莫属。
微服务容器化运维主要涉及到以下几点:
镜像仓库的概念其实跟 Git 代码仓库类似,就是有一个集中存储的地方,把镜像存储在这里,在服务发布的时候,各个服务器都访问这个集中存储来拉取镜像,然后启动容器。
这个阶段主要是解决在哪些机器上启动容器的问题,特别是规模比较大的公司,一般有物理机集群,虚拟机集群,私有云和公有云。对接这么多不同的平台,难度还是不小的。
需要统一管理来自不同集群的机器权限管理、成本核算以及环境初始化等操作,这个时候就需要有一个统一的层来完成这个操作。
很显然,靠人工是肯定不行的,需要搭建统一的部署运维平台。
大部分情况下,微服务之间是相互独立的,在进行容器调度的时候不需要考虑彼此。
但有时候也会存在一些场景,比如服务 A 调度的前提必须是先有服务 B,这样的话就要求在进行容器调度的时候,还需要考虑服务之间的依赖关系。
Service Mesh 的概念最早是由 Buoyant 公司的 CEO William Morgan 提出,他给出的服务网格的定义是:
A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native Application. In practice, the service mesh is typically implemented as an array of lightweight.NETwork proxies that are deployed alongside application code, without the application needing to be aware.
被很多人定义为下一代的微服务架构。
目前,Service Mesh 的代表产品当属 Google 和 IBM 的 lstio。
Istio 的架构可以说由两部分组成,分别是 Proxy 和 Control Plane。