前言
Feign是一个流行的Java HTTP客户端,用于简化基于HTTP的API调用。在本文中,我们将介绍如何使用Feign与OkHttp或HttpClient集成来优化应用程序的性能。我们将研究各种因素,例如连接池,超时和性能指标,并提供具体的代码示例,以便您可以在实践中实现这些最佳实践。
Feign
Feign
是一种基于Java的声明式HTTP客户端,可以使编写HTTP客户端变得更加容易和简单。使用Feign
,可以通过创建一个接口,然后通过该接口来发送HTTP请求,而无需手动处理HTTP连接、请求和响应。Feign
将根据接口的定义,自动生成HTTP请求和响应的实现,并将其绑定到该接口上,使得发送HTTP请求变得非常容易和直观。
Feign
使用了Java的反射机制和动态代理技术,来根据接口定义动态地生成一个HTTP客户端实现。可以在接口定义中添加@RequestMapping
等注解来定义HTTP请求的细节,例如请求方法、请求URL、请求头、请求体等。然后,Feign
将使用这些注解来自动生成一个HTTP请求,并将其发送到服务器。Feign
还提供了丰富的配置选项,可以自定义HTTP客户端的行为,例如设置连接超时、读取超时、重试机制等。
Feign
的优点包括:
- 简单易用:通过声明式接口,使得编写HTTP客户端变得非常简单和直观。
- 易于集成:
Feign
与Spring Cloud等现代化开发框架无缝集成,可以方便地在微服务架构中使用。 - 可配置性:
Feign
提供了丰富的配置选项,可以自定义HTTP客户端的行为。 - 可扩展性:
Feign
是一个可扩展的框架,可以通过自定义组件和拦截器来增强其功能和灵活性。
Feign已有默认客户端,为何还要使用第三方连接池?
Feign有个Client接口,定义一个针对url执行请求并返回响应的方法
其默认实现客户端为URLConnection,是Java标准库中自带的HTTP客户端,但它的功能相对较弱,缺少一些高级功能,例如连接池、请求复用、拦截器等。并且通过下面可以看到每次都会调用openConnection()方法开启一个socket连接。
如果并发量一大,对系统整体的性能有一定的影响,因此可以考虑使用Apache HttpClient或OkHttp。
需要注意的是,如果同时使用了feign-okhttp
和feign-httpclient
依赖,Feign
会默认使用OkHttpClient
。
URLConnection
URLConnection是Java标准库中提供的一个用于发送HTTP请求和接收响应的类。它是一个基于Java IO流的HTTP客户端,提供了一种简单的方式来与Web服务器进行通信。
URLConnection可以用于发送HTTP GET、POST、PUT、DELETE等各种类型的请求,支持HTTP、HTTPS协议,并且支持基本身份验证和Cookie管理。
OKHttp
OkHttp是一个流行的Java HTTP客户端库,由Square公司开发和维护。它被设计为更加现代和高效的替代品,可以用于与Web服务器进行通信,发送HTTP请求和接收响应。OkHttp具有以下特点:
特性:
- 支持HTTP/2协议:OkHttp可以使用HTTP/2协议进行通信,这意味着它可以更有效地利用网络带宽,减少网络延迟,并允许并发请求。
- 简单易用:OkHttp的API设计简单易用,可以轻松地发送HTTP请求并处理响应。
- 高效性能:OkHttp使用连接池和请求复用机制来提高性能,减少网络延迟和资源占用。
- 支持拦截器:OkHttp支持自定义拦截器,可以方便地实现各种HTTP请求和响应的自定义处理逻辑。
- 支持Gzip压缩:OkHttp支持Gzip压缩,可以减少网络传输数据的大小,提高网络传输效率。
- 支持HTTPS:OkHttp支持HTTPS协议,并且可以验证SSL证书,保证通信的安全性。
Apache HttpClient
Apache HttpClient
是一个流行的Java HTTP客户端库,由Apache Software Foundation开发和维护。它提供了一组强大的API,可以轻松地发送HTTP请求和处理响应。Apache HttpClient
具有以下特点:
- 支持多种协议:可以用于发送HTTP、HTTPS、FTP、SMTP等多种协议的请求。
- 高效性能:采用连接池和请求复用机制来提高性能,减少网络延迟和资源占用。
- 支持拦截器:支持自定义拦截器,可以方便地实现各种HTTP请求和响应的自定义处理逻辑。
- 支持连接超时和读取超时设置:可以设置连接超时和读取超时时间,可以有效地避免网络异常和连接阻塞问题。
- 支持重定向:可以自动处理HTTP重定向,可以减少开发人员的工作量。
- 支持代理:支持使用代理服务器发送HTTP请求。
虽然Apache HttpClient
提供了很多高级功能,但是在Java 11及以上版本中,HttpClient
已经被添加到Java标准库中,因此也可以直接使用Java标准库中的HttpClient
来发送HTTP请求。
Feign使用OkHttp
全局使用OkHttp
-
默认情况下,feign不包括okhttp客户端,需要使用下述依赖引入
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>
-
引入后,在配置文件中加入配置打开okhttp
feign.okhttp.enabled=true
-
可以借用feign调整okhttp的部分参数
# 最大连接数 feign.httpclient.maxConnections=200 # 每个路由的最大连接数 feign.httpclient.maxConnectionsPerRoute=50 # 连接存活时间 feign.httpclient.timeToLive=900 # 连接超时时间 feign.httpclient.connectionTimeout=2000
指定Feign接口使用OkHttp
-
将OkHttp客户端添加到项目中
<dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.14.9</version> </dependency>
-
创建一个实现了Feign的
Client
接口的OkHttp客户端import feign.Client; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class OkHttpFeignClient implements Client { private final OkHttpClient client; public OkHttpFeignClient() { this.client = new OkHttpClient(); } @Override public Response execute(Request request, Request.Options options) throws IOException { okhttp3.Request.Builder builder = new okhttp3.Request.Builder() .url(request.url()) .method(request.method(), request.body()); request.headers().forEach((name, values) -> { values.forEach(value -> { builder.addHeader(name, value); }); }); okhttp3.Request okhttpRequest = builder.build(); okhttp3.Response okhttpResponse = client.newCall(okhttpRequest).execute(); return Response.builder() .status(okhttpResponse.code()) .reason(okhttpResponse.message()) .headers(okhttpResponse.headers().toMultimap()) .body(okhttpResponse.body().byteStream(), okhttpResponse.body().contentLength()) .request(request) .build(); } }
-
在Feign客户端接口中,使用
@FeignClient
注解将Client
属性设置为OkHttpFeignClient
import feign.Headers; import feign.Param; import feign.RequestLine; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient(name = "example", url = "http://example.com", configuration = MyFeignConfiguration.class, client = OkHttpFeignClient.class) public interface MyFeignClient { @GetMapping("/api/users/{userId}") @Headers("Authorization: {token}") User getUser(@Param("userId") long userId, @Param("token") String token); }
Feign使用HttpClient
全局使用HttpClient
-
添加
HttpClient
依赖<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency>
-
修改配置文件
feign.httpclient.enabled=true
指定接口使用HttpClient
-
添加
HttpClient
依赖<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency>
-
创建配置类
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.apache.http.impl.client.HttpClientBuilder; import feign.httpclient.ApacheHttpClient; @Configuration public class ExampleClientConfiguration { @Bean public feign.Client feignClient() { return new ApacheHttpClient(HttpClientBuilder.create().build()); } }
-
在Feign客户端接口中,使用
@FeignClient
注解将Client
属性设置为ExampleClientConfiguration
@FeignClient(name = "example", url = "http://example.com", configuration = ExampleClientConfiguration.class) public interface ExampleClient { @RequestMapping(method = RequestMethod.GET, value = "/api/data") String getData(); }