一个真实的设置案例:某电商公司促销日,后台webservice接口被瞬间涌入的访问5000个并发请求打爆,数据库连接池耗尽,次数整个系统瘫痪了40分钟。设置运维主管盯着监控屏幕骂了一句:“这接口不是访问设了最大连接数吗?”——设了,但设的次数是服务容器的连接数,不是设置webservice本身的并发上限。
很多人把“同时访问次数”理解成web服务器(Tomcat、访问IIS)的次数线程池大小。这是设置两码事。webservice的访问并发控制,本质上是次数应用层对业务处理能力的自我约束。你让Tomcat开200个线程,设置但如果webservice内部没做限制,访问每个线程都可能同时去抢数据库连接、次数文件句柄,最终照样把资源耗尽。
具体怎么设?以Java的JAX-WS为例,最直接的是用@Resource注解注入一个Executor,然后通过WebServiceContext设置。但更常见的做法是在部署描述符里配。比如GlassFish的sun-web.xml里有个
真正靠谱的限流逻辑要分层。第一层,容器线程池设一个硬上限,比如100;第二层,webservice业务逻辑里用Semaphore或RateLimiter控制实际同时处理的请求数,比如50;第三层,数据库连接池最大连接数设成比第二层稍大,比如60。这三层配合,才能保证即使流量冲到5000,系统也只是排队,而不是雪崩。
有个细节容易被忽略:异步处理。如果你的webservice是同步阻塞的,那并发数基本等于线程数。但如果改成异步(比如用@Asynchronous或JAX-RS的异步响应),一个线程可以同时挂起多个请求,并发量就能大幅提升。代价是客户端超时时间得调长,而且你得自己处理回调状态。我见过某支付系统,把同步改异步后,同样的硬件配置下并发能力提升了4倍,但排查bug的难度也翻了倍——因为你没法用传统的线程栈去定位问题。
再提醒一句:别迷信“无限制”。有些框架允许你设成Integer.MAX_VALUE,那等于没设。实际压测时你会发现,当并发超过某个阈值,响应时间会呈指数级恶化。这个阈值和你的业务复杂度、硬件配置强相关。拿一个简单查询接口来说,4核8G的机器,纯内存操作,500并发可能还扛得住;但如果是写数据库、调第三方API,50并发就够呛了。
最后给个土办法:在webservice入口加个计数器,用AtomicInteger记录当前正在处理的请求数。超过阈值直接返回503,别让请求继续往下走。别小看这招,很多老系统就是这么干的,简单粗暴但有效。我参与过一个遗留系统改造,就是用这种方式,把并发从默认的无限变成200,系统稳定性立刻上来了——代价是高峰期部分请求会被快速拒绝,但总比整个服务挂掉强。
设置同时访问次数,本质上是给系统装个阀门。阀门拧多大,取决于下游能扛多少。别只盯着webservice本身,数据库、文件系统、外部API,每一环都是瓶颈候选。你设了1000并发,但下游只能处理100,那剩下的900全在等,最终等成超时风暴。
