微服务调优

# 1. 容器调优

# 1.1 Undertow

server:
  undertow:
    # HTTP POST请求最大的大小  -1 不限制
    max-http-post-size: -1
    # 每块buffer的空间大小,越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可
    buffer-size: 1024
    # 是否分配的直接内存(NIO直接分配的堆外内存)
    direct-buffers: true
    threads:
      # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
      # 不要设置过大,如果过大,启动项目会报错:打开文件数过多
      io: 16
      # 阻塞任务线程池, 当执行类似servlet请求阻塞IO操作, undertow会从这个线程池中取得线程
      # 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
      worker: 256
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 1.2 Tomcat

server:
  tomcat:
    # HTTP POST请求最大的大小  -1 不限制
    max-http-post-size: -1
    #最小线程数
    min-spare-threads: 150
    #最大线程数
    max-threads: 500
    #最大链接数
    max-connections: 1000
    #最大等待队列长度
    accept-count: 500 
1
2
3
4
5
6
7
8
9
10
11
12

# 2. 配置调优

# 2.1 eureka

# 2.1.1 服务端配置

eureka:
  # 实例配置
  instance:
    # 主机名
    hostname: ${spring.cloud.client.ip-address}
    instanceId: ${spring.cloud.client.ip-address}:${server.port}
    preferIpAddress: true
  # 客户端配置
  client:
    # 是否注册自身到eureka服务器,因为当前这个应用就是eureka服务器,没必要注册自身,所以这里是false
    registerWithEureka: false
    # 是否从eureka服务器获取注册信息
    fetchRegistry: false
    serviceUrl:
      # 设置eureka服务器所在的地址,查询服务和注册服务都需要依赖这个地址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
  # 服务端配置
  server:
    # 关闭自我保护模式
    enableSelfPreservation: false
    # 主动失效检测间隔5秒
    evictionIntervalTimerInMs: 5000
    # 刷新readCacheMap的时间 3秒
    responseCacheUpdateIntervalMs: 3000
    # 是否使用response cache,中小集群建议关闭
    useReadOnlyResponseCache: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 2.1.2 客户端配置

eureka-server: http://192.168.1.1:8000

eureka:
  instance:
    # 是否以IP注册到Eureka Server上,如果false则不是IP而是服务器名称
    preferIpAddress: true
    # 服务续约 - 服务续约间隔时间。默认每隔30秒,客户端会向服务端发送心跳
    leaseRenewalIntervalInSeconds: 1
    # 服务续约 - 服务失效时间。缺省为90秒服务端接收不到客户端的心跳,则剔除该客户端服务实例。
    leaseExpirationDurationInSeconds: 2
    # 实例ID
    instanceId: ${spring.cloud.client.ip-address}:${server.port}

  client:
    # 缓存清单更新时间,默认30秒。
    # 如果想eureka server剔除服务后尽快在client体现,我觉得可缩短此时间。
    registryFetchIntervalSeconds: 5
    #Eureka服务的位置
    serviceUrl:
      # 尾部不需要"/"
      defaultZone: ${eureka-server}/eureka
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 2.2 zuul


zuul:
  # 适用于ApacheHttpClient,如果是okhttp无效
  # host:
    # connect-timeout-millis: 70000
    # socket-timeout-millis: 60000
    # max-per-route-connections: 100000
    # max-total-connections: 100000
  semaphore:
    # 每个服务的最大信号量
    # 这是一个绝对值,无时间窗口,相当于亚毫秒级的。
    # 当请求达到或超过该设置值后,其余请求会被拒绝。默认100,这个值最好是根据每个后端服务的访问情况,单独设置。
    max-semaphores: 2000
  # zuul集成hystrix默认使用的是信号量隔离,不是线程隔离。
  ribbon-isolation-strategy: SEMAPHORE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2.3 ribbon

ribbon:
  okhttp:
    enabled: true
  #Ribbon允许最大连接数,即所有后端微服务实例请求并发数之和的最大值。
  MaxTotalConnections: 100000
  #单个后端微服务实例能接收的最大请求并发数
  MaxConnectionsPerHost: 10000
  #建议设置超时时间,以免因为等待时间过长造成请求处理失败(一)
  #Http请求中的socketTimeout
  ReadTimeout: 90000
  #Http请求中的connectTimeout
  #当此时间小于hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds且发生了
  #路由链接超时,会自动进行重试(大于时不会重试),如果重试失败,Zuul会抛出异常
  ConnectTimeout: 90000
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.4 hystrix


