上一篇用DiscoveryClient+RestTemplate按服务名调商品。能跑,但每个调用方都要手写「拉实例 → 拼 URL → exchange」,DTO 还容易散在各服务里。
这篇只办两件事:
抽一个薄模块
hm-api,专放跨服务契约用OpenFeign替换手写 RestTemplate,并修掉「找不到 ItemClient Bean」
Gateway、Sentinel 不塞这篇。素材:黑马「抽取 Feign」+ 本机 hmall。
1. 为什么要从 RestTemplate 换成 Feign
| DiscoveryClient + RestTemplate | OpenFeign | |
|---|---|---|
| 写法 | 自己拉实例、拼 URI、exchange | 接口 + 注解,像调本地方法 |
| 服务名 | 字符串 + 手工选实例 | @FeignClient("item-service") |
| 负载均衡 | 自己 random / 后面再加@LoadBalanced | 配 LoadBalancer 即可 |
| 契约复用 | DTO/路径容易复制多份 | 抽到hm-api一处维护 |
上篇是「看懂注册发现」;本篇是「声明式调用 + 契约模块化」。
2. 改造前后差在哪
改造前:商品服务里塞着 dto、FeignClient;购物车、订单直接依赖商品服务整坨。
改造后:
┌──────── hm-api ────────┐ │ client / dto / config │ └──────────▲─────────────┘ ┌───────────────────┼───────────────────┐ item-service cart-service order-service (实现商品) (依赖 api 调商品) (同上)
hm-api:接口 + 模型(普通 jar,不要做成可启动的 Boot 模块)业务服务:写实现;要远程调用的只依赖
hm-api
3. IDEA 怎么建模块(和拆 item-service 同一套规矩)
新建模块→ Java/Maven,父工程选hmall,名称hm-api,JDK 与项目一致(如 11)。
父pom.xml必须有:
<module>hm-api</module>
推荐包:
com.hmall.api ├─ client # ItemClient、CartClient… ├─ dto # ItemDTO… └─ config # 可选:Feign 日志等
自检:父 modules 有没有登记;子模块有没有挂hmallparent。目录在父工程下但没挂 parent,只是文件夹——上篇 Maven 踩坑同样适用。
4. 调用方 pom 要带什么
业务服务(如 cart)依赖hm-api,还要能发 Feign:
<dependency> <groupId>com.hmall</groupId> <artifactId>hm-api</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency>groupId/ 版本以你父工程为准。Nacos discovery 上篇已配过,调用方继续连同一套server-addr。
5. 必踩坑:required a bean of type ItemClient
启动购物车时报类似:
Parameter 0 of constructor in ...CartServiceImpl required a bean of type 'com.hmall.api.client.ItemClient' that could not be found.
原因:@EnableFeignClients默认只扫启动类所在包及子包。启动类在com.hmall.cart,接口在com.hmall.api.client,扫不到就不生成代理 Bean。
修法一:扫包
@EnableFeignClients(basePackages = "com.hmall.api.client") @SpringBootApplication public class CartApplication { }修法二:点名客户端
@EnableFeignClients(clients = { ItemClient.class }) @SpringBootApplication public class CartApplication { }两个都行;客户端多时用扫包省事。
6. Feign 日志:怎么把请求响应打出来
OpenFeign不会默认把每次调用打到控制台。要日志,两件事一起做:
Feign 所在包日志级别开到DEBUG
声明
feign.Logger.Level的 Bean(注意是feign.Logger,不是 slf4j)
| 级别 | 打什么 |
|---|---|
| NONE | 不打(默认) |
| BASIC | 方法、URL、状态码、耗时 |
| HEADERS | BASIC + 头 |
| FULL | 头 + Body,排障最全 |
本地用 FULL;生产慎用。
public class DefaultFeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }局部挂某一个 Client:
@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class) public interface ItemClient { }全局默认:
@EnableFeignClients( basePackages = "com.hmall.api.client", defaultConfiguration = DefaultFeignConfig.class )
yaml:
logging: level: com.hmall.api.client: DEBUG
控制台若出现GET http://item-service/items?ids=...且是服务名不是写死 IP,说明走了注册中心 + LoadBalancer。
7. 自测顺序
Nacos 已起,
item-service已在服务列表cart 启动成功(不再报找不到 ItemClient)
调一个会远程查商品的接口,返回正常
(可选)打开 FULL 日志,能看见 URL / 状态码 / Body
8. 一句话串讲
跨服务契约进
hm-api;调用方依赖 api + OpenFeign + LoadBalancer。 找不到 ItemClient:@EnableFeignClients写basePackages或clients。 上篇手写发现是为了看懂;本篇 Feign 是为了少写样板、契约一处维护。要看明细就Logger.Level+ 包 DEBUG。
| 点 | 记住 |
|---|---|
| 远程调用三件套 | openfeign + loadbalancer;@EnableFeignClients;写FeignClient |
| 最佳实践 | 提供方抽独立 api 模块:Client + DTO |
| 扫不到 Bean | basePackages/clients |
| 日志 | Level Bean + 包 DEBUG;局部configuration,全局defaultConfiguration |
下篇预告
Gateway:统一入口
Sentinel:课上有再写
上一篇:Nacos 注册发现(含 No DataSource) 本篇:OpenFeign + hm-api 下篇:Gateway 统一入口(前端只打网关,不连 Nacos)