背景
在微服务架构中,服务心跳是一个简单但非常重要的机制,用于确认微服务的存活状态。UAVStack 中的心跳是一个 Http 请求,MonitorAgent(以下简称 MA)通过定时向 HealthManager(以下简称 HM)发送一个带有特定报文格式的 Http 请求完成一次心跳的发送过程。心跳报文含有发送时的时间戳,用于更新 HM 端的数据状态。
与普通的心跳不同,UAVStack 中的心跳还负责上送 MA 端的应用容器和进程监控数据。每次发送心跳的时候,在 MA 端会有定时任务去收集 MA 所在的应用容器心跳的基本信息,及应用容器上的进程数据,随着心跳数据包一起上送。
本文将首先介绍 UAVStack 的基础心跳机制,之后对应用容器、进程的数据采集做详细说明。
基本架构
心跳的实现有很多种方式,心跳的发起可以由客户端发起也可以由服务端发起,只需完成确认存活这一基本功能即可。但是在一般的实现中,我们更倾向于客户端主动向服务端进行报告,因为当客户端逐渐增加,单纯通过服务端的轮询会导致服务端的压力,影响性能。
在 UAVStack 的实现中,我们也采用了这样的方式,通过客户端(MA)主动向服务端(HM)发送心跳信息,告知 HM 自身的存活情况。
一次心跳由 UAV 的 MA 和 HM 共同完成:
MA 定时生成心跳数据,携带 MA 节点的应用容器信息、进程信息以及服务信息,通过 Http 请求上报给 HM;
HM 负责将接收到的心跳数据存入 Redis 缓存****,并定时扫描心跳数据,确认节点的存活状态。对于随同的应用容器等监控信息,会在 Redis 进行暂存,后续随着 HM 的定时任务最终存入 OpenTSDB 进行落盘。整体的架构如下所示:
基础心跳机制
心跳服务主要流程如上图所示,其逻辑有以下几步:
MA 的定时心跳任务生成一个空心跳数据,将心跳数据交给 MA 端的容器、进程数据采集任务。
MA 端的容器、进程数据采集任务负责产生心跳数据的时间戳、采集节点的应用容器、进程监控数据、节点的基本信息、节点的可用服务信息等。经过以上过程之后,心跳数据将包含以下内容:
心跳时间戳:节点发送心跳时的时间戳,方便 HM 后续比对判定节点的存活状态。
应用容器的基本信息:包括节点 ID、名称、主机名、IP 等。
应用容器的简单监控数据:包括 CPU 负载、内存使用、硬盘使用等。
应用容器上的进程信息:包括每个进程的 pid、资源占用等。
节点的能力信息:包括该节点上启用的 Feature 功能。
节点的服务信息:包括该节点上可提供的服务及其访问接口,用以实现服务发现。
(可选)子节点的节点信息:如果 MA 和 HM 部署在不同的网段,则 MA 无法通过直接 Http 的方式推送数据给 HM,此时需要 MA 将数据上送给自己网段内的 HM,再由该 HM 的心跳客户端发送给总的 HM。这种情况下,上报的数据中会携带子节点的节点信息。每个节点信息均会包含上述几种数据。
最后将心跳数据发送给 HM。
HM 端在接收到心跳数据之后,将其存入自身的 Redis 缓存。使用上报数据中的服务信息更新 Redis 中的服务状态,用于服务发现请求。
HM 端在启动心跳接收服务时,会同时启动心跳检查任务。这个任务会定时扫描 Redis 中的心跳数据,根据当前系统时间与心跳时间戳的差,判断心跳节点的存活状态,更新节点的状态,并对于过期的节点做删除处理。
应用容器、进程数据采集
UAV 的心跳数据除了完成心跳功能之外,还要上报节点的应用容器及进程的监控数据。
将应用容器与进程数据通过 Http 方式上报是为了保证应用容器监控数据与应用监控数据的隔离,通过不同方式的上送可以保证在 MQ 服务不能使用时不影响容器与进程数据的采集。
本节将集中说明这些数据的采集细节。
1. 应用容器数据采集
应用容器的数据分为两部分:
其一是容器的基本信息,即节点的 ID,主机名,系统信息和 JVM 信息等;
另一部分是一些简单的实时监控采集数据,包括 CPU 的负载、内存占用情况和磁盘占用情况等。这些数据在每次上报心跳数据的时候会分别从以下数据源实时采集:
应用启动后的 System.getProperty:这部分数据主要包括操作系统的基本信息,JVM 信息等。
Java 提供的工具类:这部分主要包括网卡信息。
通过 JMX 获取的信息:包括 CPU 占用、内存占用等。
系统本身记录的信息:这部分包括可提供的服务信息、启动的 Feature 信息、节点 ID 等。
通过执行系统命令得到的信息:包括磁盘占用情况。
通过直接读取/proc 目录下的文件获取的信息:包括 CPU 占用、内存占用等。
2. 进程数据采集
不同于应用容器数据采集,进程的数据并不是在心跳进程中进行采集的,而是由专门的 Feature 负责。在 Feature 中将进程数据采集进一步分解成进程端口流量数据采集以及其他数据采集。这两者均由定时任务完成,互相协作,最终由进程探测的定时任务更新心跳客户端的进程数据。
这种使用多个采集任务分别采集的方式可以针对不同的数据进行不同频度的采集。如对于网络端口流量的采集,就可以以更长的周期进行,以减低数据采集带来的性能损耗。同时,不同的任务也可以使用不同的线程执行,提升执行的效率。
进程数据采集流程大致如下图所示:
进程端口流量探测定时任务每隔一定时间读取本地变量端口列表,获取要采集的端口号。
之后对于 Windows 环境,采用 JPcap 获取网卡对象,并在网卡上设置 tcp 过滤器来统计一段时间内的端口流量。对于 Linux 环境则是直接通过调用 Python 脚本打开 socket,分析流过的数据包获得。
获得全部端口上的流量数据后,任务会将采集数据交给进程数据采集任务,更新其本地变量,同时设置本次采集的时间戳。
进程探测定时任务由一系列子任务构成,在任务开始的时候,会先准备好一个 Map 结构的数据容器,用于存放采集到的进程信息,每个进程由 pid 区分,作为 Map 的 key。
任务会先扫描所有的进程,获取 pid 和进程的端口。扫描到的进程会经过一个过滤器排除不需要采集数据的进程,之后正式采集每个进程上的数据。
对于每一个进程,会通过运行系统命令采集连接数、CPU、内存占用,磁盘读写数据以及网络端口流量数据。其中网络端口流量数据是由端口流量探测任务采集并更新的本地变量,而进程探测任务也会将扫描到的最新的端口列表更新到端口流量探测任务的本地变量。
如果应用是部署在容器上的,则还会有对应的容器信息采集。最后进程探测任务会将采集到的进程数据更新到心跳客户端的本地变量,随着每次心跳数据的生成被一起采集并上报。
进程数据的采集分别来自以下数据源:
系统命令:包括 CPU、内存、连接数等(top 等命令)
/proc 目录下各进程子目录:包括 CPU、内存等信息、磁盘读写等信息
执行脚本:包括 Linux 环境下的端口流量数据采集
第三方工具包:包括 win 环境下的端口流量数据采集(JPcap)
HM****处理
心跳数据和容器数据在通过 Http 上送到 HM 端之后,会由 HM 端对应的服务进行处理。
HM 在启动时会启动自己的心跳客户端,负责发送本机的心跳数据和采集 HM 所在容器的监控数据。同时还会启动一个心跳服务,负责接收处理所有上送的心跳和容器数据信息。
心跳服务在收到心跳数据请求后,会根据 HM 的配置,判定当前的 HM 是不是 Master 节点。如果 HM 是 Master 节点,心跳服务会从 Http 携带的报文中拿出上报的数据,取得上报节点中的可用服务用于更新服务发现信息,之后将数据存入后端的 Redis 缓存中;如果不是 Master 节点,则会将数据移交至本机的心跳客户端,由其下次发送心跳时一起上送。
这样的设计是考虑到大规模监控时会有跨机房的情况存在,此时各监控节点往往不在同一个网段内,通过将同一个网段内的机器上交到边界的“网关”统一上交可解决这一问题。此时的 HM 即充当着“网关”这一角色。
HM 在启动的时候同时还会启动一个定时任务,这个任务负责处理各节点的存活状况。任务定时从 Redis 中读取全部心跳数据,依次检查上送心跳数据中的客户端时间戳与当前系统时间戳的差值。
当时间超过一定的上送时间间隔之后,更改对应的节点存活状态。当超过一倍上送时间间隔,意味节点可能死亡,处于 dying 状态。当超过两倍时间间隔时,意味着节点已经死亡。当超过三倍时间间隔时,心跳服务会删除该节点的缓存记录。
随心跳一起上报的容器和进程数据会随着心跳数据一同被存入 Redis 中,后续由 HM 的其他定时任务读取并发送给预警中心进行处理,最终监控指标被格式化成特定的结构存入 OpenTSDB。
同时采集的容器数据和进程数据会提供前端 AppHub 查看界面,如图所示:
点击页面上的每一个节点,可以查看详细的节点信息,包括节点的操作系统信息、JVM 信息、提供的服务和安装的 Feture 等等。这些也就是前文所说的随心跳数据上报的那部分信息。如图所示:
总结
心跳是微服务架构基础但重要的机制,通过定时发送心跳数据,MA 节点报告了自身的存活状态,使得 HM 能够知晓当前系统的运行状态。
同时,UAVStack 的心跳数据还同时负责上报节点的容器及进程监控数据,随着这些数据的上报,HM 可以对监控的容器和进程做出预警,也能够在前端实时看到应用容器和进程的运行状态。
本文转载自宜信技术学院网站。
原文链接:http://college.creditease.cn/detail/214
评论