Thursday, May 8, 2014

Apache MPM Worker + mod_fastcgi + PHP-FPM

Apache MPM Worker + mod_fastcgi + PHP-FPM

Quick Notes:
[] There are two kinds of Apache MPM (Multi-Processing Modules):
- prefork
- worker (threaded MPM)

[] Apache MPM:
- MPM prefork + mod_php
- MPM worker (threaded MPM) + mod_fastcgi
- MPM worker (threaded MPM) + mod_fcgid

Note: if you want to use either mod_fastcgi or mod_fcgid, be aware of that each process has its own APC opcode cache, which in turn wastes memory resource, unless you use PHP-FPM to share APC cache between processes.

[] 由於 php-fpm 是自帶的 FastCGI Process Manager , 所以和原本的 php fastcgi 有所不同 , php-fpm 若要搭配 Apache 只能以 mod_fastcgi 來運作 , 原本 Apache 有開發 mod_fcgid , 但 mod_fcgid 本身算是個 FastCGI Process Manager , 只能執行本地端 FastCGI , 而 php-fpm 可以用 Unix Socket 或 TCP 模式讓任何具備執行外部 FastCGI 程序的 WebServer 連接 , 因此 lighttpd 或 Ngix 等都可以 , 唯獨世上次爛的 Apache 要外掛 mod_fastcgi , 最爛的當然是 IIS 了 , 哈

[] PHP-FPM not only supports TCP/IP connections (ex: 127.0.0.1:9000) but also the socket based connections (ex: /tmp/php-fpm.sock).

The advantage of running PHP-FPM on socket connections instead of TCP/IP is that the socket connections are much more faster than TCP/IP connections (around 10-15%) because it saves the passing the data over the different layers of TCP/IP stack.
Therefore, it is recommended to run the PHP-FPM on socket connections over TCP/IP when you are using the same server for Apache and PHP-FPM. If you are using the different servers for Apache and PHP-FPM then the socket connections for PHP-FPM will not work.

[] the topic of sharing APC opcode cache between processes.

[] Apache 改為 MPM Worker 模式運作 (instead of MPM prefork)

甚麼是 worker 模式 ? 簡單來說就是 Apache 會搭配多行程 + 多執行緒的方式運作 , 每個行程會有多個執行續同時服務客戶端連線 , 記憶體及效能都較好 , 由於 php-fpm 或 php fastcgi 都已經是獨立於 Apache 行程了 , 所以 Apache 若再跑 prefork 模式就浪費了 , 因此可以切換為 worker 模式 。

[] Use mod_fastcgi's FastCgiExternalServer to point to PHP-FPM, no more wrapper script please!!

[] Most of apache config examples are outdated or wrong, or copy from other wrong articles.

[] PHP-FPM is a daemon and mod_fcgid's official site (http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html) is not talking about PHP-FPM, it is talking about php patched with fastcgi library http://www.fastcgi.com/drupal/node/5?q=node/10.

[] PHP-FPM is native in PHP since 5.3.3, so the mod_fcgid is not the choice for PHP user at the mean time, unless it provide Directive similiar to mod_fastcgi's FastCgiExternalServer.

[] Why shouldn't I use Apache2 with a threaded MPM in a production environment?
PHP is glue. It is the glue used to build cool web applications by sticking dozens of 3rd-party libraries together and making it all appear as one coherent entity through an intuitive and easy to learn language interface. The flexibility and power of PHP relies on the stability and robustness of the underlying platform. It needs a working OS, a working web server and working 3rd-party libraries to glue together. When any of these stop working PHP needs ways to identify the problems and fix them quickly. When you make the underlying framework more complex by not having completely separate execution threads, completely separate memory segments and a strong sandbox for each request to play in, further weaknesses are introduced into PHP's system.

If you want to use a threaded MPM, look at a FastCGI configuration where PHP is running in its own memory space.

http://php.net/manual/en/faq.installation.php

System Specifications
- FreeBSD 8.2-STABLE-201105
- apache-worker-2.2.19
- php5-5.3.6_1
- php5-extensions-1.5
- mysql-server-5.5.13
- ap22-mod_fastcgi-2.4.6_1
- pecl-APC-3.1.9_1
- memcached-1.4.5_2
- pecl-memcached-1.0.2

