在这里插入图片描述

在复杂时序指标(接口耗时、QPS、错误率、CPU 使用率等)中,除了平滑的趋势变化外,还会出现各种“阶段性抬升”和“短时突刺”:

某一时间段内 RT 突然整体抬高了一截,是永久性退化还是短期事件?
错误率是否在某个阶段阶跃式升高?
QPS 是否出现了超过阈值的一段“洪峰区间”?

本案例基于 Kotlin Multiplatform(KMP)与 OpenHarmony,实现了一个累积和变化点检测与突刺分析器

  • 使用前缀和(累积和)构造基础的阶段性统计;
  • 对前后两个窗口的平均值做差,粗略识别“阶段平均水平跃迁点”;
  • 根据阈值识别连续的“突刺区间”(所有点都超过指定阈值);
  • 通过 ArkTS 单页面展示原始序列、累积和序列、变化点与突刺区间说明。

一、问题背景与典型场景

在 AIOps 与性能排障中,常见的问题类型包括:

  1. 阶段性退化 / 升级
    某次发布后接口耗时整体抬升了几十毫秒,需要验证“发布前后阶段”的平均水平变化是否显著。

  2. 持续性高错误率时段
    某一段时间错误率持续高于告警阈值,需要精确圈定这段“高错误时段”进行根因排查。

  3. 洪峰流量区间
    QPS 在一小段时间内连续高于某个阈值,可能压垮下游系统,需要提前发现和扩容。

  4. 业务指标阶跃变化
    某一版本上线后转化率、点击率等指标出现阶跃式变化,需要快速定位“变化点”并进行归因分析。

这些问题可以抽象为:

给定时序数据 \( x_0, x_1, \dots, x_{n-1} \) 与阈值 \( T \),
识别:

  • 前后两个阶段平均值发生显著变化的位置(变化点);
  • 连续超过阈值 \( T \) 的“突刺区间”。

二、Kotlin 累积和变化点分析引擎

1. 输入格式设计

本案例继续采用统一的文本配置风格:

threshold=80
series=10,15,20,18,22,95,120,110,30,28,26
  • threshold:用于判断阶段平均值跃迁与突刺区间的数值阈值;
  • series:一串以 , / 空格 / ; / 换行分隔的数值样本。

解析时亦支持将样本拆分为多行,只要保持可被解析为 Double 即可。


2. Kotlin 分析主入口

App.kt 中,我们定义了对外暴露的分析函数,并通过 @JsExport 让 OpenHarmony 端可以直接调用:

