新坑-代驾微服务项目
前言:一直都想做一个有点高级但是又重复度低的项目,正好让我遇上了这个尚硅谷刚发的《乐尚代驾》,做了一下感觉还是有学到很多东西的,不管以后放不放在简历里面,算是先开了一个小头,弄懂再说。
项目介绍
该项目是模仿滴滴的一个代驾系统,前端是基于uniapp的微信小程序,分为三个端口:客户端,司机端和管理系统。业务很纯粹,就是乘客基于当前位置呼叫周围正在接单的司机,司机接单后对车辆的基本情况进行上传,然后就可以开始代驾了,到达终点后计算金额并用微信支付;此外还涉及到优惠券业务,也就是传统的那套秒杀逻辑,分布式锁+lua脚本那些。
技术栈
- SpringCloudAlibaba:包括nacos,openfeign,gateway这些,其实后续还可以做一些熔断和限流操作。
- redis:项目中主要是用到了geo数据结构,在司机开启抢单的时候将地理坐标上传到redis;此外也用到了redisson实现分布式锁和延迟队列(这里的延迟队列就是超时15分钟自动取消,其实也可以用rabbitmq实现)
- xxl-job:项目最核心的业务是通过xxl-job实现的,每次下单客户端都会创建一个新的任务调度,这个任务可以定时寻找周围司机。
- mongodb:在代驾订单进行过程中的地理坐标不好放在mysql里面,用mongodb存储地理坐标,考虑了其在实时性方面的优化
- rabbitmq:延迟队列,业务解耦和流量削峰
- Drools:第一次听说,规则引擎,就是把一些业务计算逻辑抽取出来,不硬编码在程序中,项目中用规则引擎定义了路程和费用的计算,还有最后算金额也用到了规则引擎,它有自己的一套语法。
- seata:分布式事务,在分布式系统中一个事务的回调是无法用@Transactional进行回调的,这里就用了阿里的这个分布式事务中间件完成,目前还没有太了解这个,只知道导入包只会加一个注释就能完成功能。
- minio:存储上传照片和音频文件
项目亮点
- 使用aop+注解+threadlocal的方式完成登录状态保存,微信小程序登录,做到了对业务代码的零入侵。
- 在下单后用xxl-job进行任务调度,定时搜索周围司机,并基于redis的geo数据结构,提高附近司机的搜索速度,完成司乘对接。
- 为了减小mysql访问压力使用mongodb记录沿途地理坐标,并使用基于rabbitmq的延迟队列将多次写请求合并为一次写入,再次减小了数据库操作。
- 使用策略模式和规则引擎Drools,定义了业务执行过程中的多种流程,例如代驾费用计算,司机积分计算以及优惠卷最大优惠策略计算。
- 使用Redisson分布式锁解决了高并发场景下的司机抢单和优惠卷超发问题,使用Redisson提供的延时队列完成了订单超出时间限制后自动取消。
- 使用CompletableFuture完成了订单结束提交过程的异步编排,提高了响应效率,并使用seata来保证分布式事务的执行。
可以有增量的地方
- 数据库分库分表,ShardingSphere可以尝试一下
- 二级缓存,本地caffeine和redis二级缓存
- 做限流和熔断,以及redis集群和哨兵
- 点赞(set),评论,排行榜(zset),签到(bitmap)和UV统计(hyperloglog)都是可以用redis解决的
- kafka-stream还是没能找到应用的场景,
- binlog实现与mysql的持久化
- 优惠卷兑换算法和优惠卷的最大优惠计算
业务流程
乘客端:
登录–选择代驾地址–呼叫代驾–等待接单–15分钟没有司机接单自动取消–15内有司机接单,司乘同显–账单支付
- 登录:前端首先调用wx.login(),返回一串字符串,然后后端拿到这个去请求微信服务器得到当前登录的openid,也就是微信的唯一id(可以理解为wx.login()请求发出之后,wx的缓存中就保存了这个键值对,键是这个字符串,值是当前用户的结构,必须是连贯的动作,可能设置了失效时间,这也是为什么我后面拿postman同样请求得不到的原因)
- 选择代驾地址:百度地图的api返回距离和时间,这个参数给后端规则引擎计算出费用
- 等待接单:xxl-job新增一个订单任务,每隔一分钟根据redis的geo搜索周围的汽车,然后在附近开启接单的汽车的队列里面(通过redis的list实现)添加这个订单。
- 15分钟没有司机接单自动取消:通过redisson实现或者rabbitmq的延迟队列实现
- 15内有司机接单,司乘同显:当司机抢到了该订单后,订单状态改变,进入司乘同显模式,此后基本上没有乘客端什么事情了,前端会一直轮询订单当前状态,并根据司机端存在mongodb中的数据获取车的位置。
- 账单支付:微信支付
司机端:
登录–认证–开始接单–抢单–开始代驾–生成账单,发送乘客
- 登录:逻辑类似
- 认证:司机需要上传身份证驾驶证人脸等,通过腾讯云,我懒得注册这部分就跳过了
- 开始接单:开始接单后会把当前的坐标上传到redis,然后每隔5s询问一次当前队列状态,当xxl-job把订单放在了该司机的队列里面,前端就会有显示可以抢单
- 抢单:分布式锁,无需多言,抢单之后将订单状态改变,这样乘客端轮询这个订单的时候就会发现有司机已经接单了。
- 到达乘客指定地点,这里会有一个刷单的校验,也就是说在距离地点1km之内才能有效
- 上传车辆状况和车牌号:拍照上传minio
- 开始代驾:每隔几秒钟将当前的坐标上传到mongodb中,实现与乘客端同步显示。
- 生成账单:根据规则引擎分账,然后推送微信支付给乘客。