一、导入项目
- 执行
cd /root和docker compose down命令,删除同目录下,可能存在相同的项目; - 安装mysql
将mysql目录(配置文件和初始化脚本)复制加载到虚拟机中------执行命令1(创建通用网络)------执行命令2(安装mysql,创建mysql容器)------执行命令3(查看mysql容器)------
bash
#命令1
docker network create yue-net
# 命令2
docker run -d \
--name mysql \
-p 3306:3306 \
-e TZ=Asia/Shanghai \
-e MYSQL_ROOT_PASSWORD=123456 \
-v /home/yueyue/mysql/data:/var/lib/mysql \
-v /home/yueyue/mysql/conf:/etc/mysql/conf.d \
-v /home/yueyue/mysql/init:/docker-entrypoint-initdb.d \
--network yue \
mysql
#命令3
docker ps

-
Java后端代码
将后端项目代码复制到工作空间------使用idea打开------按下
ALT+8键打开services窗口,新增一个启动项------点击Run Configuration Type------鼠标向下滚动,找到Spring Boot------点击启动运行或者Debug运行------给启动项做简单配置(XXXApplication右键点击------弹出窗口------Edit Configuration------Active profiles对应写local值)浏览器访问:http://localhost:8080/hi
-
前端代码
将前端项目代码复制到工作空间(非中文、不包含特殊字符的目录)------进入项目目录,利用cmd启动------执行命令
PowerShell
# 启动nginx
start nginx.exe
# 停止
nginx.exe -s stop
# 重新加载配置
nginx.exe -s reload
# 重启
nginx.exe -s restart
特别注意:
nginx.exe 不要双击启动,而是打开cmd窗口,通过命令行启动。停止的时候也一样要是用命令停止。如果启动失败不要重复启动,而是查看logs目录中的error.log日志,查看是否是端口冲突。如果是端口冲突则自行修改端口解决。
浏览器访问:http://localhost:18080/
二、认识微服务
2.1 单体架构
单体架构(monolithic structure): 整个项目中的所有功能模块都在一个工程中开发,项目部署时需要对所有模块一起编译、打包;项目的架构设计、开发模式都非常简单;
优点:
当项目规模较小时,该模式上手快,部署、运维都方便;
缺点:
- 团队协作成本高: 所有人改同一个代码库,频繁的代码合并极易产生冲突,解决冲突会耗费大量时间
- 系统发布效率低: 每次修改无论大小都要重新构建、测试并部署整个应用,导致发布流程冗长且易出错。
- 系统可用性差: 某个模块(如秒杀)占用大量资源,可能导致整个系统崩溃,拖垮其他无关模块。
2.2 微服务
微服务架构: 首先是服务化,就是将单体架构中的功能模块从单体应用中拆分出来,独立部署为多个服务。同时要满足下面的一些特点:
- 单一职责: 一个微服务负责一部分业务功能,并且其核心数据不依赖于其它模块;
- 团队自治: 每个微服务都有自己独立的开发、测试、发布、运维人员,团队人员规模不超过10人;
- 服务自治: 每个微服务都独立打包部署,访问自己独立的数据库。并且要做好服务隔离,避免对其它服务产生影响;
解决单体架构存在的问题:
- 团队协作成本高?
- 由于服务拆分,每个服务代码量大大减少,参与开发的后台人员在1~3名,协作成本大大降低
- 系统发布效率低?
- 每个服务都是独立部署,当有某个服务有代码变更时,只需要打包部署该服务即可
- 系统可用性差?
- 每个服务独立部署,并且做好服务隔离,使用自己的服务器资源,不会影响到其它服务。
分布式 就是服务拆分的过程 ,其实微服务架构正式分布式架构的一种最佳实践的方案;
微服务架构虽然能解决单体架构的各种问题,但在拆分的过程中,还会面临很多其它问题:
- 如果出现跨服务的业务该如何处理?
- 页面请求到底该访问哪个服务?
- 如何实现各个服务之间的服务隔离?
2.3 SpringCloud
微服务拆分 以后碰到的各种问题都有对应的解决方案和微服务组件,而SpringCloud框架可以说是目前Java领域最全面的微服务组件的集合了。

