Varnish进程及存储
  • Varnish进程结构
    varnish_process_architecture
  • Varnish管理进程

    varnishd启动之后有两个进程:管理进程(父进程ID为1)和子进程(父进程ID为管理进程ID).

    # ps -ef | grep varnishd | grep -v grep

    管理进程每隔几秒探测一下子进程的活动.如果在一定的时间之内,没有获得回复,管理进程就会杀死子进程并重启它.管理进程的日志将会记录到syslog.由于管理进程启动子进程的速度很快,因此很难察觉到.通过监控syslog和varnish的启动时间有助于解决问题.

    # tail -f /var/log/messages
    # varnishstat -f uptime
  • Varnish子进程及线程模型

    子进程包含几种类型的线程,包括但不限于:

    线程名称线程数目任务
    acceptor线程1个接收新连接并委派他们
    cache-worker线程每个会话(活动连接)一个处理请求
    cache-main线程1个用于Varnish启动
    expiry线程1个将旧内容从缓存中移除
    ban lurker线程1个清除bans
    epoll/kqueue线程可配置,默认为2个管理线程池
    backend线程每个后端探测有一个探测健康检查

    当线程需要获得或修改内存的时候,Varnish使用工作空间(workspaces)来减少线程之间的竞争.Varnish有多个工作空间,但用来操作会话数据的会话工作空间(session workspace)是最重要的.比如,session workspace为5M,有1000个线程,那么虚拟内存使用是5GB,但实际的内存使用不一定是5GB.内存控制器和操作系统会跟踪记录实际使用的内存.

    为了跟系统的其它部分通讯,子进程使用了文件系统可访问的共享内存日志(shared memory log).这意味着如果一个线程需要记录日志,它所需要做的就是获得一个”锁”,写进内存区域,然后释放”锁”.此外,每个worker线程都有一个日志数据的缓存,以减少”锁”的竞争.

    共享内存日志文件通常大约为90MB,并且分成两个部分.第一部分是计数器(counters),第二部分是请求数据(request data).可以使用Varnish的辅助工具(如:varnishlog,varnishstat,varnishtop)来查看实际数据.

    Varnish的每个会话都使用一个线程.Varnish可以使用的线程数决定了Varnish可以同时并发处理多少个请求.
    Varnish运行时使用多个线程池.当一个连接被接受的时候,该连接会被委派给其中一个线程池.这个线程池会进一步委派这个被接受的连接给可用的worker线程.如果没有可用的worker线程,则将该连接放到queue.如果Queue已经满了,则该连接被丢弃.Varnish默认使用2个线程池,这已经被证明足够用,即使对很忙的Varnish服务器也适用.

  • VCL编译

    Varnsih缓存策略的配置是通过VLC(Varnish配置语言)来实现的.管理进程会解析VCL并转化为C,然后由C编译器来编译.最终,编译后的VCL会被关联到正在运行的Varnish实例.编译后的VCL文件一直被保留直到重启Varnish或通过管理客户端的vcl.discard命令丢弃它.

    在Varnish不停止运行的情况下,可通过管理客户端重新加载VCL配置,新VLC的缓存策略会立即生效.

    查看当前Varnish实例的可用vcl列表:

    # varnishadm vcl.list
    available       0 boot
    available       0 reload_2013-04-06T23:00:44
    available       0 reload_2013-04-06T23:00:47
    available       0 reload_2013-04-06T23:00:48
    active          0 reload_2013-04-06T23:00:49

    编译一个新的vcl,并加载使用:

    # varnishadm vcl.load full_vcl /usr/local/varnish/etc/varnish/full_fdfs.vcl
    # varnishadm vcl.list  
    available       0 boot
    available       0 reload_2013-04-06T23:00:44
    available       0 reload_2013-04-06T23:00:47
    available       0 reload_2013-04-06T23:00:48
    active          0 reload_2013-04-06T23:00:49
    available       0 full_vcl
    # varnishadm vcl.use full_vcl

    丢弃一个名为”reload_2013-04-06T23:00:44″的vcl配置:

    # varnishadm vcl.discard reload_2013-04-06T23:00:44
  • Varnish存储方式

    Varnish支持几种方式来给缓存分配空间,包括:file,malloc和persistent (experimental).

    • malloc存储方式:
      Varnish会使用malloc(内存分配)请求分配整个预设缓存大小的内存.操作系统通过使用swap将不能装载到内存的数据存储到磁盘.

    • file存储方式:
      Varnish会在文件系统上创建一个文件去包含整个缓存,并且告诉操作系统通过mmap(内存映射)将整个文件映射到内存中(如果可能的话),即file方式实际上也是使用内存去缓存数据.
      需要注意的是,在停止或重启Varnish的时候,file存储的方式不会保留数据.使用file存储方式,Varnish不会记录哪些数据写入到磁盘,哪些没有写入.因此不可能知道磁盘上的缓存是否可用,它仅仅是随机数据.如果使用file存储方式的话,Varnish不会(也不可以)重新使用旧的缓存.

    • persistent:
      类似file存储方式,是一种试验性的持久存储方式.它还没有很好解决空间全部消耗完的情况.

    • 存储方式选择
      选择存储方式的时候,如果内存足够大的话,最好选择malloc,这样可以让缓存全部或是大部分保存在内存中.
      如果需要缓存的数据超过可用物理内存,可以选择file存储方式.

    • Varnish额外开销
      通过-s参数设置的是预设的实际缓存大小.Varnish还需要额外的开销来跟踪记录缓存,大概每个储存对象需要约1K的额外开销.(100万的对象就需要额外使用1GB内存).此外,启动Varnish(无任何存储对象时)也需要大概100MB左右的开销.

