系列文章来源于 学习《极客时间——从0开始学习微服务》分享之后笔记载录和读后感。 作者胡忠想,微博技术专家。 从 2012 年加入微博到现在,从 2012 年加入微博到现在,我一直在做微博首页信息流相关的业务研发,几乎亲历了微博后端架构的每一次重大升级。不仅参与了微博后端架构从大的单体应用迁移到微服务架构的改造;还作为主要负责人之一,主导了微服务架构在公司多个业务线的推广和落地,作者有很多实战干货想和你分享。
服务描述:服务调用首先解决的问题就是服务如何对外描述。 常用的服务描述方式包括 RESTful API、XML 配置以及 IDL 文件三种。
主要被用作 HTTP 或者 HTTPS 协议的接口定义,即使在非微服务架构体系下,也被广泛采用
一般是私有 RPC 框架会选择 XML 配置这种方式来描述接口,因为私有 RPC 协议的性能比 HTTP 协议高,所以在对性能要求比较高的场景下,采用 XML 配置比较合适。这种方式的服务发布和引用主要分三个步骤:
优势:
IDL 就是接口描述语言(interface description language)的缩写,通过一种中立的方式来描接口,使得在不同的平台上运行的对象和不同语言编写的程序可以相互通信交流。常用的 IDL:一个是 Facebook 开源的 Thrift 协议,另一个是 Google 开源的 gRPC 协议。无论是 Thrift 协议还是 gRPC 协议,他们的工作原来都是类似的。
优势:
劣势:
具体采用哪种服务描述方式是根据实际情况决定,通常情况下, 如果只是企业内部之间的服务调用,并且都是 Java 语言的话,选择 XML 配置方式是最简单的。如果企业内部存在多个服务,并且服务采用的是不同语言平台,建议使用 IDL 文件方式进行描述服务。如果还存在对外开放服务调用的情形的话,使用 RESTful API 方式则更加通用。
在微服务架构下, 主要有三种角色:服务提供者(RPC Server)、服务消费者(RPC Client)和服务注册中心(Registry),三者的交互关系如图
注册中心一般都是采用集群部署来保证高可用性,并通过分布式一致性协议来确保集群中不同节点之间的数据保持一致。
注册中心可以说是实现服务话的关键,因为服务话之后,服务提供者和服务消费者不在同一个进程中运行,实现了解耦,这就需要一个纽带去连接服务提供者和服务消费者,而注册中心就正好承担了这一角色。此外,服务提供者可以任意伸缩即增加节点或者减少节点,通过服务健康状态检测,注册中心可以保持最新的服务节点信息,并将变化通知给订阅服务的服务消费者。
注册中心一般采用分布式集群部署,来保证高可用性,并且为了实现异地多活,有的注册中心还采用多 IDC 部署,这就对数据一致性产生了很高的要求,这些都是注册中心在实现时必须要解决的问题。
HTTP 通信是基于应用层HTTP 协议的,而 HTTP 协议又是基于传输层 TCP 协议的。一次 HTTP 通信过程就是发起一次 HTTP 调用,而一次 HTTP 调用就会建立一个 TCP 连接,经历一次下图所示的 “三次握手”的过程来建立连接。
完成请求后,再经历一次“四次挥手”的过程来断开连接。
Socket 通信是基于 TCP/IP 协议的封装,建立一次Socket 连接至少需要一对套接字,其中一个运行于客户端,称为 ClientSocket ;另一个运行于服务器端,称为 ServerSocket 。
当客户端和服务端建立网络连接后,就可以起发起请求了。但网络不一定总是可靠的,经常会遇到网络闪断、连接超时、服务端宕机等各种异常,通常的处理手段有两种:
最为稳妥的方式是使用成熟的开源方案,比如 Netty、MINA 等,它们都是经过业界大规模应用后,被充分论证是很可靠的方案。
无论是开放的还是私有的协议,都必须定义一个“契约”,以便服务消费和服务提供者之间能够达成共识。服务消费者按照契约,对传输的数据进行编码,然后通过网络传输过去;服务提供者从网络上接收到数据后,按照契约,对传输的数据进行解码,然后处理请求,再把处理后的结果进行编码,通过网络传输返回给服务消费者;服务消费者再对返回的结果进行解码,最终得到服务提供者处理后的返回值。
一般数据在网络中进行传输,都要先在发送方一段对数据进行编码,经过网络传输到达另一段后,再对数据进行解码,这个过程就是序列化和反序列化
常用的序列化方式分为两类:文本类如 XML/JSON 等,二进制类如 PB/Thrift 等,而具体采用哪种序列化方式,主要取决于三个方面的因素。
这三部分就组成了一个完成的RPC 调用框架,通信框架提供了基础的通信能力,通信协议描述了通信契约,而序列化和反序列化则用于数据的编/解码。一个通信框架可以适配多种通信协议,也可以采用多种序列化和反序列化的格式,比如服务话框架 不仅支持 Dubbo 协议,还支持 RMI 协议、HTTP 协议等,而且还支持多种序列化和反序列化格式,比如 JSON、Hession 2.0 以及 Java 序列化等。
在谈论监控微服务监控调用前,首先要搞清楚三个问题:监控的对象是什么?具体监控哪些指标?从哪些维度进行监控?
对于一个微服务来说,必须要明确监控哪些对象、哪些指标,并且还要从不同的维度进行监控,才能掌握微服务的调用情况。
不管是哪种方式,首先要考虑的问题就是采样率,也就是采集数据的频率。一般来说,采样率越高,监控的实时性就越高,精确度也越高。但采样对系统本身的性能也会有一定的影响,尤其是采集后的数据需要写到本地磁盘的时候,过高的采样率会导致系统写入的 I/O 过高,进而会影响到正常的服务调用。所以合理的采样率是数据采集的关键,最好是可以动态控制采样率,在系统比较空闲的时候加大采样率,追求监控的实时性与精确度;在系统负载比较高的时候减少采样率,追求监控的可用性与系统的稳定性。
无论哪种传输方式,数据格式十分重要,尤其是对带宽敏感以及解析性能要求比较高的场景,一般数据传输时采用的数据格式有两种:
聚合后的数据需要持久化到数据库中存储,所选用的数据库一般分为两种:
跟踪记录一次用户请求都发起了哪些调用,经过哪些服务处理,并且记录每一次调用所涉及的详细信息,这时候如果发生调用失败,就可以通过这个日志快速定位是在哪个环节出了问题。
服务追踪原理
服务追踪鼻祖:Google 发布的一篇的论文Dapper, [a Large-Scale Distributed Systems Tracing Infrastructure
讲解下服务追踪系统中几个最基本概念
traceId 是用于串联某一次请求在系统中经过的所有路径,spanId 是用于区分系统不同服务之间调用的先后关系,而annotation 是用于业务自定义一些自己感兴趣的数据,在上传 traceId 和 spanId 这些基本信息之外,添加一些自己感兴趣的信息。
上面是服务追踪系统架构图,一个服务追踪系统可以分三层:
作用:在系统的各个不同的模块中尽心埋点,采集数据并上报给数据处理层进行处理。
作用:把数据上报的数据按需计算,然后落地存储供查询使用
作用:将处理后的链路信息以图形化的方式展示给用户和做故障定位
一次服务调用,服务提供者、注册中心、网络这三者都可能会有问题,此时服务消费者应该如何处理才能确保调用成功呢?这就是服务治理要解决的问题。
无论是服务哪种原因,都有两种节点管理手段:
这种机制要求服务提供者定时的主动向注册中心汇报心跳,注册中心根据服务提供者节点最近一次汇报心跳的时间与上一次汇报心跳时间做比较,如果超出一定时间,就认为服务提供者出现问题,继而把节点从服务列表中摘除,并把最近的可用服务节点列表推送给服务消费者。
虽然注册中心主动摘除机制可以解决服务提供者节点异常的问题,但如果是因为注册中心与服务提供者之间的网络出现异常,最坏的情况是注册中心会把服务节点全部摘除,导致服务消费者没有可能的服务节点调用,但其实这时候提供者本身是正常的。所以,将存活探测机制用在服务消费者这一端更合理,如果服务消费者调用服务提供者节点失败,就将这个节点从内存保存的可用夫提供者节点列表一处。
常用的负载均衡算法主要包括以下几种:
常用的手段主要有以下几种:
一般对于幂等的调用可以选择 FailOver 或者 FailCache,非幂等的调用可以选择 Failback 或者 FailFast