尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

分布式服务日常巡检应先看什么

分布式服务日常巡检应先看什么 分布式服务日常巡检应先看什么巡检的目标不是产生更多告警而是用少量可操作的信号发现风险。服务变多后应把采集频率、告警分级和抑制规则作为同一项设计。排查时运维工程师需要逐个登录 30 多个微服务节点查看/actuator/health再去 Kafka 控制台对比消费位点最后到 Redis 集群上跑bigkeys诊断。一套人工巡检走下来要花整整 3 个小时。很多时候隐性故障如由于分布式事务补偿逻辑残留导致的某张业务表中死锁数据静默积累在发展成全面瘫痪前根本无法被粗粒度的健康检查发现。1. 拆分后分布式隐患现场诊断证据链在一次针对分布式消费队列与缓存集群的无预警抽查中跳板机上的诊断脚本捕获到了极具代表性的“静默倾斜”现场。通过命令行工具提取消息队列、缓存与数据库事务锁的健康度证据# 1. 检查 Kafka 消费者组 Lag 积压情况与消费速率 kafka-consumer-groups.sh --bootstrap-server kafka.infra.internal:9092 \ --describe --group order-process-group # 2. 扫描 Redis 集群中的 BigKey 与慢命令执行日志 redis-cli -h redis-cluster.infra.internal -p 6379 --bigkeys redis-cli -h redis-cluster.infra.internal -p 6379 SLOWLOG GET 10 # 3. 扫描 MySQL 集中库中的分布式长事务与未提交事务锁 mysql -h db-master.infra.internal -u root -p -e SELECT t.trx_id, t.trx_state, t.trx_started, TIMESTAMPDIFF(SECOND, t.trx_started, NOW()) AS duration_sec, t.trx_query FROM information_schema.innodb_trx t WHERE TIMESTAMPDIFF(SECOND, t.trx_started, NOW()) 10;诊断输出揭示了三个隐蔽的分布式坑点# Kafka Consumer Group 报告Partition 3 的 Lag 居然积压了 42,000 条 GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG order-process-group order-created 3 1029480 1071480 42000 # Redis SLOWLOG 报告hgetall 导致线程阻塞 120ms 1) 1) (integer) 482 2) (integer) 1724142100 3) (integer) 120400 4) 1) HGETALL 2) cache:tenant:order:hashmap # MySQL 长事务报告存在运行超过 45 秒未提交的 Seata 分布式事务 trx_id: 894102, duration_sec: 45, trx_query: UPDATE account_tbl SET money money - 100 WHERE user_id U1002服务拆分后每个单体看似正常但由于 Kafka 某个分区消费倾斜、Redis 散列表过大导致单线程卡顿、分布式事务锁持有时间过长分布式系统的整体可用性正在被静默侵蚀。2. 分布式系统健康度自动化巡检架构为了摆脱低效的手工巡检我们设计了轻量级的分布式健康度自动化巡检与预警架构。巡检架构的核心设计原则并发并行探测使用 CompletableFuture / Reactor 并发并行拉取 30 节点指标将巡检总耗时从 3 小时收敛至 10 秒以内。深度业务指标注入超越简单的 TCP PING深入检查分布式事务补偿表积压量、 Kafka Partition 倾斜率与 BigKey 增长速率。差分告警与日报归档只在指标出现“趋势性恶化”如 Lag 连续 3 次翻倍时触发高优先级告警避免告警风暴正常数据自动汇总为 HTML 日报发送给团队。3. 生产级 Kafka Lag 巡检器与并发 Health 探针代码以下为自定义的 Kafka Consumer Group Lag 巡检 Indicator 与自动化审计组件package com.architecture.distributed.inspection.kafka; import org.apache.kafka.clients.admin.AdminClient; import org.apache.kafka.clients.admin.ConsumerGroupDescription; import org.apache.kafka.clients.admin.ListConsumerGroupOffsetsResult; import org.apache.kafka.clients.consumer.OffsetAndMetadata; import org.apache.kafka.common.TopicPartition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; import java.util.Map; import java.util.concurrent.TimeUnit; Component public class KafkaConsumerLagHealthIndicator implements HealthIndicator { private static final Logger log LoggerFactory.getLogger(KafkaConsumerLagHealthIndicator.class); private static final String TARGET_GROUP order-process-group; private static final long MAX_ALLOWED_LAG 5000L; private final AdminClient kafkaAdminClient; public KafkaConsumerLagHealthIndicator(AdminClient kafkaAdminClient) { this.kafkaAdminClient kafkaAdminClient; } Override public Health health() { try { // 1. 获取消费者组的 Offset 信息 ListConsumerGroupOffsetsResult offsetsResult kafkaAdminClient.listConsumerGroupOffsets(TARGET_GROUP); MapTopicPartition, OffsetAndMetadata offsets offsetsResult.valid().get(3, TimeUnit.SECONDS); long totalLag 0; long maxPartitionLag 0; TopicPartition worstPartition null; // 2. 解析每个 Partition 的 Lag 积压 for (Map.EntryTopicPartition, OffsetAndMetadata entry : offsets.entrySet()) { long currentOffset entry.getValue().offset(); // 简化示范实际中可通过 adminClient.listOffsets 获取 LogEndOffset 进行求差 long logEndOffset currentOffset getEstimatedPartitionLag(entry.getKey()); long partitionLag logEndOffset - currentOffset; totalLag partitionLag; if (partitionLag maxPartitionLag) { maxPartitionLag partitionLag; worstPartition entry.getKey(); } } if (totalLag MAX_ALLOWED_LAG) { return Health.down() .withDetail(reason, Kafka 消费者组 Lag 严重积压) .withDetail(targetGroup, TARGET_GROUP) .withDetail(totalLag, totalLag) .withDetail(worstPartition, worstPartition ! null ? worstPartition.toString() : N/A) .withDetail(maxPartitionLag, maxPartitionLag) .build(); } return Health.up() .withDetail(targetGroup, TARGET_GROUP) .withDetail(totalLag, totalLag) .build(); } catch (Exception e) { log.error(Kafka Lag 巡检探测失败, e); return Health.unknown().withException(e).build(); } } private long getEstimatedPartitionLag(TopicPartition tp) { // 探针模拟逻辑返回当前分区的估算 Lag return 120L; } }配合 Kafka Lag 探针编写 Shell 并发巡检脚本10 秒内抓取整个集群所有微服务节点的/actuator/health并生成状态矩阵#!/usr/bin/env bash # parallel_cluster_health_check.sh - 30 节点分布式并发巡检脚本 set -euo pipefail # 微服务节点 IP 列表 NODES( ${PRIMARY_INSTANCE_1} ${PRIMARY_INSTANCE_2} ${PRIMARY_INSTANCE_3} ${CANARY_INSTANCE_1} ${CANARY_INSTANCE_2} ${CANARY_INSTANCE_3} ) REPORT_FILE/tmp/health_report_$(date %Y%m%d_%H%M%S).log echo 分布式集群健康度自动化巡检报告 ($(date)) ${REPORT_FILE} check_node() { local node$1 local urlhttp://${node}/actuator/health # 设置 2 秒超时防止某个挂死的节点阻塞全局巡检 local response response$(curl -s --max-time 2 ${url} || echo {status:DOWN,reason:TIMEOUT}) local status status$(echo ${response} | grep -o status:[^]* | head -n 1 | cut -d -f4 || echo UNKNOWN) if [ ${status} UP ]; then echo [OK] Node: ${node} - STATUS: UP else echo [FAIL] Node: ${node} - STATUS: ${status} | Resp: ${response} fi } export -f check_node # 使用 xargs -P 开启 10 线程并发巡检 printf %s\n ${NODES[]} | xargs -I {} -P 10 bash -c check_node $ _ {} ${REPORT_FILE} echo 巡检完成报告已归档至 ${REPORT_FILE} cat ${REPORT_FILE} | grep [FAIL] || echo 集群所有节点健康度 100% 通过!4. 日常巡检落地后的防线原则自动化巡检体系落地后我们建立了三条分布式演进防线拒绝“全量成功”盲目自信巡检指标不仅看status: UP更要看趋势离群值。一旦某台机器的响应延迟比同组其他机器高出 3 倍巡检引擎直接标记为DEGRADED并发出警告。死锁与未提交事务巡检收口在 MySQL 侧开启定时巡检任务每 5 分钟扫描一次运行超过 10 秒的 InnoDB 事务自动 kill 挂死的分布式补偿事务。巡检脚本版本化与 CI 绑定所有的巡检 Shell 与 Java Indicator 代码必须随微服务代码一起迭代提交禁止运维在宿主机上维护“个人私有脚本”。把隐患消灭在日常 10 秒钟的自动化巡检里远比深夜被告警电话惊醒后再去翻日志体面得多。
返回列表