Install Apache
# cd /usr/ports/www/apache22-worker-mpm ; make install

Install PHP5
# cd /usr/ports/lang/php5 ; make install
or
# cd /usr/ports/lang/php52 ; make install

FPM=on "Build FPM version (experimental)"
MULTIBYTE=on "Enable zend multibyte support"
SUHOSIN=on "Enable Suhosin protection system"
MAILHEAD=on "Enable mail header patch"
LINKTHR=on "Link thread lib (for threaded extensions)"

/usr/ports/lang/php52 # make showconfig
===> The following configuration options are available for php52-5.2.17_2:
CLI=on "Build CLI version"
CGI=on "Build CGI version"
APACHE=off "Build Apache module"
DEBUG=off "Enable debug"
SUHOSIN=on "Enable Suhosin protection system (not for jails)"
MULTIBYTE=on "Enable zend multibyte support"
IPV6=off "Enable ipv6 support"
MAILHEAD=on "Enable mail header patch"
REDIRECT=off "Enable force-cgi-redirect support (CGI only)"
DISCARD=off "Enable discard-path support (CGI only)"
FASTCGI=on "Enable fastcgi support (CGI only)"
FPM=on "Enable fastcgi process manager (CGI only)"
PATHINFO=on "Enable path-info-check support (CGI only)"
LINKTHR=on "Link thread lib (for threaded extensions)"
===> Use 'make config' to modify these settings

Copy php.ini file
// For PHP5.3
# cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini

// For PHP5.2
# cp /usr/local/etc/php.ini-recommended /usr/local/etc/php.ini

Install PHP Extensions
# cd /usr/ports/lang/php52-extensions ; make install

/usr/ports/lang/php52-extensions # make showconfig
===> The following configuration options are available for php52-extensions-1.3_1:
BCMATH=off "bc style precision math functions"
BZ2=off "bzip2 library support"
CALENDAR=off "calendar conversion support"
CTYPE=on "ctype functions"
CURL=on "CURL support"
DBA=off "dba support"
DBASE=off "dBase library support"
DOM=on "DOM support"
EXIF=off "EXIF support"
FILEINFO=off "fileinfo support"
FILTER=on "input filter support"
FRIBIDI=off "FriBidi support"
FTP=off "FTP support"
GD=on "GD library support"
GETTEXT=on "gettext library support"
GMP=off "GNU MP support"
HASH=on "HASH Message Digest Framework"
ICONV=on "iconv support"
IMAP=off "IMAP support"
INTERBASE=off "Interbase 6 database support (Firebird)"
JSON=on "JavaScript Object Serialization support"
LDAP=off "OpenLDAP support"
MBSTRING=on "multibyte string support"
MCRYPT=off "Encryption support"
MHASH=off "Crypto-hashing support"
MING=off "ming shockwave flash support"
MSSQL=off "MS-SQL database support"
MYSQL=off "MySQL database support"
MYSQLI=on "MySQLi database support"
NCURSES=off "ncurses support (CLI only)"
ODBC=off "unixODBC support"
OPENSSL=off "OpenSSL support"
PCNTL=off "pcntl support (CLI only)"
PCRE=on "Perl Compatible Regular Expression support"
PDF=off "PDFlib support (implies GD)"
PDO=on "PHP Data Objects Interface (PDO)"
PDO_SQLITE=on "PDO sqlite driver"
PDO_MYSQL=on "PDO mysql driver"
PGSQL=off "PostgreSQL database support"
POSIX=on "POSIX-like functions"
PSPELL=off "pspell support"
READLINE=off "readline support (CLI only)"
RECODE=off "recode support"
SESSION=on "session support"
SHMOP=off "shmop support"
SIMPLEXML=on "simplexml support"
SNMP=off "SNMP support"
SOAP=off "SOAP support"
SOCKETS=off "sockets support"
SPL=on "Standard PHP Library"
SQLITE=on "sqlite support"
SYBASE_CT=off "Sybase database support"
SYSVMSG=off "System V message support"
SYSVSEM=off "System V semaphore support"
SYSVSHM=off "System V shared memory support"
TIDY=off "TIDY support"
TOKENIZER=on "tokenizer support"
WDDX=off "WDDX support (implies XML)"
XML=on "XML support"
XMLREADER=on "XMLReader support"
XMLRPC=off "XMLRPC-EPI support"
XMLWRITER=on "XMLWriter support"
XSL=off "XSL support (Implies DOM)"
YAZ=off "YAZ support (ANSI/NISO Z39.50)"
ZIP=off "ZIP support"
ZLIB=off "ZLIB support"
===> Use 'make config' to modify these settings

