共计 1751 个字符,预计需要花费 5 分钟才能阅读完成。
从一次促销故障说起
去年双十一大促时,某电商平台的「技能兑换」模块(Skill Service)出现连锁故障:由于用户积分服务(Point Service)响应延迟,导致技能兑换接口线程池耗尽,进而引发整个商品详情页超时。事后分析发现,这两个服务间存在强依赖——每次兑换请求都需要实时校验用户积分余额。

架构选型:解耦与性能的双重挑战
通信协议对比
- REST:
- 优势:调试直观,兼容性好
-
痛点:每次兑换需 3 次 HTTP 往返(查询积分→扣减→兑换)
-
gRPC:
- 优势:二进制协议节省带宽,支持流式处理(Streaming)
- 实测数据:相同 ECS 实例下,QPS 从 1200 提升至 3100
数据模式对比
-
CRUD:
UPDATE user_skills SET level=5 WHERE user_id=123; -- 状态覆盖问题 -
Event Sourcing:
INSERT INTO skill_events VALUES('123','LEVEL_UP',5); -- 完整溯源链
核心实现细节
事件契约定义(Protocol Buffers)
// 行号 1 -15
syntax = "proto3";
message SkillEvent {
string event_id = 1;
string user_id = 2;
enum EventType {
UNLOCK = 0;
LEVEL_UP = 1;
EXPIRE = 2;
}
EventType type = 3;
int32 value = 4;
google.protobuf.Timestamp occurred_at = 5;
}
带退避的重试机制(Go)
// 行号 17-35
func callWithRetry(ctx context.Context, fn func() error) error {
baseDelay := 100 * time.Millisecond
maxRetries := 3
for i := 0; i < maxRetries; i++ {err := fn()
if err == nil {return nil}
select {case <-time.After(baseDelay << i): // 指数退避
case <-ctx.Done():
return ctx.Err()}
}
return errors.New("max retries exceeded")
}
事件存储设计(PostgreSQL)
-- 行号 37-50
CREATE TABLE skill_events (
event_id UUID PRIMARY KEY,
user_id VARCHAR(36) NOT NULL,
event_type VARCHAR(20) NOT NULL,
payload JSONB NOT NULL,
version INTEGER NOT NULL, -- 用于乐观锁
created_at TIMESTAMPTZ DEFAULT NOW());
CREATE INDEX idx_skill_events_user ON skill_events(user_id);
性能优化成果
测试配置(k6)
// 行号 52-65
import http from 'k6/http';
import {check} from 'k6';
export let options = {
stages: [{ duration: '30s', target: 1000},
{duration: '1m', target: 5000}
],
thresholds: {http_req_duration: ['p(95)<300ms']
}
};
实测数据(AWS c5.xlarge)
| 方案 | QPS | P95 延迟 |
|---|---|---|
| HTTP 同步调用 | 1,200 | 450ms |
| gRPC 流式处理 | 3,100 | 210ms |
| 事件溯源 + 异步处理 | 4,800 | 150ms |
踩坑实录
- 事件版本兼容 :
- 新增字段时需保持旧版解析逻辑
-
采用 Protobuf 的 reserved 标记废弃字段
-
最终一致性 :
- 使用事务发件箱(Transaction Outbox)模式
- 补偿任务扫描未发布事件
延伸思考
事件存储的冷热分离方案:
– 热数据:保留最近 3 个月事件在 PostgreSQL
– 冷数据:归档至 S3 + Athena 查询
– 平衡点取决于业务查询频次与存储成本比
写在最后
这次架构改造最意外的收获是:通过事件溯源还原出某个技能被异常重置的全过程。这让我意识到,技术方案的选择不仅影响性能指标,更决定了系统的问题排查能力。或许在分布式系统中,可观测性本身就是一种生产力。
正文完
