第一章:【灵雀云Go开发者紧急通告】:Go 1.21+升级后tls.Config.GetConfigForClient返回nil引发的静默连接中断
自 Go 1.21 起,crypto/tls 包对 tls.Config.GetConfigForClient 回调函数的行为进行了严格语义强化:若该回调返回 nil,TLS 握手将立即中止并静默关闭连接(不触发 Write 错误、不返回 net.OpError),仅记录 tls: client didn't provide a certificate 类似日志(即使未启用客户端证书)。该变更在 Go 1.21.0 中引入,在 Go 1.22+ 中进一步收紧,导致大量依赖动态 SNI 配置的灵雀云微服务出现偶发性 503/EOF 连接失败,且监控无明显 TLS 层告警。
根本原因分析
GetConfigForClient 的 nil 返回值被 Go TLS 栈解释为“拒绝该客户端”,而非“使用默认配置”。常见误用模式包括:
- 未覆盖所有 SNI 域名分支,缺失
default或通配符兜底逻辑; - 错误地将
nil作为“复用主配置”的信号(旧版行为); - 异步加载证书时,缓存未命中直接返回
nil而非阻塞等待或返回默认配置。
立即修复方案
将所有 GetConfigForClient 实现替换为显式返回有效 *tls.Config:
func (s *Server) GetConfigForClient(chi *tls.ClientHelloInfo) (*tls.Config, error) {
// ✅ 正确:必须返回非nil *tls.Config
cfg := s.defaultTLSConfig.Clone() // 使用 Clone() 避免并发修改
if sniCert, ok := s.certCache.Load(chi.ServerName); ok {
cfg.Certificates = []tls.Certificate{sniCert.(tls.Certificate)}
}
return cfg, nil // ❌ 绝对禁止 return nil, nil
}
验证与观测清单
| 检查项 | 命令/方法 |
|---|---|
| TLS 握手失败率 | curl -v --tlsv1.2 https://your-service/ 2>&1 | grep "SSL connection" |
| Go 版本确认 | go version(需 ≥1.21.0) |
| 运行时诊断 | 启用 GODEBUG=tls13=1 并捕获 tls: no config returned for client hello 日志 |
所有灵雀云 Go 服务必须在 48 小时内完成代码修正,并通过 go test -run TestTLSHandshakeWithSNI 验证多域名握手稳定性。未修复服务将面临生产环境连接抖动加剧风险。
第二章:TLS握手机制演进与Go 1.21+运行时行为变更深度解析
2.1 Go TLS协议栈中GetConfigForClient的语义契约与历史实现逻辑
GetConfigForClient 是 tls.Config 中的关键回调函数,用于在 TLS 握手初期动态选择服务端配置(如证书、密码套件、ALPN 协议等),其语义契约要求:必须幂等、无副作用、返回非 nil 配置或明确错误,且不可阻塞。
核心调用时机
- 在 ClientHello 解析后、ServerHello 构造前触发
- 每次新连接(非复用 session)必调用
- 若返回
nil,TLS 栈将使用tls.Config默认配置
历史实现演进关键点
- Go 1.3 引入,仅支持静态证书切换
- Go 1.8 支持 SNI 主机名匹配(
ClientHelloInfo.ServerName) - Go 1.19 起严格校验返回值非空,否则 panic
func (s *server) getConfigForClient(chi *tls.ClientHelloInfo) (*tls.Config, error) {
if chi.ServerName == "api.example.com" {
return s.apiTLSConfig, nil // 动态绑定域名专属配置
}
return s.defaultTLSConfig, nil // fallback
}
该回调接收
*tls.ClientHelloInfo,含ServerName、SupportedProtos、CipherSuites等只读字段;返回*tls.Config必须预先完成Certificates初始化,否则握手失败。
| 版本 | 关键行为变更 |
|---|---|
| 1.3 | 初始支持,无 SNI 感知 |
| 1.8 | chi.ServerName 可靠可用 |
| 1.21 | 支持 GetConfigForClient 返回 nil 表示拒绝连接(RFC 8446 兼容) |
graph TD
A[ClientHello received] --> B{Parse SNI & ALPN}
B --> C[Call GetConfigForClient]
C --> D[Return *tls.Config?]
D -->|Yes| E[Proceed handshake]
D -->|Nil| F[Use tls.Config default]
D -->|Panic on nil in <1.21| G[Handshake fail]
2.2 Go 1.21引入的ServerName匹配优化及其对nil返回值的隐式语义扩展
Go 1.21 对 crypto/tls.Config.GetConfigForClient 的 ServerName 匹配逻辑进行了关键优化:当 ServerName 为空字符串或不匹配任何已注册域名时,不再强制要求返回非-nil 配置,而是允许返回 nil 并隐式启用默认配置。
匹配逻辑变更示意
func (c *Config) GetConfigForClient(chi *tls.ClientHelloInfo) (*tls.Config, error) {
if chi.ServerName == "" {
return nil, nil // ✅ Go 1.21起:nil config + nil error = use c (default)
}
if cfg := c.namedConfigs[chi.ServerName]; cfg != nil {
return cfg, nil
}
return nil, nil // ✅ 合法:触发 fallback 到 *tls.Config receiver
}
逻辑分析:
nil, nil返回值被 runtime 解释为“无显式匹配,采用外层*tls.Config实例”,避免了此前必须克隆配置的冗余操作;chi.ServerName为空时也统一纳入该语义。
行为对比(Go 1.20 vs 1.21)
| 场景 | Go 1.20 行为 | Go 1.21 行为 |
|---|---|---|
ServerName=="" |
要求返回有效 *tls.Config |
允许 return nil, nil → 使用 c |
| 无匹配域名 | panic 或需手动 fallback | nil, nil 触发静默 fallback |
关键语义升级路径
graph TD
A[ClientHello] --> B{ServerName empty?}
B -->|yes| C[return nil, nil]
B -->|no| D{Match in namedConfigs?}
D -->|yes| E[return matched *tls.Config]
D -->|no| C
C --> F[Use outer *tls.Config receiver]
2.3 灵雀云多租户网关场景下SNI路由与Config协商失败的真实调用链复现
在灵雀云多租户网关中,SNI路由与控制面Config同步存在强时序耦合。当租户证书未就绪而TLS握手提前触发时,将触发协商失败。
关键失败路径
- 网关监听
443端口,解析ClientHello中的SNI字段(如tenant-a.alipaycloud.com) - 异步查询控制面
/v1/config?sn=tenant-a,但HTTP超时(默认800ms) - 缓存中无对应路由规则,回退至默认404或503响应
Config同步异常日志片段
[WARN] sni_router.go:127 — failed to fetch tenant config for 'tenant-a': context deadline exceeded (timeout=800ms)
[ERROR] tls_handshake.go:92 — no matching route found after SNI 'tenant-a.alipaycloud.com'
该日志表明:SNI解析成功,但Config获取因网络抖动或控制面延迟超时,导致路由决策缺失。
失败调用链(mermaid)
graph TD
A[Client TLS ClientHello] --> B{SNI extracted}
B --> C[Query Config API]
C -->|timeout| D[Cache miss → fallback]
C -->|success| E[Apply route + cert]
D --> F[503 Service Unavailable]
| 阶段 | 耗时阈值 | 触发条件 |
|---|---|---|
| SNI解析 | TLS record解码完成 | |
| Config查询 | 800ms | 控制面gRPC/HTTP超时 |
| 路由缓存写入 | 5ms | 仅当Config返回200时生效 |
2.4 基于net/http/httptest与crypto/tls的最小可验证PoC构建与断点追踪实践
构建无依赖TLS测试服务
// 创建自签名证书用于本地HTTPS测试
cert, key := generateSelfSignedCert()
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("secured"))
}))
ts.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
ts.StartTLS() // 启动带TLS的测试服务器
generateSelfSignedCert() 返回 *tls.Certificate,ts.TLS 赋值后启用HTTPS;StartTLS() 替代 Start(),使 ts.URL 自动变为 https:// 协议。
断点注入与请求链路观测
- 在 Handler 内插入
runtime.Breakpoint()触发调试器中断 - 使用
http.DefaultTransport.(*http.Transport).TLSClientConfig.InsecureSkipVerify = true绕过证书校验 ts.Client()返回已配置信任该测试证书的*http.Client
关键参数对照表
| 参数 | 类型 | 作用 |
|---|---|---|
ts.TLS |
*tls.Config |
注入服务端证书链 |
ts.URL |
string |
自动切换为 https://127.0.0.1:port |
ts.Client() |
*http.Client |
预置信任服务端证书 |
graph TD
A[httptest.NewUnstartedServer] --> B[设置ts.TLS]
B --> C[调用StartTLS]
C --> D[生成https URL]
D --> E[客户端自动信任]
2.5 对比Go 1.20 vs 1.21+ TLS handshake trace日志差异定位静默中断根因
Go 1.21 引入 GODEBUG=tlstrace=1 环境变量,替代旧版 GODEBUG=http2debug=2 中混杂的 TLS 日志,实现协议层解耦。
日志结构演进
- Go 1.20:TLS 握手日志嵌套在 HTTP/2 调试输出中,无独立事件标记,
ClientHello/ServerHello无时间戳与状态码 - Go 1.21+:每条 TLS 事件带
tls: [event]前缀、毫秒级时间戳及错误分类(如tls: handshake failure (unexpected_message))
关键差异对比表
| 特性 | Go 1.20 | Go 1.21+ |
|---|---|---|
| 日志开关 | GODEBUG=http2debug=2 |
GODEBUG=tlstrace=1 |
ClientHello 可见性 |
隐式(需正则提取) | 显式独立行,含 SNI 和 ALPN 列表 |
| 静默中断标识 | 无明确 alert 事件 |
输出 tls: alert: internal_error |
// 启用 Go 1.21+ TLS 追踪(需 runtime.SetEnv 或启动时设置)
os.Setenv("GODEBUG", "tlstrace=1")
该代码启用细粒度 TLS 事件捕获,tlstrace 会注入 crypto/tls 内部钩子,在 handshakeMessage 构造/解析时触发日志,参数 1 表示启用全部握手阶段(ClientHello → Finished),避免 1.20 中因 HTTP/2 复用导致的握手日志丢失。
graph TD
A[Client发起Connect] --> B{Go 1.20}
B --> C[HTTP/2 debug混入TLS片段]
B --> D[无alert事件→静默EOF]
A --> E{Go 1.21+}
E --> F[tlstrace独立事件流]
E --> G[显式alert: decode_error]
第三章:灵雀云生产环境影响面评估与风险分级策略
3.1 基于APIServer、Ingress Controller、Service Mesh Sidecar三类组件的影响矩阵分析
不同控制面组件对流量可观测性、策略执行时机与服务拓扑感知能力存在本质差异:
控制面职责边界对比
| 组件类型 | 策略生效层 | 流量可见粒度 | TLS终止位置 | 是否感知应用协议 |
|---|---|---|---|---|
| APIServer | Kubernetes API 层 | 资源变更事件 | 否 | 否(仅HTTP/HTTPS元数据) |
| Ingress Controller | L7 网关层 | HTTP/GRPC 请求级 | 是(可选) | 是(需插件支持) |
| Service Mesh Sidecar | 应用连接层 | 连接+请求+RPC 方法级 | 是(mTLS) | 是(如HTTP/2, Thrift) |
数据同步机制
Sidecar 通过 xDS 协议与控制平面同步配置,典型流程如下:
# envoy.yaml 片段:启用RDS动态路由发现
dynamic_resources:
lds_config:
resource_api_version: V3
path: /etc/istio/proxy/bootstrap.yaml
cds_config:
resource_api_version: V3
path: /etc/istio/proxy/bootstrap.yaml
该配置使 Envoy 在启动时加载初始引导文件,并通过 gRPC 长连接持续接收 RouteConfiguration(RDS)、Cluster(CDS)等动态资源更新,避免重启即可生效新路由规则。
graph TD
A[Control Plane] -->|xDS v3 gRPC Stream| B(Envoy Sidecar)
B --> C[监听器更新]
B --> D[路由表热加载]
B --> E[集群健康探测]
3.2 连接复用率、TLS会话恢复成功率、ALPN协商失败率等核心指标监控方案落地
数据采集层设计
通过 eBPF(bpftrace)在内核侧无侵入捕获 TLS 握手事件,过滤 SSL_write/SSL_read 及 SSL_set_session 调用上下文,提取 session_id, session_ticket, alpn_protocol 字段。
# 捕获 TLS 1.3 Session Ticket 复用事件
bpftrace -e '
kprobe:ssl_set_session {
@sess_reuse[tid] = count();
}
kretprobe:ssl_get_servername /retval == 0/ {
@alpn_fail[tid] = count();
}
'
逻辑分析:kprobe:ssl_set_session 触发即表示会话被显式复用;kretprobe:ssl_get_servername 返回 0 表明 ALPN 协商未匹配服务端支持列表。@sess_reuse 和 @alpn_fail 是 per-thread 计数器,避免锁竞争。
指标聚合与告警阈值
| 指标名 | 健康阈值 | 数据来源 |
|---|---|---|
| 连接复用率 | ≥85% | reused_conn / total_conn |
| TLS 会话恢复成功率 | ≥92% | session_hit / session_lookup |
| ALPN 协商失败率 | ≤0.5% | alpn_fail / tls_handshakes |
上报与可视化闭环
graph TD
A[eBPF采集] --> B[Prometheus Exporter]
B --> C[Metrics: tls_session_reused_total]
C --> D[Grafana 面板实时下钻]
3.3 客户侧存量Go SDK与自定义TLS配置代码的自动化扫描工具设计与实操
为精准识别客户代码中潜在的TLS配置风险(如 InsecureSkipVerify: true、弱密码套件、未校验证书链),我们设计轻量级AST扫描工具 tls-scan-go。
核心扫描逻辑
基于 go/ast 和 go/parser 构建遍历器,聚焦 crypto/tls.Config 字面量及字段赋值节点。
// 示例:匹配 tls.Config 字面量中 InsecureSkipVerify = true 的模式
if ident, ok := expr.(*ast.Ident); ok && ident.Name == "InsecureSkipVerify" {
if kv, ok := parent.(*ast.KeyValueExpr); ok {
if lit, ok := kv.Value.(*ast.BasicLit); ok && lit.Kind == token.TRUE {
report(v.FileSet.Position(kv.Pos()), "insecure TLS skip verify detected")
}
}
}
该代码块通过AST节点类型断言逐层定位键值对,kv.Pos() 提供精确源码位置;v.FileSet 支持跨文件路径映射,保障报告可追溯性。
支持的检测项概览
| 风险类型 | 检测目标 | 严重等级 |
|---|---|---|
| 跳过证书验证 | InsecureSkipVerify: true |
CRITICAL |
| 明文HTTP重定向 | http.Client 未启用 TLS 且无重定向防护 |
HIGH |
| 过时 TLS 版本 | MinVersion: tls.VersionTLS10 |
MEDIUM |
执行流程
graph TD
A[解析Go源码目录] --> B[构建AST并遍历]
B --> C{是否命中TLS配置节点?}
C -->|是| D[提取字段值 & 校验策略]
C -->|否| E[继续遍历]
D --> F[生成JSON/CSV报告]
第四章:面向云原生场景的兼容性修复与工程化加固方案
4.1 非侵入式GetConfigForClient包装器设计:兜底Config生成与错误注入测试
该包装器在不修改原有 GetConfigForClient 接口签名的前提下,实现运行时行为增强。
核心能力矩阵
| 能力 | 生产环境 | 测试环境 | 实现方式 |
|---|---|---|---|
| 兜底配置返回 | ✅ | ✅ | defaultConfig |
| 错误注入 | ❌ | ✅ | errorMode flag |
| 原函数透传 | ✅ | ✅ | originalFn() |
错误注入测试逻辑
func GetConfigForClientWrapper(clientID string) (Config, error) {
if testMode && shouldInjectError(clientID) {
return Config{}, errors.New("injected network timeout") // 模拟下游不可用
}
cfg, err := originalGetConfigForClient(clientID)
if err != nil {
return defaultConfigFor(clientID), nil // 非侵入式兜底
}
return cfg, nil
}
逻辑分析:当启用
testMode且命中预设 clientID 规则时,主动返回可控错误;否则调用原函数。失败时返回基于 clientID 动态生成的默认配置(如 region-aware fallback),避免 panic 或空值传播。
数据同步机制
兜底配置通过本地内存缓存 + TTL 刷新,保障低延迟与一致性。
4.2 灵雀云Kubernetes Operator中tls.Config热重载机制增强与版本感知降级逻辑
核心增强点
- 支持
tls.Config字段级变更监听(如MinVersion、CurvePreferences) - 引入
generation与observedGeneration双版本戳,实现精确版本感知
热重载触发流程
// watch tls.Config ConfigMap 变更,触发 reconcile
if cm.Data["tls.yaml"] != oldCM.Data["tls.yaml"] {
r.requeueWithDelay(100 * time.Millisecond) // 避免抖动
}
该逻辑确保仅当 TLS 配置内容实际变更时才触发重载;100ms 延迟防止高频更新导致的 goroutine 泛滥。
降级策略决策表
| 条件 | 行为 | 触发场景 |
|---|---|---|
spec.tls.version < observedVersion |
拒绝降级,保留当前配置 | 用户误删高版本字段 |
apiVersion mismatch |
自动 fallback 至兼容 schema v1beta1 | CRD 升级后旧客户端写入 |
版本协同流程
graph TD
A[ConfigMap 更新] --> B{解析 tls.Config}
B --> C{校验 generation}
C -->|匹配| D[应用新配置]
C -->|不匹配| E[触发版本感知降级判断]
E --> F[保留旧配置并记录 Warning 事件]
4.3 基于eBPF的TLS握手阶段观测探针开发:实时捕获nil Config触发上下文
核心观测点定位
TLS握手早期(crypto/tls.(*Conn).Handshake)若传入 nil *tls.Config,将触发 panic 前的栈回溯关键帧。eBPF需在 go_tls_handshake_entry(Go运行时符号)处挂载 kprobe。
探针核心逻辑
// tls_handshake_probe.c
SEC("kprobe/go_tls_handshake_entry")
int trace_handshake(struct pt_regs *ctx) {
struct tls_ctx ctx_data = {};
bpf_probe_read_kernel(&ctx_data.config_ptr, sizeof(ctx_data.config_ptr),
(void *)PT_REGS_PARM2(ctx)); // PARM2 = *Config 指针
if (!ctx_data.config_ptr) {
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &ctx_data, sizeof(ctx_data));
}
return 0;
}
逻辑分析:
PT_REGS_PARM2(ctx)对应 Go 函数第二参数(*tls.Config),bpf_probe_read_kernel安全读取指针值;零值即为nil触发告警。该检查规避了直接解引用空指针导致探针崩溃。
上下文捕获字段
| 字段 | 类型 | 说明 |
|---|---|---|
config_ptr |
u64 |
*tls.Config 地址(0 表示 nil) |
pid |
u32 |
进程 ID |
timestamp_ns |
u64 |
纳秒级时间戳 |
触发链路示意
graph TD
A[Go 应用调用 tls.Conn.Handshake] --> B[kprobe 拦截 go_tls_handshake_entry]
B --> C{读取 PARM2 config_ptr}
C -->|== 0| D[perf event 输出 nil 上下文]
C -->|!= 0| E[静默继续]
4.4 CI/CD流水线中Go版本兼容性门禁规则(go.mod require + build constraint校验)
在CI流水线中,需双重校验Go版本兼容性:go.mod声明与源码实际约束必须严格一致。
go.mod require 版本锚定
// go.mod
module example.com/app
go 1.21
require (
golang.org/x/net v0.25.0 // requires Go >= 1.21
)
该go 1.21指令强制构建器使用≥1.21的Go工具链;require项隐含最低SDK兼容要求,CI需解析并比对GOROOT版本。
构建约束动态拦截
// version_guard.go
//go:build go1.21
// +build go1.21
package main
若CI检测到GOVERSION=1.20,此文件被忽略,导致main包缺失——构建直接失败,实现硬性门禁。
门禁校验流程
graph TD
A[CI拉取代码] --> B[解析go.mod中的go指令]
B --> C[提取//go:build版本约束]
C --> D[比对当前GOROOT版本]
D -->|不匹配| E[中断构建并报错]
| 校验维度 | 工具链命令 | 失败示例 |
|---|---|---|
go.mod声明 |
go version -m go.mod |
go 1.22 but GOROOT=1.20 |
build tag约束 |
go list -f '{{.GoFiles}}' ./... |
空包列表触发无主入口错误 |
第五章:总结与展望
核心技术栈的生产验证结果
在2023年Q3至2024年Q2的12个关键业务系统迁移项目中,基于Kubernetes + Argo CD + OpenTelemetry构建的可观测性交付流水线已稳定运行586天。故障平均定位时间(MTTD)从原先的47分钟降至6.3分钟,发布回滚成功率提升至99.97%。某电商大促期间,该架构支撑单日峰值1.2亿次API调用,Prometheus指标采集延迟始终低于800ms(P99),Jaeger链路采样率动态维持在0.8%–3.2%区间,未触发资源过载告警。
典型故障复盘案例
2024年4月某支付网关服务突发5xx错误率飙升至18%,通过OpenTelemetry追踪发现根源为下游Redis连接池耗尽。进一步分析Envoy代理日志与cAdvisor容器指标,确认是Java应用未正确关闭Jedis连接导致TIME_WAIT状态连接堆积。团队立即上线连接池配置热更新脚本(见下方代码块),3分钟内恢复服务:
kubectl patch cm redis-config -n payment --type='json' \
-p='[{"op": "replace", "path": "/data/maxIdle", "value":"200"}]'
多云环境适配挑战
当前架构在AWS EKS、阿里云ACK及本地VMware Tanzu三套环境中部署时,暴露了CNI插件兼容性差异:Calico在Tanzu上出现NodePort端口映射冲突,而Cilium在ACK上需额外启用eBPF加速开关。下表对比了各平台关键配置项的适配方案:
| 平台 | CNI插件 | 必启参数 | 验证方式 |
|---|---|---|---|
| AWS EKS | Cilium | --enable-bpf-masquerade |
cilium status --verbose |
| 阿里云ACK | Terway | terway-eniip模式 |
kubectl get eni -A |
| VMware Tanzu | Calico | FELIX_IPINIPENABLED=false |
calicoctl get ippool -o wide |
开源组件演进路线图
根据CNCF年度报告与社区PR合并趋势,未来18个月将重点跟进两项变更:一是Kubernetes v1.31计划弃用PodSecurityPolicy(PSP),全面切换至Pod Security Admission(PSA)策略;二是Istio 1.23将默认启用Wasm扩展沙箱,要求所有自定义Filter重构为WebAssembly模块。团队已在测试集群完成PSA策略迁移验证,覆盖全部142个命名空间的baseline与restricted级别策略。
边缘计算场景落地进展
在智能工厂边缘节点部署中,采用K3s + KubeEdge组合实现低带宽环境下的断网续传。当厂区网络中断超过12分钟时,边缘侧自动启用本地MQTT Broker缓存设备上报数据,并通过edgecontroller定时同步至中心集群。实测数据显示,128台PLC设备在72小时离线状态下,数据完整率达100%,重连后平均同步耗时2.1分钟。
社区协作机制优化
建立“每日信号灯”机制:早9点自动拉取GitHub Issues中area/observability标签且priority/critical的未解决项,推送至企业微信机器人。过去三个月推动3个核心Issue进入Kubernetes SIG-Auth维护队列,其中关于RBAC规则批量校验的PR已被v1.32合并。
安全合规强化路径
等保2.1三级要求中“审计日志留存180天”条款,促使团队改造现有Loki日志系统:将冷数据自动归档至MinIO对象存储并启用WORM(Write Once Read Many)策略,同时通过OPA Gatekeeper实施日志保留策略强制校验,任何试图缩短保留期的ConfigMap更新均被拒绝。
工程效能度量体系
引入DORA四大指标作为持续改进基线:部署频率(当前:日均4.7次)、变更前置时间(中位数:28分钟)、变更失败率(0.8%)、服务恢复时间(P95:9.2分钟)。每个迭代周期生成可视化看板,驱动SRE团队聚焦瓶颈环节——例如发现CI阶段单元测试覆盖率低于85%的模块,其变更失败率高出均值3.2倍。
技术债偿还优先级矩阵
采用影响范围×修复成本二维模型评估待处理事项:将“遗留Spring Boot 2.5应用升级至3.2”列为高优(影响47个微服务,但自动化脚本已就绪),而“替换Nginx Ingress为Gateway API”暂列中优(需协调6个业务方联调)。每季度末召开技术债评审会,依据线上事故根因分析动态调整矩阵权重。
