微服务架构➖SpringCloud➖openFeign
关于作者
- 作者介绍
🍓 博客主页:作者主页
🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉 🏆、阿里云专家博主 、51CTO专家博主
🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨💻
1. 前言
上一节讲到Ribbon做了负载均衡 ,用 Eureka-Client 来做服务发现 , 通过 RestTemplate 来完成服务调用,但是这都不是我们的终极方案,终极方案是使用 OpenFeign。
2. 简介
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
.
3. OpenFeign 快速入门
3.1 调用的设计图
3.2 启动一个 eureka-server 服务,这里不重复演示,参考 第二部门中的 eureka-server
3.3 先创建 provider-order-service,选择依赖
3.4 provider-order-service 修改配置文件
yml
3.7 再创建 consumer-user-service,选择依赖
3.8 consumer-user-service 修改配置文件
yml
server:
port: 8081
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
3.9 consumer-user-service 创建一个接口(重点)
java
package com.zmz.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @ProjectName: springcloud
* @Package: com.zmz.feign
* @ClassName: UserOrderFeign
* @Author: 张晟睿
* @Date: 2022/10/4 17:19
* @Version: 1.0
*/
/**
* @FeignClient 声明是 feign 的调用
* value = "provider-order-service" value 后面的值必须和提供者的服务名一致
*
*/
@FeignClient(value = "provider-order-service")
public interface UserOrderFeign {
@GetMapping("doOrder")
String doOder();
}
3.10 consumer-user-service 创建 controller
java
package com.zmz.controller;
import com.zmz.feign.UserOrderFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ProjectName: springcloud
* @Package: com.zmz.controller
* @ClassName: UserController
* @Author: 张晟睿
* @Date: 2022/10/4 17:21
* @Version: 1.0
*/
@RestController
public class UserController {
@Autowired
UserOrderFeign userOrderFeign;
/**
* 用户远程调用下单的接口
*
* @return
*/
@GetMapping("userDoOrder")
public String userDoOrder() {
String result = userOrderFeign.doOder();
System.out.println(result);
return result;
}
}
3.11 consumer-user-service 修改启动类
java
package com.zmz;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients //标记feign客户端
public class ConsumerUserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerUserServiceApplication.class, args);
}
}
3.12 启动调用测试
[localhost:8081/userDoOrder](http://localhost:8081/userDoOrder)
3.13 本次调用总结
consumer-user-service---》 /userDoOrder ---》通过 feign 调用 /doOrder ---》 provider-order-service 下单成功
3.14 测试 feign 调用的负载
测试访问:
3.15 调用超时设
因为 ribbon 默认调用超时时长为 1s ,可以修改,超时调整可以查看 不知道如何配置feign,搜索DefaultClientConfigImpl.class 进行搜索。
yaml
feign:
ReadTimeout: 3000 #给3s超时时间
ConnectTimeout: 5000 #修改连接时长为 5s
4. OpenFeign 调用参数处理(开发重点)
4.1 前言
Feign传参确保消费者和提供者的参数列表一致包括返回值方法签名要一致
- 通过URL传参数,GET请求,参数列表使用
@Pathvariable ("")
- 如果是GET请求,每个基本参数必须加
@RequestParam ("")
- 如果是POST请求,而且是对象集合等参数,必须加
@Requestbody
或者@RequestParam
4.2 修改 provider-order-service
4.2.1 创建 BaseResult 类
java
package com.zmz.util;
import java.io.Serializable;
/**
* @ProjectName: springcloud
* @Package: com.zmz.util
* @ClassName: BaseResult
* @Author: 张晟睿
* @Date: 2022/10/5 0:42
* @Version: 1.0
*/
public class BaseResult implements Serializable {
private Integer code;
private String msg;
private Object data;
public BaseResult() {
}
public BaseResult(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static BaseResult success(Integer code, String msg, Object data) {
BaseResult result = new BaseResult();
result.setCode(code);
result.setMsg(msg);
result.setData(data);
return result;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
4.2.2 创建 Order 类
JAVA
package com.zmz.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Date;
/**
* @ProjectName: springcloud
* @Package: com.zmz.pojo
* @ClassName: Order
* @Author: 张晟睿
* @Date: 2022/10/5 0:46
* @Version: 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Order implements Serializable {
private String orderSn;
private String orderName;
private String orderDetail;
private Date orderTime;
private String userId;
}
4.2.3 创建 TestParamController
JAVA
package com.zmz.controller;
import com.zmz.pojo.Order;
import com.zmz.util.BaseResult;
import org.springframework.web.bind.annotation.*;
/**
* @ProjectName: springcloud
* @Package: com.zmz.controller
* @ClassName: TestParamController
* @Author: 张晟睿
* @Date: 2022/10/5 0:52
* @Version: 1.0
*/
@RestController
public class TestParamController {
/**
* 配置单个参数
* @param name
* @return
*/
@GetMapping("testOneParam")
public BaseResult oneParam(@RequestParam("name") String name) {
System.out.println(name);
return BaseResult.success(200, "成功", "ok");
}
/**
* 配置多个参数
* @param name
* @return
*/
@GetMapping("testTwoParam")
public BaseResult twoParam(@RequestParam("name") String name,
@RequestParam("age") Integer age) {
System.out.println(name + ":" + age);
return BaseResult.success(200, "成功", "ok");
}
/**
* 测试一个对象的传参
*
* @param order
* @return
*/
@PostMapping("testObjectParam")
public BaseResult objectParam(@RequestBody Order order) {
System.out.println(order);
return BaseResult.success(200, "成功", "ok");
}
/**
* 测试一个对象 一个参数
*
* @param order
* @param name
* @return
*/
@PostMapping("testOneObjectOneParam")
public BaseResult oneObjectOneParam(@RequestBody Order order,
@RequestParam("name") String name) {
System.out.println(order);
System.out.println(name);
return BaseResult.success(200, "ok", order);
}
/**
* 测试 url 传参
* @param id
* @return
*/
@GetMapping("testUrlParam/{id}")
public BaseResult testUrlParam(@PathVariable("id") Integer id) {
System.out.println(id);
return BaseResult.success(200, "ok", id);
}
}
4.3 修改 consumer-user-service
4.3.1 将 Order 类和 BaseResult 类拷贝过来,后面会抽到公共模块里
4.3.2 修改 UserOrderFeign 接口
java
package com.zmz.feign;
import com.zmz.pojo.Order;
import com.zmz.util.BaseResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
/**
* @ProjectName: springcloud
* @Package: com.zmz.feign
* @ClassName: UserOrderFeign
* @Author: 张晟睿
* @Date: 2022/10/4 17:19
* @Version: 1.0
*/
/**
* @FeignClient 声明是 feign 的调用
* value = "provider-order-service" value 后面的值必须和提供者的服务名一致
*
*/
@FeignClient(value = "provider-order-service")
public interface UserOrderFeign {
@GetMapping("doOrder")
String doOder();
@GetMapping("testOneParam")
BaseResult oneParam(@RequestParam("name") String name);
@GetMapping("testTwoParam")
BaseResult twoParam(@RequestParam("name") String name,
@RequestParam("age") Integer age);
@PostMapping("testObjectParam")
BaseResult objectParam(@RequestBody Order order);
@PostMapping("testOneObjectOneParam")
BaseResult oneObjectOneParam(@RequestBody Order order,
@RequestParam("name") String name);
@GetMapping("testUrlParam/{id}")
BaseResult testUrlParam(@PathVariable("id") Integer id);
}
4.3.3 在 UserController 类中添加新的测试接口
java
package com.zmz.controller;
import com.zmz.feign.UserOrderFeign;
import com.zmz.pojo.Order;
import com.zmz.util.BaseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.Date;
/**
* @ProjectName: springcloud
* @Package: com.zmz.controller
* @ClassName: UserController
* @Author: 张晟睿
* @Date: 2022/10/4 17:21
* @Version: 1.0
*/
@RestController
public class UserController {
@Autowired
UserOrderFeign userOrderFeign;
/**
* 用户远程调用下单的接口
*
* @return
*/
@GetMapping("userDoOrder")
@LoadBalanced
public String userDoOrder() {
String result = userOrderFeign.doOder();
System.out.println(result);
return result;
}
@RequestMapping("testFeignParam")
public String testFeignParam() {
//测试一个参数
BaseResult result1 = userOrderFeign.oneParam("clearlove777");
System.out.println(result1);
System.out.println("--------------------------------------------------");
//测试多个参数
BaseResult result2 = userOrderFeign.twoParam("clearlove777", 666);
System.out.println(result2);
System.out.println("--------------------------------------------------");
//测试一个对象
// Order order = new Order("110", "牛排", "一份牛排 256g", new Date(), "159357");
SimpleDateFormat sdf = new SimpleDateFormat();
String format = sdf.format(new Date());
Order order = Order.builder()
.orderSn("110")
.orderName("牛排")
.orderDetail("一份牛排 256g")
.orderTime(new Date())
.userId("159357").build();
BaseResult result3 = userOrderFeign.objectParam(order);
System.out.println(result3);
System.out.println("--------------------------------------------------");
//测试 url 传参
BaseResult result4 = userOrderFeign.testUrlParam(999);
System.out.println(result4);
System.out.println("--------------------------------------------------");
//测试一个对象 一个参数
BaseResult result5 = userOrderFeign.oneObjectOneParam(order,
"clearlove77");
System.out.println(result5);
System.out.println("--------------------------------------------------");
return "ok";
}
}
4.3.4 测试调试
访问:http://localhost:8081/testFeignParam
4.3.5 时间日期参数问题
使用 feign 远程调用时,传递 Date 类型,接收方的时间会相差 14 个小时,是因为时区造成的。处理方案:
- 使用字符串传递参数,接收方转换成时间类型(推荐使用)不要单独传递时间
- 使用 JDK8 的 LocalDate(日期) 或 LocalDateTime(日期和时间,接收方只有秒,没有毫 秒)
- 自定义转换方法 传参总结:
- get 请求只用来传递基本参数 而且加注解
@RequestParam
- post 请求用来传递对象参数 并且加注解
@RequestBody
- get 请求只用来传递基本参数 而且加注解