SpringCloud实用-OpenFeign整合okHttp

文章目录

  • 前言
  • 正文
    • [一、OkHttpFeignConfiguration 的启用](#一、OkHttpFeignConfiguration 的启用)
      • [1.1 分析配置类](#1.1 分析配置类)
      • [1.2 得出结论,需要增加配置](#1.2 得出结论,需要增加配置)
      • [1.3 调试](#1.3 调试)
    • [二、OkHttpFeignLoadBalancerConfiguration 的启用](#二、OkHttpFeignLoadBalancerConfiguration 的启用)
      • [2.1 分析配置类](#2.1 分析配置类)
      • [2.2 得出结论](#2.2 得出结论)
      • [2.3 测试](#2.3 测试)
  • 附录
    • 附1:本系列文章链接
    • [附2:OkHttpClient 增加拦截器配置](#附2:OkHttpClient 增加拦截器配置)
      • [附2.1 新建配置类OkHttpConfig](#附2.1 新建配置类OkHttpConfig)
      • [附2.2 调试结果](#附2.2 调试结果)

前言

众所周知,我们在使用SpringCloud OpenFeign时,默认使用的是老旧的连接器HttpURLConnection。性能以及并发量方面都差强人意。

一般而言都会对其进行优化调整。本文采用OpenFeign整合okHttp的方式替换原有的Client,去做请求。

使用java 17,spring cloud 4.0.4,springboot 3.1.4

使用项目是本系列第一篇中的项目

本文介绍两种方式的配置,一个是LoadBalancer 的,都是默认带有连接池的。

  • OkHttpFeignConfiguration
  • OkHttpFeignLoadBalancerConfiguration

正文

一、OkHttpFeignConfiguration 的启用

1.1 分析配置类

首先我们需要加入什么依赖配置呢?

依据 OkHttpFeignConfiguration 的配置内容,进行分析:

java 复制代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty("spring.cloud.openfeign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {
		// 类路径下有连接池时,构建连接池
		@Bean
		@ConditionalOnMissingBean(ConnectionPool.class)
		public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties) {
			int maxTotalConnections = httpClientProperties.getMaxConnections();
			long timeToLive = httpClientProperties.getTimeToLive();
			TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
			return new ConnectionPool(maxTotalConnections, timeToLive, ttlUnit);
		}

		// 构建OkHttpClient实例
		@Bean
		public okhttp3.OkHttpClient okHttpClient(okhttp3.OkHttpClient.Builder builder, ConnectionPool connectionPool,
				FeignHttpClientProperties httpClientProperties) {
			boolean followRedirects = httpClientProperties.isFollowRedirects();
			int connectTimeout = httpClientProperties.getConnectionTimeout();
			boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
			Duration readTimeout = httpClientProperties.getOkHttp().getReadTimeout();
			List<Protocol> protocols = httpClientProperties.getOkHttp().getProtocols().stream().map(Protocol::valueOf)
					.collect(Collectors.toList());
			if (disableSslValidation) {
				disableSsl(builder);
			}
			this.okHttpClient = builder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
					.followRedirects(followRedirects).readTimeout(readTimeout).connectionPool(connectionPool)
					.protocols(protocols).build();
			return this.okHttpClient;
		}
}

1.2 得出结论,需要增加配置

从配置类中观察到,它的启用条件有2个,一个是需要引入 okhttp的包,另一个需要一行启用配置:

xml 复制代码
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-okhttp -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>13.1</version>
</dependency>
properties 复制代码
# 启用okhttp配置
spring.cloud.openfeign.okhttp.enabled=true

1.3 调试

正常请求,在SynchronousMethodHandler.executeAndDecode(...) 中打断点,调试观察client的类型:

发现已经不再是之前的Default 了,换成了OkHttpClient 类型。并且,也内置了连接池以及一些基本配置信息。

二、OkHttpFeignLoadBalancerConfiguration 的启用

2.1 分析配置类

依据 OkHttpFeignLoadBalancerConfiguration 中的配置项,分析:

java 复制代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty("spring.cloud.openfeign.okhttp.enabled")
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
class OkHttpFeignLoadBalancerConfiguration {

	@Bean
	@ConditionalOnMissingBean
	@Conditional(OnRetryNotEnabledCondition.class)
	public Client feignClient(okhttp3.OkHttpClient okHttpClient, LoadBalancerClient loadBalancerClient,
			LoadBalancerClientFactory loadBalancerClientFactory,
			List<LoadBalancerFeignRequestTransformer> transformers) {
		OkHttpClient delegate = new OkHttpClient(okHttpClient);
		return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory,
				transformers);
	}

	// 省略其他配置
}

2.2 得出结论

想要让这个配置生效,需要满足OkHttpClientLoadBalancerClientOnRetryNotEnabledCondition

也就是2个依赖项,2个配置:

xml 复制代码
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-okhttp -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>13.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-loadbalancer -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    <version>4.0.4</version>
</dependency>

而配置方面需要增加:

properties 复制代码
# 启用okhttp配置
spring.cloud.openfeign.okhttp.enabled=true

# 关闭负载重试
spring.cloud.loadbalancer.retry.enabled=false

2.3 测试

正常请求,在SynchronousMethodHandler.executeAndDecode(...) 中打断点,调试观察client的类型:

发现已经不再是之前的Default 了,换成了OkHttpClient 类型。

附录

附1:本系列文章链接

SpringCloud系列文章目录(总纲篇)

附2:OkHttpClient 增加拦截器配置

附2.1 新建配置类OkHttpConfig

增加以下拦截器配置,对请求和响应进行日志打印。

java 复制代码
package org.feng.config;

import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

/**
 * okhttp配置
 *
 * @author feng
 */
@Slf4j
@Configuration
public class OkHttpConfig {

    @Bean
    public okhttp3.OkHttpClient.Builder okHttpClientBuilder() {
        return new okhttp3.OkHttpClient.Builder()
                .addInterceptor(new LoggingInterceptor());
    }

    /**
     * okhttp3 请求日志拦截器
     */
    static class LoggingInterceptor implements Interceptor {
        
        @NotNull
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            long start = System.nanoTime();
            log.info(String.format("Sending request %s on %s%n%s",
                    request.url(), chain.connection(), request.headers()));
            Response response = chain.proceed(request);
            long end = System.nanoTime();
            log.info(String.format("Received response for %s in %.1fms%n%s",
                    response.request().url(), (end - start) / 1e6d, response.headers()));
            return response;
        }
    }
}

附2.2 调试结果

可以观察到日志中,打印出来了请求路径、请求头等信息。

2023-11-24T16:48:56.358+08:00  INFO 35987 --- [nio-8080-exec-1] org.feng.config.OkHttpConfig             : Sending request http://localhost:10080/hello/post on null
Content-Length: 100
Accept: */*


2023-11-24T16:48:56.713+08:00  INFO 35987 --- [nio-8080-exec-1] org.feng.config.OkHttpConfig             : Received response for http://localhost:10080/hello/post in 347.4ms
Content-Type: application/json
Transfer-Encoding: chunked
Date: Fri, 24 Nov 2023 08:48:56 GMT
Keep-Alive: timeout=60
Connection: keep-alive
相关推荐
wang_book3 小时前
Gitlab学习(007 gitlab项目操作)
java·运维·git·学习·spring·gitlab
一个诺诺前行的后端程序员5 小时前
springcloud微服务实战<1>
spring·spring cloud·微服务
Tatakai258 小时前
Mybatis Plus分页查询返回total为0问题
java·spring·bug·mybatis
掐指一算乀缺钱10 小时前
SpringBoot 数据库表结构文档生成
java·数据库·spring boot·后端·spring
一叶飘零_sweeeet10 小时前
为什么 Feign 要用 HTTP 而不是 RPC?
java·网络协议·http·spring cloud·rpc·feign
bug菌¹10 小时前
滚雪球学SpringCloud[4.1讲]: Spring Cloud Gateway详解
java·spring cloud·微服务
bug菌¹11 小时前
滚雪球学SpringCloud[4.2讲]: Zuul:Netflix API Gateway详解
spring·spring cloud·gateway
小哇66611 小时前
spring-TransactionTemplate 编程式事务
数据库·spring
小筱在线12 小时前
SpringCloud微服务实现服务熔断的实践指南
java·spring cloud·微服务
ChinaRainbowSea12 小时前
十三,Spring Boot 中注入 Servlet,Filter,Listener
java·spring boot·spring·servlet·web