Varnish性能调优
  • Varnish参数

    查看Varnish当前的所有参数设置:

    # varnishadm 'param.show -l'

    优化参数的设置可以通过管理客户端varnishadm设置,也可以在通过varnishd的启动参数-p来设置.
    通过varnishadm设置的参数不会被保存,Varnish重启的时候将会丢失.最好的设置方式将参数放到启动脚本.

  • 系统内核调优
    # echo 'fs.file-max=1000000' >> /etc/sysctl.conf
    # echo 'net.ipv4.ip_local_port_range = 1024 65535' >> /etc/sysctl.conf
    # sysctl -p
    # echo '* soft nofile 1000000' >> /etc/security/limits.conf
    # echo '* hard nofile 1000000' >> /etc/security/limits.conf
    # echo 'session required pam_limits.so' >> /etc/pam.d/login
    # ulimit -n 1000000
    # ulimit -n -H

    注:不要设置tw_reuse为1 (sysctl).那样会对NAT的客户端造成问题.

  • Varnish启动参数调优

    Varnish的默认参数大多已经是优化的了.需要调优的可能是线程数(number of threads)和工作空间(workspace).

    • thread_pools:worker线程池的数量,默认值为2.
      增加worker线程池数量可以减少”锁”的竞争.但是过多的线程池会消耗CPU和RAM的资源,线程池数目多于CPU的核数目对性能是有损害的.
      尽管可使用多个线程池,但生产环境和经验已经证明有2个线程池之后,增加更多的线程池并不会提高性能.

    • thread_pool_min:每个线程池的最小的线程数,默认值为5.

    • thread_pool_max:每个线程池的最大的线程数,默认值是500.
      不要将该值设置得太高,因为过量的worker线程会消耗RAM和CPU,从而适得其反.
      worker线程数受文件描述符数限制,总的worker线程数不应该超过5000,否则可能出现文件描述符相关的问题.

    • thread_pool_add_delay:创建线程之间最少需要等待的时间,默认值为2ms.
      设置时间过长会导致worker线程不足,设置过短会增加worker线程堆积的风险.
      使用2ms就好,可以快速创建线程,避免发生队列填满和丢弃请求,减少Varnish的启动时间.

    • thread_pool_timeout:线程空闲的阈值(在被移除之前),默认值300s.该值很少需要改变.
      当线程数目超过thread_pool_min,并且处于空闲状态至少这么久的时间,就很可能被清除.

    • thread_pool_fail_delay:在线程创建失败之后,创建另一个线程需要至少等待多少时间.
      创建worker线程失败通常是系统出问题的征兆.因为进程已经消耗完留给线程堆栈(thread stacks)的RAM资源.
      这样的延迟可以减少不必要的拥挤状况.
      如果线程创建失败成为了问题,那么请检查thread_pool_max是否设置太高.增加thread_pool_timeout和thread_pool_min也可能有助于降低线程销毁和重新创建的速率.

    • lru_interval:储存对象在LRU列表上移动之前的宽限时间.
      存储对象只能被移动到LRU列表的前面,如果他们在这个时间之内还没有被移动到那里的话.
      这样可以减少给LRU列表访问的“锁”操作的数量.不要修改该值,否则可能会很危险(如果你突然需要LRU列表的话).

    • sess_workspace: 分配给会话的进入的HTTP头的工作空间(来自客户端).默认是64K,最小需要1K.
      这个空间必须足够大,分配给整个HTTP协议头部和任何在VCL中对HTTP头的修改.调优的话,可以取值16k~10M.

    • session_linger:为了看是否有新的请求马上出现,worker线程在一个会话上逗留多长时间.默认值为50ms.
      如果会话被重复使用,在之前一次的请求完成之后的头100ms内,最多只有一半的重用发生.
      设置该值过高会导致worker线程不会为这样的逗留时间做任何事情,设置过低意味着更多的会话绕过了等待.
      在CPU吃紧的时候,让每个worker线程等待几个新的请求很重要,这样可以避免过多的上下文切换.
      该值的设置取决于分发存储对象的通常需要多少时间.此外,这也会减少线程堆积的数量.

    • sess_timeout:留给持久会话的空闲超时(即keep-alive timeout).默认是5s.(来自客户端)
      如果一个HTTP请求在这么多时间之内还没有被接受,那么这个会话就会被关闭.
      调高sess_timeout的值,会增加文件描述符的消耗.这样做没有什么好处,还可能会造成文件描述符耗尽的危险.

    • send_timeout:客户端连接的发送超时.默认值为60s.
      如果HTTP响应数据在这么多的时间内没有被传输,那么会话就被关闭.

    • cli_timeout:子进程回应来自管理进程CLI请求的超时时间.默认是10s.
      cli_timeout是管理进程在认定子线程死掉之前,等待子线程进行回应的超时时间.如果超过这个时间,管理进程就会杀掉子进程,并将它重启.Varnish负载比较大的话,管理进程可能会出现无法及时管理子线程而造成误杀.适当调高该值可以避免这种情况发生.

    优化varnishd的启动参数设置例子:

                      
    -p thread_pools = 2                          
    -p thread_pool_min = 200                      
    -p thread_pool_max = 2000   
    -p thread_pool_add_delay=2                  
    -p session_linger = 100                       
    -p sess_workspace=262144
    -p cli_timeout=25
    

    通常使用500~1000个总的最小线程来运行Varnish.通过监控n_wrk_queued可以知道这样的线程设置是否合适.
    n_wrk_queued(queued work requests)在Varnish启动之后应该是几乎静止不变的.

    # varnishstat -f n_wrk_queued
    
  • VCL参数优化
    • connect_timeout:后端的默认连接超时(OS/network延迟).默认是0.7s
      Varnish在放弃连接之前只会尝试连接后端这么多时间.VCL中可以针对每个后端和后端请求覆盖该值.

    • first_byte_timeout:接收后端的第一个字节数据的默认超时(页面生成?).默认是60s.
      Varnish在放弃之前只等待这么长时间去接收第一个字节数据.如果设置该值为0,意味着永远不超时.
      VCL中可以针对每个后端和后端请求覆盖该值.该参数不适用于pipe.

    • between_bytes_timeout:接收后端数据时字节之间的默认超时.默认是60s.
      Varnish在放弃之前在字节之间只等待这么多时间.如果该值为0,意味着永远不超时.
      CL中可以针对每个后端和后端请求覆盖该值.该参数不适用于pipe.

    VCL后端逻辑例子:

    ...
    backend default {
        .host = "192.168.1.191";
        .port = "80";
        .connect_timeout = 1s;
        .first_byte_timeout = 100s;
        .between_bytes_timeout = 100s;
        .probe = {
            .url = "/status.php";
            .interval = 20s;
            .timeout = 10s;
            .window = 2;
            .threshold = 2;
        }
    }
    ...

转载:http://www.zrwm.com/?cat=138 作者:Jose