Spring Cloud OpenFeign服务调用,OpenFeign是一个声明式的Web客户端,使编写Web服务客户端变得非常容易,只需要创建一个接口并在接口上添加注解即可。 前面在使用Ribbon + RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。 但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用, 所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。

1. 简介

  1. Feign的作用:前面在使用Ribbon + RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用, 所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进-步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。
  2. Feign集成了Ribbon:利用Ribbon维护了服务列表信息,并通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只需要定义服务绑定接口并且以声明式的方法,优雅而简单的完成了服务调用。

2.Feign和OpenFeign

2.1 Feign

Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端 Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口就可以调用服务注册中心的服务.

<dependency>
<groupId>org. springframework. cloud</ groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

2.2 OpenFeign

OpenFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如@RequesMapping等等。OpenFeign的@FeignClient可以解析 SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

3. 消费者服务

OpenFeign是客户端服务调用框架,所以是应用在消费者服务端(cloud-consumer-feign-oder80)

3.1 项目依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>live.yremp.springcloud</artifactId>
        <groupId>live.yremp</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-feign-order80</artifactId>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

<!--        eureka client-->
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>live.yremp</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

3.2 application.yml

server:
  port: 80

spring:
  application:
    name: cloud-order-service

eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: false
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      #集群则注册到多个Eureka 服务端
      defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka

3.3 主启动类

这个时候需要在主启动类上面添加 @EnableFeignClients 注解

package live.yremp.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class,args);
    }
}

3.4 服务调用接口

在 cloud-consumer-feign-oder80 services层新建下面的接口调用服务提供者cloud-provider-payment8001 中的 /payment/get/{id} 请求接口

package live.yremp.springcloud.services;

import live.yremp.springcloud.entries.CommonResult;
import live.yremp.springcloud.entries.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}

3.5 Controller调用

Controller 调用上面的 PaymentFeignService 进行 cloud-provider-payment8001cloud-provider-payment8002 下面的 @GetMapping(value = "/payment/get/{id}") 请求并且同时可以使用Ribbon的负载均衡

package live.yremp.springcloud.controller;

import live.yremp.springcloud.entries.CommonResult;
import live.yremp.springcloud.entries.Payment;
import live.yremp.springcloud.services.PaymentFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderController {
    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        return  paymentFeignService.getPaymentById(id);
    }
}

3.6 初步测试

启动 下面五个项目测试

  1. cloud-consumer-feign-order80
  2. cloud-eureka-server7001
  3. cloud-eureka-server7002
  4. cloud-provider-payment8001
  5. cloud-provider-payment8001

服务之间的关系如下

测试结果如下:

可以访问并且以Ribbon的轮询方式完成负载均衡。

4. OpenFeign超时等待

4.1 异常测试

OpenFeign默认超时等待时间为1 s,请求超过这个时间就会报超时异常,下面再服务提供者添加一个模拟的超时请求:

@GetMapping("/payment/feign/timeout")
    public String timeOut() throws InterruptedException {
        sleep(3000);
        return serverPort;
    }

PaymentFeignService接口添加

package live.yremp.springcloud.services;

import live.yremp.springcloud.entries.CommonResult;
import live.yremp.springcloud.entries.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);

    @GetMapping("/payment/feign/timeout")
    public String timeOut();
}

在消费者服务调用这个请求

package live.yremp.springcloud.controller;

import live.yremp.springcloud.entries.CommonResult;
import live.yremp.springcloud.entries.Payment;
import live.yremp.springcloud.services.PaymentFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderController {
    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        return  paymentFeignService.getPaymentById(id);
    }

    @GetMapping("/consumer/payment/feign/timeout")
    public String feignTimeOut(){
        return paymentFeignService.timeOut();
    }
}

此时去访问,会提示读取超时异常

4.2 设置超时时间

可以在配置文件里设置超时相关设置

ribbon:
  #指的是连接后从服务器读取可用资源的时间
  ReadTimeout: 5000
  #建立连接需要的时间
  Connecttimeout: 5000

配置之后,再次测试

5. 日志配置

5.1 日志级别

OpenFeign日志级别有下面几种

  1. NONE:默认的,不显示任何日志;
  2. BASIC:仅记录请求方法、URL、 响应状态码及执行时间;
  3. HEADERS:除了BASIC 中定义的信息之外,还有请求和响应的头信息;
  4. FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。

5.2 配置类设置

package live.yremp.springcloud.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    Logger.Level feignLogger(){
        return  Logger.Level.FULL;
    }
}

5.4 配置文件设置

logging:
  level:
    #feign以什么级别监听什么接口
    live.yremp.springcloud.services.PaymentFeignService: debug

测试访问

标签云

ajax AOP Bootstrap cdn Chevereto CSS Docker Editormd GC Github Hexo IDEA JavaScript jsDeliver JS樱花特效 JVM Linux Live2D markdown Maven MyBatis MyBatis-plus MySQL Navicat Oracle Pictures QQ Sakura SEO Spring Boot Spring Cloud Spring Cloud Alibaba SpringMVC Thymeleaf Vue Web WebSocket Wechat Social WordPress Yoast SEO 代理 分页 图床 小幸运 通信原理

Spring Cloud OpenFeign服务调用
Spring Cloud OpenFeign服务调用