而且SpringCloud依托于SpringBoot的自动装配能力,大大降低了其项目搭建、组件使用的成本。对于没有自研微服务组件能力的中小型企业,使用SpringCloud全家桶来实现微服务开发可以说是最合适的选择了!
SpringCloud官网 :https://spring.io/projects/spring-cloud#overview
Alibaba的微服务产品SpringCloudAlibaba目前也成为了SpringCloud组件中的一员,我们课堂中也会使用其中的部分组件
三、微服务拆分
3.1 服务拆分原则
服务拆分要考虑的几个问题:
- 什么时候拆?
- 怎么拆?
3.1.1 什么时候拆
对于大多数小型项目来说,一般是先采用单体架构 ,随着用户规模扩大、业务复杂后再逐渐拆分为微服务架构 。这样初期成本会比较低,可以快速试错。但是,这么做的问题就在于后期做服务拆分时,可能会遇到很多代码耦合带来的问题,拆分比较困难(前易后难 ) 。
而对于一些大型项目,在立项之初目的就很明确,为了长远考虑,在架构设计时就直接选择微服务架构。虽然前期投入较多,但后期就少了拆分服务的烦恼(前难后易)
3.1.2 怎么拆
服务拆分目标:
微服务拆分时粒度要小,这其实是拆分的目标。具体可以从两个角度来分析:
高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖,或者依赖接口的稳定性要强
高内聚 首先是单一职责 ,但不能说一个微服务就一个接口,而是要保证微服务内部业务的完整性为前提。目标是当我们要修改某个业务时,最好就只修改当前微服务,这样变更的成本更低。
一旦微服务做到了高内聚,那么服务之间的耦合度自然就降低了。
服务拆分方式:
纵向拆分- 纵向拆分,就是按照项目的功能模块来拆分。如商城项目中,有用户管理功能、订单管理功能、购物车功能、商品管理功能、支付功能等,按照功能模块将他们拆分成一个个服务,这种拆分模式可以尽可能提高服务的内聚性;
横向拆分- 横向拆分,是看各个功能模块之间有没有公共的业务部分,如果有将其抽取出来作为通用服务。如用户登录是需要发送消息通知,记录风控数据,下单时也要发送短信,记录风控数据。因此消息发送、风控数据记录就是通用的业务功能,因此可以将他们分别抽取为公共服务:消息中心服务、风控管理服务,这种拆分模式可以提高业务的复用性,避免重复开发,通用业务一般接口稳定性较强,也不会使服务之间过分耦合;
3.1.3 服务调用
服务拆分之后,不可避免的会出现跨微服务的业务,此时微服务之间就需要进行远程调用,微服务之间的远程调用被称为RPC,即远程过程调用。
跨服务远程调用 (RPC,即R emote P roduce Call)实现方式:
- 基于Http协议方式不关系服务提供者的具体技术实现,只要对外暴露Http接口即可,更符合未服务的需要;
- 基于Dubbo协议
Java发送http请求可以使用Spring提供的RestTemplate,使用的基本步骤:
- 注册RestTemplate到Spring容器;
- 调用RestTemplate的API发送请求,常见方法有:
getForObject:发送Get请求并返回指定类型对象;PostForObject:发送Post请求并返回指定类型对象;put:发送PUT请求;delete:发送Delete请求;exchange:发送任意类型请求,返回ResponseEntity;
- RestTemplate
RestTemplate通常用作共享组件,它的配置不支持并发修改,因此它的配置通常是在启动时准备的。 - 远程调用
定义一个配置类,将RestTemplate注册为一个Bean
java
/**
* 将RestTemplate注册为一个Bean
*/
@Configuration
public class RemoteCallConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
远程调用
java
private void handleCartItems(List<CartVO> vos) {
// TODO 1.获取商品id
Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
// 2.查询商品
// List<ItemDTO> items = itemService.queryItemByIds(itemIds);
// 2.1.利用RestTemplate发起http请求,得到http的响应
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
"http://localhost:8081/items?ids={ids}", //请求路径
HttpMethod.GET, //请求方法
null, //请求实体
new ParameterizedTypeReference<List<ItemDTO>>() {}, //返回值类型
Map.of("ids", CollUtil.join(itemIds, ",")) //请求参数
);
// 2.2.解析响应
if (!response.getStatusCode().is2xxSuccessful()) {
// 查询失败,直接结束
return;
}
List<ItemDTO> items = response.getBody();
if (CollUtils.isEmpty(items)) {
return;
}
// 3.转为 id 到 item的map
Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
// 4.写入vo
for (CartVO v : vos) {
ItemDTO item = itemMap.get(v.getItemId());
if (item == null) {
continue;
}
v.setNewPrice(item.getPrice());
v.setStatus(item.getStatus());
v.setStock(item.getStock());
}
}
四、服务注册与发现
4.1 注册中心原理
在微服务远程调用的过程中,包括两个角色:
- 服务提供者: 提供接口供其他微信服务访问,比如item-service;
- 服务消费者: 调用其他微服务提供的接口,比如cart-service;
在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心 的概念。注册中心、服务提供者、服务消费者三者间关系如下:

