Nacos 限流最佳实践

Nacos自开源以来,版本迭代速度很快,已经发布了0.9版本,准备发1.0的正式版本,支持企业使用Nacos生产高可用。在生产环境,Nacos首先需要保证自身服务的稳定性,在正常的运行环境下不会出现服务挂掉的情况。当然在一些依赖的系统出问题的时候(比如磁盘和DB),Nacos服务会受到影响,需要监控系统发现这些问题并能及时的介入处理。

作为高性能的服务发现和配置管理服务,Nacos也是存在自己的性能基线的,当瞬时的高峰流量超过自身的性能基线的时候,需要对高峰流量进行限流,以保证整体服务的健康运行而不影响到其他核心应用。

Tomcat 限流

Nacos基于spring boot使用内嵌的tomcat,tomcat线程分为acceptor线程和worker线程,acceptor线程负责从内核accept队列中取出连接并交给worker线程,而worker线程则负责处理连接(读取参数、执行处理、返回响应等)

  • acceptCount

当tomcat不能及时处理新的连接时,内核中新建的连接将会进入连接队列排队,acceptCount参数能够设置tomcat accept连接队列大小,当新的连接数超过acceptCount则拒绝连接,立即返回给client,不会让client一直等待造成响应很慢或超时

  • maxConnections

接受了的连接需要由worker线程调度处理,当worker线程处理的连接数越来越多时,处理的速度会越来越慢造成client响应时间变长,需要根据系统和机器情况设置合理的maxConnections,当连接数到达maxConnections时,不再接受新的连接,让新连接排队等待,超出队列长度则直接拒绝。

  • maxThreads

maxThreads参数设置tomcat的最大线程数,过高的线程数会让系统运行的负载过高、响应变慢,过低的线程数让系统的资源利用率太低,需要根据实际的运行情况(CPU、IO等)设置合理的最大线程数。

Nginx 限流

tomcat限流只能做到自身负载的调节,在实际生产环境中还远远不够,需要依赖Nacos自身的限流来提高系统的限流能力。

Nacos的open API都是基于http协议,可以很方便地使用nginx来做限流,不需要自身再开发限流模块来支持各种限流策略。nginx的基本使用以及nginx+lua模块安装网上资源很丰富,这里就不再介绍。

Nacos每个接口执行的代价不尽相同,一般来说写操作代价比读操作大,与此同时还有高频操作和低频操作之分,SDK调用的接口一般来说是高频接口,容易出现问题,所以在生产环境需要将这些接口区别对待,根据服务自身的实际情况采取合理的限流策略,以防错用方打垮Nacos服务。下面介绍一下Nacos在生产环境的几种限流场景

限制访问速率

1、限制单个接口的请求QPS

limit_get_config对读操作进行限流,正常使用Nacos获取动态配置一般就启动和运行时修改配置推送到client,获取配置相对来说是低频操作,如果频繁获取配置肯定是client有错用或者应用不正常(比如数据平台任务failover重试任务)

limit_req_zone $limit_key zone=limit_get_config:10m rate=10r/s;

server {
        listen       8080;
        server_name  localhost;

        location /nacos/v1/cs/configs {
            if ($request_method = POST ) {
               rewrite ^ /limit_publish_config_url last;
            }

            rewrite ^ /limit_get_config_url last;
        }

        location /limit_get_config_url {
            set $limit_key "$remote_addr+$arg_dataid+$arg_group+$arg_tenant";
            limit_req zone=limit_get_config burst=10 nodelay;
            proxy_pass http://127.0.0.1:8848/nacos/v1/cs/configs;
        }
}
  • limit_req_zone设置限流key和内存大小,以及请求速率

  • limit_key由[ip,dataId,group,tenant]四元组组成,可以防止client错用频繁访问单个配置

  • burst设置漏桶算法的桶的大小

  • nodelay设置非延迟模式,如果桶满了则会马上返回给客户端错误码

  • proxy_pass指定后端Nacos的接口url

limit_publish_config对写操作进行限流,可以有效防止热点写问题。对同一个数据的高频写会触发mysql的行锁,从而导致mysql的多线程任务因等待行锁排队,最终导致mysql所有操作都超时服务不可用。这里通过nginx lua模块获取post请求的参数,设置limit_key

limit_req_zone $limit_key zone=limit_publish_config:10m rate=5r/s;

location /limit_publish_config_url {
    set $dataId $arg_dataid;
    set $group $arg_group;
    set $tenant $arg_tenant;
    set $limit_key "$remote_addr+$dataId+$group+$tenant";
    lua_need_request_body on;
    rewrite_by_lua '
      ngx.req.read_body()
      local post_args = ngx.req.get_post_args()
      if post_args["dataId"] then
        ngx.var.dataId = post_args["dataId"];
        ngx.var.group = post_args["group"];
        ngx.var.tenant = post_args["tenant"];
        ngx.var.limit_key = ngx.var.remote_addr.."+"..ngx.var.dataId.."+"..ngx.var.group;
        if ngx.var.tenant then
          ngx.var.limit_key = ngx.var.limit_key.."+"..ngx.var.tenant;
        end
      end
    '
;

    limit_req zone=limit_publish_config burst=10 nodelay;
    proxy_pass http://127.0.0.1:8848/nacos/v1/cs/configs;
}
  • lua_need_request_body on;用于读取post请求的request body

  • rewrite_by_lua指令在http rewrite阶段执行lua代码

2、限制单机访问QPS

perclient对单个client的所有请求限制低于500QPS,可以有效防止单台client的重试***

