记一次openfeign接口变慢与优化
影响feign调用的因素:
jvm频繁GC停顿
服务器物理内存
网络通信抖动
feign编解码影响性能
jvm虚拟机大小没设置对
问题:
一段时间是好的,偶尔会超时或者执行时间达到12s。
原因分析:
查看服务器物理内存为8G,
jstat -gc pid 1s 每秒打印一下发现有频繁GC情况,2天有48此FGC,更别说YGC了。
看看堆的使用情况 jmap -heap pid, 发现总共800M的堆大小,这太不合理了。
JVM默认初始化堆大小是物理内存的64/1,最大是4/1,也就是说我们的jvm已启动 堆大小是127,最大可以扩容到2G,每次满了就会触发FGC以及扩容
解决办法是启动时设置jvm初始化大小
-Xms4096m,-Xmx4096m,-XX:MetaspaceSize=256m,-XX:MaxMetaspaceSize=256m
固定了堆初始就4G,最大也4G不扩容,元空间规定256M
系统swap压缩内存
问题:
测试环境上有时候老是第一次调用feign就很慢,后面就好了。
原因分析:
1 常规检查:系统8G内存还有800M的空间,还有10%。
2 swap策略不是所有的系统都会开启的,可以top下看看Swap的total是不是0,如果有值就证明开启了这个策略。当遇到服务变慢的情况就可以优先看看是不是这里导致的了
3 输出swap的进程最终发现了问题。使用脚本发现有5G的java内存被压缩,这些都是不频繁使用的应用。
centos7在内存达到一定上限时为了保证运行更多的进程会做一个swap操作,就是将不常用的内存缓存到硬盘这样会释放出一定的内存用于运行更多的进程。当某个被swap的内存被用到时,系统会将其从硬盘中在回写到内存,这样一来可以保证系统的正常使用,而负作用就是慢。在常规检查中虽然看起来内存还有800M但其实已经透支了。
一下时网上大佬写的用于检查swap的脚本
#!/bin/bash
# Get current swap usage for all running processes
# Erik Ljungstrom 27/05/2011
# Modified by Mikko Rantalainen 2012-08-09
# Pipe the output to "sort -nk3" to get sorted output
# Modified by Marc Methot 2014-09-18
# removed the need for sudo
SUM=0
OVERALL=0
for DIR in `find /proc/ -maxdepth 1 -type d -regex "^/proc/[0-9]+"`
do
PID=`echo $DIR | cut -d / -f 3`
PROGNAME=`ps -p $PID -o comm --no-headers`
for SWAP in `grep VmSwap $DIR/status 2>/dev/null | awk '{ print $2 }'`
do
let SUM=$SUM+$SWAP
done
if (( $SUM > 0 )); then
echo "PID=$PID swapped $SUM KB ($PROGNAME)"
fi
let OVERALL=$OVERALL+$SUM
SUM=0
done
echo "Overall swap used: $OVERALL KB"解决办法:
扩大服务器物理内存
优化测试环境jvm的堆大小,是他不会被swap压缩。
连接池不够了
feign默认走的JDK的http客户端,每次请求都新建链接,需要改成httpClient、或者okHttp3基于连接池的http客户端。
feign的编解码影响性能
问题:
在可以调整的调整好后,还是发现有400m+延迟,在内网环境下不应该
原因分析:
查看了feign的配置类,发现有编解码器,但是在本次项目上不怎么用到。如果没有什么其他的业务需要,不要配置feign的编解码器,非常的影响性能。