流程如下:
- 服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心;
- 调用者可以从注册中心订阅 想要的服务,获取服务对应的实例列表(1个服务可能多实例部署);
- 调用者自己对实例列表负载均衡,挑选一个实例;
- 调用者向该实例发起远程调用;
当服务提供者的实例宕机或者启动新实例时,调用者如何得知呢?
- 服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求);
- 当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除;
- 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表;
- 当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表;
4.2 Nacos注册中心
目前开源的注册中心框架有很多,国内比较常见的有:
- Eureka: Netflix公司出品,目前被集成在SpringCloud当中,一般用于Java应用;
- Nacos: Alibaba公司出品,目前被集成在SpringCloudAlibaba中,一般用于Java应用;
- Consul: HashiCorp公司出品,目前集成在SpringCloud中,不限制微服务语言;
Nacos 是国内产品,中文文档比较丰富,而且同时具备配置管理功能,Nacos官网
基于Docker部署Nacos注册中心
修改nacos文件夹中的 custom.env文件,MYSQL_SERVICE_HOST也就是mysql地址,需要修改为自己的虚拟机IP地址------将nacos目录上传到虚拟机/root目录------进入root目录,执行命令1

bash
#命令1
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim

访问地址:http://192.168.19.128:8848/nacos/,注意将192.168.150.101替换为你自己的虚拟机IP地址,账号和密码均为nacos

4.3 服务注册
把服务(如item-service)注册到Nacos,步骤如下:
- 引入依赖
- 配置Nacos地址
- 重启
- 添加依赖
在需要注册的服务(如item-service)的pom.xml中添加依赖:
xml
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 配置Nacos
在需要注册的服务(如item-service)的application.yml中添加nacos地址配置:
yaml
spring:
application:
name: item-service # 服务名称
cloud:
nacos:
server-addr: 192.168.150.101:8848 # nacos地址,192.168.150.101修改成自己的虚拟机地址
- 启动服务实例
为了测试一个服务多个实例的情况,我们再配置一个item-service的部署实例:

然后配置启动项,注意重命名并且配置新的端口,避免冲突:

bash
-Dserver.port=8083
重启item-service的两个实例:

访问nacos控制台,可以发现服务注册成功:

点击详情,可以查看到item-service服务的两个实例信息:

4.4 服务发现
服务的消费者要去nacos订阅服务,这个过程就是服务发现,步骤如下:
- 引入依赖
- 配置Nacos地址
- 发现并调用服务
- 引入依赖
服务发现除了要引入nacos依赖以外,由于还需要负载均衡,因此要引入SpringCloud提供的LoadBalancer依赖。
消费者(如cart-service)中的pom.xml中添加下面的依赖:
xml
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
可以发现,这里Nacos的依赖于服务注册时一致,这个依赖中同时包含了服务注册和发现的功能。因为任何一个微服务都可以调用别人,也可以被别人调用,即可以是调用者,也可以是提供者。
因此,等一会儿cart-service启动,同样会注册到Nacos
- 配置Nacos地址
在cart-service的application.yml中添加nacos地址配置:
yaml
spring:
cloud:
nacos:
server-addr: 192.168.150.101:8848
- 发现并调用服务
服务调用者cart-service就可以去订阅item-service服务了。不过item-service有多个实例,而真正发起调用时只需要知道一个实例的地址。
因此,服务调用者必须利用负载均衡的算法,从多个实例中挑选一个去访问。常见的负载均衡算法有:
- 随机
- 轮询
- IP的hash
- 最近最少访问
- ...
这里我们可以选择最简单的随机负载均衡
服务发现需要用到一个工具,DiscoveryClient,SpringCloud已经帮我们自动装配,我们可以直接注入使用:

