Inheritance, Logging, Compression in Feign of Spring Cloud

Posted by Hillary on Mon, 05 Aug 2019 10:23:22 +0200

The last article shared with you the basic usage of Feign, a declarative micro-service invocation component. I believe you have learned the benefits of using Feign. Feign effectively solves the problem of code template when using RestTemplate, which makes the invocation between services easier and more convenient, but also less error-prone. However, careful readers may also find that the Feign we learned in the previous article has some obvious drawbacks. For example, when we define interfaces in provider s, it may be as follows:

@RestController
public class GirlController {
    @GetMapping("/girl")
    public String girl(String name) {
        return "love " + name + " !";
    }
}

Then it is defined in feign-consumer:

@FeignClient("provider")
public interface GirlService {
    @GetMapping("/girl")
    String gril(@RequestParam("name") String name);
}

You can see that the provider and feign-consumer code are obviously duplicated, and if the parameters invoked are inconsistent with those provided, then an error will be reported. If you are not careful, this will inevitably happen. So how to avoid such a thing happening? So we can use feign inheritance.

Dead work

First, we create a FeignAdvanced common maven project as the parent project, then we create a SpringBook project of eureka as the sub-module in the parent project. I will not elaborate on the creation of eureka project here, because it is very simple, you can refer to the article I wrote earlier.
After creating the eureka project, we create a common maven project as a sub-module.
Its dependence is as follows:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.6.RELEASE</version>
        </dependency>
    </dependencies>

Just a simple web dependency, and then we create a GirlService interface in the commons project:

public interface GirlService {
  @GetMapping("/girl")
  String girl(@RequestParam String name);
}

In this GirlService interface, we will extract and define the common parts of the provider and feign-consumer mentioned earlier, then call the interface in the provider and implement the interface in feign-consumer.

Once this interface is defined, we are going to implement our inheritance.

Inheritance

Inheritance in Feign consists of two steps:

  1. Implementing Common Interface in provider
  2. Call the interface in provider in feign-consumer

Implementing Interface in provider

Then we create a SpringBook project as a sub-project of the provider, and the provider is specifically created. I'm not going to go into details here. It's very simple. You can refer to my previous article. After creating the provider project, we add our commons to the dependencies:

 <dependencies>
        <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.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>cn.com.scitc</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

The above is the dependency of providers, and then we need to register providers in eureka. This step is very simple, you can refer to my previous article.

Then we define a GirlController in the provider to implement the GirlService interface defined in commons:

@RestController
public class GirlController  implements GirlService {
    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public String girl(String name) {
        log.info("provider provide girl Services");
        return "love" + name + "!";
    }
}

This completes the development of our provider interface.

Calling interfaces in feign-consumer

Let's create a feign-consumer SpringBook project as a sub-module with the following dependencies:

<dependencies>
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>cn.com.scitc</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

Then modify the configuration file and register feign-consumer on eureka, which is not described here. Then we need to add the @EnableFeignClients annotation to the feign-consumer startup class to enable Feign support:

@SpringBootApplication
@EnableFeignClients
public class FeignConsumerApplication {

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

Then we add a FeignGirlService interface in feign-consumer and inherit the GirlService interface in commons dependencies, as follows:

@FeignClient("provider")
public interface FeignGirlService extends GirlService {
}

It should be noted that the FeignGirlService interface here is directly inherited from GirlSerivce. After inheritance, FeignGirlService automatically has the interface in GirlSerivce, so it can be used directly after the @FeignClient("provider") annotation is used to bind the service.

Then I select a LoveGirl Controller from feign-consumer and use FeignGirl Service in LoveGirl Controller:

@RestController
public class LoveGirlController {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    @Autowired
    FeignGirlService girlService;

    @GetMapping("/girl")
    public String girl(String name) {
        log.info("consumer Called provider Provided girl service");
        return girlService.girl(name);
    }
}

Then we start eureka, provider, feign-consumer to access our services. The results are as follows:

Analysis of Advantages and Disadvantages

What's the difference between this writing and our previous writing? What are the disadvantages and advantages?