@JsExport
fun cumulativeChangePointAnalyzer(inputData: String): String {
    val sanitized = inputData.trim()
    if (sanitized.isEmpty()) {
        return "❌ 输入为空,请按 threshold=80\\nseries=10,15,20,... 形式提供数据"
    }

    val lines = sanitized.lines()
        .map { it.trim() }
        .filter { it.isNotEmpty() }

    var threshold: Double? = null
    val values = mutableListOf<Double>()

    for (line in lines) {
        when {
            line.startsWith("threshold=", ignoreCase = true) -> {
                threshold = line.substringAfter("=").trim().toDoubleOrNull()
            }
            line.startsWith("series=", ignoreCase = true) -> {
                val parsed = line.substringAfter("=")
                    .split(",", " ", ";", "\n")
                    .mapNotNull { it.trim().takeIf { s -> s.isNotEmpty() }?.toDoubleOrNull() }
                values += parsed
            }
            else -> {
                val parsed = line.split(",", " ", ";", "\n")
                    .mapNotNull { it.trim().takeIf { s -> s.isNotEmpty() }?.toDoubleOrNull() }
                values += parsed
            }
        }
    }

    if (values.isEmpty()) {
        return "❌ 未解析到任何数值,请检查 series=10,15,20,... 的格式是否正确"
    }
    if (threshold == null) {
        return "❌ 未找到 threshold=... 配置,请提供阶段突刺判断的阈值(如 80)"
    }

    val n = values.size
    val prefix = DoubleArray(n + 1)
    for (i in values.indices) {
        prefix[i + 1] = prefix[i] + values[i]
    }

    // 简单的“阶段平均值变化”检测:以滑动窗口形式比较前后两段的均值差异
    val window = max(3, n / 5) // 至少 3 个点,约 1/5 长度
    data class ChangePoint(val index: Int, val beforeAvg: Double, val afterAvg: Double, val delta: Double)
    val changePoints = mutableListOf<ChangePoint>()

    for (i in window until n - window) {
        val beforeSum = prefix[i] - prefix[i - window]
        val afterSum = prefix[i + window] - prefix[i]
        val beforeAvg = beforeSum / window
        val afterAvg = afterSum / window
        val delta = afterAvg - beforeAvg
        if (kotlin.math.abs(delta) >= threshold!!) {
            changePoints += ChangePoint(i, beforeAvg, afterAvg, delta)
        }
    }

    // 基于阈值的突刺区间: 找出连续超过 threshold 的段
    val spikeThreshold = threshold!!
    val spikeRanges = mutableListOf<Pair<Int, Int>>() // [start, end]
    var currentStart: Int? = null
    for (i in values.indices) {
        if (values[i] >= spikeThreshold) {
            if (currentStart == null) currentStart = i
        } else {
            if (currentStart != null) {
                spikeRanges += currentStart!! to (i - 1)
                currentStart = null
            }
        }
    }
    if (currentStart != null) {
        spikeRanges += currentStart!! to (n - 1)
    }

    val builder = StringBuilder()
    builder.appendLine("📈 累积和变化点检测与突刺分析报告")
    builder.appendLine("━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
    builder.appendLine("样本数量: $n")
    builder.appendLine("变化阈值 (threshold): $threshold")
    builder.appendLine()

    builder.appendLine("🧮 原始序列")
    builder.appendLine(values.joinToString(prefix = "[", postfix = "]"))
    builder.appendLine()

    builder.appendLine("📊 累积和序列 (前缀和)")
    builder.appendLine(prefix.drop(1).joinToString(prefix = "[", postfix = "]") { round2(it).toString() })
    builder.appendLine()

    builder.appendLine("🔍 阶段平均值变化点(前后窗口均值差超出阈值)")
    if (changePoints.isEmpty()) {
        builder.appendLine("未检测到显著的阶段平均值变化点")
    } else {
        changePoints.forEach {
            builder.appendLine("索引 ~${it.index} 附近: 前窗口均值=${round2(it.beforeAvg)}, " +
                "后窗口均值=${round2(it.afterAvg)}, Δ=${round2(it.delta)}")
        }
    }
    builder.appendLine()

    builder.appendLine("🚨 突刺区间(连续值超过阈值 threshold 的区段)")
    if (spikeRanges.isEmpty()) {
        builder.appendLine("未检测到超过阈值 $threshold 的连续突刺区间")
    } else {
        spikeRanges.forEach { (start, end) ->
            val segment = values.subList(start, end + 1)
            builder.appendLine("区间 [$start, $end] -> ${segment.joinToString(prefix = \"[\", postfix = \"]\")}")
        }
    }

    builder.appendLine()
    builder.appendLine("🧠 工程化解读")
    builder.appendLine("- 通过前缀和和窗口均值对比,可以粗略识别“前后两个阶段”平均水平的跃迁;")
    builder.appendLine("- 结合连续超过阈值的突刺区间,有助于锁定一段时间内 RT/QPS/错误率的异常抬升段;")
    builder.appendLine("- 在生产中可以与分位数、直方图、滑动平均等模块联动,构建更完整的时序异常画像。")

    return builder.toString().trim()
}

通过这种实现,我们利用“累积和 + 窗口均值差”给出了一个简单可解释的阶段变化检测方案,并结合阈值定义的突刺区间为运维分析提供直接线索。


三、OpenHarmony 侧调用与 UI 展示思路

在 ArkTS 侧,可以像前面案例一样从 hellokjs 导入分析函数:

import { cumulativeChangePointAnalyzer } from './hellokjs'

页面状态可以设计为:

  • threshold: 突刺与阶段变化判断阈值(如 "80");
  • seriesInput: 时序样本数据,如 "10,15,20,18,22,95,120,110,30,28,26"

组装 payload 并调用:

const seriesLine = this.seriesInput.includes('series=') ? this.seriesInput : `series=${this.seriesInput}`
const payload = `threshold=${this.threshold}\n${seriesLine}`
this.result = cumulativeChangePointAnalyzer(payload)

展示层可以:

  • 使用等宽字体输出“原始序列 + 累积和序列 + 变化点列表 + 突刺区间列表”;
  • 对“🔍 阶段平均值变化点”“🚨 突刺区间”段落使用更醒目的颜色或背景;
  • 如有需要,可以在 ArkTS 侧进一步对原始曲线做简单绘制,并用竖线/阴影标出变化点与突刺区间。

四、复杂度与工程实践建议

复杂度分析:

  • 前缀和计算:\( O(n) \);
  • 窗口平均变化检测:窗口大小固定为 \( w \),每个候选位置常数级运算,总体 \( O(n) \);
  • 突刺区间扫描:单次线性扫描,\( O(n) \);
  • 整体时间复杂度 \( O(n) \),空间复杂度 \( O(n) \)(主要为前缀和与结果存储)。

工程实践上的扩展方向:

  1. 多阈值分级突刺
    支持多档阈值(如告警、严重告警),对突刺区间进行严重程度分级。

  2. 与移动平均/分位数组合
    先用移动平均或分位数平滑掉短期噪声,再在平滑后的序列上做变化点与突刺检测,减少误报。

  3. 统计检验增强
    将简单阈值判断升级为 t 检验、CUSUM 等更严谨的统计方法,提高变化点检测的置信度。

  4. 设备端实时监控
    将本案例嵌入 OpenHarmony 终端,实现本地化的轻量级变化点检测,在弱网场景下也能及时发现异常阶段。

通过这个累积和变化点检测与突刺分析案例,你可以在终端设备上快速捕捉“阶段性退化”和“突刺区间”,
与此前的分位数、直方图、滑动平均等案例形成一套完整的时序诊断工具箱。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