微服务基本概念

  • 内容
  • 评论
  • 相关

什么是微服务?什么时候适合微服务改造?微服务架构到底是什么样的?

理解微服务之前首先要知道什么是服务化,用通俗的话来讲,服务化就是把传统的单机应用中通过 JAR包依赖产生的本地方法调用,改造成通过 RPC 接口产生的远程方法调用。一般在编写业务代码时,对于一些通用的业务逻辑,我会尽力把它抽象并独立成为专门的模块,因为这对于代码复用和业务理解都大有裨益。通过服务化,可以解决单体应用膨胀、团队开发耦合度高、协作效率低下的问题。

1. 什么是微服务

服务拆分粒度更细

· 微服务可以说是更细维度的服务化,小到一个子模块,只要该模块依赖的资源与其他模块都没有关系,那么就可以拆分为一个微服务。

###服务独立部署

· 每个微服务都严格遵循独立打包部署的准则,互不影响。比如一台物理机上可以部署多个 Docker 实例,每个Docker 实例可以部署一个微服务的代码。

服务独立维护

· 每个微服务都可以交由一个小团队甚至个人来开发、测试、发布和运维,并对整个生命周期负责。

服务治理能力要求高

· 因为拆分为微服务之后,服务的数量变多,因此需要有统一的服务治理平台,来对各个服务进行管理。

2. 那么到底什么时候应该拆分单体应用?拆分单体应用有哪些标准可依呢

什么时候进行服务化拆分

· 个人实际项目经验,一旦单体应用同时进行开发的人员超过10人,这个时候就该考虑进行服务化拆分

· 建议的标准是按照每个开发人员负责不超过 3 个大的服务为标准,毕竟每个人的精力是有限的,所以在拆分微服务时,可以按照开发人员的总人数来决定。

服务化拆分的两种姿势

· 纵向拆分

· 从业务维度进行拆分。标准是按照业务的关联程度来决定,关联比较密切的业务适合拆分为一个微服务,而功能相对比较独立的业务适合单独拆分为一个微服务。

· eg:社交 App 为例,你可以认为首页信息流是一个服务,评论是一个服务,消息通知是一个服务,个人主页也是一个服务。

· 横向拆分

· 从公共且独立功能维度拆分。标准是按照是否有公共的被多个其他服务调用,且依赖的资源独立不与其他业务耦合。

· eg:社交 App 举例,无论是首页信息流、评论、消息箱还是个人主页,都需要显示用户的昵称。假如用户的昵称功能有产品需求的变更,你需要上线几乎所有的服务,这个成本就有点高了。显而易见,如果我把用户的昵称功能单独部署成一个独立的服务,那么有什么变更我只需要上线这个服务即可,其他服务不受影响,开发和上线成本就大大降低了。

服务化拆分的前置条件

下面几个问题,是从单体应用迁移到微服务架构时必将面临也必须解决的:

· 服务如何定义:无论是采用哪种通讯协议,是 HTTP 还是 RPC,服务之间的调用通过接口描述来约定,约定内容包括接口名称、接口参数以及接口返回值。

· 服务如何发布和订阅:注册中心

· 服务如何监控:就需要一种通用的监控方案,能够覆盖业务埋点、数据收集、数据处理,最后到数据展示的全链路功能。

· 服务如何治理:可以设定一个调用性能阈值,如果一段时间内一直超过这个值,那么依赖服务的调用可以直接返回,这就是熔断,也是服务治理最常用的手段之一。

· 故障如何定位:需要有一种解决方案能够将一次用户请求进行标记,并在多个依赖的服务系统中继续传递,以便串联所有路径,从而进行故障定位。

3. 初探微服务架构

首先服务提供者(就是提供服务的一方)按照一定格式的服务描述,向注册中心注册服务,声明自己能够提供哪些服务以及服务的地址是什么,完成服务发布。 接下来服务消费者(就是调用服务的一方)请求注册中心,查询所需要调用服务的地址,然后以约定的通信协议向服务提供者发起请求,得到请求结果后再按照约定的协议解析结果。 而且在服务的调用过程中,服务的请求耗时、调用量以及成功率等指标都会被记录下来用作监控,调用经过的链路信息会被记录下来,用于故障定位和问题追踪。在这期间,如果调用失败,可以通过重试等服务治理手段来保证成功率。

