简介:一份基于Spring Boot与Dubbo构建的探花交友平台源码包,面向有一定Java基础、想学习分布式服务开发或积累社交类项目经验的后端开发者和求职者。项目围绕陌生人交友场景,实现了手机号一键登录、个人信息完善、黑名单管理、测灵魂测试题、桃花传音语音消息,以及基于地理位置搜索附近10公里内用户并筛选等核心功能,可帮助理解用户中心、好友动态与交友业务的后端设计。资源共809个文件,以171个Java源码和504个XML配置为主,覆盖业务实现、MyBatis映射与Spring/Dubbo相关配置,另有20个YAML配置文件、52张JPG图片素材及少量IDEA工程文件,源码目录结构清晰,整体约3.13MB,导入IDE后可按Controller、Service、Mapper分层查看完整项目与启动配置。目前已有86人学习下载,适合用来拆解Dubbo服务暴露与调用链路、服务拆分思路及社交场景常见实现,是一份可运行、可快速上手学习的实战源码。
1. 拿到「探花交友平台」源码后,到底拿到了一套什么系统
刚拿到这个zip,先别急着全局搜索“推荐算法”,你会失望的——它的探花刷新,本质就是“按地理位置和活跃状态拉一批候选人再过滤”,真正的技术含量全在 Spring Boot 和 Dubbo 那一层 RPC 拆分上。这套源码是典型的社交产品后端骨架:Spring Boot 负责 Web 层、业务聚合和第三方对接,Dubbo 负责把用户、圈子、消息这些核心链路拆成可独立部署的模块。它能帮你低成本复刻一款陌生人社交应用的后端主链路,搞清楚微服务拆分、RPC 调用、Redis 缓存和 MySQL 建模是怎么配合的;适合有一定 Spring Boot 基础、想往上走一步到分布式实战的开发者,也适合拿社交业务做课程项目或毕业设计的同学。理解到这层,后面读代码、改功能、部署上线才不会被名字误导。
2. 探花交友平台为什么用 Spring Boot + Dubbo:架构拆解与模块边界
2.1 Dubbo 在这里解决什么:接口拆分、注册发现与 RPC 边界
社交类项目和普通管理后台最大的区别在于:单一 Spring Boot 工程能跑,但拆不动。用户服务、探花推荐服务、圈子动态服务、会话消息服务,各自的扩容压力和发布频率完全不同。比如探花推荐链路要扛住高并发刷滑动,而圈子动态主要是读多写少,强行放在同一个进程里,一次发版就是全量上线,排障范围也被放大。Dubbo 在这里的价值不是替代 Spring Boot,而是把“业务接口”变成“可寻址的远程服务”。
Dubbo 的关键动作有两个:provider 启动时把接口注册到注册中心,consumer 启动时订阅并建立连接。调用方写起来像调本地接口,实际是走 TCP 长连接 RPC。常见做法是用 Nacos 或 ZooKeeper 做注册中心,老一点的源码多配 ZooKeeper,新工程用 Nacos 更方便,因为自带控制台和配置管理,单机调试不用额外装客户端工具。要注意 Dubbo 的接口包名必须 provider 和 consumer 两端一致,否则注册能成功、订阅也能成功,调用时照样报方法找不到。这是这类源码最容易翻车的地方。
// provider 侧:在实现类上标注 DubboService,启动后自动注册到注册中心 // timeout 控制单个 RPC 调用最长等待时间,retries 控制失败重试次数 @DubboService(timeout = 5000, retries = 0) public class ProbeRecommendServiceImpl implements ProbeRecommendApi { @Override public RecommendResult listCandidates(RecommendQuery query) { // 按经纬度 + 活跃度查库过滤,这里省略具体实现 return RecommendResult.of(new ArrayList<>()); } }// consumer 侧:注入远程接口,调用方不关心 provider 部署在哪台机器 // check = false 表示消费端启动时不强校验 provider 是否在线,便于本地按随意顺序启动 @RestController public class ProbeController { @DubboReference(check = false) private ProbeRecommendApi probeRecommendApi; @GetMapping("/api/probe/recommend") public Result recommend(@RequestParam Double lat, @RequestParam Double lng) { return Result.ok(probeRecommendApi.listCandidates(new RecommendQuery(lat, lng))); } }代码里两个参数值得细看。timeout = 5000是给查询类接口留的宽裕值,因为推荐查询要跨服务拿候选列表,还要做过滤;retries = 0是写操作和查询操作都要守住的原则,重试在查询场景可能造成重复耗时,在滑动记录这类写场景会造成重复数据。实际项目里常见做法是把 Dubbo 超时和重试做成配置项,按接口维度覆盖,而不是在代码里写死。
2.2 核心主链路拆解:探花推荐、圈子动态、即时消息、后台审核
这个项目的业务主链路可以拆成四条,每条链路对应一批 Dubbo 接口和一组数据库表,值得在动手改代码前先对着源码梳理一遍。
第一条是探花推荐链:App 端上传经纬度,Web 层调用探花推荐 Dubbo 服务,服务端算出候选用户列表,排除掉已经滑过的人和互相不喜欢的人,返回给前端展示。已滑过的记录一般放 Redis 的 set 结构,key 按用户维度组织,查询时直接把整个 set 拿出来做排除。这条链路是 Dubbo + Redis 配合最紧密的部分,也是很多人拿到源码后第一个想改造的点。
第二条是圈子动态链:用户发布图文动态、上传图片、拉取关注人的动态流。这块涉及图片存储、文本内容校验、分页拉取三个子问题。源码里通常用本地目录或对象存储保存图片,动态流用 MySQL 分页查询,数据量上来后才会考虑 feed 流缓存。改造时可以先从分页大小和索引入手。
第三条是即时消息链:常见做法是 Web 层对接第三方即时通讯服务,后端只负责生成用户标识和登录凭证,消息收发都走第三方通道。也有源码自带 WebSocket 实现,点对点聊天、离线消息存库。两种方案没有绝对好坏,第三方方案起步快,自研方案可控性高。这套源码里重点看 Dubbo 服务是否参与了消息凭证的生成,如果参与了,说明接口边界在“账户体系”而不是“消息管道”。
第四条是后台审核链:运营后台对用户资料、动态内容做审核,涉及状态流转,比如待审核、通过、驳回、封禁。这块通常是一个独立的管理端 Spring Boot 工程,复用同一个数据库,但不一定走 Dubbo。理解这四条链路后,再去看 zip 里的模块划分,基本一眼就能看出哪个目录是干什么的。
2.3 打开 zip 先看点哪几个目录:源码结构识别法
拿到源码包,不要先点开 controller 从头看到尾,那是阅读源码最低效的方式。正确顺序是:先看 pom.xml 的模块列表,再看各模块下的启动类,最后看配置文件。这套源码大概率是按 Maven 多模块组织的,常见拆分是api模块放接口和 DTO,server或provider模块放 Dubbo 实现,web或app模块放 Controller,admin模块放管理后台。不同源码命名习惯不同,别假设名字一定叫这个。
# 在解压目录里找所有 Spring Boot 启动类, # 启动类特征是 public static void main + @SpringBootApplication find . -name "*.java" | xargs grep -l "SpringBootApplication" | grep "src/main/java"# 查看 maven 模块结构,确认工程拆了哪些子模块 grep "<module>" pom.xml这两条命令能帮你快速建立源码地图。找到启动类后,每个启动类所在模块就是一个可独立部署的进程。常见部署模型有三种:单进程跑所有模块、provider 和 web 分离部署、再加上 registry 和中间件形成完整分布式环境。源码默认配置一般是第二种,也就是 Dubbo 服务独立成进程,Web 层通过注册中心发现服务。先数清楚有几个启动类,再对应启动几个进程,后面调试才不会漏服务。
3. 用 Spring Boot 与 Dubbo 把探花交友平台跑起来:完整复现步骤
3.1 环境准备清单与版本匹配
这是源码能不能一次跑起来的关键。很多同学在这个项目的环境上连续踩坑,比如 Dubbo 版本和注册中心不兼容、Spring Boot 版本与 Redis 客户端冲突。动手前先把环境清单列清楚。
| 组件 | 常见版本区间 | 说明 |
|---|---|---|
| JDK | 1.8 | 大多数这类源码基于 JDK 1.8,遇到编译错误先检查这个 |
| Maven | 3.6 及以上 | 用来拉依赖和打包 |
| MySQL | 5.7 / 8.0 | 注意驱动版本,5.7 配 8.x 驱动没问题,8.0 要配时区参数 |
| Redis | 5.x / 6.x | 需要本机可连接,确认端口和密码 |
| Nacos 或 ZooKeeper | Nacos 1.x / ZK 3.4+ | 以 pom.xml 里的 dubbo-registry 依赖为准 |
| 前端运行环境 | Node 或内置 Tomcat | H5 端可能需要 npm 构建,老项目直接静态部署 |
# 进到解压目录,先看 spring boot 和 dubbo 的版本,避免凭感觉装环境 grep -A 3 "<parent>" pom.xml grep -i "dubbo" pom.xml版本匹配的原则是:Spring Boot 2.x 配 Dubbo 2.7.x 是常见的成熟组合;Spring Boot 3.x 需要配合 Dubbo 3.x,配置方式略有差异。用 Nacos 时注意 Nacos 客户端版本和服务端版本不能差太多,否则注册成功但心跳超时导致服务列表频繁上下线。用 ZooKeeper 时则要小心 ZK 版本 3.5+ 和 3.4 之间的端口差异,客户端连接串写法不同,很多“启动不报错但服务不注册”的玄学问题都出在这。
3.2 初始化数据库:SQL 脚本导入与数据校验
源码包一般自带 sql 目录,文件名可能是 init.sql、schema.sql 或者按模块拆分。常见习惯是先建库再导入脚本,库名在 application.yml 里体现,比如flower_db。建立好库之后,导入顺序有讲究:先基础表再业务表再关联表。如果脚本用外键约束,顺序错了会直接失败。
# 典型导入流程:建库、导入脚本、确认表数量 mysql -h127.0.0.1 -uroot -p -e "CREATE DATABASE IF NOT EXISTS flower_db DEFAULT CHARACTER SET utf8mb4;" mysql -h127.0.0.1 -uroot -p flower_db < sql/init_db.sql mysql -h127.0.0.1 -uroot -p flower_db -e "SHOW TABLES;"导入之后不要急着启动服务,先用 SQL 校验数据完整性:看用户表里有没有初始化账号,看字典表有没有状态值。很多源码会预置一个测试手机号和验证码,比如手机号 13800000000、验证码 123456,方便登录调试。如果脚本没预置,就得自己去 redis 里手动生成验证码,这个坑后面会具体说。另外注意 utf8mb4 是必须的,社交应用要存 emoji 表情,utf8 字符集会报 “Incorrect string value” 错误。
3.3 按顺序启动服务:从注册中心到前端
启动顺序是这类项目能不能一次跑通的关键,我的习惯是严格按依赖方向启动:先注册中心,再中间件,再 provider,再 web 层。顺序反了不一定报错,但消费端会在启动时尝试建立连接,产生大量无意义的错误日志。
# 1. 启动注册中心: # 如果用的是 Nacos,执行 bin/startup.sh -m standalone # 如果用的是 ZooKeeper,执行 bin/zkServer.sh start # 2. 启动 Redis 和 MySQL,确认端口可连接 # 3. 启动所有 Dubbo provider 模块: # 在每个 provider 模块目录下执行 mvn spring-boot:run -Dspring-boot.run.profiles=dev # 4. 确认 provider 全部注册成功后,再启动 web 模块 # 同样执行 mvn spring-boot:run,但新开一个终端窗口 mvn spring-boot:run -Dspring-boot.run.profiles=dev为什么严格按照这个顺序,因为 Dubbo 的 consumer 启动时如果check=true,会去注册中心找 provider,找不到就抛异常终止启动。源码里有些接口是 check=false,有些是 true,你很难控制每一处。最稳妥的方式就是先起 provider,等它在注册中心上线后,再起 web 层。验证 provider 是否注册成功,可以看启动日志里有没有 “Export dubbo service” 类似字样,也可以在注册中心控制台里查看服务列表。
3.4 最小配置:application.yml 示例与关键项说明
源码里通常有多个配置文件,bootstrap.yml 负责加载注册中心配置,application.yml 负责数据源和业务参数,application-dev.yml 属于环境覆盖。单机调试时只需要关注数据库、Redis、注册中心三个地址,其他配置能不动就不动。
spring: profiles: active: dev redis: host: 127.0.0.1 port: 6379 database: 0 datasource: url: jdbc:mysql://127.0.0.1:3306/flower_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: root dubbo: application: name: app-server-provider registry: address: nacos://127.0.0.1:8848 protocol: name: dubbo port: 20880 threads: 200 consumer: timeout: 3000 retries: 0 check: false这段配置里有几处值得说清楚。数据源 URL 里serverTimezone=Asia/Shanghai是 MySQL 8.0 驱动必须的,不加会报时区错误。Dubbo 的 registry 地址如果是 ZooKeeper 要写成zookeeper://127.0.0.1:2181,两种写法不能混。端口 20880 是 Dubbo 默认协议端口,如果本机多个 provider 工程同时跑,每个工程要改一个不同的端口,否则端口冲突。check: false只是方便本地调试,生产环境建议按实际需求单独配置。配置改完,启动类里记得看@EnableDubbo注解是否存在,有些源码需要手动加,有些则依赖 starter 自动开启。
4. 把探花交友平台调到可用的状态:必调参数与业务配置
4.1 Dubbo 的 timeout / retries / loadbalance:推荐参数与副作用
源码默认的 Dubbo 参数通常比较保守,直接上线会遇到两类现象:查询接口偶尔超时、写接口重复执行。调整参数前要理解每个参数的作用半径。
| 参数 | 作用 | 推荐值 | 副作用 |
|---|---|---|---|
| timeout | 单次 RPC 调用的最大等待时间 | 查询 3000~5000ms,写操作 2000ms | 过大会占满消费端线程 |
| retries | 失败后的重试次数 | 查询 0,写操作 0 | 重试导致重复写、消息重复发 |
| loadbalance | 负载均衡策略 | random 或 roundrobin | 权重不一致会影响分布 |
| threads | provider 业务线程池大小 | 200 | 太小请求排队,太大占用内存 |
| accepts | provider 可接受的连接数 | 500 | 超过后新连接排队等待 |
实际调试中,超时报错是最常见的 Dubbo 问题表象。比如探花推荐接口第一次请求耗时 2.8 秒,默认 timeout 只有 1 秒,调用就失败。这时候直接把 timeout 调到 5000 可能掩盖了真正的性能问题。正确思路是:先看日志里慢调用的耗时分布,确定是数据库慢查询还是第三方接口慢,再决定是调超时还是优化实现。retries 的情况更危险,如果滑动记录接口超时但实际已写库,重试就会产生两条重复记录,所以写操作一律设置retries = 0。
// 推荐用 @DubboReference 的注解属性做接口级覆盖 // 这样不会因为全局配置太宽松而掩盖问题 @DubboReference(timeout = 4000, retries = 0, loadbalance = "random") private ProbeRecommendApi probeRecommendApi;这种接口级配置的好处是精细可控。公共服务比如用户查询可放宽超时,滑动、点赞这类写操作锁死重试次数,避免“错误被自动放大”。
4.2 Redis 键设计:探花滑动记录与 token 有效期
社交源码的 Redis 使用集中在三块:登录 token、验证码、滑动记录。其中滑动记录是探花核心逻辑的依赖。常见键设计是这样的:
| 用途 | Key 示例 | 类型 | 过期时间 |
|---|---|---|---|
| 登录 token | login:token:{userId} | String | 7 天 |
| 验证码 | login:code:{phone} | String | 5 分钟 |
| 已滑过的用户 | probe:swiped:{userId} | Set | 30 天 |
| 相互喜欢的标记 | probe:like:{userId}:{targetId} | String | 永久或业务决定 |
实现过滤的逻辑很直接:推荐候选列表从数据库查出后,把当前用户已滑过的 set 拿出来做排除,避免前端重复展示已经划过的人。set 的容量一般不大,几百个成员全量取出做内存过滤完全没问题。
// 用户刷探花时,把滑过的对端用户记录到 Redis set // 下次推荐查询时直接排除,减少重复展示 SetOperations<String, String> setOps = stringRedisTemplate.opsForSet(); String key = "probe:swiped:" + userId; setOps.add(key, targetUserId.toString()); // 可以设置过期时间,避免长期占用内存 stringRedisTemplate.expire(key, 30, TimeUnit.DAYS);这里有个常见的错误:滑动记录用 StringRedisTemplate 写入,但查询的时候用 RedisTemplate 读取,两个模板的序列化方式不同,导致读出来报错或直接 key 找不到。全程统一用 StringRedisTemplate 是这类项目最省心的做法。另外 token 过期时间不要设太短,社交 App 的用户不会频繁重新登录,7 天是合理起点;验证码 5 分钟足够,时间长了反而增加被爆破的风险。
4.3 上传路径与静态资源映射
社交应用绕不开图片上传。源码演示环境一般把图片存本地磁盘,配置一个 upload.dir 路径,再通过 Web 层的静态资源映射把 URL 指向这个目录。这个机制不复杂的,但配置项特别容易漏,而且漏了之后前端图片全是 404。
// 本地存储的图片要能通过 URL 访问,需要显式添加静态资源映射 @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Value("${upload.dir:/data/upload}") private String uploadDir; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // file: 前缀必加,否则路径会被当成 classpath 资源处理 registry.addResourceHandler("/upload/**") .addResourceLocations("file:" + uploadDir + "/"); } }这里真正的坑是 Windows 和 Linux 路径写法差异。Windows 下file:D:/upload/,Linux 下file:/data/upload/,写错一个斜杠,资源映射就不生效。还有一点,如果前端部署在 Nginx,图片 URL 走 Nginx 的 alias 指向同一目录,此时 Spring Boot 的映射可以不用,但要保证两边目录一致,别出现上传写到一个路径、Nginx 访问另一个路径的情况。
4.4 数据源连接池与分页插件的边界
源码里数据源配置需要检查连接池参数是否合理。常见的连接池是 HikariCP 或 Druid,如果 pom 里两个都引入了,启动时会报 DataSource 相关冲突,需要排除一个。连接池参数建议先保底线:初始连接数 5~10,最大连接数 50~100,连接超时 30 秒。
分页查询在列表页特别常见,比如动态圈子、后台审核列表。MyBatis 分页插件和 PageHelper 是本类源码常用的库,配置不当会出现两个典型问题:一是 PageHelper 分页参数被线程本地变量残留,导致下一次查询莫名分页;二是分页数量无上限,用户拉取动态列表时直接全表扫。应对方式:每次都重新设置 PageHelper.startPage,用完清空上下文;业务层限制 pageSize 最大值,比如 20 条,超出按 20 处理。
// 用 PageHelper 分页必须注意:startPage 后紧跟第一条查询语句 // 中间不能插入其他 SQL 操作,否则分页会作用到错误的查询上 PageHelper.startPage(pageNum, Math.min(pageSize, 20)); List<Dynamic> list = dynamicMapper.selectFriendDynamics(userId); PageInfo<Dynamic> pageInfo = new PageInfo<>(list);在这类源码里,分页还被用来实现“下拉加载更多”的场景,前端传 lastId 比传 pageNum 更不容易出现重复数据。如果源码用的是纯 offset 分页,数据频繁变化时用户会看到内容重复或遗漏,这是体验问题,改造时优先切换到游标分页方案。
5. 探花交友平台源码运行避坑:5 个高频问题与排查思路
5.1 服务启动后调用报 “No provider available”
现象:Web 层启动成功,但访问登录或探花推荐接口时,控制台报No provider available for the service。原因基本可以锁定在三个方向:注册中心地址没对上、provider 未成功暴露接口、消费端接口包名不匹配。排查顺序是先确认 provider 是否在注册中心列表里,再看两端接口名。
# 如果用的 Nacos,可以通过 API 查看服务是否已经注册 # 注意不同 Nacos 版本 API 路径略有不同 curl "http://127.0.0.1:8848/nacos/v1/ns/catalog/services?pageNo=1&pageSize=10"如果在列表里看到服务名,说明 provider 已注册,问题多数在 consumer 引用的接口类路径不一致,去检查两端的@DubboService和@DubboReference指向的接口全限定名是否相同。如果服务列表里没有,先看 provider 日志有没有 “Export dubbo service” 字样,没有就说明注册动作没完成,查 registry 地址和网络连通性。
5.2 前端头像和动态图片 404
现象:数据库里图片地址是http://127.0.0.1:8080/upload/xxx.jpg,浏览器访问却是 404。原因几乎都是静态资源映射没配,或者上传目录和映射目录不是同一个。解决方式分两步:先确认 upload.dir 配置的实际路径,再看 WebMvcConfig 里的 addResourceLocations 是否加了file:前缀。这两个环节有一个不一致,图片必然打不开。还有一个隐蔽问题是上传时保存了完整磁盘路径到数据库,而前端拿到的 URL 应该是基于/upload/开头的相对路径,如果二者混用,部署到其他机器时会 404。
5.3 Redis 反序列化异常
现象:登录验证码存进去了,校验时却报SerializationException或者强转类型失败。原因很典型:项目里 RedisTemplate 和 StringRedisTemplate 混用。RedisTemplate 默认用 JDK 序列化,写入的 value 带二进制头,StringRedisTemplate 读它会读到一堆不可读字节。解决方法是全项目统一使用 StringRedisTemplate,或者自定义一个固定序列化方式的 RedisTemplate Bean。统一之后,登录模块写入的验证码,校验模块直接按字符串读取,不再依赖对象序列化。
// 自定义 RedisTemplate,key 和 value 全部使用 String 序列化 // 避免不同代码之间用不同模板读写导致的反序列化问题 @Bean public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, String> template = new RedisTemplate<>(); template.setConnectionFactory(factory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new StringRedisSerializer()); return template; }这类问题在源码调试里出现频率极高,而且报错信息容易误导人。看清栈信息里是序列化器问题,就不要去查业务逻辑,直接查模板配置。
5.4 “探花推荐”接口很慢
现象:第一次调用探花推荐接口耗时 3 到 5 秒,第二次就快很多。原因一般是候选列表没有预热,每次请求都实时查库,加上地理位置转城市名称调了第三方接口。第三方接口的耗时不可控,这是主要瓶颈。解决思路是把“候选人列表”和“位置解析”拆开:候选人列表按城市维度缓存到 Redis,定时任务每 5 分钟刷新一次;位置解析只在用户主动刷新或首次登录时调用一次,结果缓存起来。接口里只做内存过滤,不再实时查库。
附带还有一个数据库层面的问题:候选查询如果是用WHERE city_id = ?查询,city_id 字段没建索引,数据量一上来就会慢。源码里这种基础索引缺失很常见,跑起来能用只是数据少,上线前要逐一检查。这类“玄学”性能问题,抽丝剥茧后多半是缓存没加、索引没建。
5.5 启动时端口占用或连接注册中心超时
现象:同时启动多个 provider 模块时,第二个模块报端口占用;或者启动时报连不上 Nacos/ZooKeeper,直接启动失败。端口占用是因为多个 Dubbo provider 默认都用 20880 端口,必须逐个修改。连接注册中心超时则要先确认注册中心进程是否真的起来了,很多人习惯性先启动业务服务,忽略了依赖的先后顺序。
这两类问题本质都是启动流程不规范。我的习惯是开三个终端窗口:第一个给注册中心,第二个给 Redis 和 MySQL,第三个才启动业务服务。顺序固定后,这类问题基本可以彻底避免。改 Dubbo 端口时注意 protocol 下的 port 配置,同时 consumer 的 URL 不用改,它通过注册中心拿地址,只有直连调试才需要手动指定端口。
6. 验证一次探花推荐调用:日志链路、微调与上线习惯
6.1 用一个 traceId 串起整条探花链
代码改完、服务跑通,怎么证明这一次推荐调用真的走完了 Dubbo 链路,而不是前端拿到了 mock 数据?我习惯在日志里找一个 traceId 从头追到尾。实现方式是在 Web 层生成一个唯一 ID,塞进 Dubbo 的 attachment,provider 侧从 RpcContext 取出来放进日志 MDC,后续打印的所有日志都会带上这个 ID。
// consumer 侧:生成 traceId 并放入 Dubbo attachment,随 RPC 传递 String traceId = UUID.randomUUID().toString().replace("-", ""); RpcContext.getContext().setAttachment("traceId", traceId);// provider 侧:取出 traceId,放入日志上下文,日志即可串联 String traceId = RpcContext.getContext().getAttachment("traceId"); MDC.put("traceId", traceId);验证的时候,先记录 Web 层接口 200 响应,然后去 provider 日志里按同一个 traceId 过滤,能看到推荐候选查询的 SQL 和过滤逻辑的执行记录。整条链路在日志里连续出现,说明 Dubbo 网络通信正常、注册中心路由正常、Redis 过滤生效。这只是本地验证,线上应用通常还会接入全链路监控组件,但原理完全一致。
6.2 做一次真实滑动数据验证
建议按真实业务场景走一遍:A 用户登录后刷探花,划过 10 个用户,关掉 App 重进,继续刷。确认前端没有再出现已划过的用户。这个验证能同时覆盖 Redis set 写入、推荐查询时的排除逻辑、token 有效期内重新登录的状态恢复。如果发现重复出现,优先查是不是缓存 key 没按用户维度区分,或者 set 过期时间太短导致滑过的记录被清理。
另一个值得做的调优是为推荐候选人列表加预热任务。定时刷新各城市的候选人到 Redis,探花接口实时读取,冷启动性能就会明显改善。上线顺序上我有一个固定习惯:先在测试环境用一个小城市数据量跑通全流程,再切到生产数据验证索引和缓存,确认无慢 SQL 后才放量。现在拿到任何 Dubbo 源码都是先按这个节奏走,不急着改业务逻辑,等链路验证稳定了,再根据实际流量调整线程池和超时参数。这套流程能少走很多弯路,希望帮到你。
本文还有配套的精品资源,点击获取