【项目总结】易到家家政服务平台 —— 派单调度(7)

派单调度需求分析

在抢单业务中,用户下单成功由服务人员或机构进行抢单,抢单成功服务人员上门服务,除了抢单业务系统还设计了派单业务,由系统根据用户订单的特点自动派给合适的服务人员。

流程如下:

  1. 首先获取待分配的订单。
  2. 根据订单的属性,包括:地理位置、服务项目等去服务提供池中匹配服务提供者。
  3. 根据派单策略对符合条件的服务提供者进行规则匹配,每个派单策略通常有多个规则,从第一个规则逐个匹配其它规则。
  4. 最后获取匹配成功的服务提供者,系统进行机器抢单。
  5. 机器抢单成功,派单成功。
    在这里插入图片描述

系统设计

  1. 涉及到距离搜索,参考抢单业务需要借助Elasticsearch搜索服务人员,所以需要将服务人员同步到Elasticsearch。根据搜索匹配的条件:服务技能,服务时间、接单范围,需要将服务人员和机构的相关信息同步到Elasticsearch。
    同步的方式使用Canal+MQ实现。
  2. 如果为订单派单失败每隔3分钟再次对订单进行派单,考虑性能问题在redis创建派单池,调度程序扫描派单池获取订单,所以需要将MySQL中派单池的数据同步到Redis。
  3. 根据需求,派单策略有三种,后期还可能扩展,这里使用策略模式实现,提高系统扩展性。
  4. 根据需求,每个派单策略有多个规则,按规则逐个去匹配,只要匹配成功或规则使用完成,这里使用责任链模式,提高系统扩展性。
  5. 派单程序将订单和服务人员匹配成功,接下来调用抢单接口进行机器抢单,这样也体现公平性,因为派单的同步有服务人员也在抢单,这里是机器(平台)和人工(服务人员)在共同抢单。

服务提供池索引结构

根据设计,第一步需要向Elasticsearch中同步服务提供者的数据。
将customer数据库的serve_provider_sync表和orders数据库的serve_provider_sync表同步到ES的服务提供池。
在这里插入图片描述
customer数据库的serve_provider_sync表中除了evaluation_score字段以外,其它字段是由服务人员设置服务技能、接单开关、接单范围保存的信息
evaluation_score字段存储服务人员的评分,用户通过评价系统评分,由评价系统同步到此字段中。
orders数据库的serve_provider_sync表中记录了服务人员接单数统计,当服务人员或机构抢单成功后进行统计得到。

Redis派单池

  1. 订单分流处理
    在订单分流中对于距离服务开始时间在120分钟(默认值可修改)以内时将订单写入orders数据库的派单池表。
  2. 定时任务处理
    抢单池的订单没有人抢,距离服务开始时间在120分钟以内时将订单写入orders数据库的派单池表。
    派单池表的结构如下:
    在这里插入图片描述

Redis的派单池用什么数据结构呢?

根据需求,订单派单失败每隔3分钟再次对失败的订单进行派单,有什么办法可以在查询订单时将失败的订单过滤掉,并且还能根据时间对早进入派单池的订单进行优先派单,这里涉及到排序,很自然我们想到了Redis的SortedSet结构

value为是订单id、score为进入派单池的时间(时间戳,double类型),当派单失败我们score加3分钟(提升订单的优先级),第一次查询SortedSet查询score小于当前时间的订单

八股🌟:redis数据结构、zset

责任链模式🌟 —— 派单规则

责任链模式是一种行为型设计模式,它允许你将请求沿着处理者链进行传递,直到有一个处理者处理请求为止。每个处理者都可以决定是否将请求传递给下一个处理者。

根据派单的需求,根据订单信息从服务提供池中获取师傅及机构的信息,通过距离优先规则、评分优先规则等最终获取一个要派单的服务或机构。
在这里插入图片描述
数据处理规则的接口:

public interface IProcessRule {

    /**
     * 根据派单规则过滤服务人员
     * @param serveProviderDTOS
     * @return
     */
    List<ServeProviderDTO> filter(List<ServeProviderDTO> serveProviderDTOS);

    /**
     * 获取下一级规则
     *
     * @return
     */
    IProcessRule next();
}

实际上接口的两个方法实现对于每个具体规则来说都是一样的,只有doFilter过滤规则不同,我们可以抽象出一个抽象类让具体的规则来继承。

@Setter
public abstract class AbstractIDispatchRule implements IDispatchRule {
    /**
     * 下一条规则
     */
    private IDispatchRule next;

    public AbstractIDispatchRule(IDispatchRule next) {
        this.next = next;
    }

    public abstract List<ServeProviderDTO> doFilter(List<ServeProviderDTO> originServeProviderDTOS);

    @Override
    public List<ServeProviderDTO> filter(List<ServeProviderDTO> serveProviderDTOS) {
        List<ServeProviderDTO> result = this.doFilter(serveProviderDTOS);
        if(CollUtils.size(result) > 1 && next != null) {
            return next.filter(result);
        }else {
            return result;
        }
    }

    @Override
    public IDispatchRule next() {
        return next;
    }
}

对于每个具体的规则,只需要继承这个抽象类,然后实现 doFilter 规则即可。

@Setter
@Slf4j
public class AcceptNumDispatchRule extends AbstractIDispatchRule {

    public AcceptNumDispatchRule(IDispatchRule next) {
        super(next);
    }

    @Override
    public List<ServeProviderDTO> doFilter(List<ServeProviderDTO> originServeProviderDTOS) {
        // 1.判断originServeProviderDTOS列表是否少于2,少于2直接返回
        if (CollUtils.size(originServeProviderDTOS) < 2) {
            return originServeProviderDTOS;
        }
        //  2.按照比较器进行排序,排在最前方优先级最高
        originServeProviderDTOS = originServeProviderDTOS.stream().sorted(Comparator.comparing(ServeProviderDTO::getAcceptanceNum)).collect(Collectors.toList());
        // 3.遍历优先级最高一批数据
        ServeProviderDTO first = CollUtils.getFirst(originServeProviderDTOS);

        //获取相同级别的
        return originServeProviderDTOS.stream()
                .filter(origin -> Comparator.comparing(ServeProviderDTO::getAcceptanceNum).compare(origin, first) == 0)
                .collect(Collectors.toList());
    }
}

在这里插入图片描述

具体应用:

这个代码显然是可以继续优化的,不然每次有新的策略就要重复写如下的代码,不利于拓展。

        // 策略1:构建责任链,先距离优先,距离相同再判断评分
       IProcessRule rule = new AcceptNumRule(null);
        IProcessRule ruleChain = new DistanceRule(rule);
        // 策略2:构建责任链,先评分优先,评分相同再判断接单数
        IProcessRule rule = new AcceptNumRule(null);
        IProcessRule ruleChain = new ScoreRule(rule);
        // 策略3:构建责任链,先接单数优先,接单数相同再判断评分
        IProcessRule rule = new ScoreRule(null);
        IProcessRule ruleChain = new AcceptNumRule(rule);
/**
 * 最少接单优先策略
 * 先根据接单计算得分,每接1单等于1分,分值越高优先级越低,如果最高优先级数量大于1时按最少接单数规则计算得分,依次类推,算规则执行顺序如下:
 * 按最少接单数规则->按评分计算规则
 */
@Component("leastAcceptOrderDispatchStrategy")
@DispatchStrategy(DispatchStrategyEnum.LEAST_ACCEPT_ORDER)
public class LeastAcceptOrderDispatchStrategyImpl extends AbstractDispatchStrategyImpl {
    @Override
    protected IDispatchRule getRules() {
        // 按评分计算规则,评分越高优先级越高
        IDispatchRule evaluationDispatchRule = new EvaluationScoreDispatchRule(null);
        // 按最少接单数规则,接单数量越少优先级越高
        IDispatchRule acceptNumDispatchRule = new AcceptNumDispatchRule(evaluationDispatchRule);
        return acceptNumDispatchRule;
    }
}

策略模式🌟 —— 定义派单策略

通过将不同的规则进行组合,来决定具体要使用什么模式来派单。

根据需求我们平台支持距离优先策略、评分优先策略、最少接单优先策略**,针对上边的代码我们可以基于策略模式定义不同的策略去优化。**

在这里插入图片描述

策略接口
public interface IProcessStrategy {
    /**
     * 从服务人员/机构列表中获取高优先级别的一个,如果出现多个相同优先级随机获取一个
     *
     * @param serveProviderDTOS 服务人员/机构列表
     * @return
     */
    ServeProviderDTO getPrecedenceServeProvider(List<ServeProviderDTO> serveProviderDTOS);
}
抽象的策略实现类

每个策略类都需要实现getPrecedenceServeProvider(List serveProviderDTOS)方法,其逻辑是一样的,每个策略类组装的责任链是不同的,我们定义抽象类将共同的方法写在抽象类中

public abstract class AbstractStrategyImpl implements IProcessStrategy {

    private final IProcessRule processRule;

    public AbstractStrategyImpl() {
        this.processRule = getRules();
    }

    /**
     * 设置派单规则
     *
     * @return
     */
    protected abstract IProcessRule getRules();

