编程技术分享平台

网站首页 > 技术教程 正文

Java开发大型互联网企业高并发限流特技

xnh888 2024-10-13 10:20:30 技术教程 19 ℃ 0 评论

引言

并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。

什么是限流?

限流一个应用程序和使用的策略是一种架构上的决策,该决策会影响整个系统的设计。应该在应用程序设计早期阶段就考虑限流,因为将限流增加到一个已经实现的系统是很不容易的。

限流必须被快速地执行。系统必须有能力检测到活动的增加并且做出相应地反应。在负载消除后,系统也必须能够快速还原到原始状态。这要求持续地捕捉和监控相关恰当的性能数据。

如果某个服务需要暂时地拒绝用户请求,它应该返回某个特定错误码,以便于客户端应用程序明白某个操作被拒绝的原因是因为限流了。客户端应用程序可以等一段时间在重试请求。

当系统自动扩展时,限流可以被用来作为一个临时的措施。如果活动上的爆发是突然的并且预期不会很长时间,在某些情况下,最好是简单地限流,而不是去扩展,,因为扩展会增加运行时成本。

当系统自动扩展时,如果限流被用来作为一个临时的措施,并且如果资源需求增长特别快,系统可能不会继续工作——即使处于限流模式中。如果不能接受这种,考虑维护大容量的储备并且配置更有侵略性的自动扩展策略

接入层限流

接入层通常指请求流量的入口,该层的主要目的有:负载均衡、非法请求过滤、请求聚合、缓存、降级、限流、A/B测试、服务质量监控等等.

对于Nginx接入层限流可以使用Nginx自带了两个模块:连接数限流模块ngx_http_limit_conn_module和漏桶算法实现的请求限流模块ngx_http_limit_req_module。还可以使用OpenResty提供的Lua限流模块lua-resty-limit-traffic进行更复杂的限流场景。

limit_conn用来对某个KEY对应的总的网络连接数进行限流,可以按照如IP、域名维度进行限流。limit_req用来对某个KEY对应的请求的平均速率进行限流,并有两种用法:平滑模式(delay)和允许突发模式(nodelay)。

ngx_http_limit_conn_module

limit_conn是对某个KEY对应的总的网络连接数进行限流。可以按照IP来限制IP维度的总连接数,或者按照服务域名来限制某个域名的总连接数。但是记住不是每一个请求连接都会被计数器统计,只有那些被Nginx处理的且已经读取了整个请求头的请求连接才会被计数器统计。

配置示例:

================================

