共计 2564 个字符,预计需要花费 7 分钟才能阅读完成。
业务场景与技术挑战
Skill 市场本质是连接技能提供者与需求者的双边平台,典型场景包括:1)实时匹配地理位置的技能服务,2)交易过程中的信用风控,3)高并发下的库存一致性。技术挑战集中在秒级响应、分布式事务和防欺诈三个维度。

技术选型:为什么选择 Spring Boot
微服务框架选型时,我们对比了 Spring Cloud 和 Dubbo:
- Spring Cloud 优势 :
- 全家桶式解决方案(Config/Bus/Gateway 等)
- 与 Spring Boot 深度集成,约定优于配置
-
Netflix 组件成熟度高(2023 年统计 70% 企业选用)
-
Dubbo 适用场景 :
- 内部服务间 RPC 调用占比高的场景
- 需要精细控制序列化协议的情况
最终选择 Spring Boot 的核心原因:
- 快速原型开发能力(starter 依赖自动装配)
- Actuator 提供的生产级监控端点
- 与 Redis/Security 等组件开箱即用的集成
领域驱动设计实战
聚合根划分示例
/**
* 技能服务聚合根(含价格、库存等不变规则)*/
public class Skill {
@EmbeddedId
private SkillId id;
@Column(nullable = false)
private Money price;
@Version
private Long version; // 乐观锁
// 领域方法示例
public void reserveInventory(int quantity) {if(this.inventory < quantity) {throw new DomainException("库存不足");
}
this.inventory -= quantity;
}
}
分布式库存锁实现
Redis+Lua 原子操作
/**
* 使用 Lua 保证库存扣减原子性
* @param skillId 技能 ID
* @param quantity 购买数量
* @return 剩余库存
*/
public Long deductInventory(String skillId, int quantity) {
String script =
"if redis.call('exists', KEYS[1]) == 1 then\n" +
"local stock = tonumber(redis.call('get', KEYS[1]))\n" +
"if stock >= tonumber(ARGV[1]) then\n" +
"return redis.call('decrby', KEYS[1], ARGV[1])\n" +
"end\n" +
"end\n" +
"return -1";
Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
Collections.singletonList("skill:stock:" + skillId),
String.valueOf(quantity)
);
if(result == -1) {throw new BusinessException("库存不足");
}
return result;
}
OAuth2.0 授权实现
Security 配置核心代码
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.inMemory()
.withClient("web-app")
.secret(passwordEncoder.encode("secret"))
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("read", "write");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager);
}
}
性能优化实战
压测数据对比(JMeter 1000 并发)
| 架构类型 | 平均响应时间 | QPS |
|---|---|---|
| Redis 单节点 | 238ms | 4200 |
| Redis 集群 (3 节点) | 89ms | 11200 |
分库分表策略
-- 按用户 ID 哈希分片(16 个库×16 表)CREATE TABLE skill_order_${dbIdx}_${tblIdx} (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL COMMENT '分片键',
skill_id VARCHAR(32) NOT NULL,
INDEX idx_user (user_id) -- 分片键必须建索引
) ENGINE=InnoDB;
安全防护方案
XSS 过滤工具类
/**
* 使用 Jsoup 进行 HTML 标签白名单过滤
* @param html 原始输入
* @return 安全文本
*/
public static String cleanHtml(String html) {Whitelist whitelist = Whitelist.basicWithImages()
.addTags("div", "p")
.addAttributes(":all", "style");
return Jsoup.clean(html, whitelist);
}
交易幂等性方案
- 客户端生成唯一请求 ID
- 服务端 Redis 记录已处理 ID(SETNX+ 过期时间)
- 数据库唯一索引约束
生产检查清单
- K8s 部署 :
- 配置 HPA 基于 CPU>70% 自动扩容
- 使用 ConfigMap 管理环境变量
-
就绪探针检查 Redis 连接
-
监控指标 :
- Grafana 监控 Redis 缓存命中率
- 告警规则:订单失败率 >1%
- ELK 收集 JVM FullGC 日志
开放式问题
- 如何设计技能评价体系能有效识别刷单行为?考虑:
- 时间维度异常检测(短时间内大量好评)
-
社交关系图谱分析
-
冷启动阶段如何平衡供需匹配?可能的方案:
- 虚拟订单激励(平台补贴)
- 基于 LBS 的定向推广
正文完
发表至: 技术分享
近两天内
