Microservices: OpenFeign service calls

Introduction

The full name of OpenFeign is Spring Cloud OpenFeign. It is a declarative service call and load balancing component officially launched by Spring. It appears to replace Feign that has entered the maintenance state. Spring Cloud openfeign enhances Feign to support Spring MVC annotations, and also integrates Ribbon and Nacos, making Feign more convenient to use. Feign’s use of http to call methods remotely is like calling local methods, and it doesn’t feel like remote methods. Its use is the same as writing the control class directly, exposing the interface to provide calls, we only need to write the call interface + @FeignClient annotation, when using this API, we only need to define the method, and call this method at that time. This kind of call between services is very convenient to use, and the experience is better.

How to realize the interface call?

In the usual springboot project, how is this kind of rest service called? Usually, Httpclient, Okhttp, HttpURLConnection, RestTemplate are used, among which RestTemplate is the most common. RestTemplate was used in the nacos configuration center before.

SpringCloud integrates OpenFeign

Just use an example to simply use OpenFeign to call between services, and learn about the functions of Feign components through examples.

Introducing dependencies

Using OpenFeign components requires the introduction of client dependencies

<!--OpenFeign-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Copy Code

Write call interface

When calling services remotely through OpenFeign, it is more convenient than RestTemplate, just like writing a controller interface. You need to write the @FeignClient annotation, which configures the microservice name and @RequestMapping(“/api/store”) of rest, or you can write the complete path when declaring and calling pai. The simple correspondence is shown in the figure below

code show as below:

package com.lyd.demo.feign;
import com.lyd.demo.feign.config.FeignOkhttpConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Map;
/**
 * @author: lyd
 * @description: Remotely call the service-store service
 * @Date: 2022/9/24
 * introduce:
 * name / value : the name of the microservice to call
 * path: the path above the control class --- @RequestMapping("/api/store")
 */
@FeignClient(name = "service-store", path = "/api/store")
public interface StoreFeignService {
    // Declare the rest to call
    @GetMapping("/{id}")
    Map<String, Object> getStoreNum(@PathVariable String id);
}
/**
 * @RestController
 * @RequestMapping("/api/store")
 * public class StoreController {
 * @Value("${server.port}")
 * private String currentPort;
 * @GetMapping("/{id}")
 * public Map<String, Object> getStoreNum(@PathVariable String id) throws InterruptedException {
 * Map<String, Object> map = new HashMap<>();
 * map.put("port", currentPort);
 * map.put("num", 10);
 * return map;
 * }
 * }
 */
Copy Code

Annotation @EnableFeignClients needs to be written in the startup class

@Autowired
private StoreFeignService storeFeignService;
// call directly in the business
storeFeignService.getStoreNum(uid);
Copy Code

OpenFeign custom configuration

Feign provides a lot of extension mechanisms, allowing users to use it more flexibly. feign.Logger.Level: modify the log level, including four different levels: NONE, BASIC, HEADERS, FULL feign.codec.Decoder: the parser for the response result, Parse the results of http remote calls, for example, parse json strings into java objects feign.codec.Encoder: Request parameter encoding, encode request parameters, so that it is convenient to send through http request feign.Contract< /strong>: Supported annotation format, the default is SpringMVC annotation feign. Retryer: Failure retry mechanism, retry mechanism for request failure, default is not, but Ribbon retry will be used

Log Configuration

You can display the required logs by configuring Feign’s log level.

1), define the configuration class

Define a feign configuration file and hand it over to spring management. Feign’s log level defaults to NONE at the beginning, and does not display any logs. You can return the log level by defining a bean

package com.lyd.demo.feign.config;
import feign. Logger;
import feign. Retryer;
import org.springframework.context.annotation.Bean;
import java.util.concurrent.TimeUnit;
/**
 * @author: lyd
 * @description: feign configuration file - log
 * @Date: 2022/9/24
 */
@Configuration
public class FeignConfig {
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.BASIC;
    }
}
Copy Code

There are four log levels:

  • NONE [best performance, suitable for production]: Do not record any logs (default value).
  • BASIC [Suitable for production environment tracking issues]: Only record the request method, URL, response status code, and execution time.
  • HEADERS: On the basis of recording the BASIC level, record the headers of the request and response.
  • FULL [Comparatively suitable for positioning problems in development and test environments]: Record the header, body and metadata of requests and responses.

2), configuration file setting level

The default level of springboot is info, which is relatively high and needs to be configured in the configuration file. If the level is only configured under loggin.level, it is a global configuration, so we can specify the package and specify the log level under which package.

logging:
  level:
    com.lyd.demo.feign:debug
Copy Code

3), configuration domain

Global configuration: Add the @Configuration annotation to the feign configuration class and throw it directly to spring for management to achieve global configuration. Partial configuration: ①. Partial configuration can be specified by specifying the configuration file in the feign client, just add the specified configuration class after the annotation

