构建一个简易的火车票售票系统,Newbe.Claptrap框架用例,第一步——业务分析
Newbe.Claptrap 框架非常适合于解决具有并发问题的业务系统。火车票售票系统,就是一个非常典型的场景用例。
本系列我们将逐步从业务、代码、测试和部署多方面来介绍,如何使用 Newbe.Claptrap 框架来构建一个简易的火车票售票系统。
吹牛先打草稿
让我们来首先界定一下这个简易的火车售票系统所需要实现的业务边界和性能要求。
业务边界
该系统仅包含车票的余票管理部分。即查询剩余座位,下单买票减座。
而生成订单信息,付款,流量控制,请求风控等等都不包含在本次讨论的范围中。
业务用例
- 查询余票,能够查询两个车站间可用的车次以及剩余座位数量。
- 查询车次对应的车票余票,能够查询给定的车次,在各个车站之间还有多少剩余座位。
- 支持选座下单,客户能够选择给定的车次及座位,并下单买票。
性能要求
- 查询余票和下单购票消耗平均不得超过 20ms。该时间仅包含服务端处理时间,即页面网络传输,页面渲染等等不是框架关心的部分不计算在内。
- 余票查询可以存在延时,但不是超过 5 秒钟。延时,即表示,可能查询有票,但是下单无票的情况是被允许的。
难点分析
余票管理
火车票余票管理的难点,其实就在于其余票库存的特殊性。
普通的电商商品,以 SKU 为最小单位,每个 SKU 之间相互独立,互不影响。
例如:当前我正在售卖原产自赛博坦星球的阿姆斯特朗回旋加速炮,红色和白色两款分别一万个。那么用户在下单时,只要分别控制红色和白色两款的库存分别不超卖即可。他们之间没有相互关系。
但是火车票余票,却有所不同,因为余票会受到已卖票起终点而受到影响。下面结合一个简单的逻辑模型,来详细的了解一下这种特殊性。
现在,我们假设存在一个车次,分别经过 a,b,c,d 四个站点,同时,我们简化场景,假设车次中只有一个座位。
那么在没有任何人购票之前,这个车次的余票情况就如下所示:
起终点 | 余票量 |
---|---|
a,b | 1 |
a,c | 1 |
a,d | 1 |
b,c | 1 |
b,d | 1 |
c,d | 1 |
如果现在有一位客户购买了一张 a,c 的车票。那么由于只有一个座位,所以,除了 c,d 其他的余票就都没有。余票情况就变成了如下所示:
起终点 | 余票量 |
---|---|
a,b | 0 |
a,c | 0 |
a,d | 0 |
b,c | 0 |
b,d | 0 |
c,d | 1 |
更直白一点,如果有一位客户购买了全程车票 a,d,那么所有的余票都将全部变为 0。因为这个座位上始终都坐着这位乘客。
这也就是火车票的特殊性:同一个车次的同一个座位,其各个起终点的余票数量,会受到已售出的车票的起终点的影响。
延伸一点,很容易得出,同一车次的不同座位之间是没有这种影响的。
余票查询
正如上一节所述,由于余票库存的特殊性。对于同一个车次 a,b,c,d,其可能的购票选择就有 6 种。
并且我们很容易就得出,选择的种类数的计算方法实际上就是在 n 个站点中选取 2 个的组合数,即 c(n,2) 。
那么如果有一辆经过 34 个站点的车次,其可能的组合就是 c(34,2) = 561 。
如何高效应对可能存在的多种查询也是该系统需要解决的问题。
Claptrap 逻辑设计
Actor 模式是天生适合于解决并发问题的设计模式。基于该理念之上的 Newbe.Claptrap 框架自然也能够应对以上提到的难点。
最小竞争资源
类比多线程编程中“资源竞争”的概念,这里笔者提出在业务系统中的“最小竞争资源”概念。借助这个概念可以很简单的找到如何应用 Newbe.Claptrap 的设计点。
例如在笔者售卖阿布斯特朗回旋加速炮的例子中,同款颜色下的每个商品都是一个“最小竞争资源”。
注意,这里不是说,同款颜色下的所有商品是一个“最小竞争资源”。因为,如果对一万个商品进行编号,那么抢购一号商品和二号商品,本身其实不存在竞争关系。因此,每个商品都是一个最小竞争资源。
那么在火车票余票的例子中,最小竞争资源则是:同一车次上的同一个座位。
正如上面所述,同一车次上的同一座位,在选择不同的起终点是,余票情况时存在竞争关系的。具体一点,比如笔者想购买 a,c 的车票,而读者想买 a,b 的车票。那么我们就有竞争关系,我们只会有一个人能够成功的购买到这个“最小竞争资源”。
这里有一些笔者认为可用的例子:
- 在一个只允许单端登录的业务系统中,一个用户的登录票据就是最小竞争资源
- 在一个配置系统中,每个配置项都是最小竞争资源
- 在一个股票交易市场中,每个买单或者卖单都是最小竞争资源
这是笔者自己的臆造词,没有参考其他资料,如果有类似资料或者名词可以佐证该内容,也希望读者可以留言说明。