不一样的思想~~ http://shop34712791.taobao.com MSN:wins0910@hotmail.com

发布新日志

  • PHP 版本与memory_limit

    2008-04-04 21:19:20

    php5.2.0内存管理改进发布:dxy 发布日期:2007-1-22 字体:[增加 减小] 类型:转载
    pHP5.2.0的内存管理做了较大的改进,某些情况下内存不能释放的问题不存在了。
    测试php脚本(mem.php),我用echo N>>和sleep来控制脚本在某个阶段暂停以便检测状态。
    [复制此代码]CODE:
    <?php 
    echo '1>>'; 
    sleep(5); 
    $o = array(); 
    for ($i=0;$i<=100000;$i++) { 
        $o[]='aaaaaaaaaaaaaaaaaaaaa'; 

    echo '2>>'; 
    sleep(5); 
    unset($o); 
    echo '3>>'; 
    while (true) { 
        echo '..'; 
        sleep(10); 

    ?>
    监测内存使用情况的bash scrīpt(注意:里面的”mem”是取自上面的php脚本名):
    while true;do clear;ps au|grep -v "\(vi\|grep\)"|grep "\(mem\|RSS\)";sleep 2;done;
    以下是$/usr/local/bin/php mem.php这个进程在三种状态(数组创建前,数组创建后,数组销毁后)下,用5.1.6和5.2.0的PHP(我使用了一样的configure参数)测试的RSS(内存使用值,单位KB)结果。
    php5.1.6:
    3164
    18076
    17572
    PHP5.2.0:
    4088
    14400
    4424
    大家可以看到5.1.6这个版本,unset数组之后,内存并没有从进程里释放出来,虽然它是可以继续被这个php进程再利用,但是却无法被系统的其他进程所使用。而5.2.0则真正释放出了内存。
    大家可能还注意到,在最开始,5.2.0的内存使用比5.1.6多了几个kb,这是由于5.2.0增加了一些新的东西,这个是很正常的。
    另外php5.2.0的内存分配也进行了大的改进,官方的说法是对memory_limit的检测由每次呼叫emalloc()的时候进行改为直接检测从系统请求的内存数据块(blocks)。需要更进一步了解的朋友可以自己去研究代码。而由于内存分配的实现的更改,内存控制可以更精确地被控制在memory_limit之下,也就是说以前php代码里,如果有超过了memory_limit的内存使用而没有出错的,在php5.2.0里可能会报错。为了平衡这次改进,PHP5.2.0的默认的memory_limit由以前的8MB改为了16MB.搜索源代码可以看到这个修改(find . -name \*c -type f |xargs cat |grep memory_limit)。 
    本文来自: 脚本之家(www.jb51.net) 详细出处参考:http://www.jb51.net/article/6507.htm

  • Apache apr中内存分配子的current_free_index成员作用

    2008-04-04 11:04:25

                  Apache apr中内存分配子的current_free_index成员作用     CSDN Blog鎺ㄥ嚭鏂囩珷鎸囨暟姒傚康锛屾枃绔犳寚鏁版槸瀵笲log鏂囩珷缁煎悎璇勫垎鍚庢帹绠楀嚭鐨勶紝缁煎悎璇勫垎椤瑰垎鍒槸璇ユ枃绔犵殑鐐瑰嚮閲忥紝鍥炲娆℃暟锛岃缃戞憳鏀跺綍鏁伴噺锛屾枃绔犻暱搴﹀拰鏂囩珷绫诲瀷锛涙弧鍒?00锛屾瘡鏈堟洿鏂颁竴娆°? title=  

    1. 背景


    这个问题最初起源于tingya的blog中apache代码分析系列文章 ( http://blog.csdn.net/tingya )。当对内存池部分进行分析时,涉及到了内存分配子(allocator)的内容。内存分配子结构体中包含一个叫做current_free_index的成员。对于该成员的具体作用,广大网友众说纷纭。相关的讨论可以在tingya的blog中看到( http://blog.csdn.net/tingya/archive/2005/12/23/559815.aspx )。

    2. 历史

    关于apr是何时把current_free_index成员放入内存分配子结构体中的,我google了一下,找到了这样一篇文章:

    http://www.mail-archive.com/dev@apr.apache.org/msg06563.html

    从文章中的补丁可以看出,在这之前,内存分配子结构体中是没有current_free_index和max_free_index这两个成员的。并且,那时的apache在内存分配子未被释放之前是不会释放内存分配子中保留的内存。而之所以加入这两个成员,目的就是为了控制内存分配子中保管内存的数量。至此,可以基本断定,current_free_index和max_free_index是协同工作来控制分配子中的内存。

    3. current_free_index与max_free_index的作用

    在tingya的blog上面的讨论中,有一个叫做allan 的网友对current_free_index的功能作了如下的描述:

    current_free_index,是当前应该释放的index值(超过这个值就应该释放)
    这个值是动态调整的,根据你系统中内存的使用情况来判断的
    除了在设置max_free_index的函数中改变了这个值以外,
    在其他的两个地方也有涉及到,分别是:
    用分配子分配node的时候,current_free_index加上了该node的index值
    用分配子释放node的时候,current_free_index减去了这个node的index值
    在一般情况下,这个值一增一减是平衡的。
    考虑一种极端情况:你调用max_free_set之后,max_free_index和current_free_index都不为0,然后一直向分配子申请内存
    分配子的内存不够用了,需要向系统申请,这种内存只有在调用分配子释放的时候,归还到分配子的free链表中
    当你不断释放的时候,current_free_index不断减少,相当于归还给系统的内存门槛在降低,因为这些内存是从操作系统申请而来的
    如果不归还给操作系统,系统资源会耗尽(全部给了分配子)

    这样,即使当你调用max_free_set设置了一个超大的内存归还门槛。你在向系统不断申请内存后调用分配子释放的时候
    分配子也会逐渐调低current_free_index的门槛,把内存归还给操作系统
     
    之后,allan又说了:
    呵呵,其实刚才说的是表面现象,apr靠这样一种机制保证了,分配子只在内存中占有max_free_index这么多的空间,如果你向系统申请了超过这么的空间,apr只把不超过的部分留在自己的free链表中,超过的部分统统还给系统。

    这就是靠我刚才说的使用current_free_index这样一个机制来实现的,呵呵。
     
    至此,current_free_index的功能已经基本有了眉目。
    而tingya在文章中对max_free_index的作用概括为:“如果结点的大小超过了完全释放的阙值max_free_index,那么我们就不能将其简单的归还到索引链表中,而必须将其完全归还给操作系统。
    但是在通过阅读代码之后,可以发现,二人的说法虽然各有一定道理,但是对current_free_index和max_free_index的功能概括却并不准确。我在这里试着对这两个成员的功能作一个更加准确的概括:
    max_free_index用于记录内存分配子中最多可以容纳的内存空间大小。
    current_free_index用于记录内存分配子中当前可以继续容纳的内存空间的大小。
    其实,allan已经基本对这两个功能进行了描述,但是current_free_index并非仅仅如他所说的,“是当前应该释放的index值(超过这个值就应该释放)
    这两个变量控制了内存分配子内容量的大小。这样做是基于以下的事实:
    内存分配子中保存的都是从操作系统申请来的,但是至今仍是闲置的内存空间。这些空间是以多个链表的形式保存的。这样的空间不能太大——否则大量的系统资源被占用却未被利用,影响了系统的效率;同时,这样的空间又不能太小——否则频繁地从操作系统内申请和释放内存同样会影响系统的效率。因此,内存分配子中保存的这些闲置内存的数量就要控制在一定范围之内。这样,通过max_free_index来记录内存分配子中最多可以容纳的内存空间大小。在调用apr_allocator_free函数释放一个分配子节点到分配子中时,会检查分配子中当前内存数,加上这个即将被释放的分配子节点携带的内存空间数之和是否超过了max_free_index。而因为current_free_index记录了内存分配子中当前可以继续容纳的内存空间大小,那么,只要检测这个即将释放的分配子节点携带的内存空间数是否大于current_free_index就好了。这个检测是通过判断 index > current_free_index 的值来完成的。
     
    4. 关于current_free_index溢出的问题
     
    在google的时候,我同样发现了这样的一篇文章:
    http://marc2.theaimsgroup.com/?l=apache-httpd-dev&m=113600944405209&w=2
    其中提到了:
    We probably should have a check in apr_allocator_free() to
    make sure that we're not causing an unsigned int overflow in
    allocator->current_free_index, e.g.,
    + if (index > current_tree_index) {
    + current_tree_index = 0;
    + }
    + else {
    current_free_index -= index;
    + }
    按照如此说法,current_free_index对于溢出的检测的确存在问题。但是就此问题我也做了一个粗略的分析。
    (1) 当max_free_index不等于0时
    即,对于内存分配子中的内存大小作了限制。这时,当释放一个节点时,会执行以下if语句:
            if (max_free_index != APR_ALLOCATOR_MAX_FREE_UNLIMITED
                && index > current_free_index) {
                node->next = freelist;
                freelist = node;
            }
    因为已经假设max_free_index不等于零,即,判断语句的前半部分为真,如果index > current_free_index,则会将分配子节点释放给操作系统而不对current_free_index进行修改。一旦index > current_free_index为假,那么执行current_free_index -= index也不会发生溢出,因为current_free_index - index >= 0。
    (2) 当max_free_index等于0时
    即,对于内存分配子中内存大小不做限制。那么很显然,既然不做限制,current_free_index也便没有了任何作用,它的溢出也就无所谓了。而如果在current_free_index溢出之后再通过apr_allocator_max_free_set调整回收门槛,由于current_free_index溢出变成了一个很大的整数,加上即将释放的节点的index后可能依然大于max_free_index,但是由于下面语句的存在:
        if (allocator->current_free_index > max_free_index)
            allocator->current_free_index = max_free_index;
    所以current_free_index值会被设成max_free_index的值。
     
    但是仍然存在一个问题:
    假如通过apr_allocator_create创建了一个无回收门槛的分配子allocator,调用apr_allocator_alloc在allocator中申请了一个100K的节点。之后,通过调用apr_allocator_alloc将这个节点释放给allocator。此时,allocator->current_free_index已经溢出变为一个很大的正整数。最后再通过apr_allocator_max_free_set调整回收门槛为50K。调用结束,current_free_index和max_free_index值会相等,都表示与50K对应的索引值。但是,此时内存分配子中却存储着一个100K的节点。这样,current_free_index就不再表示当前分配子可以继续存储的内存空间大小。除非用户的再次申请大于或者等于100K的空间,否则分配子中的节点索引的和会大于max_free_index值。不过,这种调用方法(即,先申请 释放内存后再设置门槛)可以比较容易地避免。因此,这种内存浪费的问题也便不是很明显了。
     
    注: 本人十一长假期间在家,手头的电脑不是自己的,只有windows系统且无开发环境,所以无法做相关的测试。因此所说的一些话仅仅是臆断,不能保证正确性。还望有相关测试条件的人员帮忙替我做完这个测试,特别是对我最后提出的那个疑问的测试。文章中不正确的地方,很可能是我自身疏忽的缘故,还望多多谅解。
    5. 溢出问题的解决 
    关于解决上述的current_free_index溢出问题,存在着两种方法(别着急,先别急着改代码,一会我会说原因)。
    第一种,就是如第4节开头部分的补丁所示,修改allocator_free函数,在减少current_free_index前对减数和被减数进行检测,避免current_free_index溢出。
    第二种,就是修改apr_allocator_max_free_set,在其中加入释放多余空间的功能。也就是说,如果max_free_index设置的值表示为a,则将内存分配子中的节点释放直到其中的总大小小于等于a为止。并调整相应的current_free_index值。
    比较起来,第一种方案似乎容易实现些。
    但是,为什么apache项目组在05年收到补丁后却迟迟不肯修改自己的代码呢?我个人认为是有以下原因的:
    1. current_free_index溢出的可能性不是很大。正如我在前面一节说的,它的溢出只有在特定的调用顺序和参数的前提下才会发生,而且会随着下次调用apr_allocator_alloc而逐渐消除溢出对系统的影响。
    2. apr_allocator_free这个函数调用的频率相对较高,它执行的效率直接影响着整个系统的效率。如果要在其中加入什么代码,必须要经过深思熟虑——哪怕是一个简单的if-else。
    3. 正如前面一节所说的,current_free_index溢出是在特定的函数调用顺序和参数的情况下才发生。概括起来,可以说是在申请并释放大量内存后,调用apr_allocator_max_free_set,将本是无回收门槛的分配子设置上门槛。但是,凭借常识可以知道,在一系列大量申请并释放内存后,系统往往会逐渐地恢复到申请小片内存。换句话说,一般的系统,很少在申请大量内存过后,突然间改变成仅仅申请少量的内存而不再申请大片内存。这之间往往会存在一个过渡期,因此,在这个过渡期中,在分配子里面存储一些过剩的内存空间以备不时之需往往是有必要的。况且,这种内存溢出带来的问题可以在调用一次或几次apr_allocator_alloc之后得到消除。如果在调用apr_allocator_max_free_set时便一下子把内存分配子中的节点释放到相应的数量,那么既消耗了系统资源(因为多次调用了free,把节点占用的内存还给操作系统),又不能应付以后可能发生的过渡时期。因此,这么做显然不是非常理智。
    如前所述,消除溢出的方法的确存在,但是比起现在的做法,不如让溢出继续保留——反正这种溢出发生的可能性不是很大,而且非常容易避免。
    最后,感谢tingya对apache代码的分析,我受益匪浅。
    感谢allan对current_free_index的总结。
    感谢各位网友的积极讨论。
  • PHP优化(摘抄)

    2008-03-20 22:35:43

    PHP is a very fast programming language, but there is more to optimizing PHP than just speed of code execution.
    php 是一个高效的语言,不过优化php不仅仅是优化程序的代码

    In this chapter, we explain why optimizing PHP involves many factors which are not code related, and why tuning PHP requires an understanding of how PHP performs in relation to all the other subsystems on your server, and then identifying bottlenecks caused by these subsystems and fixing them. We also cover how to tune and optimize your PHP scrīpts so they run even faster.

    在这篇文章中,我们将揭示为什么优化php包括很多代码没有涉及的因素。和为什么调整php需要理解你的服务器上的所有子系统上PHP的表现并寻找出由这些系统引起的瓶颈并修复他们。当然这也适用于调整和优化你的PHP脚本来使他们运行的更快。

    Achieving High Performance
    达到高性能

    When we talk about good performance, we are not talking about how fast your PHP scrīpts will run. Performance is a set of tradeoffs between scalability and speed. scrīpts tuned to use fewer resources might be slower than scrīpts that perform caching, but more copies of the same scrīpt can be run at one time on a web server.
    当我们谈及高性能时,我们不是谈论你的PHP脚本如何的快。性能是在资源占用和速度之间的折中。调整脚本去使用更少的资源会比使用更多的缓存要慢。但是可以有更多的拷贝同时在服务器上运行[就是我们常说的以资源换速度还是以速度换资源—by achieverain]

    In the example below, A.php is a sprinter that can run fast, and B.php is a marathon runner than can jog forever at the nearly the same speed. For light loads, A.php is substantially faster, but as the web traffic increases, the performance of B.php only drops a little bit while A.php just runs out of steam.
    在下面的例子中,A.php 是一个跑的很快的短跑选手。B.php是一个可以永远跑下去的马拉松选手。在轻量级负载中。A.php绝对领先,但是当网页的流量增加时,B.php的速度仅仅下降了一点,但A.php已经被淘汰了。

    Let us take a more realistic example to clarify matters further. Suppose we need to write a PHP scrīpt that reads a 250K file and generates a HTML summary of the file. We write 2 scrīpts that do the same thing: hare.php that reads the whole file into memory at once and processes it in one pass, and tortoise.php that reads the file, one line at time, never keeping more than the longest line in memory. Tortoise.php will be slower as multiple reads are issued, requiring more system calls.
    让我们举更过的类似的例子来证明这个事实。假设我们需要写一个读取一个250k大小的文件并生成一个HTML摘要文件。我们同时写两个脚本来做这个事情:hare.php[hare 是兔子的意思 –by achieverain]每次运行直接把整个文件读入内存,tortoise.php[tortoise 是乌龟的意思 –by achieverain]每次只读文件的一行,并且让内存的数据保持在一行。Tortoise.php当然因为因为多次的读取而更慢,需要更多次的系统调用

    Hare.php requires 0.04 seconds of CPU and 10 Mb RAM and tortoise.php requires 0.06 seconds of CPU and 5 Mb RAM. The server has 100 Mb free actual RAM and its CPU is 99% idle. Assume no memory fragmentation occurs to simplify things.
    hare.php 需要CPU运行0.04秒。10M 内存,tortoise.php 需要CPU运行0.06秒。5M 内存,服务器有100M的空闲物理内存,CPU的空闲资源(idle)为99%.简单起见:假设没有内存碎片。

    At 10 concurrent scrīpts running, hare.php will run out of memory (10 x 10 = 100). At that point, tortoise.php will still have 50 Mb of free memory. The 11th concurrent scrīpt to run will bring hare.php to its knees as it starts using virtual memory, slowing it down to maybe half its original speed; each invocation of hare.php now takes 0.08 seconds of CPU time. Meanwhile, tortoise.php will be still be running at its normal 0.06 seconds CPU time.
    在10个并发的情况下,hare.php将把所有的内存用完(10 x 10 = 100)。而tortoise.php将还有50M的剩余内存。如果要同时运行第11个 hare.php将是服务器使用虚拟内存,降低的速度也许是原始速度的一般;每次调用hare.php将消耗0.08秒,与此同时,tortoise.php将仍然是0.06秒。
    In the table below, the faster php scrīpt for different loads is in bold:
    在下面的图表中,不同负载情况下更快的php脚本将被加黑。
    Connections
    CPU seconds required to satisfy 1 HTTP request
    CPU seconds required to satisfy 10 HTTP requests
    CPU seconds required to satisfy 11 HTTP requests

    hare.php
    0.04
    0.40
    0.88
    (runs out of RAM)

    tortoise.php
    0.06
    0.60
    0.66

    As the above example shows, obtaining good performance is not merely writing fast PHP scrīpts. High performance PHP requires a good understanding of the underlying hardware, the operating system and supporting software such as the web server and database.
    由上可知,取得良好的表现并不仅仅是写一个高速的php脚本。高性能的PHP需要很好的理解程序所基于的硬件基础,操作系统,web server [如apache]和数据库之类的支援软件

    Bottlenecks
    瓶颈

    The hare and tortoise example has shown us that bottlenecks cause slowdowns. With infinite RAM, hare.php will always be faster than tortoise.php. Unfortunately, the above model is a bit simplistic and there are many other bottlenecks to performance apart from RAM:
    刚才那个例子给我们展示了瓶颈的存在导致速度降低,如果有无限的内存,hare.php将永远比tortoise.php跑的快。不幸的是,上面的有点太单纯了,而且很多其他的瓶颈也在蚕食着内存。

    (a) Networking
    (a)网络

    Your network is probably the biggest bottleneck. Let us say you have a 10 Mbit link to the Internet, over which you can pump 1 megabyte of data per second. If each web page is 30k, a mere 33 web pages per second will saturate the line.
    你的网络情况也许会是一个最大的瓶颈。假设服务器带宽10Mbit那么你每秒能传输大约1Mbyte数据。如果每个网页30K,那么你每秒最多传输33个网页。
    More subtle networking bottlenecks include frequent access to slow network services such as DNS, or allocating insufficient memory for networking software.
    更多在网络不佳时导致访问速度降低的原因有DNS或者内存不足等。
    (b) CPU
    (b) CPU

    If you monitor your CPU load, sending plain HTML pages over a network will not tax your CPU at all because as we mentioned earlier, the bottleneck will be the network. However for the complex dynamic web pages that PHP generates, your CPU speed will normally become the limiting factor. Having a server with multiple processors or having a server farm can alleviate this.
    如果你控制你的CPU负载。发送一个无格式的HTML页基本不占用你的CPU资源。然而一个完全由PHP生成的动态页会占用很多CPU资源。多CPU服务器或者服务器集群可以缓解这个问题。

    (c) Shared Memory
    (c) 共享内存

    Shared memory is used for inter-process communication, and to store resources that are shared between multiple processes such as cached data and code. If insufficient shared memory is allocated any attempt to access resources that use shared memory such as database connections or executable code will perform poorly.
    共享内存是为进程间通讯准备的,如果系统内存不足的话,使用共享内存的操作如 数据库联接,执行代码将会是低效的。
    (d) File System
    (d) 文件系统

    Accessing a hard disk can be 50 to 100 times slower than reading data from RAM. File caches using RAM can alleviate this. However low memory conditions will reduce the amount of memory available for the file-system cache, slowing things down. File systems can also become heavily fragmented, slowing down disk accesses. Heavy use of symbolic links on Unix systems can slow down disk accesses too.
    访问硬盘比访问内存要慢50到100倍。将文件缓存存入内存可以解决这个问题。但是服务器内存会被文件缓存的数据大量占用,反而将系统速度减慢。严重的文件碎片也会降低访问硬盘的速度,在Unix系统上大量使用符号链接也会降低访问硬盘的速度

    Default Linux installs are also notorious for setting hard disk default settings which are tuned for compatibility and not for speed. Use the command hdparm to tune your Linux hard disk settings.
    默认设置下安装的linux有个糟糕的问题就是硬盘的设置是兼容性优先而不是速度。使用 hdparm 这个命令可以调整你的linux硬盘的设置
    (e) Process Management
    (e) 进程管理

    On some operating systems such as Windows creating new processes is a slow operation. This means CGI applications that fork a new process on every invocation will run substantially slower on these operating systems. Running PHP in multi-threaded mode should improve response times (note: older versions of PHP are not stable in multi-threaded mode).
    在windows等的操作系统中建立一个新的进程是个缓慢的操作。这意味着CGI软件在这些系统上的调用是缓慢的。在多线程模式下运行PHP可以加快速度(注意:老版本的PHP不支持多线程模式)

    Avoid overcrowding your web server with too many unneeded processes. For example, if your server is purely for web serving, avoid running (or even installing) X-Windows on the machine. On Windows, avoid running Microsoft Find Fast (part of Office) and 3-dimensional screen savers that result in 100% CPU utilization.
    避免你的服务器被过多没有必要的进程所拥塞。例如:假如你的服务器纯粹用于网站服务,不在服务器上运行(甚至安装)X-Windows,在windows上,不运行Microsoft Find Fast (part of Office)和3D屏保可以防止100%CPU的情况

    Some of the programs that you can consider removing include unused networking protocols, mail servers, antivirus scanners, hardware drivers for mice, infrared ports and the like. On Unix, I assume you are accessing your server using SSH. Then you can consider removing:

    deamons such as telnetd, inetd, atd, ftpd, lpd, sambad
    sendmail for incoming mail
    portmap for NFS
    xfs, fvwm, xinit, X

    You can also disable at startup various programs by modifying the startup files which are usually stored in the /etc/init* or /etc/rc*/init* directory.

    Also review your cron jobs to see if you can remove them or reschedule them for off-peak periods.

    (f) Connecting to Other Servers

    一些你可以考虑移除的网络控制软件,邮件服务器,杀毒软件,鼠标驱动,红外驱动等等一些.在Unix上,我认为你是通过SSH访问的网站.那么你就可以考虑移除一些像  
    telnetd, inetd, atd, ftpd, lpd, sambad
    sendmail for incoming mail
    portmap for NFS
    xfs, fvwm, xinit, X
    之类的服务,你也可以考虑仅用一些启动时加载的项目.你可以通过修改 /etc/init* 或/etc/rc*/init*的启动配置文件来实现他们的配置.你一个可以看下你的计划任务,不需要的就移除,需要执行的就把他放在没人访问服务器的时候执行.

    If your web server requires services running on other servers, it is possible that those servers become the bottleneck. The most common example of this is a slow database server that is servicing too many complicated SQL requests from multiple web servers.
    如果你的服务器需要一些在其他服务器上运行的服去.这也许也会成为一个瓶颈.最常见的例子就是一个缓慢的或者被过多连接拥塞的数据库服务器

    When to Start Optimizing?
    什么时候开始优化?

    Some people say that it is better to defer tuning until after the coding is complete. This advice only makes sense if your programming team's coding is of a high quality to begin with, and you already have a good feel of the performance parameters of your application. Otherwise you are exposing yourselves to the risk of having to rewrite substantial portions of your code after testing.
    一些人说最好推迟到代码完成之后.这个建议指挥令你感觉到你的程序员团队有高质量的代码,你已经对你的程序表现十分满意.否则就是你认为你将在测试之后重写你的部分代码

    My advice is that before you design a software application, you should do some basic benchmarks on the hardware and software to get a feel for the maximum performance you might be able to achieve. Then as you design and code the application, keep the desired performance parameters in mind, because at every step of the way there will be tradeoffs between performance, availability, security and flexibility.

    我的建议是在你设计一个程序之前,你就应该做一些你可以做到的能够提升性能的基准。然后你开始设计软件的代码。在脑子里时刻想着这些准则,因为每一步设计都有可能是你在性能,实用性,安全性,易用性的折中方案。

    Also choose good test data. If your database is expected to hold 100,000 records, avoid testing with only a 100 record database – you will regret it. This once happened to one of the programmers in my company; we did not detect the slow code until much later, causing a lot of wasted time as we had to rewrite a lot of code that worked but did not scale.

    选择良好的测试用数据。如果你的数据库将要承载100,000条记录,避免使用只有100条记录的数据库进行测试–这样会让你后悔的.这种事情曾经在我所在的公司的一个程序员身上发生过:我们没有发现导致缓慢的代码.直到很久以后,程序执行消耗了大量时间.我们不得不重写达赖能够的数据.

    Tuning Your Web Server for PHP
    为PHP调整你的服务器

    We will cover how to get the best PHP performance for the two most common web servers in use today, Apache 1.3 and IIS. A lot

    of the advice here is relevant for serving HTML also.
    接下来我们谈谈如何在现今最流行的两个网络服务器平台 Apache 和 IIS 上让PHP取得最好的的表现。这里也有很多关于HTML的建议(??)

    The authors of PHP have stated that there is no performance nor scalability advantage in using Apache 2.0 over Apache 1.3

    with PHP, especially in multi-threaded mode. When running Apache 2.0 in pre-forking mode, the following discussion is still

    relevant (21 Oct 2003).
    PHP的作者统计过PHP在 Apache 2.0 上的表现比apache 1.3 上的表现在实际上并没有实质的优势,尤其在多线程模式下。当在apache 2.0的预

    编译模式下运行。他们的讨论结果也是类似的

    (a) Apache 1.3/2.0
    (a) Apache 1.3/2.0

    Apache is available on both Unix and Windows. It is the most popular web server in the world. Apache 1.3 uses a pre-forking

    model for web serving. When Apache starts up, it creates multiple child processes that handle HTTP requests. The initial

    parent process acts like a guardian angel, making sure that all the child processes are working properly and coordinating

    everything. As more HTTP requests come in, more child processes are spawned to process them. As the HTTP requests slow down,

    the parent will kill the idle child processes, freeing up resources for other processes. The beauty of this scheme is that it

    makes Apache extremely robust. Even if a child process crashes, the parent and the other child processes are insulated from

    the crashing child.

    Apache 在Unix和Windows都可以使用。他是世界上最流行的网络服务器。apache为网络服务使用了预编译模式(pre-forking model)。当

    apache启动时。他创建了多个等待http请求的子进程。最初的父进程(parent process)就像个守护天使,他保证那些子进程在正常工作。当出现

    更多的HTTP请求时,更多的子进程被创建。当HTTP请求减少时,空闲的子进程就会被结束,他们(子进程)占用的资源也将会被释放。这种完美

    的配置使Apache高效的运行。甚至当一个子进程崩溃时,父进程将把它和其他子进程隔离。
    The pre-forking model is not as fast as some other possible designs, but to me that it is "much ado about nothing" on a

    server serving PHP scrīpts because other bottlenecks will kick in long before Apache performance issues become significant.

    The robustness and reliability of Apache is more important.
    预编译模式并不比其他一些必要的设计/实现更快。但是对于我们来说在其他瓶颈影响apache性能之前这个是非常简单的。apache的稳定和可靠

    是更重要的。

    Apache 2.0 offers operation in multi-threaded mode. My benchmarks indicate there is little performance advantage in this

    mode. Also be warned that many PHP extensions are not compatible (e.g. GD and IMAP). Tested with Apache 2.0.47 (21 Oct 2003).
    apache 2.0 提供在多线程模式的操作。我的测试显示在这个模式下的性能只有一点点的提升。当然要提醒一下:很多PHP扩展不被兼容。

    Apache is configured using the httpd.conf file. The following parameters are particularly important in configuring child

    processes:

    APACHE 使用httpd.conf作为配置文件。下面的这些对配置子进程非常重要

    Directive
    Default
    Descrīption

    MaxClients
    256
    The maximum number of child processes to create. The default means that up to 256 HTTP requests can be handled concurrently.

    Any further connection requests are queued.

    StartServers
    5
    The number of child processes to create on startup.

    MinSpareServers
    5
    The number of idle child processes that should be created. If the number of idle child processes falls to less than this

    number, 1 child is created initially, then 2 after another second, then 4 after another second, and so forth till 32 children

    are created per second.

    MaxSpareServers
    10
    If more than this number of child processes are alive, then these extra processes will be terminated.

    MaxRequestsPerChild
    0
    Sets the number of HTTP requests a child can handle before terminating. Setting to 0 means never terminate. Set this to a

    value to between 100 to 10000 if you suspect memory leaks are occurring, or to free under-utilized resources.

    For large sites, values close to the following might be better:

    MinSpareServers 32

    MaxSpareServers 64

    对于大型网站。建议按照以下的数值进行配置

    MinSpareServers 32

    MaxSpareServers 64

    Apache on Windows behaves differently. Instead of using child processes, Apache uses threads. The above parameters are not

    used. Instead we have one parameter: ThreadsPerChild which defaults to 50. This parameter sets the number of threads that can

    be spawned by Apache. As there is only one child process in the Windows version, the default setting of 50 means only 50

    concurrent HTTP requests can be handled. For web servers experiencing higher traffic, increase this value to between 256 to

    1024.
    apache在windows上的表现不同。apache在windows使用上使用线程(threads).上面的那些参数将不会被使用。作为替代的是我们使用一个参数

    :ThreadsPerChild (默认数值为50)由于在windows版本上只有一个子进程,默认设为50意味着只有50个http并发请求。大流量的服务器请把

    这个数值提升到256至1024。<

    Other useful performance parameters you can change include:
    其他一些你应该修改的有用参数
    Directive
    Default
    Descrīption

    SendBufferSize
    Set to OS default
    Determines the size of the output buffer (in bytes) used in TCP/IP connections. This is primarily useful for congested or

    slow networks when packets need to be buffered; you then set this parameter close to the size of the largest file normally

    downloaded. One TCP/IP buffer will be created per client connection.

    KeepAlive [on|off]
    On
    In the original HTTP specification, every HTTP request had to establish a separate connection to the server. To reduce the

    overhead of frequent connects, the keep-alive header was developed. Keep-alives tells the server to reuse the same socket

    connection for multiple HTTP requests.

    If a separate dedicated web server serves all images, you can disable this option. This technique can substantially improve

    resource utilization.

    KeepAliveTimeout
    15
    The number of seconds to keep the socket connection alive. This time includes the generation of content by the server and

    acknowledgements by the client. If the client does not respond in time, it must make a new connection.

    This value should be kept low as the socket will be idle for extended periods otherwise.

    MaxKeepAliveRequests
    100
    Socket connections will be terminated when the number of requests set by MaxKeepAliveRequests is reached. Keep this to a

    high value below MaxClients or ThreadsPerChild.

    TimeOut
    300
    Disconnect when idle time exceeds this value. You can set this value lower if your clients have low latencies.

    LimitRequestBody
    0
    Maximum size of a PUT or POST. O means there is no limit.

    If you do not require DNS lookups and you are not using the htaccess file to configure Apache settings for individual

    directories you can set:

    # disable DNS lookups: PHP scrīpts only get the IP address

    HostnameLookups off

    # disable htaccess checks

    <Directory />

    AllowOverride none

    </Directory>

    If you are not worried about the directory security when accessing symbolic links, turn on FollowSymLinks and turn off

    SymLinksIfOwnerMatch to prevent additional lstat() system calls from being made:

    Options FollowSymLinks

    #Options SymLinksIfOwnerMatch

    (b) IIS Tuning
    (b) IIS 调整
    IIS is a multi-threaded web server available on Windows NT and 2000. From the Internet Services Manager, it is possible to

    tune the following parameters:
    IIS 是一个在 Windows NT 和 2000上运行的多线程网络服务器。通过IIS管理器,他可以调整以下的参数:
    Performance Tuning based on the number of hits per day.
    Determines how much memory to preallocate for IIS. (Performance Tab).

    Bandwidth throttling
    Controls the bandwidth per second allocated per web site. (Performance Tab).

    Process throttling
    Controls the CPU% available per Web site. (Performance Tab).

    Timeout
    Default is 900 seconds. Set to a lower value on a Local Area Network. (Web Site Tab)

    HTTP Compression
    HTTP 压缩
    In IIS 5, you can compress dynamic pages, html and images. Can be configured to cache compressed static html and images. By

    default compression is off.
    在IIS 5,你可以压缩动态页,html页和图片。他可以被配置为缓存静态页和图片。默认情况下压缩是关闭的

    HTTP compression has to be enabled for the entire physical server. To turn it on open the IIS console, right-click on the

    server (not any of the subsites, but the server in the left-hand pane), and get Properties. Click on the Service tab, and

    select "Compress application files" to compress dynamic content, and "Compress static files" to compress static content.

    You can also configure the default isolation level of your web site. In the Home Directory tab under Application Protection,

    you can define your level of isolation. A highly isolated web site will run slower because it is running as a separate

    process from IIS, while running web site in the IIS process is the fastest but will bring down the server if there are

    serious bugs in the web site code. Currently I recommend running PHP web sites using CGI, or using ISAPI with Application

    Protection set to high.
    你也可以单独编辑你的网站的等级。在网站管理的根目录的标签上,你可以单另定义你的等级。由于他运行在一个单独的进程下,高度自定义

    等级的网站会慢一些。尽管在IIS上运行网站一般很快,但是当代码有缺陷的时候会减慢网站的运行速度。我强烈建议PHP以CGI方式在IIS上运

    行,或者当以ISAPI模式运行时使用高等级。

    You can also use regedit.exe to modify following IIS 5 registry settings stored at the following location:
    你也可以使用 regedit.exe 直接编辑IIS 5 在注册表中的信息,他的位置是:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Inetinfo\Parameters\

    MemCacheSize
    内存缓存大小
    Sets the amount of memory that IIS will use for its file cache. By default IIS will use 50% of available memory. Increase if

    IIS is the only application on the server. Value is in megabytes.
    当IIS使用文件缓存的时候设置他使用内存的大小。默认情况下IIS将使用50%可使用的内存。只有当IIS是服务器上唯一的网络服务器的时候将

    会增长占用的内存。他增长的数量单位是MB

    MaxCachedFileSize
    Determines the maximum size of a file cached in the file cache in bytes. Default is 262,144 (256K).

    ObjectCacheTTL
    Sets the length of time (in milliseconds) that objects in the cache are held in memory. Default is 30,000 milliseconds (30

    seconds).

    MaxPoolThreads
    Sets the number of pool threads to create per processor. Determines how many CGI applications can run concurrently. Default

    is 4. Increase this value if you are using PHP in CGI mode.

    ListenBackLog
    Specifies the maximum number of active Keep Alive connections that IIS maintains in the connection queue. Default is 15, and

    should be increased to the number of concurrent connections you want to support. Maximum is 250.

    If the settings are missing from this registry location, the defaults are being used.
    如果设置错了。服务器就会使用默认数值。

    High Performance on Windows: IIS and FastCGI
    在windows上高效的 :IIS 和 FastCGI

    After much testing, I find that the best PHP performance on Windows is offered by using IIS with FastCGI. CGI is a protocol

    for calling external programs from a web server. It is not very fast because CGI programs are terminated after every page

    request. FastCGI modifies this protocol for high performance, by making the CGI program persist after a page request, and

    reusing the same CGI program when a new page request comes in.
    在多次测试之后,我发现在windows上PHP表现最好的情况是以 FastCGI方式在IIS上运行(我个人持保留意见—by achieverain)。CGI是网络服

    务器调用扩展程序的一个协议。他由于每个页面请求结束之后都回停止而不是很快。FastCGI是一种高效的协议。当出现重复的页面请求的时候

    他会恢复并返回一个相同的页面执行的结果。

    As the installation of FastCGI with IIS is complicated, you should use the EasyWindows PHP Installer. This will install PHP,

    FastCGI and Turck MMCache for the best performance possible. This installer can also install PHP for Apache 1.3/2.0.
    以FastCGI 方式安装到IIS上是很复杂的,你必须使用EasyWindows PHP Installer。他将把PHP, FastCGI 和 Turck MMCache以最优化的方式安

    装好。这个安装包也可以给Apache 1.3/2.0安装PHP。(因为这是比较古老的文章[2005年],我实际使用中发现EasyWindows PHP Installer并不

    是万灵丹,还是自己老老实实调整吧 —by achieverain)
    This section on FastCGI added 21 Oct 2003.

    PHP4's Zend Engine
    PHP4的 Zend 引擎
    The Zend Engine is the internal compiler and runtime engine used by PHP4. Developed by Zeev Suraski and Andi Gutmans, the

    Zend Engine is an abbreviation of their names. In the early days of PHP4, it worked in the following fashion:
    Zend引擎是PHP4使用的内部编译器和运行用引擎,他是由Zeev Suraski 和 Andi Gutmans开发。zend引擎是他们名字的混合体。在PHP4的早期

    ,他是以下方式运行的:

    The PHP scrīpt was loaded by the Zend Engine and compiled into Zend opcode. Opcodes, short for operation codes, are low level

    binary instructions. Then the opcode was executed and the HTML generated sent to the client. The opcode was flushed from

    memory after execution.
    PHP脚本被Zend引擎读取并被编译成Zend中间代码,中间代码比操作代码短,比二进制描述要低级。然后中间代码会被执行,Html被生成并发送

    到客户端。在执行完毕之后中间代码就被从内存中清除。

    Today, there are a multitude of products and techniques to help you speed up this process. In the following diagram, we show

    the how modern PHP scrīpts work; all the shaded boxes are optional.
    现在,有很多产品和技术帮助你加速这个过程。在以下的简述中,我们将展现当代PHP的工作方式,所有隐藏的技巧将被展现。

    PHP scrīpts are loaded into memory and compiled into Zend opcodes. These opcodes can now be optimized using an optional

    peephole optimizer called Zend Optimizer. Depending on the scrīpt, it can increase the speed of your PHP code by 0-50%.
    PHP脚本读入内存并被编译成Zend的中间代码。这些中间代码将被一个内置解析器: Zend Optimizer 优化。通过这些步骤,他可以提高你的程

    序0-50%的执行速度

    Formerly after execution, the opcodes were discarded. Now the opcodes can be optionally cached in memory using several

    alternative open source products and the Zend Accelerator (formerly Zend Cache), which is a commercial closed source product.

    The only opcode cache that is compatible with the Zend Optimizer is the Zend Accelerator. An opcode cache speeds execution by

    removing the scrīpt loading and compilation steps. Execution times can improve between 10-200% using an opcode cache.
    原本在执行结束之后,中间代码将被清除。但现在使用一些开源代码和 Zend Accelerator(原生的zend缓存,虽然不是个开源软件)
    Where to find Opcode Caches
    哪里可以找到中间代码的缓存

    Zend Accelerator: A commercial opcode cache developed by the Zend Engine team. Very reliable and robust. Visit

    http://zend.com for more information.
    Zend Accelerator: 一个由Zend 引擎团队开发的商业缓存。非常可靠和稳定。你可以访问http://zend.com去得到相关信息。

    You will need to test the following open source opcode caches before using them on production servers as their performance

    and reliability very much depends on the PHP scrīpts you run.
    你要在部署以下开源缓存之前详细测试他们。因为他们的稳定性和速度很依赖于你的PHP脚本。

    Turcke MMCache: http://turck-mmcache.sourceforge.net/ is no longer maintained. See eAccelerator, which is a branch of mmcache

    that is actively maintained (Added 28 Feb 2005).

    Alternative PHP Cache: http://apc.communityconnect.com/

    PHP Accelerator: http://www.php-accelerator.co.uk/

    AfterBurner Cache: http://www.bwcache.bware.it/

    One of the secrets of high performance is not to write faster PHP code, but to avoid executing PHP code by caching generated

    HTML in a file or in shared memory. The PHP scrīpt is only run once and the HTML is captured, and future invocations of the

    scrīpt will load the cached HTML. If the data needs to be updated regularly, an expiry value is set for the cached HTML. HTML

    caching is not part of the PHP language nor Zend Engine, but implemented using PHP code. There are many class libraries that

    do this. One of them is the PEAR Cache, which we will cover in the next section. Another is the Smarty template library.
    一个取得高性能的秘密就是不要写过快的程序,但是要避免在文件缓存或者在共享内存中生成HTML。PHP脚本只是在HTML被捕获的时候运行一次

    ,之后的调用PHP就会读取缓存的HTML。如果数据需要经常改变。缓存的HTML就需要设置一个失效时间。HTML缓存并不是PHP语言或Zend引擎中

    的一部分,但可以用PHP实现。现在有很多的类库可以做这个。其中一个就是PEAR 缓存。另一个是Smarty 模版库
    Finally, the HTML sent to a web client can be compressed. This is enabled by placing the following code at the beginning of

    your PHP scrīpt:
    最后,发送到客户端的HTML可以被压缩。这需要你在PHP代码的开始加上以下的代码
    <?php

    ob_start("ob_gzhandler");

    :
    :

    ?>

    If your HTML is highly compressible, it is possible to reduce the size of your HTML file by 50-80%, reducing network

    bandwidth requirements and latencies. The downside is that you need to have some CPU power to spare for compression.
    如果你的HTML被高度压缩,他可能将你的HTML减少50-80%的体积。这可以节省网络带宽和减少数据延迟,而代价仅仅是增加少量的CPU占用。
    HTML Caching with PEAR Cache
    PEAR Cache中的HTML缓存
    The PEAR Cache is a set of caching classes that allows you to cache multiple types of data, including HTML and images.
    PEAR Cache是一个允许包括HTML和图片在内多种类型数据的缓存类。

    The most common use of the PEAR Cache is to cache HTML text. To do this, we use the Output buffering class which caches all

    text printed or echoed between the start() and end() functions:
    PEAR Cache最常见的使用便是缓存HTML文本。我们可以在输出缓存类使用start() 和 end()输出所有缓存的数据。

    require_once("Cache/Output.php");

    $cache = new Cache_Output("file", array("cache_dir" => "cache/") );

    if ($contents = $cache->start(md5("this is a unique key!"))) {

    #
    # aha, cached data returned
    #

      print $contents;
      print "<p>Cache Hit</p>";

    } else {

    #
    # no cached data, or cache expired
    #

      print "<p>Don't leave home without it…</p>"; # place in cache
      print "<p>Stand and deliver</p>"; # place in cache
      print $cache->end(10);

    }

    Since I wrote these lines, a superior PEAR cache system has been developed: Cache Lite; and for more sophisticated

    distributed caching, see memcached (Added 28 Feb 2005).
    当我们写下这些代码之后,一个超级PREA缓存系统就被开发出来了:缩水版缓存;如果需要复杂的缓存,看看memcached 。
    The Cache constructor takes the storage driver to use as the first parameter. File, database and shared memory storage

    drivers are available; see the pear/Cache/Container directory. Benchmarks by Ulf Wendel suggest that the "file" storage

    driver offers the best performance. The second parameter is the storage driver options. The options are "cache_dir", the

    location of the caching directory, and "filename_prefix", which is the prefix to use for all cached files. Strangely enough,

    cache expiry times are not set in the options parameter.
    缓存创建的第一个参数是存储的路径,文件,数据库,共享内存都可以;看看pear/Cache/Containe的路径。Ulf Wendel的测试指出“文件”存

    储提供了最好的性能,第二个参数是存储的选项。这个选项是"cache_dir",存储缓存的路径;filename_prefix:所有缓存文件名字的前缀。

    很奇怪,缓存失效时间并不在这些参数里设置。

    To cache some data, you generate a unique id for the cached data using a key. In the above example, we used md5("this is a

    unique key!").
    为缓存以下数据,你需要为缓存数据生成唯一ID。在上面的例子中,我们使用了d5("this is a unique key!")
    The start() function uses the key to find a cached copy of the contents. If the contents are not cached, an empty string is

    returned by start(), and all future echo() and print() statements will be buffered in the output cache, until end() is

    called.
    start()函数生成一个key以寻找缓存的内容的拷贝。如果内容没有被缓存,start()将返回一个空字符串,当调用end()时所有的输出将会被写

    进缓存。
    The end() function returns the contents of the buffer, and ends output buffering. The end() function takes as its first

    parameter the expiry time of the cache. This parameter can be the seconds to cache the data, or a Unix integer timestamp

    giving the date and time to expire the data, or zero to default to 24 hours.
    end()函数返回并输出缓冲中的数据。end()函数的第一个参数就是失效时间,这个参数可以设置数据的秒(后失效)或者失效的Unix时间戳,或

    者默认的0(即24小时);
    Another way to use the PEAR cache is to store variables or other data. To do so, you can use the base Cache class:
    另一个使用PREA缓存的方式是存储变量和其他数据。为实现这个目标,你可以使用基础的缓存类:

    <?php

    require_once("Cache.php");

    $cache = new Cache("file", array("cache_dir" => "cache/") );
    $id = $cache->generateID("this is a unique key");

    if ($data = $cache->get($id)) {

      print "Cache hit.<br>Data: $data";

    } else {

      $data = "The quality of mercy is not strained…";
      $cache->save($id, $data, $expires = 60);
      print "Cache miss.<br>";

    }

    ?>

    To save the data we use save(). If your unique key is already a legal file name, you can bypass the generateID() step.

    Objects and arrays can be saved because save() will serialize the data for you. The last parameter controls when the data

    expires; this can be the seconds to cache the data, or a Unix integer timestamp giving the date and time to expire the data,

    or zero to use the default of 24 hours. To retrieve the cached data we use get().
    为了存储这些数据我们将使用save(),如果你的唯一ID已经生成,你可以跳过生成ID这一步。因为save()序列化了数据,所以他可以存储类和

    数组。最后一个参数控制着数据的实效时间;这个可以是缓存数据的失效秒数。或者UNIX时间戳。或者默认的24小时。重新取得这些缓存过的

    数据我们使用get()函数。

    You can delete a cached data item using $cache->delete($id) and remove all cached items using $cache->flush().
    你可以使用$cache->delete($id)删除一个缓存过的数据,使用$cache->flush()删除所有缓存。
    New: A faster Caching class is Cache-Lite. Highly recommended.
    提示:一个更快的缓存类是Cache-Lite,高度推荐。
    Using Benchmarks
    使用测试

    In earlier section we have covered many performance issues. Now we come to the meat and bones, how to go about measuring and

    benchmarking your code so you can obtain decent information on what to tune.
    在前面的部分我们谈论了很多方便的性能。现在我们上正餐吧,如何测试你的代码的性能以使你取得如何调整的信息。

  • show innodb status (mysql)

    2008-03-11 23:29:43

    MySQL: SHOW INNODB STATUS 详解(二)
    2007年12月18日 星期二 20:30

    当前活跃的事务:

    1. ------------
    2. TRANSACTIONS
    3. ------------
    4. Trx id counter 0 80157601
    5. Purge done for trx's n:o <0 80154573 undo n:o <0 0
    6. History list length 6
    7. Total number of lock structs in row lock hash table 0
    8. LIST OF TRANSACTIONS FOR EACH SESSION:
    9. ---TRANSACTION 0 0, not started, process no 3396, OS thread id 1152440672
    10. MySQL thread id 8080, query id 728900 localhost root
    11. show innodb status
    12. ---TRANSACTION 0 80157600, ACTIVE 4 sec, process no 3396, OS thread id 1148250464, thread declared inside InnoDB 442
    13. mysql tables in use 1, locked 0
    14. MySQL thread id 8079, query id 728899 localhost root Sending data
    15. select sql_calc_found_rows * from b limit 5
    16. Trx read view will not see trx with id>= 0 80157601, sees <0 80157597
    17. ---TRANSACTION 0 80157599, ACTIVE 5 sec, process no 3396, OS thread id 1150142816 fetching rows, thread declared inside InnoDB 166
    18. mysql tables in use 1, locked 0
    19. MySQL thread id 8078, query id 728898 localhost root Sending data
    20. select sql_calc_found_rows * from b limit 5
    21. Trx read view will not see trx with id>= 0 80157600, sees <0 80157596
    22. ---TRANSACTION 0 80157598, ACTIVE 7 sec, process no 3396, OS thread id 1147980128 fetching rows, thread declared inside InnoDB 114
    23. mysql tables in use 1, locked 0
    24. MySQL thread id 8077, query id 728897 localhost root Sending data
    25. select sql_calc_found_rows * from b limit 5
    26. Trx read view will not see trx with id>= 0 80157599, sees <0 80157595
    27. ---TRANSACTION 0 80157597, ACTIVE 7 sec, process no 3396, OS thread id 1152305504 fetching rows, thread declared inside InnoDB 400
    28. mysql tables in use 1, locked 0
    29. MySQL thread id 8076, query id 728896 localhost root Sending data
    30. select sql_calc_found_rows * from b limit 5
    31. Trx read view will not see trx with id>= 0 80157598, sees <0 80157594

    如果连接数量很大,InnoDB只能打印出其中的一部分,以维持整个列表的长度。

    Transaction id是当前事务的标识符。Purge done for trx's n:o 是回收程序启动事务的数量。如果回收程序没有运行事务,InnoDB只能回收旧版本。旧的未提交事务可能会阻塞回收进程而吃掉资源。通过查看当前事务数和 最近一次回收事务数的差值就可以发现这个问题。在某些情况下,回收程序效率很低而要维持更新率,在这种情况下差值也会增加,这时innodb_max_purge_lag 会有所帮助。"undo n:o"显示回收程序正在处理的undo日志记录数,如果当前没有回收程序运行,则该值为0。

    History list length 是在回滚段中为回收的事务数。当更新事务提交时该值增加,当回收线程运行时该值减少。

    Total number of lock structs in row lock hash table是所有事务分配的行锁结构数。该值并不等于锁住的行数-因为一个锁结构中包含很多行。

    MySQL的连接要么是not started 状态(该连接没有活动的事务),要么是ACTIVE状态(有活动事务)。注意,尽管连接是"SLEEP"状态,但事务也可以是活跃的-如果事务中有多个语句。事务状态就是事务正在做什么,如"fetching rows", "updating"。"Thread declared inside InnoDB 400"意思是线程正在InnoDB内核中运行,当前还可以有400个线程可以进来。InnoDB限制内核中的并发线程数使用变量innodb_thread_concurrency 。如果线程没在InnoDB内核中运行,状态应该是"waiting in InnoDB queue" 或"sleeping before joining InnoDB queue"。为避免太多线程竞争进入InnoDB队列,InnoDB有时会让线程先sleep,而不是wait。这样就会使InnoDB内核中的活跃线程少于innodb_thread_concurrency。这会减少线程等待进入队列的时间,可以调整innodb_thread_sleep_delay 的值,该值为微秒。

    mysql tables in use 1, locked 0 是由线程正使用的表数,和线程锁住的表数。InnoDB一般不会锁表,因此该值一般为0,除非ALTER TABLELOCK TABLES 相似的操作。

    文件I/O:

    1. --------
    2. FILE I/O
    3. --------
    4. I/O thread 0 state: waiting for i/o request (insert buffer thread)
    5. I/O thread 1 state: waiting for i/o request (log thread)
    6. I/O thread 2 state: waiting for i/o request (read thread)
    7. I/O thread 3 state: waiting for i/o request (write thread)
    8. Pending normal aio reads: 0, aio writes: 0,
    9. ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
    10. Pending flushes (fsync) log: 0; buffer pool: 0
    11. 17909940 OS file reads, 22088963 OS file writes, 1743764 OS fsyncs
    12. 0.20 reads/s, 16384 avg bytes/read, 5.00 writes/s, 0.80 fsyncs/s

    IO的四个进程(括号中的名字)。插入缓存合并,异步日志刷新,read-ahead和脏缓存刷新。如果线程准备好了则为waiting for i/o request 状态,否则它就正在执行某种操作。

    Number of pending operation is shown for each of helper threads - these are amount of operations queued for execution or being executed at the same time. Also number of pending fsync operations is displayed. For writes Innodb has to ensure data makes it to the disk - just passing it to OS cache is not enough. This is typically done by calling fsync() for modified files. Constant high values for any of these variables is indication of IO bound workload. Note however - IO requests submited by threads executing requests are not accounted here so you may have these at zeroes while workload being IO bound still.

    Next, number of file IO operations is shown as well as computed averages. This is parameters which is great for graphing and monitoring.
    "16384 avg bytes/read" shows average size of read requests. For random IO these should be 16K - page size, for full table scan or index scan read-ahead may be performed which can increase average read size significantly. So you can think about this value as read-ahead efficiency.

    CODE:
    1. -------------------------------------
    2. INSERT BUFFER AND ADAPTIVE HASH INDEX
    3. -------------------------------------
    4. Ibuf for space 0: size 1, free list len 887, seg size 889, is not empty
    5. Ibuf for space 0: size 1, free list len 887, seg size 889,
    6. 2431891 inserts, 2672643 merged recs, 1059730 merges
    7. Hash table size 8850487, used cells 2381348, node heap has 4091 buffer(s)
    8. 2208.17 hash searches/s, 175.05 non-hash searches/s

    This section shows insert buffer and adaptive hash status. First line shows status of insert buffer - segment size and free list as well as if
    there are any records is insert buffer. Next it shows how many inserts were done in insert buffer, how many recs were merged and how many merges did it took. Ratio of number of merges to number of inserts is pretty much insert buffer efficiency.

    Adaptive hash index is hash index Innodb builds for some pages to speed up row lookup replacing btree search with hash search. This section shows hash table size, number of used cells and number of buffers used by adaptive hash index. You can also see number of hash index lookups and number of non-hash index lookups which is indication of hash index efficiency.

    There is currently not much you can do to adjust adaptive hash index or insert buffer behavīor so it is pretty much for informational purposes only.

    CODE:
    1. ---
    2. LOG
    3. ---
    4. Log sequence number 84 3000620880
    5. Log flushed up to 84 3000611265
    6. Last checkpoint at 84 2939889199
    7. 0 pending log writes, 0 pending chkp writes
    8. 14073669 log i/o's done, 10.90 log i/o's/second

    Log section provides information about log subsystem of Innodb. You can see current log sequence number - which is amount of bytes Innodb has written in log files since system tablespace creation. You can also see up to which point logs have been flushed - so how much data is unflushed in log buffer as well as when last checkpoint was performed. Innodb uses fuzzy checkpointing so this line hold log sequence, all changes up to which has been flushed from buffer pool. Changes having higher log sequences may still only be recored in logs and not flushed from buffer pool so such log sequences can't be overwritten in log files. By monitoring log sequence number and value up to which logs have been flushed you can check if your innodb_log_buffer_size is optimal - if you see more than 30% of log buffer size being unflushed you may want to increase it.

    You also can see number of pending normal log writes and number of checkpoint log writes. Number of log/io operations allows to separate tablespace related IO from log related IO so you can see how much IO your log file requires. Note depending on your innodb_flush_log_at_trx_commit value your log writes may be more or less expensive. If innodb_flush_logs_at_trx_commit=2 log writes are done to OS cache, and being sequential writes these logs writes are pretty fast.

    CODE:
    1. ----------------------
    2. BUFFER POOL AND MEMORY
    3. ----------------------
    4. Total memory allocated 4648979546; in additional pool allocated 16773888
    5. Buffer pool size 262144
    6. Free buffers 0
    7. Database pages 258053
    8. Modified db pages 37491
    9. Pending reads 0
    10. Pending writes: LRU 0, flush list 0, single page 0
    11. Pages read 57973114, created 251137, written 10761167
    12. 9.79 reads/s, 0.31 creates/s, 6.00 writes/s
    13. Buffer pool hit rate 999 / 1000

    该部分描述了内存的使用情况。包括的信息有:InnoDB分配的总内存,额外分配的内存,缓存池中的页数,空闲页数,数据库页和脏页数。从这些值中 可以看出是否InnoDB缓存分配得恰当-如果总有很多空闲页,表明数据库要比内存小,可以把innoDB缓存调小些。如果空闲页为0,那么数据库页不会 和缓存容量相同,因为还要存锁、索引和其他得系统结构。

    Pending reads and writes are pending requests on buffer pool level. Innodb may merge multiple requests to one on file level so these are different. We can also see different types of IO submited by Innodb - pages to be flushed via LRU pages - dirty pages which were not accessed long time, flush list - old pages which need to be flushed by checkpointing process and single page - independent page writes.

    We can also see number of pages being read and written. Created pages is empty pages created in buffer pool for new data - when previous page content was not read to the buffer pool.

    Finally you can see buffer pool hit ratio which measures buffer pool efficiency. 1000/1000 corresponds to 100% hit rate. It is hard to tell what buffer pool hit rate is good enough - it is very workload dependent. Sometimes 950/1000 will be enough, sometimes you can see IO bound workload with hit rate of 995/1000.

    CODE:
    1. --------------
    2. ROW OPERATIONS
    3. --------------
    4. 0 queries inside InnoDB, 0 queries in queue
    5. 1 read views open inside InnoDB
    6. Main thread process no. 10099, id 88021936, state: waiting for server activity
    7. Number of rows inserted 143, updated 3000041, deleted 0, read 24865563
    8. 0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s

    Finally last section - row operations which shows activity on the row basics and some system information.

    It shows innodb thread queue status - how many threads are waiting and being active. How many read views are open inside Innodb - this is when transaction was started but no statement is currently active, state of Innodb main thread which controls scheduling of number of system operations - flushing dirty pages, checkpointing, purging, flusing logs, doing insert buffer merge. Values for "state" field are rather self explanatory.

    You can also see number of rows operation since system startup as well as average values. This is also very good values to monitor and graph - row operations is very good measure of Innodb load. Not all row operations are created equal of course and accessing of 10 byte rows is much cheaper than accessing 10MB blog, but it is still much more helpful than number of queries, which is even more different.

    One more thing to note - SHOW INNODB STATUS is not consistent - it does not correspond to some particular point in time. Different lines in SHOW INNODB STATUS populated in different point in times, so sometimes you may see a bit conflicting information. This is by design as requiring global lock to provide consitent information would cause significant overhead.


  • mysql sleep 线程过多 (摘抄)

    2008-03-11 23:28:25

    关于Apache::DBI与MYsql的持续连接太多的问题


    (这条文章已经被阅读了 324次) 时间:2002年03月08日 19:10 来源:韩伟 收藏

    关于Apache::DBI与MYsql的持续连接太多的问题大家已经研
    究了一段时间了。

    我经过这几天研究发现mod_perl提供的持续连接并不智能(也许是我还没有找到更
    好的解决方案),如果脚本中的参数和初始化的参数不一样,在内存中会出现两个
    句柄。

    Apache::DBI->connect_on_init("DBI:mysql:mysql:localhost","root","root",
    {
    PrintError => 1,
    RaiseError => 0,
    AutoCommit => 1,
    });



    my $dbh = DBI->connect("DBI:mysql:database=localhost","root","root",{
    PrintError => 1,
    RaiseError => 0,
    AutoCommit => 1,
    });

    以上两个语句实现同一个连接,返回同一种结果,但是他们的概念完全不一样。

    经过统一参数,写作方法,和格式。测试后,发现process不再无故增长,达到预
    期效果。

    事故原因分析:对于一个数据库,Apache::DBI建立一个持续连接, 如果在
    startup.pl文件中初始化的时候采用一种书写格式,参数,而脚本中
    DBI->connect又采用不同的连接方式和格式,因此假如一个以上的句柄实现同一个
    目的(连接同一个数据库)的话,最后一个连接进入时,它会企图查找最后一个相
    匹配的句柄(Handler),换句话说就是找一个参数,格式都一样的句柄。这里问题
    就出来了,内存中存在两个实现同一目的的句柄(但是格式或参数不一样),这个
    连接无法识别应该使用哪一个,所以被迫建立一个新的句柄,而导致原先的句柄继
    续Sleep(我管它叫做僵尸句柄)。

    解决方案:其实解决方法非常简单,因为昨天我也在其他操作系统上(RedHat 7)装
    了Apache+Mod_perl+Mysql,仍然出现相同的问题。根据内存分析与同伴们的测
    试。得出一个结论,当内存中连接同一数据库的句柄数在一个以上的时候,
    process会无故增加,当句柄为一个的时候,process不会增长,而采用原有内存中
    的句柄,这个就是我们想要的。为实现这一目的,我们需要统一初始化和connect
    的格式,参数和相关信息。

    关于并发处理:测试发现,句柄初始化在process中建立的5个连接完全可以人工控
    制。修改MinSpareServers(最小空闲服务器)和MaxSpareServers(最大空闲服务器)
    的数量。该数量放的越大,服务器的并发处理能力就越高。但是我们要付出的是宝
    贵的内存为代价,根据网站的点击次数合理调节该数量是最为明智的选择。如果服
    务器遇到大量并发请求,系统会自动克隆相应的句柄以适应并发的数量。当请求完
    毕后,系统将自动断开克隆的句柄以释放沉余的内存。大家看下面的例子,我采用
    ab(ApacheBench)这个工具对服务器请求5000次,并发数为100,观看mysql的句柄
    线程数,最高峰值达到50,当请求完毕后,释放沉余的句柄空闲内存,这时线程数
    量又恢复到5个(我的Apache Config File中 MaxSpareServers 设置为5)。

    D:\>ab -n 5000 -c 100 http://10.4.1.32/mod_perl/dbi.cgi
    This is ApacheBench, Version 1.3e <> apache-2.0
    Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.
    net/
    Copyright (c) 1998-2001 The Apache Software Foundation,
    http://www.apache.org/

    Benchmarking 10.4.1.32 (be patient)
    Completed 500 requests
    Completed 1000 requests
    Completed 1500 requests
    Completed 2000 requests
    Completed 2500 requests
    Completed 3000 requests
    Completed 3500 requests
    Completed 4000 requests
    Completed 4500 requests
    Finished 5000 requests
    Server Software: Apache/1.3.20
    Server Hostname: 10.4.1.32
    Server Port: 80

    Document Path: /mod_perl/dbi.cgi
    Document Length: 99 bytes

    Concurrency Level: 100
    Time taken for tests: qd.qd seconds
    Complete requests: 5000
    Failed requests: 0
    Write errors: 0
    Total transferred: 1220000 bytes
    HTML transferred: 495000 bytes
    Requests per second: 132.37 [#/sec] (mean)
    Time per request: 755.48 [ms] (mean)
    Time per request: 7.55 [ms] (mean, across all concurent requests)
    Transfer rate: 32.30 [Kbytes/sec] received

    Connnection Times (ms)
    min mean[+/-sd] median max
    Connect: 0 3 72.2 0 2964
    Processing: 80 743 3199.6 260 37263
    Waiting: 10 725 3198.1 240 37253
    Total: 80 746 3200.0 260 37263

    Percentage of the requests served within a certain time (ms)
    50% 260
    66% 320
    75% 420
    80% 490
    90% 741
    95% 3054
    98% 4606
    99% 4626
    100% 37263 (last request)

    --------------------------

    多种原因

    mysql_pconnect()/mysql_close()和mysql_connect()

    PHP.ini
    my.conf: wait_timeout/interactive_timeout

    slow_query.log

     


     

  • C/C++ mem leak tool

    2008-02-26 23:36:23

     

    用C/C++开发其中最令人头疼的一个问题就是内存管理,有时候为了查找一个内存泄漏或者一个内存访问越界,需要要花上好几天时间,如果有一款工具能够帮助我们做这件事情就好了,valgrind正好就是这样的一款工具。

    Valgrind是一款基于模拟linux下的程序调试器和剖析器的软件套件,可以运行于x86, amd64和ppc32架构上。valgrind包含一个核心,它提供一个虚拟的CPU运行程序,还有一系列的工具,它们完成调试,剖析和一些类似的任务。valgrind是高度模块化的,所以开发人员或者用户可以给它添加新的工具而不会损坏己有的结构。

    valgrind的官方网址是:http://valgrind.org

    你可以在它的网站上下载到最新的valgrind,它是开放源码和免费的。

    一、介绍

    valgrind包含几个标准的工具,它们是:

    1、memcheck

    memcheck探测程序中内存管理存在的问题。它检查所有对内存的读/写操作,并截取所有的malloc/new/free/delete调用。因此memcheck工具能够探测到以下问题:

    1)使用未初始化的内存

    2)读/写已经被释放的内存

    3)读/写内存越界

    4)读/写不恰当的内存栈空间

    5)内存泄漏

    6)使用malloc/new/new[]和free/delete/delete[]不匹配。

    2、cachegrind

    cachegrind是一个cache剖析器。它模拟执行CPU中的L1, D1和L2 cache,因此它能很精确的指出代码中的cache未命中。如果你需要,它可以打印出cache未命中的次数,内存引用和发生cache未命中的每一行代码,每一个函数,每一个模块和整个程序的摘要。如果你要求更细致的信息,它可以打印出每一行机器码的未命中次数。在x86和amd64上,cachegrind通过CPUID自动探测机器的cache配置,所以在多数情况下它不再需要更多的配置信息了。

    3、helgrind

    helgrind查找多线程程序中的竞争数据。helgrind查找内存地址,那些被多于一条线程访问的内存地址,但是没有使用一致的锁就会被查出。这表示这些地址在多线程间访问的时候没有进行同步,很可能会引起很难查找的时序问题。

    二、valgrind对你的程序都做了些什么

    valgrind被设计成非侵入式的,它直接工作于可执行文件上,因此在检查前不需要重新编译、连接和修改你的程序。要检查一个程序很简单,只需要执行下面的命令就可以了

    valgrind --tool=tool_name program_name

    比如我们要对ls -l命令做内存检查,只需要执行下面的命令就可以了

    valgrind --tool=memcheck ls -l

    不管是使用哪个工具,valgrind在开始之前总会先取得对你的程序的控制权,从可执行关联库里读取调试信息。然后在valgrind核心提供的虚拟CPU上运行程序,valgrind会根据选择的工具来处理代码,该工具会向代码中加入检测代码,并把这些代码作为最终代码返回给valgrind核心,最后valgrind核心运行这些代码。

    如果要检查内存泄漏,只需要增加--leak-check=yes就可以了,命令如下

    valgrind --tool=memcheck --leak-check=yes ls -l

    不同工具间加入的代码变化非常的大。在每个作用域的末尾,memcheck加入代码检查每一片内存的访问和进行值计算,代码大小至少增加12倍,运行速度要比平时慢25到50倍。

    valgrind模拟程序中的每一条指令执行,因此,检查工具和剖析工具不仅仅是对你的应用程序,还有对共享库,GNU C库,X的客户端库都起作用。

    三、现在开始

    首先,在编译程序的时候打开调试模式(gcc编译器的-g选项)。如果没有调试信息,即使最好的valgrind工具也将中能够猜测特定的代码是属于哪一个函数。打开调试选项进行编译后再用valgrind检查,valgrind将会给你的个详细的报告,比如哪一行代码出现了内存泄漏。

    当检查的是C++程序的时候,还应该考虑另一个选项 -fno-inline。它使得函数调用链很清晰,这样可以减少你在浏览大型C++程序时的混乱。比如在使用这个选项的时候,用memcheck检查openoffice就很容易。当然,你可能不会做这项工作,但是使用这一选项使得valgrind生成更精确的错误报告和减少混乱。

    一些编译优化选项(比如-O2或者更高的优化选项),可能会使得memcheck提交错误的未初始化报告,因此,为了使得valgrind的报告更精确,在编译的时候最好不要使用优化选项。

    如果程序是通过脚本启动的,可以修改脚本里启动程序的代码,或者使用--trace-children=yes选项来运行脚本。

    下面是用memcheck检查ls -l命令的输出报告,在终端下执行下面的命令

    valgrind --tool=memcheck ls -l

    程序会打印出ls -l命令的结果,最后是valgrind的检查报告如下:

    ==4187==

    ==4187== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 19 from 2)

    ==4187== malloc/free: in use at exit: 15,154 bytes in 105 blocks.

    ==4187== malloc/free: 310 allocs, 205 frees, 60,093 bytes allocated.

    ==4187== For counts of detected errors, rerun with: -v

    ==4187== searching for pointers to 105 not-freed blocks.

    ==4187== checked 145,292 bytes.

    ==4187==

    ==4187== LEAK SUMMARY:

    ==4187== definitely lost: 0 bytes in 0 blocks.

    ==4187== possibly lost: 0 bytes in 0 blocks.

    ==4187== still reachable: 15,154 bytes in 105 blocks.

    ==4187== suppressed: 0 bytes in 0 blocks.

    ==4187== Reachable blocks (those to which a pointer was found) are not shown.

    ==4187== To see them, rerun with: --show-reachable=yes

    这里的“4187”指的是执行ls -l的进程ID,这有利于区别不同进程的报告。memcheck会给出报告,分配置和释放了多少内存,有多少内存泄漏了,还有多少内存的访问是可达的,检查了多少字节的内存。

    下面举两个用valgrind做内存检查的例子

    例子一 (test.c):

    #include <string.h>
    
    int main(int argc, char *argv[])
    {
        char *ptr;
    
        ptr = (char*) malloc(10);
        strcpy(ptr, "01234567890");
    
        return 0;
    }

    编译程序

    gcc -g -o test test.c

    用valgrind执行命令

    valgrind --tool=memcheck --leak-check=yes ./test

    报告如下

    ==4270== Memcheck, a memory error detector.

    ==4270== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.

    ==4270== Using LibVEX rev 1606, a library for dynamic binary translation.

    ==4270== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.

    ==4270== Using valgrind-3.2.0, a dynamic binary instrumentation framework.

    ==4270== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.

    ==4270== For more details, rerun with: -v

    ==4270==

    ==4270== Invalid write of size 1

    ==4270== at 0x4006190: strcpy (mc_replace_strmem.c:271)

    ==4270== by 0x80483DB: main (test.c:8)

    ==4270== Address 0x4023032 is 0 bytes after a block of size 10 alloc'd

    ==4270== at 0x40044F6: malloc (vg_replace_malloc.c:149)

    ==4270== by 0x80483C5: main (test.c:7)

    ==4270==

    ==4270== Invalid write of size 1

    ==4270== at 0x400619C: strcpy (mc_replace_strmem.c:271)

    ==4270== by 0x80483DB: main (test.c:8)

    ==4270== Address 0x4023033 is 1 bytes after a block of size 10 alloc'd

    ==4270== at 0x40044F6: malloc (vg_replace_malloc.c:149)

    ==4270== by 0x80483C5: main (test.c:7)

    ==4270==

    ==4270== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 12 from 1)

    ==4270== malloc/free: in use at exit: 10 bytes in 1 blocks.

    ==4270== malloc/free: 1 allocs, 0 frees, 10 bytes allocated.

    ==4270== For counts of detected errors, rerun with: -v

    ==4270== searching for pointers to 1 not-freed blocks.

    ==4270== checked 51,496 bytes.

    ==4270==

    ==4270==

    ==4270== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1

    ==4270== at 0x40044F6: malloc (vg_replace_malloc.c:149)

    ==4270== by 0x80483C5: main (test.c:7)

    ==4270==

    ==4270== LEAK SUMMARY:

    ==4270== definitely lost: 10 bytes in 1 blocks.

    ==4270== possibly lost: 0 bytes in 0 blocks.

    ==4270== still reachable: 0 bytes in 0 blocks.

    ==4270== suppressed: 0 bytes in 0 blocks.

    ==4270== Reachable blocks (those to which a pointer was found) are not shown.

    ==4270== To see them, rerun with: --show-reachable=yes

    从这份报告可以看出,进程号是4270,test.c的第8行写内存越界了,引起写内存越界的是strcpy函数,

    第7行泄漏了10个字节的内存,引起内存泄漏的是malloc函数。

    例子二(test2.c)

    #include <stdio.h>
    
    int foo(int x)
    {
        if (x < 0) {
            printf("%d ", x);
        }
    
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        int x;
       
        foo(x);
    
        return 0;
    }

    编译程序

    gcc -g -o test2 test2.c

    用valgrind做内存检查

    valgrind --tool=memcheck ./test2

    输出报告如下

    ==4285== Memcheck, a memory error detector.

    ==4285== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.

    ==4285== Using LibVEX rev 1606, a library for dynamic binary translation.

    ==4285== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.

    ==4285== Using valgrind-3.2.0, a dynamic binary instrumentation framework.

    ==4285== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.

    ==4285== For more details, rerun with: -v

    ==4285==

    ==4285== Conditional jump or move depends on uninitialised value(s)

    ==4285== at 0x8048372: foo (test2.c:5)

    ==4285== by 0x80483B4: main (test2.c:16)

    ==4285==p p

    ==4285== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 12 from 1)

    ==4285== malloc/free: in use at exit: 0 bytes in 0 blocks.

    ==4285== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.

    ==4285== For counts of detected errors, rerun with: -v

    ==4285== All heap blocks were freed -- no leaks are possible.

    从这份报告可以看出进程PID是4285,test2.c文件的第16行调用了foo函数,在test2.c文件的第5行foo函数使用了一个未初始化的变量。

  • vrlgrind

    2008-02-26 23:35:10

    这段时间在对一个产品进行稳定性测试,让程序在Purify下运行,结果Purify最后出现了错误。同事告诉我有一款工具——valgrind,可以定位程序内存方面的错误,于是我下载下来,运行测试,效果很好,这个工具运行稳定,给出的结果也很清晰。
        valgrind这款工具是运行在linux下的,可以用来定位c/c++程序中内存使用方面的错误。包括:内存泄漏、使用未初始化的内存、读/写已释放的内存、读/写内存越界、使用malloc/new/new[]和free/delete/delete[]不匹配,等等。
        valgrind官方网站:http://valgrind.org/,是一款open source软件。我下载的时候最新版是3.2.3。安装过程很简单——configure,make,make install,安装后不需要任何设置,直接可以开始使用。
        valgrind运行方法很简单:valgrind --leak-check=full --track-fds=yes 程序 参数
      这样你的程序就会在valgrind监控下运行了,结果会直接输出到屏幕上。如果想把结果输出到日志文件(通常也需要这样),用如下的命令:valgrind --log-file=valgrind_log --leak-check=full --track-fds=yes 程序 参数
  • logrotate

    2008-02-26 23:22:57

    /usr/local/apache-server1/logs/* {
       weekly
       size=100M
       rotate 4
       compress

       postrotate
       /bin/kill -1 `cat /usr/local/apache/PID/httpd.pid`
       endscrīpt
    }

    向进程发送HUP信号,使其重新读取配置文件。
    因为apache是持续性运行,日志文件始终处于打开状态,执行日志轮转后,日志不会被保存到新的access_log中。而/var/log/wtmp只是在是在用户登陆系统时被打开,存入日志后文件后被关闭。
     
    ----
    sort -m -t " " -k 4 -o logall log1 log2
     
    ----
     
    安装了awstats来分析日志 用命令
    /usr/local/awstats/wwwroot/cgi-bin/perl awstats.pl -config=www.abc.com -update -lang=cn
    分析日志没有问题
    此时日志格式如下
    LogFile="/usr/local/apache/logs/access_log"
    设置
    AllowToUpdateStatsFromBrowser=1
    在页面可以点更新 且正常分析日志

    但是当日志格式设置为
    LogFile="gzip -d </usr/local/apache/logs/access_log.gz |"
    时 用命令分析日志没有问题 但是启用从浏览器中实时更新统计即AllowToUpdateStatsFromBrowser=1时
    点击更新出现Error: Command for pipe 'gzip -d </usr/local/apache/logs/access_log.gz |' failed
    Setup ('/etc/awstats/awstats.www.abc.com.conf' file, web server or permissions) may be wrong.
    Check config file, permissions and AWStats documentation (in 'docs' directory).

    虽然说做到crontab中可以更新。但总感觉不爽,也搜了些资料说是权限问题 但还是无法解决 特
    请教!看下面帮助说是权限问题
    可我的如下,还是有问题
    /usr/local/awstats/data
    drwxr-xr-x 2 daemon daemon 4096 01-15 05:21 data
    data内文件也都属主daemon 权限为755了
    # When AWStats updates its statistics, it stores results of its analysis in
    # files (AWStats database). All those files are written in the directory
    # defined by the "DirData" parameter. Set this value to the directory where
    # you want AWStats to save its database and working files into.
    # Warning: If you want to be able to use the "AllowToUpdateStatsFromBrowser"
    # feature (see later), you need "Write" permissions by web server user on this
    # directory (and "Modify" for Windows NTFS file systems).
    # Example: "/var/lib/awstats"
    # Example: "../data"
    # Example: "C:/awstats_data_dir"
    # Default: "."          (means same directory as awstats.pl)
    #
    DirData="/usr/local/awstats/data"

    # When this parameter is set to 1, AWStats add a button on report page to
    # allow to "update" statistics from a web browser. Warning, when "update" is
    # made from a browser, AWStats is ran as a CGI by the web server user defined
    # in your web server (user "nobody" by default with Apache, "IUSR_XXX" with
    # IIS), so the "DirData" directory and all already existing history files
    # awstatsMMYYYY[.xxx].txt must be writable by this user. Change permissions if
    # necessary to "Read/Write" (and "Modify" for Windows NTFS file systems).
    # Warning: Update process can be long so you might experience "time out"
    # browser errors if you don't launch AWStats enough frequently.
    # When set to 0, update is only made when AWStats is ran from the command
    # line interface (or a task scheduler).
    # Possible values: 0 or 1
    # Default: 0
    #
    AllowToUpdateStatsFromBrowser=1
     
  • apache httpd相关

    2008-01-31 23:04:09

    lsof |grep apache

    Apache是运行在Linux操作系统上的头号Web服务器。很多小地方都可以用来调整Apache的性能,并降低它对系统资源的影响。其中一个就是调整内存使用率,当然达到这一目的可能还是需要花点功夫的。

    例如,通过ps来确定httpd线程的内存使用率,可以输入下面的命令:

    # ps -U apache -u apache u
    USERPID %CPU %MEMVSZRSS TTYSTAT START TIME COMMAND
    apache130670.05.3 149704 54504 ?SOct071:53 /usr/sbin/httpd -f /etc/httpd/conf/httpd.conf -DAPACHE2
    ...

    上面这段输出显示了单个httpd进程使用了50 MB的RSS(驻留集大小)内存(或者非交换物理内存),以及149 MB的VSZ(虚拟)内存。这当然在很大程度上取决于你在Apache里加载和运行的模块数量。这决不是一个固定的数字。由于这个数字里还包含了共享库包,所以不是100%的准确。我们可以认为RSS数字的一半是httpd线程真正使用的内存数,这可能还有点保守,但是离我们的目的已经非常接近了。

    在本文里,我们假设每个httpd进程都在使用了27 MB内存。然后,你需要确定可以让httpd真正使用的内存数。根据运行在机器上的其他进程,你可能希望要求50%的物理内存都供Apache使用。在一个装有1GB内存的系统上,就有512MB的内存可以被划分为多个27MB的内存,也就是大约19个并发的httpd内存。有些人坚持认为每个httpd 线程“真正”使用大约5MB的内存,所以从理论上讲你可以把512MB的内存划分出102个并发进程供Apache使用(要记住的是,除非你的网站需要极其巨大的流量,否则这种情况是非常罕见的)。

    在默认状态下,Apache会分配最大256个并发客户端连接,或者256个进程(每一个都对应一个请求)。按照这种设置,一个流量巨大的网站会在顷刻间崩溃(即使你假设每个进程占用5MB内存,那也需要1.3GB的内存来满足请求的数量)。如果不采取其它措施,系统会通过硬盘来尝试使用交换空间以处理它无法在物理内存中完成的任务。

    其他可以调整的项目包括KeepAlive、KeepAliveTimeout和MaxKeepAliveRequests等设置。可以放在httpd.conf文件里的推荐设置有:

    ServerLimit 128MaxClients 128KeepAlive OnKeepAliveTimeout 2MaxKeepAliveRequests 100

    通过将KeepAliveTimeout从15秒减到2秒,可以增加MaxClients命令;19太小,而128要好得多。通过减少进程存活的秒数,你可以在相同的时间内允许更多的连接。

    当然,如果没有真正的测试在背后支持,数字就是毫无意义的,这就是ab的作用之所在。使用ab对Apache配置文件(MaxClients等于 256、ServerLimit等于256、KeepAliveTimeout等于15)进行调整,使其能够满足1000个请求(100个连续请求并发产生)的调整方法如下。(在执行测试的时候要确保服务器上有一个终端打开以观察系统的负载。)

    $ ab -n 1000 -c 100 -k http://yoursite.com/index.php

    现在把上面的服务器设置改为更加保守的设置,重新启动Apache,试着再次测试(总是从远程计算机上进行,而不是本机)。

    在这里的测试中,不同的设置导致执行所消耗的时间产生了一倍的差距(分别为27.8s和16.8s),但是负载的平均值为0.03和0.30。这可能会使得你的网站变得稍慢,但是会确保它不会在高负载的情况下崩溃。还要记住的是,你将需要进行多次测试,以便取得一个平均值。

    使用ab是测试调整Apache配置的一个极佳方法,应该在你每次做出影响性能的更改时使用它。

  • Thread 摘抄

    2008-01-29 22:55:48

    我在Delphi中一个TSocketServer控件看到一个东西,它的概念不是很清楚,我说说它的原理.  
           
          定义一个CacheThreadSize,   default:10,当有客户连接,Accept返回时,到一个线程数组列表中找有没有空闲的线程,有则直接激活(SetEvent)这个线程,并将Accept   SocketHandle传于它,进入与客户会话期间,会话结束后,根据这个线程的索引决定是否退出这个线程,如果它是CacheThreadSize中的线程,即是线程池的线程,则waitfor.  
          如果没有空闲的线程,则新建一个线程,这个线程是否是线程池内的线程,是根据它是第几个线程,如果超过CacheThreadSize,说明不是,会话结束后线程也结束.  
           
          这样如果有10客户连接后,如果有客户断开,那Server端并不删除线程,只是进入等待,如有另外客户连接,则激活线程,超过CacheThreadSize后,则是新建线程==>会话==>断开连接==>析构线程.
  • mysql tuning

    2008-01-20 23:55:38

    mysql性能指标

    mysqladmin extended (绝对值)
    重点去监视的值有:
    * Slave_running:如果系统有一个从复制服务器,这个值指明了从服务器的健康度
    * Threads_connected:当前客户端已连接的数量。这个值会少于预设的值,但你也能监视到这个值较大,这可保证客户端是处在活跃状态。
    * Threads_running:如果数据库超负荷了,你将会得到一个正在(查询的语句持续)增长的数值。这个值也可以少于预先设定的值。这个值在很短的时间内超过限定值是没问题的。当Threads_running值超过预设值时并且该值在5秒内没有回落时, 要同时监视其他的一些值。
    2)mysqladmin extended(计数器)
    * Aborted_clients:客户端被异常中断的数值(因为连接到mysql服务器的客户端没有正常地断开或关闭)。对于一些应用程序是没有影响的,但对于另一些应用程序可能你要跟踪该值,因为异常中断连接可能表明了一些应用程序有问题。
    * Questions:每秒钟获得的查询数量。也可以是全部查询的数量(注:可以根据你输入不同的命令会得到你想要的不同的值)。
    * Handler_*:如果你想监视底层(low-level)数据库负载,这些值是值得去跟踪的。如果Handler_read_rnd_next值相对于你认为是正常值相差悬殊,可能会告诉你需要优化或索引出问题了。Handler_rollback表明事务被回滚的查询数量。你可能想调查一下原因。
    * Opened_tables:表缓存没有命中的数量。如果该值很大,你可能需要增加table_cache的数值。典型地,你可能想要这个值每秒打开的表数量少于1或2.
    * Select_full_join: 没有主键(key)联合(Join)的执行。该值可能是零。这是捕获开发错误的好方法,因为一些这样的查询可能降低系统的性能。
    * Select_scan:执行全表搜索查询的数量。在某些情况下是没问题的,但占总查询数量该比值应该是常量(注:Select_scan除以总查询数量商应该是常数)。如果你发现该值持续增长,说明需要优化,缺乏必要的索引或其他问题。
    * Slow_queries:超过该值(--long-query-time)的查询数量,或没有使用索引查询数量。对于全部查询会有小的冲突。如果该值增长,表明系统有性能问题。
    * Threads_created:该值应该是低的。较高的值可能意味着你需要增加thread_cache的数值,或你遇到了持续增加的连接,表明了潜在的问题。
    3)mysqladmin processlist or "SHOW FULLPROCESSLIST"命令
    你可以通过使用其他的统计信息得到已连接线程数量和正在运行线程的数量,检查正在运行的查询花了多长时间是一个好主意。如果有一些长时间的查询(由于很差的构思或公式),管理员可以被通知。你可能也想了解多少个查询是在"Locked"的状态—---该值作为正在运行的查询不被计算在内而是作为非活跃的。一个用户正在等待一个数据库响应。
    4) "SHOW INNODB STATUS"命令
    该语句产生很多信息,从中你可以得到你感兴趣的。首先你要检查的就是“从最近的XX秒计算出来的每秒的平均负载”。
    * Pending normal aio reads: 该值是innodb io请求查询的大小(size)。如果该值大到超过了10—20,你可能有一些瓶颈。
    * reads/s, avg bytes/read, writes/s, fsyncs/s:这些值是io统计。对于reads/writes大值意味着io子系统正在被装载。适当的值取决于你系统的配置。
    * Buffer pool hit rate:这个命中率非常依赖于你的应用程序。当你觉得有问题时请检查你的命中率
    * inserts/s, updates/s, deletes/s, reads/s:有一些Innodb的底层操作。你可以用这些值检查你的负载情况查看是否是期待的数值范围。
    4)OS数据。查看系统状态好的工具是vmstat/iostat/mpstat,这些可以看man手册
    5)SQL" ōnclick="tagshow(event)" class="t_tag">MySQL错误日志-----在服务器正常完成初始化后,什么都不会写到错误日志中,因此任何在该日志中的信息都要引起管理员的注意。
    6)InnoDB表空间信息。InnoDB仅有的危险情况就是表空间填满----日志不会填满。检查的最好方式就是:show table status;你可以用任何InnoDB表来监视InnoDB表的剩余空间。
    ----

    我最近在网上查了一些有关优化MySql的资料,并对照MySql手册对一些调优设置进行了细结,但是由于时间比较勿忙,没来得及对这些设置进行实验/测试,你可以把这些方法应用到实际中,得出具体结论。调优方法大致如下:

     

    MySql服务器的后台管理程序,要想使用客户端程序,该程序必须运行,因为客户端通过连接服务器来访问数据库。下面让我们以服务器的系统变量和状态变量为根据,优化我们的MySql数据库服务。


    在这之前,我们需要掌握以下方法:


    查看MySql状态及变量的方法:


    Mysql> show status ——显示状态信息(扩展show status like 'XXX')
    Mysql> show variables ——显示系统变量(扩展show variables like 'XXX')
    Mysql> show innodb status ——显示InnoDB存储引擎的状态
    Shell> mysqladmin variables -u username -p password——显示系统变量
    Shell> mysqladmin extended-status -u username -p password——显示状态信息


    查看状态变量及帮助:


    Shell> mysqld --verbose --help [|more #逐行显示]


    首先,让我们看看有关请求连接的变量:


    为了能适应更多数据库应用用户,MySql提供了连接(客户端)变量,以对不同性质的用户群体提供不同的解决方案,笔者就max_connections,back_log做了一些细结,如下:


    max_connections是指MySql的最大连接数,如果服务器的并发连接请求量比较大,建议调高此值,以增加并行连接数量,当然这建立在机器能支撑的情况下,因为如果连接数越多,介于MySql会为每个连接提供连接缓冲区,就会开销越多的内存,所以要适当调整该值,不能盲目提高设值。可以过'conn%'通配符查看当前状态的连接数量,以定夺该值的大小。


    back_log是要求MySQL能有的连接数量。当主要MySQL线程在一个很短时间内得到非常多的连接请求,这就起作用,然后主线程花些时间(尽管很短)检查连接并且启动一个新线程。back_log值指出在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。如果期望在一个短时间内有很多连接,你需要增加它。也就是说,如果MySql的连接数据达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源。另外,这值(back_log)限于您的操作系统对到来的TCP/IP连接的侦听队列的大小。你的操作系统在这个队列大小上有它自己的限制(可以检查你的OS文档找出这个变量的最大值),试图设定back_log高于你的操作系统的限制将是无效的。


    优化了MySql的连接后属性后,我们需要看看缓冲区变量:


    使用MySql数据库存储大量数据(或使用复杂查询)时,我们应该考虑MySql的内存配置。如果配置MySQL服务器使用太少的内存会导致性能不是最优的;如果配置了太多的内存则会导致崩溃,无法执行查询或者导致交换操作严重变慢。在现在的32位平台下,仍有可能把所有的地址空间都用完,因此需要审视。


    计算内存使用的秘诀公式就能相对地解决这一部分问题。不过,如今这个公式已经很复杂了,更重要的是,通过它计算得到的值只是“理论可能”并不是真正消耗的值。事实上,有8GB内存的常规服务器经常能运行到最大的理论值(100GB甚至更高)。此外,你轻易不会使用到“超额因素”(它实际上依赖于应用以及配置)。一些应用可能需要理论内存的10%而有些仅需1%。
    那么,我们可以做什么呢?


    来看看那些在启动时就需要分配并且总是存在的全局缓冲吧!


    全局缓冲:
    key_buffer_size, innodb_buffer_pool_size, innodb_additional_mem_pool_size,innodb_log_buffer_size, query_cache_size


    注:如果你大量地使用MyISAM表,那么你也可以增加操作系统的缓存空间使得MySQL也能用得着。把这些也都加到操作系统和应用程序所需的内存值之中,可能需要增加32MB甚至更多的内存给MySQL服务器代码以及各种不同的小静态缓冲。这些就是你需要考虑的在MySQL服务器启动时所需的内存。其他剩下的内存用于连接。


    key_buffer_size决定索引处理的速度,尤其是索引读的速度。一般我们设为16M,通过检查状态值Key_read_requests和Key_reads,可以知道key_buffer_size设置是否合理。比例key_reads / key_read_requests应该尽可能的低,至少是1:100,1:1000更好(上述状态值可以使用'key_read%'获得用来显示状态数据)。key_buffer_size只对MyISAM表起作用。即使你不使用MyISAM表,但是内部的临时磁盘表是MyISAM表,也要使用该值。可以使用检查状态值'created_tmp_disk_tables'得知详情。


    innodb_buffer_pool_size对于InnoDB表来说,作用就相当于key_buffer_size对于MyISAM表的作用一样。InnoDB使用该参数指定大小的内存来缓冲数据和索引。对于单独的MySQL数据库服务器,最大可以把该值设置成物理内存的80%。


    innodb_additional_mem_pool_size指定InnoDB用来存储数据字典和其他内部数据结构的内存池大小。缺省值是1M。通常不用太大,只要够用就行,应该与表结构的复杂度有关系。如果不够用,MySQL会在错误日志中写入一条警告信息。


    innodb_log_buffer_size指定InnoDB用来存储日志数据的缓存大小,如果您的表操作中包含大量并发事务(或大规模事务),并且在事务提交前要求记录日志文件,请尽量调高此项值,以提高日志效率。


    query_cache_size是MySql的查询缓冲大小。(从4.0.1开始,MySQL提供了查询缓冲机制)使用查询缓冲,MySQL将SELECT语句和查询结果存放在缓冲区中,今后对于同样的SELECT语句(区分大小写),将直接从缓冲区中读取结果。根据MySQL用户手册,使用查询缓冲最多可以达到238%的效率。通过检查状态值’Qcache_%’,可以知道query_cache_size设置是否合理:如果Qcache_lowmem_prunes的值非常大,则表明经常出现缓冲不够的情况,如果Qcache_hits的值也非常大,则表明查询缓冲使用非常频繁,此时需要增加缓冲大小;如果Qcache_hits的值不大,则表明你的查询重复率很低,这种情况下使用查询缓冲反而会影响效率,那么可以考虑不用查询缓冲。此外,在SELECT语句中加入SQL_NO_CACHE可以明确表示不使用查询缓冲。


    除了全局缓冲,MySql还会为每个连接发放连接缓冲。


    连接缓冲:
    每个连接到MySQL服务器的线程都需要有自己的缓冲。大概需要立刻分配256K,甚至在线程空闲时,它们使用默认的线程堆栈,网络缓存等。事务开始之后,则需要增加更多的空间。运行较小的查询可能仅给指定的线程增加少量的内存消耗,然而如果对数据表做复杂的操作例如扫描、排序或者需要临时表,则需分配大约read_buffer_size,sort_buffer_size,read_rnd_buffer_size,tmp_table_size大小的内存空间。不过它们只是在需要的时候才分配,并且在那些操作做完之后就释放了。有的是立刻分配成单独的组块。tmp_table_size 可能高达MySQL所能分配给这个操作的最大内存空间了。注意,这里需要考虑的不只有一点 —— 可能会分配多个同一种类型的缓存,例如用来处理子查询。一些特殊的查询的内存使用量可能更大——如果在MyISAM表上做成批的插入时需要分配 bulk_insert_buffer_size 大小的内存;执行 ALTER TABLE, OPTIMIZE TABLE, REPAIR TABLE 命令时需要分配 myisam_sort_buffer_size 大小的内存。

    read_buffer_size是MySql读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySql会为它分配一段内存缓冲区。read_buffer_size变量控制这一缓冲区的大小。如果对表的顺序扫描请求非常频繁,并且你认为频繁扫描进行得太慢,可以通过增加该变量值以及内存缓冲区大小提高其性能。

    sort_buffer_size是MySql执行排序使用的缓冲大小。如果想要增加ORDER BY的速度,首先看是否可以让MySQL使用索引而不是额外的排序阶段。如果不能,可以尝试增加sort_buffer_size变量的大小。

    read_rnd_buffer_size是MySql的随机读缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySql会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但MySql会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。

    tmp_table_size是MySql的heap(堆积)表缓冲大小。所有联合在一个DML指令内完成,并且大多数联合甚至可以不用临时表即可以完成。大多数临时表是基于内存的(HEAP)表。具有大的记录长度的临时表 (所有列的长度的和)或包含BLOB列的表存储在硬盘上。如果某个内部heap(堆积)表大小超过tmp_table_size,MySQL可以根据需要自动将内存中的heap表改为基于硬盘的MyISAM表。还可以通过设置tmp_table_size选项来增加临时表的大小。也就是说,如果调高该值,MySql同时将增加heap表的大小,可达到提高联接查询速度的效果。

    当我们设置好了缓冲区大小之后,再来看看:

    table_cache所有线程打开的表的数目,增大该值可以增加mysqld需要的文件描述符的数量。每当MySQL访问一个表时,如果在表缓冲区中还有空间,该表就被打开并放入其中,这样可以更快地访问表内容。通过检查峰值时间的状态值’Open_tables’和’Opened_tables’,可以决定是否需要增加table_cache的值。如果你发现open_tables等于table_cache,并且opened_tables在不断增长,那么你就需要增加table_cache的值了(上述状态值可以使用’Open%tables’获得)。注意,不能盲目地把table_cache设置成很大的值。如果设置得太高,可能会造成文件描述符不足,从而造成性能不稳定或者连接失败。

    做了以上方面的调优设置之后,MySql应该基本能满足您需求(当然是建立在调优设置适当的情况下),我们还应该了解并注意:

    只有简单查询OLTP(联机事务处理)应用的内存消耗经常是使用默认缓冲的每个线程小于1MB,除非需要使用复杂的查询否则无需增加每个线程的缓冲大小。使用1MB的缓冲来对10行记录进行排序和用16MB的缓冲基本是一样快的(实际上16MB可能会更慢,不过这是其他方面的事了)。

    找出MySQL服务器内存消耗的峰值。这很容易就能计算出操作系统所需的内存、文件缓存以及其他应用。在32位环境下,还需要考虑到32位的限制,限制 “mysqld” 的值大约为2.5G(实际上还要考虑到很多其他因素)。现在运行 “ps aux” 命令来查看 “VSZ” 的值(MySQL 进程分配的虚拟内存)。监视着内存变化的值,就能知道是需要增加或减少当前的内存值了。

    最后来看看调优设置方法:

    安装好MySql后,配制文件应该在 ./share/mysql ("./"即MySql安装目录) 目录中,配制文件有几个,有my-huge.cnf my-medium.cnf my-large.cnf my-small.cnf。win环境下即存在于MySql安装目录中的.ini文件。不同的流量的网站和不同配制的服务器环境,当然需要有不同的配制文件了。

    一般的情况下,my-medium.cnf这个配制文件就能满足我们的大多需要;一般我们会把配置文件拷贝到 /etc/my.cnf ,win环境下则拷备到 my.ini 下即可,只需要修改这个配置文件就可以了。

  • 摘抄

    2008-01-14 23:41:46

  • JMeter j技巧集锦(二)

    2007-11-25 19:22:00

              图 4  在代理服务器组建中增加一个高斯随机定时器


      定时器将会使相应的的取样器被延迟。 延时的规则是,在上一个访问请求被响应并延时了指定的时间后,下一个被定时器影响的取样访问请求才会被发送出去。 因此, 你必须手工删除第一个取样器中自动生成的定时器,因为第一个取样器不需要定时器。

      在启动HTTP代理服务器以前,要在测试计划中增加一个线程组(thread group),在线程组中增加一个录制控制器(recording controller)用于存储生成的结果。 否则, 生成的元件将会被直接添加到工作台里。另外, 在录制控制器里增加一个HTTP请求默认值元件HTTP Request Defaults 元件 (是一个配置元件) 也很重要,这样Jmeter就不填写使用了默认值的字段。

      录制完成后, 停止HTTP 代理服务器; 在录制控制器元件上单击右键将记录的元件保存为一个文件用于以后重用,另外,不要忘了恢复浏览器的代理服务器设置。

      指定响应时间需求并校验结果

      尽管本节内容与Jmeter不是直接相关,但是Jmeter仍旧是指定响应时间需求和校验测试结果这两个负载测试评价任务互相联系的纽带。

      在web应用的环境里,响应时间指的是从提交访问请求到等到HTML结果所耗费的时间。从技术的角度看,响应时间也应包括浏览器重绘HTML页面的时间,但是浏览器一般是一块接着一块地显示而不是直接显示完整的整个页面,让人感觉响应时间要少一些。 另外,典型的情况是,负载测试工具不会考虑浏览器的重绘时间。 因此, 在实际的性能测试中,我们将考虑以上描述的情形, 如果不能确信,可以在正常的响应时间上加一个固定值,如0.5秒。

      以下是一套众所周知的确定相应时间的标准:
       ·用户将不会注意到少于0.1秒的延迟
       ·少于1秒的延迟不会中断用户的正常思维, 但是一些延迟会被用户注意到
       ·延迟时间少于10秒,用户会继续等待响应
       ·延迟时间超过10秒后,用户将会放弃并开始其他操作

      这些阀值很有名并且一般不会改变,因为是关乎人类的感知特性的。 所以要根据这些规则来设置响应时间需求, 也需要适当调整以适应实际应用。例如,亚马逊公司(Amazon.com) 的主页也遵循了以上规则,但是由于更偏重于风格上的一致,所以在响应时间上有一点损失。

      乍一看,好像有两种不同的方式来确定相应时间需求:
       ·平均响应时间(Average response time )
       ·绝对响应时间(Absolute response time);即, 所有的响应时间必须低于某一阀值

      指定平均响应时间比较简单一些(straightforward),但是由于数据变化的干扰,这个需求往往难以实现。为什么取样中的20%的响应时间要比平均值高3倍以上呢?请注意,JMeter 计算平均响应时间与图形结果监视器中的标准偏差是一致的。

      另一方面, 对绝对响应时间需求过于苛求是不实际的。 如果只有0。5%的取样不能通过测试该怎么办?如果再测一次,又会有很大的变化。 幸运的是, 使用置信区间(confidence interva)分析这种正规的统计方法可以顾及到取样变化的影响。
    在继续进行前,让我们首先回顾一些基本的统计学知识。

      中心极限定理(The central limit theorem)

      中心极限定理表明如果总体的分布有一个平均值μ和标准偏差σ,那么对于一个十分大的n(>30),其取样平均值的分布将接近于正态分布,其平均值μmean = μ ,标准偏差σmean = σ/√n。注意取样平均值的分布是正态的,而取样自身的分布不必是正态的。也就是说如果多次运行测试脚本则测试结果的平均响应时间将会是正态的。

      图 5 和图 6 分别展示了两个正态分布。 在这里横坐标是采样响应时间的均值, 总体的均值被调整到坐标的原点(shifted so the population mean is at the origin)。 图5 表明90%的时间里,采样均值位于±Zσ的区间里(percent of the time, the sampling means are within the interval ±Zσ,),这里的Z=1.645 和 σ 是标准偏差。 图 6 表明了99%的情况下的情形这时的Z=2.576。 在给定的概率下,如90%, 我们可以看到相应的Z呈现正态曲线,反之亦然。

    ---

                 Figure 5。 Z value for 90 percent

                 Figure 6。 Z value for 99 percent

      在相关资料中所列的是可提供正态曲线计算的一些网站。在这些网站,我们可以计算随意的相对区间内的概率(如,-1.5 < X < 1.5)或者在一个聚集的区域(cumulated area)内 ,(如, X < 1.5)。 也可以从下面的表中得到近似值。

      表 1  对应于给定的置信区间(confidence interval)的标准偏差范围(Standard deviation range)

      表 2  对应于给定的标准偏差范围(Standard deviation)的置信区间(confidence interval)

      置信区间(Confidence interval)

      置信区间(confidence interval)的定义是[取样平均值- Z*σ/√n, 取样平均值+ Z*σ/√n]。 例如, 如果置信区间(概率)是90%, 经查找可知Z 值是1。645, 于是置信区间就是 [取样平均值- 1。645*σ/√n, 取样平均值+ 1。645*σ/√n], 这意味着在90%的时间里, 总体平均值(population mean)(是未知的) 会落入这个置信区间内。 也就是说, 我们的测试结果是十分接近的。 如果 σ(标准偏差) 更大一些, 置信区间也会更大,这就意味着置信区间的上限就会更可能会越过可以接受的范围,即σ 越大,结果越不可信。

      响应时间需求(Response-time requirements )

      现在我们把所有的信息都归结到响应时间需求上来。首先。必须要定义性能需求,如: %95概率的置信区间的平均响应时间的上限必须小于5秒。 当然,最好有相应的需求或场景。

      在性能测试结束后,假设进分析得出结论是平均响应时间是4.5秒,标准偏差时4.9秒,样本数量是120个,然后就可以计算%95概率的置信区间了。 通过查表1,找到Z值是 1。95996。 于是置信区间就是 [4.5 – 1.95996*4.9/√120, 4.5 + 1.95996*4.9/√120], 也就是 [3.62, 5.38]。 尽管看起来这个响应时间看起来很不错,但这个结果(因为超出了需求的要求,因而)是不可接受的。 实际上, 可以检验的是即使是对于80%概率的可信区间,这个测试结果也是不能接受的。正如你所看到的,使用了置信区间分析后,会得到一个十分精确的方法来估算测试质量。

      在web应用中,为了测定某一场景的响应时间,我们一般要通过测试工具来发送多个访问请求,例如:
        登陆
        显示表单
        提交表单

      假设我们对请求3更感兴趣。为进行置信区间分析,我们需要的仅是请求3的所有样本的响应时间均值和标准偏差,而不是全部被统计的样本的。

      在Jmeter的图表结果监听器中计算的却是全部请求的响应时间均值和标准偏差。 而Jmeter的聚合报告监听器计算的是独立的采样器的响应时间均值,可惜没有计算标准偏差。
    总之, 仅仅指定响应时间均值是危险的, 因为不能反映出数据的变化。 即使响应时间均值是可以接受的,但是置信区间仅有75%,这个结果也不能令人信服。但是,使用置信区间分析还是会带来更多的确定性。

      结论

      本文讨论了以下内容:
       ·详细讲解了Jmeter 线程组在加载负载时的特别设置

       ·使用Jmeter代理服务器(Proxy Server)元件自动建立测试脚本的指导方针,其重点在于模拟用户思考时间(user think time )。

       ·置信区间分析(Confidence interval analysis), 一种我们可以用来更好地满足响应时间需求的统计分析方法

      通过使用本文提及的技术可以改善测试脚本的质量,更广泛地说,本文所讨论的内容属于是性能测试的一个工作流程的一部分, 是其中的一个较困难的部分。性能测试包括并不仅限于以下内容:

       ·编写性能测试需求
       ·选择测试情景
       ·准备测试环境
       ·编写测试脚本
       ·执行测试
       ·回顾测试脚本和测试结果 指出性能瓶颈
       ·书写测试报告

      此外, 性能测试结果,包括确定下来的瓶颈, 都需要反馈给开发团队或者架构师进行优化设计。 在这个过程中,并写测试脚本和回顾测试脚本是其中很重要的部分,要精心筹划和管理实施。凭借测试脚本指导和一个好的性能测试流程,你将会有更多的机会来在较重负载下优化软件性能。

  • JMeter 技巧集锦

    2007-11-25 19:20:01

    JMeter 是一个流行的用于负载测试的开源工具, 具有许多有用的功能元件,如线程组(thread group), 定时器(timer), 和HTTP 取样 (sampler) 元件。 本文是对JMeter 用户手册的补充,而且提供了关于使用Jmeter的一些模拟元件开发质量测试脚本的指导。

      本文同时也讨论了一项重要的内容:在指定了精确的响应时间要求后,如何来校验测试结果,特别是在采用了置信区间分析这种严格的统计方式的情况下应如何操作。请注意,我假定本文的读者们了解关于Jmeter的基础知识,本文的例子基于Jmeter2.0.3版。

      确定一个线程组的ramp-up period (Determine)

      Jmeter脚本的第一个要素是线程组(Thread Group),因此首先让我们来回顾一下。 正如图一所示,线程组需要设置以下参数:
      ·线程数量。
      ·ramp-up period。
      ·运行测试的次数。
      ·启动时间:立即或者预定的时间,如果是后者,线程组所包含的元素也要指定这个起止时间。

               图 1  JMeter 线程组(JMeter Thread Group)   

      每个线程均独立运行测试计划。因此, 线程组常用来模拟并发用户访问。如果客户机没有足够的能力来模拟较重的负载,可以使用Jmeter的分布式测试功能来通过一个Jmeter控制台来远程控制多个Jmeter引擎完成测试。

      参数 ramp-up period 用于告知JMeter 要在多长时间内建立全部的线程。默认值是0。如果未指定ramp-up period ,也就是说ramp-up period 为零, JMeter 将立即建立所有线程,假设ramp-up period 设置成T 秒, 全部线程数设置成N个, JMeter 将每隔T/N秒建立一个线程。

      线程组的大部分参数是不言自明的,只有ramp-up period有些难以理解, 因为如何设置适当的值并不容易。 首先,如果要使用大量线程的话,ramp-up period 一般不要设置成零。 因为如果设置成零,Jmeter将会在测试的开始就建立全部线程并立即发送访问请求, 这样一来就很容易使服务器饱和,更重要的是会隐性地增加了负载,这就意味着服务器将可能过载,不是因为平均访问率高而是因为所有线程的第一次并发访问而引起的不正常的初始访问峰值,可以通过Jmeter的聚合报告监听器看到这种现象。 这种异常不是我们需要的,因此,确定一个合理的ramp-up period 的规则就是让初始点击率接近平均点击率。当然,也许需要运行一些测试来确定合理访问量。

      基于同样的原因,过大的ramp-up period 也是不恰当的,因为将会降低访问峰值的负载,换句话说,在一些线程还未启动时,初期启动的部分线程可能已经结束了。   

      那么,如何检验ramp-up period I太小了或者太大了呢?首先,推测一下平均点击率并用总线程除点击率来计算初始的ramp-up period。 例如,假设线程数为100, 估计的点击率为每秒10次, 那么估计的理想ramp-up period 就是 100/10 = 10 秒。 那么,应怎样来提出一个合理的估算点击率呢?没有什么好办法,必须通过运行一次测试脚本来获得。   

      其次, 在测试计划(test plan)中增加一个聚合报告监听器,如图2所示,其中包含了所有独立的访问请求(一个samplers)的平均点击率。 第一次取样的点击率(如http请求)与ramp-up period 和线程数量密切相关。通过调整ramp-up period 可以使首次取样的奠基率接近平均取样的点击率。

                      图2 JMeter 聚合报告

      第三, 查验一下Jmeter日志(文件位置:JMeter_Home_Directory/bin) 的最后一个线程开始时第一个线程是否真正结束了,二者的时间差是否正常。

      总之,是否能确定一个适当的ramp-up time 取决于以下两条规则:
      ·第一个取样器的点击率(hit rate)是否接近其他取样器的平均值,从而能否避免ramp-up period 过小。

    --

    ·在最后一个线程启动时,第一个线程是否在真正结束了,最好二者的时间要尽可能的长,以避免ramp-up period过大。

      有时,这两条规则的结论会互相冲突。 这就意味着无法找到同时满足两条规则的合适的ramp-up period。 糟糕的测试计划通常会导致这些问题,这是因为在这样的测试计划里,取样器将不能充分地采集数据,可能因为测试计划执行时间太短并且线程会很快的运行结束。


      用户思考时间(User think time),定时器,和代理服务器(proxy server)

      在负载测试中需要考虑的的一个重要要素是思考时间(think time), 也就是在两次成功的访问请求之间的暂停时间。 有多种情形挥发导致延迟的发生: 用户需要时间阅读文字内容,或者填表, 或者查找正确的链接等。未认真考虑思考时间经常会导致测试结果的失真。例如,估计数值不恰当,也就是被测系统可以支持的最多用户量(并发用户)看起来好像要少一些等。

      Jmeter提供了一整套的计时器(timer)来模拟思考时间(think time), 但是仍旧存在一个问题:: 如何确定适当的思考时间呢?幸运的是, JMeter 提供了一个不错的答案:使用 JMeter HTTP 代理服务器(Proxy Server)元件。

      代理服务器会记录在使用一个普通的浏览器(如FireFox 或 Internet Explorer)浏览一个web应用时的操作。 另外, JMeter 在记录操作的同时会建立一个测试计划(test plan)。 这个功能能提供以下便利:

      ·不必手工建立HTTP 访问请求, 尤其是当要设置一些令人乏味的参数时(然而,非英文的参数也许不能正常工作) 。JMeter 将会录制包括隐含字段(hidden fields)在内的所有内容。

      ·在生成的测试计划中,Jmeter会包含浏览器生成的所有的 HTTP 报头,如User-Agent (e。g。, Mozilla/4。0), 或AcceptLanguage (e。g。, zh-tw,en-us;q=0。7,zh-cn;q=0。3)等。

      ·JMeter 会根据设置在录制操作的同时建立一些定时器,其延迟时间是完全根据真实的操作来设置的。

      现在让我们来看一下如何配置Jmeter的录制功能。 在JMeter 的控制台上, 在工作台(WorkBench)元件上单击右键,然后选择”add the HTTP Proxy Server “。 注意是在WorkBench 上单击右键而不是在Test Plan上, 因为现在是要为记录操作进行配置而不是要运行测试计划。 HTTP Proxy Server 的实现原理就是通过配置浏览器的代理服务器而使所有的访问请求通过JMeter发送(,因而被Jmeter把访问过程录制下来)。

      如图3所示, HTTP代理服务器(HTTP Proxy Server)元件的一些参数必须被配置:

      ·端口(port): 代理服务器的监听端口

      ·目标控制器(Target Controller): 是代理用于存储生成的数据的控制器,默认情况下,, JMeter 将会在当前的测试计划中找一个记录用的控制器用于存储,此外也可以在下拉菜单中选择任意控制起来存储,通常默认值就可以了。

      ·分组(Grouping): 确定在测试计划中如何来为生成的元件分组。 有多个选项, 一般可以选择“只存储每个组的第一个样本”,否则,将会原样录制URLs,包括包含图像和Javascrīpts脚本的页面。当然 也可以尝试一下默认值“不对样本分组”("Do not group samples"),来看一下JMeter 建立的原版的测试计划。

      ·包含模式(Patterns to Include) 和 排除模式(Patterns to Exclude) :帮助过滤一些不需要的访问请求。

               图 3  JMeter 代理服务器(Proxy Server)

      当你点击开始(Start)按钮时,代理服务器就会开始记录所接受的HTTP 访问请求。 当然,在开始记录前,要首先设置好浏览器的代理服务器设置。在代理服务器元件中可以增加一个定时器子元件(配置元件),用于告知Jmeter来在其生成的HTTP请求中自动的增加一个定时器。Jmeter会自动把实际的延迟时间存储为一个被命名为T的Jmeter变量,因此,如果在代理服务器元件里使用了高斯随机定时器,就应该在其中的固定延迟偏移(Constant Delay Offset)设置项里添上${T}(用于自动引用纪录的延迟时间),如图4所示。这是另一个节省时间的便利特性。

  • JMeter 摘抄

    2007-11-25 19:15:08

    使用 JMeter 完成常用的压力测试

    developerWorks
    文档选项
    将此页作为电子邮件发送

    将此页作为电子邮件发送



    级别: 初级

    胡 键 (jianhgreat@hotmail.com), 西安交通大学硕士

    2006 年 6 月 26 日

    本文介绍了 JMeter 相关的基本概念。并以 JMeter 为例,介绍了使用它来完成最常用的三种类型服务器,即 Web 服务器、数据库服务器和消息中间件,压力测试的方法、步骤以及注意事项。

    讲到测试,人们脑海中首先浮现的就是针对软件正确性的测试,即常说的功能测试。但是软件仅仅只是功能正确是不够的。在实际开发中,还有其它的非功能因素也起着决定性的因素,例如软件的响应速度。影响软件响应速度的因素有很多,有些是因为算法不够高效;还有些可能受用户并发数的影响。

    在众多类型的软件测试中,压力测试正是以软件响应速度为测试目标,尤其是针对在较短时间内大量并发用户的访问时,软件的抗压能力。本文以 JMeter 为例,介绍了如何使用它来完成常用的压力测试:Web 测试、数据库测试和 JMS 测试。

    概述

    JMeter 最早是为了测试 Tomcat 的前身 JServ 的执行效率而诞生的。到目前为止,它的最新版本是2.1.1,它的测试能力也不再仅仅只局限于对于Web服务器的测试,而是涵盖了数据库、JMS、Web Service、LDAP等多种对象的测试能力。在最新的 2.1.1 中,它还提供了对于 JUNIT 的测试。

    JMeter 的安装非常简单,从官方网站上下载,解压之后即可使用。运行命令在%JMETER_HOME%/bin 下,对于 Windows 用户来说,命令是 jmeter.bat。运行前请检查JMeter 的文档,查看是否具备相关的运行条件。对于最新版(即2.1.1),需要JDK的版本要求是JDK 1.4。

    JMeter 的主要测试组件总结如下:

    1. 测试计划是使用 JMeter 进行测试的起点,它是其它 JMeter 测试元件的容器。

    2. 线程组代表一定数量的并发用户,它可以用来模拟并发用户发送请求。实际的请求内容在Sampler中定义,它被线程组包含。

    3. 监听器负责收集测试结果,同时也被告知了结果显示的方式。

    4. 逻辑控制器可以自定义JMeter发送请求的行为逻辑,它与Sampler结合使用可以模拟复杂的请求序列。

    5. 断言可以用来判断请求响应的结果是否如用户所期望的。它可以用来隔离问题域,即在确保功能正确的前提下执行压力测试。这个限制对于有效的测试是非常有用的。

    6. 配置元件维护Sampler需要的配置信息,并根据实际的需要会修改请求的内容。

    7. 前置处理器和后置处理器负责在生成请求之前和之后完成工作。前置处理器常常用来修改请求的设置,后置处理器则常常用来处理响应的数据。

    8. 定时器负责定义请求之间的延迟间隔。

    JMeter的使用非常的容易,在 ONJava.com 上的文章 Using JMeter 提供了一个非常好的入门。





    回页首


    常用测试

    压力测试不同于功能测试,软件的正确性并不是它的测试重点。它所看重的是软件的执行效率,尤其是短时间内访问用户数爆炸性增长时软件的响应速度,压力测试往往是在功能测试之后进行的。在实际的开发过程中,软件潜在的效率瓶颈一般都是那些可能有多个用户同时访问的节点。

    就目前 Java EE 的平台下开发的软件来说,这种节点通常可能是:Web 服务器、数据库服务器和 JMS 服务器。它们都是请求主要发生的地点,请求频率较其它的节点要高,而且处于请求序列的关键路径之上。如果它们效率无法提高的话,对于整个软件的效率有致命的影响。而且在这些节点上一般都会发生较大规模的数据交换,有时其中还包含有业务逻辑处理,它们正是在进行压力测试时首先需要考虑的。

    本文以这三种节点为例,介绍如何使用 JMeter 来完成针对于它们的压力测试。

    Web 服务器

    对于大多数的项目来说,并不会自行开发一个Web服务器,因此Web服务器压力测试的对象实际就是--发布到Web服务器中的软件。最简单的Web测试计划只需要三个 JMeter 的测试元件,如下图:



    其中:

    • 在线程组中定义线程数、产生线程发生的时间和测试循环次数。
    • 在http请求中定义服务器、端口、协议和方法、请求路径等。
    • 表格监听器负责收集和显示结果。

    这种设置对于包含了安全机制的 web 应用是不够的,典型的 web 应用一般都会:

    1. 有一个登录页,它是整个应用的入口。当用户登录之后,应用会将用户相关的安全信息放到 session 中。

    2. 有一个 filter,它拦截请求,检查每个请求相关的 session 中是否包含有用户安全信息。如果没有,那么请求被重定向到登录页,要求用户提供安全信息。

    在这种配置下应用上面的测试计划,那么除了登录页之外的其它请求都将因为缺少用户安全信息,而使请求实际定位到登录页。如果不加断言,那么在监听器看来所有的请求都是成功。而实际上,这些请求最终都没有到达它们应该去的地方。显然,这种测试结果不是我们所期望的。

    为了成功的测试,至少有2种方法:

    • 方法一,去掉程序的安全设置,如filter,使得不需要用户安全信息也能访问受限内容;
    • 方法二,不修改程序,使用JMeter提供的"Http URL重写修饰符"或"Http Cookie管理器"。

    对于第一种方法,有其局限性:

    • 需要修改程序配置,如去掉web.xml中关于安全filter的设置。需要维护多个版本的web.xml,如压力测试和功能测试分别各自的web.xml,增加了维护成本,而且有可能会在测试之后忘记将web.xml修改回来。
    • 对于一些需要用户安全信息的页面无能为力,如某些业务审计操作需要用户安全信息来记录。因为缺少这样的信息,注定了测试的失败。如果解决为了这个问题进一步的修改程序,那么因为存在多个版本的程序,那么其维护难度将大大增加。

    虽然,第二种方法配置难度增加了,但是它不用修改程序。而且还可将测试计划保存成文件,以便重复使用。因此,选用第二种方法是较为理想的做法。下面以一个简化的例子说明使用方法二的配置步骤。

    1. 例子由以下几个文件组成:

    • AuthorizenFilter.java,过滤器负责检验session中是否存在用户信息。如果没有,那么就转向到 login.jsp。它的主要方法 doFilter 内容如下:

      public void doFilter(ServletRequest request,
                           ServletResponse response,
                           FilterChain chain)
                           throws IOException, ServletException {
          HttpServletRequest req = (HttpServletRequest)request;
          HttpServletResponse res = (HttpServletResponse)response;
          HttpSession session= req.getSession();
          User user = (User)session.getAttribute("user");
          if(null == user){
              String uri= req.getRequestURI();
              //如果请求页是登录页,不转向
              if( uri.equalsIgnoreCase("/gWeb/login.jsp")){
                  chain.doFilter(request, response);
      		} else{
                  res.sendRedirect("/gWeb/login.jsp");
      		}
      	}else{
              chain.doFilter(request, response);
          }    
      }
      

    • User.java,用户类负责记录用户的信息。为了简化,这里的登录操作只允许指定用户名和密码。主要内容如下:

      public class User {
      	private String user;
      	private String pwd;
      	public User(String user, String pwd) {
      		this.user = user;
      		this.pwd = pwd;
      	}
      	public boolean login(){
      		return user.equals("foxgem") && pwd.equals("12345678");
      	}
      	public String getUser() {
      		return user;
      	}
      	public void setUser(String user) {
      		this.user = user;
      	}
      }
      

    • Login.jsp 和welcome.jsp。其中 login.jsp 负责生成 User 对象,并调用 User 的login。当 login 返回为 true 时转向到 welcome.jsp。其验证部分的代码:

      <%
        if( request.getParameter("Submit") != null) {
      	  User ur= new User( request.getParameter("user"), request.getParameter("pwd"));
            if( ur.login()){
        	     session.setAttribute("user", ur);
               response.sendRedirect("/gWeb/welcome.jsp");
            } else{
               session.setAttribute( "LOGIN_ERROR_MSG", 
      "无效的用户,可能原因:用户不存在或被禁用。");
               response.sendRedirect("/gWeb/index.jsp");
               return;
            }
        }
      %>
      

    • web.xml,配置 filter 拦截所有访问 JSP 页面的请求:

      <filter>
          <filter-name>authorizen</filter-name>
          <filter-class>org.foxgem.jmeter.AuthorizenFilter</filter-class>
      </filter>
      <filter-mapping>
          <filter-name>authorizen</filter-name>
      	<url-pattern>*.jsp</url-pattern>
      </filter-mapping>
      

    2. 创建如下结构的Web测试计划:



    其中主要测试元件说明如下:

    • http请求默认值负责记录请求的默认值,如服务器、协议、端口等。
    • 第一个http请求,请求login.jsp,并附加验证所需要的参数(user=foxgem,pwd=12345678,Submit=Submit);其包含的响应断言验证url中包含"welcome.jsp",这一点可以从程序中反应。
    • 第二个http请求,请求是welcome.jsp;其包含的响应断言验证响应文本中包含"foxgem",它是welcome.jsp页面逻辑的一部分。
    • http cookie管理器负责管理整个测试过程中使用的cookie,它不需要设置任何属性。
    • 循环控制器设置发送第二个请求的循环次数,表格监听器负责收集和显示第二个请求的测试结果。

    启动测试计划之后,执行的顺序是:首先,第一个请求登录页进行登录;成功登录之后,使用循环控制器执行第二个请求。请求welcome.jsp时,响应断言用来验证是否确实是welocme.jsp来处理请求,而不是因为其它页。在这个测试计划中需要注意的是http cookie管理器。正是由于它的作用,使得第二个请求能顺利的发送到welcome.jsp进行处理,而不是因为缺少用户安全信息转发到login.jsp。

    在这个例子中,我们并没有在程序中使用cookie(使用的是session),那么http cookie管理器怎么会起作用呢?这是因为在servlet/jsp规范中对于session的状态跟踪有2种方式:

    • 使用cookie,保留和传递sessionid。它不要求程序对于url有什么特殊的处理,但是要求浏览器允许cookie。在这个例子中,就是这种情形。
    • 使用url重写,每次显式的在浏览器和服务器之间传递sessionid。它要求程序对url进行编码,对浏览器没有要求。

    对于第二种情形,可以使用JMeter前置管理器中的http url重写修饰符来完成。对于Tomcat,Session参数是jsessionid,路径扩展使用";"。使用url编码时需要注意,必须将浏览器的cookie功能关闭。因为url编码函数,如encodeURL,会判断是否需要将sessionid编码到url中。当浏览器允许cookie时,就不会进行编码。

    如果cookie而不是session来保存用户安全信息,那么直接使用http cookie管理器就行了。此时,需要将使用的cookie参数和值直接写到管理器中,由它负责管理。对于其它的cookie使用,也是如此操作。

    登录问题解决之后,对于 Web 服务器的测试就没什么难点了。剩下的就是根据实际需要,灵活运用相关的测试组件搭建编写的测试计划。(当然,对于安全问题还有其它的使用情景。在使用时需要明确:JMeter 是否支持,如果支持使用哪种测试组件解决。)

    数据库服务器

    数据库服务器在大多数企业项目中是不可缺少的,对于它进行压力测试是为了找出:数据库对象是否可以有效地承受来自多个用户的访问。这些对象主要是:索引、触发器、存储过程和锁。通过对于SQL语句和存储过程的测试,JMeter 可以间接的反应数据库对象是否需要优化。

    JMeter 使用 JDBC 发送请求,完成对于数据库的测试。一个数据库测试计划,建立如下结构即可:



    其中:

    • JDBC连接配置,负责配置数据库连接相关的信息。如:数据库url、数据库驱动类名、用户名和密码等等。在这些配置中,"绑定到池的变量名"(Variable Name Bound to Pool)是一个非常重要的属性,这个属性会在JDBC请求中被引用。通过它, JDBC请求和JDBC连接配置建立关联。(测试前,请将所需要的数据库驱动放到JMeter的classpath中)。
    • JDBC请求,负责发送请求进行测试。
    • 图形结果,收集显示测试结果。

    在实际的项目中,至少有2种类型的JDBC请求需要关注:select语句和存储过程。前者反应了select语句是否高效,以及表的索引等是否需要优化;后者则是反应存储过程的算法是否高效。它们如果效率低下,必然会带来响应上的不尽如人意。对于这两种请求,JDBC请求的配置略有区别:

    • Select语句





    • 存储过程





    如果对于Oracle,如果测试的是函数,那么也可以使用select语句来进行配置,此时可以使用:select 函数(入参) from dual形式的语句来测试,其中dual是oracle的关键字,表示哑表。对于其它厂商的数据库产品,请查找手册。

    JMS服务器

    MOM 作为消息数据交换的平台,也是影响应用执行效率的潜在环节。在 Java 程序中,是通过 JMS 与 MOM 进行交互的。作为 Java 实现的压力测试工具,JMeter 也能使用 JMS 对应用的消息交换和相关的数据处理能力进行测试。这一点应该不难理解,因为在整个测试过程中,JMeter 测试的重点应该是消息的产生者和消费者的本身能力,而不是 MOM本身。

    根据 JMS 规范,消息交换有2种方式:发布/订阅和点对点。JMeter针对这两种情形,分别提供了不同的Sampler进行支持。以下MOM我们使用ActiveMQ 3.2.1,分别描述这两种消息交换方式是如何使用 JMeter 进行测试。

    1. 测试前的准备(两种情况都适用)

    JMeter 虽然能使用 JMS 对 MOM 进行测试,但是它本身并没有提供JMS需要使用的包。因此,在测试之前需要将这些包复制到 %JMETER_HOME%/lib 下。对于 ActiveMQ 来说,就是复制 %ACTIVEMQ_HOME%/lib。%ACTIVEMQ_HOME%/optional 是可选包,可根据实际情况来考虑是否复制。

    JMeter 在测试时使用了 JNDI,为了提供 JNDI 提供者的信息,需要提供 jndi.properties。同时需要将 jndi.properties 放到 JMeter 的 classpath 中,建议将它与 bin下的 ApacheJMeter.jar 打包在一起。对于 ActiveMQ,jndi.properties 的示例内容如下:


    java.naming.factory.initial = org.activemq.jndi.ActiveMQInitialContextFactory
    java.naming.provider.url = tcp://localhost:61616
    
    #指定connectionFactory的jndi名字,多个名字之间可以逗号分隔。
    #以下为例:
    #对于topic,使用(TopicConnectionFactory)context.lookup("connectionFactry")
    #对于queue,(QueueConnectionFactory)context.lookup("connectionFactory")
    connectionFactoryNames = connectionFactory
    
    #注册queue,格式:
    #queue.[jndiName] = [physicalName]
    #使用时:(Queue)context.lookup("jndiName"),此处是MyQueue
    queue.MyQueue = example.MyQueue
    
    #注册topic,格式:
    # topic.[jndiName] = [physicalName]
    #使用时:(Topic)context.lookup("jndiName"),此处是MyTopic
    topic.MyTopic = example.MyTopic
    

    2. 发布/订阅

    在实际测试时,发布者和订阅者并不是需要同时出现的。例如,有时我们可能想测试单位时间内消息发布者的消息产生量,此时就不需要消息发布者,只需要订阅者就可以了。本例为了说明这两种Sampler的使用,因此建立如下的测试计划:



    其中JMS Publisher和JMS Subscriber的属性:选择"使用jndi.properties",连接工厂是connectionFactory,主题是MyTopic,其它使用默认配置。对于JMS Publisher,还需提供测试用的文本消息。

    启动ActiveMQ,运行测试计划。如果配置正确,那么与ActiveMQ成功连接之后,在JMeter的后台会打印出相关信息。在测试过程中,JMeter 后台打印可能会出现java.lang.InterruptedException 信息,这个是正常现象,不会影响测试过程和结果。这一点可以从 bin 下的 jmeter.log 看出。

    3. 点对点

    对于点对点,JMeter只提供了一种Sampler:JMS Point-to-Point。在例子中,建立如下图的测试计划:



    其中:Communication style是Request Only。对于另一种风格:Request Response,会验证收到消息的JMS Header中的JMSCorrelationID,以判断是否是对请求消息的响应。





    回页首


    结论

    本文介绍了如何使用JMeter完成最常用的三种类型服务器的压力测试,这三种类型的压力测试涵盖了很大一部分的使用情形,然而需要记住的是工具毕竟是工具。效果好不好,关键还是在于使用的人。而且,对于压力测试,测试计划的好坏是关键。针对不同的情况,分析后有针对的进行测试,比起拿枪乱打、无的放矢显然要高效得多。



    参考资料

  • apache日志的cronolog轮循(转贴)

    2007-11-04 14:37:59

    多服务器的日志合并统计——apache日志的cronolog轮循


    内容摘要:你完全不必耐心地看完下面的所有内容,因为结论无非以下2点:
    1 用 cronolog 干净,安全地轮循apache“日”志
    2 用 sort -m 合并排序多个日志

    根据个人的使用经历:
    1 先介绍apache日志的合并方法;
    2 然后根据由此引出的问题说明日志轮循的必要性和解决方法,介绍如何通过cronolog对apache日志进行轮循;
    中间有很多在设计日志合并过程中一些相关工具的使用技巧和一些尝试的失败经历……
    我相信解决以上问题的路径不止这一条途径,以下方案肯定不是最简便或者说成本最低的,希望能和大家有更多的交流。


    多服务器日志合并统计的必要性

    越来越多大型的WEB服务使用DNS轮循来实现负载均衡:使用多个同样角色的服务器做前台的WEB服务,这大大方便了服务的分布规划和扩展性,但多个服务器的分布使得日志的分析统计也变得有些麻烦。如果使用webalizer等日志分析工具对每台机器分别做日志统计:
    1 会对数据的汇总带来很多麻烦,比如:统计的总访问量需要将SERVER1 SERVER2...上指定月份的数字相加。
    2 会大大影响统计结果中唯一访客数unique visits,唯一站点数unique sites的等指标的统计,因为这几个指标并非几台机器的代数相加。

    统一日志统计所带来的好处是显而易见的,但如何把所有机器的统计合并到一个统计结果里呢?
    首先也许会想:多个服务器能不能将日志记录到同一个远程文件里呢?我们不考虑使用远程文件系统记录日志的问题,因为带来的麻烦远比你获得的方便多的多……
    因此,要统计的多个服务器的日志还是:分别记录=>并通过一定方式定期同步到后台=>合并=>后用日志分析工具来进行分析。

    首先,要说明为什么要合并日志:因为webalizer没有将同一天的多个日志合并的功能
    先后运行
    webalizer log1
    webalizer log2
    webalizer log3
    这样最后的结果是:只有log3的结果。

    能不能将log1<<log2<<log3简单叠加呢?
    因为一个日志的分析工具不是将日志一次全部读取后进行分析,而且流式的读取日志并按一定时间间隔,保存阶段性的统计结果。因此时间跨度过大(比如2条日志间隔超过5分钟),一些日志统计工具的算法就会将前面的结果“忘掉”。因此, log1<<log2<<log3直接文件连接的统计结果还是:只有log3的统计结果。

    多台服务日志合并问题:把多个日志中的记录按时间排序后合并成一个文件

    典型的多个日志文件的时间字段是这样的:
    log1 log2 log3
    00:15:00 00:14:00 00:11:00
    00:16:00 00:15:00 00:12:00
    00:17:00 00:18:00 00:13:00
    00:18:00 00:19:00 00:14:00
    14:18:00 11:19:00 10:14:00
    15:18:00 17:19:00 11:14:00
    23:18:00 23:19:00 23:14:00

    日志合并必须是按时间将多个日志的交叉合并。合并后的日志应该是:
    00:15:00 来自log1
    00:15:00 来自log2
    00:16:00 来自log1
    00:17:00 来自log3
    00:18:00 来自log2
    00:19:00 来自log1
    ....

    如何合并多个日志文件?
    下面以标准的clf格式日志(apache)为例:
    apche的日志格式是这样的:
    %h %l %u %t \"%r\" %>s %b
    具体的例子:
    111.222.111.222 - - [03/Apr/2002:10:30:17 +0800] "GET /index.html HTTP/1.1" 200 419

    最简单的想法是将日志一一读出来,然后按日志中的时间字段排序
    cat log1 log2 log3 |sort -k 4 -t " "
    注释:
    -t " ": 日志字段分割符号是空格
    -k 4: 按第4个字段排序,也就是:[03/Apr/2002:10:30:17 +0800] 这个字段
    -o log_all: 输出到log_all这个文件中

    但这样的效率比较低,要知道。如果一个服务已经需要使用负载均衡,其服务的单机日志条数往往都超过了千万级,大小在几百M,这样要同时对多个几百M的日志进行排序,机器的负载可想而之……
    其实有一个优化的途径,要知道:即使单个日志本身已经是一个“已经按照时间排好序“的文件了,而sort对于这种文件的排序合并提供了一个优化合并算法:使用 -m merge合并选项,
    因此:合并这样格式的3个日志文件log1 log2 log3并输出到log_all中比较好方法是:
    sort -m -t " " -k 4 -o log_all log1 log2 log3
    注释:
    -m: 使用 merge优化算法

    注意:合并后的日志输出最好压缩以后再发给webalizer处理
    有的系统能处理2G的文件,有的不能。有的程序能处理大于2G的文件,有的不能。尽量避免大于2G的文件,除非确认所有参与处理的程序和操作系统都能处理这样的文件。所以输出后的文件如果大于2G,最好将日志gzip后再发给webalizer处理:大于2G的文件分析过程中文件系统出错的可能性比较大,并且gzip后也能大大降低分析期间的I/O操作。

    日志的按时间排序合并就是这样实现的。

    日志的轮循机制

    让我们关心一下数据源问题:webalizer其实是一个按月统计的工具,支持增量统计:因此对于大型的服务,我可以按天将apache的日志合并后送给 webalizer统计。WEB日志是如何按天(比如每天子夜00:00:00)截断呢?
    如果你每天使用crontab:每天0点准时将日志备份成access_log_yesterday
    mv /path/to/apache/log/access_log /path/to/apache/log/access_log_yesterday
    的话:你还需要:马上运行一下:apache restart 否则:apache会因为的日志文件句柄丢失不知道将日志记录到哪里去了。这样归档每天子夜重启apache服务会受到影响。
    比较简便不影响服务的方法是:先复制,后清空
    cp /path/to/apache/log/access_log /path/to/apache/log/access_log_yesterday
    echo >/path/to/apache/log/access_log

    严肃的分析员会这样做发现一个问题:
    但cp不可能严格保证严格的0点截断。加入复制过程用了6秒,截断的access_log_yesterday日志中会出现复制过程到00:00:06期间的日志。对于单个日志统计这些每天多出来几百行日志是没有问题的。但对于多个日志在跨月的1天会有一个合并的排序问题:
    [31/Mar/2002:59:59:59 +0800]
    [31/Mar/2002:23:59:59 +0800]
    [01/Apr/2002:00:00:00 +0800]
    [01/Apr/2002:00:00:00 +0800]

    要知道[01/Apr/2002:00:00:00 这个字段是不可以进行“跨天排序”的。因为日期中使用了dd/mm/yyyy,月份还是英文名,如果按照字母排序,很有可能是这样的结果:排序导致了日志的错误
    [01/Apr/2002:00:00:00 +0800]
    [01/Apr/2002:00:00:00 +0800]
    [01/Apr/2002:00:00:00 +0800]
    [01/Apr/2002:00:00:00 +0800]
    [01/Apr/2002:00:00:00 +0800]
    [01/Apr/2002:00:00:00 +0800]
    [01/Apr/2002:00:00:00 +0800]
    [31/Mar/2002:59:59:59 +0800]
    [31/Mar/2002:59:59:59 +0800]
    [31/Mar/2002:23:59:59 +0800]
    [31/Mar/2002:59:59:59 +0800]
    [31/Mar/2002:23:59:59 +0800]

    这些跨天过程中的非正常数据对于webalizer等分析工具来说简直就好像是吃了一个臭虫一样,运行的结果是:它可能会把前一个月所有的数据都丢失!因此这样的数据会有很多风险出现在处理上月最后一天的数据的过程中。

    问题的解决有几个思路:
    1 事后处理:
    。所以一个事后的处理的方法是:用grep命令在每月第1天将日志跨月的日志去掉,比如:
    grep -v "01/Apr" access_log_04_01 > access_log_new

    修改SORT后的日志:所有跨天的数据去掉。也许对日志的事后处理是一个途径,虽然sort命令中有对日期排序的特殊选项 -M(注意是:大写M),可以让指定字段按照英文月份排序而非字母顺序,但对于apache日志来说,用SORT命令切分出月份字段很麻烦。(我尝试过用 "/"做分割符,并且使用“月份” “年:时间”这两个字段排序)。虽然用一些PERL的脚本肯定可以实现,但最终我还是放弃了。这不符合系统管理员的设计原则:通用性。并且你需要一直问自己:有没有更简单的方法呢?
    还有就是将日志格式改成用TIMESTAMP(象SQUID的日志就没有这个问题,它的日志本身就是使用TIMESTAMP做时间时间戳的),但我无法保证所有的日志工具都能识别你在日期这个字段使用了特别的格式。

    2 优化数据源:
    最好的办法还是优化数据源。将数据源保证按天轮循,同一天的日志中的数据都在同一天内。这样以后你无论使用什么工具(商业的,免费的)来分析日志,都不会因为日志复杂的预处理机制受到影响。

    首先可能会想到的是控制截取日志的时间:比如严格从0点开始截取日志,但在子夜前1分钟还是后一分钟开始截取是没有区别的,你仍然无法控制一个日志中有跨 2天记录的问题,而且你也无法预测日志归档过程使用的时间。
    因此必须要好好考虑一下使用日志轮循工具的问题,这些日志轮循工具要符合:
    1 不中断WEB服务:不能停apache=>移动日志=>重启apache
    2 保证同一天日志能够按天轮循:每天一个日志00:00:00-23:59:59
    3 不受apache重启的影响:如果apache每次重启都会生成一个新的日志是不符合要求的
    4 安装配置简单

    首先考虑了apache/bin目录下自带的一个轮循工具:rotatelogs 这个工具基本是用来按时间或按大小控制日志的,无法控制何时截断和如何按天归档。
    然后考虑logrotate后台服务:logrotate是一个专门对各种系统日志(syslogd,mail)进行轮循的后台服务,比如SYSTEM LOG,但其配置比较复杂,放弃,实际上它也是对相应服务进程发出一个-HUP重启命令来实现日志的截断归档的。

    在apache的FAQ中,推荐了经过近2年发展已经比较成熟的一个工具cronolog:安装很简单:configure=>make=> make install

    他的一个配置的例子会让你了解它有多么适合日志按天轮循:对httpd.conf做一个很小的修改就能实现:
    TransferLog "|/usr/sbin/cronolog /web/logs/%Y/%m/%d/access.log"
    ErrorLog "|/usr/sbin/cronolog /web/logs/%Y/%m/%d/errors.log"

    然后:日志将写入
    /web/logs/2002/12/31/access.log
    /web/logs/2002/12/31/errors.log
    午夜过后:日志将写入
    /web/logs/2003/01/01/access.log
    /web/logs/2003/01/01/errors.log
    而2003 2003/01 和 2003/01/01 如果不存在的话,将自动创建

    所以,只要你不在0点调整系统时间之类的话,日志应该是完全按天存放的(00:00:00-23:59:59),后面日志分析中: [31/Mar/2002:15:44:59这个字段就和日期无关了,只和时间有关。

    测试:考虑到系统硬盘容量,决定按星期轮循日志
    apache配置中加入:
    #%w weekday
    TransferLog "|/usr/sbin/cronolog /path/to/apache/logs/%w/access_log"

    重启apache后,除了原来的CustomLog /path/to/apche/logs/access_log继续增长外,系统log目录下新建立了 3/目录(测试是在周3),过了一会儿,我忽然发现2个日志的增长速度居然不一样!
    分别tail了2个日志才发现:
    我设置CustomLog使用的是combined格式,就是包含(扩展信息的),而TransferLog使用的是缺省日志格式,看了apache的手册才知道,TransferLog是用配置文件中离它自己最近的一个格式作为日志格式的。我的httpd.conf里写的是:
    LogFormat ..... combined
    LogFormat ... common
    ...
    CustomLog ... combined
    TransferLog ...

    所以TrasferLog日志用的是缺省格式,手册里说要让TRANSFER日志使用指定的格式需要:
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
    TransferLog "|/usr/local/sbin/cronolog /path/to/apache/logs/%w/access_log"

    重启,OK,日志格式一样了。
    这样的设置结果其实是同时在logs目录下分别记录2个日志access_log和%w/access_log,能不能只记录%w/下的日志那?
    查apache手册,更简单的方法:直接让CustomLog输出到cronolog归档日志,并且还能指定格式。
    CustomLog "|/usr/local/sbin/cronolog /path/to/apache/logs/%w/access_log" combined

    最后是一个日志同步的问题。

    任务:每天凌晨找到前1天的日志,另存一个文件准备发送到服务器上。
    比如我要保留前1周的日志:每天复制前1天的日志到指定目录,等待日志服务器来抓取:
    /bin/cp -f /path/to/apache/logs/`date -v-1d +%w`/access_log /path/for/backup/logs/access_log_yesterday

    在FREEBSD上使用以下命令
    date -v-1d +%w
    注释:
    -v-1d: 前1天,而在GNU/Linux上这个选项应该是date -d yesterday
    +%w: weekday,由于使用的都是标准时间函数库,所有工具中的WEEKDAY定义都是一样的 0-6 => 周日-周六

    注意:
    写到CRONTAB里的时候"%"前面需要加一个"\"转义:每天0点5分进行一次日志归档,
    另外一个问题就是在cront中需要用:rm -f {} ; 而不是rm -f {}\;
    5 0 * * * /bin/cp /path/to/logs/`date -v-1d +\%w`/access_log /path/to/for_sync/logs/access_yesterday
    37 10 * * * /usr/bin/find /home/apache/logs/ -name access_log -mtime +1 -exec /bin/rm -f {} ;

    首次开始cronolog日志统计是周3,一周以后日志又将轮循回3/access_log
    但这次日志是追加到3/access_log还是重新创建一个文件呢?>>access_log or >access_log?
    我测试的结果是日志将被追加:
    [01/Apr/2002:23:59:59 +0800]
    [01/Apr/2002:23:59:59 +0800]
    [08/Apr/2002:00:00:00 +0800]
    [08/Apr/2002:00:00:00 +0800]

    肯定是不希望每次日志还带着上周的数据的并重复统计一次的(虽然对结果没影响),而且这样%w/下的日志不是也越来越多了吗?
    解决方法1 把每天的cp改成mv
    解决方法2 每天复制完成后:删除6天以前的access_log日志
    find /path/to/apache/logs -name access_log -mtime +6 -exec rm -f {}\;
    多保留几天的日志还是有必要的:万一日志分析服务器坏了一天呢?

    以下是把apache安装在/home/apache下每天统计的一个脚本文件:
    #!/bin/sh

    #backup old log
    /bin/cp -f /home/apache/logs/`date -d yesterday +%w`/access_log /home/apache/logs/access_log_yesterday

    #remove old log
    /usr/bin/find /home/apache/logs -name access_log -mtime +6 -exec rm -f {}\;

    #analysis with webalizer
    /usr/local/sbin/webalizer

    总结:
    1 用 cronolog 干净,安全地轮循日志
    2 用 sort -m 排序合并多个日志


    参考资料:

    日志分析统计工具:
    http://directory.google.com/Top/Computers/Software/Internet/Site_Management/Log_Analysis/

    Apche的日志设置:
    http://httpd.apache.org/docs/mod/mod_log_config.html

    Apache的日志轮循:
    http://httpd.apache.org/docs/misc/FAQ.html#rotate

    Cronolog
    http://www.cronolog.org

    Webalizer
    http://www.mrunix.net/webalizer/
    Webalzer的Windows版
    http://www.medasys-lille.com/webalizer/

    AWStats的使用简介
    http://www.chedong.com/tech/awstats.html

  • apache 容量规划(转贴)

    2007-11-04 14:33:56

    Apache 1.3.x的安装与配置笔记


    内容摘要:

    Apache是一个历史悠久并且功能十分强大的WEB服务器,但其丰富的功能对于一个新手来说往往不知道从何下手。我个人感觉Apache的设计充分体现了模块化设计的优势,通过在动态模块加载(DSO)模式下的安装,任何子应用模块都可以通过配置文件的简单修改进行积木式的灵活配置。安装的过程可以从简单的静态html服务开始,一个模块一个模块的学习使用。从单纯的HTML静态服务(core),到复杂的动态页面服务(core + php, core + resin, core + php + mod_gzip, core + resin + mod_expire)。

    本文主要从简化安装==>性能调优==>维护方便的角度,介绍了WEB服务的规划、HTTPD安装/应用模块配置、升级/维护等过程。让Apache和PHP,Resin等应用模块的独立升级,完全互不影响。

    1. WEB应用容量规划:根据硬件配置和WEB应用的特点进行WEB服务的规划及一些简单的估算公式;
    2. Apache安装过程:apache的通用的简化安装选项,方便以后的应用的模块化配置;
      修改 HARD_SERVER_LIMIT:
      vi /path/to/apache_src/src/include/httpd.h
      #define HARD_SERVER_LIMIT 2560 <===将原来的 HARD_SERVER_LIMIT 256 后面加个“0”
      apache编译:
      ./configure --prefix=/home/apache --enable-shared=max --enable-module=most
    3. 可选应用模块/工具的安装:php resin mod_gzip mod_expire及各个模块之间的配合;
      mod_php安装:./configure --with-apxs=/home/apache/bin/apxs --enable-track-vars --with-mysql
      mod_resin安装:./configure --with-apxs=/home/apache/bin/apxs
      mod_gzip安装:修改Makefile中的 apxs路径:然后make make install
      工具:日志轮循工具cronolog安装:http://www.cronolog.org
    4. 升级/维护:看看通用和模块化的安装过程如何简化了日常的升级/维护工作;
      按照以上的方法:系统管理员和应用管理员的职责可以清楚的分开,互相独立。
      系统安装:系统管理员的职责就是安装好一台DSO模式的Apache,然后COLON即可,
      应用安装:由应用管理员负责具体应用所需要的模块,比如PHP Resin等,并设置httpd.conf中相关的配置。
      系统升级:系统管理员:升级操作系统/升级Apache
      应用升级:应用管理员:升级应用模块,PHP Resin等。

    WEB应用的容量规划

    Apache主要是一个内存消耗型的服务应用,我个人总结的经验公式:
    apache_max_process_with_good_perfermance < (total_hardware_memory / apache_memory_per_process ) * 2
    apache_max_process = apache_max_process_with_good_perfermance * 1.5

    为什么会有一个apache_max_process_with_good_perfermance和apache_max_process呢?原因是在低负载下系统可以使用更多的内存用于文件系统的缓存,从而进一步提高单个请求的响应速度。在高负载下,系统的单个请求响应速度会慢不少,而超过 apache_max_process,系统会因为开始使用硬盘做虚拟内存交换空间而导致系统效率急剧下降。此外,同样的服务:2G内存的机器的 apache_max_process一般只设置到1G内存的1.7倍,因为Apache本身会因为进程过多导致性能下降。

    例子1:
    一个apache + mod_php的服务器:一个apache进程一般需要4M内存
    因此在一个1G内存的机器上:apache_max_process_with_good_perfermance < (1g / 4m) * 2 = 500
    apache_max_process = 500 * 1.5 = 750
    所以规划你的应用让服务尽量跑在500个进程以下以保持比较高的效率,并设置Apache的软上限在800个。

    例子2:
    一个apache + mod_resin的服务器: 一个apache进程一般需要2M内存
    在一个2G内存的机器上:
    apache_max_process_with_good_perfermance < (2g / 2m ) * 2 = 2000
    apache_max_process = 2000 * 1.5 = 3000

    以上估算都是按小文件服务估算的(一个请求一般大小在20k以下)。对于文件下载类型站点,可能还会受其他因素:比如带宽等的影响。

    Apache安装过程

    服务器个数的硬上限HARD_SERVER_LIMIT的修改:
    在Apache的源代码中缺省的最大进程数是256个,需要修改apache_1.3.xx/src/include/httpd.h
    #ifndef HARD_SERVER_LIMIT
    #ifdef WIN32
    #define HARD_SERVER_LIMIT 1024
    #elif defined(NETWARE)
    #define HARD_SERVER_LIMIT 2048
    #else
    #define HARD_SERVER_LIMIT 2560 <===将原来的HARD_SERVER_LIMIT 256 后面加个“0”
    #endif
    #endif

    解释:
    Apache缺省的最大用户数是256个:这个配置对于服务器内存还是256M左右的时代是一个非常好的缺省设置,但随着内存成本的急剧下降,现在大型站点的服务器内存配置一般比当时要高一个数量级不止。所以256个进程的硬限制对于一台1G内存的机器来说是太浪费了,而且Apache的软上限 max_client是受限于HARD_SERVER_LIMIT的,因此如果WEB服务器内存大于256M,都应该调高Apache的 HARD_SERVER_LIMIT。根据个人的经验:2560已经可以满足大部分小于2G内存的服务器的容量规划了(Apache的软上限的规划请看后面)。

    Apache的编译:以下通用的编译选项能满足以后任意模块的安装
    ./configure --prefix=/another_driver/apache/ --enable-shared=max --enable-module=most
    比如:
    ./configure --prefix=/home/apache/ --enable-shared=max --enable-module=most

    解释:
    --prefix=/another_driver/apache/:建议将apache服务安装在另外一个驱动设备上的目的在于硬盘往往是一个系统使用寿命最低的设备,因此:将服务数据和系统完全分开,不仅能提高了数据的访问速度,更重要的,大大方便系统升级,应用备份和恢复过程。

    --shared-module=max:使用动态加载方式载入子模块会带来5%的性能下降,但和带来的配置方便相比更本不算什么:比如模块升级方便,系统升级风险降低,安装过程标准化等

    --enable-module=most:用most可以将一些不常用的module编译进来,比如后面讲到的mod_expire是就不在 apache的缺省常用模块中

    如果不想build so, 也可以这样:
    ./configure \
    "--with-layout=Apache" \
    "--prefix=/path/to/apache" \
    "--disable-module=access" \
    "--disable-module=actions" \
    "--disable-module=autoindex" \
    "--disable-module=env" \
    "--disable-module=imap" \
    "--disable-module=negotiation" \
    "--disable-module=setenvif" \
    "--disable-module=status" \
    "--disable-module=userdir" \
    "--disable-module=cgi" \
    "--disable-module=include" \
    "--disable-module=auth" \
    "--disable-module=asis"

    但结果会发现,这样编译对服务性能只能有微小的提高(5%左右),但却失去了以后系统升级和模块升级的灵活性,无论是模块还是Apache本身升级都必须把Apache和PHP的SOURCE加在一起重新编译。

    apache的缺省配置文件一般比较大:可以使用去掉注释的方法精简一下:然后再进入具体的培植过程能让你更快的定制出你所需要的。
    grep -v "#" httpd.conf.default >httpd.conf

    需要修改的通用项目有以下几个:

    #服务端口,缺省是8080,建议将整个Apache配置调整好后再将服务端口改到正式服务的端口
    Port 8080 => 80

    #服务器名:缺省没有
    ServerName name.example.com

    #最大服务进程数:根据服务容量预测设置
    MaxClients 256 => 800

    #缺省启动服务后的服务进程数:等服务比较平稳后,按平均负载下的httpd个数设置就可以
    StartServers 5 => 200

    不要修改:
    以前有建议说修改:
    MinSpareServers 5 => 100
    MaxSpareServers 10 => 200

    但从我的经验看来:缺省值已经是非常优化的了,而且让Apache自己调整子共享进程个数还是比较好的。

    特别修改:
    在solaris或一些比较容易出现内存泄露的应用上:
    MaxRequestsPerChild 0 =>3000

    应用模块和工具的安装配置:

    由于使用模块动态加载的模式,所以可以方便的通过简单的配置调整来把Apache定制成你需要的:最好把不常用模块全部清除(无论处于安全还是效率)。
    比如:对于静态页面服务器:就什么其他子模块都不加载,对于PHP应用就加上PHP模块,对于JAVA应用就把Resin模块加载上。而且各种模块的插拔非常简单,这样调试过程中就可以简单的通过注释掉不需要的模块,而不用重新编译。

    一般说来,可以不需要的模块包括:
    #LoadModule env_module libexec/mod_env.so
    #LoadModule negotiation_module libexec/mod_negotiation.so
    #LoadModule status_module libexec/mod_status.so
    #server side include已经过时了
    #LoadModule includes_module libexec/mod_include.so
    #不需要将没有缺省index文件的目录下所有文件列出
    #LoadModule autoindex_module libexec/mod_autoindex.so
    #尽量不使用CGI:一直是Apache安全问题最多的地方
    #LoadModule cgi_module libexec/mod_cgi.so
    #LoadModule asis_module libexec/mod_asis.so
    #LoadModule imap_module libexec/mod_imap.so
    #LoadModule action_module libexec/mod_actions.so
    #不使用安全认证可以大大提高访问速度
    #LoadModule access_module libexec/mod_access.so
    #LoadModule auth_module libexec/mod_auth.so
    #LoadModule setenvif_module libexec/mod_setenvif.so

    最好保留的有:
    #用于定制log格式
    LoadModule config_log_module libexec/mod_log_config.so
    #用于增加文件应用的关联
    LoadModule mime_module libexec/mod_mime.so
    #用于缺省index文件:index.php等
    LoadModule dir_module libexec/mod_dir.so

    可用可不用的有:
    #比如:需要在~/username/下调试php可以将
    LoadModule userdir_module libexec/mod_userdir.so
    #比如:需要将以前的URL进行转向或者需要使用CGI scrīpt-alias
    LoadModule alias_module libexec/mod_alias.so

    常用的模块:
    最常用的可能就是php和JAVA应用服务器的前端,此外,从性能上讲利用mod_gzip可以减少40%左右的流量,减少机器用于传输的负载,而 mod_expires可以减少10%左右的重复请求,让重复的用户对指定的页面请求结果都CACHE在本地,根本不向服务器发出请求。

    建议将所有MODULE的配置都放到相应模块的配置内部:<IfModule some_module.c>some_module config </IfModule>

    PHP的安装:
    /path/to/php_src/configure --with-apxs=/path/to/apache/bin/apxs --with-other-modules-you-need
    需要修改的配置:
    AddType application/x-httpd-php .php .php3 .any_file_in_php

    resin的安装设置:
    /path/to/resin/src/configure --with-apxs=/path/to/apache/bin/apxs

    具体的resin设置放在另外一个文件中:比如/home/resin/conf/resin.conf
    <IfModule mod_caucho.c>
    CauchoConfigFile /path/to/apache/conf/resin.conf
    </IfModule>

    mod_expires的安装配置:
    <IfModule mod_expires.c>
        ExpiresActive on
        ExpiresByType image/gif "access plus 1 month"
        ExpiresByType text/css "now plus 1 month"
        ExpiresDefault "now plus 1 day"
    </IfModule>

    注释:
    所有的.gif文件1个月以后过期
    所有的文件缺省1天以后过期

    mod_gzip的安装
    http://www.chedong.com/tech/compress.html

    日志的轮循:cronolog的安装和设置

    cronolog可以非常整齐的将日志按天轮循存储
    缺省编译安装到/usr/local/bin/下,只需要将配置改成:

    CustomLog "|/usr/local/sbin/cronolog /home/apache/logs/%w/access_log" combined

    日志将按天截断并存放在以星期为目录名的目录下:比如:log/1是周一,log/5是周五, log/0是周日

    用gzip压缩每天的日志:
    30 4 * * * /usr/bin/gzip -f /home/apache/logs/`date -d yesterday +%w`/access_log

    日志的定期删除:
    30 5 * * */usr/bin/find /home/apache/logs/ -name access_log.gz -mtime +3 |xargs -r /bin/rm -f

    升级维护

    由于使用动态模块加载方式(DSO模式)安装Apache,Apache的HTTPD核心服务和应用模块以及应用模块之间都变的非常灵活,建议将所有独立模块的配置都放在
    <IfModule mod_name>
    CONFIGURATIONS..
    </IfModule>
    里,这样配置非常容易通过屏蔽某个模块来进行功能调整:比如:
    #AddModule mod_gzip.c
    就屏蔽了mod_gzip,而其他模块不首任何影响。

    安装和维护过程:

    • 系统安装:系统管理员的职责就是安装系统和一个按照DSO模式安装的Apache,然后COLON。
    • 应用安装:由应用管理员负责具体应用所需要的模块并设置HTTPD。
    • 系统升级:系统管理员:升级系统/升级Apache
    • 应用升级:应用管理员:升级应用模块:PHP CAUCHO等
    • 系统备份/恢复:如果Apache不在缺省的系统盘上,只需要将Apache目录备份就可以了,遇到系统分区的硬件问题直接使用预先准备好的系统COLON,再直接将Apache所在物理盘恢复就行了。
    系统管理员:Apache的最简化安装 OS + Apache (httpd core only)
    应用管理员:应用模块定制 纯静态页面服务
    core
    PHP动态页面
    core+so
    +php
    JAVA应用
    core+so
    +caucho
    +ssl
    应用例子: www.example.com
    image.example.com
    bbs.example.com mall.example.com


    例子:Apache和PHP模块的独立升级。

    如果Apache是按照以下方式安装:
    ./configure --prefix=/home/apache --enable-shared=max --enable-module=most
    PHP是按照以下方式安装:
    ./configure --with-apxs=/home/apache/bin/apxs --enable-track-vars --with-mysql

    以后单独升级Apache的时候,仍然是:
    ./configure --prefix=/home/apache --enable-shared=max --enable-module=most
    make
    su
    #/home/apache/bin/apachectl stop
    #make install

    单独升级php时,仍然是:
    ./configure --with-apxs=/home/apache/bin/apxs --enable-track-vars --with-mysql
    make
    su
    #/home/apache/bin/apachectl stop
    #make install
    #/home/apache/bin/apachectl start

    基于反相代理的WEB加速:
    squid和mod_proxy都可以实现反相代理加速。而基于缓存的代理加速比起原有WEB服务,速度会有数量级的提升。

    小提示:

    Apache安装后,缺省根目录下没有但很有用的2个文件:

    • favicon.ico: favicon.ico是一个16x16的站点图标文件,如果浏览器发现有这个文件,在地址栏中会用这个图标替换调浏览器的网页图标。IE6和 MOZILLA等主流浏览器都支持这个功能。
      例如: http://www.chedong.com/favicon.ico
    • robots.txt: 用于告诉搜索引擎的爬虫程序(spider)网站那些页面可以被索引,那些不可以。
      具体说明请参考:http://www.robotstxt.org/wc/robots.html

    参考文档:

    Apache项目
    http://httpd.apache.org

    PHP
    http://www.php.net

    Resin
    http://www.caucho.com

    mod_gzip
    http://sourceforge.net/projects/mod-gzip/

    Cronolog
    http://www.cronolog.org

    mod_expires
    http://httpd.apache.org/docs/mod/mod_expires.html

    面向搜索引擎的CMS设计:
    http://www.chedong.com/tech/cms.html

  • Jmeter 摘抄

    2007-10-28 21:02:13

    zz 大部分转自 jackie的测试生活和人文生活读本---一个很好的blog

    作为一个纯 JAVA 的GUI应用,JMeter 对于CPU和内存的消耗还是很惊人的,所以当需要模拟数以千计的并发用户时,使用单台机器模拟所有的并发用户就有些力不从心,甚至还会引起JAVA内存溢出的错误。不过,JMeter 也可以像 LoadRunner 一样通过使用多台机器运行所谓的 Agent 来分担 Load Generator 自身的压力,并借此来获取更大的并发用户数。根据 JMeter官方文档的署名,你需要自己完成这个配置,不过不用担心,这将非常简单 ^_^

    1.在所有期望运行 JMeter 作为 Load Generator 的机器上安装 JMeter,并确定其中一台机器作为 Controller,其他的机器作为 Agent。然后运行所有 Agent 机器上的JMeter-server.bat文件——假定我们使用两台机器 192.168.0.1 和 192.168.0.2 作为 Agent;

    2.在Controller 机器的 JMeter 安装目录下找到 bin 目录,再找到 JMeter.properties 这个文件,使用记事本或者其他文字编辑工具打开它;

    3.在打开的文件中查找“remote_hosts=”这个字符串,你可以找到这样一行“remote_hosts=127.0.0.1”。其中的 127.0..0.1 表示运行 JMeter Agent 的机器,这里需要修改为“remote_hosts=192.168.0.1:1099,192.168.0.2:1099”——其中的 1099为端口号。一般资料显示1644 为 JMeter 的 Controller 和 Agent 之间进行通讯的默认 RMI 端口号,但是我在测试的过程中发现设置为1644运行不成功。

    在设置中还会出现一个问题,就是rmiregistry文件找不到,一般只要设置一下Java_home和JMeter_home就能成功,或者一个很野蛮的方法是可以直接将rmiregistry.exe放到%JMeter_home%bin下。

    ----

    2.4.3 Non-GUI Mode (Command Line mode)

    For non-interactive testing, you may choose to run JMeter without the GUI. To do so, use the following command options

    -n This specifies JMeter is to run in non-gui mode

    -t [name of JMX file that contains the Test Plan].

    -l [name of JTL file to log sample results to].

    -r Run all remote servers specified in JMeter.properties (or remote servers specified on command line by overriding properties)

    The scrīpt also lets you specify the optional firewall/proxy server information:

    -H [proxy server hostname or ip address]

    -P [proxy server port]

    Example : JMeter -n -t my_test.jmx -l log.jtl -H my.proxy.server -P 8000

    ----

    在 JMeter 压力测试工具中使用变量

    Apache JMeter( http://jakarta.apache.org/jmeter/ )是来自 Apache Jakarta 项目的一个压力测试工具, 目前版本2.0.3, JMeter 支持 HTTP, FTP, SOAP/XML-RPC, JDBC 等多种目标的压力测试(参见下图).



    关于 JMeter 的一般使用在它的官方主页和其它网站可以搜索到不少文章, 但是很少看到如何使用一些动态内容(比如在 HTTP 请求中使用变量作为参数)的文章, 最近因为工作需要, 在这方面做了一些摸索, 总结如下.

    0.测试项目概述

    为了尝试如何使用变量, 我们首先需要建立一个测试项目, 在这里使用了 Buffalo (一种 AJAX 技术, 详细资料参见 http://www.amowa.net/buffalo/index.html ), 或者可以看一下我下载的这个文件(att:在 JMeter 压力测试工具中使用变量.Buffalo-info.zip)中的说明和例子. 目前 Buffalo 还不是很稳定, 但是建立一个测试环境已经足够了, 而且很方便.

    我们建立的这个例子叫做 "buffalo-jmeter", 将这个压缩文件(att:在 JMeter 压力测试工具中使用变量.buffalo-jmeter.zip)中的 buffalo-jmeter.war 文件复制到 Tomcat(我用的是 Tomcat 5.0.30) 的 webapps 目录下, 待 Tomcat 自动发布完成之后就可以通过 http://localhost:8080/buffalo-jmeter/ (假设是发布在本地的Tomcat上) 访问测试页面(如下图).



    在这个例子中我们假设一个业务: 首先通过 getToken() 获得一个凭证, 然后通过这个凭证使用 getOrder() 去申请一个订单, 凭证上存在时间记录, 如果超过设定的时间(例子中是10秒)后这个凭证就失效而无法用于申请订单了(在上图中的对话框正说明了这种情形).

    1.测试中遇到的问题

    首先我们需要知道 Buffalo 其实是一种 XML-RPC 技术, 所以我们可以使用 JMeter 的 SOAP/XML-RPC Request 这个 Sampler 进行测试, 但是为了方便快捷进行测试, 以下两个问题需要解决:

    1. 如果测试服务器发生变化, 如何方便的一次性改变所有请求的 URL 地址;
    2. 如上一节所述, 10秒钟后凭证会失效, 因此我们在测试 getOrder() 的时候不能输入固定的凭证号, 应该每10秒左右获取一个新的凭证, 这样操作的难点在于如何自动让 JMeter 得到新获得的凭证号并应用到 getOrder() 请求中.

    2.静态变量(用户定义的变量)

    JMeter 允许对一个测试计划(*.jmx)设置用户定义的变量, 因此我们可以把象 URL 等需要统一修改的值作为变量定义起来(如下图);


    变量在使用时可以使用 ${变量名} 的方式引用, 如下图:



     

    3.从 Response 中获得数据

    可以使用 JMeter 提供的 后置处理器(Post Processers) --> 正则表达式提取器 (Regular Expression Extractor) 从返回的结果中取得数据, 在确定 getToken() 请求的返回值是类似下列的 XML 之后,

    <?xml version="1.0" encoding="utf-8"?>
    <burlap:reply xmlns:burlap="http://www.amowa.net/burlap/">
        <string>TK1119466440468</string>
    </burlap:reply>

    我们可以使用正则表达式 "<string>(.*)<\/string>" 来提取我们需要的凭证号.
    首先我们可以使用Javascrīpt 正则表达式测试页面来测试一下这个正则表达式是如何被执行的(如下图):
    可以看到执行结果中, 我们需要的凭证号处于"array[1]"的位置.


    这样使用"正则表达式提取器"(如下图), 注意图中的"引用名称"就可以认为是存放提取出来的数据的变量名:



    提取出来的变量可以这样被引用(如下图), 其中"_g1"代表"group number"(参见 JMeter 的联机帮助: ... [refname]_g# ... ... and # is the group number, where group 0 is the entire match, group 1 is the match from the first set of parentheses, etc.)



     

    4.测试结果分析

    我们使用3个线程同时对测试项目进行压力测试(如下图)



    通过对结果的分析我们看到了提取出来的变量确实在起作用, 而且, 这个变量是每个线程各自独立的(如下图)


     


    后记

     

     

    在 JMeter 压力测试工具中使用函数(Function)

    文章 JMeter 压力测试工具中使用变量 中谈到了可以 JMeter 中使用变量来方便进行压力测试, 此外, JMeter 还支持测试计划中使用函数, 下图是 JMeter 提供的"函数助手对话框":


    通过"函数助手对话框"中的"帮助"按钮可以查找到相关函数的帮助.



     

    实现方式

    下面使用一个例子来说明如何使用函数, 如下面的几张图所示, 例子使用了 JMeter 提供的 "Java请求" 这个 Sampler.

      • <1>这里设置了一个 Label "JavaTest001", 以便运行结果中区分当前请求
      • <2>${_ _javascrīpt((new Date()).getTime(),timestamp)}就是 JMeter 中使用函数 "_ _javascrīpt" 的方法
      • <3>这里演示了如何使用上一条中 "_ _javascrīpt" 函数中产生的结果 "timestamp"
      • <4>这里使用了另外一个 JMeter 函数 "_ _threadNum", 用以获得运行是线程编号
      • <1>这里设置了一个 Label "JavaTest002", 以便与"Java请求01"中的运行结果分开
      • <2>这里演示了如何再次使用"Java请求01"中 "_ _javascrīpt" 函数中产生的结果 "timestamp"


     

    结果分析

      • 从这个结果可以看到 "_ _javascrīpt" 函数的测试结果, 以及如何使用这个函数执行时产生的结果 "timestamp"(=1120144567828)
      • 从这个结果中可以看到另外一个函数 "_ _threadNum" 的使用效果
      • 从这个结果中可以看到"Java请求01"中 "_ _javascrīpt" 函数中产生的结果 "timestamp" 是如何被引用 "Java请求02" 中的


     

    其它

    -----

    在 LR 中是有一个“网页细分图”的,通过这个图,你可以比较容易的区分哪些请求的响应时间最长,如果响应时间过程,是消耗在server处理的时候,还是消耗在网络传输过程中——也就是所谓的 Server time 和 Network time。
    JMeter 并没有提供这么详细的区分——至少目前尚未发现,但是在 JMeter 的执行结果中也有一个字段可以利用一下。如果想看到这一项,首先要设置将 JMeter 运行结果保存到 XML 格式。

    JMeter.properties 中找到
    JMeter.save.saveservice.output_format=csv   改为
    JMeter.save.saveservice.output_format=xml

    重新启动 JMeter ,执行一个脚本并保存测试结果。
    使用任何一个文本编辑工具打开 .jtl 文件,内容如下:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <testResults version="1.2">
     3 <httpSample t="2969" lt="1906" ts="1159349557390" s="true" lb="http://jackei.cnblogs.com/" rc="200" rm="OK" tn="Thread Group 1-1" dt="text" ng="5" na="5"/>
     4 <httpSample t="2797" lt="1719" ts="1159349557609" s="true" lb="http://jackei.cnblogs.com/" rc="200" rm="OK" tn="Thread Group 1-2" dt="text" ng="5" na="5"/>
     5 <httpSample t="2625" lt="1594" ts="1159349558015" s="true" lb="http://jackei.cnblogs.com/" rc="200" rm="OK" tn="Thread Group 1-4" dt="text" ng="5" na="5"/>
     6 <httpSample t="2843" lt="1812" ts="1159349557812" s="true" lb="http://jackei.cnblogs.com/" rc="200" rm="OK" tn="Thread Group 1-3" dt="text" ng="5" na="5"/>
     7 <httpSample t="2687" lt="1110" ts="1159349558218" s="true" lb="http://jackei.cnblogs.com/" rc="200" rm="OK" tn="Thread Group 1-5" dt="text" ng="5" na="5"/>
     8 <httpSample t="844" lt="391" ts="1159349560374" s="true" lb="http://jackei.cnblogs.com/" rc="200" rm="OK" tn="Thread Group 1-1" dt="text" ng="5" na="5"/>
     9 <httpSample t="843" lt="437" ts="1159349560406" s="true" lb="http://jackei.cnblogs.com/" rc="200" rm="OK" tn="Thread Group 1-2" dt="text" ng="4" na="4"/>
    10 <httpSample t="781" lt="422" ts="1159349560640" s="true" lb="http://jackei.cnblogs.com/" rc="200" rm="OK" tn="Thread Group 1-4" dt="text" ng="3" na="3"/>
    11 <httpSample t="782" lt="391" ts="1159349560905" s="true" lb="http://jackei.cnblogs.com/" rc="200" rm="OK" tn="Thread Group 1-5" dt="text" ng="2" na="2"/>
    12 <httpSample t="1188" lt="485" ts="1159349560655" s="true" lb="http://jackei.cnblogs.com/" rc="200" rm="OK" tn="Thread Group 1-3" dt="text" ng="1" na="1"/>
    13 
    14 </testResults>
    15 


    找到 lt 这一项。

    结合 JMeter 的几篇文章和 email,解释一下 lt 的意思。
    lt = latency time (ms)

    JMeter 中执行一个脚本时,大概的过程如下:

    Start timer
    Send Request
    Wait for data
    Initial (first) response packet occurs - this is latency
    more data
    ...
    end of response
    Stop timer - this is the response time


     


    这里可以看到 lt 是接收到响应的第一个包的时间。

    而上面的 XML 文件中,t 这一项表示的是 elapsed time。也就是一个请求从发出到收到完整的响应的时间。
    那么 lt 就相当于 LR 中的 Server time,而 t-lt 就相当于 LR 中的 Netwrok time。

  • apache 性能调优-2.2版中文手册(转)

    2007-10-28 12:37:39

    硬件和操作系统

    影响web服务器性能的最大的因素是内存。一个web服务器应该从不使用交换机制,因为交换产生的滞后使用户总感觉"不够快",所以用户就可能去按"停止"和"刷新",从而带来更大的负载。你可以,也应该,控制MaxClients的设置,以避免服务器产生太多的子进程而发生交换。这个过程很简单:通过top命令计算出每个Apache进程平均消耗的内存,然后再为其它进程留出足够多的内存。

    其他因素就很普通了,装一个足够快的CPU,一个足够快的网卡,几个足够快的硬盘,这里说的"足够快"是指能满足实际应用的需求。

    操作系统是很值得关注的又一个因素,已经被证实的很有用的经验有:

    • 选择能够得到的最新最稳定的版本并打好补丁。近年来,许多操作系统厂商都提供了可以显著改善性能的TCP协议栈和线程库。

    • 如果你的操作系统支持sendfile()系统调用,则务必安装带有此功能的版本或补丁(译者注:Linux2.4内核支持sendfile()系统调用,但2.6内核已经不再支持;对Solaris8的早期版本,则需要安装补丁)。在支持sendfile的系统中,Apache2可以更快地发送静态内容而且占用较少的CPU时间。

    top

    运行时的配置

    HostnameLookups 和其他DNS考虑

    在Apache1.3以前的版本中,HostnameLookups默认被设为 On 。它会带来延迟,因为对每一个请求都需要作一次DNS查询。在Apache1.3中,它被默认地设置为 Off 。如果需要日志文件提供主机名信息以生成分析报告,则可以使用日志后处理程序logresolve ,以完成DNS查询,而客户端无须等待。

    推荐你最好是在其他机器上,而不是在web服务器上执行后处理和其他日志统计操作,以免影响服务器的性能。

    如果你使用了任何"Allow from domain"或"Deny from domain"指令(也就是domain使用的是主机名而不是IP地址),则代价是要进行两次DNS查询(一次正向和一次反向,以确认没有作假)。所以,为了得到最高的性能,应该避免使用这些指令(不用域名而用IP地址也是可以的)。

    注意,可以把这些指令包含在<Location /server-status>段中使之局部化。在这种情况下,只有对这个区域的请求才会发生DNS查询。下例禁止除了.html.cgi以外的所有DNS查询:

    HostnameLookups off
    <Files ~ "\.(html|cgi)$">
    HostnameLookups on
    </Files>

    如果在某些CGI中偶尔需要DNS名称,则可以调用gethostbyname来解决。

    FollowSymLinks 和 SymLinksIfOwnerMatch

    如果网站空间中没有使用 Options FollowSymLinks ,或使用Options SymLinksIfOwnerMatch ,Apache就必须执行额外的系统调用以验证符号连接。文件名的每一个组成部分都需要一个额外的调用。例如,如果设置了:

    DocumentRoot /www/htdocs
    <Directory />
    Options SymLinksIfOwnerMatch
    </Directory>

    在请求"/index.html"时,Apache将对"/www"、"/www/htdocs"、"/www/htdocs/index.html"执行lstat()调用。而且lstat()的执行结果不被缓存,因此对每一个请求都要执行一次。如果确实需要验证符号连接的安全性,则可以这样:

    DocumentRoot /www/htdocs
    <Directory />
    Options FollowSymLinks
    </Directory>

    <Directory /www/htdocs>
    Options -FollowSymLinks +SymLinksIfOwnerMatch
    </Directory>

    这样,至少可以避免对DocumentRoot路径的多余的验证。注意,如果AliasRewriteRule中含有DocumentRoot以外的路径,那么同样需要增加这样的段。为了得到最佳性能,应当放弃对符号连接的保护,在所有地方都设置FollowSymLinks ,并放弃使用SymLinksIfOwnerMatch

    AllowOverride

    如果网站空间允许覆盖(通常是用.htaccess文件),则Apache会试图对文件名的每一个组成部分都打开.htaccess ,例如:

    DocumentRoot /www/htdocs
    <Directory />
    AllowOverride all
    </Directory>

    如果请求"/index.html",则Apache会试图打开"/.htaccess"、"/www/.htaccess"、"/www/htdocs/.htaccess"。其解决方法和前面所述的 Options FollowSymLinks 类似。为了得到最佳性能,应当对文件系统中所有的地方都使用 AllowOverride None

    内容协商

    实践中,内容协商的好处大于性能的损失,如果你很在意那一点点的性能损失,则可以禁止使用内容协商。但是仍然有个方法可以提高服务器的速度,就是不要使用通配符,如:

    DirectoryIndex index

    而使用完整的列表,如:

    DirectoryIndex index.cgi index.pl index.shtml index.html

    其中最常用的应该放在前面。

    还有,建立一个明确的type-map文件在性能上优于使用"Options MultiViews",因为所有需要的信息都在一个单独的文件中,而无须搜索目录。请参考内容协商文档以获得更详细的协商方法和创建type-map文件的指导。

    内存映射

    在Apache2.0需要搜索被发送文件的内容时,比如处理服务器端包含时,如果操作系统支持某种形式的mmap() ,则会对此文件执行内存映射。

    在某些平台上,内存映射可以提高性能,但是在某些情况下,内存映射会降低性能甚至影响到httpd的稳定性:

    • 在某些操作系统中,如果增加了CPU,mmap还不如read()迅速。比如,在多处理器的Solaris服务器上,关闭了mmap ,Apache2.0传送服务端解析文件有时候反而更快。

    • 如果你对作为NFS装载的文件系统中的一个文件进行了内存映射,而另一个NFS客户端的进程删除或者截断了这个文件,那么你的进程在下一次访问已经被映射的文件内容时,会产生一个总线错误。

    如果有上述情况发生,则应该使用 EnableMMAP off 关闭对发送文件的内存映射。注意:此指令可以被针对目录的设置覆盖。

    Sendfile

    在Apache2.0能够忽略将要被发送的文件的内容的时候(比如发送静态内容),如果操作系统支持sendfile() ,则Apache将使用内核提供的sendfile()来发送文件。译者注:Linux2.4内核支持sendfile()系统调用,但2.6内核已经不再支持。

    在大多数平台上,使用sendfile可以通过免除分离的读和写操作来提升性能。然而在某些情况下,使用sendfile会危害到httpd的稳定性

    • 一些平台可能会有Apache编译系统检测不到的有缺陷的sendfile支持,特别是将在其他平台上使用交叉编译得到的二进制文件运行于当前对sendfile支持有缺陷的平台时。

    • 对于一个挂载了NFS文件系统的内核,它可能无法可靠的通过自己的cache服务于网络文件。

    如果出现以上情况,你应当使用"EnableSendfile off"来禁用sendfile 。注意,这个指令可以被针对目录的设置覆盖。

    进程的建立

    在Apache1.3以前,MinSpareServers, MaxSpareServers, StartServers的设置对性能都有很大的影响。尤其是为了应对负载而建立足够的子进程时,Apache需要有一个"渐进"的过程。在最初建立StartServers数量的子进程后,为了满足MinSpareServers设置的需要,每一秒钟只能建立一个子进程。所以,对一个需要同时处理100个客户端的服务器,如果StartServers使用默认的设置5,则为了应对负载而建立足够多的子进程需要95秒。在实际应用中,如果不频繁重新启动服务器,这样还可以,但是如果仅仅为了提供10分钟的服务,这样就很糟糕了。

    "一秒钟一个"的规定是为了避免在创建子进程过程中服务器对请求的响应停顿,但是它对服务器性能的影响太大了,必须予以改变。在Apache1.3中,这个"一秒钟一个"的规定变得宽松了,创建一个进程,等待一秒钟,继续创建第二个,再等待一秒钟,继而创建四个,如此按指数级增加创建的进程数,最多达到每秒32个,直到满足MinSpareServers设置的值为止。

    从多数反映看来,似乎没有必要调整MinSpareServers, MaxSpareServers, StartServers 。如果每秒钟创建的进程数超过4个,则会在ErrorLog中产生一条消息,如果产生大量此消息,则可以考虑修改这些设置。可以使用mod_status的输出作为参考。

    与进程创建相关的是由MaxRequestsPerChild引发的进程的销毁。其默认值是"0",意味着每个进程所处理的请求数是不受限制的。如果此值设置得很小,比如30,则可能需要大幅增加。在SunOS或者Solaris的早期版本上,其最大值为10000以免内存泄漏。

    如果启用了持久链接,子进程将保持忙碌状态以等待被打开连接上的新请求。为了最小化其负面影响,KeepAliveTimeout的默认值被设置为5秒,以谋求网络带宽和服务器资源之间的平衡。在任何情况下此值都不应当大于60秒,参见most of the benefits are lost

    top

    编译时的配置

    选择一个MPM

    Apache 2.x 支持插入式并行处理模块,称为多路处理模块(MPM)。在编译Apache时你必须选择也只能选择一个MPM,这里有几个针对非UNIX系统的MPM:beos, mpm_netware, mpmt_os2, mpm_winnt。对类UNIX系统,有几个不同的MPM可供选择,他们都会影响到httpd的速度和可伸缩性:

    • workerMPM使用多个子进程,每个子进程中又有多个线程。每个线程处理一个请求。该MPM通常对高流量的服务器是一个不错的选择。因为它比preforkMPM需要更少的内存且更具有伸缩性。
    • preforkMPM使用多个子进程,但每个子进程并不包含多线程。每个进程只处理一个链接。在许多系统上它的速度和workerMPM一样快,但是需要更多的内存。这种无线程的设计在某些情况下优于workerMPM:它可以应用于不具备线程安全的第三方模块(比如php3/4/5),且在不支持线程调试的平台上易于调试,而且还具有比workerMPM更高的稳定性。

    关于MPM的更多内容,请参考其文档

    模块

    既然内存用量是影响性能的重要因素,你就应当尽量去除你不需要的模块。如果你将模块编译成DSO ,取消不必要的模块就是一件非常简单的事情:注释掉LoadModule指令中不需要的模块。

    如果你已经将模块静态链接进Apache二进制核心,你就必须重新编译Apache并去掉你不想要的模块。

    增减模块牵涉到的一个问题是:究竟需要哪些模块、不需要哪些模块?这取决于服务器的具体情况。一般说来,至少要包含下列模块:mod_mime, mod_dir, mod_log_config 。你也可以不要mod_log_config ,但是一般不推荐这样做。

    原子操作

    一些模块,比如mod_cacheworker使用APR(Apache可移植运行时)的原子API。这些API提供了能够用于轻量级线程同步的原子操作。

    默认情况下,APR在每个目标OS/CPU上使用其最有效的特性执行这些操作。比如许多现代CPU的指令集中有一个原子的比较交换(compare-and-swap, CAS)操作指令。在一些老式平台上,APR默认使用一种缓慢的、基于互斥执行的原子API以保持对没有CAS指令的老式CPU的兼容。如果你只打算在新式的CPU上运行Apache,你可以在编译时使用 --enable-nonportable-atomics 选项:

    ./buildconf
    ./configure --with-mpm=worker --enable-nonportable-atomics=yes

    --enable-nonportable-atomics 选项只和下列平台相关:

    • SPARC上的Solaris
      默认情况下,APR使用基于互斥执行的原子操作。如果你使用 --enable-nonportable-atomics 选项,APR将使用SPARC v8plus操作码来加快基于硬件的CAS操作。注意,这仅对UltraSPARC CPU有效。
    • x86上的Linux
      默认情况下,APR在Linux上使用基于互斥执行的原子操作。如果你使用 --enable-nonportable-atomics 选项,APR将使用486操作码来加快基于硬件的CAS操作。注意,这仅对486以上的CPU有效。

    mod_status 和 "ExtendedStatus On"

    如果Apache在编译时包含了mod_status ,而且在运行时设置了"ExtendedStatus On",那么Apache会对每个请求调用两次gettimeofday()(或者根据操作系统的不同,调用times())以及(1.3版之前)几个额外的time()调用,使状态记录带有时间标志。为了得到最佳性能,可以设置"ExtendedStatus off"(这也是默认值)。

    多socket情况下的串行accept

    警告

    这部分内容尚未完全根据Apache2.0中的变化进行更新 。一些信息依然有效,使用中请注意。

    这里要说的是 Unix socket API 的一个缺点。假设web服务器使用了多个Listen语句监听多个端口或者多个地址,Apache会使用select()以检测每个socket是否就绪。select()会表明一个socket有至少一个连接正等候处理。由于Apache的模型是多子进程的,所有空闲进程会同时检测新的连接。一个很天真的实现方法是这样的(这些例子并不是源代码,只是为了说明问题而已):

    for (;;) {
    for (;;) {
    fd_set accept_fds;

    FD_ZERO (&accept_fds);
    for (i = first_socket; i <= last_socket; ++i) {
    FD_SET (i, &accept_fds);
    }
    rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL);
    if (rc < 1) continue;
    new_connection = -1;
    for (i = first_socket; i <= last_socket; ++i) {
    if (FD_ISSET (i, &accept_fds)) {
    new_connection = accept (i, NULL, NULL);
    if (new_connection != -1) break;
    }
    }
    if (new_connection != -1) break;
    }
    process the new_connection;
    }

    这种天真的实现方法有一个严重的"饥饿"问题。如果多个子进程同时执行这个循环,则在多个请求之间,进程会被阻塞在select ,随即进入循环并试图accept此连接,但是只有一个进程可以成功执行(假设还有一个连接就绪),而其余的则会被阻塞accept 。这样,只有那一个socket可以处理请求,而其他都被锁住了,直到有足够多的请求将它们唤醒。此"饥饿"问题在PR#467中有专门的讲述。目前至少有两种解决方案。

    一种方案是使用非阻塞型socket ,不阻塞子进程并允许它们立即继续执行。但是这样会浪费CPU时间。设想一下,select有10个子进程,当一个请求到达的时候,其中9个被唤醒,并试图accept此连接,继而进入select循环,无所事事,并且其间没有一个子进程能够响应出现在其他socket上的请求,直到退出select循环。总之,这个方案效率并不怎么高,除非你有很多的CPU,而且开了很多子进程。

    另一种也是Apache所使用的方案是,使内层循环的入口串行化,形如(不同之处以高亮显示):

    for (;;) {
    accept_mutex_on ();
    for (;;) {
    fd_set accept_fds;

    FD_ZERO (&accept_fds);
    for (i = first_socket; i <= last_socket; ++i) {
    FD_SET (i, &accept_fds);
    }
    rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL);
    if (rc < 1) continue;
    new_connection = -1;
    for (i = first_socket; i <= last_socket; ++i) {
    if (FD_ISSET (i, &accept_fds)) {
    new_connection = accept (i, NULL, NULL);
    if (new_connection != -1) break;
    }
    }
    if (new_connection != -1) break;
    }
    accept_mutex_off ();
    process the new_connection;
    }

    函数accept_mutex_onaccept_mutex_off实现了一个互斥信号灯,在任何时刻只被为一个子进程所拥有。实现互斥的方法有多种,其定义位于src/conf.h(1.3以前的版本)或src/include/ap_config.h(1.3或以后的版本)中。在一些根本没有锁定机制的体系中,使用多个Listen指令就是不安全的。

    AcceptMutex指令被用来改变在运行时使用的互斥方案。

    AcceptMutex flock

    这种方法调用系统函数flock()来锁定一个加锁文件(其位置取决于LockFile指令)。

    AcceptMutex fcntl

    这种方法调用系统函数fcntl()来锁定一个加锁文件(其位置取决于LockFile指令)。

    AcceptMutex sysvsem

    (1.3及更新版本)这种方案使用SysV风格的信号灯以实现互斥。不幸的是,SysV风格的信号灯有一些副作用,其一是,Apache有可能不能在结束以前释放这种信号灯(见ipcs()的man page),另外,这种信号灯API给与网络服务器有相同uid的CGI提供了拒绝服务攻击的机会(所有CGI,除非用了类似suexeccgiwrapper)。鉴于此,在多数体系中都不用这种方法,除了IRIX(因为前两种方法在IRIX中代价太高)。

    AcceptMutex pthread

    (1.3及更新版本)这种方法使用了POSIX互斥,按理应该可以用于所有完整实现了POSIX线程规范的体系中,但是似乎只能用在Solaris2.5及更新版本中,甚至只能在某种配置下才正常运作。如果遇到这种情况,则应该提防服务器的挂起和失去响应。只提供静态内容的服务器可能不受影响。

    AcceptMutex posixsem

    (2.0及更新版本)这种方法使用了POSIX信号灯。如果一个运行中的线程占有了互斥segfault ,则信号灯的所有者将不会被恢复,从而导致服务器的挂起和失去响应。

    如果你的系统提供了上述方法以外的串行机制,那就可能需要为APR增加代码(或者提交一个补丁给Apache)。

    还有一种曾经考虑过但从未予以实施的方案是使循环部分地串行化,即只允许一定数量的进程进入循环。这种方法仅在多个进程可以同时进行的多处理器的系统中才是有价值的,而且这样的串行方法并没有占用整个带宽。它也许是将来研究的一个领域,但是由于高度并行的网络服务器并不符合规范,所以其被优先考虑的程度会比较低。

    当然,为了得到最佳性能,最后就根本不使用多个Listen语句。但是上述内容还是值得读一读。

    单socket情况下的串行accept

    上述对多socket的服务器进行了一流的讲述,那么对单socket的服务器又怎样呢?理论上似乎应该没有什么问题,因为所有进程在连接到来的时候可以由accept()阻塞,而不会产生进程"饥饿"的问题,但是在实际应用中,它掩盖了与上述非阻塞方案几乎相同的问题。按大多数TCP栈的实现方法,在单个连接到来时,内核实际上唤醒了所有阻塞在accept的进程,但只有一个能得到此连接并返回到用户空间,而其余的由于得不到连接而在内核中处于休眠状态。这种休眠状态为代码所掩盖,但的确存在,并产生与多socket中采用非阻塞方案相同的负载尖峰的浪费。

    同时,我们发现在许多体系结构中,即使在单socket的情况下,实施串行化的效果也不错,因此在几乎所有的情况下,事实上就都这样处理了。在Linux(2.0.30,双Pentium pro 166/128M RAM)下的测试显示,对单socket,串行化比不串行化每秒钟可以处理的请求少了不到3%,但是,不串行化对每一个请求多了额外的100ms的延迟,此延迟可能是因为长距离的网络线路所致,并且仅发生在LAN中。如果需要改变对单socket的串行化,可以定义SINGLE_LISTEN_UNSERIALIZED_ACCEPT ,使单socket的服务器彻底放弃串行化。

    延迟的关闭

    正如draft-ietf-http-connection-00.txt section 8所述,HTTP服务器为了可靠地实现此协议,需要单独地在每个方向上关闭通讯(重申一下,一个TCP连接是双向的,两个方向之间是独立的)。在这一点上,其他服务器经常敷衍了事,但从1.2版本开始被Apache正确实现了。

    但是增加了此功能以后,由于一些Unix版本的短见,随之也出现了许多问题。TCP规范并没有规定FIN_WAIT_2必须有一个超时,但也没有明确禁止。在没有超时的系统中,Apache1.2经常会陷于FIN_WAIT_2状态中。多数情况下,这个问题可以用供应商提供的TCP/IP补丁予以解决。而如果供应商不提供补丁(指SunOS4 -- 尽管用户们持有允许自己修补代码的许可证),那么只能关闭此功能。

    实现的方法有两种,其一是socket选项SO_LINGER ,但是似乎命中注定,大多数TCP/IP栈都从未予以正确实现。即使在正确实现的栈中(指Linux2.0.31),此方法也被证明其代价比下一种方法高昂。

    Apache对此的实现代码大多位于函数lingering_close(位于http_main.c)中。此函数大致形如:

    void lingering_close (int s)
    {
    char junk_buffer[2048];

    /* shutdown the sending side */
    shutdown (s, 1);

    signal (SIGALRM, lingering_death);
    alarm (30);

    for (;;) {
    select (s for reading, 2 second timeout);
    if (error) break;
    if (s is ready for reading) {
    if (read (s, junk_buffer, sizeof (junk_buffer)) <= 0) {
    break;
    }
    /* just toss away whatever is here */
    }
    }

    close (s);
    }

    此代码在连接结束时多了一些开销,但这是可靠实现所必须的。由于HTTP/1.1越来越流行,而且所有连接都是稳定的,此开销将由更多的请求共同分担。如果你要玩火去关闭这个功能,可以定义NO_LINGCLOSE ,但绝不推荐这样做。尤其是,随着HTTP/1.1中管道化稳定连接的启用,lingering_close已经成为绝对必须。而且,管道化连接速度更快,应该考虑予以支持。

    Scoreboard 文件

    Apache父进程和子进程通过scoreboard进行通讯。通过共享内存来实现当然是最理想的。在我们曾经实践过或者提供了完整移植的操作系统中,都使用共享内存,其余的则使用磁盘文件。磁盘文件不仅速度慢,而且不可靠(功能也少)。仔细阅读你的体系所对应的src/main/conf.h文件,并查找USE_MMAP_SCOREBOARDUSE_SHMGET_SCOREBOARD 。定义其中之一(或者分别类似HAVE_MMAP和HAVE_SHMGET),可以使共享内容的相关代码生效。如果你的系统提供其他类型的共享内容,则需要修改src/main/http_main.c文件,并把必需的挂钩添加到服务器中。(也请发送一个补丁给我们)

    注意:在对Linux的Apache1.2移植版本之前,没有使用内存共享,此失误使Apache的早期版本在Linux中表现很差。

    DYNAMIC_MODULE_LIMIT

    如果你不想使用动态加载模块(或者是因为看见了这段话,或者是为了获得最后一点点性能上的提高),可以在编译服务器时定义 -DDYNAMIC_MODULE_LIMIT=0 ,这样可以节省为支持动态加载模块而分配的内存。

    top

    附录:踪迹的详细分析

    在Solaris8的MPM中,Apache2.0.38使用一个系统调用以收集踪迹:

    truss -l -p httpd_child_pid.

    -l 参数使truss记录每个执行系统调用的LWP(lightweight process--Solaris核心级线程)的ID。

    其他系统可能使用不同的系统调用追踪工具,诸如strace, ktrace, par ,其输出都是相似的。

    下例中,一个客户端向httpd请求了一个10KB的静态文件。对非静态或内容协商请求的记录会有很大不同(有时也很难看明白)。

    /67:    accept(3, 0x00200BEC, 0x00200C0C, 1) (sleeping...)
    /67:    accept(3, 0x00200BEC, 0x00200C0C, 1)            = 9

    下例中,监听线程是 LWP #67 。

    注意对accept()串行化支持的匮乏。与这个特殊平台对应的MPM在默认情况下使用非串行的accept ,除了在监听多个端口的时候。
    /65:    lwp_park(0x00000000, 0)                         = 0
    /67:    lwp_unpark(65, 1)                               = 0

    接受了一个连接后,监听线程唤醒一个工作线程以处理此请求。下例中,处理请求的那个工作线程是 LWP #65 。

    /65:    getsockname(9, 0x00200BA4, 0x00200BC4, 1)       = 0

    为了实现虚拟主机,Apache需要知道接受连接的本地socket地址。在许多情况下,有可能无须执行此调用(比如没有虚拟主机,或者Listen指令中没有使用通配地址),但是目前并没有对此作优化处理。

    /65:    brk(0x002170E8)                                 = 0
    /65:    brk(0x002190E8)                                 = 0

    brk()调用是从堆中分配内存的,它在系统调用记录中并不多见,因为httpd在多数请求处理中使用了自己的内存分配器(apr_poolapr_bucket_alloc)。下例中,httpd刚刚启动,所以它必须调用malloc()以分配原始内存块用于自己的内存分配器。

    /65:    fcntl(9, F_GETFL, 0x00000000)                   = 2
    /65:    fstat64(9, 0xFAF7B818)                          = 0
    /65:    getsockopt(9, 65535, 8192, 0xFAF7B918, 0xFAF7B910, 2190656) = 0
    /65:    fstat64(9, 0xFAF7B818)                          = 0
    /65:    getsockopt(9, 65535, 8192, 0xFAF7B918, 0xFAF7B914, 2190656) = 0
    /65:    setsockopt(9, 65535, 8192, 0xFAF7B918, 4, 2190656) = 0
    /65:    fcntl(9, F_SETFL, 0x00000082)                   = 0

    接着,工作线程使客户端连接处于非阻塞模式。setsockopt()getsockopt()调用是Solaris的libc对socket执行fcntl()所必须的。

    /65:    read(9, " G E T   / 1 0 k . h t m".., 8000)     = 97

    工作线程从客户端读取请求。

    /65:    stat("/var/httpd/apache/httpd-8999/htdocs/10k.html", 0xFAF7B978) = 0
    /65:    open("/var/httpd/apache/httpd-8999/htdocs/10k.html", O_RDONLY) = 10

    这里,httpd被配置为"Options FollowSymLinks"和"AllowOverride None"。所以,无须对每个被请求文件路径中的目录执行lstat(),也不需要检查.htaccess文件,它简单地调用stat()以检查此文件是否存在,以及是一个普通的文件还是一个目录。

    /65:    sendfilev(0, 9, 0x00200F90, 2, 0xFAF7B53C)      = 10269

    此例中,httpd可以通过单个系统调用sendfilev()发送HTTP响应头和被请求的文件。Sendfile因操作系统会有所不同,有些系统中,在调用sendfile()以前,需要调用write()writev()以发送响应头。

    /65:    write(4, " 1 2 7 . 0 . 0 . 1   -  ".., 78)      = 78

    write()调用在访问日志中对请求作了记录。注意,其中没有对time()的调用的记录。与Apache1.3不同,Apache2.0使用gettimeofday()以查询时间。在有些操作系统中,比如Linux和Solaris,gettimeofday有一个优化的版本,其开销比一个普通的系统调用要小一点。

    /65:    shutdown(9, 1, 1)                               = 0
    /65:    poll(0xFAF7B980, 1, 2000)                       = 1
    /65:    read(9, 0xFAF7BC20, 512)                        = 0
    /65:    close(9)                                        = 0

    工作线程对连接作延迟的关闭。

    /65:    close(10)                                       = 0
    /65:    lwp_park(0x00000000, 0)         (sleeping...)

    最后,工作线程关闭发送完的文件和块,直到监听进程把它指派给另一个连接。

    /67:    accept(3, 0x001FEB74, 0x001FEB94, 1) (sleeping...)

    其间,监听进程可以在把一个连接指派给一个工作进程后立即接受另一个连接(但是如果所有工作进程都处于忙碌状态,则会受MPM中的一些溢出控制逻辑的制约)。虽然在此例中并不明显,在工作线程刚接受了一个连接之后,下一个accept()会(在高负荷的情况下更会)立即并行产生。

  • 批量修改配置文件

    2007-10-19 12:12:13

    #file:db_config_v1.2.sh
    #author:sujh
    #create time:2007-08-17 11:56
    #describe:N/A
    #check the option
    while getopts ":hp:" opt;
    do
        case $opt in
            h)
                printf "USAGE:\n sh $0 -p WORKPATH\n"
                exit 0
                ;;
            p)
                workPath=$OPTARG
                printf "Get the work-path:"${workPath}"\n"
                ;;
            :)
                printf ">> Error: '-$OPTARG' requires an argument \n please use: sh $0 -h \n"
                exit 1
                ;;
            ?)
                printf ">> Error: '-$OPTARG' not supported \n please use: sh $0 -h\n"
                exit 1
                ;;
        esac
    done
    #printf $OPTIND

    if [ "$OPTIND" = "1" ];then
        printf "Error:Option '-p WORKPATH' is need\n"
        exit 1
    fi

    #shield interregnum
    stty susp ^@#$
    stty intr ^@$#
    stty quit ^*#$

    matchStr="<value>jdbc:oracle:thin:@.*<\/value>";

    dbconf="<value>jdbc:oracle:thin:@10.17.42.111:1521:bnet</value>";

    if cd ${workPath}; then
        localPath=`pwd`
        printf "Success to open WORKPATH:"$localPath"\n" 
        bakDir="BAKDIR"`date +%s`
        mkdir ${localPath}"/"${bakDir}
        printf "Created backup directory:"${bakDir}"\n"
        cp ./* ${bakDir}
        printf "Backup file complete\n"
        find $localPath -maxdepth 1 -type f -exec basename {} \;|xargs -i awk '{
            if(match($0,/'$matchStr'/))
                gsub(/'$matchStr'/,"'$dbconf'",$0);
            print "echo \""$0"\" >> {}.new"
        }' {} |sh
        printf "Update filename\n"
        find $localPath -maxdepth 1 -type f -

    name "*.new"|awk '{print "mv "$0" " substr($0,0,length($0)-4)}'|sh
        printf "REPLACE COMPLETE\n"
    else
        printf "Open WORKPATH(${wordPath}) fail\n"
    fi
    #resume interregnum
    stty susp ^Z
    stty intr ^C
    stty quit ^\\
     

231/212>
Open Toolbar