⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 perf-tuning.html

📁 这个是我在web培训时老师提供的手册
💻 HTML
📖 第 1 页 / 共 3 页
字号:
        </li>
      </ul>

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

    

    <h3><a name="process" id="process">进程的建立</a></h3>

      

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

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

      <p>从多数反映看来,似乎没有必要调整<code class="directive"><a href="../mod/prefork.html#minspareservers">MinSpareServers</a></code>, <code class="directive"><a href="../mod/prefork.html#maxspareservers">MaxSpareServers</a></code>, <code class="directive"><a href="../mod/mpm_common.html#startservers">StartServers</a></code> 。如果每秒钟创建的进程数超过4个,则会在<code class="directive"><a href="../mod/core.html#errorlog">ErrorLog</a></code>中产生一条消息,如果产生大量此消息,则可以考虑修改这些设置。可以使用<code class="module"><a href="../mod/mod_status.html">mod_status</a></code>的输出作为参考。</p>

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

    <p>如果启用了持久链接,子进程将保持忙碌状态以等待被打开连接上的新请求。为了最小化其负面影响,<code class="directive"><a href="../mod/core.html#keepalivetimeout">KeepAliveTimeout</a></code>的默认值被设置为<code>5</code>秒,以谋求网络带宽和服务器资源之间的平衡。在任何情况下此值都不应当大于<code>60</code>秒,参见<a href="http://www.research.digital.com/wrl/techreports/abstracts/95.4.html">most of the benefits are lost</a>。</p>

    

  </div><div class="top"><a href="#page-header"><img alt="top" src="../images/up.gif" /></a></div>
