Posted in

Go流程监控告警体系搭建:Prometheus自定义指标+Alertmanager静默策略实战

第一章:Go流程监控告警体系搭建:Prometheus自定义指标+Alertmanager静默策略实战

在高可用Go服务中,精细化的流程级监控与可控的告警响应是稳定性保障的核心。本章聚焦于从零构建面向业务逻辑的监控闭环:在Go应用中暴露关键流程指标,由Prometheus采集并触发告警,再通过Alertmanager实现基于上下文的智能静默。

在Go应用中定义并暴露自定义指标

使用prometheus/client_golang库注册流程维度指标。例如,统计HTTP请求处理各阶段耗时及异常路径:

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

// 定义流程耗时直方图(按handler名、状态码、错误类型分片)
requestDuration := prometheus.NewHistogramVec(
    prometheus.HistogramOpts{
        Name:    "go_app_request_duration_seconds",
        Help:    "Latency distribution of HTTP requests by handler and status",
        Buckets: prometheus.DefBuckets,
    },
    []string{"handler", "status_code", "error_type"},
)
prometheus.MustRegister(requestDuration)

// 在中间件中记录:start := time.Now() → defer func(){...}
// requestDuration.WithLabelValues("order_submit", "200", "none").Observe(time.Since(start).Seconds())

启动HTTP服务暴露/metrics端点:http.Handle("/metrics", promhttp.Handler())

配置Prometheus抓取Go应用指标

prometheus.yml中添加作业配置:

- job_name: 'go-app'
  static_configs:
  - targets: ['localhost:8080']  # Go服务监听地址
  metrics_path: '/metrics'
  scheme: http

重启Prometheus后,可在http://localhost:9090/targets确认目标UP状态,并用查询语句验证:rate(go_app_request_duration_seconds_count{handler="order_submit"}[5m])

Alertmanager静默策略实战

针对发布窗口或已知维护期,避免误报干扰。创建静默规则JSON文件(silence.json):

{
  "matchers": [
    {"name": "alertname", "value": "HighErrorRate", "isRegex": false},
    {"name": "service", "value": "payment-api", "isRegex": false}
  ],
  "startsAt": "2024-06-15T14:00:00Z",
  "endsAt": "2024-06-15T15:30:00Z",
  "createdBy": "ops-team",
  "comment": "Scheduled deployment maintenance"
}

调用API激活静默:
curl -X POST -H "Content-Type: application/json" -d @silence.json http://localhost:9093/api/v2/silences

