前言
该博客为OpenFeign
学习笔记,主要目的是为了帮助后期快速复习使用
学习视频:7小快速通关SpringCloud
辅助文档:SpringCloud快速通关
一、简介
OpenFeign官网
OpenFeign
是一个声明式远程调用客户端,用于简化服务间的 HTTP
调用。与传统的 RestTemplate
不同,OpenFeign
通过接口注解实现自动化服务调用,减少了样板代码。
它支持负载均衡、熔断等功能,特别适合微服务架构中。在使用 OpenFeign
时,开发者无需手动编写 HTTP
请求代码,只需定义接口和注解,框架自动完成与远程服务的交互,大大提高了开发效率和可维护性。
二、快速入门
2.1 引入Openfeign
<!-- 远程调用Openfeign --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
2.2 开启功能
在主启动类上加上 @EnableFeignClients
,开启远程调用功能
@SpringBootApplication
@EnableFeignClients // 开启远程调用功能
public class OrderMainApplication {public static void main(String[] args) {SpringApplication.run(OrderMainApplication.class, args);}
}
2.3 远程调用
2.3.1 业务API
这里我们通过一个案例测试下功能,在订单微服务中有个创建订单的方法,在其中需要调用商品微服务查询商品信息
首先需要创建一个FeignClient
接口
@FeignClient(value = "service-product") // feign客户端 value属性指定想要调用微服务的名字
public interface ProductFeignClient {// mvc注解的两套使用逻辑// 1、标注在Controller上,是接受这样的请求// 2、标注在FeignClient上,是发送这样的请求@GetMapping("/product/{id}")Product getProductById(@PathVariable("id") Long id); // 表示向名为service-product微服务发送一个GET请求,路径参数为传入的id,并且将对方服务的JSON返回值封装成Product对象
}
注意:
mvc注解的两套使用逻辑
- 标注在Controller上,是接受这样的请求
- 标注在FeignClient上,是发送这样的请求
编写Controller
层
@RestController
@RequiredArgsConstructor
public class OrderController {private final OrderService orderService;// 创建订单@GetMapping("/create")public Order createOrder(@RequestParam("productId") Long productId,@RequestParam("userId") Long userId) {return orderService.createOrder(productId, userId);}
}
编写Service
层
@Service
@Slf4j
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {private final ProductFeignClient productFeignClient;@Overridepublic Order createOrder(Long productId, Long userId) {// 使用feign完成远程调用Product product = productFeignClient.getProductById(productId);Order order = new Order();order.setId(1L);// 总金额order.setTotalAmount(product.getPrice().multiply(BigDecimal.valueOf(product.getNum())));order.setUserId(userId);order.setNickName("jyh");order.setAddress("北京");// 远程查询商品列表order.setProductList(Arrays.asList(product));return order;}}
小技巧:如何编写好OpenFeign声明式的远程调用接口
- 如果调用自己编的业务API,直接把对方的Controller签名复制粘贴在FeignClient中就行
- 如果调用第三方API,需要参考第三方的接口文档,如何发请求,就如何定义
2.3.2 第三方API
使用高德天气API之前需要再高德控制台申请一个web服务API
秘钥[key]
在@FeignClient
上指定url地址
// 高德天气API示例:https://restapi.amap.com/v3/weather/weatherInfo?city=110101&key=<用户key>
// 如果在使用@FeignClient注解时,指定了url地址,会给指定的地址发送请求
// 如果在使用@FeignClient注解时,没有指定url地址,会给微服务发送请求,feign会自动连上配置中心根据微服务名称进行负载均衡
@FeignClient(value = "weather-client", url = "https://restapi.amap.com") // feign客户端
public interface WeatherFeignClient {// 在这里的@RequestParam注解表示该参数的位置在url上,为请求参数// 在这里的@RequestHeader注解表示该参数的位置在请求体中,为请求头@GetMapping("/v3/weather/weatherInfo")String getWeather(@RequestParam("city")String city,@RequestParam("key")String key);}
编写测试类
@SpringBootTest
public class WeatherTest {@Autowiredprivate WeatherFeignClient weatherFeignClient;@Testpublic void test() {String weather = weatherFeignClient.getWeather("110101", "your-key");System.out.println(weather);}
}
面试题:客户端负载均衡 与 服务端负载均衡 区别
客户端负载均衡与服务端负载均衡的核心区别在于负载均衡的决策位置和责任方。以下是两者的详细对比:
维度 客户端负载均衡 服务端负载均衡 决策位置 客户端(如微服务消费者) 服务端(如独立的负载均衡器) 责任方 客户端负责选择目标实例 服务端负载均衡器负责转发请求 服务发现 客户端需主动获取服务列表(如通过注册中心) 负载均衡器维护服务列表,客户端> 无需感知 网络跳转 直接连接目标实例,减少延迟 需经过负载均衡器,可能增加一跳延迟 配置与维护 集成在客户端代码中,需更新客户端 独立部署,集中配置和维护
三、进阶配置
3.1 开启日志
3.1.1 编写配置文件
logging:level:com.atguigu.order.feign: debug # 日志级别
3.1.2 添加LoggerLevel组件
在任意配置类中加上feignLoggerLevel
组件
import feign.Logger;@Configuration
public class OrderConfig {@BeanLogger.Level feignLoggerLevel() {return Logger.Level.FULL; //日志级别}
}
3.1.3 运行测试
可以看到,当开启日志后,会打印详细的请求信息,可以帮助我们快速排查问题
3.2 超时控制
- 连接超时:指的是建立连接阶段超时,默认值为
10s
- 读取超时:指的是建立连接后,对方处理业务速度过慢,导致接收响应超时,默认值为
60s
添加配置
spring:cloud:openfeign:client:config:default: # 默认配置logger-level: full # 全局日志级别connect-timeout: 1000 # 连接超时时间read-timeout: 2000 # 读取超时时间service-product: # 精确配置logger-level: fullconnect-timeout: 3000read-timeout: 5000
小技巧:
通过spring.profiles.include
,你可以在主配置文件中统一启用某些功能,而无需在每个环境的配置文件中重复配置。
3.3 重试机制
远程调用超时失败后,还可以进行多次尝试,如果某次成功返回ok,如果多次依然失败则结束调用,返回错误
在任意配置类中加入重试器Retryer
组件
@Configuration
public class OrderConfig {@BeanRetryer retryer(){return new Retryer.Default(); //使用默认的重试规则,默认重试5次,每次重试间隔100ms,最大重试间隔为1秒}
}
3.4 拦截器
创建一个拦截器实现RequestInterceptor
接口,并重写apply
方法,调用方法修改请求模板
@Component
public class XTokenRequestInterceptor implements RequestInterceptor {/*** 请求拦截器* @param requestTemplate 请求模板*/@Overridepublic void apply(RequestTemplate requestTemplate) {System.out.println("XTokenRequestInterceptor启动...");requestTemplate.header("X-Token", UUID.randomUUID().toString());}
}
3.5 fallback - 兜底返回
目的:为了能在远程调用失败的时,能返回默认数据(缓存数据、假数据等)让业务继续往下推进。
注意:
此功能需要整合 Sentinel
才能实现
3.5.1 引入 sentinel
<!-- 服务保护Sentinel -->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
3.5.2 开启熔断
feign:sentinel:enabled: true
3.5.3 编写 fallback 函数
@FeignClient(value = "service-product", fallback = ProductFeignClientFallback.class) // 添加fallback属性
public interface ProductFeignClient {@GetMapping("/product/{id}")Product getProductById(@PathVariable("id") Long id);
}
编写一个Feign客户端的兜底实现类
@Component
public class ProductFeignClientFallback implements ProductFeignClient {@Overridepublic Product getProductById(Long id) {System.out.println("兜底回调...");// 编写假数据Product product = new Product();product.setId(id);product.setPrice(new BigDecimal(0));product.setProductName("未知商品");product.setNum(0);return product;}
}