调度系统和服务器有什么区别,订单调度系统

履约是一个承上启下的系统,上承前端交易,往下对接各种仓配系统。提供定仓、建单、接单、拆单、合单、截单等等业务功能,这些功能以发货单为载体,订单调度系统就是如何调度发货单进行业务流转的系统

v1版本

发货单的状态流转为

bfaae4327084

单据到履约系统后会被停30分钟进行Hold单,因为用户下单后有很大概率会在半小时内退款,Hold单30分钟可以很有效的减少仓库的实操成本。当时的调度处理如下:

bfaae4327084

依赖了一个分布式的任务调度中间件

Hold单任务3分钟执行一次,下发任务1分钟执行一次。两者调度流程是一样的

因为数据分了512张表,所以第一次调度时随机选中一台服务器,然后生成32个任务,每个任务扫描16张表,然后将这些任务投递给集群内所有机器。这个功能依赖于任务调度中间件

v2版本

很快的在一次大促中,仓库系统被打挂了,因为外包仓库的WMS系统性能太差,需要对下发做流控。当时做流控碰到两个问题:

1、下发任务一分钟一次,需要改成秒级

2、数据分了512张表,按指定流量捞取很不方便

所以,我们将待下发的订单单独做了一张单表保存做为待下发的订单池(这个没有分表,因为当时单量不大,而且下发成功后会立刻删除,所以这个表数据量不大)

bfaae4327084有了订单池后,下发的调度变为

bfaae4327084

没有了分表,所以去掉了分发的过程,下发任务每一次都只在一台服务器运行,从而控制了下发的流量

v3版本

v2版本在跑了时候,偶尔会出现下发流量翻倍或者连续好几秒没有任何下发流量,这个问题在大促的时候是个极大的风险点,究其原因是分布式任务中间件秒级调度并不能100%保证。所以我们打算在下发流程中去掉对该中间件的依赖。改起来也很容易:用zk选个master,在master上启动定时任务,监听机器变更消息,重新选主

bfaae4327084

v4版本

业务发展比较快,单量越来越多,同时接入的仓库也从1家增加到多家,导致下发的流量倍增,master捞取订单流控变为:

for(store_i : stores)

qps_i = qps(store_i)

List orders_i = selectFromDB(store_i, qps_i)

process(orders_i)

同时,在1s内既要捞取订单同时还有处理下发流程,master开始抗不住了。此时,我们决定将订单调度跟下发的业务逻辑彻底解耦,所以引入了消息中间件,调度流程变为

bfaae4327084 这步改动引入了MQ,将下发的业务逻辑从单个master处理,扩展到集群内任何服务器都可以执行,大大的减轻了master的负载

v5版本

当引入MQ后,发现有时候下发给仓库的流量会超过限定值,特别是下游系统出问题的时候。原因是在下发消息消费失败后,会踢回去依靠MQ重试,也即在异常情况下多了一个数据源。为了解决这个问题,一开始我们采取在消费端也加上限流:

1、使用Guava的RateLimiter,因为这个是单机限流,所以我们订阅zk消息获取集群机器数,计算单机的限流值。但是,问题是RateLimiter会阻塞线程,而当时我们好几个仓库同时使用一个线程池,导致下发的吞吐量抖降

2、使用redis做集群限流,但重试机制依赖于MQ,而MQ采用的指数退避算法,在很短一段时间内会连续重试。这些异常的订单不停的重试,挤压了正常的流量,导致一段时间内正常下发的流量明显小于流控值

所以,我们还是决定不在消费端限流,也就是去掉MQ的重试

producer(orderTask):

sendMQ(orderTask)

orderTask.retryScheTime = getRetryScheTime(orderTask)

persistToTB(orderTask)

consumer(orderTask):

processStatus = fasle

try{

...

processStatus = true

}catch(e){

processStatus = false

}

if(processStatus == true){

deleteDB(orderTask)

}

return true

v6版本

在这个版本我们将系统的能力开放给了第三方仓库,导致对接的仓库一下变成了几十家。因为对接了第三方仓库的接口,这些接口的稳定性不可保证,所以需要在消费端做线程池的隔离,避免一个仓库接口挂了,导致整个系统下发出现异常。

bfaae4327084

v7版本

系统跑到这个版本,开始出现瓶颈,瓶颈主要体现在性能跟业务功能两个方面

性能

1、订单量越来越大,单表的订单池中订单数越来越多

2、自从开放能力给第三方仓库后,接入的合作仓库上升到了上百家,为了在生产端做流控,master每次调度都需要查订单池表上百次

业务功能

1、hold单类型的增加,有之前的半小时hold单变成了:

---- 1>、各个仓库对hold单时间要求不一致,有的甚至希望不hold直接下发,增加用户的退款成本从而减少退款数

----- 2>、有些hold单需要消息触发才能往下推进,比如拼团、负卖

------3>、有些商家希望订单经过他们审核才可以发货

------4>、hold类型是可以组合的,比如拼团的订单,用户拼团成功之后,还可以hold单固定时间,或者说一个订单即可以是负卖又可以是拼团

2、特殊订单拦截功能的增加

---- 1>、一些黄牛订单需要拦截,而规则必须是可配的。比如拦截指定用户、指定订单、指定商品、指定收货人等等

---- 2>、运营上需求可能说某一个商品类目的订单需要拦截下来,或者商品上有某一个标等也可以拦截下来

---- 3>、符合某些规则的订单需要优先下发,而这些规则是可配的

因为这些需求跟瓶颈,在v7版本我们对订单调度做了一次全新的架构升级

模型

bfaae4327084

首先抽象出调度策略这个模型,每个策略跟一个队列一一对应。每个策略可以配置入队的规则,出队的规则,这个队列调度的cron表达式以及队列的消费速度等等,同时总共有3大类型的队列:

1、 hold单队列

2、下发队列

3、重试队列

调度策略与服务器的关系

因为要对每个队列做流控,那么每个队列只在一台服务器进行调度是最容易控制的

bfaae4327084

整体流程

bfaae4327084

负载均衡

这里的负载是一个机器上的调度策略的权重值。每个调度策略都有一个权重,这个权重表示这个调度策略的计算消耗。比如,一个调度策略每隔1s从一个队列拉取一个订单的权重为1,那么每隔1s从一个队列拉取10个订单的权重可以简单设为10. 因为一个调度策略只在一台服务器上进行调度,才能有效的进行流量控制。所以这里的负载均衡就是根据策略权重,将策略分配到某个服务器,从而保证他们调度所消耗的计算资源大体是一样的。比如,有5个调度策略,3台服务器,负载策略可能为:

bfaae4327084

通过Zookeeper监听机器上下线,同时调度策略发生变化会发送MQ消息通知负载均衡器重新计算负载

技术实现

1、 队列采用redis的zset

2、调度cron采用quartz

3、订单抓取采用zset的range,每次range的量不会很大

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值