第一章:Go开源影响力提升的底层逻辑与目标拆解
Go语言的开源影响力并非源于单一事件或营销动作,而是由其设计哲学、社区机制与工程实践三者深度耦合所驱动的系统性结果。其底层逻辑根植于“可预测的简单性”——通过显式错误处理、无隐式继承、统一格式化(gofmt)、强制依赖管理(go.mod)等约束,大幅降低协作熵值,使跨组织、跨时区的代码共建成为可持续行为。
开源影响力的本质是可复用性的规模化验证
一个Go项目若被100个独立生产环境采用,意味着其API稳定性、错误路径覆盖、交叉平台兼容性已通过真实压力检验。这种验证无法被测试覆盖率数字替代,而依赖于开发者主动 import 并长期维护依赖关系。例如:
# 通过 pkg.go.dev 检查模块被引次数(真实指标)
curl -s "https://pkg.go.dev/+packages?importers=github.com/gin-gonic/gin" | \
grep -o '"importerCount":[0-9]*' | head -1
# 输出示例: "importerCount":28471
该数值反映的是去中心化生态中的实际采用广度,而非中心化平台的星标数。
目标需按可测量维度拆解
影响力提升不可泛泛而谈,必须锚定可观测、可归因、可迭代的子目标:
- 可发现性:确保模块在 pkg.go.dev 的文档完整率 ≥95%,含示例代码与可点击运行按钮
- 可集成性:提供零配置的
go install入口(如go install github.com/urfave/cli/v2@latest),并验证GOOS=windows GOARCH=arm64 go build跨平台构建成功率 - 可演进性:每个主版本发布前,必须通过
gorelease工具校验API兼容性,阻断破坏性变更
| 维度 | 验证方式 | 合格阈值 |
|---|---|---|
| 文档完备性 | godoc -http=:6060 & + 人工抽检 |
示例可执行率100% |
| 构建稳定性 | GitHub Actions 多平台矩阵测试 | 3个OS × 2架构全通 |
| 依赖健康度 | go list -u -m all + govulncheck |
高危漏洞数为0 |
真正的影响力增长始于将抽象目标转化为每日CI流水线中的一行断言。
第二章:打造1个高质量PR:从选题到合并的全链路实践
2.1 源码级问题诊断:如何精准定位Kubernetes/TiDB/Docker中Go模块的真实痛点
核心思路:从 panic traceback 追溯至模块初始化链
Go 程序崩溃时的 runtime.Stack() 输出常隐含模块加载顺序异常。例如 TiDB 启动失败时,panic: runtime error: invalid memory address 往往源于 plugin.Open() 调用后未校验 *plugin.Plugin 的 Lookup 结果。
// 示例:Docker daemon 中插件注册的健壮性检查
p, err := plugin.Open("/usr/lib/docker/plugins/volume.so")
if err != nil {
log.Fatal("failed to open plugin", err) // ❌ 缺失上下文
}
sym, err := p.Lookup("VolumeDriver") // ⚠️ 若符号不存在,err 为 plugin.ErrNotFound
if err != nil {
log.Fatalf("missing symbol VolumeDriver: %v", err) // ✅ 显式暴露模块契约断裂点
}
逻辑分析:
plugin.Lookup失败不触发 panic,但后续类型断言(如sym.(volume.Driver))会引发 nil dereference。此处err直接关联 Go 插件模块的 ABI 兼容性痛点——TiDB 的tikv/client-go与 PD 版本不匹配、Kubernetes CNI 插件 ABI 升级断层均复现同类模式。
常见模块依赖断裂场景对比
| 系统 | 触发模块 | 典型错误信号 | 根因层级 |
|---|---|---|---|
| Kubernetes | k8s.io/client-go |
cannot assign *v1.PodList to *unstructured.Unstructured |
类型注册表未同步 |
| TiDB | github.com/pingcap/tidb/parser |
panic: reflect: Call of nil func value |
parser.New() 初始化被提前调用 |
| Docker | github.com/moby/buildkit |
context canceled during solver.Solve() |
session.Manager 生命周期泄漏 |
定位路径收敛图
graph TD
A[panic traceback] --> B{是否有 runtime.main / init?}
B -->|是| C[检查 import _ “module/init” 侧效应]
B -->|否| D[追踪 goroutine 创建栈:newproc1 → goexit]
C --> E[审查 init 函数中 sync.Once.Do 调用]
D --> F[定位 runtime.gopark 阻塞点 → 检查 channel/select 死锁]
2.2 PR设计黄金法则:最小可行变更(MVC)+ 可测试性 + 向后兼容性保障
什么是最小可行变更(MVC)?
MVC ≠ 最小功能,而是在保障核心契约前提下,变更粒度最小、影响面最窄的代码提交。例如仅修改单个函数逻辑,不新增接口、不调整参数签名。
可测试性落地实践
# ✅ 遵循依赖注入,便于单元测试打桩
def sync_user_profile(user_id: str, db: DatabaseInterface) -> bool:
user = db.fetch_by_id(user_id) # 可 mock
if not user.is_active:
return False
db.update_last_sync(user_id) # 可验证调用
return True
逻辑分析:
db作为抽象接口传入,解耦数据源;所有分支路径均可覆盖;无全局状态或隐式副作用。参数user_id为唯一输入依赖,db为可控协作者。
向后兼容性三原则
- 接口签名不变(含默认参数位置)
- 返回结构字段可扩展,不可删减
- 错误码范围只增不减
| 兼容动作 | 允许 | 禁止 |
|---|---|---|
| 新增可选参数 | ✅ | ❌ 修改必填参数顺序 |
添加 v2 字段到 JSON 响应 |
✅ | ❌ 删除 name 字段 |
graph TD
A[PR提交] --> B{是否仅修改一处逻辑?}
B -->|是| C[是否所有新/改代码均有对应单元测试?]
B -->|否| D[拒绝合并]
C -->|是| E[响应体/错误码是否未破坏旧客户端?]
C -->|否| D
E -->|是| F[批准]
E -->|否| D
2.3 Go语言专项实践:Context传播、Error Wrapping、泛型适配与pprof集成实操
Context跨层传递最佳实践
在HTTP handler中注入超时与取消信号,避免goroutine泄漏:
func handleRequest(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
defer cancel() // 必须显式调用,否则资源不释放
if err := process(ctx); err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
}
}
r.Context()继承请求生命周期;WithTimeout返回新ctx与cancel函数,需defer确保执行。
Error Wrapping与诊断增强
if err := db.QueryRow(ctx, sql).Scan(&user); err != nil {
return fmt.Errorf("fetching user %d: %w", id, err) // %w保留原始error链
}
%w支持errors.Is()/errors.As()精准匹配,便于错误分类处理。
pprof动态启用开关
| 端点 | 用途 | 安全建议 |
|---|---|---|
/debug/pprof/ |
概览页 | 仅限内网或认证后访问 |
/debug/pprof/profile?seconds=30 |
CPU采样 | 避免生产环境长时采集 |
graph TD
A[HTTP请求] --> B{pprof已启用?}
B -->|是| C[启动CPU profile]
B -->|否| D[返回404]
2.4 跨仓库协作流程:CLA签署、CI/CD门禁绕过策略、Bot交互话术与Reviewer心理预判
CLA自动核验与Bot响应话术
当PR提交时,cla-bot通过GitHub App监听pull_request事件,调用/api/v1/cla/check验证签名状态:
# .github/workflows/cla-check.yml
- name: Verify CLA
uses: opensource-cla/cla-checker@v2.3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
org-name: "acme-inc" # 关联CLA签署组织
require-signed: true # 强制签署才允许合并
该动作在PR描述末尾自动追加✅/❌状态徽章,并触发评论模板。若未签署,Bot发送预设话术:“Hi @contributor,感谢贡献!请先签署ACME CLA以继续审核流程。”
CI/CD门禁绕过策略(仅限白名单)
| 场景 | 绕过条件 | 审计要求 |
|---|---|---|
| 文档更新 | docs/** + chore:前缀 |
自动记录至audit-log/ci-bypass.csv |
| 依赖微调 | package-lock.json变更且无src/改动 |
需@infra-team二次确认 |
Reviewer心理预判要点
- 提前标注“WIP: needs API contract review”可降低等待焦虑;
- 在
CONTRIBUTING.md中明示平均响应时间(如“90% PRs reviewed within 8h工作时间”),提升协作确定性。
2.5 合并后影响力放大:自动同步changelog、生成GoDoc示例、触发下游依赖方验证
数据同步机制
PR 合并后,CI 流水线自动解析 git log --oneline HEAD~1..HEAD 提取变更范围,驱动三路协同:
- 同步
CHANGELOG.md(按语义化版本自动归类 feat/fix/chore) - 调用
godoc -ex -html生成含可运行示例的文档片段 - 向依赖方 Webhook 发送
{"repo": "lib-go", "commit": "a1b2c3d", "impacted_packages": ["svc-auth", "cli-tool"]}
自动化流水线执行逻辑
# .github/workflows/merge-trigger.yml 片段
- name: Generate GoDoc examples
run: |
go run golang.org/x/tools/cmd/godoc@latest \
-ex -src -template=doc.tmpl \
./pkg/auth # ← 指定包路径,-ex 表示包含可执行示例
-ex 启用示例代码提取(需函数名以 Example* 开头且含 Output: 注释),-template 指向自定义 HTML 渲染模板,确保示例与 API 文档强绑定。
下游验证触发流程
graph TD
A[PR Merged] --> B[解析依赖图谱]
B --> C{影响 svc-auth?}
C -->|是| D[触发 svc-auth CI]
C -->|否| E[跳过]
| 触发条件 | 验证动作 | 超时阈值 |
|---|---|---|
go.mod 版本更新 |
运行 go test -race |
8min |
| 接口签名变更 | 执行契约测试(Pact) | 5min |
第三章:撰写2篇高穿透力技术文档
3.1 深度源码解析文档:以TiDB的PD调度器Go Client为例的接口契约建模
PD(Placement Driver)是TiDB集群的元数据与调度中枢,其Go Client通过pd.Client抽象暴露强契约化的调度接口。核心契约体现在GetOperator()与PutOperator()的幂等性、超时语义及错误分类上。
接口契约关键约束
- 所有RPC调用默认携带
context.WithTimeout(ctx, 5s) Operator结构体字段不可空(如ID,Status为必填枚举)- 错误返回严格遵循
errors.Is(err, pd.ErrOperatorNotFound)等哨兵错误
典型调用示例
// 获取待执行调度操作
op, err := client.GetOperator(ctx, regionID)
if errors.Is(err, pd.ErrOperatorNotFound) {
// 无待调度任务,非异常路径
return nil
}
该调用隐含契约:regionID必须为已注册Region,否则返回ErrInvalidRegion;op若非nil,则op.Status必为Running或Pending。
| 字段 | 类型 | 含义 | 契约约束 |
|---|---|---|---|
ID |
uint64 |
全局唯一调度ID | 不可为0 |
Status |
OperatorStatus |
调度生命周期状态 | 枚举值限定为Pending/Running/Success/Failed |
graph TD
A[Client.GetOperator] --> B{Region存在?}
B -->|否| C[ErrInvalidRegion]
B -->|是| D{Operator存在?}
D -->|否| E[ErrOperatorNotFound]
D -->|是| F[返回Operator+Status]
3.2 生产级最佳实践指南:Kubernetes Operator中Go泛型Controller的错误恢复模式
核心恢复策略分层设计
- 瞬时错误(如临时API超时):指数退避重试 + context.WithTimeout
- 持久错误(如Schema不匹配):进入
ErrorReconcile状态,触发事件告警并暂停队列 - 不可恢复错误(如RBAC缺失):主动标记
RequeueAfter=0并上报ConditionTypeFailed
泛型控制器中的结构化错误处理
func (r *GenericReconciler[T]) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
obj := new(T)
if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
if apierrors.IsNotFound(err) {
return ctrl.Result{}, nil // 资源已删除,无需重试
}
return ctrl.Result{RequeueAfter: 5 * time.Second}, err // 可重试错误
}
// ...业务逻辑
}
RequeueAfter控制退避间隔;apierrors.IsNotFound是K8s官方错误分类工具,避免误判;泛型T确保类型安全的同时复用错误路径。
恢复行为决策矩阵
| 错误类型 | 重试策略 | 状态更新 | 日志级别 |
|---|---|---|---|
IsServerTimeout |
指数退避(max 30s) | Status.Conditions |
Warn |
IsForbidden |
停止重试 | Status.ObservedGeneration = 0 |
Error |
graph TD
A[Reconcile开始] --> B{Get资源失败?}
B -->|是| C[分类K8s API错误]
B -->|否| D[执行业务逻辑]
C --> E[NotFound→忽略]
C --> F[Timeout→RequeueAfter]
C --> G[Forbidden→标记失败Condition]
3.3 Docker Go SDK进阶手册:基于containerd-shim-v2的生命周期钩子注入与调试技巧
containerd-shim-v2 支持在容器生命周期关键节点(prestart、poststart、poststop)动态注入钩子,需通过 runtime.v1 配置显式声明:
// 创建容器时嵌入钩子配置
spec := &rspec.Spec{
Hooks: &rspec.Hooks{
Prestart: []rspec.Hook{{
Path: "/usr/local/bin/prestart-hook",
Args: []string{"prestart-hook", "--container-id", "{{.ContainerID}}"},
Env: []string{"HOOK_TYPE=prestart"},
}},
},
}
逻辑分析:
Prestart钩子在容器命名空间创建后、用户进程exec前触发;{{.ContainerID}}由 shim 渲染为实际 ID;Env可透传上下文变量供钩子脚本消费。
调试关键路径
- 使用
ctr -n moby events --type=task实时监听 shim 事件流 - 检查
/run/containerd/io.containerd.runtime.v2.task/moby/<id>/log.json获取钩子执行日志
常见钩子行为对照表
| 钩子类型 | 触发时机 | 是否阻塞容器启动 | 典型用途 |
|---|---|---|---|
prestart |
命名空间就绪后 | 是 | 安全策略注入、cgroup预设 |
poststart |
用户进程 PID 确认后 | 否 | 监控探针注册、服务发现上报 |
poststop |
容器进程完全退出后 | 否 | 清理网络/存储资源 |
graph TD
A[Create Container] --> B[shim-v2 初始化]
B --> C{Prestart Hook?}
C -->|Yes| D[执行钩子并等待退出码]
D -->|0| E[继续启动流程]
C -->|No| E
E --> F[启动用户进程]
F --> G[Poststart Hook 异步触发]
第四章:完成3次高价值社区答疑
4.1 GitHub Issue深度响应:用Go pprof+trace分析Docker build失败的goroutine泄漏根因
某次 CI 中 docker build 卡死在 RUN go build 阶段,go tool pprof -goroutines 显示超 2000 个 goroutine 持久存活。
诊断流程
- 启用
GODEBUG=gctrace=1观察 GC 压力 - 采集 trace:
go run -gcflags="-l" main.go & sleep 30 && kill -SIGUSR2 $! - 分析
pprof -http=:8080 cpu.pprof
关键泄漏点定位
// build/executor.go:127 —— 错误地复用未关闭的 http.Client
client := &http.Client{
Transport: &http.Transport{ // 缺少 MaxIdleConnsPerHost 控制
DialContext: dialer,
},
}
// ❌ 未设置 Timeout / IdleConnTimeout → 连接池 goroutine 永不退出
该 client 被注入至构建上下文,每次 docker build --build-arg 触发新构建时重复实例化,但底层 persistConn.readLoop goroutine 因连接空闲未回收而堆积。
pprof 对比数据
| 指标 | 正常构建 | 故障构建 |
|---|---|---|
| Goroutines | ~150 | 2147 |
net/http.(*persistConn).readLoop |
0 | 1892 |
graph TD
A[Build Start] --> B[New http.Client]
B --> C{IdleConnTimeout set?}
C -->|No| D[ReadLoop goroutine leaks]
C -->|Yes| E[Auto-close after idle]
4.2 CNCF Slack实时攻坚:在TiDB SIG-Cloud频道用Go test -bench定位Region分裂性能拐点
在TiDB SIG-Cloud频道的深夜协同中,开发者通过go test -bench快速复现Region分裂延迟突增现象:
go test -bench=BenchmarkSplitRegion -benchmem -benchtime=10s \
-args --region-size=96MB --concurrency=32
该命令以32并发模拟批量分裂,
--region-size=96MB逼近PD默认分裂阈值(96MB),-benchtime=10s延长观测窗口以捕获GC与Raft日志写入竞争导致的毛刺。
关键参数影响分析
--region-size:低于96MB时分裂频次上升但延迟平稳;≥96MB后P99延迟跳升37%(见下表)--concurrency:超过CPU核心数×2后,goroutine调度开销反超吞吐收益
| Region Size | Avg Latency (ms) | P99 Latency (ms) | Throughput (ops/s) |
|---|---|---|---|
| 64MB | 8.2 | 21.5 | 1420 |
| 96MB | 11.7 | 29.8 | 1380 |
| 128MB | 13.1 | 33.6 | 1290 |
根因定位路径
graph TD
A[Slack消息报障] --> B[复现脚本共享]
B --> C[基准测试横向对比]
C --> D[pprof火焰图聚焦raftstore::split]
D --> E[发现region split prewrite阶段锁竞争]
4.3 Kubernetes Discuss技术帖重构:将模糊提问转化为可复现的Go test case并附PR链接
当用户在 Kubernetes Discuss 提出“Pod 有时不触发 readiness probe”的模糊问题时,核心挑战在于缺失最小可复现路径。我们将其提炼为一个结构化 Go 测试用例:
func TestReadinessProbeRaceCondition(t *testing.T) {
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "test-pod", Namespace: "default"},
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Name: "nginx",
Image: "nginx:1.25",
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{HTTPGet: &corev1.HTTPGetAction{
Port: intstr.FromInt(80),
}},
InitialDelaySeconds: 0,
PeriodSeconds: 1, // 高频探测暴露竞态
},
}},
},
}
// 构造含 probe 状态更新的 fake client
fakeClient := fake.NewSimpleClientset(pod)
}
该测试模拟高频 readiness probe 下的 status 更新时序,PeriodSeconds: 1 是关键复现参数——它放大了 kubelet 与 API server 的状态同步窗口。
关键转化步骤
- ✅ 提取原始日志中的 Pod YAML 片段
- ✅ 注入可控 probe 参数(
InitialDelaySeconds,PeriodSeconds) - ✅ 使用
fakeclientset隔离集群依赖
PR 链接与影响
| 仓库 | PR 号 | 关联 Issue |
|---|---|---|
| kubernetes/kubernetes | #126789 | kubernetes#123456 |
graph TD
A[Discourse 模糊提问] --> B[提取环境/配置片段]
B --> C[构造最小 Pod + Probe 语义]
C --> D[注入 fake client 与断言]
D --> E[提交可执行 test + 文档注释]
4.4 答疑影响力沉淀:自动生成FAQ Markdown+Go Playground可运行示例+视频切片锚点
当用户在文档页提交高频问题,系统自动触发三重沉淀流水线:
流程概览
graph TD
A[用户提问] --> B[语义聚类+意图识别]
B --> C[生成FAQ Markdown]
B --> D[注入Go Playground可执行片段]
B --> E[绑定视频时间戳锚点]
Go Playground 示例生成
// 自动生成的可运行示例:演示context.WithTimeout用法
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
select {
case <-time.After(1 * time.Second):
fmt.Println("timeout!")
case <-ctx.Done():
fmt.Println("done:", ctx.Err()) // 输出:done: context deadline exceeded
}
}
逻辑分析:该示例精准复现FAQ中“如何避免goroutine泄漏”场景;context.WithTimeout参数为context.Background()(根上下文)和500*time.Millisecond(超时阈值),defer cancel()确保资源及时释放。
沉淀元数据对照表
| 组件 | 字段名 | 示例值 |
|---|---|---|
| FAQ Markdown | anchor_id |
faq-context-timeout |
| Video Anchor | video_id |
vid-2024-go-concurrency |
| Playground | runnable_hash |
sha256:ab3f...e8c1 |
第五章:官方背书获取路径与长期影响力护城河构建
官方认证的阶梯式准入机制
以 CNCF(Cloud Native Computing Foundation)为例,项目需经历 Sandbox → Incubating → Graduated 三阶段评审。2023年,KubeVela 通过提交 17 份技术治理文档、完成 3 轮安全审计(含 CVE-2023-2431 漏洞响应 SLA 承诺)、接受 TOC 成员 12 小时现场代码审查后,从 Incubating 升级为 Graduated 项目。该过程强制项目建立可追溯的贡献者协议(CLA)、标准化 release 签名流程(使用 cosign v2.2+ 和 Fulcio 签名服务),并接入 CNCF CI/CD 流水线(基于 Tide + Prow 构建)。
社区共建与厂商协同的双轨验证
TiDB 在获得 MySQL 兼容性官方认证(MySQL 8.0.32 认证套件全量通过)的同时,主动向 Oracle 提交 JDBC 驱动兼容性测试报告(共覆盖 412 个 SQL 语法边界用例),推动其在 MySQL 官方文档「Compatible Alternatives」栏目中被列为推荐方案。此举使阿里云、PingCAP 联合客户在金融核心系统迁移中缩短合规审批周期 68%(某股份制银行实测数据)。
技术标准嵌入与反向定义权争夺
OpenTelemetry 的 Trace SDK 已被 W3C Trace Context 规范(v1.3)直接引用为参考实现;其 SpanContext 序列化格式更成为 AWS X-Ray、Azure Monitor、Google Cloud Trace 的默认 wire protocol。这意味着任何云厂商若想保持可观测性互操作性,必须实现 OpenTelemetry 的 tracestate 字段解析逻辑——技术事实标准已转化为协议层强制约束。
| 背书类型 | 获取周期 | 关键交付物 | 影响半径(典型) |
|---|---|---|---|
| CNCF Graduated | 14–18月 | 3+ 生产级用户案例、TOC 投票通过 | 全球云原生采购决策链 |
| ISO/IEC 27001认证 | 6–9月 | SOC2 Type II 报告、密钥轮转审计日志 | 金融/政务招标硬性门槛 |
| FIDO Alliance认证 | 4–6月 | UAF/U2F 协议栈完整实现、硬件安全模块集成 | 消费级身份认证生态准入 |
flowchart LR
A[提交 GitHub Issue 至官方仓库] --> B{是否符合 RFC 流程?}
B -->|否| C[退回补全设计文档与兼容性矩阵]
B -->|是| D[进入 TSC 投票队列]
D --> E[≥2/3 TSC 成员赞成]
E --> F[发布正式 Logo 使用许可]
E --> G[同步更新官网 Partner 页面]
F --> H[授权企业使用 “Certified by XXX” 标识]
开源协议与商业授权的动态平衡
Rust 编译器 rustc 在获得 Mozilla 基金会官方背书后,将 MIT/Apache-2.0 双许可证升级为“MIT OR Apache-2.0 WITH LLVM-exception”,明确允许闭源链接 Rust 标准库而不触发 GPL 式传染条款。该修订直接促成微软 Windows Subsystem for Linux 2 中 Rust 工具链的深度集成,并推动 AWS Lambda Runtime for Rust 进入 GA 阶段。
文档即权威的可信锚点建设
Elasticsearch 官方文档每个版本均附带机器可读的 OpenAPI 3.1 Schema(位于 /docs/api/rest/spec/),且所有示例请求/响应均经 CI 自动回放验证(每日执行 2,387 条断言)。当用户在 Kibana 控制台点击「Copy as cURL」时,生成命令自动注入 X-Elastic-Product: 8.12.0 头部,确保调试流量被路由至对应版本文档服务器——技术文档本身已成为 API 行为的事实仲裁者。
供应链安全的可验证承诺
SLSA(Supply-chain Levels for Software Artifacts)Level 3 认证要求构建环境全程不可变、所有二进制文件具备 provenance(来源证明)。2024 年,Helm Charts 官方仓库通过 Google Chainguard 镜像签名服务,为每个 chart 包生成 SLSA Provenance JSON-LD 文件,内含完整构建链路哈希(含 GitHub Actions Runner ID、容器镜像 digest、Git commit 签名),供 Kubernetes admission controller 实时校验。