解决办法:
删除配置类后,调用的feign的时候基本也是1-2ms,微服务耗时也是1-2ms,整体接口也缩小至100ms内返回了。
feign的优化
开启feign日志
日志等级:
NONE:不输出日志(用于正式生产环境)
BASIC:输出请求方法及url,响应的状态码及响应时间(用户生产环境问题追踪调试)
HEADERS:输出请求和响应的头信息
FULL:输出请求和响应的请求头,消息体及元数据(最详细用于开发测试调试)
feign:
client:
config:
default:
loggerLevel: full #feign显示日志
logging:
level:
com.example.yourpackage.feignclient: DEBUG #log4j的日志级别
#你可以将com.example.yourpackage.feignclient替换为你自己的Feign客户端所在的包路径。GZIP压缩
server:
port: 9006
compression:
enabled: true #开启浏览器<----->consumer的gzip压缩
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.65.132:8848 #注册中心的地址
application:
name: feign-consumer #注册的名字
feign:
compression:
request:
enabled: true #开启feign<---->provider的gzip压缩
mime-types: text/xml,application/xml, application/json # 压缩类型
min-request-size: 1024 # 最小压缩值标准,当数据大于 1024 才会进行压缩
response:
enabled: true
http请求使用连接池
TCP有三次握手和四次挥手,每次访问结束后都会关闭连接,而开启连接池后每次从连接池中建立连接就可以,大大提高了效率,而优化的方式也很简单,只需要导入以下依赖即可
可以使用httpclient 也可以使用okhttp,自动配置类HttpClientFeignConfiguration
<!-- openfeign 所使用组件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
#bootstrap.yml
feign:
httpclient: # 开启 HttpClient
enabled: trueokhttp的自动配置类OkHttpFeignConfiguration
<!-- openfeign 所使用组件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
#bootstrap.yml
feign:
okhttp:
enabled: true使用apache httpclient5,自动配置类HttpClient5FeignConfiguration
<!-- openfeign 所使用组件 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- feign-httpclient5-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hc5</artifactId>
</dependency>
#bootstrap.yml
fegin:
#启用feign-hc5, 是apache httpclient4对应的5版本
httpclient:
hc5:
enabled: truefeign超时设置
feign:
client:
config:
default:
connectTimeout: 5000 #请求连接的超时时间
readTimeout: 5000 #请求处理的超时时间openFeign的负载均衡
在 2020 年以前的 SpringCloud 采用 Ribbon 作为负载均衡,但是 2020 年之后,SpringCloud 吧 Ribbon 移除了,而是使用自己编写的 LoadBalancer 替代.
LoadBalancer默认提供了两种负载均衡策略:
RandomLoadBalancer - 随机分配策略
(默认) RoundRobinLoadBalancer - 轮询分配策略
接下来自定义负载均衡策略:
//这里不用加 @Configuration 注解
public class LoadBalancerConfig {
//将官方提供的 RandomLoadBalancer 注册为Bean
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory){
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}通过 @LoadBalancerClient(value = "服务名", configuration = LoadBalancerConfig.class) 指定负载均衡策略为随机.
@FeignClient("article")
@LoadBalancerClient(value = "article", configuration = LoadBalancerConfig.class) //指定负载均衡策略为随机
public interface ArticleClient {
//@LoadBalanced(可以写,也可以不用写,默认所有方法都自动加 @LoadBalanced)
@GetMapping("/article/start")
String userStart();
}@SpringBootApplication
@EnableFeignClients
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
logger.info("Tutorial 成功启动");
}
}对@RefreshScope支持
如果启用了 Feign 客户端刷新,则使用以下命令创建每个 Feign 客户端:
feign.Request.Options作为刷新范围的 Bean。这意味着像connectTimeout and readTimeout 等属性可以针对任何 Feign 客户端实例刷新。
在 下换行的网址org.springframework.cloud.openfeign.RefreshableUrl。这意味着 Feign 客户端的 URL(如果已定义)spring.cloud.openfeign.client.config.{feignName}.url 属性,可以针对任何 Feign 客户端实例刷新。
可以通过 刷新这些属性。POST /actuator/refresh
默认情况下,Feign 客户端中的刷新行为处于禁用状态。使用以下属性启用刷新行为:
spring.cloud.openfeign.client.refresh-enabled=true