开源一个能跑通的代驾系统:Spring Boot + Netty 实时派单、计价引擎的落地复盘
最近把之前做的一套代驾服务系统整理开源了涵盖用户下单、司机抢单、实时定位、计价结算、在线支付的完整闭环。这篇不讲 PPT 架构直接挑三个真正花了时间的难点结合源码聊聊是怎么落地的、以及现在回看有哪些坑。开源地址放在文末欢迎 Star / Issue / PR。一、整体架构前后端分离后端按职责拆成两个可独立部署的 API 服务 一个公共模块biaoma-cloud-app-api用户 / 司机端接口 Netty 长连接网关biaoma-cloud-admin-api运营管理后台接口biaoma-cloud-common实体、Service、工具、支付/消息等公共能力管理后台Vue2 Element UI ECharts主要技术栈能力选型框架Spring Boot 2.7 MyBatis-PlusJDK 17实时通信NettyWebSocket / TCP 长连接缓存 / 在线状态Redis异步解耦RabbitMQ抢单等异步消息地图高德地图定位、距离、轨迹支付微信支付 支付宝数据库MySQL 5.7二、难点一基于 Netty 的长连接网关代驾场景里派单、抢单、司机坐标上报、语音播报都要求毫秒级到达HTTP 轮询扛不住所以单独起了一个 Netty 服务。服务端启动用主从 Reactor 模型端口走配置配合 Spring 生命周期做优雅停机ComponentpublicclassNettyServer{Value(${netty.port})privateIntegerport;privatefinalEventLoopGroupbossGroupnewNioEventLoopGroup(Runtime.getRuntime().availableProcessors()*2);privatefinalEventLoopGroupworkerGroupnewNioEventLoopGroup(1000);publicvoidinit(){ServerBootstrapbootstrapnewServerBootstrap();bootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG,1024*1024).childOption(ChannelOption.TCP_NODELAY,true).childOption(ChannelOption.SO_KEEPALIVE,true).childHandler(dataAcceptInitializer);// pipeline编解码 WebSocket 处理newThread(()-{try{ChannelFuturefbootstrap.bind(port).sync();f.channel().closeFuture().sync();}catch(InterruptedExceptione){log.info(TCP服务器启动失败{},e.getMessage());}}).start();}PreDestroypublicvoiddestroy(){bossGroup.shutdownGracefully().syncUninterruptibly();workerGroup.shutdownGracefully().syncUninterruptibly();}}pipeline 里区分文本帧和二进制帧文本帧交给业务 HandlerpublicclassWebSocketHandleextendsSimpleChannelInboundHandlerObject{protectedvoidchannelRead0(ChannelHandlerContextctx,Objectmsg){if(msginstanceofTextWebSocketFrame){dataAcceptHandler.channelRead0(ctx,(TextWebSocketFrame)msg);}elseif(msginstanceofBinaryWebSocketFrame){// 二进制帧如压缩数据单独处理}}}怎么找到“该推给谁”用一张内存表维护 用户ID 与 Channel 的映射业务侧推送时按 ID 取通道即可publicclassUserChanelRel{// key 前缀区分角色driver:{id} / user:{id}privatestaticfinalHashMapString,ChannelmanagenewHashMap();publicstaticvoidput(StringsenderId,Channelchannel){manage.put(senderId,channel);}publicstaticChannelget(StringsenderId){returnmanage.get(senderId);}}推送时一行代码ChannelchannelUserChanelRel.get(StaticUtils.DRIVERdriver.getId());channel.writeAndFlush(newMsgVo(NettyCodeEnums.DRIVER_START_THE_SERVICE).toJsonStringbuf());通信协议用一个枚举统一约定消息类型端上只认 code前后端好维护KEEPALIVE(1,心跳正常),CONNECT(2,第一次连接正常),COORDINATE_DRIVER(3,司机坐标传输),ORDER_BEST_PUSH(11,订单优推),GRAB_SINGLE_POND_IS_REFRESH(12,刷新抢单池),USER_START_THE_SERVICE(14,订单被接单),DRIVER_START_THE_SERVICE(22,抢单成功),DRIVER_VOICE(1001,语音播报)// 共 30 种业务事件现在回看的坑重点UserChanelRel 是单机 HashMapNetty 多实例部署时通道不共享没法水平扩展。生产演进方向是把“用户在哪个节点”注册到 Redis推送时先查节点再转发或直接上 MQ 广播另外 worker 线程数固定写死 1000、SO_BACKLOG 设到百万级都属于“能跑但不优雅”更合理的是 worker 用默认的 2 倍 CPU 核数、backlog 按实际峰值设置。这些我也标在后续 Roadmap 里。三、难点二优推、普通派单、抢单池的三级派单派单不是简单“丢给最近的人”。真实策略是层层降级尽量提高成单率查出半径内的在线司机如果用户有“推荐司机”邀请关系优先定向派给他其次派给开了“优推”的司机按距离从近到远依次尝试再派给普通司机按距离、服务分排序尝试全都没接订单落入抢单池并广播通知附近司机“抢单池有新单”。核心逻辑有删减publicvoidorderBestPush(BigDecimalradius,BigDecimallat,BigDecimallon,Integerprecedence,OrderMainmain,CalcuUtilscal,OrderValuationVovo){ListDDriverdriversdDriverService.selectNearbyDriver(radius,precedence,lat,lon,1);if(CollectionUtils.isNotEmpty(drivers)){// 1) 邀请关系的推荐司机优先// 2) 优推司机(precedence1) 按距离排序依次尝试// 3) 普通司机按距离、type 排序尝试// 4) 都没接住 → 入抢单池pondGrabSingle(vo,cal,lat,lon);}else{pondGrabSingle(vo,cal,lat,lon);}}privatevoidpondGrabSingle(OrderValuationVovo,CalcuUtilscal,BigDecimallat,BigDecimallon){grabSinglePondService.saveGrabSinglePond(vo);// 订单落抢单池resetPond(cal,lat,lon);// 通知附近司机刷新}入池后通知半径内默认 5km可由参数 pool_scope 配置的所有在线司机ListDDriverdriversdDriverService.selectNearbyDriver(pondKm*1000,null,lat,lon,null);for(DDriverd:drivers){ChannelchannelUserChanelRel.get(StaticUtils.DRIVERd.getId());if(channel!null){channel.writeAndFlush(newMsgVo(NettyCodeEnums.GRAB_SINGLE_POND_IS_REFRESH).toJsonStringbuf());}}司机不在线怎么办不能让消息丢了这里做了多通道兜底通道在线Netty 直推通道不在线把消息写进 Redis用户消息 60s、司机消息 20s重连后补投用户已关注服务号走微信公众号模板消息通知“已成功呼叫代驾 司机信息”司机端再叠加一次语音播报DRIVER_VOICE降低漏单率。抢单这个动作本身通过 RabbitMQ 异步化避免高峰期在 HTTP 线程里做一堆锁竞争。四、难点三用 BigDecimal 搭一个能算账的计价引擎代驾计费项很多起步价、起步含里程、超里程单价、起步前等待、行驶中等待、用户加价、动态溢价、超程返程费、优惠券、平台信息费、保险。用 double 算钱迟早出精度事故所以全程 BigDecimal并封了一个 CalcuUtils 统一做四舍五入和 scale 控制。最终订单金额的拼装有删减// 超出起步里程部分向上取整里程费 超程公里 × 单价 / 计费单位BigDecimalorderMileagecal.sub(totalMileage,startMileage,2);BigDecimalmileageFeecal.div(cal.mul(orderMileage,extraMileagePrice,2),extraMileage,2);// 订单金额 起步价 行驶等待费 用户加价 起步前等待费 里程费BigDecimalamountcal.add(2,startMoney,waitFee,raiseMoney,startingWaitFee,mileageFee);// 超过返程阈值公里数后超出段按比例加收返程费if(totalMileage.compareTo(exceedMileage)1){BigDecimalreturnFeecal.mul(cal.div(cal.mul(overPart,extraMileagePrice,2),extraMileage,2),exceedDistanceRatio,2);amountcal.add(2,amount,returnFee,premium);// 再叠加动态溢价}// 支付金额 订单金额 - 优惠券BigDecimalpayPricecouponMoney.compareTo(amount)0?cal.sub(amount,couponMoney,2):BigDecimal.ZERO;两个细节处理得比较实在实际里程低于预估里程时按预估里程计可由 pricing_manner 配置开关防止司机被恶意超短途订单白跑平台信息费支持 4 种抽成模式且都带封顶价 maxValueswitch(platformRatioType){case1:固定金额;// 线上/线下分别配置case2:按司机当月已完成单量分档抽成;case3:线上单/线下单区别抽成;case4:按下单时间段抽成夜间高峰可设不同比例;}// 司机所得 订单金额 - 平台信息费 - 保险orderMain.setDriverMoney(cal.sub(practicalMoney,2,platformScale,insurance));五、其他鉴权拦截器 Redis Token维护一份白名单登录、支付回调、上传等OPTIONS 预检直接放行支付微信、支付宝下单与异步回调都在 PayController / PayService回调地址白名单同样放行轨迹司机端定时上报坐标协议码 3服务端累计里程并落轨迹供后台轨迹回放定时任务订单超时、自动结算、优推重试等用 Spring Task 驱动。六、写在最后这套系统不是“演示 Demo”下单、派单、行程、支付、结算的主链路是跑得通的但也必须坦白单机 Channel 表、写死的线程和 backlog 参数意味着它离“开箱即上大规模生产”还有距离更适合作为学习真实业务建模、二次开发的脚手架。文章里提到的演进点欢迎一起来改。完整后端 管理后台 数据库脚本都在仓库里按 README 几步就能本地拉起Gitee 开源地址https://gitee.com/zhoujian6666/biaoma-ride-car-service协议以仓库 LICENSE 为准。如果对你有帮助点个 Star 就是最大的支持也欢迎在 Issue 里交流派单和计价的更好做法。

相关新闻

圆满收官|500名选手齐聚!2026「凯瑞麟杯」GPCC广州赛区决赛,奔赴大湾区匹克球荣耀

圆满收官|500名选手齐聚!2026「凯瑞麟杯」GPCC广州赛区决赛,奔赴大湾区匹克球荣耀

9月2627日,2026「凯瑞麟杯」GPCC 粤港澳大湾区匹克球城市挑战赛・广州赛区城市决赛,在白云区永旺梦乐城POPO网球匹克球运动中心圆满收官。 本届赛事汇聚约500名选手同台竞技,是近年粤港澳大湾区规模较大的匹克球专业赛事。赛事以“上场&#…

2026/10/10 12:20:57 阅读更多 →
2026.10.9 (1)

2026.10.9 (1)

5 最长回文子串class Solution:def longestPalindrome(self, s: str) -> str:nlen(s)left_ansright_ans0for i in range(n):lriwhile l>0 and r<n and s[l]s[r]:l-1r1if r-l-1>right_ans-left_ans:left_ansl1right_ansrfor i in range(n):liri1while l>0 and r&…

2026/10/10 12:20:57 阅读更多 →
02 | Kubernetes 部署 FastAPI LLM Serving 并接入 Ollama

02 | Kubernetes 部署 FastAPI LLM Serving 并接入 Ollama

1. 总览 基于 Kubernetes 部署 FastAPI LLM Serving&#xff0c;通过 Kubernetes Service 和 Ingress 对外提供 API&#xff0c;并通过 host.docker.internal 访问 MacBook 上的 Ollama。 核心调用链&#xff1a; Client → Kubernetes Ingress → Kubernetes Service → FastA…

2026/10/10 12:20:57 阅读更多 →

最新新闻

网页消息提醒音JS落地指南:自动播放解锁与实战避坑

网页消息提醒音JS落地指南:自动播放解锁与实战避坑

简介&#xff1a;网页消息提醒音js是一份面向网页前端开发者的实用示例资源&#xff0c;主要解决页面在收到新消息或事件时准确播放提示音的问题&#xff0c;适合在实时通讯、社交网络、在线协作等需要即时感知的场景中使用。资源压缩包共包含3个文件&#xff0c;有可直接运行的…

2026/10/10 15:19:38 阅读更多 →
Postman × Codex:如何将API集合封装成智能体Skill

Postman × Codex:如何将API集合封装成智能体Skill

直接上一个我最近在空隙时间里折腾完的东西&#xff1a;把 Postman 里的 API 集合&#xff0c;做成 Codex 可以直接调用的智能体 Skill。简单说&#xff0c;就是让 AI 代理能像人一样用 Postman 里的接口去查数据、发请求、跑流程&#xff0c;而不是只能对着文档“空谈”。这个…

2026/10/10 15:19:38 阅读更多 →
手工构造TINY词法分析器:从词法规则到Java实现与踩坑指南

手工构造TINY词法分析器:从词法规则到Java实现与踩坑指南

简介&#xff1a;面向编译原理课程设计与实验场景&#xff0c;这份资源聚焦TINY语言词法分析器的手工构造&#xff0c;适合正在学习编译器前端、需要完成类似实验的本专科生及自学者。内容围绕C/C实现展开&#xff0c;涵盖TINY词法规则识别、Token类型定义、确定有限状态自动机…

2026/10/10 15:19:38 阅读更多 →
ReelMimic完全指南:如何用最爱的视频当模板,一键生成同款风格的AI视频

ReelMimic完全指南:如何用最爱的视频当模板,一键生成同款风格的AI视频

【免费下载链接】reelmimic Show it a video you love. Get a new video in the same style. An AI crew (Claude Code or Codex) plans, builds and reviews it with you. 项目地址&#xff1a; https://gitcode.com/gh_mirrors/re/reelmimic 点击查看 免费下载 ReelMimic 是…

2026/10/10 15:18:38 阅读更多 →
10 分钟上手:给 Claude Code 装上 ai-memory,让 Agent 跨会话记住你的项目偏好

10 分钟上手:给 Claude Code 装上 ai-memory,让 Agent 跨会话记住你的项目偏好

10 分钟上手&#xff1a;给 Claude Code 装上 ai-memory&#xff0c;让 Agent 跨会话记住你的项目偏好 【免费下载链接】ai-memory Solution for long term memory for agent coding CLIs and to facilitate handoff between different agent vendors 项目地址: https://gitc…

2026/10/10 15:18:38 阅读更多 →
cal.diy 集成 Discord:静态会议链接型应用从配置到代码的完整拆解

cal.diy 集成 Discord:静态会议链接型应用从配置到代码的完整拆解

后端前端企业应用 【免费下载链接】cal.diy Scheduling infrastructure for absolutely everyone. 项目地址&#xff1a; https://gitcode.com/GitHub_Trending/ca/cal.diy 点击查看 免费下载 本指南以 cal.diy&#xff08;Cal.com 开源调度平台&#xff09;应用商店中的 Disc…

2026/10/10 15:18:37 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起&#xff1a;为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念&#xff0c;很多人会觉得它离自己很远——不就是天上的星星怎么转吗&#xff1f;但如果你正在做航天任务规划、遥感数据接收、星座设计&#xff0c;甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起&#xff1a;为什么你的代码里到处都是重复逻辑刚入行那会儿&#xff0c;我写过一个用户管理模块&#xff0c;注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么&#xff0c;能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介&#xff1a;这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目&#xff0c;以Boss直聘岗位数据为对象&#xff0c;适合用作毕业设计、课程设计或期末大作业。资源包共38个文件&#xff0c;约246KB&#xff0c;以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →