想咨詢一下。前幾天在其他項目改了下異步日志(非php)效果還是杠杠的。
同步
現(xiàn)在服務(wù)器都ssd,寫入很快,日志沒必要異步。
class IndexController
{
public function index(Request $request)
{
Log::info(str_pad('', 500, '0'));
return 'ok';
}
}
windows單進程壓測每個請求磁盤寫入500字節(jié)數(shù)據(jù),QPS為高達3.7萬,也就是說單進程平均每次寫入500字節(jié)的日志僅需0.00002秒。然而一次數(shù)據(jù)庫讀寫0.001秒都算快的了。在那0.00002秒上優(yōu)化感覺沒啥必要。
如果寫5/10次呢。如果一個接口寫入日志的次數(shù)變多,很明顯的。當然不是所有的接口都有大量的日志寫入。但是同步日志其實是很明顯的,而且日志寫入一旦占了大頭的時候,多線程也沒辦法拯救的。所有的都會阻塞在寫入這一點。多線程利用了cpu. https://learnku.com/laravel/t/63523
如果日志寫入占大頭,比較劃算的方案是適當多開一些進程,能有效提高吞吐量。
稍微復雜一點的,可以寫一個monolog的handler,收集合并日志,當日志達到一個磁盤塊比如4k的時候?qū)懭氪疟P,最大限度降低磁盤寫入開銷。
monolog 只可以做到集中寫。異步的話,還是得做不小的改造。如果日志通過其他手段發(fā)送到日志服務(wù)器的話,你說的這個方案也可以。但是如果是本機的話,開多了也會被頻繁的寫等待所拖累。提升不了多少的。
日志一般都是依賴monolog,主要是為了統(tǒng)一標準;
自己如果想折騰異步,最簡單的方式就是自行實現(xiàn)monolog的handler;
當然monolog本身也一定程度支持異步,你使用他的redishandler或者amqphandler,然后消費端做日志落庫/文件;
當然如果你想折騰異步io,那么可以安裝libeio的php-ext eio,然后自行根據(jù)monolog handler來實現(xiàn)基于eio的異步文件寫入即可。
redishandler和amqphandler感覺性能也達不到題主要求,因為走網(wǎng)絡(luò)IO往往比本地磁盤更慢,雖然可以非阻塞調(diào)用,但是資源代價很大。eio感覺也不行,因為要調(diào)用eio_event_loop,猜測這個調(diào)用也是阻塞的。
建議自己實現(xiàn)monolog的handler,handler建立緩沖區(qū)緩沖日志,然后利用webman的event-loop(要用select,不能用epoll),在磁盤可寫時寫入日志。
當然阻塞IO比較多想提高并發(fā)最簡單的方案是多開一些進程。
緩沖區(qū)做日志的前提也得是消費能力>生產(chǎn)能力,不然緩沖區(qū)滿了一樣有問題,用共享內(nèi)存吧,把日志丟APCu,然后下游多個進程去寫,這樣走的是共享內(nèi)存,沒有文件io沒有網(wǎng)絡(luò)io,是最快的,只不過就是APCu有讀寫鎖罷了,但是也夠用了。
如果是單機應(yīng)用,我一般都會把log寫到/dev/shm里,反正一天就頂多1G,計劃任務(wù)每天移動到實盤上,缺點就是關(guān)機重啟的話沒落實盤的數(shù)據(jù)就沒了,但既然是單機應(yīng)用了,這個問題可以忽略
多機器的話其實也可以,無非是落到實盤再加個合并操作
Wind Framework 日志組件異步寫入,可以參考這里的實現(xiàn)。
https://wind-framework.github.io/docs/#/v0/zh-cn/component/log?id=%e5%bc%82%e6%ad%a5%e5%86%99%e5%85%a5