Install mod_fastcgi
# cd /usr/ports/www/mod_fastcgi ; make install

Edit httpd.conf
# vim /usr/local/etc/apache22/httpd.conf
LoadModule fastcgi_module libexec/apache22/mod_fastcgi.so
Include etc/apache22/extra/httpd-mpm.conf

Edit php.conf
# vim /usr/local/etc/apache22/Includes/php.conf
<IfModule prefork.c>
  LoadModule php5_module libexec/apache22/libphp5.so
  AddType application/x-httpd-php .php .html
  AddType application/x-httpd-php-source .phps
</IfModule>

<IfModule worker.c>
  FastCGIExternalServer /usr/local/sbin/php-fpm -socket /tmp/php-fpm.sock -idle-timeout 900
  AddHandler php-fastcgi .php
  Action php-fastcgi /usr/local/sbin/php-fpm.fcgi
  ScriptAlias /usr/local/sbin/php-fpm.fcgi /usr/local/sbin/php-fpm

  <Directory /usr/local/sbin>
    Options ExecCGI FollowSymLinks
    SetHandler fastcgi-script
    Order allow,deny
    Allow from all
  </Directory>
</IfModule>

DirectoryIndex index.php index.html index.htm

Edit php-fpm.conf:
# vim /usr/local/etc/php-fpm.conf
#listen = 127.0.0.1:9000
listen = /tmp/php-fpm.sock

listen.owner = www
listen.group = www
listen.mode = 0660

Edit rc.conf
# vim /etc/rc.conf
## fpm
php_fpm_enable="YES"

## apache
apache22_enable="YES"
apache22_http_accept_enable="YES"

Edit no-accf.conf
# vim /usr/local/etc/apache22/Includes/no-accf.conf
<IfDefine NOHTTPACCEPT>
### make sure you do comment out following two lines
### in order to make accf_http module (the 'httpready' Accept Filter) to work.
#AcceptFilter http none
#AcceptFilter https none
</IfDefine>

Start php-fpm, apache22:
# /usr/local/etc/rc.d/php-fpm start
# /usr/local/etc/rc.d/apache22 start

Make sure it's running as MPM Worker
# httpd -V
Server MPM: Worker

Benchmarking Apache to give us an impression of how our current Apache installation performs
# ab -c 100 -n 10000 http://web-test.local/index.php

測試心得
隨便寫個 <?php echo "hello world"; ?> 來測試

我用 ab -c xx -n xxxx 的方式來測試效能及穩定度 , 分別用 20 , 100 , 500 條連線來測試 50000 次 request

大概可以直接說結果 , 就不看數據了

在 20 及 100 條連線下 , mod_php 的執行時間大約在 3~4 秒 , php-fpm 則在 7~8 秒 , 看樣子 php-fpm 挺鳥的說 ~~

但 500 條連線就見真章了 , mod_php 會很不穩 , 中間會 lag , 所以測試的數據忽上忽下 , 有時 7 秒 , 有時 10 秒甚至 15 秒

而 php-fpm 仍然在 7~8 秒 , 非常穩定

這代表甚麼 ????

Apache worker 模式 + php-fpm 在一個連線數量很多很操的 Server 上會表現得很穩定 , 另外我要說一下 , mod_fastcgi 其實效能並不佳 , 很多測試數據都有說比 Apache 自己寫的 mod_fcgid 差很多 , 我自己也玩過 mod_fcgid + php 也是如此 , 但 mod_fastcgi 表現卻比較穩定 , mod_fcgid 在連線數量多及連續 request 次數多時也會有 lag 情形 , 因此, 有興趣尋求最佳php運作環境的人的可以試試看 lighttpd 或 ngix 甚至是號稱比 lighttpd 及 ngix 快很多的 cherokee 來搭配 php-fpm , 應該會有更棒的表現

