第一章:合众汇富Golang生产环境SRE体系概览
合众汇富的Golang生产环境SRE体系以“稳定性为基、自动化为刃、可观测为眼”为核心设计理念,覆盖从代码提交到服务下线的全生命周期。该体系并非孤立工具堆砌,而是深度融合金融级合规要求、高并发交易场景与微服务治理实践形成的工程化保障框架。
核心能力分层
- 可靠性保障层:基于Service Level Objective(SLO)驱动的故障预算机制,所有Golang核心服务(如订单路由、实时风控引擎)均配置
error_budget_burn_rate告警策略,阈值严格遵循《证券期货业信息系统运维管理规范》; - 自动化运维层:通过自研
go-sre-operator实现Kubernetes原生CRD管理,支持自动扩缩容、灰度发布与熔断回滚,无需人工介入变更流程; - 可观测性中枢:统一采集指标(Prometheus)、日志(Loki+LogQL)、链路(Jaeger+OpenTelemetry SDK),所有Golang服务默认注入
otel-collector探针,启用HTTP/GRPC双向trace采样。
关键基础设施组件
| 组件名称 | 用途说明 | Golang集成方式 |
|---|---|---|
sre-guardian |
实时SLO健康度计算与自动降级决策引擎 | 作为Go module嵌入主服务,import "github.com/hzhf/sre-guardian" |
gops-exporter |
暴露Go运行时指标(goroutines、GC等) | 启动时调用gops.Export()并注册至Prometheus target |
快速验证SRE基础就绪状态
执行以下命令检查本地开发环境是否满足生产部署前置条件:
# 1. 验证Go版本与安全补丁(要求≥1.21.6且含CVE-2023-45283修复)
go version && go list -m all | grep -E "(crypto/tls|net/http)"
# 2. 检查SRE SDK初始化完整性(需在main.go中存在)
grep -A 5 "sre.Init(" ./cmd/app/main.go
# 正常输出应包含sre.Init(sre.WithTracing(), sre.WithMetrics())
该体系已在沪深两市交易网关、私募基金估值引擎等关键系统稳定运行超18个月,P99延迟波动率低于±3%,年均非计划停机时间小于2.1分钟。
第二章:Golang服务全链路健康巡检实践
2.1 进程态与资源占用基线校验(含137项巡检项分类逻辑与动态权重机制)
巡检项按语义与影响域划分为四大类:进程生命周期类(32项)、内存/句柄泄漏类(41项)、CPU瞬时毛刺类(29项)、跨代资源耦合类(35项)。每类初始权重由历史故障归因率标定,再通过滑动窗口实时修正。
动态权重更新逻辑
def update_weight(base_w: float, recent_fail_rate: float, decay=0.92) -> float:
# base_w: 初始权重(如进程类默认0.28)
# recent_fail_rate: 近7天该类告警触发后确认为根因的比例
# decay: 防抖衰减因子,避免权重震荡
return max(0.1, min(0.9, base_w * (1 + 2.5 * (recent_fail_rate - 0.15)) * decay))
该函数确保权重在[0.1, 0.9]区间内自适应收敛,当某类故障归因率超阈值(0.15)时正向放大,低于则收缩。
巡检项分类概览
| 类别 | 代表指标 | 权重调节敏感度 |
|---|---|---|
| 进程生命周期类 | ZombieCount, RestartFreq |
中(τ=3h) |
| 内存/句柄泄漏类 | RSS_GrowthRate, HandleLeakScore |
高(τ=45min) |
| CPU瞬时毛刺类 | CPU_Burst_99th, SchedulerLatency_us |
低(τ=6h) |
校验执行流程
graph TD
A[采集进程快照] --> B{是否满足基线偏移阈值?}
B -->|是| C[触发加权评分]
B -->|否| D[进入低频监控模式]
C --> E[按137项分类调用对应检测器]
E --> F[聚合动态加权得分]
2.2 Go Runtime指标深度解析(GC停顿、Goroutine泄漏、内存逃逸实测诊断法)
GC停顿实时观测
使用 runtime.ReadMemStats 结合 debug.SetGCPercent 动态调优:
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("PauseTotalNs: %v ms\n", m.PauseTotalNs/1e6) // 累计GC停顿毫秒数
PauseTotalNs 是自程序启动以来所有STW暂停的纳秒总和,需结合 NumGC 计算平均停顿;高频小停顿可能预示对象分配风暴。
Goroutine泄漏快速定位
go tool pprof http://localhost:6060/debug/pprof/goroutine?debug=2
- 查看输出中重复出现的 goroutine 栈帧(如
http.HandlerFunc未关闭 channel) - 关键信号:
goroutine count > 10k且持续增长
内存逃逸分析三步法
| 工具 | 命令示例 | 诊断重点 |
|---|---|---|
go build -gcflags="-m -m" |
go build -gcflags="-m -m main.go |
检查 moved to heap 行 |
go tool compile -S |
go tool compile -S main.go |
定位 CALL runtime.newobject |
graph TD
A[源码变量] --> B{是否在函数外被引用?}
B -->|是| C[逃逸至堆]
B -->|否| D[栈上分配]
C --> E[增加GC压力与停顿]
2.3 微服务依赖拓扑验证(HTTP/gRPC/Redis/Kafka/MQ连接性+时延双维度探活)
微服务间依赖需同时验证可达性与服务质量。单靠 TCP 心跳无法反映真实调用链路健康度,必须融合协议语义级探活。
探活维度设计
- ✅ 连接性:建立底层通道(如 Redis
PING、KafkaMETADATA请求) - ✅ 时延:采集端到端 P95 延迟(含序列化、网络、服务端处理)
多协议统一探活 SDK(Go 示例)
// 支持 HTTP/gRPC/Redis/Kafka 的泛型探活器
func Probe(ctx context.Context, target string, proto Protocol) (bool, time.Duration, error) {
switch proto {
case HTTP:
return httpProbe(ctx, target) // GET /health,超时设为 2s
case GRPC:
return grpcProbe(ctx, target) // 调用 HealthCheck/Check RPC
case REDIS:
return redisProbe(ctx, target) // 执行 PING 并测量 RTT
}
}
httpProbe 使用 http.DefaultClient 并启用 ctx.WithTimeout(2*time.Second);grpcProbe 复用已建连接池避免重复握手;redisProbe 通过 conn.Ping(ctx).Result() 获取精确 RTT。
探活结果聚合视图
| 依赖类型 | 连接状态 | P95 时延 | 健康标识 |
|---|---|---|---|
| user-svc (gRPC) | ✅ | 42ms | GREEN |
| cache-redis | ✅ | 8ms | GREEN |
| order-topic (Kafka) | ❌ | — | RED |
graph TD
A[探活调度器] --> B{协议分发}
B --> C[HTTP Handler]
B --> D[gRPC Client]
B --> E[Redis Conn]
C --> F[解析 HTTP 200 + X-Resp-Time]
D --> G[捕获 Status.Code 和 latency]
2.4 日志与trace一致性审计(OpenTelemetry采样率对齐、错误码分布热力图识别)
数据同步机制
为保障日志(Log)与 trace 的上下文一致性,需强制对齐 OpenTelemetry 的采样决策点:在 TracerProvider 初始化时注入统一采样器,并通过 TraceID 关联日志结构体字段。
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased
# 全局采样率设为 0.1(10%),确保日志采集与 trace 采样同源
provider = TracerProvider(
sampler=TraceIdRatioBased(0.1) # ⚠️ 必须与日志采集模块的 sampling_rate=0.1 严格一致
)
逻辑分析:TraceIdRatioBased(0.1) 基于 trace ID 哈希值做确定性采样,避免因随机采样导致日志与 trace 割裂;参数 0.1 表示仅保留 10% 的 trace,对应日志模块也须配置相同阈值过滤。
错误码热力图构建
采集各服务 status.code 与 error.type 维度聚合后,渲染二维热力图:
| HTTP 状态码 | 500 | 502 | 503 | 504 |
|---|---|---|---|---|
DB_TIMEOUT |
127 | 8 | 0 | 3 |
AUTH_FAILED |
2 | 0 | 19 | 0 |
审计验证流程
graph TD
A[原始Span] --> B{采样命中?}
B -->|是| C[注入trace_id到log record]
B -->|否| D[丢弃Span & 对应日志]
C --> E[ES中按trace_id+timestamp联合查询]
E --> F[校验error.code分布一致性]
2.5 安全合规性快照检查(TLS 1.3强制启用、敏感配置硬编码扫描、pprof未授权暴露拦截)
TLS 1.3 强制启用验证
Go 服务需在 http.Server 中显式配置 tls.Config,禁用旧协议:
srv := &http.Server{
Addr: ":443",
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS13, // 强制最低为 TLS 1.3
CurvePreferences: []tls.CurveID{tls.X25519, tls.CurvesSupported[0]},
},
}
MinVersion 确保握手不降级;CurvePreferences 优先启用抗量子特性更强的 X25519。
敏感配置硬编码扫描
使用正则匹配常见密钥模式(如 (?i)password|api[_-]?key|token.*=),结合 AST 解析提升准确率。
pprof 暴露拦截策略
| 检查项 | 合规动作 |
|---|---|
/debug/pprof/ |
仅允许内网 IP + JWT 鉴权 |
/debug/pprof/heap |
生产环境默认禁用 |
graph TD
A[HTTP 请求] --> B{路径匹配 /debug/pprof/?}
B -->|是| C[校验来源 IP + Authorization Header]
C -->|拒绝| D[HTTP 403]
C -->|通过| E[放行]
第三章:七类核心告警阈值建模方法论
3.1 基于P99延迟漂移的自适应阈值算法(结合历史滑动窗口与突增检测)
传统固定阈值在流量波动场景下误告率高。本算法以P99延迟为观测核心,动态构建双层窗口:
- 长周期基线窗口(24h):捕获业务常态分布
- 短周期检测窗口(5min):实时感知突增偏移
核心计算逻辑
def adaptive_p99_threshold(p99_recent, p99_history, std_history):
# 漂移因子:当前P99超出历史均值的σ倍数
drift_ratio = (p99_recent - np.mean(p99_history)) / (std_history + 1e-6)
# 自适应缩放:漂移越显著,阈值上浮越缓(防误触发)
scale = 1.0 + 0.3 * np.tanh(drift_ratio) # 平滑饱和
return np.percentile(p99_history, 99) * scale
p99_history为滑动窗口内每5分钟P99组成的数组(长度288);tanh确保scale∈[0.7, 1.3],避免阈值震荡。
决策流程
graph TD
A[采集5min P99] --> B{是否>自适应阈值?}
B -->|是| C[触发突增标记]
B -->|否| D[更新历史窗口]
C --> E[启动分级告警]
关键参数对照表
| 参数 | 默认值 | 说明 |
|---|---|---|
window_long |
288 | 24h内5min粒度采样点数 |
alpha |
0.1 | 指数衰减权重,平滑历史基线 |
min_window_size |
60 | 窗口最小有效长度(防冷启偏差) |
3.2 内存增长速率告警的Go特化判定(heap_inuse vs. total_alloc趋势分离建模)
Go运行时内存行为具有强语义特征:heap_inuse反映当前活跃堆内存,而total_alloc是累计分配总量——二者增速背离常预示内存泄漏或对象生命周期异常。
核心判据逻辑
heap_inuse缓升 +total_alloc陡增 → 高频短命对象(GC压力上升)heap_inuse持续陡升 +total_alloc线性增长 → 长周期对象累积(典型泄漏)
// 基于runtime.MemStats的双序列斜率比判定
func computeGrowthRatio(stats []runtime.MemStats) float64 {
if len(stats) < 3 { return 0 }
inuse := slopeOfLastN(stats, func(s *runtime.MemStats) uint64 { return s.HeapInuse })
alloc := slopeOfLastN(stats, func(s *runtime.MemStats) uint64 { return s.TotalAlloc })
return float64(inuse) / math.Max(float64(alloc), 1e-6) // 避免除零
}
该函数计算最近采样窗口内HeapInuse与TotalAlloc的增长斜率比。比值显著偏离1(如1.5)触发分级告警。
| 指标组合 | 含义 | 建议动作 |
|---|---|---|
inuse↑↑, alloc↑ |
对象长期驻留 | 检查全局map/缓存未清理 |
inuse→, alloc↑↑ |
GC后仍高频分配 | 审查循环中新建结构体 |
graph TD
A[采集MemStats流] --> B{计算双序列斜率}
B --> C[比值∈[0.7,1.3]?]
C -->|是| D[静默]
C -->|否| E[触发特化告警]
3.3 Goroutine数异常膨胀的根因定位范式(阻塞型vs. 泄漏型goroutine特征提取)
核心区分维度
- 阻塞型:goroutine 卡在系统调用(如
net.Conn.Read、time.Sleep)或同步原语(sync.Mutex.Lock、chan send/receive),处于syscall或chan receive状态,数量稳定但响应停滞; - 泄漏型:goroutine 启动后因逻辑缺陷(如未关闭 channel、无限 for 循环无退出条件)持续累积,
GoroutineProfile中数量线性增长。
典型泄漏代码示例
func spawnLeak() {
for i := 0; i < 100; i++ {
go func(id int) {
select {} // 永久阻塞,且无回收机制 → 泄漏
}(i)
}
}
select {}使 goroutine 进入永久休眠(Gwaiting状态),runtime.Stack()可捕获其堆栈,但pprof/goroutine?debug=2显示其状态为runnable(非阻塞),是泄漏型关键标识。
状态特征对比表
| 特征 | 阻塞型 | 泄漏型 |
|---|---|---|
runtime.NumGoroutine() 趋势 |
稳定高位 | 持续上升 |
pprof/goroutine?debug=2 主要状态 |
chan receive, semacquire |
runnable, running |
是否可被 GC 回收 |
否(持有栈/资源) | 否(无引用但永不退出) |
定位流程图
graph TD
A[观察 NumGoroutine 持续增长] --> B{pprof/goroutine?debug=2}
B -->|大量 runnable| C[检查启动逻辑与退出条件]
B -->|大量 chan receive| D[检查 channel 关闭与超时]
C --> E[定位未关闭的 goroutine 启动点]
D --> F[定位未 close 的 channel 或无 timeout 的 I/O]
第四章:五类高频故障的一键诊断脚本设计
4.1 “熔断雪崩前兆”诊断脚本(整合Hystrix状态+下游超时率+重试放大系数分析)
该诊断脚本以实时可观测性为出发点,聚合三大关键指标,识别服务链路中即将触发级联失败的早期信号。
核心指标联动逻辑
- Hystrix Circuit State:
CLOSED→OPEN切换前常伴随errorPercentage > 50%持续30s - 下游超时率:基于
feign.client.config.default.connectTimeout与实际responseTime > 2×P95的请求占比 - 重试放大系数:
totalRequests / uniqueRequestIds,>1.8 即预警(暴露盲目重试)
关键诊断代码(Python + Prometheus Client)
# 从Prometheus拉取近5分钟指标并计算熔断前兆得分
query = '''
sum(rate(hystrix_command_latency_total{status="timeout"}[5m]))
/ sum(rate(hystrix_command_latency_total[5m]))
'''
timeout_rate = prom.query(query)[0]['value'][1]
# 注:需配合hystrix.stream暴露的/metrics端点或Micrometer埋点
熔断风险等级判定表
| 超时率 | Hystrix错误率 | 重试系数 | 风险等级 |
|---|---|---|---|
| ≤1.2 | 安全 | ||
| ≥15% | ≥60% | ≥2.0 | 高危(立即干预) |
graph TD
A[采集Hystrix状态] --> B[计算下游超时率]
B --> C[统计重试放大系数]
C --> D{三指标加权融合}
D -->|≥0.75| E[触发熔断前兆告警]
4.2 “GC风暴溯源”诊断脚本(pprof heap/profile/trace三源联动与内存分配热点定位)
当服务突发高 GC 频率时,单一 pprof 视图常掩盖根因。需协同分析 heap(内存快照)、profile(CPU+alloc采样)与 trace(goroutine 生命周期)。
三源联动采集策略
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/heapgo tool pprof -alloc_space http://localhost:6060/debug/pprof/profile?seconds=30go tool trace http://localhost:6060/debug/pprof/trace?seconds=15
核心诊断脚本(bash + go)
#!/bin/bash
# 同步拉取三源数据,带时间戳对齐
TS=$(date +%s)
curl -s "http://localhost:6060/debug/pprof/heap" > heap.$TS.pb.gz
curl -s "http://localhost:6060/debug/pprof/profile?seconds=30" > cpu.$TS.pb.gz
curl -s "http://localhost:6060/debug/pprof/trace?seconds=15" > trace.$TS.trace
# 关键:用 go tool pprof 定位分配热点(-alloc_space + -top)
go tool pprof -alloc_space -top cpu.$TS.pb.gz | head -n 10
此脚本确保三类 profile 时间窗口严格对齐(误差 -alloc_space 参数启用堆分配字节数统计,
-top输出按累计分配量降序排列的函数栈,精准暴露高频 new/make 调用点。
| 指标 | heap | profile (alloc) | trace |
|---|---|---|---|
| 关注维度 | 对象存活量 | 分配总量/频次 | goroutine 创建/阻塞链 |
| 典型命令参数 | -inuse_space |
-alloc_space |
go tool trace |
graph TD
A[GC Storm告警] --> B[并发拉取heap/profile/trace]
B --> C{时间戳对齐校验}
C --> D[pprof -alloc_space 分析]
D --> E[定位 top3 分配函数]
E --> F[结合 trace 查看其 goroutine 阻塞上下文]
4.3 “连接池耗尽”诊断脚本(net.Conn生命周期追踪+空闲连接回收策略有效性验证)
核心诊断逻辑
通过 http.Transport 的 IdleConnTimeout 与 MaxIdleConnsPerHost 配置联动,结合 net/http/httptrace 追踪每个 *net.Conn 的创建、复用与关闭事件。
连接生命周期钩子示例
trace := &httptrace.ClientTrace{
GotConn: func(info httptrace.GotConnInfo) {
if info.Reused {
log.Printf("reused conn, idle=%v", info.IdleTime)
} else {
log.Printf("new conn established")
}
},
ConnectDone: func(net, addr string, err error) {
if err == nil { log.Printf("connect success") }
},
}
该钩子捕获连接复用状态与空闲时长,用于验证 IdleConnTimeout 是否触发及时回收;GotConnInfo.IdleTime 直接反映连接在池中驻留时间,是评估回收策略有效性的关键观测指标。
空闲连接回收有效性验证维度
| 指标 | 健康阈值 | 检测方式 |
|---|---|---|
| 平均空闲时长 | 日志聚合统计 | |
| 复用率(Reused/Total) | > 75% | trace 事件计数比 |
| 池中连接峰值数 | ≤ MaxIdleConnsPerHost | http.DefaultTransport.IdleConnState 反射读取 |
回收行为验证流程
graph TD
A[发起HTTP请求] --> B{GotConnInfo.Reused?}
B -->|true| C[记录IdleTime]
B -->|false| D[记录新连接创建]
C --> E[IdleTime > IdleConnTimeout?]
E -->|yes| F[预期:连接被Close]
E -->|no| G[持续监控复用链路]
4.4 “Context超时级联失效”诊断脚本(调用链中deadline传递完整性自动校验)
当微服务调用链中任意节点未正确继承上游 context.WithDeadline,将导致下游超时失控、goroutine 泄漏与雪崩风险。
核心检测逻辑
通过反射提取各层 context.Context 的 deadline 字段,比对调用链上下游是否单调递减且非零:
func checkDeadlineCascading(ctx context.Context, parentDeadline time.Time) error {
if d, ok := ctx.Deadline(); !ok {
return errors.New("missing deadline") // 上游未设deadline即中断级联
} else if d.Before(parentDeadline) || d.Equal(parentDeadline) {
return nil // 合法:严格早于父deadline
}
return fmt.Errorf("deadline %v not earlier than parent %v", d, parentDeadline)
}
逻辑说明:
ctx.Deadline()返回(time.Time, bool);d.Before(parentDeadline)确保子 deadline 必须严格早于父 deadline,杜绝“平移”或“延长”错误;!ok捕获未设置 deadline 的中间节点。
典型失效模式对照表
| 场景 | 是否破坏级联 | 原因 |
|---|---|---|
context.Background() 直接传入下游 |
✅ 是 | 丢失所有 deadline 信息 |
context.WithTimeout(ctx, 0) |
✅ 是 | timeout=0 导致 deadline=zero time |
context.WithCancel(ctx) |
❌ 否 | 不影响 deadline 传递 |
自动化校验流程
graph TD
A[入口HTTP请求] --> B[Middleware提取ctx.Deadline]
B --> C{下游gRPC调用前校验}
C -->|失败| D[记录trace_id+span_id告警]
C -->|通过| E[透传ctx至client.Invoke]
第五章:合众汇富Golang SRE演进路线图
演进背景与初始痛点
2021年Q3,合众汇富核心交易网关由Java迁移至Golang,初期采用单体架构+裸跑进程部署模式。上线后首月发生3次P0级故障:服务启动耗时超90秒导致K8s探针失败、内存泄漏引发OOM Killer强制杀进程、日志无结构化导致ELK解析失败率高达47%。SRE团队紧急成立Golang专项小组,以“可观测性先行、稳定性筑基、自动化提效”为三大原则启动演进。
核心指标定义与基线建设
团队确立四大黄金信号(Latency、Traffic、Errors、Saturation)在Golang场景下的具体实现方式:
- Latency:基于
prometheus/client_golang采集http_request_duration_seconds_bucket直方图,按/api/v1/order等关键路径打标; - Errors:通过
gin.RecoveryWithWriter捕获panic,并统一上报至Sentry,错误码映射表纳入CMDB同步; - Saturation:自研
golang_runtime_collector采集goroutine数、GC pause时间、heap alloc rate,阈值设为goroutine > 5000且持续5分钟。
| 阶段 | 时间窗口 | 关键动作 | 量化成果 |
|---|---|---|---|
| 一期 | 2021.10–2022.03 | 接入Prometheus+Grafana监控栈,落地OpenTelemetry tracing | MTTR从47分钟降至11分钟 |
| 二期 | 2022.04–2022.12 | 构建Chaos Mesh故障注入平台,覆盖网络延迟、Pod Kill等6类场景 | 故障自愈率提升至68%(自动降级+流量切走) |
| 三期 | 2023.01–2023.09 | 上线Argo CD GitOps流水线,配置变更100%通过PR评审+自动灰度发布 | 配置错误导致的回滚率下降92% |
生产环境Go Runtime调优实践
针对高频GC问题,团队通过pprof分析发现encoding/json.Unmarshal在订单反序列化中触发大量临时对象分配。采用jsoniter替换标准库,并启用jsoniter.ConfigCompatibleWithStandardLibrary().Froze()预编译解析器,GC pause时间从平均18ms降至2.3ms。同时,在K8s Deployment中设置resources.limits.memory=2Gi并启用GOMEMLIMIT=1.6Gi,避免runtime向OS申请过多内存。
// 订单服务健康检查增强示例
func (h *HealthHandler) Check(ctx context.Context) error {
// 并发检测依赖服务连通性
var wg sync.WaitGroup
errs := make(chan error, 3)
wg.Add(3)
go func() { defer wg.Done(); errs <- h.checkRedis(ctx) }()
go func() { defer wg.Done(); errs <- h.checkPostgreSQL(ctx) }()
go func() { defer wg.Done(); errs <- h.checkRiskEngine(ctx) }()
wg.Wait()
close(errs)
// 任一依赖异常即返回 unhealthy
for err := range errs {
if err != nil {
return fmt.Errorf("dependency failed: %w", err)
}
}
return nil
}
自动化应急响应闭环
构建基于EventBridge的事件驱动响应体系:当Prometheus告警service_latency_p99{job="order-gateway"} > 2s触发时,自动执行以下动作链:
- 调用Ansible Playbook扩容副本数(从3→5);
- 向Slack #sre-alerts发送带traceID的告警卡片;
- 若5分钟内未恢复,则触发
kubectl exec -it order-gateway-xxx -- /app/bin/dump-goroutines生成堆栈快照并存入S3。
混沌工程常态化机制
每月第二个周四定为“韧性演练日”,使用自研CLI工具chaosctl执行标准化剧本:
# 模拟上游风控服务超时(仅影响灰度集群)
chaosctl inject network-delay \
--namespace=prod-gray \
--target-pod-labels="app=order-gateway" \
--duration=300s \
--latency=1500ms \
--percent=30
所有演练结果自动写入Confluence知识库,并关联Jira缺陷单跟踪改进项。2023年共执行42次演练,暴露出3个未覆盖的熔断边界条件,均已合入主干代码。
