服务器并发数规律研究

来源:互联网 发布:辐射4女捏脸数据 编辑:程序博客网 时间:2024/05/01 05:02

        在web服务器性能测试中,并发数是一个很多人比较关心的话题之一,web服务器软件中也有很多并发数设置的地方。但是怎样的并发数设置才是合理的呢,这就涉及到并发数调优了。本文在前人研究的并发数规律的基础上进行了jmeter的压测实践,验证了此规律。

        为了充分利用CPU资源,一般人可能认为适合的线程数量等于CPU数量+1或CPU数量-1,但是,淘宝蒋江伟在QCon2011的演讲PPT(点此查看)中指出,线程数量=((CPU时间+CPU等待时间) / CPU时间) * CPU数量。这个公式只是针对CPU而言,准确来讲,是当机器的瓶颈资源是CPU的时候而言。更一般的,瓶颈资源的线程并行数=瓶颈资源的总份数/单次请求占用瓶颈资源的份数。

对此用下面几个简单servlet测试下:

(jmeter请求端与tomat服务端在同一机器上;测试机器CPU为intel双核;servlet容器:tomat5.5,jvm xmin=xmax=256m;)

1. 模拟进行IO操作,但是瓶颈资源为CPU,因为用sleep来模拟模拟IO,所以IO不存在性能瓶颈。

@Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws             ServletException,IOException {        long t = System.nanoTime();        long a = 0;        long times = Long.valueOf(req.getParameter("times"));        for (int i = 0; i < times; i++) {            a = a + 1;        }        long timeCost = (System.nanoTime() - t) / 1000000;        try {            Thread.sleep(100);        } catch (InterruptedException e) {            logger.warning(e.getMessage());        }        resp.setContentType("text/html");        PrintWriter out = resp.getWriter();        out.println("success.times:" + times + ",cpuTimeCost(ms):" + timeCost);        out.flush();        out.close(); // 这里本来不需要close,但是压测请求过多后,会出现因为没及时关闭流,而导致java.net.SocketException:Connect reset    }
在上述servlet的doGet方法中,仅仅让线程sleep100毫秒,模拟IO操作。其中servlet传参times=1000000。jmeter测试结果如下:线程循环次数线程数平均响应时间(毫秒)tpscpu2000211417.415%~20%2000511343.7>50%2000101079260%左右200015107137.5<80%200020109176.190% 左右200025112212.3<100%200030118237.4100%200035130238.4100%

从统计数据来看,在10并发前,平均响应时间还没达到最高,cpu负载不高;(但不知为什么响应时间没达到最理想)在10~20并发时,平均响应时间小于110ms,cpu负载比较饱满,到目前为止tps都在稳步提升;在30并发以后,平均响应时间开始变长,tps已经没有上升空间

对照之前给出的公式,最快的在107ms左右,而模拟IO占100ms,所以cpu运算时间为7ms,按公式,线程数=(100 + 7) / 7 * 2= 30.5。 而统计数据中,正是在30并发后,基本达到瓶颈。满足期望规律。

2. 模拟进行CPU密集请求。瓶颈资源仍为CPU。

    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws             ServletException, IOException {        long t = System.nanoTime();        long a = 0;        long times = Long.valueOf(req.getParameter("times"));        for (int i = 0; i < times; i++) {            a = a + 1;        }        long timeCost = (System.nanoTime() - t) / 1000000;        resp.setContentType("text/html");        PrintWriter out = resp.getWriter();        out.println("success.times:" + times + ",cpuTimeCost(ms):" + timeCost);        out.flush();        out.close(); // 这里本来不需要close,但是压测请求过多后,会出现因为没及时关闭流,而导致java.net.SocketException:Connect reset    }
在上述servlet的doGet方法中,没有sleep模拟IO。其中servlet传参times=1000000。jmeter测试结果如下:
线程循环次数线程数平均响应时间(毫秒)tpscpu10018411.6100%10028422.4100%100312922.5100%

从统计结果看到,在2个并发情况就把机器资源占满了。在3个并发时候tps已经没有上升空间了。

按公式,线程数=84/84 * 2 = 2,在2个线程时候就达到资源最高利用了。

        上面2个例子都是以cpu为瓶颈的场景,如果数据库是瓶颈,那么就要响应地调整了公式中的项了,但原理都是一样的,这里就不再举例验证了。所以,在系统的并发数调优时,不能只从cpu的数量来加1或减一,而要从瓶颈资源到角度去做相应的判断。当然,实际并发调优还受诸多因素影响,如http服务器,web容器的参数,jvm设置等等,本文只是从瓶颈资源的角度看待并发数。后续会继续探讨。