  1. With the inheritance feature, the code is concise and error-prone, and there is no need to worry about whether the return value of the interface is written correctly or whether the address of the interface is written correctly. If the address of the interface changes, and there is no need for providers and feign-consumers to make a big move, only need to modify the commons module, providers and feign-consumers will naturally change;
  2. When binding an interface in feign-consumer, as mentioned earlier, if it is a parameter in the form of key/value or in the header, you must use the @RequestParam annotation or @RequestHeader annotation, which is the same rule here. That is, when defining interfaces in commons, if relevant parameters are involved, one of the @RequestParam annotations or @RequestHeader annotations should be added.
  3. Of course, the use of inheritance features is not without drawbacks. Inheritance binds provider and feign-consumer together, and the code coupling becomes higher and more changeable. At this time, strict design specifications are needed. Otherwise, it will affect the whole body and increase the difficulty of project maintenance.

Log configuration

We used Feign, and if you want to see the calls before microservices, you can use Feign's logging capabilities.
Feign has four log functions:
NONE, do not open logging, default is this
BASIC, record request method and request URL, state code of response and execution time
HEADERS, on the basis of Article 2, adds a request header and a response header
FULL, add body and metadata on the basis of Article 3

The strongest thing we use is FULL.

So how to use Feign's logging function? Very simple, just add a bean to the startup class to do the following:

@SpringBootApplication
@EnableFeignClients
public class FeignConsumerApplication {

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

    @Bean
    Logger.Level loggerLevel() {
        return Logger.Level.FULL;
    }
}

Here we choose the strongest FULL and configure it in application.yml or application.properties. Here I use yml.

logging:
  level:
    cn:
      com:
        scitc:
          FeignGirlService: debug

Here logging.level refers to the prefix at the log level, and cn.com.scitc.FeignGirlService.FeignGirlService indicates that the class outputs the log at the debug level. Of course, the class path can also be a package, which means that all classes under the package output logs at the debug level. When the configuration is complete, restart the feign-consumer project and access any of the interfaces, you can see the request log as follows:

Data Compression

Data compression, mainly to solve the transmission efficiency, the specific configuration is as follows:

feign:
  compression:
    request:
      enabled: true
      mime-types: text/html,application/json
      min-request-size: 2048
    response:
      enabled: true

The first two lines represent the open request and response compression, the third line represents the compressed data type, default is text/html,application/json,application/xml, and the fourth line represents the lower limit of compressed data, that is, when the data to be transmitted is greater than 2048, the request needs to be compressed.

Request retry

When Feign has problems, we can try to reconnect. We used Spring-retry dependency before, but Feign has a request retry function, which can be directly configured to use:

ribbon:
  MaxAutoRetries: 3
  MaxAutoRetriesNextServer: 1
  OkToRetryOnAllOperations: false

MaxAutoRetries represent the largest number of requests
MaxAutoRetriesNext Server represents the maximum number of retries
Where OkToRetryOnAllOperations represents whether to open any exceptions and retry them

Then we can also configure a request retry for a microservice:

provider:
  ribbon:
    MaxAutoRetries: 3
    MaxAutoRetriesNextServer: 1
    OkToRetryOnAllOperations: false

This configuration is for a service whose name is provider. Note that the provider service name here is the name in spring.application.name.

We can also use a bean directly without configuring it through a configuration file:

 @Bean
    public Retryer feignRetryer() {
        Retryer.Default retryer = new Retryer.Default();
        return retryer;
    }

summary

This paper mainly introduces some advanced features of Feign, a declarative microservice invocation tool, such as inheritance mechanism, log configuration, request compression, request retry, etc. The advantages and disadvantages of the inheritance feature are analyzed. In practical development, flexible use of these attributes can make our micro services run at a higher efficiency. Through the study of these characteristics, I believe that you will have a deeper understanding of Feign.

Source address

github

Topics: Spring Maven snapshot JSON