[系列]微服务·什么是微服务
本文为系列篇
微服务
的第一篇,将会介绍什么是微服务、为什么是微服务、微服务的核心理念和相关周边组件的介绍。
1. 什么是微服务
微服务是一种开发软件的架构和组织方法,其中软件由通过明确定义的 API 进行通信的小型独立服务组成。这些服务由各个小型独立团队负责。
微服务架构使应用程序更易于扩展和更快地开发,从而加速创新并缩短新功能的上市时间。
微服务并非是一个全新的技术而是跟随着互联网技术的发展的一个服务部署架构方案。与微服务相对的便是传统巨石架构(整体架构)。可以理解为微服务的出现解决了巨石架构的大部分的问题,因此微服务这个概念也被大部分的开发者所熟悉。
下面我们通过对比微服务和巨石架构的方式对微服务进行了解。
1.1 巨石架构
巨石架构又名为整体架构,在以往互联网应用开发过程一直是大家默认的一种架构方案。将所有的功能能力都在一个项目内实现,甚至包括前端页面的代码。
这种方式对于快速发展的业务来说非常适合的,不管太多的代码规范架构规范,功能快速实现快速上线作为第一目标,从而衍生出了很多一键生成项目的各类框架模板,只是为了更快速的完成项目的迭代。
这种架构的优点是快速迭代、代码相对聚合、团队内所有人只维护一个仓库即可。但是其缺点也比较明显,首先随着代码的增长维护仓库的成本越来越高从而新增、修改新功能变得越来越麻烦。不仅仅是开发同学的工作量变多变复杂,QA 同学的工作也会越来越复杂。仅仅一个小功能的添加,由于是一个整体的大项目,功能测试回归测试都变得异常棘手。逐渐的项目的快速迭代的优点就没有了,迭代速度会越来越慢。
其次是项目中局部升级几乎变得不可能了,例如依赖的包、底层数据库、MQ 的切换等等,很难确保这些组件不与业务逻辑代码没有关联。即便是升级一些第三方的包,也需要考虑所有引用的地儿,升级了也需要大量的回归测试。
然后是代码 debug 变得异常麻烦。巨石架构内虽说包含了所有的代码无需跳转项目的 debug,但是由于都是直接调用代码内的方法,很难做到日志和链路追踪,线上出故障的话只能硬着头皮在大量日志中找相关日志。
然后是部署方式比较死板,由于最后打包出来的进程包含了所有的能力,部署之后很难预估服务的请求量和压力,资源利用率会相对低。
最后是容易受语言的限制。我认为没有所谓的好语言不好的语言,每个语言都有各自的擅长和不擅长的领域。随着项目的迭代总会有一些需求,用项目的主语言实现遇到很多的困难,虽然明智换一种语言很快能解决当前的需求,但是巨石架构不允许这样的操作,也很难说为了这个功能新增一个项目进行部署。
总体来说,巨石架构在项目初期的时候几乎不会展现出他的缺点,但是经过一段时间的迭代,巨石架构下的项目就很难进行很好地维护了。
1.2 微服务架构
微服务架构是由一组小微的服务的组合而成。这些服务之前通过 rpc 的方式进行通信,从而提供一个整体的能力。微服务的优势有以下几点:
- 敏捷性:微服务促进若干小型独立团队形成一个组织,这些团队负责自己的服务。各团队在小型且易于理解的环境中行事,并且可以更独立、更快速地工作。这缩短了开发周期时间。您可以从组织的总吞吐量中显著获益。
- 灵活扩展:通过微服务,您可以独立扩展各项服务以满足其支持的应用程序功能的需求。这使团队能够适当调整基础设施需求,准确衡量功能成本,并在服务需求激增时保持可用性。
- 轻松部署:微服务支持持续集成和持续交付,可以轻松尝试新想法,并可以在无法正常运行时回滚。由于故障成本较低,因此可以大胆试验,更轻松地更新代码,并缩短新功能的上市时间。
- 技术自由:微服务架构不遵循“一刀切”的方法。团队可以自由选择最佳工具来解决他们的具体问题。因此,构建微服务的团队可以为每项作业选择最佳语言和工具。
- 弹性:服务独立性增加了应用程序应对故障的弹性。在整体式架构中,如果一个组件出现故障,可能导致整个应用程序无法运行。通过微服务,应用程序可以通过降低功能而不导致整个应用程序崩溃来处理总体服务故障。
当然微服务并不是什么 silver bullet,微服务虽说相对巨石架构有很多有点,但是随微服务一起来的那就是比较复杂的部署环境。同时有多个不同的服务在运行且得确保各个服务直接的网络的互通,加上微服务会依赖服务发现、日志收集、链路追踪等组件,需要一定的架构能力。
2. 为什么是微服务
在对巨石架构和微服务的优缺点有了一定的认知。我们现在聊一聊为什么是微服务或者是说 为什么微服务为什么被越来越多的开发者所接受。
微服务并非是近几年才出现的概念,其实这种架构方式一直都有被提到,但是由于以往的服务都是物理机的方式部署,很难为灵活的微服务架构提供一个展现他优势的平台。
而容器技术的出现我认为是微服务架构崛起的开始。服务可以以一个镜像为单位部署在服务器上,且可限制资源、可任意扩缩容、可快速升级。这种部署方式使得微服务架构的优点更加体现出来了,可以说是容器技术的出现成就了微服务。
之后再出现的容器编排(kubernetes),把容器技术的优势展现给了所有人,越来越多的服务不再像以前那么臃肿,一个服务只做一件事儿,与其他服务组成一个完整的架构体系。
与此同时出现了一大批优秀的开源产品/概念如 etcd、Prometheus、open tracing 等,为微服务架构的发展和发扬起了很大作用。
除此之外,云计算/云平台的出现,让我们认识了 aws、azure、Google Cloud、腾讯云、阿里云等这些云厂商。他们给我带来了很多 Paas,Saas 服务,使得我们的项目架构可以更灵活,我们不用自己搞所有的东西了,在我们的架构体系里可以有不同语言实现服务、可以有不同云厂商提供的服务。
这些种种新的的技术的横空出世以及原先技术的更新换代,为微服务架构体现带来了很多的可能性,这就是为什么是微服务的答案。
3. 微服务的核心理念
微服务核心理念我认为是小而精,由多个不同职责的小微服务去组成一个强大的服务体系。这个体系中的每一个模块可单独维护、可单独升降级、可单独替换从而不影响整体能力。
而这些模块作为一个个微服务或者基础组件的形式,存在这个体系当中。从外部来看,整体体系只有一个或几个入口,各个服务和组件互相依赖调用,从而做到一个完整的请求链,与此同时会有类似 sidecar 的组件去维护整体的完整性。整体架构更像一个金字塔模型,越底层的组件/服务会越多,这些组件/服务的组合为上一层提供服务,而上一层的职责就是根据业务整合不同的底层组件/服务提供的数据,为其上一层服务。
4. 微服务架构下的主要组件
在本篇开头的图片我们能看到,一个比较完整的微服务架构中会有很多与业务无关的组件和服务在运行,下面我看挑几个核心的组件进行其能力的讲解和该组件在架构中的作用。
4.1 服务发现服务注册
服务发现服务注册应该算是微服务架构中的核心概念之一,各个服务之间的通信都离不开该组件的能力。可以从字面意思理解,该组件的核心能力就是一个服务注册中心,服务启动后会将自己的信息,包括名字、通信协议、端口等,注册到注册中心,而其他服务从注册中心拉取其他服务信息,需要时根据服务信息去调用这些服务的 rpc 方法(或 http 接口)。而整个过程中各个服务之前都不需要互相感知服务的状态,仅通过统一的服务注册中心来拉取所需要的服务的基本信息即可。
而这种模式的好处在于,任意一个服务意外退出或用新的服务区替换一个旧的服务,对于其他服务来说是几乎无感知的,不需要对需要调用这个服务的其他服务进行任何改动。如果服务意外退出,其他服务不再从服务注册中心拿到该服务的信息,因此不会出现请求超时或者网络不通的位置情况,从请求发出前已经知道对方服务已经是不可用的状态了,可以做降级处理。
而更换服务(甚至更换服务 ip 端口)都对其他服务是无感知的,因为这些信息本来就是从服务注册中心拿到的,有变化很正常,直接用新的继续使用就可以。
4.2 配置中心
以往的服务部署都会带着一个配置文件,然后再服务启动时从配置文件或者环境变量读取服务启动必要的参数。但是随着服务数量的变多,不同服务之间的配置文件会有重叠的情况(比如公用一个数据库,mq,cache 等)。如果运行过程中需要改变这些配置文件会变得非常麻烦,至少需要一次改变配置和重启服务的过程。如果是修改哪些重叠的配置,则更加麻烦,很难保证所有的服务都已经修改且短时间内快速生效。
配置中心则很好的解决了这些问题,类似服务注册中心,配置中心统一管理所有的配置,并且可以做到同一块配置可以被多个应用共享,而一些敏感的配置,配置中心可以做到权限校验从而确保数据安全性(比如有的服务不应该拥有写权限的 db 角色)。
4.3 服务构建与部署
微服务架构中的服务,一般要求是可快速部署(甚至自动化部署),传统的服务器或者虚拟机上直接运行二进制文件的方式,对于微服务来说是不能接受的。前面也说了容器化技术的出现加快了微服务架构的发展,因此微服务的部署很大程度上是依赖容器技术的。在提交代码或者手动编译时,需要生成一个docker 镜像,推到镜像仓库中,而部署时直接拉取对应版本的镜像启动即可。
这样在业务迭代或者需要快速扩缩容时,配合容器编排器进行容器的调度,这相对于传统服务器/虚拟机环境下的调度时效性会高很多。同时可以更充分的利用现有的资源,提高利用率。
4.4 服务观测
服务的观测指的是日志、监控、链路追踪这三个模块,全面的对服务进行观测,达到尽快发现问题,尽快定位的作用。
4.4.1 日志服务
日志这块一般从两个方面入手,一是结构化日志,二是收集日志。
先说结构化日志。
日志是不仅是为了观察服务运行是否正常,更是为了在出现问题是时候根据相关日志尽快定位问题所在。因此日志怎么打,应该注意哪些事项应该有一份规范。这规范可根据自身情况而定,一般情况下是日志分等级,然后不同情况下打印不同级别的日志。除此之外,日志的结构化,日志不能只只是于人能看懂,更应是机器能看懂。根据日志分析服务质量、错误率、业务情况等大数据分析,从而可以预测一些场景,从而对服务带来一些益处。
再说收集日志。
由于微服务架构下的服务大多数是在 docker 或 kubernetes 环境下运行,这就意味着日志文件在各自的容器内。这个时候需要日志收集的 agent 和统一日志中心来聚合分散的日志,提升查询效率。一般部署一个 ES或者部署一套 ELK 来解决这个问题,当然对于依赖云厂商服务或者没有资源部署这些组件的开发者,对接到云厂商提供的日志组件也是足矣。我之前公司的时候也是对接阿里云的日志服务,整体使用起来也是 OK 的。
除此之外,最近还被安利了一个 Go 语言实现的轻量级的开源日志查询、分析、报警的可视化平台 – Click Visual。大概看了一下页面与阿里云的日志组件页面很相似,可能为了保持用户使用习惯。之后我会花时间去体验这个工具,并出一个安装&体验的博客(先挖个坑)。
4.4.2 服务监控与告警
服务监控与告警并不是微服务独有的概念,正常任何一个线上业务都会基于各种技术手段去监控其业务的各种指标,同时根据规则进行告警。
一般微服务架构中常见的监控和告警组合为 Prometheus + Grafana。
Prometheus 作为一个这几年出现的开源项目,已经被众多知名的开源项目或开源组织所拥护,其指标收集和查询能力可以说是业界数一数二的了。看一下其用户就知道其强大了:
而 Grafana 是一个数据展示面板,其丰富的组件和高度可制定性,给使用者带来了无数种可能性。且 Grafana 支持包括 Prometheus 在内的 100 多种数据源,可以做到一个数据面,看到所有组件的监控指标并且每一种指标都可以配置监控规则。因此 Grafana 也受大量开发者的欢迎。
4.4.3 OpenTracing
OpenTracing 是 CNCF 提出的分布式追踪的标准。它提供用厂商中立的 API,并提供 Go、Java、JavaScript、Python、Ruby、PHP、Objective-C、C++ 和 C# 这九种语言的库。目前支持 Tracer 包括 Zipkin、Skywalking、Jaeger 等,支持的框架包括 gRPC、MOTAN、django、Flask、Sharding-JDBC 等。
OpenTracing 中有两个关键的概念,分别是 Trace,Span。
Trace
Trace 表示一个完成的调用链,如一次完成的请求。
Span
Span 表示调用链中的一个最小单元,一个 Trace 由多个 Span 组成。如一次请求中的中服务之间的调用可以是一个 span,一次数据库查询可以是一个 span。
如下图JaegerUI 展示的一次 Trace,整个过程是一个 Trace,而过程中的每一个模块是一个 Span。Trace 拥有一个唯一的 TraceID, Span 拥有一个唯一的 SpanID 和其所属的 TraceID。
配合 UI,可以很快定位哪一个阶段出问题、哪一个阶段处理变慢了等情况,对于跨多个服务多个组件的请求,这种追踪方式无疑使非常好用的。
目前 OpenTracing 最新状态是 CNCF 于 2022 年 1 月 31 日归档了(cncf-archives-the-opentracing-project)。其主要原因是 CNCF 推出了 OpenTelemetry 项目,是一个更全面的可观测性标准。
OpenTelemetry(也称为 OTel)是一个开源可观测能力框架,由一系列工具、API 和 SDK 组成,使 IT 团队能够检测、生成、收集和导出远程监测数据以进行分析和了解软件性能和行为。
要了解 OTel 所做的工作,了解可观测能力会有所帮助。粗略定义一下,可观测能力是一种根据系统生成外部数据(通常是日志、指标和跟踪)研判系统内部发生情况的能力。
OpenTelemetry致力于如何收集和发送可观测能力数据并使其具有通用格式。作为云原生计算基金会 (CNCF) 的孵化项目,OTel 旨在提供与供应商无关的统一库和 API 集——主要用于收集数据并将其传输到某个地方。自项目启动以来,包括Dynatrace在内的众多厂商,已加入这一阵营,同心协力让海量数据收集更简便、更易于使用。
要了解可观测能力和 OTel 的处理方法的重要性,让我们更深入地了解远程监测数据本身,以及它如何帮助组织转变其经营方式。
4.5 API 网关
API 网关作为整个服务的入口,其核心功能应该包括以下几点:
- 鉴权
- 限流熔断降级
- 感知后端服务,根据业务/规则调用不同的服务
一般成熟的微服务框架都带有 API 网关相关逻辑,也有单独的网关开源项目,如 istio, apisix等。
4.6 其他
除了上述几个常见的组件/概念之外,根据业务/架构需求,会有安全、负载均衡、DB、MQ、代理等组件。而这些组件/概念在开发中如何使用,应该怎么写微服务的代码对于一个刚接触微服务的人来说确实是个大问题。而这个问题的答案就是使用一些成熟的微服务框架,这些框架已经把上述的所有概念通过代码抽象出来,看完这些框架的 demo,基本可以上手使用。
常见的框架:
- asim/go-micro:该框架可以算得上是一个老牌一流的框架,很多新兴框架上都能看到他的影子,强烈建议阅读其各种接口,项目结构的定义,能收获很多。
- go-kratos/kratos:B 站员工们推出的微服务框架,最近两年非常火,其设计理念与 go-micro 很类似,也很值得尝试使用,我本人 GoIM 项目也尝试使用该框架,整体还是不错的。其介绍也说了设计受 go-micro 的影响。
这些框架并非把所有微服务的代码都帮你写好,你完全可以替代其任意模块,因为大部分微服务框架都采用定义 api,然后单独实现api 后注册的方式使用。因此框架内几乎没有对固定某个组件的依赖,全部采用 api 的方式。你可以自己实现其 log 模块,或其服务注册服务发现模块。
4.6.1 说点 rpc 相关的
微服务之间的通信一般是以 rpc 的方式,一般我们常听过的 rpc 有:
- Dubbo:阿里推出的 rpc 框架,仅支持Java
- Tars:腾讯推出的 rpc 框架,仅支持Java
- Spring Cloud:Pivotal 公司推出的 rpc 框架,仅支持 Java
上述几个 rpc 框架都是在特定的语言范围内,对于整个微服务体系只有一种语言的情况下才能使用,如果跨语言只能靠通用的 http 等协议去通信。但是越来越多的的微服务架构内不止只有 java 一种语言,所以跨语言的 rpc 框架也是不可缺少的。
- gRPC:Google 推出的 rpc 框架,支持大部分主流语言
- Thrift:Apache 基金会的开源项目,也是支持大部分主流语言
在 Go 语言环境下,gRPC 无非是最好的选择,况且选择很多主流的开源项目都支持 gRPC,其适用面还是很广的。
在不同微服务之间,共同维护一个 proto buffer 文件(gRPC 的通信编码协议),服务通过protoc 工具生成各自语言的代码,从而可以达到服务之间的 rpc 调用。
以一个发送消息的接口定义为例:
|
|
这块定义可以通过 protoc 生成 Go 语言的代码,包括 struct 定义以及rpc 方法的定义和 client 端调用该 rpc 方法的代码。由于代码量比较多,这里放一个跳转地址,感兴趣的同学可以跳转查看。
5. 总结
本篇篇幅较长,由于微服务涉及到的面比较广,况且我想尽量写的全面一点,就写的有点多了。这里对本篇的内容做个简单的总结:
- 讲述巨石架构 vs 微服务架构
- 讲述为什么是微服务架构,是什么促成了微服务的发展
- 微服务的核心理念有哪些
- 微服务核心组件的介绍
- 服务发现服务注册
- 配置中心
- 服务的构建和部署
- 服务的观测
- API 网关
- 微服务框架 & rpc 框架 & gRPC
本篇是我个人对微服务的一点理解,若有我理解错或者比我更好的理解,欢迎下面评论讨论。