後來想想 , hello world 在傳輸的數據太少 , 於是再多測試 <?php phpinfo(); ?> 這個方式

也是用 20 /100/500 條連線來 request 1 萬次

20 條連線時 , mod_php 最佳時間為 6.45 秒 , 而 php-fpm 為 6.42 秒 , 差不多 , 而且都不會 lag

100 條連線時 , mod_php 最佳時間為 6.43 秒 ,php-fpm 為 6.37 秒 , 還是差不多 , 而且都不會 lag

500 條連線時 , mod_php 最佳時間為 6.69 秒 , php-fpm 為 6.42 秒 , 看起來還是一樣 , 但 500 條連線時 , mod_php 發生 lag 情形較多次 , 也就是說可能會看到超過 10 秒以上的時間挺多次的

從上面的測試不難發現 , Apache fork 模式的架構仍有一定的瓶頸 , 而且在實際網站應用上不光只有 php , 還有其他的靜態網頁 , 如果使用 Apache+mod
_php 跑 , 這樣靜態網頁的效能會跟著被拖慢 , 而 Apache worker + php-fpm (或 php fastcgi) 的好處是 , Apache 只把 php 的處理權交出去 , 靜態網頁仍由 Apache 處理 , 這樣在整體效能及記憶體用量都會改善許多

若以單項只跑 php 來看 , mod_php 在一個人不多的網站其實是最快的 , 但當流量大到一定程度 , 除非多買硬體啦 , 若要節省成本 , 採用 FastCGI 架構才是最好的方式

Reference:
http://www.brandonturner.net/blog/2009/07/fastcgi_with_php_opcode_cache/
http://voidweb.com/2010/10/get-high-performance-php-fpm-socket-connections/
http://www.pigo.idv.tw/archives/832
http://benpptung.blogspot.com/2010/11/modfcgi-vs.html

Thursday, May 1, 2014

1萬個小時的錘鍊,造就真正的專精

莫札特(Wolfgang Amadeus Mozart)寫出經典的第九號鋼琴協奏曲時,已持續創作協奏曲10年;

投資大師華倫‧巴菲特(Warren Buffett)早在11歲時,就進行了人生第一筆股票投資交易;

英國搖滾天團披頭四(Beatles)在1964年聲名大噪前,至少已做過1200次現場演出;

微軟(Microsoft)創辦人比爾‧蓋茲(Bill Gates)在上大學前5年,幾乎天天都在寫程式。

這些「成功人士」的共同點是什麼?《異數》(Outliers)作者麥爾坎‧葛拉威爾(Malcolm Gladwell)認為,「成功最重要的關鍵似乎是準備,而非才能。」成功者都在專業生涯剛起步時,就開始密集練習日後藉以揚名立萬的專業,葛拉威爾將此稱之為「1萬小時的努力」。

愈有價值的能力,必須投入愈長時間

葛拉威爾以心理學家艾瑞克森(K. Anders Ericsson)的實驗,說明「勤於練習」的重要。

艾瑞克森把一所音樂學院主修小提琴的學生分成3組,研究發現,「可望成為國際級小提琴家」的第一組學生,在20歲前累計的練習時間已超過1萬小時;「可成為一般職業演奏家」的第二組學生,練習約8000小時;至於「只能當音樂老師」的第三組學生,約練習了4000小時。研究人員最後得出的結論是,「真正的專精,必須經過1萬個小時的錘鍊。」

「1萬小時的錘鍊」並非定律,練習時數究竟要抵達多少小時,才能達到真正的專精,也沒有精確、客觀的標準;然而專注、充分地練習專業技能,確實是成功不可或缺的條件。

