2016年5月7日 星期六

nginx 優化突破十萬並行連線數


nginx 優化突破十萬並行連線數
資料來源: http://www.yanghengfei.com/archives/326/
nginx 的安裝與基本配置文檔網上已經有很多了,但具體講優化的文章還比較少,偶爾發現有這麼一篇《nginx 優化突破
十萬並發》仔細拜讀後,轉至此做以收藏,感謝原作的辛苦編寫
一般來說 nginx 配置文件中對優化比較有作用的為以下幾項:
worker_processes 8;
nginx 進程數,建議按照 cpu 數目來指定,一般為它的倍數。
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000
10000000;
為每個進程分配 cpu,上例中將 8 個進程分配到 8 cpu,當然可以寫多個,或者將一個進程分配到多個 cpu
worker_rlimit_nofile 102400;
這個指令是指當一個 nginx 進程打開的最多文件描述符數目,理論值應該是最多打開文件數(ulimit -n)與 nginx 進程數
相除,但是 nginx 分配請求並不是那麼均勻,所以最好與 ulimit -n 的值保持一致。
use epoll;
使用 epoll I/O 模型,這個不用說了吧。
worker_connections 102400;
每個進程允許的最多連接數,理論上每台 nginx 服務器的最大連接數為 worker_processes*worker_connections
keepalive_timeout 60;
keepalive 超時時間。
client_header_buffer_size 4k;
客戶端請求頭部的緩衝區大小,這個可以根據你的系統分頁大小來設置,一般一個請求的頭部大小不會超 1k,不過由
於一般系統分頁都要大 1k,所以這裡設置為分頁大小。分頁大小可以用命令 getconf PAGESIZE 取得。
open_file_cache max=102400 inactive=20s;
這個將為打開文件指定緩存認是沒有啟用的max 指定緩存數量議和打開文件數一inactive 是指經過多長時
間文件沒被請求後刪除緩存。
open_file_cache_valid 30s;
這個是指多長時間檢查一次緩存的有效信息。
open_file_cache_min_uses 1;
open_file_cache 指令中的 inactive 參數時間內文件的最少使用次數,如果超過這個數字,文件描述符一直是在緩存中打
開的,如上例,如果有一個文件在 inactive 時間內一次沒被使用,它將被移除。
關於內核參數的優化:
net.ipv4.tcp_max_tw_buckets = 6000
timewait 的數量,默認是 180000
net.ipv4.ip_local_port_range = 1024 65000
允許系統打開的端口範圍。
net.ipv4.tcp_tw_recycle = 1
啟用 timewait 快速回收。
net.ipv4.tcp_tw_reuse = 1
開啟重用。允許將 TIME-WAIT sockets 重新用於新的 TCP 連接。
net.ipv4.tcp_syncookies = 1
開啟 SYN Cookies,當出現 SYN 等待隊列溢出時,啟用 cookies 來處理。
net.core.somaxconn = 262144
web 應用中 listen 函數的 backlog 默認會給我們內核參數的 net.core.somaxconn 限製到 128,而 nginx 定義的
NGX_LISTEN_BACKLOG 默認為 511,所以有必要調整這個值。
net.core.netdev_max_backlog = 262144
每個網絡接口接收數據包的速率比內核處理這些包的速率快時,允許送到隊列的數據包的最大數目。
net.ipv4.tcp_max_orphans = 262144
系統中最多有多少個 TCP 套接字不被關聯到任何一個用戶文件句柄上。如果超過這個數字,孤兒連接將即刻被復位並打
印出警告信息。這個限制僅僅是為了防止簡單的 DoS 攻擊,不能過分依靠它或者人為地減小這個值,更應該增加這個值
(如果增加了內存之)
net.ipv4.tcp_max_syn_backlog = 262144
記錄的那些尚未收到客戶端確認信息的連接請求的最大值。對於有 128M 內存的系統而言,缺省值是 1024小內存的系
統則是 128
net.ipv4.tcp_timestamps = 0
時間戳可以避免序列號的捲繞。一個 1Gbps 的鏈路肯定會遇到以前用過的序列號。時間戳能夠讓內核接受這種異常
數據包。這裡需要將其掉。
net.ipv4.tcp_synack_retries = 1
為了打開對端的連接,內核需要發送一個 SYN 並附帶一個回應前面一 SYN ACK也就是所謂三次握手中的第二
握手。這個設置決定了內核放棄連接之前發 SYN+ACK 包的數量。
net.ipv4.tcp_syn_retries = 1
在內核放棄建立連接之前發送 SYN 包的數量。
net.ipv4.tcp_fin_timeout = 1
如果套接字由本端要求關閉,這個參數決定了它保持在 FIN-WAIT-2 狀態的時間。對端可以出錯並永遠不關閉連接,甚
至意外當機。缺省值是 60 秒。2.2 內核的通常值是 180 你可以按這個設置,但要記住的是,即使你的機器是一個輕
載的 WEB 服務器,也有因為大量的死套接字而內存溢出的風險,FIN- WAIT-2 的危險性比 FIN-WAIT-1 要小,因為它最
多只能吃掉 1.5K 內存,但是它們的生存期長些。
net.ipv4.tcp_keepalive_time = 30
keepalive 起用的時候,TCP 發送 keepalive 消息的頻度。缺省是 2 小時。
下面貼一個完整的內核優化設置:
net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.ipv4.tcp_max_tw_buckets = 6000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rmem = 4096 87380 4194304

