这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天
一、本堂课重点内容
- 系统设计方法论
- 电商秒杀业务介绍
- 课程实践
二、详细知识点介绍
1. 系统设计方法论
1.1 为什么要做系统设计
-
个人:
- 来自面试
- 提升个人能力
- 拓展技术视野
-
工作:
- 业务驱动
- 系统需要重构
- 突破和创新
1.2 如何做系统设计
流程: 场景-->存储-->服务-->扩展
- 场景分析:什么系统 需要哪些功能多大的并发量
- 存储设计:数据如何组织 sql 存储 nosql存储
- 服务设计:业务功能实现和逻辑整合
- 可扩展性:解决设计缺陷
1.3 如何保证可用性和稳定性
-
链路梳理
- 核心链路
- 流量漏斗
- 强弱依赖
-
可观测性
- 链路追踪
- 核心监控
- 业务报警
-
全链路测试
- 压力测试
- 负载测试
- 容量测试
-
稳定性控制
- 系统限流
- 业务兜底
- 熔断降级
-
容灾演练
- 混沌工程
- 应急手册
- 容灾预案
2. 电商秒杀业务介绍
三要素:人、货、场
2.1 电商介绍
商品:具有交易价值和属性的信息载体。
SPU : Standard Product Unit(标准化产品单元),区分品种。是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。 SKU : Stock Keeping Unit(库存量单位),区分单品。可以是以件、盒、托盘等为单位,通常表示:规格、颜色、款式。
2.2 秒杀业务的特点
- 瞬时流量高
- 读多写少
- 实时性要求高
2.3 秒杀的挑战
- 资源有限性
- 反欺诈性
- 高性能
- 防止超卖
- 流量管控
- 扩展性
- 鲁棒性(系统稳定性)
2.4 如何设计秒杀系统
还是按照场景-->存储-->服务-->扩展的流程分析:
2.4.1 场景(Scenario)
-
功能:
- 秒杀活动发布
- 秒杀商品详情
- 秒杀下单
-
并发
- 万人参与秒杀
- QPS 1w+,QPS是每秒请求数,就是说服务器在一秒的时间内处理了多少个请求
- TPS 1K+,TPS是服务器每秒处理的事务数,是软件测试结果的测量单位。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。
2.4.2 存储(Storage)
MySQL —>Redis —>Localcache
2.4.3 服务(Service)
-
子服务:
- 用户服务
- 风控服务
- 活动服务
- 订单服务
-
基础组件
- ID生成器
- 缓存组件
- MQ组件
- 限流组件 这里要注意,id 使用雪花生成算法,确保唯一id;缓存使用redis MQ使用rabbitMQ;限流组件可以使用微服务中的。
2.4.4 扩展(Scale)
- 流量隔离、CDN、缓存优化、流量管控
- 数据库扩展、MQ扩展、Redis扩展、服务水平扩展、服务垂直扩展
3. 课程实践
部分代码:
import com.camp.promotion.convertet.Converter;
import com.camp.promotion.model.CreateActivityModel;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.function.Function;
@Data
public class CreateActivityRequest implements Converter<CreateActivityRequest, CreateActivityModel> {
/**
* 活动名称
*/
@NotNull(message = "promoName不能为null")
private String promoName;
/**
* 开始时间
*/
@NotNull(message = "开始时间不能为null")
private Long startTime;
/**
* 结束时间
*/
@NotNull(message = "结束时间不能为null")
private Long endTime;
/**
* 参与秒杀的商品
*/
@NotEmpty(message = "秒杀商品不能为空")
private List<CreatePromoProductRequest> promoProducts;
@Override
public CreateActivityModel convert(Function<CreateActivityRequest, CreateActivityModel> f) {
return f.apply(this);
}
}
import com.camp.promotion.convertet.ConvertFunction;
import com.camp.promotion.convertet.Converter;
import com.camp.promotion.model.CreateOrderModel;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.function.Function;
@Data
public class CreateOrderRequest implements Converter<CreateOrderRequest, CreateOrderModel> {
/**
* 用户id
*/
@NotNull(message = "用户id不能为null")
private Long userId;
/**
* 活动id
*/
@NotNull(message = "秒杀活动id不能为null")
private Long promoId;
/**
* 活动spu id
*/
@NotNull(message = "spuId不能为null")
private Long spuId;
/**
* 活动sku id
*/
@NotNull(message = "skuId不能为null")
private Long skuId;
/**
* 数量
*/
@Min(1)
@NotNull(message = "quantity不能为null")
private Integer quantity;
/**
* 价格
*/
@Min(0)
@NotNull(message = "promoPrice不能为null")
private Integer promoPrice;
/**
* 总价
*/
@Min(0)
@NotNull(message = "totalAmount不能为null")
private Integer totalAmount;
/**
* 收货地址id
*/
@NotNull(message = "addressId不能为null")
private Long addressId;
/**
* 收货地址
*/
@NotBlank(message = "shippingAddress不能为null")
private String shippingAddress;
@Override
public CreateOrderModel convert(Function<CreateOrderRequest, CreateOrderModel> f) {
return f.apply(this);
}
}
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@Data
public class CreatePromoProductRequest {
/**
* 活动spu id
*/
@NotNull(message = "spuId不能为null")
private Long spuId;
/**
* 活动sku id
*/
@NotNull(message = "skuId不能为null")
private Long skuId;
/**
* 数量
*/
@Min(1)
@NotNull(message = "promoStock不能为null")
private Integer promoStock;
/**
* 价格
*/
@Min(0)
@NotNull(message = "promoPrice不能为null")
private Integer promoPrice;
}
三、个人总结
- 服务无状态(当前服务不存储状态,不存储数据)
- 批量写入(降低系统压力很好的手段)
- 最终一致性(redis ,MQ 等可能导致数据不一致)
四、引用参考
- 【实践课】手把手教你做系统设计 - 第五届字节跳动青训营