下雨天在家,原本想改寫某個專案的api,從php改寫成node.js,可寫到一半突然覺得到底有沒有這個必要,所以反正機器都有了,就寫了各種不同版本來實測看看效能怎樣。
每個版本的功能都一樣,也在同一台機器,連同一個資料庫,下一樣的sql指令,回傳一樣的檔案格式JSON。使用一樣的測試工具,一樣的參數,指令如下
ab -n 1000 -c 1000 -t 10 http://example.com/
首先第一號選手登場的是純粹apache+php7.2的組合,程式碼是這樣
<?php $servername = "mysql.example.com"; $username = "root"; $password = "123456"; $dbname = "test_menu_db"; // Create connection $conn = mysqli_connect($servername, $username, $password, $dbname); mysqli_set_charset( $conn, 'utf8'); // Check connection if (!$conn) { die("Connection failed: " . mysqli_connect_error()); } $sql = "SELECT * FROM menu limit 200"; $result = mysqli_query($conn, $sql); $myArray = mysqli_fetch_all($result,MYSQLI_ASSOC); header("Content-Type:text/html; charset=utf-8"); header('Content-Type: application/json; charset=utf-8'); $json = json_encode($myArray, JSON_UNESCAPED_UNICODE); echo $json; mysqli_close($conn); ?>
雖然這是最原始最古早的寫法,可是我試了幾次,包含試了後面的組合,覺得這其實是最簡單暴力,能簡單快速達到高效能的方法。但就是吃效能,如果有很多記憶體可以揮霍是沒差。下面是測試結果
第二號選手是用swoole,聽說是php的強化版,效能很棒棒就是了,可以看其他網路文章,下面是程式碼
<?php $http = new swoole_http_server("127.0.0.1", 8080); $http->set(array( 'worker_num' => 40, 'backlog' => 128, )); $http->on("start", function ($server) { //var_dump($server); echo "Swoole http server is started at http://127.0.0.1:8080\n"; }); $http->on("request", function ($request, $response) { $servername = "mysql.example.com"; $username = "root"; $password = "123456"; $dbname = "test_menu_db"; $conn = mysqli_connect($servername, $username, $password, $dbname); mysqli_set_charset( $conn, 'utf8'); $sql = "SELECT * FROM menu"; $result = mysqli_query($conn, $sql); $res_array = mysqli_fetch_all($result,MYSQLI_ASSOC); $response->header('content-type', 'application/json'); $response->end(json_encode($res_array)); }); $http->start();
他可以設worker_num數量,這個只有設1的話結果很慘,完成數只有兩位數而已,這邊我是故意設一個數據比較好看的,以下設40的結果,設100,完成數可達到1370。那這樣跟原始php比起來,其實我也不知道哪個好,因為我不知道原始的php開了多少個執行緒,只是swool比較可以彈性調整。
第三號選手登場的是node.js,使用express
const express = require('express') const app = express() const mysql = require('mysql') const pool = mysql.createPool({ host: "mysql.example.com", user: "root", password: "123456", database: "test_menu_db" }) app.get('/', function (req, res) { pool.query("SELECT * FROM menu", function (err, result, fields) { if (err) throw err; res.json(result); }); }) app.listen(8080, function () { console.log('Example app listening on port 8080!'); });
剛使用node.js連接mysql的時候,我不知道有連接池這種東西,一用才發現效能提升不少,下面是使用連接池的結果,只使用單一連線的話,完成數只有105
第四號選手一樣使用node.js,只是改用也是聽說效能很好的框架koa。這邊我把連接mysql的程式,封裝成promise了,封裝方法可看這裡,因為這個框架限制必須用promise,大概是因為這樣,效能才這麼好吧
const Koa = require('koa') const app = new Koa() const query = require('./database.js'); app.use(async ctx => { let result = await query("SELECT * FROM menu") ctx.status = 200 ctx.response.type = 'json'; ctx.response.body = result; }) app.listen(8080) console.log('Example app listening on port 8080!');
實測結果,果然就是比express快了一些,完成數528
這樣看起來好像node.js的性能比php還差,不過我想吃硬體資源的程度可能差很多,php可以很暴力的每個request都開一個執行緒,但node基本上是單執行緒的,要多執行緒執行node也是有辦法的,我上網查到了看這個
於是有了加開第五號選手,多執行緒的koa,express我就懶得試了,大概就是慢一點吧,我一次開了40個執行緒,仿照上面swoole的worker_num,結果如下,完成數達1028,有種封印解除的感覺。
那為了看node和swoole消耗的硬體資源,我用ps -aux指令來看占用的百分比。
swoole如果開了80個worker_number,完成的要求數有1344,平均每個執行緒使用了0.7%的記憶體,總共佔了0.7*80=56%,cpu使用率都顯示0,可能執行完就釋放了看不到。
node開了40個執行緒,完成的要求數是1028,平均使用記憶體2%,總共使用了80%,cpu使用率平均也大概2%,總共也約80%
以這個結果來看,swoole的cp值比較高。但以單一執行緒來說,node還是很高效,而且占用的資源也很低,相對cp值就高了很多,在使用硬體資源上也相對謹慎。
所以結論就是見仁見智,看個人喜好,哈哈,有說跟沒說一樣。個人還是比較偏好node啦,至少社群活耀,要找什麼套件資源或問題比較好找。
以後有空再來試golane,看有沒有真的那麼厲害。