http {

limit_conn_zone$binary_remote_addr zone=addr:10m;

limit_conn_log_level error;

limit_conn_status 503;

...

server {

...

location /limit {

limit_conn addr 1;

}

================================

limit_conn:要配置存放KEY和计数器的共享内存区域和指定KEY的最大连接数;此处指定的最大连接数是1,表示Nginx最多同时并发处理1个连接;

limit_conn_zone:用来配置限流KEY、及存放KEY对应信息的共享内存区域大小;此处的KEY是“$binary_remote_addr”其表示IP地址,也可以使用如$server_name作为KEY来限制域名级别的最大连接数;

limit_conn_status:配置被限流后返回的状态码,默认返回503;

limit_conn_log_level:配置记录被限流后的日志级别,默认error级别。

limit_conn的主要执行过程如下所示:

1、请求进入后首先判断当前limit_conn_zone中相应KEY的连接数是否超出了配置的最大连接数;

2.1、如果超过了配置的最大大小,则被限流,返回limit_conn_status定义的错误状态码;

2.2、否则相应KEY的连接数加1,并注册请求处理完成的回调函数;

3、进行请求处理;

4、在结束请求阶段会调用注册的回调函数对相应KEY的连接数减1。

limt_conn可以限流某个KEY的总并发/请求数,KEY可以根据需要变化。

按照IP限制并发连接数配置示例:

首先定义IP维度的限流区域:

================================

limit_conn_zone $binary_remote_addrzone=perip:10m;

================================

接着在要限流的location中添加限流逻辑:

================================

location /limit {

limit_conn perip 2;

echo "123";

}

================================

即允许每个IP最大并发连接数为2。

使用AB测试工具进行测试,并发数为5个,总的请求数为5个:

================================

ab -n 5 -c 5 http://localhost/limit

================================

将得到如下access.log输出:

================================

[08/Jun/2016:20:10:51+0800] [1465373451.802] 200

[08/Jun/2016:20:10:51+0800] [1465373451.803] 200

[08/Jun/2016:20:10:51 +0800][1465373451.803] 503

[08/Jun/2016:20:10:51 +0800][1465373451.803] 503

[08/Jun/2016:20:10:51 +0800][1465373451.803] 503

================================

此处我们把access log格式设置为log_format main '[$time_local] [$msec] $status';分别是“日期日期秒/毫秒值响应状态码”。

如果被限流了,则在error.log中会看到类似如下的内容:

================================

2016/06/08 20:10:51 [error] 5662#0: *5limiting connections by zone "perip", client: 127.0.0.1, server: _,request: "GET /limit HTTP/1.0", host: "localhost"

================================

按照域名限制并发连接数配置示例:

首先定义域名维度的限流区域:

================================

limit_conn_zone $ server_name zone=perserver:10m;

================================

接着在要限流的location中添加限流逻辑:

================================

location /limit {

limit_conn perserver 2;

echo "123";

}

================================

即允许每个域名最大并发请求连接数为2;这样配置可以实现服务器最大连接数限制。

ngx_http_limit_req_module

limit_req是令牌桶算法实现,用于对指定KEY对应的请求进行限流,比如按照IP维度限制请求速率。

应用级限流

限流总并发/连接/请求数

对于一个应用系统来说一定会有极限并发/请求数,即总有一个TPS/QPS阀值,如果超了阀值则系统就会不响应用户请求或响应的非常慢,因此我们最好进行过载保护,防止大量请求涌入击垮系统。

如果你使用过Tomcat,其Connector其中一种配置有如下几个参数:

acceptCount:如果Tomcat的线程都忙于响应,新来的连接会进入队列排队,如果超出排队大小,则拒绝连接;

maxConnections:瞬时最大连接数,超出的会排队等待;

maxThreads:Tomcat能启动用来处理请求的最大线程数,如果请求处理量一直远远大于最大线程数则可能会僵死。

详细的配置请参考官方文档。另外如Mysql(如max_connections)、Redis(如tcp-backlog)都会有类似的限制连接数的配置。

限流总资源数

如果有的资源是稀缺资源(如数据库连接、线程),而且可能有多个系统都会去使用它,那么需要限制应用;可以使用池化技术来限制总资源数:连接池、线程池。比如分配给每个应用的数据库连接是100,那么本应用最多可以使用100个资源,超出了可以等待或者抛异常。

限流某个接口的总并发/请求数

如果接口可能会有突发访问情况,但又担心访问量太大造成崩溃,如抢购业务;这个时候就需要限制这个接口的总并发/请求数总请求数了;因为粒度比较细,可以为每个接口都设置相应的阀值。可以使用Java中的AtomicLong进行限流:

================================

try{

if(atomic.incrementAndGet() > 限流数) {

//拒绝请求

}

//处理请求

}finally{

atomic.decrementAndGet();

}

=========================================

适合对业务无损的服务或者需要过载保护的服务进行限流,如抢购业务,超出了大小要么让用户排队,要么告诉用户没货了,对用户来说是可以接受的。而一些开放平台也会限制用户调用某个接口的试用请求量,也可以用这种计数器方式实现。这种方式也是简单粗暴的限流,没有平滑处理,需要根据实际情况选择使用;

限流某个接口的时间窗请求数

即一个时间窗口内的请求数,如想限制某个接口/服务每秒/每分钟/每天的请求数/调用量。如一些基础服务会被很多其他系统调用,比如商品详情页服务会调用基础商品服务调用,但是怕因为更新量比较大将基础服务打挂,这时我们要对每秒/每分钟的调用量进行限速;一种实现方式如下所示:

================================

LoadingCache counter =

CacheBuilder.newBuilder()

.expireAfterWrite(2, TimeUnit.SECONDS)

.build(newCacheLoader() {

@Override

publicAtomicLong load(Long seconds)throwsException {

return newAtomicLong(0);

}

});

longlimit =1000;

while(true) {

//得到当前秒longcurrentSeconds = System.currentTimeMillis() /1000;

if(counter.get(currentSeconds).incrementAndGet() > limit) {

System.out.println("限流了:"+ currentSeconds);

continue;

}

//业务处理

}

================================

我们使用Guava的Cache来存储计数器,过期时间设置为2秒(保证1秒内的计数器是有的),然后我们获取当前时间戳然后取秒数来作为KEY进行计数统计和限流,这种方式也是简单粗暴,刚才说的场景够用了。

解决方案

一个替代的自动扩展策略是只允许应用程序使用有限定上限的资源,当限定达到时,对应用程序限流。系统应该监控资源时如何使用的,这样才能当利用率超过阈值时,系统能够限流某个或多个用户的请求。这将会使得系统继续工作,并且满足任何存在的服务等级协议(SLAs)。

系统可以实现一些限流策略,包括:

拒绝某个在特定时间段内已经访问每秒n次以上的系统APIs的单独用户。这就要求系统计量每个租户或用户使用的资源。

禁用或降级一些非必要的服务功能,以便必要的服务能够以足够的资源无碍的运行。例如,如果应用程序是视频流输出,可以切换到某个低分辨率。

使用负载分级来平滑活动的容量。在某个多租户环境中,该方式将会减少对于每个租户的性能。如果系统必须支持混合不同SLAs的租户,对于高价值的租户的工作可能会立即被处理。对于其它租户的请求可能会被延迟,当消除了待办任务时,才去处理这些请求。优先级队列模式可以用来实现这种方式。

延迟低优先级应用程序或租户的操作。这些操作可以被挂起或限制,产生一个异常来通知租户系统繁忙和该操作应该等会重试。

总结

以 上就是我对Java开发大型互联网企业高并发限流特技问题及其优化总结,分享给大家,觉得收获的话可以点个关注收藏转发一波喔,谢谢大佬们支持!

最后,每一位读到这里的网友,感谢你们能耐心地看完。希望在成为一名更优秀的Java程序员的道路上,我们可以一起学习、一起进步!都能赢取白富美,走向架构师的人生巅峰!

想了解学习Java方面的技术内容以及Java技术视频的内容可加群:722040762 验证码:头条(06 必过)欢迎大家的加入哟!

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表