<div class="section">
<h2><a name="compiletime" id="compiletime">编译时的配置</a></h2>


    <h3>选择一个MPM</h3>

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

      <ul>

        <li><code class="module"><a href="../mod/worker.html">worker</a></code>MPM使用多个子进程,每个子进程中又有多个线程。每个线程处理一个请求。该MPM通常对高流量的服务器是一个不错的选择。因为它比<code class="module"><a href="../mod/prefork.html">prefork</a></code>MPM需要更少的内存且更具有伸缩性。</li>

        <li><code class="module"><a href="../mod/prefork.html">prefork</a></code>MPM使用多个子进程,但每个子进程并不包含多线程。每个进程只处理一个链接。在许多系统上它的速度和<code class="module"><a href="../mod/worker.html">worker</a></code>MPM一样快,但是需要更多的内存。这种无线程的设计在某些情况下优于<code class="module"><a href="../mod/worker.html">worker</a></code>MPM:它可以应用于不具备线程安全的第三方模块(比如php3/4/5),且在不支持线程调试的平台上易于调试,而且还具有比<code class="module"><a href="../mod/worker.html">worker</a></code>MPM更高的稳定性。</li>

      </ul>

      <p>关于MPM的更多内容,请参考其<a href="../mpm.html">文档</a>。</p>

    

    <h3><a name="modules" id="modules">模块</a></h3>

        

        <p>既然内存用量是影响性能的重要因素,你就应当尽量去除你不需要的模块。如果你将模块编译成<a href="../dso.html">DSO</a> ,取消不必要的模块就是一件非常简单的事情:注释掉<code class="directive"><a href="../mod/mod_so.html#loadmodule">LoadModule</a></code>指令中不需要的模块。</p>

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

        <p>增减模块牵涉到的一个问题是:究竟需要哪些模块、不需要哪些模块?这取决于服务器的具体情况。一般说来,<em>至少</em>要包含下列模块:<code class="module"><a href="../mod/mod_mime.html">mod_mime</a></code>, <code class="module"><a href="../mod/mod_dir.html">mod_dir</a></code>, <code class="module"><a href="../mod/mod_log_config.html">mod_log_config</a></code> 。你也可以不要<code>mod_log_config</code> ,但是一般不推荐这样做。</p>

    	<h3 name="atomics" id="atomics">原子操作</h3>

      <p>一些模块,比如<code class="module"><a href="../mod/mod_cache.html">mod_cache</a></code>和<code class="module"><a href="../mod/worker.html">worker</a></code>使用APR(Apache可移植运行时)的原子API。这些API提供了能够用于轻量级线程同步的原子操作。</p>

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

      <div class="example"><p><code>
        ./buildconf<br />
        ./configure --with-mpm=worker --enable-nonportable-atomics=yes
      </code></p></div>

      <p><code>--enable-nonportable-atomics</code> 选项只和下列平台相关:</p>

      <ul>

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

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

      </ul>


    <h3>mod_status 和 "ExtendedStatus On"</h3>

      

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

    

    <h3>多socket情况下的串行accept</h3>

      

    <div class="warning"><h3>警告</h3>
      <p>这部分内容尚未完全根据Apache2.0中的变化进行更新 。一些信息依然有效,使用中请注意。</p>
    </div>

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

      <div class="example"><p><code>
        for (;;) {<br />
        <span class="indent">
          for (;;) {<br />
          <span class="indent">
            fd_set accept_fds;<br />
            <br />
            FD_ZERO (&amp;accept_fds);<br />
            for (i = first_socket; i &lt;= last_socket; ++i) {<br />
            <span class="indent">
              FD_SET (i, &amp;accept_fds);<br />
            </span>
            }<br />
            rc = select (last_socket+1, &amp;accept_fds, NULL, NULL, NULL);<br />
            if (rc &lt; 1) continue;<br />
            new_connection = -1;<br />
            for (i = first_socket; i &lt;= last_socket; ++i) {<br />
            <span class="indent">
              if (FD_ISSET (i, &amp;accept_fds)) {<br />
              <span class="indent">
                new_connection = accept (i, NULL, NULL);<br />
                if (new_connection != -1) break;<br />
              </span>
              }<br />
            </span>
            }<br />
            if (new_connection != -1) break;<br />
          </span>
          }<br />
          process the new_connection;<br />
        </span>
        }
      </code></p></div>

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

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

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

      <div class="example"><p><code>
        for (;;) {<br />
        <span class="indent">
          <strong>accept_mutex_on ();</strong><br />
          for (;;) {<br />
          <span class="indent">
            fd_set accept_fds;<br />
            <br />
            FD_ZERO (&amp;accept_fds);<br />
            for (i = first_socket; i &lt;= last_socket; ++i) {<br />
            <span class="indent">
              FD_SET (i, &amp;accept_fds);<br />
            </span>
            }<br />
            rc = select (last_socket+1, &amp;accept_fds, NULL, NULL, NULL);<br />
            if (rc &lt; 1) continue;<br />
            new_connection = -1;<br />
            for (i = first_socket; i &lt;= last_socket; ++i) {<br />
            <span class="indent">
              if (FD_ISSET (i, &amp;accept_fds)) {<br />
              <span class="indent">
                new_connection = accept (i, NULL, NULL);<br />
                if (new_connection != -1) break;<br />
              </span>
              }<br />
            </span>
            }<br />
            if (new_connection != -1) break;<br />
          </span>
          }<br />
          <strong>accept_mutex_off ();</strong><br />
          process the new_connection;<br />
        </span>
        }
      </code></p></div>

      <p><a id="serialize" name="serialize">函数</a><code>accept_mutex_on</code>和<code>accept_mutex_off</code>实现了一个互斥信号灯,在任何时刻只被为一个子进程所拥有。实现互斥的方法有多种,其定义位于<code>src/conf.h</code>(1.3以前的版本)或<code>src/include/ap_config.h</code>(1.3或以后的版本)中。在一些根本没有锁定机制的体系中,使用多个<code class="directive"><a href="../mod/mpm_common.html#listen">Listen</a></code>指令就是不安全的。</p>

      <p><code class="directive"><a href="../mod/mpm_common.html#acceptmutex">AcceptMutex</a></code>指令被用来改变在运行时使用的互斥方案。</p>

      <dl>
        <dt><code>AcceptMutex flock</code></dt>

        <dd>
          <p>这种方法调用系统函数<code>flock()</code>来锁定一个加锁文件(其位置取决于<code class="directive"><a href="../mod/mpm_common.html#lockfile">LockFile</a></code>指令)。</p>
        </dd>

        <dt><code>AcceptMutex fcntl</code></dt>

        <dd>
          <p>这种方法调用系统函数<code>fcntl()</code>来锁定一个加锁文件(其位置取决于<code class="directive"><a href="../mod/mpm_common.html#lockfile">LockFile</a></code>指令)。</p>
        </dd>

        <dt><code>AcceptMutex sysvsem</code></dt>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -