Feign原理以及feign调优
1. Feign原理
Feign是一个轻量级的HTTP客户端库,它可以用来进行RESTful API的调用。在Feign中,你只需要定义一个接口,然后就可以使用这个接口来进行HTTP请求。这种方式可以让我们的代码更加简洁和易于维护。
Feign的核心接口是feign.Client,它定义了发送HTTP请求的方法。具体来说,这个接口定义了一个execute方法,它接受一个Request对象作为参数,然后返回一个Response对象。这个execute方法会根据Request对象的内容发送HTTP请求,然后返回服务器的响应。Feign中支持多种HTTP客户端,具体实现都是基于这个Client接口的。
Feign的请求接口定义是通过Java接口的方式实现的。在接口中,我们可以使用@RequestLine、@Headers、@Param等注解来定义HTTP请求。例如:
@RequestLine("GET /users/{id}")
@Headers("Authorization: Basic {auth}")
User getUser(@Param("id") String id, @Param("auth") String auth);
这个接口定义了一个GET请求,请求的URL是/users/{id},其中{id}是一个参数。这个接口还定义了一个Authorization的请求头部信息,值为Basic {auth},其中{auth}是另外一个参数。这个接口会返回一个User对象。
Feign的核心类是feign.ReflectiveFeign,它通过Java反射的方式将接口转换为一个HTTP请求。具体来说,它会解析接口定义中的注解,然后将这些注解转换为一个HTTP请求。这个类还支持将HTTP响应转换为接口定义中定义的返回值。这个转换过程是通过使用feign.codec.Decoder和feign.codec.Encoder这两个接口实现的。
Feign的请求处理过程可以分为三个步骤:
解析接口定义,生成一个HTTP请求。在这个过程中,ReflectiveFeign会根据接口定义中的注解生成一个Request对象,然后将这个Request对象交给Client发送HTTP请求。
发送HTTP请求,获取服务器返回的响应。在这个过程中,Client会根据Request对象中的内容发送HTTP请求,然后返回服务器的响应。这个响应会包含HTTP状态码、HTTP头部信息和响应体等内容。
将服务器返回的响应转换为接口定义中定义的返回值。在这个过程中,ReflectiveFeign会根据接口定义中定义的返回值类型,使用Decoder将HTTP响应体转换为一个Java对象。然后将这个Java对象作为接口方法的返回值。
Feign还支持拦截器,可以在发送HTTP请求前或者处理服务器响应后对请求和响应进行处理。这个功能可以用来实现一些通用的处理逻辑,例如添加统一的请求头部信息、记录日志等。具体来说,Feign中的拦截器是通过实现feign.RequestInterceptor和feign.ResponseInterceptor这两个接口实现
在Feign的请求处理过程中,还有一个重要的组件是feign.Contract。它用于解析Feign接口上的注解并生成对应的请求元数据。默认情况下,Feign使用的是feign.Contract.Default实现,它支持@RequestLine、@Headers、@Param、@QueryMap等注解。
Feign还支持负载均衡和服务发现,可以让我们更加方便地处理多个服务实例之间的负载均衡。Feign提供了一个名为feign.ribbon.RibbonClient的客户端,可以通过它来实现负载均衡。这个客户端是基于Netflix的Ribbon实现的。
最后,Feign的一些核心组件的实现类如下:
feign.okhttp.OkHttpClient:基于OkHttp实现的HTTP客户端。
feign.jaxrs.JAXRSContract:支持JAX-RS注解的Feign Contract实现。
feign.jackson.JacksonDecoder和feign.jackson.JacksonEncoder:使用Jackson库实现的Decoder和Encoder,用于将Java对象和JSON之间进行转换。
feign.ribbon.RibbonClient:基于Netflix的Ribbon实现的客户端,支持负载均衡和服务发现。
2. feign优化
2.1. 开启feign日志
feign:
client:
config:
#feign-provider:
default:
loggerLevel: full #开启feign日志
logging:
level:
com.bjpowernode.feign: debug #log4j的日志级别
在应用程序中启用Feign客户端的日志记录功能后,Feign将会记录以下信息:
请求/响应头信息:请求/响应的HTTP头信息将被记录下来,包括请求方法、请求路径、请求头、响应码等等。
请求/响应体信息:如果请求或响应包含消息体(如JSON或XML),Feign将记录它们。
错误信息:如果请求返回了错误响应,Feign将记录错误信息,包括响应码和错误消息。
以下是一个示例Feign日志记录:
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> POST http://example.com/api/login HTTP/1.1
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> Content-Type: application/json;charset=UTF-8
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> Accept: application/json;charset=UTF-8
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> Content-Length: 37
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> {"username":"johndoe","password":"secret"}
2022-03-16 08:23:45.456 DEBUG [feign.Logger$Default] <-- 200 OK http://example.com/api/login (333ms)
2022-03-16 08:23:45.456 DEBUG [feign.Logger$Default] <-- Content-Type: application/json;charset=UTF-8
2022-03-16 08:23:45.456 DEBUG [feign.Logger$Default] <-- Content-Length: 123
2022-03-16 08:23:45.456 DEBUG [feign.Logger$Default] <-- {"access_token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyMzkwMjJ9.TpZ7SK4CE92GMYRUkgF2fswd4yJUG-wXl3qHZ32aWe8","token_type":"Bearer","expires_in":3600}
在此示例中,Feign记录了一个POST请求,包括请求头、请求体、响应头和响应体信息。请求返回了200 OK响应,并包含一个JSON格式的响应体。
除了上面提到的基本信息,Feign还可以记录其他有用的信息,例如重试次数、请求/响应时间、连接池状态等。以下是Feign日志中可能包含的其他信息:
重试次数:如果启用了Feign的重试功能,并且请求失败后进行了重试,Feign将记录重试次数。
请求/响应时间:Feign将记录请求和响应的时间戳,并计算请求/响应时间。这可以帮助您分析性能问题。
连接池状态:如果您的应用程序使用连接池来管理HTTP连接,Feign将记录连接池中连接的状态,例如空闲连接数、活动连接数等。
以下是一个包含所有这些信息的示例Feign日志记录:
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> POST http://example.com/api/login HTTP/1.1
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> Content-Type: application/json;charset=UTF-8
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> Accept: application/json;charset=UTF-8
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> Content-Length: 37
2022-03-16 08:23:45.123 DEBUG [feign.Logger$Default] --> {"username":"johndoe","password":"secret"}
2022-03-16 08:23:45.456 DEBUG [feign.Logger$Default] [RetryableFeignLoadBalancer] [c7e21e95-2cf7-4b49-890f-9d0b8ec79f1a] [GET] [example.com] is alive
2022-03-16 08:23:45.789 DEBUG [feign.Logger$Default] [RetryableFeignLoadBalancer] [c7e21e95-2cf7-4b49-890f-9d0b8ec79f1a] [GET] [example.com] requesting to GET http://example.com/api/user/123
2022-03-16 08:23:46.012 DEBUG [feign.Logger$Default] [RetryableFeignLoadBalancer] [c7e21e95-2cf7-4b49-890f-9d0b8ec79f1a] [GET] [example.com] [Response received in 223ms] [Retry#1]
2022-03-16 08:23:46.456 DEBUG [feign.Logger$Default] <-- 200 OK http://example.com/api/login (333ms)
2022-03
2.2. 开启GZIP压缩
GZIP是一种数据压缩算法,可以将数据压缩为更小的大小,从而节省网络带宽和存储空间。它是一种无损压缩算法,可以在不丢失数据的情况下压缩数据。以下是GZIP压缩的一些详细说明:
压缩原理:GZIP压缩算法采用LZ77算法和霍夫曼编码进行压缩。LZ77算法使用重复的数据块来减小数据的大小,而霍夫曼编码将最频繁出现的数据编码成较短的码字,从而减小数据的大小。
压缩效率:GZIP压缩算法可以将数据压缩为原始数据的40%~60%左右。这意味着在传输数据时,使用GZIP可以显著减少传输的数据量,从而减少网络带宽的使用。
压缩格式:GZIP压缩算法生成的压缩数据是一种二进制格式,通常包含文件头和文件尾。文件头包含一些元数据,例如压缩方法、时间戳、操作系统类型等,而文件尾包含一些校验和信息,用于检测数据是否完整。
解压缩:解压缩GZIP数据很容易。大多数操作系统都提供了GZIP的解压缩工具,例如Linux中的gunzip命令和Windows中的WinZip工具。您也可以在Java中使用java.util.zip包提供的GZIPInputStream和GZIPOutputStream类进行压缩和解压缩。
应用场景:GZIP压缩算法通常用于Web应用程序中,以减少HTTP请求和响应的数据量。例如,Web服务器可以将静态资源(例如HTML、CSS、JavaScript文件)进行GZIP压缩,并在HTTP响应中设置Content-Encoding头以指示数据已经被压缩。Web浏览器将自动解压缩数据并显示页面内容。这可以显著减少页面加载时间和网络带宽的使用。
server:
compression:
enabled: true #开启浏览器<----->consumer的gzip压缩
feign:
ompression: #开启feign<----->provider的gzip压缩
request:
enabled: true
response:
enabled: true
2.3. 开启Http 连接池
HTTP连接池是一种管理HTTP连接的技术,它可以在多个HTTP请求之间共享和重用连接,以提高HTTP请求的性能和效率。以下是HTTP连接池的一些详细说明:
连接池原理:HTTP连接池将多个HTTP连接放入连接池中,并在需要时从池中获取连接来处理HTTP请求。当请求完成后,连接将被放回连接池中以供重用。通过重用连接,连接池可以避免创建和关闭连接的开销,从而提高HTTP请求的性能和效率。
连接池大小:连接池的大小是连接池性能和效率的重要因素之一。连接池的大小应该根据系统负载和网络带宽进行调整。如果连接池太小,HTTP请求可能会等待可用连接,从而影响性能。如果连接池太大,会占用过多的内存和CPU资源,从而影响效率。
连接超时:连接池应该具有连接超时功能。当连接在指定的时间内无法建立时,连接池应该抛出异常并释放连接资源。这可以避免因连接问题而导致HTTP请求超时的问题。
连接空闲超时:连接池应该具有连接空闲超时功能。当连接在指定的时间内未被使用时,连接池应该关闭连接并释放连接资源。这可以避免连接资源浪费和系统负载的问题。
连接重用:连接池应该尽可能地重用连接,以减少创建和关闭连接的开销。连接池应该确保连接的状态是正确的,例如连接是否被关闭、是否存在未读取的数据等。
线程安全:连接池应该是线程安全的。多个线程可以同时从连接池中获取连接,并使用连接来处理HTTP请求。连接池应该确保在多线程环境中正确地管理连接资源。
实现方式:连接池可以使用Java中的Apache HttpClient或Java HttpClient等第三方库来实现。这些库提供了可配置的连接池,可以根据应用程序的需要进行调整。
HTTP连接池可以在大多数Web应用程序中提高性能和效率。它可以避免频繁创建和关闭连接的开销,从而提高HTTP请求的性能。
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency
2.4. 设置feign超时
在使用Feign进行HTTP请求时,由于网络延迟或服务器处理时间过长等原因,可能会导致请求超时。Feign提供了一些配置选项,可以用于设置请求超时时间,以便在请求超时时进行适当的处理。
方式一:
ribbon:
ConectionTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求响应的超时时间
方式二:
feign:
client:
config:
#feign-provider:
default:
onectionTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求响应的超时时间