@FeignClient(name = "service-store", path = "/api/store", configuration = FeignConfig.class)
Copy Code

②. Local configuration can also be specified directly through the yml configuration file.

feign:
  client:
    config:
      service-goods: FULL # Specify which service and give it a type.
Copy Code

Configuration timeout

Configure the timeout period directly through yml

feign:
  client:
    config:
      default: # Here default is the global configuration, if it is to write the service name, it is the configuration for a microservice
        connectTimeout: 2000
        readTimeout: 2000
Copy Code

Add a Thread.sleep(5000) to the store service, and you can see the timeout exception SocketTimeoutException.

Retry mechanism configuration

Create a retryer by adding beans (retry period (50 milliseconds), maximum retry period (2000 milliseconds), maximum number of attempts 3 times), feign does not use a linear retry mechanism but uses an index level (multiplication) retry mechanism each retry time current retry time *= 1.5

@Bean
public Retryer retryer() {
    return new Retryer. Default(50, TimeUnit. SECONDS. toMillis(2), 3);
}
Copy Code

Let’s take a look at the default constructor to understand the meaning of the parameters more clearly.

public Default(long period, long maxPeriod, int maxAttempts) {
    this. period = period;
    this.maxPeriod = maxPeriod;
    this.maxAttempts = maxAttempts;
    this.attempt = 1;
}
Copy Code

As shown in the figure, it will retry until an exception is reported at the end. Not only that, but you can also configure contract settings, add interceptors, and more. . .

Feign usage optimization

The bottom layer of Feign initiates http requests and relies on other frameworks. Its underlying client implementation includes:

  • URLConnection: default implementation, does not support connection pooling
  • Apache HttpClient: support connection pool
  • OKHttp: support connection pool

This time using OkHttp

Import dependencies

<!--okHttp-->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>
Copy Code

Set configuration class

package com.lyd.demo.feign.config;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
/**
 * @author: lyd
 * @description: Configuration of OkHttpFeign
 * @Date: 2022/9/24
 */
@Configuration
@ConditionalOnClass({OkHttpClient. class})
@ConditionalOnProperty({"feign.okhttp.enabled"})
public class FeignOkhttpConfig {
    @Bean
    public okhttp3. OkHttpClient okHttpClient(OkhttpProperties okhttpProperties) {
        return new okhttp3.OkHttpClient.Builder()
                //Set connection timeout
                .connectTimeout(okhttpProperties.getConnectTimeout(), TimeUnit.MILLISECONDS)
                //Set read timeout
                .readTimeout(okhttpProperties.getReadTimeout(), TimeUnit.MILLISECONDS)
                // Whether to automatically reconnect
                .retryOnConnectionFailure(true)
                .connectionPool(new ConnectionPool())
                .addInterceptor(new OkHttpLogInterceptor())
                //Construct the OkHttpClient object
                .build();
    }
}
Copy Code

yml configuration

feign:
  client:
    config:
      default:
        connectTimeout: 2000
        readTimeout: 2000
  httpclient:
    enabled: false
  okhttp:
    enabled: true
    connectTimeout: 4000
    readTimeout: 3000
Copy Code

Get timeout by class

package com.lyd.demo.feign.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author: lyd
 * @description: configuration parameters
 * @Date: 2022/9/24
 */
@Data
@Component
@ConfigurationProperties(prefix = "feign.okhttp")
public class OkhttpProperties {
    private Long connectTimeout;
    private Long readTimeout;
}

Copy Code

Interceptor

The code of business requirements can be configured in the interceptor.

package com.lyd.demo.feign.config;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import java.io.IOException;
/**
 * @author: lyd
 * @description: Interceptor
 * @Date: 2022/9/24
 */
@Slf4j
public class OkHttpLogInterceptor implements Interceptor {
    @Override
    public Response intercept(Interceptor. Chain chain) throws IOException {
        //This chain contains request and response, so you can get anything you want from here
        Request request = chain. request();
        long t1 = System.nanoTime();//The time when the request was initiated
        log.info(String.format("Send request %s on %s%n%s",
                request.url(), chain.connection(), request.headers()));
        Response response = chain. proceed(request);
        long t2 = System.nanoTime();//Time to receive the response
        //Note that you cannot directly use response.body().string() to output logs here
        //Because after response.body().string(), the stream in the response will be closed and the program will report an error. We need to create a new response for the application layer to process
        ResponseBody responseBody = response. peekBody(1024 * 1024);
        log.info(String.format("received response: [%s] %n returns json:【%s】%.1fms%n%s",
                response.request().url(),
                responseBody. string(),
                (t2 - t1) / 1e6d,
                response. headers()));
        return response;
    }
}
Copy Code

Import configuration

@FeignClient(name = "service-store", path = "/api/store", configuration = FeignOkhttpConfig.class)
Copy Code

operation result:

syntaxbug.com © 2021 All Rights Reserved.