服务描述:服务调用首先解决的问题就是服务如何对外描述

常用的服务描述方式包括 RESTful API、XML 配置以及 IDL 文件三种:

· RESTful API 方式通常用于 HTTP/HTTPS 协议的服务描,并且常用 Wiki 或者 Swagger 来进行管理。下面是一个RESTful API 方式的服务描述的例子。

· clip_image001

· XML 配置方式多用作 RPC 协议的服务描述,通过 *.xml 配置文件来定义接口名、参数以及返回值类型等。下面是一个 XML 配置方式的服务描述的例子。

· clip_image002

· IDL 文件方式通常用作 Thrift 和 gRPC 这类跨语言服务调用框架中,比如 gRPC 就是通过 Protobuf 文件来定义服务的接口名、参数以及返回值的数据结构,示例如下。 service HelloService { rpc SayHello (HelloRequest) returns (HelloResponse); }

· message HelloRequest {

· string greeting = 1;

· }

·

· message HelloResponse {

· string reply = 1;

· }

注册中心(解决的问题就是服务的发布和订阅)

一般来讲,注册中心的工作流程是:

· 服务提供者在启动时,根据服务发布文件中配置的发布信息向注册中心注册自己的服务。

· 服务消费者在启动时,根据消费者配置文件中配置的服务信息向注册中心订阅自己所需要的服务。

· 注册中心返回服务提供者地址列表给服务消费者。

· 当服务提供者发生变化,比如节点新增或者销毁,注册中心将变更通知给服务消费者

clip_image003

服务框架

通过注册中心,服务消费者就可以获取到服务提供者的地址,有了地址后就可以发起调用。但在发起调用之前你还需要解决一下问题:

· 服务通信采用什么协议?就是说服务提供者和服务消费者之间以什么样的协议进行网络通信,是采用四层 TCP、UDP 协议,还是采用七层 HTTP 协议,还是采用其他协议?

· 数据传输采用什么方式?就是说服务提供者和服务消费者之间的数据传输采用哪种方式,是同步还是异步,是在单连接上传输,还是多路复用。

· 数据压缩采用什么格式?通常数据传输都会对数据进行压缩,来减少网络传输的数据量,从而减少带宽消耗和网络传输时间,比如常见的 JSON 序列化、Java 对象序列化以及 Protobuf序列化等。

服务监控(发现问题)

· 指标收集。就是要把每一次服务调用的请求耗时以及成功与否收集起来,并上传到集中的数据处理中心。

· 数据处理。有了每次调用的请求耗时以及成功与否等信息,就可以计算每秒服务请求量、平均耗时以及成功率等指标。

· 数据展示。数据收集起来,经过处理之后,还需要以友好的方式对外展示,才能发挥价值。通常都是将数据展示在 Dashboard 面板上,并且每隔 10s 等间隔自动刷新,用作业务监控和报警等。

服务追踪(定位问题):记录服务调用经过的每一层链路,以便进行问题追踪和故障定位。

服务追踪的工作原理大致如下:

· 服务消费者发起调用前,会在本地按照一定的规则生成一个 requestid,发起调用时,将 requestid 当作请求参数的一部分,传递给服务提供者。

· 服务提供者收到请求后,记录下这次请求的 requestid,然后处理请求。如果服务提供者继续请求其他服务,会在本地再生成一个 requestid,然后把这两个requestid 都当作请求参数继续往下传递。

以此类推,通过这种层层往下传递的方式,一次请求,无论最后依赖多少次服务调用、经过多少服务节点,都可以通过最开始生成的 requestid 串联所有节点,从而达到服务追踪的目的。

服务治理(解决问题):通过一系列的手段来保证在各种意外情况下,服务调用仍然能够正常进行。

在生产环境中,你应该经常会遇到下面几种状况。

· 单机故障:通过一定的策略,自动摘除故障节点,不需要人为干预,就能保证单机故障不会影响业务。

· 单 IDC 故障:通过自动切换故障 IDC 的流量到其他正常 IDC,可以避免因为单 IDC 故障引起的大批量业务受影响。

· 依赖服务不可用:通过限流,在依赖服务异常的情况下,一段时期内停止发起调用而直接返回。这样一方面保证了服务消费者能够不被拖垮,另一方面也给服务提供者减少压力,使其能够尽快恢复。