spring cloud性能调优

简介: spring cloud性能调优

本文针对公司微服务并发的实际场景以及网上调研的资料,记录影响微服务并发的各种优化配置。


先说明线上调用的实际例子:

通过zuul网关 调用服务A的接口,服务A的接口里面通过Feign调用服务B的接口。


问题:

通过JMeter并发测试发现,并发数竟然没有达到30次/s,即QPS不到30。这显然不合理。


备注:

TPS(吞吐量) 系统在单位时间内处理请求的数量。

QPS(每秒查询率) 每秒的响应请求数


第一步:熔断器并发调优

首先想到的是Feign调用并发过大,导致的熔断问题,优化服务A中的熔断配置

hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests 如果并发数达到该设置值,请求会被拒绝和抛出异常并且fallback不会被调用。默认10

果然,hystrix在semaphore隔离方案下,最大的并发默认是10。

优化配置:


#线程策略
hystrix.command.default.execution.isolation.strategy=SEMAPHORE
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=50


第二步:Zuul并发调优

经历了将熔断器执行线程并发设置为500后,继续用JMeter进行并发测试,结果QPS到达100后,又出现大量请求失败。

查看日志,发现zuul很多请求连接关闭。


优化配置:


#zuul网关配置
zuul.semaphore.max-semaphores=500


再次使用JMeter测试,发现并发500没有在出现问题。


以上2个就是spring cloud并发调优最核心的2个参数。


下面系统说下spring cloud工程调优的问题

主要从以下几个方面入手:

1、hystrix熔断器并发调优

2、zuul网关的并发参数控制

3、Feign客户端和连接数参数调优

4、Tomcat并发连接数调优

5、timeout超时参数调优

6、JVM参数调优

7、ribbon和hystrix的请求超时,重试以及幂等性配置


下面说明下具体调优参数:


hystrix熔断器调优


表示HystrixCommand.run()的执行时的隔离策略,有以下两种策略


1 THREAD: 在单独的线程上执行,并发请求受线程池中的线程数限制

2 SEMAPHORE: 在调用线程上执行,并发请求量受信号量计数限制

在默认情况下,推荐HystrixCommands 使用 thread 隔离策略,HystrixObservableCommand 使用 semaphore 隔离策略。

只有在高并发(单个实例每秒达到几百个调用)的调用时,才需要修改HystrixCommands 的隔离策略为semaphore 。semaphore 隔离策略通常只用于非网络调用。

说明:高并发时,优先使用semaphore 。

hystrix.threadpool.default.coreSize=10
hystrix.threadpool.default.maximumSize=10
hystrix.threadpool.default.maxQueueSize=-1


#如该值为-1,那么使用的是SynchronousQueue,否则使用的是LinkedBlockingQueue。注意,修改MQ的类型需要重启。例如从-1修改为100,需要重启,因为使用的Queue类型发生了变化

如果想对特定的 HystrixThreadPoolKey 进行配置,则将 default 改为 HystrixThreadPoolKey 即可。


如果隔离策略是SEMAPHORE:


hystrix.command.default.execution.isolation.strategy=SEMAPHORE
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=10# 默认值


如果想对指定的 HystrixCommandKey 进行配置,则将 default 改为 HystrixCommandKey 即可。


zuul参数控制


我们知道Hystrix有隔离策略:THREAD 以及SEMAPHORE ,默认是 SEMAPHORE 。

查询资料发现是因为zuul默认每个路由直接用信号量做隔离,并且默认值是100,也就是当一个路由请求的信号量高于100那么就拒绝服务了,返回500。


线程池提供了比信号量更好的隔离机制,并且从实际测试发现高吞吐场景下可以完成更多的请求。但是信号量隔离的开销更小,对于本身就是10ms以内的系统,显然信号量更合适。


当 zuul.ribbonIsolationStrategy=THREAD时,Hystrix的线程隔离策略将会作用于所有路由。

此时,HystrixThreadPoolKey 默认为“RibbonCommand”。这意味着,所有路由的HystrixCommand都会在相同的Hystrix线程池中执行。可使用以下配置,让每个路由使用独立的线程池:

