2. 搜索服务架构:整体架构图、服务间通信方式、Redis在搜索服务中的位置
搜索服务作为电商、内容平台或企业级应用的核心中间层,其架构设计直接影响查询性能、数据一致性与可观测性。本节从整体架构切入,逐步拆解服务间通信机制,并明确 Redis 在搜索链路中的关键定位。
2.1 搜索服务整体架构图
搜索服务通常采用 分层 + 异步管道 的架构模式,将数据写入、索引构建、查询路由与结果缓存解耦。下图展示了标准搜索服务的核心组件与数据流向:
架构要点: 数据从源层单向流入索引构建层,构建完成后写入存储层;查询层接收用户请求后,先查询缓存层(Redis),未命中则回源存储层进行召回与排序。缓存层是查询性能的第一道防线。
2.2 服务间通信方式
搜索服务内部各层之间、以及搜索服务与外部依赖(如用户中心、商品中心)之间的通信,主要采用以下三种方式:
| 通信方式 | 协议 / 载体 | 典型场景 | 延迟要求 |
|---|---|---|---|
| 同步 RPC | gRPC / Thrift / Dubbo | 查询请求转发、用户鉴权、实时价格校验 | < 50ms |
| 异步消息 | Kafka / RocketMQ / RabbitMQ | 数据变更通知、索引增量更新、日志采集 | < 500ms(端到端) |
| 缓存 / 状态同步 | Redis 协议(RESP) | 热点结果缓存、分布式锁、限流计数器 | < 5ms(单次操作) |
契约测试关注点: 在同步 RPC 和 Redis 缓存通信中,错误码断言是契约测试的核心。例如,当 Redis 返回
MOVED(集群重定向)或 BUSY(脚本执行超时)时,搜索服务必须按照契约定义返回标准化的业务错误码,而非直接透传 Redis 原生错误。
2.3 Redis 在搜索服务中的位置
Redis 在搜索架构中并非存储主路径,而是作为 加速层 和 协调层 存在。其具体职责可归纳为以下四个维度:
- 查询结果缓存: 对高频、低变更的搜索请求(如热门关键词、类目导航)进行结果缓存,TTL 通常设置为 30s~300s,命中率目标 > 60%。
- 分布式限流与熔断: 使用 Redis 的
INCR+ 过期时间实现滑动窗口限流;结合SETNX实现熔断开关,保护下游索引服务。 - 索引构建状态协调: 记录全量索引版本号、增量消费位点、索引 Swap 状态,确保多副本索引构建的一致性。
- 错误码映射与断言缓存: 在契约测试框架中,Redis 存储
错误码 → 业务提示的映射表,测试用例通过断言 Redis 中的错误码值来验证服务行为。
常见误区: 不要将 Redis 作为搜索的主数据源。Redis 只适合缓存 可容忍最终一致性 的数据。对于需要强一致的搜索结果(如库存、价格),必须回源索引存储层读取,避免因缓存未及时更新导致数据错误。
2.4 契约测试视角下的通信链路
在契约测试中,搜索服务的通信链路被抽象为以下三个可断言接口:
- 客户端 → 搜索查询服务(RPC): 断言请求格式、超时行为、错误码映射。
- 搜索查询服务 → Redis(缓存协议): 断言 Redis 错误码(如
MOVED、ASK、BUSY)是否被正确转换为业务错误码。 - 搜索查询服务 → 索引存储(内部 RPC): 断言索引不可用时的降级策略与错误码。
// 契约测试示例:Redis 错误码断言
// 当 Redis 返回 MOVED 重定向时,搜索服务应返回 3001(缓存路由异常)
@Test
public void testRedisMovedErrorMapping() {
// 模拟 Redis 返回 MOVED 错误
redisMock.willReturn(new RedisException("MOVED 12345 192.168.1.1:6379"));
SearchResponse response = searchService.search("keyword");
// 断言业务错误码为 3001,而非直接透传 MOVED
assertThat(response.getCode()).isEqualTo(3001);
assertThat(response.getMessage()).contains("缓存路由异常");
}
最佳实践: 在契约测试中,为每个 Redis 错误码(
MOVED、ASK、BUSY、NOSCRIPT、READONLY)编写独立的断言用例,确保搜索服务在 Redis 集群发生拓扑变更或异常时,仍能返回符合契约的业务错误码。