hystrix:
  # 线程池
  # 设置线程池数量的主要原则是保持线程池越小越好,因为它是减轻负载并防止资源在延迟发生时被阻塞的主要工具
  threadpool:
    default:
      # 核心线程池的大小,默认值是 10,一般根据 QPS * 99% cost + redundancy count 计算得出
      coreSize: 200
      # 线程池中线程的最大数量,默认值是 10,此配置项单独配置时并不会生效,
      # 需要启用 allowMaximumSizeToDivergeFromCoreSize 项。
      maximumSize: 300
      # 最大排队长度。默认-1,使用SynchronousQueue。其他值则使用 LinkedBlockingQueue。
      # 如果要从-1换成其他值则需重启,即该值不能动态调整,若要动态调整,需要使用到下边这个配置
      maxQueueSize: 1000
      # 因为maxQueueSize值不能被动态修改,所有通过设置此值可以实现动态修改等待队列长度。
      #即等待的队列的数量大于queueSizeRejectionThreshold时(但是没有达到maxQueueSize值),
      # 则开始拒绝后续的请求进入队列。
      queueSizeRejectionThreshold: 2000
      # 是否允许线程池扩展到最大线程池数量,默认为 false
      allowMaximumSizeToDivergeFromCoreSize: true

  command:
    default:
      requestLog:
        # 表示是否开启日志,打印执行HystrixCommand的情况和事件
        enabled: true
      # 降级策略
      fallback:
        enabled: true
        isolation:
          semaphore:
            # 此属性设置从调用线程允许HystrixCommand.getFallback()方法允许的最大并发请求数
            # 如果达到最大的并发量,则接下来的请求会被拒绝并且抛出异常.
            # Exception: fallback execution rejected.
            maxConcurrentRequests: 2000

      # 熔断策略
      circuitBreaker:
        # 启用/禁用熔断机制
        enabled: true
        # 强制开启熔断
        forceOpen: false
        # 强制关闭熔断
        forceClosed: false
        # 前提条件,一定时间内发起一定数量的请求。
        # 也就是5秒钟内(这个5秒对应下面的滚动窗口长度)至少请求4次,熔断器才发挥起作用。  默认20
        requestVolumeThreshold: 2000
        # 错误百分比。达到或超过这个百分比,熔断器打开。
        # 比如:5秒内有500个请求,250个请求超时或者失败,就会自动开启熔断
        errorThresholdPercentage: 50
        # 断路器跳闸后,在此值的时间的内,hystrix会拒绝新的请求,只有过了这个时间断路器才会打开闸门
        sleepWindowInMilliseconds: 5000

      # 执行策略
      execution:
        # 是否打开超时。要使用hystrix的超时fallback,必须设置,默认开启。
        timeout:
          enabled: true
        isolation:
          # 资源隔离模式
          strategy: SEMAPHORE
          thread:
            # 超时时中断线程
            interruptOnTimeout: true
            # 设置调用者执行的超时时间(单位毫秒)
            timeoutInMilliseconds: 90000
            # 取消时候中断线程
            interruptOnFutureCancel: false
          semaphore:
            # 当HystrixCommand.run()使用SEMAPHORE的隔离策略时,设置最大的并发量
            maxConcurrentRequests: 2000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

# 2.5 feign

feign:
  hystrix:
    enabled: true
  client:
    config:
      default:
        connectTimeout: 30000
        readTimeout: 30000
        loggerLevel: none
  httpclient:
    enabled: false
  okhttp:
    enabled: true
    connection-timeout: 30000
    max-connections: 20000
    max-connections-per-route: 10000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2.6 jar 启动参数

  1. 基本参数
  • -Xmx 设置JVM最大可用内存
  • -Xms 设置JVM促使内存
  • -Xmn 设置年轻代大小,此值一般低于-Xmx的1/2,高于-Xmx的1/3
  • -XX:SurvivorRatio:设置年轻代中Eden区与Survivor区的大小比值
  1. 并行收集器相关参数
  • -XX:+UseParallelGC Full GC采用parallel MSC
  • -XX:ParallelGCThreads 并行收集器的线程数,此值最好配置与处理器数目相等
  • -XX:+UseParallelOldGC 年老代垃圾收集方式为并行收集,这个是JAVA 6出现的参数选项
  • -XX:+UseAdaptiveSizePolicy 自动选择年轻代区大小和相应的Survivor区比例,置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开.
  • -XX:+UnlockExperimentalVMOptions 可解锁JVM额外参数
  • -XX:MaxRAMFraction openjdk java -XX:MaxRAMFraction docker容器内自动调整内存限制-Xmx
-Xmx1g -Xms1g -Xmn500m -XX:SurvivorRatio=8 -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseParallelGC -XX:ParallelGCThreads=4 -XX:+UseParallelOldGC -XX:+UseAdaptiveSizePolicy -XX:+UnlockExperimentalVMOptions -XX:MaxRAMFraction=3
1
  1. 示例
#!/bin/sh