zuul:
  threadPool:
    useSeparateThreadPools: true


只有在隔离策略是thread才有效


1. 隔离策略

zuul.ribbon-isolation-strategy=thread


2. 最大信号

当Zuul的隔离策略为SEMAPHORE时:

全局设置默认最大信号量:

zuul.ribbon-isolation-strategy=Semaphore
zuul:
  semaphore:
    max-semaphores: 100 # 默认值


对路由linkflow和oauth单独设置最大信号量

routes:
    linkflow:
      path: /api1/**
      serviceId: lf
      stripPrefix: false
      semaphore:
        maxSemaphores: 2000
    oauth:
      path: /api2/**
      serviceId: lf
      stripPrefix: false
      semaphore:
        maxSemaphores: 1000


3.zuul并发连接参数

针对url的路由配置

zuul:
  host:
    max-total-connections: 200 # 默认值
    max-per-route-connections: 20 # 默认值


针对serviceId的路由配置

serviceId:
  ribbon:
    MaxTotalConnections: 0   # 默认值
    MaxConnectionsPerHost: 0 # 默认值


Feign参数调优


在默认情况下 spring cloud feign在进行各个子服务之间的调用时,http组件使用的是jdk的HttpURLConnection,没有使用线程池。本文先从源码分析feign的http组件对象生成的过程,然后通过为feign配置http线程池优化调用效率。

有种可选的线程池:HttpClient和OKHttp

个人比较推荐OKHttp,请求封装的非常简单易用,性能也很ok。


当使用HttpClient时,可如下设置:

feign.httpclient.enabled=true
feign.httpclient.max-connections=200# 默认值
feign.httpclient.max-connections-per-route=50# 默认值


代码详见:

org.springframework.cloud.netflix.feign.FeignAutoConfiguration.
HttpClientFeignConfiguration#connectionManager
org.springframework.cloud.netflix.feign.ribbon.HttpClientFeignLoadBalancedConfiguration.
HttpClientFeignConfiguration#connectionManager


当使用OKHttp时,可如下设置:

feign.okhttp.enabled=true
feign.okhttp.max-connections=200# 默认值
feign.okhttp.max-connections-per-route=50# 默认值


代码详见:

org.springframework.cloud.netflix.feign.FeignAutoConfiguration.
OkHttpFeignConfiguration#httpClientConnectionPool 。
org.springframework.cloud.netflix.feign.ribbon.OkHttpFeignLoadBalancedConfiguration.
OkHttpFeignConfiguration#httpClientConnectionPool


tomcat调优

如果使用的是内嵌的tomcat保持默认就好

server.tomcat.max-connections=0 # Maximum number of connections that the server accepts and processes at any given time.
server.tomcat.max-http-header-size=0 # Maximum size, in bytes, of the HTTP message header.
server.tomcat.max-http-post-size=0 # Maximum size, in bytes, of the HTTP post content.
server.tomcat.max-threads=0 # Maximum number of worker threads.
server.tomcat.min-spare-threads=0 # Minimum number of worker threads.

由于默认的最大连接数,最大线程数都是0,没有限制,所以在spring boot中启动内嵌的tomcat,一般保持默认的配置就可以了。


JVM参数调优


关于Jvm调优Oracle官网有一份指导说明:

Oracle官网对Jvm调优的说明

有兴趣大家可以去看看。


执行启动设置Jvm参数的操作。


java   -Xms1024m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m  -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC    -jar   user-1.0.0.jar

关于这些设置的JVM参数是什么意思,请参考第二步中的oracle官方给出的调优文档。


我在这边简单说一下:

-XX:MetaspaceSize=128m (元空间默认大小)

-XX:MaxMetaspaceSize=128m (元空间最大大小)

-Xms1024m (堆最大大小)

-Xmx1024m (堆默认大小)

-Xmn256m (新生代大小)

-Xss256k (棧最大深度大小)

-XX:SurvivorRatio=8 (新生代分区比例 8:2)

-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器)

-XX:+PrintGCDetails (打印详细的GC日志)


请求的超时,重试,以及幂等性

#配置首台服务器重试1次
ribbon.MaxAutoRetries=1
##配置其他服务器重试1次
ribbon.MaxAutoRetriesNextServer=1
##获取连接的超时时间
ribbon.ConnectTimeout=1000
###请求处理时间
ribbon.ReadTimeout=1000
##每个操作都开启重试机制
ribbon.OkToRetryOnAllOperations=true
#开启Feign请求压缩
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
feign.compression.response.enabled=true
#配置断路器超时时间,默认是1000(1秒)
feign.hystrix.enabled=true
#feign use okhttp
feign.httpclient.enabled=false
feign.okhttp.enabled=true
#是否开启超时熔断, 如果为false, 则熔断机制只在服务不可用时开启
hystrix.command.default.execution.timeout.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000

请求在1s内响应,超过1秒先同一个服务器上重试1次,如果还是超时或失败,向其他服务上请求重试1次。

那么整个ribbon请求过程的超时时间为:

ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1);
ribbonTimeout = (1000 + 1000) * (1 + 1) * (1 + 1) = 8000

由于Hystrix timeout一定要大于ribbonTimeout 超时,所以

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds>8000

超时设置是为了防止某些耗时操作积压在线程池中,导致后续请求无法进行,压爆服务器。

重试是为了防止网络抖动等原因出现偶然性异常的自动补偿机制,不过这时一定要保证所有接口的幂等性。

Feign请求压缩是为了减少网络IO传递的耗时


你的系统架构中,只要涉及到了重试,那么必须上接口的幂等性保障机制。

否则的话,试想一下,你要是对一个接口重试了好几次,结果人家重复插入了多条数据,该怎么办呢?

其实幂等性保证本身并不复杂,根据业务来,常见的方案:

可以在数据库里建一个唯一索引,插入数据的时候如果唯一索引冲突了就不会插入重复数据

或者是通过redis里放一个唯一id值,然后每次要插入数据,都通过redis判断一下,那个值如果已经存在了,那么就不要插入重复数据了。

类似这样的方案还有一些。总之,要保证一个接口被多次调用的时候,不能插入重复的数据。

目录
相关文章
|
3月前
|
消息中间件 监控 Java
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
55 6
|
3月前
|
Java 关系型数据库 MySQL
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
91 5
|
3月前
|
缓存 监控 Java
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
70 5
|
6月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
563 37
|
7月前
|
Prometheus 监控 Cloud Native
Spring Boot 性能护航!Prometheus、Grafana、ELK 组合拳,点燃数字化时代应用稳定之火
【8月更文挑战第29天】在现代软件开发中,保证应用性能与稳定至关重要。Spring Boot 作为流行的 Java 框架,结合 Prometheus、Grafana 和 ELK 可显著提升监控与分析能力。Prometheus 负责收集时间序列数据,Grafana 将数据可视化,而 ELK (Elasticsearch、Logstash、Kibana)则管理并分析应用日志。通过具体实例演示了如何在 Spring Boot 应用中集成这些工具:配置 Prometheus 获取度量信息、Grafana 显示结果及 ELK 分析日志,从而帮助开发者快速定位问题,确保应用稳定高效运行。
223 1
|
8月前
|
Java Spring
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
187 3
|
8月前
|
负载均衡 Java Spring
Spring cloud gateway 如何在路由时进行负载均衡
Spring cloud gateway 如何在路由时进行负载均衡
827 15
|
8月前
|
消息中间件 Java Nacos
通用快照方案问题之通过Spring Cloud实现配置的自动更新如何解决
通用快照方案问题之通过Spring Cloud实现配置的自动更新如何解决
104 0
|
8月前
|
缓存 监控 Java
通用快照方案问题之Spring Boot Admin的定义如何解决
通用快照方案问题之Spring Boot Admin的定义如何解决
89 0
|
8月前
|
监控 NoSQL Java
通用快照方案问题之Martin Flower提出的微服务之间的通信如何解决
通用快照方案问题之Martin Flower提出的微服务之间的通信如何解决
62 0