之前做一次分享 如何快速學習一門新的語言的直播分享 但是那是以實現一個後端框架的角度來講的,道理想通,我們要以實際的需求出發。一個小小的需求,可能會遇到很多問題,但是搜尋相關的關鍵字,就能快速實現出來,完成一個小目標,事半功倍。
死記硬背手冊,太枯燥了,反正我是看不下去,不如直接來個小專案。下面開始:
一個小需求
pc、mobile 一個地址有兩套頁面,需要在後端根據瀏覽器的 user_agent 來顯示不同的頁面。
通過 php 來做,當然可以,但是活動頁面訪問量一般都比較大,想優化些,所以想嘗試下 lua。
nginx 安裝 lua-nginx-module
可以直接上 openresty,不過有時候就是想折騰。
安裝的步驟 https://mengkang.net/994.html (如果你想實踐的話再看吧)
lua demo 指令碼
-- 判斷是否是手機瀏覽器
function isMobile(userAgent)
-- 99% 前三個都能匹配上吧
local mobile = {
"phone", "android", "mobile", "itouch", "ipod", "symbian", "htc", "palmos", "blackberry", "opera mini", "windows ce", "nokia", "fennec",
"hiptop", "kindle", "mot", "webos", "samsung", "sonyericsson", "wap", "avantgo", "eudoraweb", "minimo", "netfront", "teleca"
}
userAgent = string.lower(userAgent)
for i, v in ipairs(mobile) do
if string.match(userAgent, v) then
return true
end
end
return false
end
-- 根據id + 瀏覽器型別展示活動頁面
function showPromotionHtml(id, isMobile)
local path = "/data/www/mengkang/demo/promotion/"
local filename
if isMobile then
path = path .. "mobile"
else
path = path .. "pc"
end
filename = path .. "/" .. id .. ".html"
if file_exists(filename) then
local file = io.open(filename,"r")
io.input(file)
print(io.read("*a"))
io.close(file)
else
print("檔案不存在: " .. string.gsub(filename, "/data/www/mengkang/demo", ""))
end
end
-- 判斷檔案是否存在
function file_exists(path)
local file = io.open(path, "rb")
if file then file:close() end
return file ~= nil
end
local id = 1
local userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
showPromotionHtml(id, isMobile(userAgent))
小結
作為一個 lua 菜鳥,通過這個小需求我查了哪些資料
變數的定義
函式的寫法
迴圈的瞭解
判斷邏輯的寫法
註釋的寫法
檔案 i/o
字串拼接 ..
字串查詢 string.match
字串轉小寫 string.lower
稍微調整適配 nginx lua 模組
-- 判斷是否是手機瀏覽器
function isMobile(userAgent)
-- 99% 前三個都能匹配上吧
local mobile = {
"phone", "android", "mobile", "itouch", "ipod", "symbian", "htc", "palmos", "blackberry", "opera mini", "windows ce", "nokia", "fennec",
"hiptop", "kindle", "mot", "webos", "samsung", "sonyericsson", "wap", "avantgo", "eudoraweb", "minimo", "netfront", "teleca"
}
userAgent = string.lower(userAgent)
for i, v in ipairs(mobile) do
if string.match(userAgent, v) then
return true
end
end
return false
end
-- 根據id + 瀏覽器型別展示活動頁面
function showPromotionHtml(id, isMobile)
local path = "/data/www/mengkang/demo/promotion/"
local filename
if isMobile then
path = path .. "mobile"
else
path = path .. "pc"
end
filename = path .. "/" .. id .. ".html"
if file_exists(filename) then
local file = io.open(filename,"r")
io.input(file)
ngx.say(io.read("*a"))
io.close(file)
else
ngx.say("file not found : " .. string.gsub(filename, "/data/www/mengkang/demo", ""))
end
end
-- 判斷檔案是否存在
function file_exists(path)
local file = io.open(path, "rb")
if file then file:close() end
return file ~= nil
end
local id = ngx.var.id
local userAgent = ngx.req.get_headers().user_agent
showPromotionHtml(id, isMobile(userAgent))
nginx 配置
server
{
listen 80;
server_name mengkang.net
location ~ /promotion/(\d+)
{
set $id $1;
default_type "text/html";
content_by_lua_file /data/www/lua/1.lua;
}
}
演示地址
https://mengkang.net/promotion/1
https://mengkang.net/promotio...
切換 user_agent 即可看到,不同的 pc 和 mobile 兩個版本的頁面
和 php 效能對比
nginx 配置
rewrite ^/promotion2/(.*)$ /demo/promotion.php last;
php 程式碼
<?php
header("Content-type:text/html;charset=utf-8");
header_remove('x-powered-by');
ini_set('display_errors', 'Off');
function getId(){
$uri = $_SERVER["REQUEST_URI"];
$tmp = explode("?",$uri);
$uri = $tmp[0];
$uri = trim($uri,"/");
$tmp = explode("/",$uri);
return intval(array_pop($tmp));
}
function isMobile()
{
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower ( $_SERVER['HTTP_USER_AGENT'] ) : '';
if ( preg_match ( "/phone|itouch|ipod|symbian|android|htc_|htc-|palmos|blackberry|opera mini|windows ce|nokia|fennec|hiptop|kindle|mot |mot-|webos\/|samsung|sonyericsson|mobile|pda;|avantgo|eudoraweb|minimo|netfront|nintendo/", $user_agent ) ) {
return true;
}
return false;
}
$id = getId();
$isMobile = isMobile();
if ($isMobile){
$filename = __DIR__."/promotion/mobile/".$id.".html";
}else{
$filename = __DIR__."/promotion/pc/".$id.".html";
}
if (file_exists($filename)) {
echo file_get_contents($filename);
}else{
echo "file not found : /promotion/pc/".$id.".html";
}
exit;
也就是說訪問 http://mengkang.net/promotion/1 和 http://mengkang.net/promotion2/1 是一樣的結果
配置說明
雙核4G
nginx 配置一致
php 版本:7.0.11
php-fpm 配置:
pm = dynamic
pm.max_children = 10
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 10
php 壓測結果
ab -n 1000 -c 100 http://mengkang.net/promotion2/1
Requests per second: 3105.21 [#/sec] (mean)
Time per request: 32.204 [ms] (mean)
ab -n 4000 -c 400 http://mengkang.net/promotion2/1
Requests per second: 3361.87 [#/sec] (mean)
Time per request: 118.981 [ms] (mean)
Complete requests: 4000
Failed requests: 259
ab -n 8000 -c 800 http://mengkang.net/promotion2/1
Requests per second: 3358.20 [#/sec] (mean)
Time per request: 238.223 [ms] (mean)
Complete requests: 8000
Failed requests: 654
ab -n 10000 -c 1000 http://mengkang.net/promotion2/1
Requests per second: 3275.30 [#/sec] (mean)
Time per request: 305.315 [ms] (mean)
Complete requests: 10000
Failed requests: 9150
lua 壓測結果
ab -n 1000 -c 100 http://mengkang.net/promotion/1
Requests per second: 6014.89 [#/sec] (mean)
Time per request: 16.625 [ms] (mean)
ab -n 4000 -c 400 http://mengkang.net/promotion/1
Complete requests: 4000
Failed requests: 0
Requests per second: 6190.57 [#/sec] (mean)
Time per request: 64.614 [ms] (mean)
ab -n 8000 -c 800 http://mengkang.net/promotion/1
Complete requests: 8000
Failed requests: 0
Requests per second: 7046.66 [#/sec] (mean)
Time per request: 113.529 [ms] (mean
ab -n 10000 -c 1000 http://mengkang.net/promotion/1
Complete requests: 10000
Failed requests: 0
Requests per second: 5670.38 [#/sec] (mean)
Time per request: 176.355 [ms] (mean)
對比可見
PHP qps 在 3000左右,nginx_lua qps 在 7000 左右。qps 提升了1倍多,而且響應時間更短,而且 php 在 400 個併發的時候開始出現比較多的失敗請求,吞吐率開始下降。而 lua 的結果在 1000 個併發的時候,失敗的請求數依舊是0。