limit_req_zone $remote_addr zone=perclient:10m rate=500r/s;

server {
        listen       8080;
        server_name  localhost;

        limit_req zone=perclient burst=250 nodelay;

        location / {
            proxy_pass http://127.0.0.1:8848/nacos/v1/cs/configs;
        }
}

3、限制 Nacos 服务 QPS

perserver限制整个Nacos服务的QPS,Nacos的服务部署在nginx之后,nginx可以保证到达Nacos的流量不会打垮Nacos

limit_req zone=perserver burst=1000 nodelay;

限制并发连接数

/nacos/v1/cs/configs/listener接口是Nacos的长连接通道,一般来说,一个client一个长连接就可以满足生产需求。limit_conn_client限制client的连接数不超过10个,limit_conn_server限制Nacos单机(8核16G内存)支撑最多9000个长连接,最多可以同时服务9000个应用节点

limit_conn_zone $remote_addr zone=limit_conn_client:10m;
limit_conn_zone $server_name zone=limit_conn_server:10m;

server {
        listen       8080;
        server_name  localhost;
        location = /nacos/v1/cs/configs/listener {
            limit_conn limit_conn_client 10;
            limit_conn limit_conn_server 9000;
            proxy_pass   http://127.0.0.1:7001/diamond-server/config.co;
            tcp_nodelay     on;
            proxy_redirect  off;
            proxy_set_header Host            $host;
            proxy_set_header X-Real-IP       $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

黑名单

1、IP黑名单

当生产环境发现有错用的client影响到Nacos服务,可以使用nginx黑名单限制client的访问

deny 30.5.125.70;

从被限制的IP访问Nacos

curl -X GET "http://{IP}:8080/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test" -i

Nginx返回403状态码给client,禁止client访问

HTTP/1.1 403 Forbidden
Server: nginx/1.13.5
Date: Fri, 15 Mar 2019 08:34:33 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive

<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.13.5</center>
</body>
</html>

2、读写黑名单分离

有时候通过IP维度直接限制client访问所有Nacos接口粒度过大,会导致应用服务不可用,可以将读操作和写操作分开,禁止client写Nacos,依然允许其进行读

# 1 limit read, 0 no limit
    map "$remote_addr" $limit_read {
       #10.2.24.252    1;
       default    0;
    }

    # 1 limit write, 0 no limit
    map "$remote_addr" $limit_write {
       #10.2.24.252    1;
       default    0;
    }

    location /some_url_to_write {
      if ($limit_write = 1) {
        return 403;
      }
    }
  • map指令匹配remote_addr变量,如果$remote_addr变量在ip黑名单里面,则设置limit_read和limit_write参数为1,否则为0

  • 在写接口中对limit_write做判断,如果禁写则返回403状态码

3、应用黑名单

IP黑名单功能是nginx提供的基础能力,能够限制某些IP的访问,但是一般一个应用会有很多台机器,当一个应用出问题的时候,会有很多IP访问都有问题,通过IP的维度来限制访问达不到预期,需要有应用的维度来限制

namespace(命名空间)是一个可以区分不同应用的维度,不同的应用一般会使用不同的namespace,这样可以在namespace维度对服务的访问进行限制

map "$arg_tenant" $limit_namespace {
        af884cf8-1719-4e07-a1e1-3c4c105ab237   1;
        #a6c745b7-fd92-4c1d-be99-6dc98abfe3dc    1;
        default    0;
    }

    location /some_url {
      if ($limit_namespace = 1) {
        return 403;
      }
    }

通过匹配namespace是否在黑名单中来设置limit_namespace变量,然后在访问的url中判断limit_namespace的值,如果为1返回403状态码

ak维度:使用一个ak代表一个应用,不同的应用在启动的时候设置不同的ak。client在发起请求的时候会带上ak参数到server端,在nginx层对请求的参数进行解析,对特定的应用的ak进行访问限制

map "$http_Spas_AccessKey" $limit_ak {
        6839c164bb344cdc93107f08eda8a136   1;
        default    0;
    }

    location /some_url {
      if ($limit_ak = 1) {
        return 403;
      }
    }

总结

本文简单介绍了Nacos在实际生产环境中如何通过限流来提高自身服务的稳定性,除了自身设置tomcat参数,还可以通过高性能的nginx作为前端对流量进行过滤提高限流能力。文中难免会有个别错误或者遗漏,如果大家有更多的限流或者提高稳定性的办法可以在Nacos官网提出来。


©著作权归作者所有:来自51CTO博客作者mb5ff80520dfa04的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 没想到,这么简单的线程池用法,深藏这么多坑
  2. 协作,才能更好的中断线程
  3. 维度爆炸?Python实现数据压缩竟如此简单!
  4. Node.js多线程完全指南[每日前端夜话0x43]
  5. RPA财务机器人的应用分类丨部署方式维度
  6. 理解Redis单线程运行模式
  7. 代码详解Python多线程、多进程、协程
  8. 简单的php多线程解决方法
  9. PHP 多进程和多线程的优缺点

随机推荐

  1. Android应用程序的debug属性
  2. GridView相关
  3. Android Activity 的四种启动模式 lunchM
  4. android 布局学习
  5. Android(安卓)网络图片加载
  6. Android ListView 设置
  7. 海康威视Android SDK,即萤石Android SDK
  8. 开发Android下纯C程序时,打开时提示not f
  9. 简述修改logo以及文字
  10. Android系统自带主题样式(android:theme),An