阿里最新开源配置中心和注册中心: Nacos 限流最佳实践

  • Nacos 限流最佳实践

  • Tomcat 限流

  • Nginx 限流

    • 限制访问速率

    • 限制并发连接数

    • 黑名单

  • 总结


作者:风卿(Nacos 社区 committer)

本文系投稿。如果胖友有想要投稿的内容,欢迎后台留言。哇咔咔。

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


403 Forbidden

403 Forbidden


nginx/1.13.5

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官网提出来。

 

在此我向大家推荐一个架构学习交流群。交流学习群号:993070439 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系,还能领取免费的学习资源。

你可能感兴趣的:(Java)