    @Override
    public ServeProviderDTO getPrecedenceServeProvider(List<ServeProviderDTO> serveProviderDTOS) {
        // 1.判空
        if (CollUtils.isEmpty(serveProviderDTOS)) {
            return null;
        }

        // 2.根据优先级获取高优先级别的
        serveProviderDTOS = processRule.filter(serveProviderDTOS);

        // 3.数据返回
        // 3.1.唯一高优先级直接返回
        int size = 1;
        if ((size = CollUtils.size(serveProviderDTOS)) == 1) {
            return serveProviderDTOS.get(0);
        }
        // 3.2.多个高优先级随机返回
        int randomIndex = (int) (Math.random() * size);
        return serveProviderDTOS.get(randomIndex);
    }
}
具体的策略实现类

距离优先策略类

public class DistanceStrategyImpl extends AbstractStrategyImpl  {
    @Override
    protected IProcessRule getRules() {
        //构建责任链,先距离优先,距离相同再判断接单数
        IProcessRule acceptNumRule = new AcceptNumRule(null);
        IProcessRule ruleChain = new DistanceRule(rule);
        return ruleChain;
    }
}
测试及应用
//获取距离优先策略
        IProcessStrategy processStrategy = new DistanceStrategyImpl();
        //通过策略bean进行匹配处理
        ServeProviderDTO precedenceServeProvider = processStrategy.getPrecedenceServeProvider(serveProviderDTOS);

总体流程总结

  1. 从redis派单池查询订单
  2. 将派单任务放入线程池
  3. 执行派单任务:从服务提供者池找到符合条件的服务人员、获取派单策略、通过责任链模式匹配规则,匹配一个服务人员
  4. 匹配成功调用抢单接口进行机器抢单(相当于是平台和服务人员都在抢单,因为此时还有其他的机构或者服务人员在抢)
  5. 机器抢单成功则派单成功。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/968338.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

visual studio 在kylin v10上跨平台编译时c++标准库提示缺少无法打开的问题解决

情况1&#xff1a;提示无法打开 源文件 "string"之类导致无法编译 情况2:能编译&#xff0c;但无法打开这些库文件或标准库使用提示下划红色问题 解决方案&#xff1a; 一、通过工具->选项->跨平台里&#xff0c;在“远程标头IntelliSense管理器”更新下载一下…

SpringCould+vue3项目的后台用户管理的CURD【Taurus教育平台】

文章目录 一.SpringCouldvue3项目的后台用户管理的CURD【Taurus教育平台】 1.1 背景 二.用户列表&#xff08;分页查询&#xff09; 2.1 前端Vue3 &#xff08;Vue3-Element-Admin&#xff09;2.2 后端SpringCould 处理 三. 用户信息删除 3.1 前端Vue3 &#xff08;Vue3-Eleme…

Eclipse 插件开发相关概念

整理了Eclipse插件开发的概念&#xff0c;用于熟悉入门 SWT&#xff08;Standard Widget Toolkit&#xff09;标准图形工具箱 Java开发的GUI程序技术&#xff0c;由Eclipse开发&#xff0c;相比AWT、Swing更美观&#xff1b;对于目标平台上已经有的控件&#xff0c;SWT会直接使…

算法之 数论

文章目录 质数判断质数3115.质数的最大距离 质数筛选204.计数质数2761.和等于目标值的质数对 2521.数组乘积中的不同质因数数目 质数 质数的定义&#xff1a;除了本身和1&#xff0c;不能被其他小于它的数整除&#xff0c;最小的质数是 2 求解质数的几种方法 法1&#xff0c;根…

AndroidStudio查看Sqlite和SharedPreference

1.查看Sqlite 使用App Inspection&#xff0c;这是个好东西 打开方式&#xff1a;View → Tool Windows → App Inspection 界面如图&#xff1a; App inspection不但可以看Sqlite还可以抓包network和background task连抓包工具都省了。 非常好使 2.查看sharedPreference 使…

谈一谈数据库中的死锁问题

文章目录 死锁是什么&#xff1f;死锁的四个必要条件避免死锁的策略 本篇文章是基于《MySQL45讲》来写的个人理解与感悟。 死锁是什么&#xff1f; 死锁是指两个或两个以上的进程在执行过程中&#xff0c;由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。若无外力作用&a…

网络工程师 (31)VLAN

前言 VLAN&#xff08;Virtual Local Area Network&#xff09;即虚拟局域网&#xff0c;是一种将物理局域网划分成多个逻辑上独立的虚拟网络的技术。 一、定义与特点 定义&#xff1a;VLAN是对连接到的第二层交换机端口的网络用户的逻辑分段&#xff0c;不受网络用户的物理位置…

从深入理解 netty——》AI

想了很久&#xff0c;准备写一个系列从深入理解 netty——》AI。 先说下为啥要从netty开始&#xff0c;看看netty的重要性 rocketmq异步消息组件nacos微服务注册中心spring cloud gateway网关redission分布式缓存es全文检索sentinel流量控制&#xff0c;服务保护seata分布式…