静默生效后,匹配该规则的告警将被临时抑制,且可在Alertmanager Web界面(http://localhost:9093/#/silences)实时查看与管理。

第二章:Go应用可观测性基础与Prometheus集成实践

2.1 Go运行时指标采集原理与expvar/pprof机制剖析

Go 运行时通过 runtime 包内置的统计钩子(如 runtime.ReadMemStatsruntime.GC)持续更新内存、Goroutine、调度器等核心状态,所有指标均以原子方式写入全局只读结构体,避免锁开销。

数据同步机制

指标采集非轮询式,而是事件驱动:GC 触发时自动填充 memstatsgopark/goroutines 变更时更新 sched 计数器。expvar 模块通过注册 Var 接口暴露指标,pprof 则基于 HTTP handler 动态生成采样快照。

expvar 示例代码

import "expvar"

func init() {
    // 注册自定义计数器(线程安全)
    expvar.NewInt("http_requests_total").Add(1)
}

expvar.NewInt 返回原子整型变量,Add() 内部调用 atomic.AddInt64,确保高并发写入一致性;注册后可通过 /debug/vars 端点以 JSON 格式访问。

机制 采集粒度 实时性 典型用途
expvar 秒级 弱实时 应用级业务指标
pprof 毫秒级 强实时 CPU/内存/阻塞分析
graph TD
    A[Runtime Hooks] --> B[memstats/goroutines/sched]
    B --> C{expvar Register}
    B --> D{pprof Handler}
    C --> E[/debug/vars JSON]
    D --> F[/debug/pprof/*]

2.2 使用prometheus/client_golang暴露自定义业务指标(Counter/Gauge/Histogram)

在 Go 应用中集成 Prometheus 监控,需引入 prometheus/client_golang 并注册指标。

基础指标注册示例

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    // Counter:累计不可逆事件(如请求总数)
    httpRequestsTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total number of HTTP requests.",
        },
        []string{"method", "status"},
    )

    // Gauge:可增可减的瞬时值(如当前活跃连接数)
    activeConnections = prometheus.NewGauge(prometheus.GaugeOpts{
        Name: "active_connections",
        Help: "Current number of active connections.",
    })

    // Histogram:观测请求延迟分布
    requestDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
        Name:    "http_request_duration_seconds",
        Help:    "Latency distribution of HTTP requests.",
        Buckets: prometheus.DefBuckets, // [0.001, 0.002, ..., 10]
    })
)

func init() {
    prometheus.MustRegister(httpRequestsTotal, activeConnections, requestDuration)
}

逻辑分析CounterVec 支持多维标签(method/status),适合按维度聚合;Gauge 可调用 Inc()/Dec()/Set()Histogram 自动分桶并生成 _count_sum_bucket 三类时间序列。

指标使用场景对比

类型 是否重置 典型用途 是否支持标签
Counter 总请求数、错误发生次数
Gauge 内存使用量、并发 goroutine 数
Histogram 请求延迟、响应大小分布

数据上报流程

graph TD
    A[业务逻辑触发] --> B[调用 Inc()/Observe()/Set()]
    B --> C[指标值写入内存 Collector]
    C --> D[HTTP handler /metrics 返回文本格式]
    D --> E[Prometheus Server 定期抓取]

2.3 基于Gin/Echo中间件自动埋点HTTP请求延迟与错误率指标

核心设计思路

通过统一中间件拦截请求生命周期,在 BeforeHandler 记录起始时间,AfterHandler 计算耗时并捕获状态码,结合 Prometheus 客户端暴露指标。

Gin 实现示例

func MetricsMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        c.Next() // 执行后续 handler
        duration := time.Since(start).Seconds()
        status := float64(c.Writer.Status())

        // 上报延迟直方图(单位:秒)
        httpRequestDuration.WithLabelValues(
            c.Request.Method,
            c.HandlerName(),
            strconv.Itoa(c.Writer.Status()),
        ).Observe(duration)

        // 上报错误率(非2xx/3xx视为错误)
        if status >= 400 {
            httpRequestErrors.Inc()
        }
    }
}

逻辑说明:c.Next() 是 Gin 中间件关键分界点;c.Writer.Status() 获取最终响应码(需在 c.Next() 后调用);WithLabelValues 动态绑定 HTTP 方法、处理器名与状态码三元组,支撑多维下钻分析。

指标维度对比

维度 延迟指标(Histogram) 错误率(Counter)
数据类型 分布式观测(支持 P50/P99) 累加计数
标签粒度 method, handler, status method, handler
Prometheus 查询示例 histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[1h])) by (le, method)) rate(http_request_errors_total[1h])

流程示意

graph TD
    A[HTTP Request] --> B[MetricsMiddleware: start = time.Now()]
    B --> C[Router Match & Handler Execute]
    C --> D[c.Next()]
    D --> E[Record duration & status]
    E --> F[Observe to Histogram / Inc Counter]
    F --> G[Response Write]

2.4 Prometheus服务发现配置与Go微服务动态标签注入实战

Prometheus原生支持多种服务发现机制,其中file_sd_configs最适配微服务动态扩缩容场景。

动态标签注入原理

Go服务启动时,通过HTTP接口向中心配置服务上报元数据(如env=prodteam=backend),由配置服务生成标准化JSON文件供Prometheus轮询加载。

文件服务发现配置示例

# prometheus.yml
scrape_configs:
- job_name: 'go-microservice'
  file_sd_configs:
  - files:
      - "/etc/prometheus/targets/*.json"
    refresh_interval: 30s

refresh_interval控制重载频率;files支持通配符,便于按服务分组管理目标。

Go服务标签注入代码片段

// 向配置中心注册带标签的实例信息
func registerWithLabels() {
    payload := map[string]interface{}{
        "targets": []string{"10.0.1.23:8080"},
        "labels": map[string]string{
            "job": "go-microservice",
            "env": os.Getenv("ENV"),
            "version": build.Version,
        },
    }
    // POST to config-center /v1/targets/update
}

该逻辑在main()初始化阶段执行,确保指标流自带业务维度标签,无需静态配置。

标签字段 说明 示例
env 部署环境 staging
version 语义化版本 v1.4.2
instance 自动填充IP:port 10.0.1.23:8080

服务发现流程

graph TD
    A[Go服务启动] --> B[读取环境变量]
    B --> C[构造含标签target JSON]
    C --> D[POST至配置中心]
    D --> E[配置中心写入file_sd文件]
    E --> F[Prometheus定时reload]

2.5 指标命名规范、维度设计与高基数风险规避指南

命名黄金法则

指标名应遵循 system_subsystem_metric{dimension=value} 结构,例如:

http_requests_total{job="api-gateway", route="/user/profile", status="200"}
  • http_requests_total:语义明确,含单位(_total 表示计数器);
  • jobroute 是业务关键维度,status 是标准 HTTP 维度;
  • 避免动态值(如 user_id="12345")直接入标签,易引发高基数。

高基数陷阱对照表

风险维度 安全替代方案 基数影响
user_id user_tier="premium" 从百万级→3级
request_id has_trace="true" 从无限→布尔

维度建模决策流

graph TD
    A[原始字段] --> B{是否唯一值>10k?}
    B -->|是| C[聚合为分类/分桶]
    B -->|否| D[保留为维度标签]
    C --> E[例:latency_ms → latency_bucket=”200ms”]

第三章:Alertmanager告警生命周期管理与策略工程

3.1 告警路由树设计与基于服务/环境/严重等级的分层分派实践

告警路由树以服务(Service)为根,向下分叉为环境(prod/staging/dev)、再细化至严重等级(critical/warning/info),形成三维决策路径。

路由匹配逻辑示例

# routes.yaml:声明式路由规则
- service: "payment-gateway"
  env: "prod"
  severity: "critical"
  recipients: ["oncall-payments-p0", "slack-#alerts-critical"]
- service: "payment-gateway"
  env: "staging"
  severity: "warning"
  recipients: ["team-payments-dev"]

该配置支持前缀通配与精确匹配混合;envseverity 字段为必填项,确保无歧义路由;recipients 支持多通道标识符,解耦通知渠道。

匹配优先级表

层级 字段 匹配方式 示例值
L1 service 精确/前缀 auth-service
L2 env 精确 prod
L3 severity 枚举值 critical

路由执行流程

graph TD
  A[原始告警] --> B{匹配 service}
  B --> C{匹配 env}
  C --> D{匹配 severity}
  D --> E[投递至对应 recipient]

3.2 静默策略的CRD化管理与GitOps驱动的Silence同步机制

将静默策略(Silence)抽象为 Kubernetes 原生资源,是实现可观测性治理自治化的关键一步。

CRD 定义核心字段

# silencepolicy.crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: silencepolicies.monitoring.example.com
spec:
  group: monitoring.example.com
  versions:
    - name: v1
      schema:
        openAPIV3Schema:
          properties:
            spec:
              properties:
                matchers: # 对应 Alertmanager 的 matcher 数组
                  type: array
                startsAt: {type: string, format: date-time}
                endsAt: {type: string, format: date-time}

该 CRD 显式建模了静默生命周期与匹配逻辑,使 kubectl apply -f 成为静默配置的唯一可信入口。

GitOps 同步流程

graph TD
  A[Git 仓库提交 SilencePolicy] --> B[FluxCD 检测变更]
  B --> C[校验 CRD Schema 合法性]
  C --> D[调用 Alertmanager API 创建/更新静默]
  D --> E[Status 字段回写同步状态]

同步状态表

状态字段 含义 示例值
status.synced 是否完成远端同步 true
status.id Alertmanager 分配的 ID "a1b2c3..."
status.error 同步失败时的错误摘要 "400 Bad Matcher"

静默策略从此具备版本可追溯、变更可审计、回滚可执行的生产级治理能力。

3.3 告警抑制规则(inhibition)在流程依赖场景下的精准降噪应用

在微服务链路中,下游服务故障常触发上游告警海啸。此时需基于因果抑制而非简单静音。

抑制规则核心逻辑

service-b 持续宕机(up{job="service-b"} == 0),自动抑制 service-ahttp_request_duration_seconds_high 告警:

# alertmanager.yml 片段
inhibit_rules:
- source_match:
    alertname: "ServiceDown"
    job: "service-b"
  target_match:
    alertname: "HighLatency"
    job: "service-a"
  equal: ["instance"]

逻辑分析source_match 定义“根因告警”,target_match 定义“衍生告警”,equal: ["instance"] 确保同实例级抑制,避免跨节点误抑。该规则仅在 service-b 失联时激活,恢复后自动解除。

典型抑制场景对比

场景 是否适用抑制 关键依据
DB主库宕机 → 应用连接池耗尽 强因果、可预测链路
CPU飙升 → 日志写入延迟 弱相关、非必然传导

抑制生效流程

graph TD
  A[ServiceB Down告警触发] --> B{Inhibition Rule匹配?}
  B -->|是| C[暂停ServiceA高延迟告警推送]
  B -->|否| D[正常发送所有告警]
  C --> E[ServiceB恢复后自动解抑]

第四章:Go流程级监控场景深度建模与告警优化

4.1 工作流引擎(如Temporal/Go-Workflows)关键阶段耗时与失败率指标建模

工作流可观测性需聚焦执行阶段切片Schedule → Start → Execute → Complete/Failed。各阶段延迟与失败可独立建模为带时间戳的事件流。

核心指标定义

  • schedule_to_start_latency_ms:调度到工作者拉取任务的P95耗时
  • execution_failure_rateFailed / (Completed + Failed + TimedOut)

Temporal 指标采集示例(Go SDK)

// 注册自定义指标上报器(Prometheus)
metricsHandler := metrics.NewPrometheusMetricsHandler(
    prometheus.DefaultRegisterer,
    metrics.MustNewTaggedScope("temporal_workflow_"),
)
workflowOptions := client.StartWorkflowOptions{
    MetricsHandler: metricsHandler, // 自动注入 stage-level 指标标签
}

该配置使 Temporal Server 自动为每个 workflow execution 打标 stage=scheduled/start/execute,并暴露 temporal_workflow_stage_latency_seconds{stage="start",status="success"} 等直方图。

阶段失败率关联分析表

阶段 常见失败原因 关键依赖服务
Schedule Namespace 不可用 Frontend Service
Start Worker 离线/队列积压 Matching Service
Execute Activity 超时/重试耗尽 History Service
graph TD
    A[Workflow Task Scheduled] --> B{Start Attempt?}
    B -->|Yes| C[Worker Polls Task]
    B -->|No, timeout| D[Fail: schedule_to_start_timeout]
    C --> E[Execute Logic]
    E -->|Success| F[Complete]
    E -->|Panic/Timeout| G[Fail: execution_failed]

4.2 分布式事务链路中Saga模式各步骤状态追踪与超时告警触发

Saga 模式通过一系列本地事务与补偿操作保障最终一致性,但长链路下各子步骤的实时状态可见性与异常响应能力至关重要。

数据同步机制

状态需异步落库并同步至可观测平台。推荐采用变更数据捕获(CDC)+ 消息队列双写保障:

// SagaStepStatus.java:幂等更新状态,含版本号防覆盖
public void updateStatus(String stepId, StepState state, long timeoutMs) {
    int affected = jdbcTemplate.update(
        "UPDATE saga_step SET state = ?, version = version + 1, " +
        "updated_at = NOW(), timeout_at = DATE_ADD(NOW(), INTERVAL ? MICROSECOND) " +
        "WHERE step_id = ? AND version = ?", 
        state.name(), timeoutMs, stepId, expectedVersion); // 防并发覆盖
}

逻辑分析:timeout_at 字段精确到微秒,供后续定时扫描器识别超时;version 实现乐观锁,避免多线程竞态导致状态错乱。

超时检测与告警路径

基于 MySQL 的 timeout_at 索引字段执行轻量扫描:

扫描频率 查询条件 告警动作
5s WHERE timeout_at < NOW() AND state = 'RUNNING' 发送企业微信+触发补偿
graph TD
    A[定时任务扫描] --> B{timeout_at < NOW?}
    B -->|是| C[标记为 TIMEOUT]
    B -->|否| D[跳过]
    C --> E[推送告警事件到 Kafka]
    E --> F[告警服务消费并通知]

4.3 异步消息队列消费积压检测与重试流程健康度量化监控

核心指标定义

消费健康度 = 1 − max(积压率, 重试失败率, 处理时延超标率),取值 ∈ [0, 1],实时反映端到端可靠性。

积压动态检测逻辑

# 基于 Kafka Lag + 消费速率双维度判定
def calc_backlog_risk(topic, group_id, window_sec=60):
    lag = get_consumer_lag(topic, group_id)           # 当前未提交 offset 差值
    rate = get_avg_consumption_rate(topic, group_id, window_sec)  # 近1分钟平均TPS
    return "CRITICAL" if lag > rate * 300 else "NORMAL"  # 超5分钟等效积压即告警

该逻辑规避单点 Lag 快照误判,引入速率锚定时间维度,避免瞬时抖动触发误告。

健康度聚合看板(关键维度)

维度 计算方式 阈值(健康)
积压率 当前Lag / (速率 × 300)
重试失败率 3次以上重试消息数 / 总消费数
时延超标率 P99处理耗时 > 2s 的占比

重试流程健康状态流转

graph TD
    A[消息拉取] --> B{是否失败?}
    B -- 是 --> C[进入重试队列]
    C --> D[指数退避调度]
    D --> E{重试≤3次?}
    E -- 是 --> F[重新投递]
    E -- 否 --> G[转入死信通道]
    B -- 否 --> H[ACK并归档]

4.4 基于Prometheus Recording Rules构建流程SLA达成率聚合看板

为实现跨服务、多环节的端到端流程SLA监控,需将离散的原始指标(如process_step_duration_seconds_countprocess_step_status_total{status="success"})通过Recording Rules预聚合为标准化SLA指标。

SLA核心计算逻辑

SLA达成率 = 成功流程数 / 总流程数(按时间窗口滚动计算)。Recording Rule示例如下:

# recording_rules.yml
- record: job:sla_rate:ratio_per_1h
  expr: |
    sum_over_time(
      (sum by (job) (rate(process_step_status_total{status="success"}[1h])) 
       / ignoring(status) group_left() 
       sum by (job) (rate(process_step_status_total[1h])))
      [1h:1m]
    ) / 60
  labels:
    metric: "sla_rate"

逻辑分析:该规则每分钟计算过去1小时各job的每分钟成功率比值,再对60个采样点取平均,消除瞬时抖动;group_left()确保分母与分子按相同job标签对齐;除以60实现滑动窗口归一化。

聚合维度设计

维度 示例值 用途
job order-fulfillment 流程类型切片
env prod, staging 环境隔离
service payment, inventory 关键子服务贡献度分析

数据同步机制

  • Prometheus定期加载更新后的recording_rules.yml(reload via SIGHUP或/-/reload
  • Grafana直接查询job:sla_rate:ratio_per_1h,避免前端重复计算
graph TD
  A[原始指标] --> B[Recording Rules预聚合]
  B --> C[TSDB持久化]
  C --> D[Grafana看板实时渲染]

第五章:总结与展望

技术栈演进的实际影响

在某大型电商平台的微服务重构项目中,团队将原有单体架构迁移至基于 Kubernetes 的云原生体系。迁移后,平均部署耗时从 47 分钟缩短至 92 秒,CI/CD 流水线失败率下降 63%。关键变化在于:

  • 使用 Helm Chart 统一管理 87 个服务的发布配置
  • 引入 OpenTelemetry 实现全链路追踪,定位一次支付超时问题的时间从平均 6.5 小时压缩至 11 分钟
  • Istio 网关策略使灰度发布成功率稳定在 99.98%,近半年无因发布引发的 P0 故障

生产环境中的可观测性实践

以下为某金融风控系统在 Prometheus + Grafana 中落地的核心指标看板配置片段:

- name: "risk-service-alerts"
  rules:
  - alert: HighLatencyRiskCheck
    expr: histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="risk-api"}[5m])) by (le)) > 1.2
    for: 3m
    labels:
      severity: critical

该规则上线后,成功在用户投诉前 4.2 分钟自动触发告警,并联动 PagerDuty 启动 SRE 响应流程。过去三个月内,共拦截 17 起潜在服务降级事件。

多云架构下的成本优化成果

某政务云平台采用混合云策略(阿里云+本地数据中心),通过 Crossplane 统一编排资源后,实现以下量化收益:

维度 迁移前 迁移后 降幅
月度计算资源成本 ¥1,284,600 ¥792,300 38.3%
跨云数据同步延迟 842ms(峰值) 47ms(P99) 94.4%
容灾切换耗时 22 分钟 87 秒 93.5%

核心手段包括:基于 Karpenter 的弹性节点池自动扩缩容、S3 兼容对象存储统一网关、以及使用 Velero 实现跨集群应用状态一致性备份。

AI 辅助运维的初步验证

在某运营商核心网管系统中,集成 Llama-3-8B 微调模型用于日志根因分析。模型在真实生产日志样本集(含 23 类典型故障模式)上达到:

  • 日志聚类准确率:89.7%(对比传统 ELK+Kibana 手动分析提升 3.2 倍效率)
  • 故障描述生成 F1-score:0.82(经 12 名一线工程师盲评,83% 认可其建议操作可行性)
  • 模型推理延迟控制在 142ms 内(部署于 NVIDIA T4 GPU 节点,QPS ≥ 186)

安全左移的工程化落地

某车联网企业将 SAST 工具链深度嵌入 GitLab CI,在 MR 阶段强制阻断高危漏洞提交。2024 年 Q1 数据显示:

  • 代码合并前拦截 CVE-2023-38831 类漏洞 417 次
  • 开发人员平均修复耗时从 3.8 小时降至 22 分钟(因精准定位到行级缺陷并附带修复模板)
  • 安全审计通过率从 61% 提升至 99.2%,第三方渗透测试未发现任何中高危漏洞

下一代基础设施的关键挑战

边缘计算场景下,某智能工厂部署了 327 台树莓派 5 节点运行轻量级 K3s 集群。当前面临的真实瓶颈包括:

  • 固件 OTA 升级失败率 12.7%(受工业现场电磁干扰影响)
  • MQTT 消息端到端时延标准差达 ±340ms(远超 50ms SLA)
  • 节点离线后状态同步恢复平均耗时 18.3 分钟(依赖中心集群心跳检测机制)

开源生态协同的新路径

Apache APISIX 社区贡献者在 2024 年推动完成三项企业级特性:

  • 支持国密 SM4-GCM 加密插件(已通过国家密码管理局商用密码检测中心认证)
  • 实现与华为昇腾 NPU 的 TensorRT 推理加速直连(实测吞吐提升 4.7 倍)
  • 构建基于 eBPF 的零信任网络策略执行引擎(绕过 iptables 链,延迟降低 89%)

人机协作的组织适配

某省级医疗影像平台推行 AIOps 后,运维团队角色发生实质性转变:

  • 73% 的值班工程师每日处理告警数从 42 条降至 5.6 条
  • 新增“算法标注工程师”岗位,专职清洗训练数据并校验模型输出
  • SRE 工程师平均每周投入 11.5 小时参与模型反馈闭环(标注误报案例、补充边界条件)

技术债务的可视化治理

团队使用 CodeScene 对 1200 万行 Java 代码库进行热点分析,识别出 3 个“腐败模块”:

  • payment-core(技术熵值 8.7,变更密集度 92%,贡献 67% 的线上异常堆栈)
  • legacy-reporting(代码年龄 9.3 年,测试覆盖率 12%,年均修复成本 ¥2.1M)
  • auth-adapter(耦合度 0.89,依赖 14 个已下线外部系统)
    治理方案已纳入 2024 年 Q3 产品路线图,首期将用 Rust 重写核心鉴权逻辑。

记录一位 Gopher 的成长轨迹,从新手到骨干。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注