2016年5月2日 星期一

Nginx try_files 里的一个坑

今天下午 blog 某管理员踩到了 nginx try_files 的一个坑,导致 WordPress 博客采用了固定链接的页面无法访问。此问题持续半小时后修复。
原来的配置是这样的:
        location / {
            try_files $uri $uri/ /index.php;
            index  index.html index.htm index.php;
        }
        location ~ \.php$ { ... }
修改成了
        location / {
            try_files $uri $uri/ /index.php =404;
            index  index.html index.htm index.php;
        }
        location ~ \.php$ { ... }
增加的这个 =404,似乎是让 index.php 找不到的时候返回 404。但这么一改,形如https://servers.blog.ustc.edu.cn/2014/09/ustc-telecom-link-failure/ 这样的链接返回的竟然是 index.php 的源码!这是怎么回事呢?
这要从 nginx try_files 的工作原理说起。以 try_files $uri $uri/ /index.php; 为例,当用户请求 http://servers.blog.ustc.edu.cn/example 时,这里的 $uri 就是 /example。try_files 会到硬盘里尝试找这个文件。如果存在名为 /$root/example(其中 $root 是 WordPress 的安装目录)的文件,就直接把这个文件的内容发送给用户。显然,目录中没有叫 example 的文件。然后就看 $uri/,增加了一个 /,也就是看有没有名为/$root/example/ 的目录。又找不到,就会 fall back 到 try_files 的最后一个选项 /index.php,发起一个内部 “子请求”,也就是相当于 nginx 发起一个 HTTP 请求到http://servers.blog.ustc.edu.cn/index.php。这个请求会被 location ~ \.php$ { ... }catch 住,也就是进入 FastCGI 的处理程序。而具体的 URI 及参数是在 REQUEST_URI 中传递给 FastCGI 和 WordPress 程序的,因此不受 URI 变化的影响。
配置修改之后,/index.php 就不在 fall back 的位置了,也就是说 try_files 在文件系统里找到 index.php 之后,就会直接把文件内容(也就是 PHP 源码)返回给用户。这里的坑就是 try_files 的最后一个位置(fall back)是特殊的,它会发出一个内部 “子请求” 而非直接在文件系统里查找这个文件。

2016年4月22日 星期五

Install the latest Nginx and PHP in Ubuntu 16.04 with OpenCC, GeoIP and Imagick

