zoukankan      html  css  js  c++  java
  • 深度优化LNMP

    优化前准备工作

    Centos准备及配置

    准备安装包及软件:
    http://pan.baidu.com/s/1chHQF  下载解压到U盘即可安装
    http://pan.baidu.com/s/15TUWf  其它版本的centos 在这里 
    http://pan.baidu.com/s/1Dv3me  SecureCRT,远程linux管理软件
     
    1  安装完centos
    2  启动SecureCRT  远程连接linux服务器
    若连不上,请检查一下几项:网络防火墙  sshd
    3  若sshd未启动,则启动sshd   # /usr/sbin/sshd
    若启动时出现如下错误:
             Could not load host key: /etc/ssh/ssh_host_rsa_key
             Could not load host key: /etc/ssh/ssh_host_dsa_key
             其原因是在 SSH 连接协议中需要有 RSA 或 DSA 密钥的鉴权。因此,我们可以在服务器端使用 ssh-keygen 程序来生成一对公钥
    /私钥对
    执行  # ssh-keygen -t rsa -b 1024 -f /etc/ssh/ssh_host_rsa_key   一路回车
    命令执行成功后,在 /etc/ssh 下会看到有两个文件生成:ssh_host_rsa_key 和 ssh_host_rsa_key.pub ,前者是私钥,后者是公钥。
     
    此时,启用SecureCRT远程连接linux服务器,一般会成功!
     
    4 配置yum更新源
     
             配置国内更新源:参考我的这篇文章
             http://www.cnblogs.com/buffer/p/3426908.html
             然后执行命令,更新缓存:
             # yum clean metadata
             # yum clean dbcache
             # yum makecache
     
    5 安装lrzsz工具
             用于windows和linux间传输文件
             # yum -y install lrzsz
     
    6 配置固定ip
     
             修改或者增加 /etc/sysconfig/network-scripts/ifcfg-eth0
                       DEVICE=eth0
                       IPADDR=192.168.1.104
                       NETMASK=255.255.255.0
                       GATEWAY=192.168.1.1
                       ONBOOT=yes
                       USERCTL=no        //是否允许非root用户控制该设备
                       BOOTPROTO=static
            
             修改DNS配置文件: vi /etc/resolv.conf  增加
             nameserver 192.168.1.1             #主DNS
             nameserver 8.8.8.8                 #次DNS
             search localdomain
            
             关闭NetworkManager,因为在命令行模式下,NetworkManager 和network两个服务冲突。重启:# service network restart
     
    7 配置vi
    配置vi 作为后面的编程IDE,基本配置步骤请参考我的这篇文章:
    http://www.cnblogs.com/buffer/p/3429221.html
     
    8 安装必要的编译工具及必需的库文件
             yum -y install make apr* autoconf automake curl-devel gcc gcc-c++ zlib-devel openssl openssl-devel pcre-devel gd kernel keyutils patch perl kernel-headers compat* mpfr cpp glibc libgomp libstdc++-devel ppl cloog-ppl keyutils-libs-devel libcom_err-devel libsepol-devel libselinux-devel krb5-devel zlib-devel libXpm* freetype libjpeg* libpng* php-common php-gd ncurses* libtool* libxml2 libxml2-devel patch cmake
             总共148M左右,具体每一项的作用,大家感兴趣的可以查一查
     
    9配置防火墙(服务器安全优化)
             安全规划:开启 80 22 端口并打开回路(回环地址 127.0.0.1)
             # iptables –P INPUT ACCEPT
             # iptables –P OUTPUT ACCEPT
             # iptables –P FORWARD ACCEPT
             以上几步操作是为了在清除所有规则之前,通过所有请求,如果远程操作的话,防止远程链接断开。
    接下来清除服务器内置规则和用户自定义规则:
             # iptables –F
             # iptables -X
            
             打开ssh端口,用于远程链接用:
             # iptables –A INPUT –p tcp –-dport 22 –j ACCEPT
             # iptables -A OUTPUT -p tcp --sport 22 -m state --state ESTABLISHED,RELATED -j ACCEPT允许已建立的链接通过22端口向外发送数据包
             然后关闭INPUT 和FORWARD请求:
             # iptables –P INPUT DROP
             # iptables –P FORWARD DROP
            
             接下来设置环路,使得ping 127.0.0.1这样的包额可以通过。后面php会使用这个规则,
             Nginx中设置php-fpm访问地址:http://127.0.0.1:9000即用到这个规则
             # iptables –A INPUT –i lo –j ACCEPT
     
             接下来设置允许其他机器ping 本机,也可以不允许,不允许会更加安全。
             # iptables –A INPUT –p icmp –j ACCEPT
            
             接下来开放web服务端口 80
             # iptables –A INPUT –p tcp –dport 80 –j ACCEPT
             # iptables -A OUTPUT -p tcp --sport 80 -m state --state ESTABLISHED,RELATED -j ACCEPT允许已建立的链接通过80端口向外发送数据包
            
             接下来开放 53端口用于DNS解析
             # iptables -A INPUT -p udp --dport 53 -j ACCEPT
             # iptables -A OUTPUT -p udp --sport 53 -m state --state ESTABLISHED,RELATED -j ACCEPT
     
             最后保存设置:
             # service iptables save
             # service iptables restart
     
             至此已经完成关闭除22 80之外的所有对外端口,服务器可以通过任意端口向外发请求,但是外面的请求只能通过 80和22端口进入到内部。
             也可以直接下载这个脚本执行即可完成基本防火墙设置:
             http://pan.baidu.com/s/17qiUG
             附件:
             linux端口号大全请参考我这篇文章
             http://www.cnblogs.com/buffer/p/3386132.html
             iptables 详解,请参考我这篇文章:
             http://www.cnblogs.com/buffer/archive/2012/09/24/2700609.html
     
    10 修改启动模式,默认命令行启动
             # vi /etc/inittab
             将id:5:initdefault:改为id:3:initdefault:保存即可
             从说明里可以看出,5 是启动桌面x-window    3是启动命令行文本模式
     
    以上为配置linux基础工作环境
     

    准备nginx 基础工作环境

    1 安装nginx
             下载源码包nginx-1.4.2.tar.gz:http://pan.baidu.com/s/1chQ76
     
             创建nginx运行的用户组及用户:www  www
             #groupadd www
             # useradd -g www www -s /bin/false 创建nginx运行账户www并加入到www组,不允许www用户直接登录系统
     
             编译安装:
             # ./configure --prefix=/usr/local/webserver/nginx --user=www --group=www --with-http_ssl_module --with-http_gzip_static_module
    --with-pcre=/usr/local/webserver/softs/libs/pcre-8.32
    --with-openssl=/usr/local/webserver/softs/libs/openssl-1.0.0d
             # make & make install
             查看编译安装参数命令:# ./configure –help
             这里需要两个源码包pcre和openssl:
             pcre-8.32.tar.gz下载:http://pan.baidu.com/s/1pFcIw
             openssl-1.0.0d.tar.gz下载:http://pan.baidu.com/s/1fYYv
            
             设置nginx开机启动:
             具体设置步骤请参考我这篇文章:
                       http://www.cnblogs.com/buffer/archive/2011/08/15/2138762.html
             配置完之后,可以通过命令启动nginx服务:# service nginx start
            
             配置nginx虚拟主机。
    至此nginx服务搭建完毕。
     
    2 编译安装mysql
             下载源码包mysql-5.5.15.tar.gz:http://pan.baidu.com/s/1chRcF
            
             创建mysql服务运行的用户 mysql 并加入组www
             # useradd –g www mysql –s /bin/false
             创建mysql数据库数据存放目录:
             # mkdir -p /usr/local/webserver/mysql/data
             设置MySQL数据库目录权限
             #chown –R  mysql:www  /usr/local/webserver/mysql/data
            
             下面开始编译安装mysql:
             # cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/webserver/mysql
    -DMYSQL_DATADIR=/usr/local/webserver/mysql/data
    -DSYSCONFDIR=/etc/
    # make && make install
     
    配置mysql:
    # cp ./support-files/my-huge.cnf /etc/my.cnf
     
    编辑配置文件,在 [mysqld] 部分增加下面一行,添加MySQL数据库数据路径
    datadir = /usr/local/webserver/mysql/data/mysql
     
    接下来,生成mysql系统数据库:
    # ./scripts/mysql_install_db --user=mysql
            
             接下来,将mysql加入系统服务开机启动:
             # cp ./support-files/mysql.server /etc/rc.d/init.d/mysqld
    增加执行权限:
             # chmod 755 /etc/init.d/mysqld
             加入开机启动:
    # chkconfig mysqld on
             编辑启动文件修改mysql路径及mysql数据文件目录:
    # vi /etc/rc.d/init.d/mysqld
    basedir = /usr/local/webserver/mysql
    datadir = /usr/local/webserver/mysql /data/mysql
     
    接下来可以启动mysql服务了
    Service mysqld start
     
    mysql后续配置
    设置mysql密码:
    #./bin/mysql_secure_installation
    根据提示输入密码,删除anonymous用户,禁止以root身份远程登录,删除test数据库等
    接下来,把myslq的库文件链接到系统默认的位置,这样在编译PHP等软件时可以不用指定mysql的库文件地址:
    # ln -s /usr/local/webserver/mysql/lib/mysql /usr/lib/mysql
    # ln -s /usr/local/webserver/mysql/include/mysql /usr/include/mysql
    最后,把mysql服务加入系统环境变量,方便调用mysql客户端登录mysql
    # vi /etc/profile增加下面一行
    export PATH=$PATH:/usr/local/webserver/mysql/bin
            
    至此,mysql编译安装及配置已经全部完成。最好重启一下系统,使修改生效。

    编译安装php

             下载源码包php-5.5.5.tar.gz:http://pan.baidu.com/s/1zi33B
            
             安装php之前需要编译安装libmcrypt,到这里下载:http://pan.baidu.com/s/1uyM1L
             编译安装libmcrypt:
             # ./configure
    # make && install
     
    接下来编译安装php:
    # ./configure --prefix=/usr/local/webserver/php --with-mysql=mysqlnd
    --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-zlib --with-libxml-dir
    --with-gd  --with-jpeg-dir   --with-png-dir=/usr/lib --with-ttf --with-iconv
    --with-openssl  --with-mcrypt --enable-static --enable-maintainer-zts
    --enable-zend-multibyte --enable-inline-optimization --enable-sockets
    --enable-wddx --enable-zip --enable-calendar --enable-bcmath--enable-soap 
    --enable-mbstring --without-sqlite --disable-ipv6--with-mhash
    --with-mcrypt --enable-fpm
    # make && make install
    with-mcrypt:扩展加密算法,如果只用md5的话,则不需要此选项
    接下来配置php:
    #  cp php.ini-production /usr/local/webserver/php/etc/php.ini从源文件目录下复制php配置文件到安装目录
    #  rm -rf /etc/php.ini删除系统自带配置文件
    #  ln -s /usr/local/webserver/php/etc/php.ini /etc/php.ini添加软链接
     
    #  vi /usr/local/webserver/php/etc/php.ini
    禁用不安全函数:
    找到:disable_functions =修改为:
    disable_functions  =
    passthru,exec,system,chroot,scandir,chgrp,chown,shell_exec,proc_open,proc_get_status,
    ini_alter,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,
    stream_socket_server,escapeshellcmd,dll,popen,disk_free_space,checkdnsrr,checkdnsrr,
    getservbyname,getservbyport,disk_total_space,posix_ctermid,posix_get_last_error,
    posix_getcwd, posix_getegid,posix_geteuid,posix_getgid, posix_getgrgid,posix_getgrnam,
    posix_getgroups,posix_getlogin,posix_getpgid,posix_getpgrp,posix_getpid,posix_getppid,
    posix_getpwnam,posix_getpwuid, posix_getrlimit, posix_getsid,posix_getuid,
    posix_isatty,posix_kill,posix_mkfifo,posix_setegid,posix_seteuid,posix_setgid, posix_setpgid,
    posix_setsid,posix_setuid,posix_strerror,posix_times,posix_ttyname,posix_uname
    列出PHP可以禁用的函数,如果某些程序需要用到这个函数,可以删除,取消禁用。
    找到:;date.timezone =修改为:date.timezone = PRC #设置时区
    找到:expose_php = On修改为:expose_php = OFF #禁止显示php版本的信息
     
                       接下来配置php-fpm.conf
    # cp php/etc/php-fpm.conf.default php/etc/php-fpm.conf
    拷贝模板文件为php-fpm配置文件
    # vi php/etc/php-fpm.conf
    user = www 设置php-fpm运行账号为www
    group = www 设置php-fpm运行组为www
    pid = run/php-fpm.pid 取消前面的分号
                      
                       将php-fpm加入系统服务病设置开机启动:
                       # cp php-5.5.5/sapi/fpm/init.d.php-fpm /etc/rc.d/init.d/php-fpm
    拷贝php-fpm到启动目录
    # chmod +x /etc/rc.d/init.d/php-fpm 添加执行权限
    # chkconfig php-fpm on 设置开机启动
     
    启动php-fpm:  #  service php-fpm start
    至此,php已经编译安装完成。
     
    Lnmp已经全部初步编译安装及配置完毕,下一步,分别对lnmp 进行优化。

    LNMP优化

    本文LNMP优化从系统安全,系统资源占用率,及web服务并发负载这三个方面体现,并且主要体现在web服务并发负载这一方面。
    本文中使用的服务器配置:centos 6.4  2G内存
    本文用到主要从Linux优化、nginx优化、mysql优化、php优化这四个方面依次着手,并且用到的优化效果测试工具有以下几项:
    1 服务并发测试工具——apache bench(即ab.exe)
    这里下载:http://pan.baidu.com/s/1y2BlC除此之外,注明的web并发测试工具还有http_load 、webbench 、Loadrunner等
    2 网络负载监控工具——nload
    这里下载:http://pan.baidu.com/s/126vnm
    3  linux系统内存、IO、CPU、网络状态监测命令等。更直观的可以安装nmon工具及
     nmon  analyser ,前者是系统各项指标检测工具,后者是将检测结果数据图表化的工具。
       这里下载:
    32位 nmon 下载: http://pan.baidu.com/s/1sxkmI
    64位 nmon下载: http://pan.baidu.com/s/1rbr6C
    nmon analyser 工具下载:http://pan.baidu.com/s/1Caaa8   这是一个windows上的用excel宏做的excel图表工具
    这两款工具具体使用方法见我的这篇文章:
    http://blog.csdn.net/neubuffer/article/details/16881167
     
    为了体现优化效果,本文将优化之前和优化之后的效果保存以作对比。并且每做一步优化都会进行测试比较。
    安装完linux,并安装了基本web服务软件nginx、mysql、php之后,
    优化之前的系统内存资源占用情况及进程明细,如下图1所示:
    内存占用了274M

    图1
     
    优化之前的系统资源CPU利用率及平均负载情况,如下图2所示:
     

    图2
    load average: 0.51, 0.30, 0.12指的是 系统负载,即任务队列的平均长度,三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值。
    Cpu(s): 0.1% us 用户空间占用CPU百分比
    0.2% sy 内核空间占用CPU百分比
    0.0% ni 用户进程空间内改变过优先级的进程占用CPU百分比
    99.3.0% id 空闲CPU百分比
    0.4% wa 等待输入输出的CPU时间百分比
    Mem 和 Swap 中的buffers和cached分别指用作内核缓存的内存量及缓冲的交换区总量
    优化之前的web并发负载情况,如下图3所示:模拟1000个客户端发送200000个请求,每分钟并发 5280.34,并且出现大量TIME_WAIT


    图3

    20万并发资源消耗情况如下图4 所示:

    系统资源使用情况CPU 和 IO

    CPU
     

    磁盘

    网络
    图4
    此时看一下nginx错误日志,出现大量failed (24: Too many open files) ,并且 /var/log/message 中也出现   possible SYN flooding on port 80. Sending cookies. 
    下面开始一步一步优化,将上面日志中的警告或错误解决掉,并且提高服务器并发负载能力轻松达到1万以上并发,让有限的资源发挥最大性能
    好的,让我们开始吧!

    首先进行linux优化加固

    Linux优化加固最好的办法就是提升硬件配置,比如提高CPU运算能力,增大内存容量,提高硬盘吞吐率等。
    本文谈的Linux优化加固是在不提升硬件配置的情况下,通过优化内核配置,从而提高linux服务效率,从三个方面进行:安全加固、内核调优、优化网络,本文主要讲解内核调优及网络优化。

    1优化linux 启动项

    使用ntsysv工具将不需要的服务关闭
    优化启动服务之前的内存使用情况:使用了 275M

    图5
       进行优化,默认启动服务可以只保留以下六项必要服务:
    iptables sshd crond syslog network messagebus
    同时也要保留 nginx、mysql、php默认启动
    系统服务解释请参考我的这篇文章:http://www.cnblogs.com/buffer/p/3386346.html
    优化之后内存使用情况,使用了224M,节省了近50m内存,不要小看这50M内存,关键时候可以起很大作用

    图6
     

    2 安全加固

    Linux安全加固主要针对iptables进行,控制所有INPUT数据包,除了必要的端口打开之外,其余的端口一律关闭。禁用用户ping服务器等会更加安全。详情见LNMP优化准备工作—9配置防火墙(服务器安全优化)。
        删除不必要的用户:
        # cp /etc/passwd /etc/passwd.sav
    # cp /etc/group /etc/group.sav
    # for a in adm lp sync news uucp operator games gopher mailnull nscd rpc;
    do /usr/sbin/userdel $a -f; done
    # for a in lp news uucp games gopher users floopy nscd rpc rpcuser nfsnobody;
    do /usr/sbin/groupdel $a ; done

    3网络优化

    一般情况下,Nginx通过TCP socket来连接客户端与上游应用,默认安装的系统对TCP有许多门限值与限制,通过内核参数来设定。这些参数的默认值往往是为一般的用途而定的,并不能满足web服务器所需的高流量、短生命的要求。
    对于网络参数调优可以修改 /etc/sysctl.conf 这个文件。修改完之后使用 #sysctl –p 让内核配置生效。
    3.1 内核调优——调整TIMEWAIT,解决出现大量TIMEWAIT问题
    在调整TIMEWAIT参数之前,先解释一下tcp链接请求和tcp关闭请求中的报文响应流程。如图7所示为一个完整的数据报格式。
    E:aiduyundownload传智播客lnmp优化配置文件200711211195609944859.jpg
    图7
    Tcp的链接及断开过程中间是经过一系列状态变换的。有这些状态:
    建立链接状态:LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED
    关闭链接状态:FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT 和 CLOSED。
    各状态参数解释请参考我这篇文章:
    http://blog.csdn.net/neubuffer/article/details/16853747
     
        建立链接时,需要经过三次握手协议,建立链接时的握手协议如下图8所示。

    收到客户端发来的ack
    进入ESTABLISHED
    收到服务器发来的ack
    进入ESTABLISHED
    由LISTEN状态进入SYN-RECEIVED
    进入SYN-SENT状态


    图8
     
    Tcp链接建立过程在服务器端不会产生TIME_WAIT状态,因此本文不作为重要知识点讲解。
     
        断开链接时,需要经过四次握手协议,断开链接时的握手协议如下图9所示。
     
     

    发送FIN表示没有数据发送给客户端了,接着进入
    FIN-WAIT-1
    CLOSED
    TIME-WAIT
    LAST-ACK WWAITWAIT
    FIN-WAIT-2
    CLOSE-WAIT WWAITWAIT



           
     
    服务器端
       
    客户端
     

     
     
     

    图9
        Linux系统下,TCP连接主动断开后,会以TIME_WAIT状态保留一定的时间,然后才会释放端口。当并发请求过多的时候,就会产生大量的TIME_WAIT状态的连接,无法及时断开的话,会占用大量的端口资源和服务器资源。这个时候我们可以优化TCP的内核参数,来及时将TIME_WAIT状态的端口清理掉。
        通过 ab.exe 模拟1000个客户端发起200000个并发请求,然后统计linux tcp等待个数,结果如下图10:

    图10
    从图7中可以看出,有24986个链接等待。
     
    接下来我们优化一下TIMEWAIT,然后再统计。
    # vi /etc/sysctl.conf
    在文件中加入下面几行内容:
    net.ipv4.tcp_syncookies = 1    开启SYN Cookies,当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
    net.ipv4.tcp_tw_reuse = 1    开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
    net.ipv4.tcp_tw_recycle = 1   开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭;
    net.ipv4.tcp_fin_timeout = 30    修改系統默认的 TIMEOUT 时间,
    查看系统默认timeout时间: # cat /proc/sys/net/ipv4/tcp_fin_timeout  默认为60秒。

    接下来,优化tcp  keepalive时间,在sysctl.conf中增加以下几行:
    net.ipv4.tcp_keepalive_time = 1200 #TCP发送keepalive消息的频度。默认为2小时,改小。tcp链接在空闲 1200秒之后,内核发起关闭链接probe
    net.ipv4.tcp_keepalive_intvl = 30
    net.ipv4.tcp_keepalive_probes = 3 #如果probe 3次(每次30秒)不成功,内核才彻底放弃
    接下来优化其它一些参数:
    net.ipv4.ip_local_port_range = 1024 65000
    #向外连接的端口范围。默认为32768到61000,范围改大
    net.ipv4.tcp_max_syn_backlog = 2148
    #SYN队列的长度。默认为1024,改大
    net.ipv4.tcp_max_tw_buckets = 5000
    #系统同时保持TIME_WAIT套接字的最大数量,超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000
    net.core.netdev_max_backlog = 1000
    #进入包的最大设备队列,默认300,改大
    net.core.somaxconn = 511  # socket的监听队列backlog的上限,默认值为128,二nginx的默认为511 限制了nginx发挥,NGX_LISTEN_BACKLOG 511
    修改完之后,执行# /sbin/sysctl –p  让设置生效
    关于网络参数详解及优化,请参考我这篇文章:
    http://blog.csdn.net/neubuffer/article/details/16859829
    也可以直接下载优化过的文件放到/etc 目录下:http://pan.baidu.com/s/1uyZ7s
     
    重新通过 ab.exe 模拟1000个客户端发起200000个并发请求,然后统计linux tcp等待TIMEWAIT个数 netstat -tnp | awk '{print $6}' | sort | uniq -c ,结果如下图11:

    图11
    从图11中可以看出,链接等待数大量降低,节省了资源。
    但是此时并发量,还是在6000多,没什么提升,这是为什么呢?
    这时候看一下nginx的错误日志,# tail nginx/logs/error.log 结果如图12所示:
    E:aiduyundownload传智播客lnmp优化搜狗截图20131121021437.png
    图12
    从错误日志结果中可以看出,打开的文件太多,出现这个错误的原因主要是由于linux的打开文件描述符的数目过小造成的。
    需要修改文件描述符限制。接下来优化内核文件描述符参数。
     
    3.2 内核调优——调整文件描述符限制
        查看用户文件描述符限制:
        # ulimit –Hn   查看硬限制
        # ulimit –Sn   查看软限制

     
    增大文件描述符限制:
    查看系统file-max参数,系统所有进程一共可以打开的文件句柄描述符数量
    # cat /proc/sys/fs/file-max  结果为183972   在这里够用了不需要修改。
    此值一般默认为系统内存的10%(系统内存以kb计算),一般够用,根据情况可以调大该值。通过编辑/etc/sysctl.conf 添加内容:fs.file-max=102400   来调整。
     
        由此可见本实验中出现的问题由于用户软硬文件句柄数过小导致的,那么我们增加当前用户软硬文件句柄数:
        # vi /etc/security/limits.conf  添加以下内容,表示限制所有用户打开文件句柄数的软硬限制到102400个。你也可以根据系统需要,限制某个用户。
    * hard nofile 102400
    * soft nofile 102400
    保存,重启linux让刚才的修改生效。
     
    File-max 和 ulimit 的区别是前者指的系统所有文件描述符限制,针对整个系统;而后者指的是用户的文件描述符限制,针对的是用户。
     
    重新通过 ab.exe 模拟1000个客户端发起200000个并发请求,然后统计linux tcp等待个数 netstat -tnp | awk '{print $6}' | sort | uniq -c ,结果如下图13:


    图13
    看一下系统各项性能指标,如图14所示:

    系统整体CPU 及 IO

    CPU

    磁盘

    网络
    图14
        从结果中可以看出,与优化前相比没有太大提升。
       
    这时候再看一下nginx的错误日志,# tail nginx/logs/error.log  还是有打开文件过多错误。但是,这时候看一下 /var/log/message  里面已经没有警告了。
    继续找原因,猜想是不是nginx的限制呢?
     

    进行nginx优化

    前面修改了linux的文件描述符限制及linux内核参数,接下来优化nginx:
    修改 # vi nginx/conf/nginx.conf
    1 增加nginx工作进程数并绑定到不同的CPU上,本文所用的硬件有4个CPU,顾开辟4个进程,如下:
    worker_processes  4;
    worker_cpu_affinity 0001 0010 0100 1000;  #绑定进程和CPU对应
     
        2 增加每个进程打开文件句柄数,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n的值保持相当。
            worker_rlimit_nofile 50000; 
       
    3 采用epoll 模式,并增大每个进程连接数限制:
        events {
        use epoll;   //异步非阻塞I/O模型
        worker_connections  204800;
    }
       
        4 设置客户端请求头部缓冲区大小为系统分页大小的整数倍,可以用命令查看系统分页大小:# getconf PAGESIZE 如果经常出现 400错误,则和值有关,调大即可。
        http{
    client_header_buffer_size    4k; 
            large_client_header_buffers  4 4k;
    }
     
     
    5 打开请求文件缓存
        http{
        open_file_cache max=102400 inactive=60s;
        说明:打开文件指定缓存,默认是没有启用的,max 指定缓存数量,建议和打开文件数一致,inactive 是指经过60s时间文件没被请求后删除缓存。
        open_file_cache_valid 80s;
    说明:80s 检查一次缓存的有效信息。
    open_file_cache_min_uses 1;
    说明:文件使用次数,判断有效与否。inactive 时间内一次没被使用,它将被移除。
    }
     
        重启nginx:# service nginx restart
     
        再次通过 ab 模拟1000个客户端发起200000个并发请求,然后统计linux tcp等待个数 netstat -tnp | awk '{print $6}' | sort | uniq -c ,结果如下图15:


    图15
    看一下系统资源利用情况,如下图16所示:
    系统整体CPU及IO

     
    CPU

    磁盘

    网络

    图16
    由以上结果可见,优化之后并发达到1.1万多。并且TIMEWAIT数量大大减少,性能有明显提升。
    实际上此时并发可以达到将近2w,由于ab并发性能成为瓶颈。我们可以开3个ab,每个ab分别并发1000发送20万个请求。实验结果如下图17所示:


    图17 每个并发达到0.62万 总共并发大于1.8万
     
        系统资源利用情况如下图18所示:

    系统整体CPU及IO
     

    CPU

    磁盘

    网络

    内存空闲率
    图18
        由结果可知,优化之后,只用了不到200M内存,并发请求达到近2W,系统并发有了很大提升。其中,三个AB程序占了200M内存中的近50%。

    进行mysql优化

    提升mysql性能需要从三个方面着手,首先是硬件,提升硬件配置效果最明显;其次是mysql参数配置优化;还有数据读写架构优化。本文主要讲mysql参数配置优化,顺便解释一下硬件配置和程序中使用mysql架构优化问题。
    对于硬件配置这不用说,任何服务都是基于硬件之上的,提升硬件配置,原则上会提升服务效率,比如使用更大内存,使用磁盘高级raid,数据分区使用固态IO卡,使用xfs文件系统等。
    对于mysql架构设计优化,这个非常重要,sql语句优化,表存储引擎(MyISAM,
    InnoDB)选择,使用索引,增加redis或者memcached缓存层等。将传统web服务的2层架构(webserver+db)变成三层架构(webserver+cache+db)甚至四层架构(webserver+logicserver+cache+db),由cache来承担分流大并发读写操作。
     
    对于存储引擎选择有2个原则:第一个原则,大量读少量写 选用MyISAM,大量写少量读选用InnoDB。针对不同的需求使用不同的存储引擎。第二个原则,能不用InnoDB尽量不用InnoDB。总之,如果你想追求99.9%的稳定性,方便的扩展性和高可用性还是尽量用MyISAM吧。
    至于为什么呢?
    1         MyISAM的索引和数据是分开的,并且索引是有压缩的,内存使用率就对应提高了不少。能加载更多索引,而InnoDB是索引和数据是紧密捆绑的,没有使用压缩从而会造成InnoDB比MyISAM体积庞大不少。
    2        InnoDB的行级锁是相对的,那个只有where主键时是有效,非主键的都会锁全表的。如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”
    3        MyISAM相对简单所以在效率上要优于InnoDB。小型应用使用MyISAM是不错的选择。另外,MyISAM表是保存成文件的形式,在跨平台的数据转移很方便。
    总之,尽量不使用InnoDB,InnoDB主要用于需要外键,事务等企业级支持,代价是速度比MyISAM有倍数的下降。
       
    下面主要讲mysql参数配置优化:
    准备一个工具:http://pan.baidu.com/s/1M8Ectuning-primer.sh  mysql性能报告工具。
        在开始优化之前先测试一下当前mysql并发情况及系统资源消耗情况。用mysql自带的并发测试工具,测试myisam及innodb引擎,开启100个并发分别写入20000条数据并且查询20000次。统计消耗时间及系统资源使用情况。
        下图19结果展示了分别读写myisam和innodb效率:


    图19
    下图20是对应的资源消耗情况:

    系统整体CPU及IO

    CPU

    磁盘


    内存
    图20
    通过结果可以看出mysql占用内存太高。写入2万次myisam引擎平均需要2秒钟,innodb平均需要70秒钟,写入时瓶颈出现在IO。读取2万次 两种引擎需要时间相当,4.3秒左右,读取瓶颈出现在CPU。
     
    接下来通过运行tuning-primer.sh工具,获取初步配置建议:
    # chmod +x tuning-primer.sh
    # sh tuning-primer.sh    根据提示继续
    结果中红色部分是当前配置不恰当的地方,根据实际情况进行优化。
     
    接下来我们开始一步一步优化 /etc/my.cnf   
    本文重点以优化MyISAM 引擎为例。在[mysqld]部分增加以下内容:
    1关闭innodb引擎
        default-storage-engine=MYISAM
    innodb=OFF
    2 开启慢查询日志记录,便于抓出查询速度较慢的语句进行优化查询。
        long_query_time=2 #当某个查询时间超过2s时记录下来
    log-slow-queries= /usr/local/webserver/mysql/logs/slowquery.log
     
    3 调整max_connections
        允许的同时链接的客户端数量,默认数值是100。如果经常看到 Too many connections 错误,则增大该值。小型服务器一般默认100就够了,本文这里设置为200
     
    3 优化myisam 写入效率。
        分别并发100,200,写入10万次,统计系统资源使用情况,如图21所示:
    bin/mysqlslap --concurrency=100 --iterations=1 --number-int-cols=4 --number-char-cols=35 --auto-generate-sql --auto-generate-sql-add-autoincrement --auto-generate-sql-load-type=write --engine=myisam --number-of-queries=100000 --debug-info --verbose -uroot -predis123



    并发100系统整体CPU及IO            并发100空闲内存
     

    并发200系统整体CPU及IO            并发200空闲内存
     
    图21
    由上图资源使用情况可见,myisam写入的主要瓶颈在系统CPU和IO,所以通过配置参数来做写入优化提升空间有限,这时候就得考虑做数据库集群了。
     
    优化myisam 读取效率
        分别并发100,200,读取10万次,统计系统资源使用情况,如图22所示:
    bin/mysqlslap --concurrency=100 --iterations=1 --number-int-cols=4 --number-char-cols=35 --auto-generate-sql --auto-generate-sql-add-autoincrement --auto-generate-sql-load-type=read --engine=myisam --number-of-queries=100000 --debug-info --verbose -uroot -predis123


    并发100系统整体CPU及IO            并发100空闲内存
     


    并发200系统整体CPU及IO            并发200空闲内存
    图23
    由上图资源使用情况,myisam并发读主要瓶颈在CPU,且内存空闲率也比较多。下面通过优化myisam参数,提升并发读效率。修改 /etc/my.cnf  mysql配置文件。
        4.1 增加  key_buffer_size
           用于索引块的缓冲区大小,增加它可更好处理索引,如果只是使用MyISAM表,可以把它设置为可用内存的 30-40%。具体根据mysql当前负载情况设置,要看Key_reads和 Key_read_requests比例,如果Key_reads 从硬盘读取键的数据块的次数。如果Key_reads较大,则Key_buffer_size值可能太小。可以用Key_reads/Key_read_requests计算缓存损失率。
        
        本文将key_buffer_size设置为 256M
           4.2 增加read_buffer_size大小
           顺序读取数据时的缓冲区大小,该参数分配的内存也是以每连接为单位的。read_buffer_size 是用来当需要顺序读取数据的时候,无法使用索引的情况下的全表扫描,全索引扫描等情况下,会按照数据的存储顺序依次读取数据块,每次读取的数据快首先会暂存在 read_buffer_size 中,当 buffer 空间被写满或者全部数据读取结束后,再将 buffer 中的数据返回给上层调用者,以提高效率。
        本文将read_buffer_size 设置为 2M。
        4.3 增加 query_cache_size 大小
            该参数对应 缓存sql语句及其结果的功能。在下次接收到同样的查询请求时,不再执行实际查询处理而直接返 回结果,有这样的查询缓存能提高查询的速度,使查询性能得到优化,前提条件是你有大量的相同或相似的查询,而很少改变表里的数据,否则没有必要使用此功 能。注意:如果你查询的表更新比较频繁,而且很少有 相同的查询,最好不要使用查询缓存。设置完之后最好是跟踪一段时间,查看是否运行良好。在一定的负载压力下,如果缓存命中率太低了,就启用它。
        本文将query_cache_size设置为32M
        4.4另外还有其它参数,根据系统情况调整。
    thread_cache — 线程的创建和销毁的开销可能很大,因为每个线程的连接/断开都需要。我通常至少设置为 16。
    table_cache — 缓存打开的表。通常要加大缓存数量,使得足以最大限度地缓存打开的表。如果你有200多个表的话,那么设置为 1024 也许比较合适(每个线程都需要打开表),如果连接数比较大那么就加大它的值。
    关于INNODB的参数本文暂不作讲解。
     
    保存以上修改,重启mysql,同样分别并发100,200 读取10万次,统计消耗时间,及系统资源使用情况,如下图24所示。


    并发100系统整体CPU及IO            并发100空闲内存
     
     
     
     


    并发200系统整体CPU及IO            并发200空闲内存
    图24
    由此可见,优化之后与优化之前对比,myisam整体读取性能有了很大提升。
     
     

    进行php优化

     
    对于一个web服务来说,除去mysql瓶颈,最影响性能的瓶颈是php等动态语言。而数据库瓶颈,可以通过第三方解决方案缓解,比如采用分布式数据库,采用内存数据库加速等。解决了数据库瓶颈,接下来就要解决动态语言php瓶颈了。好的解决办法无非这几种:
    1 页面静态化,尽量避免请求动态解析。
    2 采用第三方php加速器,通过缓存PHP代码编译后的结果来提高PHP脚本的性能,优化PHP代码执行速度,降低服务器负载,可以提高PHP应用执行速度最高达几倍,甚至十几倍。比较有名的加速器有,Zend Opcache,xcache,eAccelerator。
    在php 5.5 及以上的版本中已经内置了Zend Opcache,而本文用的php 5.5.5,因此不需要做这步优化了。
        3 通过配置php参数,充分利用系统硬件资源,从而提高服务效率。本文主要讲解这一部分。
        在优化之前还是做个测试,并统计系统资源使用情况。
        首先编写一个php页面,使用ab工具,启动1000个并发,发起1万个请求。
    测试结果如下图25所示。


    系统整体CPU及IO情况内存
    图25
    每秒系统处理请求 166.34个并发,非常低。并且通过资源消耗情况看,在请求开始的前10s CPU使用率非常高,接下来接着CPU使用率突降,IO一直波动。另外,内存使用一直较低。
    接下来查看一下系统日志及nginx错误日志:
    # tail /var/log/message

    系统提示类似洪水攻击。

    Nginx错误日志中则出现大量链接php错误

    另外,TIME_WAIT数也相对过高
        由此推断,php连接数配置过低?
        下面开始优化php配置,vi php/etc/php-fpm.conf
        1 增大php-fpm对打开文件描述符的限制
            rlimit_files = 65536
        2设置允许访问Fastcgi进程解析器的IP地址,更加安全
            listen.allowed_clients = 127.0.0.1
        3 增大请求缓冲队列大小
            listen.backlog = 2048      默认为128,有点小,增大这个参数,可以解决系统日志中攻击提示问题以及nginx错误日志中的链接错误。
        4  pm.max_requests = 1024由 500改为1024
        此时再做测试,结果如下图26所示:
          


        并发提升了,达到882.28个每秒,比优化前的166.34 提升了5倍之多,并且TIME_WAIT数量也大大减少,降到合理范围内。

    系统整体CPU及IO使用情况

    内存使用情况
    图26
     
        继续优化,达到并发5000以上,才告一段落:
    在nginx配置中使用fast_cgi缓存,在http段加入以下内容:
    fastcgi_temp_path /dev/shm/fastcgi_temp;
        #cache设置
        fastcgi_cache_path /dev/shm/fastcgi_cache levels=1:2
    keys_zone=cfcache:10m inactive=50m max_size=256;
    //为FastCGI缓存指定一个路径,目录结构等级,关键字区域存储时间和非活动删除时间。以及最大占用空间。
        fastcgi_cache_key "$request_method://$host$request_uri";
        fastcgi_cache_methods GET HEAD;
        fastcgi_cache   cfcache;  //开启FastCGI缓存并且为其制定一个名称。
    fastcgi_cache_valid 200 302 301 1h;
        fastcgi_cache_valid   any 5m;   //为指定应答代码指定缓存时间,这里指定200 302 301应答缓存1小时,其余任何应答缓存5分钟
        fastcgi_cache_min_uses  1;  //缓存在fastcgi_cache_path内文件在inactive参数值时间内的最少使用次数,如上例,这里在50分钟内某文件1次也没有被使用,那么这个文件将被移除。
        fastcgi_cache_use_stale error  timeout invalid_header http_500;//对于error timeout invalid_header http_500等类型的应答内容不缓存
        fastcgi_ignore_client_abort on;//忽略客户端终止请求
     
     
        重启nginx ,并发1000发送10万个请求,统计执行效率及系统资源消耗情况。如下图27所示。
        

    图27
    由测试结果可知,优化之后并发达到 6004.95个。系统CPU及IO利用率也比较合理,内存占用较小,整体比较理想。

    优化总结

    本文采用的测试机是普通的2G内存笔记本,再做优化没有多大意义,实际生产环境中在运行着大并发请求的真实服务器上进行优化比较准确。本文Lnmp优化到此告一段落吧。
    通过合理架构,合理代码逻辑,经过优化之后服务轻松可以达到并发1-2w。

    http://php.itcast.cn/news/20140211/10284488889.shtml

  • 相关阅读:
    树状数组和线段树
    N皇后问题(函数式编程与过程式)
    单例模式
    BitSet
    蓄水池抽样问题
    关于动态规划的一些感想
    53最大子序和
    5最长回文子串
    139单词拆分
    91.解码方法
  • 原文地址:https://www.cnblogs.com/ahwu/p/3544266.html
Copyright © 2011-2022 走看看