聯強國際總裁杜書伍曾說,「愈專業化的東西、愈有價值的能力,學成的困難度愈高,必須投入的時間也愈長。」他並以「公車理論」說明培養實力、等待機會的道理:機會是所有人的公車,不會為了獨獨等你一個人而停下來。若在前往車站的路程中閒散緩慢,當公車駛來時,你仍距離車站20公尺,那是絕對搭不上車的;唯有自始至終保持全速前進的人,才能在公車駛來時,游刃有餘地上車。

熱情和毅力,抵禦練習時的孤寂

成功者通常擁有至少一種專業。所謂「專業」就是精通某一個領域,而精通形同「絕對的稱職」。領導學大師華倫.班尼斯(Warren Bennis)在《領導,不需要頭銜》(On Becoming A Leader)一書中,引述作家喬治‧李奧納德(George Leonard)對於「精通」的定義:「精通建立於勤奮不懈地練習,雖然這也是冒險所在。……無論是運動、藝術或其他工作,被稱為大師的人,都是無視於別人的想法,熱情地投入他們的天職。」

然而,不懈地練習,考驗的是人的意志力與熱情。杜書伍指出,「練習的過程,彷彿摸索著走過一條沒有盡頭的黑暗隧道,恐懼與懷疑交相煎熬下,人們往往會被逼出放棄的衝動。」

然而,熱情和毅力,是成功者的護身符。他們明白,「努力不一定成功,但是不努力一定不會成功」。群聯電子董事長潘健成在一支以他的創業故事為主題的紀錄片中提到,當年公司是以「1個月100萬元」的速度在燒錢,壓力雖然大到讓人心慌,但是基於不想解散團隊的不服輸性格,促使他和事業夥伴加速研發進程,以一天16小時、一周7天的拚勁持續地撰寫程式,終於在一年內做出產品,成功研發出全球第一支單晶片隨身碟。

年僅26歲的華裔設計師吳季剛,因為設計的禮服被美國總統夫人蜜雪兒‧歐巴馬(Michelle Obama)挑中,一炮而紅。看似少年得志的他,其實5歲起就喜歡看婚紗、幫洋娃娃設計衣裳,12歲便正式投入大師門下學習服裝設計。面對旁人異樣的眼光,吳季剛在家人的支持下持續地投入,也讓他比多數設計師同儕,足足多練習了10年。

全力投入,理性應變

在1萬小時的錘鍊過程中,有阻力、有挫折、有變數,除了願意投入時間精神、發揮毅力與熱情之外,要建立起真正的專業,還需要「能夠控制感情,以理性行動」。

《成功長青》(Success Built to Last)一書指出,成功的實踐家熱愛自己的工作,所以願意投入大量時間研究;而專注精神和專業知識,則讓他們能觀察到微妙的產業變化,掌握機會。

日本知名企管顧問大前研一則強調,真正的專業是「即使面對環境變化,也能發揮同樣的專業。」在他眼中,鈴木一朗和松井秀喜這兩位傑出的日籍棒球選手,便稱得上是真正的專業人士。相較於許多在美國大聯盟中「敗下陣來」的日本職棒選手,他們兩人即使去到陌生的異地,不但維持住一貫的高水平,甚至還能夠發揮應變能力,研磨出適應當地的新技能。

換言之,真正專業的企業經理人及領導者,即使面臨多變的環境與激烈的競爭,終究還是能夠臨危不亂,帶領組織走上正確的方向,不會讓自己的能力僅限於一時一地。

二十多年前,中美矽晶董事長盧明光在光寶集團擔任中階主管時,可說是公司的最佳救火隊員,子公司只要出現虧損,就會派他去整頓,而他也每次都不負所託,在一年內轉虧為盈。盧明光笑說,多年來在導電橡膠、連接器、半導體、太陽能、車用電子等產業的不敗經驗,總讓許多人以為他是「福將」,希望邀他入股帶來「幸運」。然而他的成功絕非偶然,而是在於「把公司當成生命一般來經營」的投入精神。

《從A到A+》(From Good to Great)一書中引用了一個經典寓言故事:「狐狸知道很多事情,但刺蝟只知道一件大事。」成功的實踐家,多半都具備刺蝟特質,專心致力於能夠點燃他們熱情的「一件事」,憑著努力、毅力與應變力,在專業的路上不斷突破創新。