我们就可以对原来的远程调用做修改了,之前调用时我们需要写死服务提供者的IP和端口:
java
private void handleCartItems(List<CartVO> vos) {
// TODO 1.获取商品id
Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
// 2.查询商品
// 单体架构,本地调用
// List<ItemDTO> items = itemService.queryItemByIds(itemIds);
// RestTemplate远程调用
/* // 2.1.利用RestTemplate发起http请求,得到http的响应
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
"http://localhost:8081/items?ids={ids}", //请求路径
HttpMethod.GET, //请求方法
null, //请求实体
new ParameterizedTypeReference<List<ItemDTO>>() {}, //返回值类型
Map.of("ids", CollUtil.join(itemIds, ",")) //请求参数
);*/
// Nacos服务发现
// 2.1 发现item-service服务的实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
// 2.2 负载均衡,挑选一个实例
ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size()));
// 2.3 发送请求,查询商品
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
instance.getUri() + "/items?ids={ids}", //请求路径
HttpMethod.GET, //请求方法
null, //请求实体
new ParameterizedTypeReference<List<ItemDTO>>() {
}, //返回值类型
Map.of("ids", CollUtil.join(itemIds, ",")) //请求参数
);
// 2.2.解析响应
if (!response.getStatusCode().is2xxSuccessful()) {
// 查询失败,直接结束
return;
}
List<ItemDTO> items = response.getBody();
if (CollUtils.isEmpty(items)) {
return;
}
// 3.转为 id 到 item的map
Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
// 4.写入vo
for (CartVO v : vos) {
ItemDTO item = itemMap.get(v.getItemId());
if (item == null) {
continue;
}
v.setNewPrice(item.getPrice());
v.setStatus(item.getStatus());
v.setStock(item.getStock());
}
}

经过swagger测试,相关代码在服务拆分部分。
五、OpenFeign
OpenFeign 利用SpringMVC的相关注解来声明请求方式、请求路径、请求参数、返回值类型 4个参数,然后基于动态代理帮我们生成远程调用的代码,而无需我们手动再编写;
5.1 快速入门
- 引入依赖
在cart-service服务的pom.xml中引入OpenFeign的依赖和loadBalancer依赖:
xml
<!--openFeign-->
<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>
- 启用OpenFeign
在cart-service的CartApplication启动类上添加注解,启动OpenFeign功能:

