04、SpringCloud之Feign组件学习笔记(一)

简介: 04、SpringCloud之Feign组件学习笔记(一)

一、认识Feign


在之前调用生产者的服务是使用RestTemplate,这种是HTTP请求调用,而对于服务调用的终极方案是使用OpenFeign。


1.1、OpenFeign简介


openfeign文档


Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. Spring Cloud integrates Eureka, Spring Cloud CircuitBreaker, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.


是一个web客户端,想要使用Feign只需要使用一个接口以及注解,feign能够支持插件式注解,并且提供编解码器,在springcloud中也有对springmvc的注解支持,并且对于Feign本身就自带了负载均衡器。


Feign 是一个远程调用的组件 (接口,注解) http 调用的


Feign 集成了 ribbon ,ribbon 里面集成了 eureka。


feign底层默认是ribbon,采用的是轮询算法。


二、实战


2.1、案例1:使用feign来进行远程调用服务


背景

版本:SpringBoot:2.3.12.RELEASE;Spring-Cloud:Hoxton.SR12。


说明:创建两个服务:order以及user。在user服务中会去使用feign来进行调用order服务。


1、创建Order服务



创建SpringBoot项目,选择依赖如下:



<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.12.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
</properties>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>



①配置文件:applicaion.yaml


server:
  port: 8081
spring:
  application:
    name: order-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka


②在启动器上添加开启eurekaclient注解


@EnableEurekaClient  //开启服务注册


③创建一个服务,用于对外提供服务


package com.changlu.orderservice.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
 * @Description:
 * @Author: changlu
 * @Date: 6:45 PM
 */
@RestController
public class OrderController {
    @GetMapping("/doOrder")
    public String doOrder() {
        return "牛奶泡芙";
    }
}



2、创建User服务(使用feign来进行远程调用order服务)



<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.12.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
</properties>
<dependency>            
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>



①配置文件:applicaion.yaml


server:
  port: 8081
spring:
  application:
    name: order-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka


②在启动器上添加开启eurekaclient注解以及开启openfeign注解(扫描包)


@EnableEurekaClient  //开启服务注册
@EnableFeignClients //开启feign客户端扫描


③创建对应的feign接口(对应order服务的controller方法)


UserOrderFeign.java:
package com.changlu.userservice.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
 * @Description: 远程调用器
 * @Author: changlu
 * @Date: 6:52 PM
 */
@FeignClient(value = "order-service")//value表示服务名
public interface UserOrderFeign {
    /**
     * 你需要调用哪个controller  就写它的方法签名
     * 方法签名(就是包含一个方法的所有的属性)
     */
    @GetMapping("/doOrder")
    public String doOrder();
}


④创建controller,进行依赖注入发起远程调用


UserController.java:
package com.changlu.userservice.controller;
import com.changlu.userservice.feign.UserOrderFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * @Description:
 * @Author: changlu
 * @Date: 6:50 PM
 */
@RestController
public class UserController {
    @Autowired
    private UserOrderFeign userOrderFeign;
    @GetMapping("/userDoOrder")
    public String userDoOrder() {
        System.out.println("用户来访问接口:/userDoOrder");
        //发起远程调用
        String res = userOrderFeign.doOrder();
        return res;
    }
}



测试案例

我们启动之前的eureka服务,接着启动自己的编写的两个服务:




此时我们来访问:http://localhost:8082/userDoOrder



可以看到远程调用成功!


2.2、超时配置处理


超时异常复现

远程调用openfeign的默认超时时长为1s,在当前的版本背景当中。


我们修改一下Order服务中的处理时长为2s,来看看是否会出现超时异常:


