Microservice-OpenFeign service interface call

Posted by benwestgarth on Sat, 26 Feb 2022 18:19:47 +0100

What is OpenFeign

Feign is a declarative Web service client that, in other words, lets you invoke a web service as easily as a method.

What can Feign do

Feign is designed to make writing Java Http clients easier.
Previously, when Ribbon+RestTemplate was used, the encapsulation of http requests using RestTemplate formed a set of templated invocation methods. However, in actual development, since there may be more than one call to a service dependency, an interface is often called from more than one place, so it is common to wrap calls to these dependent services by encapsulating some client classes for each microservice. So Feign further encapsulates this to help us define and implement the definition of dependent service interfaces. With the implementation of Feign, we can simply create an interface and configure it with annotations (previously labeled Mapper annotations on the Dao interface and now a Feign annotation on the microservice interface) to complete the interface binding to the service provider, simplifying the development of auto-encapsulating service invocation clients when using Spring cloud Ribbon.

Feign integrates Ribbon
Ribbon maintains Payment's service list information and balances the load on the client through polling. Unlike Ribbon, service calls are implemented elegantly and simply through feign by simply defining the service binding interface and using a declarative method

Differences between OpenFeign and Feign

Configuration steps

New Service Registry eureka

New module cloud-eureka-server 7001

Write the configuration file application.yml

server:
  port: 7001


eureka:
  instance:
    hostname: eureka7001.com #Instance name of eureka server side
  client:
    register-with-eureka: false     #false means that you do not register yourself with the registry.
    fetch-registry: false     #false says that my client is the registry and my responsibility is to maintain service instances without having to retrieve services
    service-url:
    #Clusters point to other eureka
#      defaultZone: http://eureka7002.com:7002/eureka/
    #Single machine is 7001 itself
      defaultZone: http://eureka7001.com:7001/eureka/
  #server:
    #Turn off self-protection to ensure unavailable services are kicked in time
    #enable-self-preservation: false
    #eviction-interval-timer-in-ms: 2000

Write Startup Class

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

New Service Producer

New user module cloud-provider-payment8001

Increase service registration configuration eureka

eureka:
  client:
    #Indicates whether to register yourself in EurekaServer by default to true.
    register-with-eureka: true
    #Whether to grab existing registration information from EurekaServer or not, defaults to true. Single node doesn't matter, cluster must be set to true to use load balancing with ribbon
    fetchRegistry: true
    service-url:
      #Single machine version
      defaultZone: http://127.0.0.1:7001/eureka
      # Cluster Edition
      #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    instance-id: payment8001
    #Access paths can display IP addresses
    prefer-ip-address: true
    #The time interval between Eureka clients sending heartbeats to the server in seconds (default is 30 seconds)
    #lease-renewal-interval-in-seconds: 1
    #The maximum wait time for the Eureka server after receiving the last heartbeat in seconds (default is 90 seconds), and the timeout will exclude the service
    #lease-expiration-duration-in-seconds: 2

Startup Class Add @EnableEurekaClient, @EnableDiscoveryClient

//A startup class on the project that injects the startup class into the container, defines the scope of the container scan, and loads some bean s in the classpath environment.
@SpringBootApplication
/**
 * Register service with eruka
 */
@EnableEurekaClient
@EnableDiscoveryClient
public class PaymentMain8001 {

    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class, args);
    }
}

New consumer module cloud-consumer-feign-order80

Write pom file

<?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>mscloud</artifactId>
        <groupId>com.zsy.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zsy.springcloud</groupId>
    <artifactId>cloud-consumer-feign-order80</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- Introduce self-defined api Generic packages that can be used Payment payment Entity -->
        <dependency>
            <groupId>com.zsy.springcloud</groupId>
            <artifactId>cloud-api-commins</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--General Basic Universal Configuration-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>
</project>

Configure yml

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
 

Write the startup class (@EnableFeignClients)

package com.atguigu.springcloud;

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

/**
 * @auther zzyy
 * @create 2020-02-03 11:55
 */
@SpringBootApplication
@EnableFeignClients
public class OrderFeignMain80
{
    public static void main(String[] args)
    {
        SpringApplication.run(OrderFeignMain80.class,args);
    }
}
 

feign consumer remote service call interface

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

Write feign consumers

@RestController
public class OrderFeignController {

    @Resource
    private PaymentFeignService paymentFeignService;


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

test

  1. Start two eureka clusters 7001/7002 first
  2. Start another microservice 8001
  3. Start OpenFeign Start
  4. http://localhost/consumer/payment/get/1
  5. Feign built-in load balancing configuration item

feign timeout control

The feign default one-second timeout allows us to reproduce the timeout problem with the following configuration

Service producers write a service that takes three seconds

@GetMapping("/payment/timeoutCall")
    public String paymentFeignTimeOut(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return "success";
    }

Consumer configures this remote service

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


    @GetMapping("/payment/timeoutCall")
     String paymentFeignTimeOut();
}

controller call code

  @GetMapping("/consumer/payment/timeoutCall")
    public String paymentFeignTimeOut() {
        log.info("Try timeout remote call to determine if it will succeed");
        return paymentFeignService.paymentFeignTimeOut();
    }

Reproduce the timeout problem

yml adds the following configuration to solve the problem

#Set feign client timeout (OpenFeign supports ribbon by default)
ribbon:
  #Refers to the time taken to establish a connection, and is applicable to the time taken to connect both ends under normal network conditions.
  ReadTimeout: 5000
  #Refers to the time taken to read the available resources from the server after a connection has been established
  ConnectTimeout: 5000

feign adds print log functionality

feign also supports configuring to print out the entire process of a web service call

log level

 
NONE: By default, no logs are displayed;
 
BASIC: Record only request methods, URL,Response status code and execution time;
 
HEADERS: except BASIC In addition to the information defined in the, there is header information for requests and responses;
 
FULL: except HEADERS In addition to the information defined in the, there is the body and metadata of the request and response.

Write Configuration Class

@Configuration
public class FeignConfig {
    @Bean
    Logger.Level feignLoggerLevel()
    {
        return Logger.Level.FULL;
    }

}

yml Add Configuration

logging:
  level:
    # At what level does the feign log monitor which interface
    com.zsy.springcloud.service.PaymentFeignService: debug

Source Address

https://gitee.com/fugongliudehua/mscloud

Topics: Java Spring Cloud Microservices