2017/08/18

puppeteer from Google Chrome

前幾天 Google 發布了 Puppeteer:一個可以控制 Chrome Headless 的 nodejs library。

透過 demo code 可以看到要產生 PDF 也好、要做 screen shot 也好,基本上都可以在數行以內解決:
const puppeteer = require('puppeteer');

(async() => {

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({path: 'example.png'});

browser.close();
})();

感覺就是拿來做壞事的好工具呀,也不用寫什麼 JavaScript emulator 去取得實際的網頁 DOM 狀態了 XD

官方文件已經有 Chrome Headless + Pupetteer 的網頁 debug demo:https://developers.google.com/web/updates/2017/04/headless-chrome

2017/08/17

rpm 列出套件時不顯示套件版本號

一般在尋找 CentOS 上到底安裝哪一些套件時,指令大概會這樣下:
$ rpm -aq | grep php-fpm
php71-php-fpm-7.1.7-1.el7.remi.x86_64

不過如果是要用 diff 比對二台機器安裝的套件是否相同,這個時候多了套件版本號就頭大了。

查了一下資料,rpm 有提供「--qf」(query-format) 可以自行設定列表要輸出的內容。

若使希望看到套件名稱,而不想列出套件版本時,指令可以這樣下:
$ rpm -aq --qf "%{NAME}\n" | grep php-fpm
php71-php-fpm

記得行尾要換行,不然就 GG 了 XD

2017/08/05

PHP strict typing performance

聽說 PHP 使用 strict type hint 會讓速度變慢,於是來做了個簡單的測試,先來個沒有 type hint 的:
<?php

function repeat($str)
{
    return $str;
}

$start = microtime(true);

for ($i = 0; $i < 100000000; $i++) {
    repeat('test');
}

$end = microtime(true);

var_dump($end - $start);

使用 PHP 7.1.8 執行,大概會花費 3.7 秒左右:
$ php main.php
float(3.713329076767)


接下來改成 strict type 的寫法:
<?php

declare(strict_types=1);

function repeat(string $str): string
{
    return $str;
}

$start = microtime(true);

for ($i = 0; $i < 100000000; $i++) {
    repeat('test');
}

$end = microtime(true);

var_dump($end - $start);

執行時間大概變成 5.3 秒左右:
$ php main.php 
float(5.3128638267517)

的確慢了不少,不過先別著急,不要因為慢了一些就打算放棄 strict type hint。

使用 PHP 5.6 去執行第一個測試 (沒有 strict type) 就大約需要 19 秒:
$ php5.6 main.php
float(19.477375030518)

其實把 PHP 版本升級上 7.x 才是正確的吧 XD

2017/08/02

Apache / Nginx 與 PHP 各種搭配方式的效能測試

以 PHP 為主的 HTTP server 有很多種架設方式,最傳統的大概就是 Apache + mod_php 來執行 PHP scripts。隨著技術一直在翻新,後來也有了 Nginx + php-fpm 的搭配模式出現,甚至連 PHP 在 5.4.0 都推出了 built-in server

因公司需要,就順手測試了以下幾種搭配:

在開始看壓力測試結果之前,先來說一下測試環境:
  • 所有的 HTTP server 除了以上搭配方式之外,其他都使用預設值 (像是不開啟 gzip 壓縮等)
  • 另外壓力測試的 PHP script 內容也很簡單,僅有「phpinfo();」一行
  • 1 core CPU
  • 4 GB RAM
  • 使用 ab (Apache Bench) 做測試
  • ab 中每個 concurrency 固定會發出 1000 個 HTTP request,如 concurrency = 1 時僅有 1000 個 request,concurrency = 100 時,則送出 1000 x 100 個 request。
  • 每次 ab 跑完一次壓力測試,均重新啟動 HTTP server 與 PHP server,以防止使用到 cache。

先簡單做個結論,詳細的壓力測試結果再請大家自行過目:
  • concurrency = 1 時,沒有壓力測試會出現的情況,不過可以把 response time 和伺服器的 CPU、記憶體使用量當作一個基準值作為參考。
  • Apache mpm-prefork 搭配 mod_php 的記憶體使用量最大,畢竟要 fork 一個 Apache instance 出來就要把 Apache 個 module 也 initialize 出來。小型伺服器不推薦這個方案。
  • php-fpm 即使設定「pm.max_children = 50」,也可以輕鬆負荷超過 50 個 concurrency。比較微妙的是從 htop 看不出 php-fpm 有使用 thread 來同時處理 request。
  • php-fpm listen on Unix socket 的效率比 listen TCP (127.0.0.1:9000) 的效率還要高一些,但是在高負荷的情況下會變得很不穩定,猜測和 OS 的 socket 實作、設定有關連。
  • PHP built-in server 遇到在高負載的情況下表現的不錯,唯獨 log 給的資訊太少,不方便做分析。但也可考慮透過 Nginx proxy 給 PHP built-in server,讓 Nginx 來處理 log。

最後做個廣告,Hackmd 真不錯用,筆記寫完最後還以轉換成 HTML 下載,有夠方便的啦!

2017/08/01

在 PHP 判斷參數為 anonymous function 的方法

在追各個 framework 的一些元件結構,anonymous function 已經被大量使用在不同的用途上了。

如果 function 要判斷外部傳進來的參數,是否為 anonymous function,有幾個作法。

一個是判斷是否可以被呼叫:
is_callable($param)

另外,由於所有的 anonymous function 其實屬於一個 PHP Closure 類別,所以也可以用下面這個方法做判斷 (且判斷結果還比較準確):
$param instanceof \Closure