JAVA_OPTS="\
-Xmx2g \
-Xms2g \
-Xmn1g \
-XX:SurvivorRatio=8 \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=512m \
-XX:+UseParallelGC \
-XX:ParallelGCThreads=4 \
-XX:+UseParallelOldGC \
-XX:+UseAdaptiveSizePolicy \
-XX:+PrintGCDetails \
-XX:+PrintTenuringDistribution \
-XX:+PrintGCTimeStamps \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/ \
-Xloggc:/gc.log \
-XX:+UseGCLogFileRotation \
-XX:NumberOfGCLogFiles=5 \
-XX:GCLogFileSize=10M  "

java ${JAVA_OPTS} -jar target/demo.jar --spring.profiles.active=dev --server.port=9999 > /dev/null &

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 2.7 常见异常

  • Caused by: java.lang.RuntimeException: Hystrix circuit short-circuited and is OPEN
引起这个问题的原因是在一个滚动窗口内,失败了二十个(默认),就会发生短路,短路时间默认为5秒,5秒之内拒绝所有的请求,之后开始运行。

解决办法如下:

1.设置熔断器失败的个数,默认为20个,这里我给了1000个,只有超过1000个才会发生短路。

hystrix详细具体的配置信息可以谷歌一下,hystrix的配置。

1
2
3
4
5
6
7
8
  • Caused by: java.lang.RuntimeException: could not acquire a semaphore for execution

  • Caused by: com.netflix.hystrix.exception.HystrixRuntimeException: auth-admin short-circuited and no fallback available.

  • Zuul exception: auth-performance-test could not be queued for execution and no fallback available.

  • Rejected command because thread-pool queueSize is at rejection threshold.

  • Number of retries on next server exceeded max 1 retries, while making a call for: 192.168.199.164:10000

# 3. Nginx 调优

#user  nobody;

# 一般设置为CPU个数
worker_processes  8;
# 一个ng进程打开最多文件描述符数量
worker_rlimit_nofile 65535;

events {
    # 仅用于linux2.6以上内核
    use epoll;
    # 单个进程最大连接数
    worker_connections  65535;
    
    # 收到一个新的连接通知后接受尽可能多的链接 
    multi_accept on;
}

http {

    # 负载均衡
    upstream providers {
        keepalive 50;
    	server localhost:10087 max_fails=1 fail_timeout=60s;
    	server localhost:10088 max_fails=1 fail_timeout=60s;
    	server localhost:10089 max_fails=1 fail_timeout=60s;

    }
    
    open_file_cache max=102400 inactive=20s;
    client_header_buffer_size 4k;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    # 关闭log
    access_log  off;
    # 开启gzip
    gzip  on;

    proxy_headers_hash_max_size 51200;
    proxy_headers_hash_bucket_size 6400;

    server {
        listen       8888;
        server_name  localhost;
        client_max_body_size 16k;

        location / {
            try_files $uri $uri/ @router;
            index  index.html;
            root D:/root/www/;
        }

        location /api/ {
            # 采用http 1.1
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-For $http_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Nginx-Proxy true;
            proxy_pass   http://providers/;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

# 4. 代码调优

# 4.1. 数据缓存

对于查询性能要求较高的接口,数据应尽可能存储在Redis中。

# 4.2. Redis数据结构

避免存储在Redis中的数据为JSON格式。JSON数据在序列化、反序列化过程中会大大降低性能。

# 4.3. 日期类型格式化

避免目标接口数据中对日期或日期字符串进行Format操作。

# 4.4. 异步处理

对于不影响接口返回的业务代码段,尽可能采用异步方式执行。

# 4.5 多线程

对于负载较高的业务接口尽可能采用多线程协作处理。

# 4.6 数据压缩

服务提供者及调用者应对传输数据进行压缩,例如使用GZIP压缩。

# 4.7 网络

确保DB、Redis等数据存储Server与服务Server之间的高效传输网络。

# 5. JMeter 压测

# 5.1 下载

wget https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.4.1.zip
1

# 5.2 Test

nohup java -Xms1024m -Xmx2048m -XX:MaxNewSize=512m -jar performance.jar > /dev/null 2>&1 &
1
./jmeter -n -t dj.jmx -r -l ./result.jtl -e -o ./html
1

# 5.3 Exception

  • Caused by: java.io.FileNotFoundException: rmi_keystore.jks (没有那个文件或目录)

    • 解决方法:修改jmeter.properites: server.rmi.ssl.disable=true,关闭ssl功能
  • Error in NonGUIDriver java.lang.RuntimeException: Following remote engines could not be configured:[127.0.0.1]

    • 关闭防火墙 systemctl stop firewalld

    • 编辑jmeter.properties

      • remote_hosts=127.0.0.1:1099
      • server_port= 1099

# 5.4 JMeter 配置说明

  • Constant Throughput Timer
    • 常数吞吐量定时器,该定时器可以方便地控制给定的取样器发送请求的吞吐量。

daijiang@hopechart.com

更新时间: 1/10/2023, 9:23:40 AM