- 编写OpenFeign客户端
在cart-service中,定义一个新的接口,编写Feign客户端:
java
package com.hmall.cart.client;
import com.hmall.cart.domain.dto.ItemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@FeignClient("item-service")
public interface ItemClient {
@GetMapping("/items")
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
只需要声明接口,无需实现方法。接口中的几个关键信息:
@FeignClient("item-service") :声明服务名称@GetMapping :声明请求方式@GetMapping("/items") :声明请求路径@RequestParam("ids") Collection<Long> ids :声明请求参数List<ItemDTO> :返回值类型
OpenFeign就可以利用动态代理 帮我们实现这个方法,并且向http://item-service/items发送一个GET请求,携带ids为请求参数,并自动将返回值处理为List
只需要直接调用这个方法,即可实现远程调用
- 使用FeignClient
在cart-service的com.hmall.cart.service.impl.CartServiceImpl中的代码,直接调用ItemClient的方法:

java
private void handleCartItems(List<CartVO> vos) {
// TODO 1.获取商品id
Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
// 2.查询商品
// 单体架构,本地调用
// List<ItemDTO> items = itemService.queryItemByIds(itemIds);
// RestTemplate远程调用
/* // 2.1.利用RestTemplate发起http请求,得到http的响应
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
"http://localhost:8081/items?ids={ids}", //请求路径
HttpMethod.GET, //请求方法
null, //请求实体
new ParameterizedTypeReference<List<ItemDTO>>() {}, //返回值类型
Map.of("ids", CollUtil.join(itemIds, ",")) //请求参数
);*/
// Nacos服务发现
/*// 2.1 发现item-service服务的实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
// 2.2 负载均衡,挑选一个实例
ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size()));
// 2.3 发送请求,查询商品
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
instance.getUri() + "/items?ids={ids}", //请求路径
HttpMethod.GET, //请求方法
null, //请求实体
new ParameterizedTypeReference<List<ItemDTO>>() {
}, //返回值类型
Map.of("ids", CollUtil.join(itemIds, ",")) //请求参数
);
// 2.2.解析响应
if (!response.getStatusCode().is2xxSuccessful()) {
// 查询失败,直接结束
return;
}
List<ItemDTO> items = response.getBody();*/
// OpenFeign
List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
if (CollUtils.isEmpty(items)) {
return;
}
// 3.转为 id 到 item的map
Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
// 4.写入vo
for (CartVO v : vos) {
ItemDTO item = itemMap.get(v.getItemId());
if (item == null) {
continue;
}
v.setNewPrice(item.getPrice());
v.setStatus(item.getStatus());
v.setStock(item.getStock());
}
}
feign替我们完成了服务拉取、负载均衡、发送http请求的所有工作,是不是看起来优雅多了。
5.2 连接池
Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现包括:
- HttpURLConnection: 默认实现,不支持连接池
- Apache HttpClient : 支持连接池
- OKHttp: 支持连接池
通常会使用带有连接池的客户端来代替默认的HttpURLConnection,比如,我们使用OK Http;
- 引入依赖
在cart-service的pom.xml中引入依赖:
xml
<!--OK http 的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
- 开启连接池
在cart-service的application.yml配置文件中开启Feign的连接池功能:
yaml
feign:
okhttp:
enabled: true # 开启OKHttp功能

重启服务,连接池就生效了。
- 验证
我们可以打断点验证连接池是否生效,在org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient中的execute方法中打断点:

Debug方式启动cart-service,请求一次查询我的购物车方法,进入断点:

可以发现这里底层的实现已经改为OkHttpClient
5.3 最佳实践
不同的服务需要请求的接口是一样,如果在每个服务中重新编码,那样耦合性提高了。
-
思路分析
避免重复编码的办法就是抽取:
思路1: 抽取到微服务之外的公共module,抽取更加简单,工程结构也比较清晰,但缺点是整个项目耦合度偏高;
思路2: 每个微服务自己抽取一个module,抽取相对麻烦,工程结构相对更复杂,但服务之间耦合度降低;

-
抽取Feign客户端
在hmall下定义一个新的module,命名为hm-api:
其依赖代码:
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hmall</artifactId>
<groupId>com.heima</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hm-api</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--open feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- load balancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- swagger 注解依赖 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.6</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
把ItemDTO和ItemClient都拷贝过来,最终结构如下:

任何微服务要调用item-service中的接口,只需要引入hm-api模块依赖即可,无需自己编写Feign客户端了
- 扫描包
我们在cart-service的pom.xml中引入hm-api模块
xml
<!--feign模块-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-api</artifactId>
<version>1.0.0</version>
</dependency>
删除cart-service中原来的ItemDTO和ItemClient,重启项目。
ItemClient定义到了com.hmall.api.client包下,而cart-service的启动类定义在com.hmall.cart包下,扫描不到ItemClient,所以报错了,解决方法,在cart-service的启动类上添加声明即可,两种方式:
方式1:声明扫描包

方式2:声明要用的FeignClient

5.4 日志配置
OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:
NONE:不记录任何日志信息,这是默认值。BASIC:仅记录请求的方法,URL以及响应状态码和执行时间HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。
- 定义日志级别
在hm-api模块下新建一个配置类,定义Feign的日志级别:

java
package com.hmall.api.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfig {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.FULL;
}
}
- 配置
要让日志级别生效,还需要配置这个类。有两种方式:
局部生效:在某个FeignClient中配置,只对当前FeignClient生效
java
@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
全局生效:在@EnableFeignClients中配置,针对所有FeignClient生效。
java
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)
日志格式:
17:35:32:148 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] ---> GET http://item-service/items?ids=100000006163 HTTP/1.1
17:35:32:148 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] ---> END HTTP (0-byte body)
17:35:32:278 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] <--- HTTP/1.1 200 (127ms)
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] connection: keep-alive
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] content-type: application/json
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] date: Fri, 26 May 2023 09:35:32 GMT
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] keep-alive: timeout=60
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] transfer-encoding: chunked
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds]
17:35:32:280 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] [{"id":100000006163,"name":"巴布豆(BOBDOG)柔薄悦动婴儿拉拉裤XXL码80片(15kg以上)","price":67100,"stock":10000,"image":"https://m.360buyimg.com/mobilecms/s720x720_jfs/t23998/350/2363990466/222391/a6e9581d/5b7cba5bN0c18fb4f.jpg!q70.jpg.webp","category":"拉拉裤","brand":"巴布豆","spec":"{}","sold":11,"commentCount":33343434,"isAD":false,"status":2}]
17:35:32:281 DEBUG 18620 --- [nio-8082-exec-1] com.hmall.api.client.ItemClient : [ItemClient#queryItemByIds] <--- END HTTP (369-byte body)