service-consumer 是这套微服务里的调用方示例。它自己没有数据库,也不承载核心业务数据,而是通过 OpenFeign 调用
service-provider,再对外暴露 /api/v1/consumer/** 接口。这个模块把三件事讲清楚:声明式调用怎么写、共享契约怎么复用、下游失败时怎么降级。
基本信息
| 项 | 值 |
|---|---|
| 服务名 | service-consumer |
| 端口 | 9002 |
| API 前缀 | /api/v1/consumer |
| 数据库 | 无 |
| 远程调用 | OpenFeign + LoadBalancer |
| 服务发现 | Nacos Discovery |
Consumer 的代码量很小,这是它最有代表性的地方:当契约被抽到 service-common 后,消费者不需要重复定义一堆 Feign 接口和 DTO。
启动类设计
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"com.zjc.common.api"})
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
三个注解分别解决三件事:
@SpringBootApplication:启动 Spring Boot 应用。@EnableDiscoveryClient:注册到 Nacos,并具备服务发现能力。@EnableFeignClients(basePackages = {"com.zjc.common.api"}):扫描公共模块里的 Feign 契约。
这里的重点是指定扫描包。如果只写 @EnableFeignClients,默认扫描 Consumer 自己的包,公共模块里的 UserFeignApi、TestApi、
MailFeignApi 不会被创建成 Bean。
包结构
com.zjc.consumer
├── ConsumerApplication
├── config
│ └── OpenApiConfig
├── controller
│ ├── UserConsumerController
│ └── TessFeignController
└── service
├── FeignService
└── impl
└── FeignServiceImpl
Controller 不直接封装 HTTP 请求,也不手写 RestTemplate;它注入 Feign 接口,让 OpenFeign 根据 Nacos 服务列表和注解声明生成代理调用。
用户消费接口
用户接口直接复用 common 中的 UserFeignApi:
@GetMapping("/user/{id}")
public ApiResponse<UserDTO> getUser(@PathVariable("id") Long id) {
return userFeignApi.getUser(id);
}
@GetMapping("/user/list")
public ApiResponse<List<UserDTO>> list() {
return userFeignApi.list();
}
Feign 契约里只写资源路径 /user/{id},真正发送请求前,common 的 Feign 拦截器会补上目标服务前缀,最终请求:
GET http://service-provider/api/v1/provider/user/1
服务名到 IP 的解析交给 Nacos,实例选择交给 LoadBalancer。
连通性测试接口
/feign/port 是一个很实用的冒烟测试接口:
@GetMapping("/feign/port")
public ApiResponse<String> getServerPort() {
return feignService.getServerPort();
}
实现层委托给 TestApi:
@Override
public ApiResponse<String> getServerPort() {
return testApi.getServerPort();
}
如果 Provider 启动了多个实例,连续调用这个接口看到端口变化,就说明 Nacos 发现和 LoadBalancer 分流都正常。
降级策略
UserFeignApi 的降级在 common 里实现:
| 接口 | 降级结果 |
|---|---|
| 查询单个用户 | success=true,data=null |
| 查询用户列表 | success=true,data=[] |
这层设计让上层 Controller 不需要写 try-catch。Provider 宕机、网络超时或调用异常时,UserFeignFallbackFactory
会记录失败原因,然后返回兜底数据。
需要注意的是,这种策略适合演示和可用性优先场景。对强一致性业务,比如扣库存、支付、下单,不能一律“成功返回空数据”;该失败的调用要明确失败,否则上游可能基于不完整数据继续决策。
Feign 与 Sentinel
Consumer 引入 Sentinel,并在生产配置中开启:
feign:
sentinel:
enabled: true
这让 Feign 方法成为可统计资源,并让 fallback factory 生效。当前网关也有自己的 Sentinel 限流和熔断规则;Consumer 侧的 Feign 熔断保护的是服务间调用,两层职责不同。
超时配置
生产环境 OpenFeign 超时配置:
spring:
cloud:
openfeign:
client:
config:
default:
connect-timeout: 3000
read-timeout: 5000
开发环境基线是连接 1000ms、读取 2000ms;生产更宽松一些,避免网络抖动造成过多降级。
如果某个接口明显更慢,可以按 contextId 单独配置,例如 userFeignApi,避免为了一个慢接口放大所有 Feign 调用的超时。
负载均衡与缓存
Consumer 依赖:
| 依赖 | 作用 |
|---|---|
spring-cloud-starter-loadbalancer |
从服务实例列表选择实例 |
caffeine |
缓存服务列表,减少 Nacos 压力 |
Feign 的 lb://service-provider 逻辑名会先由 LoadBalancer 解析成具体实例地址。Caffeine 缓存能避免每次请求都重新拉取实例列表。
配置组织
基础配置:
spring:
application:
name: service-consumer
profiles:
active: dev
include:
- api
- jasypt
- zipkin
- nacos
dev 和 prod 分别维护:
- Nacos 地址。
- Zipkin 地址。
- Feign 超时。
- Swagger 开关。
- 链路采样率。
Nacos 只做服务发现,不用 Nacos Config。
链路追踪
一次用户查询会形成:
Client
-> service-consumer
-> UserFeignApi
-> service-provider
Consumer 引入 Micrometer Tracing + Zipkin 后,Feign 请求会继续传播 W3C traceparent。日志里用 traceId 聚合,Zipkin 里能看到
Consumer 发起调用和 Provider 处理请求的 span。
常见问题
注入 UserFeignApi 报 Bean 不存在
检查启动类是否指定扫描 com.zjc.common.api。@EnableFeignClients(basePackages = {"com.zjc.common.api"}) 是这个模块的关键配置。
调用返回 404
Feign 契约写的是资源路径,实际需要自动补 /api/v1/provider 前缀。如果 common 的路径自动装配没有生效,检查业务服务是否引入
service-common,以及 config/application-api.yaml 是否被 include。
一直降级
优先验证 Provider 可用,再检查 Nacos 服务列表、Feign 超时和网络安全组。UserFeignFallbackFactory 会记录 cause,日志是最直接的排查入口。
列表为空但不知道是正常还是降级
当前降级策略会把列表调用降级为空列表。排查时要看 Consumer 是否输出了降级日志,也可以通过 Zipkin 确认 Provider span 是否存在。
模块小结
service-consumer
的代码少,但它演示了微服务间调用的关键工程约定:契约集中、声明式调用、路径自动补齐、负载均衡、超时控制、失败降级和链路传播。它是一个很好的“消费者模板”,新增业务消费者时可以按这个结构复制思路,而不是复制代码。