@GetMapping("/doOrder")
public String doOrder() {
    //这里睡眠两秒
    try {
        TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "牛奶泡芙";
}


接着我们重启下order服务,然后去访问网址:http://localhost:8082/userDoOrder



看下报错信息:



方案:配置超时时长,解决报错异常

在User服务中修改配置文件:



ribbon:  #feign 默认调用 1s 超时
  ReadTimeout: 3000   #修改调用时长为 5s
  ConnectTimeOut: 5000  # 修改连接时长为5s


此时再访问地址:http://localhost:8082/userDoOrder



可以看到能够得到访问结果。


2.3、案例2:参数传递案例(单独日期特别处理)


常见请求体+参数案例

我们在Order服务中添加请求参数的一些请求接口:



ParamController.java:


package com.changlu.orderservice.controller;
import com.changlu.orderservice.domain.Order;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/**
 * url    /doOrder/热干面/add/油条/aaa
 * get传递一个参数
 * get传递多个参数
 * post传递一个对象
 * post传递一个对象+一个基本参数
 */
@RestController
public class ParamController {
    //url参数:PathVariable
    @GetMapping("testUrl/{name}/and/{age}")
    public String testUrl(@PathVariable("name") String name, @PathVariable("age") Integer age) {
        System.out.println(name + ":" + age);
        return "ok";
    }
    //请求参数:?xx=xx
    @GetMapping("oneParam")
    public String oneParam(@RequestParam(required = false) String name) {
        System.out.println(name);
        return "ok";
    }
    //请求参数:?xx=xx&xx=xx
    @GetMapping("twoParam")
    public String twoParam(@RequestParam(required = false) String name, @RequestParam(required = false) Integer age) {
        System.out.println(name);
        System.out.println(age);
        return "ok";
    }
    //请求体参数
    @PostMapping("oneObj")
    public String oneObj(@RequestBody Order order) {
        System.out.println(order);
        return "ok";
    }
    //请求体 + 请求参数url中的xx=xx
    @PostMapping("oneObjOneParam")
    public String oneObjOneParam(@RequestBody Order order,@RequestParam("name") String name) {
        System.out.println(name);
        System.out.println(order);
        return "ok";
    }
}



接着我们来在user服务中添加对应的feign的接口方法,然后再UserController的某个接口方法里来进行远程调用:


UserOrderFeign.java·:
package com.changlu.userservice.feign;
import com.changlu.userservice.domain.Order;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/**
 * @Description: 远程调用器
 * @Author: changlu
 * @Date: 6:52 PM
 */
@FeignClient(value = "order-service")//value表示服务名
public interface UserOrderFeign {
    /**
     * 你需要调用哪个controller  就写它的方法签名
     * 方法签名(就是包含一个方法的所有的属性)
     */
    @GetMapping("/doOrder")
    public String doOrder();
    //*****请求参数测试案例*****
    //url参数:PathVariable
    @GetMapping("testUrl/{name}/and/{age}")
    public String testUrl(@PathVariable("name") String name, @PathVariable("age") Integer age);
    //请求参数:?xx=xx
    @GetMapping("oneParam")
    public String oneParam(@RequestParam(required = false) String name);
    //请求参数:?xx=xx&xx=xx
    @GetMapping("twoParam")
    public String twoParam(@RequestParam(required = false) String name, @RequestParam(required = false) Integer age);
    //请求体参数
    @PostMapping("oneObj")
    public String oneObj(@RequestBody Order order);
    //请求体 + 请求参数url中的xx=xx
    @PostMapping("oneObjOneParam")
    public String oneObjOneParam(@RequestBody Order order,@RequestParam("name") String name);
}


UserController.java:


package com.changlu.userservice.controller;
import com.changlu.userservice.domain.Order;
import com.changlu.userservice.feign.UserOrderFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
 * @Description:
 * @Author: changlu
 * @Date: 6:50 PM
 */
@RestController
public class UserController {
    @GetMapping("/testParam")
    public String testParam() {
        //第一个:url路径携带参数
        String s1 = userOrderFeign.testUrl("changlu", 18);
        System.out.println(s1);
        //第二个:1个请求参数
        String s2 = userOrderFeign.oneParam("changlu");
        System.out.println(s2);
        //第三个:两个请求参数
        String s3 = userOrderFeign.twoParam("changlu", 666);
        System.out.println(s3);
        Order order = Order.builder()
                .name("泡芙")
                .price(1000.0)
                .time(new Date())
                .id(1)
                .build();
        //第四个:请求体
        String s4 = userOrderFeign.oneObj(order);
        System.out.println(s4);
        //第五个:第一个请求体+参数
        String s5 = userOrderFeign.oneObjOneParam(order, "changlu");
        System.out.println(s5);
        return "ok";
    }
}



测试一下:





单独日期传递【特别注意】

Order服务:


//ParamController
//时间请求参数
@GetMapping("testTime")
public String testTime(@RequestParam Date date){
    System.out.println(date);
    return "ok";
}


User服务:


//接口:UserOrderFeign
@GetMapping("testTime")
public String testTime(@RequestParam Date date);
//控制器类:UserController
/**
     * Sun Mar 20 10:24:13 CST 2022
     * Mon Mar 21 00:24:13 CST 2022  +- 14个小时
     * 1.不建议单独传递时间参数
     * 2.转成字符串(推荐,比较好的方案,在服务端可以进行format转为date对象)   2022-03-20 10:25:55:213 因为字符串不会改变
     * 3.jdk LocalDate 年月日 没问题    LocalDateTime 会丢失秒
     * 4.改feign的源码
     *
     * @return
     */
@GetMapping("/testTime")
public String testTime(){
    //错误示例1:使用new Date();  
    userOrderFeign.testTime(new Date());
    //正确方案:服务端的请求接口不应该使用Date来进行接收,尽可能使用字符串、LocalDate来进行传递
    return "ok";
}


错误案例Order服务打印结果(时间不准确):Sun Jul 17 10:26:20 CST 2022


2.4、日志处理


需求:若是我们想要知道每一个请求的详细请求参数,那么我们可以来进行打开日志配置以及来注入相应的日志等级。


操作1:在启动器或者配置类中注入一个日志等级。


/**
     * 打印fein日志信息 级别
     * @return
     */
@Bean
public Logger.Level level(){
    return Logger.Level.FULL;
}


操作2:在配置文件yaml中打开配置。


# 日志等级选择
logging:
  level:
    com.changlu.userservice.feign.UserOrderFeign: debug  # 我需要答应这个接口下面的日志


接着我们来测试一下:访问网址http://localhost:8082/testTime


此时就可以看到feign发起请求的一系列参数如下:



相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
22小时前
|
SpringCloudAlibaba Java 持续交付
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
201 0
|
22小时前
|
SpringCloudAlibaba Java 网络架构
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(七)Spring Cloud Gateway服务网关
123 0
|
22小时前
|
JSON Java Apache
Spring Cloud Feign 使用Apache的HTTP Client替换Feign原生httpclient
Spring Cloud Feign 使用Apache的HTTP Client替换Feign原生httpclient
|
22小时前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
22小时前
SpringCloud Feign报错Method has too many Body parameters
SpringCloud Feign报错Method has too many Body parameters
|
22小时前
|
Nacos
SpringCloud Feign使用
SpringCloud Feign使用
25 1
|
22小时前
|
开发框架 负载均衡 Java
Spring boot与Spring cloud之间的关系
总之,Spring Boot和Spring Cloud之间的关系是一种构建和扩展的关系,Spring Boot提供了基础,而Spring Cloud在此基础上提供了分布式系统和微服务架构所需的扩展和工具。
23 4
Spring boot与Spring cloud之间的关系
|
22小时前
|
Java Spring
spring cloud 通过feign请求动弹设置请求头heades
spring cloud 通过feign请求动弹设置请求头heades
29 0
|
22小时前
|
SpringCloudAlibaba 负载均衡 Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(目录大纲)
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(目录大纲)
72 1
|
22小时前
|
Java Nacos Sentinel
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
240 0