#Install Aptitude
apt-get update
apt-get install aptitude

#Install Ubuntu Essential
aptitude update
aptitude dist-upgrade
aptitude install dstat build-essential fail2ban git
aptitude install python-software-properties

#Install the Latest Nginx:
add-apt-repository ppa:nginx/development
aptitude update
aptitude install nginx

#Install the Latest PHP7.0:
add-apt-repository ppa:ondrej/php
aptitude update
aptitude install php7.0-fpm php7.0-cli php7.0-curl php7.0-mysql php7.0-dev php7.0-cli php7.0-common php7.0-mbstring php7.0-intl

#Install the Latest GeoIP:
aptitude install libgeoip-dev
aptitude install git
git clone https://github.com/Zakay/geoip.git
cd geoip
phpize
./configure --with-php-config=/usr/bin/php-config
make
make install
echo "extension=geoip.so" > /etc/php/7.0/mods-available/geoip.ini
ln -s /etc/php/7.0/mods-available/geoip.ini /etc/php/7.0/cli/conf.d/20-geoip.ini
ln -s /etc/php/7.0/mods-available/geoip.ini /etc/php/7.0/fpm/conf.d/20-geoip.ini

#Install the Latest Imagick:
aptitude install libmagickwand-dev libmagickcore-dev
git clone https://github.com/mkoppanen/imagick.git
cd imagick
phpize
./configure --with-php-config=/usr/bin/php-config
make
make install
echo "extension=imagick.so" > /etc/php/7.0/mods-available/imagick.ini
ln -s /etc/php/7.0/mods-available/imagick.ini /etc/php/7.0/cli/conf.d/20-imagick.ini
ln -s /etc/php/7.0/mods-available/imagick.ini /etc/php/7.0/fpm/conf.d/20-imagick.ini

#Install the Latest OpenCC
aptitude install cmake doxygen
git clone https://github.com/BYVoid/OpenCC.git
cd OpenCC
make
sudo make install
git clone https://github.com/NauxLiu/opencc4php
cd opencc4php
phpize
./configure --with-php-config=/usr/bin/php-config
make
sudo make install
echo "extension=opencc.so" > /etc/php/7.0/mods-available/opencc.ini
ln -s /etc/php/7.0/mods-available/opencc.ini /etc/php/7.0/cli/conf.d/20-opencc.ini
ln -s /etc/php/7.0/mods-available/opencc.ini /etc/php/7.0/fpm/conf.d/20-opencc.ini


#Install Railgun
echo 'deb http://pkg.cloudflare.com/ wily main' | sudo tee /etc/apt/sources.list.d/cloudflare-main.list
curl -C - https://pkg.cloudflare.com/pubkey.gpg | sudo apt-key add -
aptitude update
aptitude install memcached railgun-stable

The Best my.cnf for Percona 5.7

[mysql]
socket = /var/run/mysql/mysql.sock

[mysqld]
user = mysql

bind-address = 0.0.0.0
port = 3306

basedir = /usr/local/mysql
pid-file = /var/run/mysql/mysql.pid
socket = /var/run/mysql/mysql.sock
datadir = /var/data/mysql
tmpdir = /tmp

# Log Path
#lc-messages-dir = /var/log/mysql
#lc_messages = en_US
log-error = /var/log/mysql/error.log


explicit_defaults_for_timestamp
skip-name-resolve

sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
symbolic-links = 0

max_allowed_packet              = 16M
max_connect_errors              = 1000000


thread_stack                    = 192K
thread_cache_size               = 50
max_connections                 = 500
#max_heap_table_size            = 2G
#tmp_table_size                 = 2G
table_open_cache                = 16384
table_definition_cache          = 16384
open_files_limit                = 65536
#key_buffer_size                = 512M
query_cache_type                = 0
#default_storage_engine         = TokuDB
#default_tmp_storage_engine     = TokuDB


