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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 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日志并进行多维度分析。
相关文章
|
23小时前
|
监控 负载均衡 Java
5 大 SpringCloud 核心组件详解,8 张图彻底弄懂
本文图文详解 Spring Cloud 的五大核心组件,帮助深入理解和掌握微服务架构。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
5 大 SpringCloud 核心组件详解,8 张图彻底弄懂
|
3天前
|
负载均衡 算法 Java
除了 Ribbon,Spring Cloud 中还有哪些负载均衡组件?
这些负载均衡组件各有特点,在不同的场景和需求下,可以根据项目的具体情况选择合适的负载均衡组件来实现高效、稳定的服务调用。
17 5
|
22天前
|
JSON Java 数据格式
【微服务】SpringCloud之Feign远程调用
本文介绍了使用Feign作为HTTP客户端替代RestTemplate进行远程调用的优势及具体使用方法。Feign通过声明式接口简化了HTTP请求的发送,提高了代码的可读性和维护性。文章详细描述了Feign的搭建步骤,包括引入依赖、添加注解、编写FeignClient接口和调用代码,并提供了自定义配置的示例,如修改日志级别等。
54 1
|
2月前
|
负载均衡 Java Nacos
SpringCloud基础2——Nacos配置、Feign、Gateway
nacos配置管理、Feign远程调用、Gateway服务网关
SpringCloud基础2——Nacos配置、Feign、Gateway
|
2月前
|
前端开发 API 微服务
SpringCloud微服务之间使用Feign调用不通情况举例
SpringCloud微服务之间使用Feign调用不通情况举例
448 2
|
1月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
37 0
|
2月前
|
Java API 开发者
【已解决】Spring Cloud Feign 上传文件,提示:the request was rejected because no multipart boundary was found的问题
【已解决】Spring Cloud Feign 上传文件,提示:the request was rejected because no multipart boundary was found的问题
337 0
|
3月前
|
Java Spring
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
|
3月前
|
Java Spring 容器
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
|
3月前
|
存储 Java Spring
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
下一篇
无影云桌面