美文网首页
订单调度系统

订单调度系统

作者: zxRay | 来源:发表于2019-05-04 23:31 被阅读0次

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

v1版本

发货单的状态流转为
单据到履约系统后会被停30分钟进行Hold单,因为用户下单后有很大概率会在半小时内退款,Hold单30分钟可以很有效的减少仓库的实操成本。当时的调度处理如下:
  1. 依赖了一个分布式的任务调度中间件
  2. Hold单任务3分钟执行一次,下发任务1分钟执行一次。两者调度流程是一样的
  3. 因为数据分了512张表,所以第一次调度时随机选中一台服务器,然后生成32个任务,每个任务扫描16张表,然后将这些任务投递给集群内所有机器。这个功能依赖于任务调度中间件

v2版本

很快的在一次大促中,仓库系统被打挂了,因为外包仓库的WMS系统性能太差,需要对下发做流控。当时做流控碰到两个问题:
1、下发任务一分钟一次,需要改成秒级
2、数据分了512张表,按指定流量捞取很不方便

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

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

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

v3版本

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

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开始抗不住了。此时,我们决定将订单调度跟下发的业务逻辑彻底解耦,所以引入了消息中间件,调度流程变为

这步改动引入了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版本

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

v7版本

系统跑到这个版本,开始出现瓶颈,瓶颈主要体现在性能跟业务功能两个方面
性能
1、订单量越来越大,单表的订单池中订单数越来越多
2、自从开放能力给第三方仓库后,接入的合作仓库上升到了上百家,为了在生产端做流控,master每次调度都需要查订单池表上百次
业务功能
1、hold单类型的增加,有之前的半小时hold单变成了:
---- 1>、各个仓库对hold单时间要求不一致,有的甚至希望不hold直接下发,增加用户的退款成本从而减少退款数
----- 2>、有些hold单需要消息触发才能往下推进,比如拼团、负卖
------3>、有些商家希望订单经过他们审核才可以发货
------4>、hold类型是可以组合的,比如拼团的订单,用户拼团成功之后,还可以hold单固定时间,或者说一个订单即可以是负卖又可以是拼团

2、特殊订单拦截功能的增加
---- 1>、一些黄牛订单需要拦截,而规则必须是可配的。比如拦截指定用户、指定订单、指定商品、指定收货人等等
---- 2>、运营上需求可能说某一个商品类目的订单需要拦截下来,或者商品上有某一个标等也可以拦截下来
---- 3>、符合某些规则的订单需要优先下发,而这些规则是可配的

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

模型

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

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

整体流程

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

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

技术实现
1、 队列采用redis的zset
2、调度cron采用quartz
3、订单抓取采用zset的range,每次range的量不会很大

相关文章

  • 订单调度系统

    履约是一个承上启下的系统,上承前端交易,往下对接各种仓配系统。提供定仓、建单、接单、拆单、合单、截单等等业务功能,...

  • MES订单管理与外部系统的关系

    MES订单管理与外部系统的关系:订单管理系统整体上属于以作业计划、生产调度子系统为核心的一个子系统,与其关联的外部...

  • 关于采购系统

    一、创建流程: 采购人员根据业务需求或者预警系统生成相应的采购申请单,领导审批后生成采购订单,给调度中心,调度中心...

  • MES系统工序级的生产计划调度

    MES系统生产调度是指以作业、订单、批量、成批以及工作订单等形式管理生产单元中的物料流或信息流。当车间中有事件出现...

  • 数据调度平台系统二大种类及其实现方法与流程

    什么是调度系统 调度系统,更确切地说,作业调度系统(Job Scheduler)或者说工作流调度系统(workfl...

  • 调度管理系统平台OMS——调度计划版块

    1 OMS调度系统平台 电网调度技术支持系统(OMS)系统是调度专业使用频率最高的系统平台。它是集成调度所有专业的...

  • Z_HPC_作业调度系统

    作业调度系统的发展 作业调度系统的分类 作业调度系统的特性比较 发展: 分类: 特性比较:

  • linux定时任务

    一 、简介 Linux下的任务调度分为两类,系统任务调度和用户任务调度 系统任务调度:系统需要定期执行的任务,比如...

  • Linux I/O 调度方法及读写测试

    操作系统的调度有 CPU调度 CPUschedulerIO调度 IOscheduler IO调度...

  • Linux 系统之crontab命令

    Linux下的任务调度分为两类,系统任务调度 和 用户任务调度。 系统任务调度:系统周期性所要执行的工作,比如写缓...

网友评论

      本文标题:订单调度系统

      本文链接:https://www.haomeiwen.com/subject/gciinqtx.html