# Innodb Settings
innodb_buffer_pool_size                         = 12G
innodb_buffer_pool_instances                    = 8
innodb_buffer_pool_dump_at_shutdown             = 1
innodb_buffer_pool_load_at_startup              = 1
innodb_flush_method                             = O_DIRECT
innodb_log_files_in_group                       = 2
innodb_log_file_size                            = 4G
innodb_flush_log_at_trx_commit                  = 0
innodb_change_buffering                         = all
# innodb_additional_mem_pool_size               = 32M
innodb_file_per_table                           = 1
innodb_read_io_threads                          = 16
innodb_write_io_threads                         = 16
innodb_io_capacity                              = 50000
#innodb_log_buffer_size                         = 16M
innodb_thread_concurrency                       = 0
#innodb_lock_wait_timeout                       = 120
innodb_open_files                               = 65536
innodb_autoinc_lock_mode                        = 1
#innodb_log_write_ahead_size                    = 4096
innodb_flush_neighbors                          = 0


#tokudb_loader_memory_size                      = 10G
#tokudb_directio                                = 1
#tokudb_cache_size                              = 25G
#tokudb_commit_sync                             = OFF
#tokudb_fsync_log_period                        = 1000
#tokudb_analyze_in_background                   = ON
#tokudb_analyze_mode                            = TOKUDB_ANALYZE_RECOUNT_ROWS


# BINARY LOGGING #
#log-bin = /var/data/mysql/mysql-bin
#sync-binlog                    = 1
#expire-logs-days               = 14

# Slow Query Log
#slow_query_log_file = /var/log/mysql/slow-query.log
slow_query_log                  = 0
long_query_time                 = 3

Build Percona 5.7 on Ubuntu 16.04

apt-get update
apt-get install aptitude

aptitude update
aptitude dist-upgrade
aptitude install dstat build-essential fail2ban git
aptitude install cmake libncurses5-dev libaio-dev libreadline6-dev zlib1g-dev
aptitude update

groupadd mysql
useradd -g mysql mysql
mkdir -p /etc/mysql
mkdir -p /var/data/mysql
mkdir -p /var/log/mysql
mkdir -p /var/run/mysql
chown -R mysql:mysql /usr/local/mysql
chown -R mysql:mysql /var/data/mysql
chown -R mysql:mysql /var/log/mysql
chown -R mysql:mysql /var/run/mysql

> Download Percona Server Latest Source
wget https://www.percona.com/downloads/Percona-Server-5.7/Percona-Server-5.7.11-4/source/tarball/percona-server-5.7.11-4.tar.gz
tar xvzf percona-server-5.7.11-4.tar.gz
cd percona-server-5.7.11-4/
cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_CONFIG=mysql_release -DFEATURE_SET=community -DWITH_EMBEDDED_SERVER=OFF -DWITH_BOOST=~/boost -DDOWNLOAD_BOOST=1

make -j 8
make install

# If CMake got Error then remove CMakeCache.txt and run again.
rm CMakeCache.txt


cd /usr/local/mysql
cp ./support-files/mysql.server /etc/init.d/mysql
cp ./support-files/my-default.cnf /etc/mysql/my.cnf

# Add Path to system-wide enviroment variables applying to all users.
vim /etc/environment
Add "/usr/local/mysql/bin:" to the beginning of PATH
. /etc/environment

mysqld --initialize-insecure
#Regardless of platform, use --initialize for “secure by default” installation (that is, including generation of a random initial root password). In this case, the password is marked as expired and you will need to choose a new one. With the --initialize-insecure option, no root password is generated; it is assumed that you will assign a password to the account in timely fashion before putting the server into production use.

mysql_secure_installation
mysqld stop
# This command will ask you some inputs to set root password, remove anonymous users, disallow root login remotely, remove test database and reload privilege tables.

# Register Service
Create the service file in /etc/init.d/<service name>
chmod 700 /etc/init.d/<service name>

# Auto Start Up
update-rc.d mysql defaults
update-rc.d mysql enable

service --status-all