Real case: Feign's switch to okhttp can't take effect, and I'm a little panicked by the boss!

Source: https://www.cnblogs.com/mufeng3421/p/11442412.html

Tip: If you only look at how to solve the problem, please see how to solve this problem at the end of the article

1. Scenario description

Recently, feign has been used as an http request tool in the project. Compared with httpclient and resttemplate, fegin is much more convenient to use. Then the project has the requirement of httptrace, and needs to output the request log.

So I opened feign's own log and found that its own log can only be printed at the debug level. And it is printed line by line, it is very inconvenient to read the log. So it is best to output logs in json format.

2. Solution steps

2.1 Introducing feign dependencies

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

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${自行选择适合项目的版本}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Here, spring-cloud-openfeing is used to avoid the injection of feign manually, which is the same as feign in usage.

Recommend an open source and free Spring Boot practical project:

https://github.com/javastacks/spring-boot-best-practice

2.2 configure feign

Add @EnableFeignClientsannotations

@SpringBootApplication
@EnableFeignClients
public class Application {

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

}

Use feign's own contract to conveniently use feign's own annotations to declare the http interface. A configuration class is used here

@Configuration
public class FeignConfig {
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }
}

2.3 Declare the interface

You only need to declare an interface annotated with @FeignClient to declare a Feign http request interface

@FeignClient(name = "accessPlatform", url = "${url.access-platform}")
public interface AccessPlatformFeignClient {
    @RequestLine("GET /access-platform/resource")
    List<AccessResource> queryResourceList(@QueryMap Map<String, Object> query);
}

3. Switch Feign's client to OkHttp

Since feign's built-in http client implementation is HttpURLConnection, there is no connection pool function, and the configurability is relatively poor, so we use okhttp as the specific implementation of the underlying http client.

3.1 Introduce the dependency of okhttp

 <dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>${feign-okhttp.version}</version>
</dependency>

3.2 Modify the previous FeignConfig configuration class

The problem lies here, but don't worry, let's continue

@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignConfig {
    // 注入feignContract
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }

    // 注入自定义的okHttpClient
    @Bean
    public okhttp3.OkHttpClient okHttpClient(){
        return new okhttp3.OkHttpClient.Builder()
            .readTimeout(60, TimeUnit.SECONDS)
            .connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(120, TimeUnit.SECONDS)
            .connectionPool(new ConnectionPool())
            .build();
    }
}

Open okhttp as the client of feign

# application.yml
feign:
    okhttp:
        enabled: true

Everything is perfect, and so are many blogs on the Internet. And they tell you they're done configuring? Hehe, have you tried it yourself? ?

3.3 Try asking

Of course, there is a problem here. The request is not from okhttp, or the default JDK's HttpURLConnection. Where is the problem? keep watching

4. Identify the problem

I suspect that feing did not run the configuration about okhttp at all when injecting the configuration

4.1 View the feign configuration process when the service starts

1. Adjust the service root log level to debug level

logging:
    level:
        root: debug

2. Start the service and view the console output

console output

Did you see that the configuration of okhttp does not meet the configuration operation conditions.

3. Query the details of FeignAutoConfigurationthis configuration class

@Configuration
@ConditionalOnClass({Feign.class})
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
        // .....其他的配置
      @Configuration
    @ConditionalOnClass({OkHttpClient.class})
    @ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
    @ConditionalOnMissingBean({okhttp3.OkHttpClient.class})
    @ConditionalOnProperty({"feign.okhttp.enabled"})
    protected static class OkHttpFeignConfiguration {
       // ...okhttp的配置
    }

      // .....其他的配置
}

It's the ghost of this automatic configuration class. When I saw @ConditionalOnMissingBean({okhttp3.OkHttpClient.class})this annotation, I understood. In vernacular, only when there is no instance of OkHttpClient in the container. He will run. If we inject our own defined OkHttpClient instance before FeignAutoConfiguration, then I'm sorry, I quit? All injected.

5. Solve problems

Since the automatic configuration does not work, let's do it ourselves. Copy the configuration process in FeignAutoConfigurationthe configuration class, and paste it in the FeignConfig configuration class to manually inject the feign client. OK! Perfect solution.

@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignConfig {
//    private okhttp3.OkHttpClient okHttpClient;

    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }

    @Bean
    @ConditionalOnMissingBean({Client.class})
    public Client feignClient(okhttp3.OkHttpClient client) {
        return new feign.okhttp.OkHttpClient(client);
    }

    @Bean
    @ConditionalOnMissingBean({ConnectionPool.class})
    public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, OkHttpClientConnectionPoolFactory connectionPoolFactory) {
        Integer maxTotalConnections = httpClientProperties.getMaxConnections();
        Long timeToLive = httpClientProperties.getTimeToLive();
        TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
        return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
    }

    @Bean
    public OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
        Boolean followRedirects = httpClientProperties.isFollowRedirects();
        Integer connectTimeout = httpClientProperties.getConnectionTimeout();
        Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
        return httpClientFactory.createBuilder(disableSslValidation)
                .connectTimeout((long)connectTimeout, TimeUnit.MILLISECONDS)
                .followRedirects(followRedirects)
                .connectionPool(connectionPool)
                .addInterceptor(new OkHttpLogInterceptor()) // 自定义请求日志拦截器
                .build();
    }
}

6. Summary

If you don't have the patience to read all the process. Just remember one sentence: use your own manual injection of Feign's Client implementation to replace the process of Feign's automatic configuration. See point 5 for specific configuration.

Recent hot article recommendation:

1. 1,000+ Java interview questions and answers (2022 latest version)

2. Brilliant! Java coroutines are coming. . .

3. Spring Boot 2.x tutorial, too comprehensive!

4. Don't fill the screen with explosions and explosions, try the decorator mode, this is the elegant way! !

5. The latest release of "Java Development Manual (Songshan Edition)", download quickly!

Feel good, don't forget to like + forward!

Guess you like

Origin blog.csdn.net/youanyyou/article/details/131202804