SpringCloud学习笔记(四)——OpenFeign
一、OpenFegin简介
Feign 是声明性(注解)Web 服务客户端。它使编写 Web 服务客户端更加容易。要使用 Feign,请创建一个接口并对其进行注解。它具有可插入注解支持,包括 Feign 注解和 JAX-RS 注解。Feign 还支持可插拔编码器和解码器。Spring Cloud 添加了对 Spring MVC 注解的支持,并支持使用 HttpMessageConverters,Spring Web 中默认使用的注解。Spring Cloud 集成了 Ribbon 和 Eureka 以及 Spring Cloud LoadBalancer,以在使用 Feign 时提供负载平衡的 http 客户端。
Feign 是一个远程调用的组件 (接口,注解) http 调用的
Feign 集成了 ribbon,ribbon 里面集成了 eureka
二、OpenFegin入门
2.1 本次调用设计图
2.2 首先启动一台Eureka Server
2.3 其次创建一个provider-order-service
然后修改pom文件,在主类中增加注解。
修改配置文件如下:
server: port: 8081 spring: application: name: provider-order-service eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: instance-id: ${spring.application.name}:${server.port} prefer-ip-address: true
新建一个controller,写一个访问接口。
@RestController public class OrderController { @GetMapping("doOrder") public String doOrder() { System.out.println("有用户来下单了"); return "下单成功!"; } }
然后启动用例,并进行测试。
2.4 其次创建一个consumer-user-service
同样修改pom文件并在主类中增加配置。
配置文件增加内容如下:
server: port: 8082 spring: application: name: consumer-user-service eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: instance-id: ${spring.application.name}:${server.port} prefer-ip-address: true
新建一个fegin文件夹,并在其中增加一个fegin接口:
接口中代码如下:
//@FeignClient 声明是 feign 的调用 // value = "provider-order-service" value 后面的值必须和提供者的服务名一致 @FeignClient(value = "provider-order-service") public interface UserOrderFegin { /** * 描述: 下单的方法 这里的路径必须和提供者的路径一致 * * @param : * @return java.lang.String */ @GetMapping("doOrder") String doOrder(); }
新建一个controller,并在其中增加一个接口。
@RestController public class UserController { @Autowired private UserOrderFegin userOrderFeign; /** * 用户远程调用下单的接口 * * @return */ @GetMapping("userDoOrder") public String userDoOrder() { String result = userOrderFeign.doOrder(); System.out.println(result); return result; } }
记得还要在主类中增加一个注解,否则那个fegin接口自动注入时会报错。
@SpringBootApplication @EnableEurekaClient @EnableFeignClients //标记 feign 的客户端 public class ConsumerUserServiceApplication { public static void main(String[] args) { SpringApplication.run(ConsumerUserServiceApplication.class, args); } }
然后我们启动这个项目并进行测试。
说明已经成功访问到订单服务器了。
2.5 本次调用总结
2.6 防止order-service那一段访问处理逻辑时间过长的解决办法
因 为 ribbon 默 认 调 用 超 时 时 长 为 1s , 可 以 修 改 , 超 时 调 整 可 以 查 看DefaultClientConfigImpl。
然后在user-service这一端配置上相应的参数:
ribbon: #feign 默认调用 1s 超时 ReadTimeout: 5000 #修改调用时长为 5s ConnectTimeout: 5000 #修改连接时长为 5s
三、自己手写一个fegin调用
四、fegin远程调用的传参问题
在之前provider-order-service和consumer-user-service的基础上进行传参调用。
4.1 参数传递规范
Feign 传参确保消费者和提供者的参数列表一致 包括返回值 方法签名要一致
1. 通过 URL 传参数,GET 请求,参数列表使用@PathVariable(“”)
2. 如果是 GET 请求,每个基本参数必须加@RequestParam(“”)
3. 如果是 POST 请求,而且是对象集合等参数,必须加@Requestbody 或者@RequestParam
4.2 修改provider-order-service中的内容
首先创建一个bean类order,用来测试post方法传递对象,代码如下:
@Data @AllArgsConstructor @NoArgsConstructor @Builder public class Order { private Integer id; private String name; private Double price; private Date time; }
然后创建一个ParaController类,里边提供一下接口供user-service调用,代码如下:
@RestController public class ParaController { /* *测试的参数有五类: * 1.只有一个参数的get请求; * 2.有多个参数的get请求; * 3.只有一个对象参数的post请求; * 4.有一个对象参数和一个普通参数的post请求; * 5.url请求 */ /* *测试单个参数get *注意:如果注解中没有required=false则这个参数是必须要传过来的 */ @GetMapping("testOnePara") public String testOnePara(@RequestParam(value="name",required = false) String name) { System.out.println("name:"+name); return "success"; } /* *测试两个参数的get请求,两个参数最好都加上注解 */ @GetMapping("testTwoPara") public String testTwoPara(@RequestParam String name,@RequestParam Integer age) { System.out.println("name:"+name+",age:"+age); return "success"; } /* *测试一个对象的post请求,注意要用的注解 */ @PostMapping("testObjectPara") public String testObjectPara(@RequestBody Order order) { System.out.println(order); return "success"; } /* *测试一个对象和一个参数的请求 */ @PostMapping("testObjectAndPara") public String testObjectAndPara(@RequestBody Order order,@RequestParam String name) { System.out.println("order:"+order); System.out.println("name:"+name); return "success"; } @GetMapping("testUrlParam/{id}/and/{name}") public String testUrlParam(@PathVariable("id") Integer id,@PathVariable("name") String name) { System.out.println("id:"+id); System.out.println("name:"+name); return "success"; } }
4.3 修改consumer-user-service
首先同样创建bean类oder;
然后修改fegin接口,增加远程调用接口的抽象方法,整体代码如下:
@FeignClient(value = "provider-order-service") public interface UserOrderFegin { /** * 描述: 下单的方法 这里的路径必须和提供者的路径一致 * * @param : * @return java.lang.String */ @GetMapping("doOrder") String doOrder(); /* *测试单个参数get *注意:如果注解中没有required=false则这个参数是必须要传过来的 */ @GetMapping("testOnePara") public String testOnePara(@RequestParam(value="name",required = false) String name) ; /* *测试两个参数的get请求,两个参数最好都加上注解 */ @GetMapping("testTwoPara") public String testTwoPara(@RequestParam String name,@RequestParam Integer age); /* *测试一个对象的post请求,注意要用的注解 */ @PostMapping("testObjectPara") public String testObjectPara(@RequestBody Order order); /* *测试一个对象和一个参数的请求 */ @PostMapping("testObjectAndPara") public String testObjectAndPara(@RequestBody Order order,@RequestParam String name); @GetMapping("testUrlParam/{id}/and/{name}") public String testUrlParam(@PathVariable("id") Integer id, @PathVariable("name") String name); }
新建一个controller类或者在其它controller类中增加一个接口方法。
@RestController public class ParaController { @Autowired private UserOrderFegin userOrderFegin; @GetMapping("testPara") public String testPara() { String test1 = userOrderFegin.testOnePara("zhangsan1"); System.out.println("测试单参数:"+test1); String test2 = userOrderFegin.testTwoPara("lisi",25); System.out.println("测试双参数:"+test2); Order order = Order.builder().id(111).name("蔬菜").price(15.5).time(new Date()).build(); String test3 = userOrderFegin.testObjectPara(order); System.out.println("测试一个对象:"+test3); String test4 = userOrderFegin.testObjectAndPara(order,"wangwu"); System.out.println("测试一个对象和一个参数:"+test4); String test5 = userOrderFegin.testUrlParam(15,"zhaoliu"); System.out.println("测试url传参:"+test5); return "success"; }
4.4 测试
@RestController public class ParaController { @Autowired private UserOrderFegin userOrderFegin; @GetMapping("testPara") public String testPara() { String test1 = userOrderFegin.testOnePara("zhangsan1"); System.out.println("测试单参数:"+test1); String test2 = userOrderFegin.testTwoPara("lisi",25); System.out.println("测试双参数:"+test2); Order order = Order.builder().id(111).name("蔬菜").price(15.5).time(new Date()).build(); String test3 = userOrderFegin.testObjectPara(order); System.out.println("测试一个对象:"+test3); String test4 = userOrderFegin.testObjectAndPara(order,"wangwu"); System.out.println("测试一个对象和一个参数:"+test4); String test5 = userOrderFegin.testUrlParam(15,"zhaoliu"); System.out.println("测试url传参:"+test5); return "success"; } }
五、时间日期参数问题
如果时间类型被包装在对象里传递是没有问题的,但是如果将时间作为单个参数传递,那么就会差14个小时,这是时区造成的。
正确传递时间参数有下列方案:
1.转换成字符串传递(推荐使用)
2.使用 JDK8 的 LocalDate(日期) 或 LocalDateTime(日期和时间,接收方只有秒,没有毫秒)
3.将时间参数找个对象包起来。
六、源码分析
根据上面的案例,我们知道 feign 是接口调用,接口如果想做事,必须要有实现类
可是我们并没有写实现类,只是加了一个@FeignClient(value=”xxx-service”)的注解
所以我们猜测 feign 帮我们创建了代理对象,然后完成真实的调用。
动态代理 1jdk (
invoke) 2cglib 子类继承的
1. 给接口创建代理对象(启动扫描)
2. 代理对象执行进入 invoke 方法
3. 在 invoke 方法里面做远程调用
具体我们这次的流程:
A. 扫描注解得到要调用的服务名称和 url
B. 拿到 provider-order-service/doOrder,通过 ribbon 的负载均衡拿到一个服务,
provider-order-service/doOrder---》http://ip:port/doOrder
C. 发起请求,远程调用
七、openFegin总结
OpenFeign 主要基于接口和注解实现了远程调用
源码总结:面试
1. OpenFeign 用过吗?它是如何运作的?在主启动类上加上@EnableFeignClients 注解后,启动会进行包扫描,把所有加了
@FeignClient(value=”xxx-service”)注解的接口进行创建代理对象通过代理对象,使用
ribbon 做了负载均衡和远程调用
2. 如何创建的代理对象?
当 项 目 在 启 动 时 , 先 扫 描 , 然 后 拿 到 标 记 了 @FeignClient 注 解 的 接 口 信 息 , 由
ReflectiveFeign 类的 newInstance 方法创建了代理对象 JDK 代理
3. OpenFeign 到底是用什么做的远程调用?
使用的是 HttpURLConnection (
java.net)
4. OpenFeign 怎么和 ribbon 整合的?
在代理对象执行调用的时候
八、OpenFegin的日志功能
从前面的测试中我们可以看出,没有任何关于远程调用的日志输出,如请头,参数。Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而揭开 Feign 中 Http 请求的所有细节。
8.1 查看OpenFegin的日志级别
NONE 默认的,不显示日志
BASE 仅记录请求方法,URL ,响应状态码及执行时间
HEADERS 在 BASE 之上增加了请求和响应头的信息
FULL 在 HEADERS 之上增加了请求和响应的正文及无数据
8.2 创建配置类或者直接在主类中添加如下方法
@Configuration public class FeignConfig { @Bean Logger.Level feignLogger() { return Logger.Level.FULL; } }
8.3 然后修改配置文件,允许打印该类的日志信息
知识点:日志级别有哪些
logging:
level:
com.bjpowernode.feign.UserOrderFeign: debug
8.4 然后再次使用OpenFegin调用时看日志信息即可
分类:
SpringCloud学习
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)