从 0 开始本地部署 DeepSeek:详细步骤 + 避坑指南 + 构建可视化(安装在D盘)

个人主页&#xff1a;chian-ocean 前言&#xff1a; 随着人工智能技术的迅速发展&#xff0c;大语言模型在各个行业中得到了广泛应用。DeepSeek 作为一个新兴的 AI 公司&#xff0c;凭借其高效的 AI 模型和开源的优势&#xff0c;吸引了越来越多的开发者和企业关注。为了更好地…

在anaconda环境中构建flask项目的exe文件

一、创建并激活虚拟环境 conda create -n flask_env python3.9 # python版本根据项目需求安装 conda activate flask_env # 激活环境二、安装必要依赖 推荐使用conda&#xff0c;pip没尝试过&#xff0c;但是deepseek给出了命令 conda install flask …

腾讯云服务器中Ubuntu18.04搭建python3.7.0与TensorFlow1.15.0与R-4.0.3环境

所有踩过的坑&#xff0c;都化成了这条平坦的路 云服务器配置 基础配置选择竞价实例&#xff08;便宜/需求小&#xff09; 选择地区&#xff08;距离自己近的就行&#xff09; 实例配置选择异构计算&#xff08;能力较强&#xff0c;性价比高&#xff09;根据GPU显存需求选择…

金融风控项目-1

文章目录 一. 案例背景介绍二. 代码实现1. 加载数据2. 数据处理3. 查询 三. 业务解读 一. 案例背景介绍 通过对业务数据分析了解信贷业务状况 数据集说明 从开源数据改造而来&#xff0c;基本反映真实业务数据销售&#xff0c;客服可以忽略账单周期&#xff0c;放款日期账单金…

JAVA安全—Shiro反序列化DNS利用链CC利用链AES动态调试

前言 讲了FastJson反序列化的原理和利用链&#xff0c;今天讲一下Shiro的反序列化利用&#xff0c;这个也是目前比较热门的。 原生态反序列化 我们先来复习一下原生态的反序列化&#xff0c;之前也是讲过的&#xff0c;打开我们写过的serialization_demo。代码也很简单&…

基于Spring Boot的医院挂号就诊系统【免费送】

基于Spring Boot的医院挂号就诊系统 效果如下&#xff1a; 系统登陆页面 系统主页面 挂号页面 客服页面 挂号管理页面 公告信息管理页面 审核页面 在线咨询管理页面 研究背景 随着医疗技术的不断发展和人们健康意识的提高&#xff0c;医院作为提供医疗服务的核心机构&#x…

玩转适配器模式

文章目录 解决方案现实的举例适用场景实现方式适配器模式优缺点优点:缺点:适配器模式可比上一篇的工厂模式好理解多了,工厂模式要具有抽象的思维。这个适配器模式,正如字面意思,就是要去适配某一件物品。 假如你正在开发一款股票市场监测程序, 它会从不同来源下载 XML 格…

栈的简单介绍

一.栈 栈是一种先进后出的结构&#xff1a;&#xff08;先出来的是45&#xff0c;要出12就必须先把前面的数据全部出完。&#xff09; 2.实例化一个栈对象&#xff1a; 3.入栈&#xff1a; 4.出栈&#xff1a;&#xff08;当走完pop就直接弹出45了。&#xff09; 5.出栈的…

【Java】-【面试】-【Java进阶】

一、分布式 1、分布式锁 2、分布式ID 3、分布式事务

Leetcode - 周赛435

目录 一、3442. 奇偶频次间的最大差值 I二、3443. K 次修改后的最大曼哈顿距离三、3444. 使数组包含目标值倍数的最少增量四、3445. 奇偶频次间的最大差值 II 一、3442. 奇偶频次间的最大差值 I 题目链接 本题使用数组统计字符串 s s s 中每个字符的出现次数&#xff0c;然后…

kafka了解-笔记

文章目录 kafka快速上手Kafka介绍Kafka快速上手理解Kafka的集群工作机制Kafka集群的消息流转模型 Kafka客户端小型流转流程客户端工作机制 kafka快速上手 Kafka介绍 MQ的作用 MQ&#xff1a;MessageQueue&#xff0c;消息队列&#xff0c;是一种FIFO先进先出的数据结构&#…

支持向量机原理

支持向量机&#xff08;简称SVM&#xff09;虽然诞生只有短短的二十多年&#xff0c;但是自一诞生便由于它良好的分类性能席卷了机器学习领域。如果不考虑集成学习的算法&#xff0c;不考虑特定的训练数据集&#xff0c;尤其在分类任务中表现突出。在分类算法中的表现SVM说是排…