摘要:順風車運營研發團隊黃桃現象在線上腳本機器寫入單條日志過長時會出現交叉打印的現象被交叉的日志很有規律,都是單條日志過長被截斷的,建議優化下此處寫入日志的字符串長度為原因分析腳本服務寫入日志代碼如下在調用方法寫入,為什么在寫入超長字符串是交叉
順風車運營研發團隊 黃桃
現象在線上腳本機器寫入單條日志過長時會出現交叉打印的現象:
被交叉的日志很有規律,都是單條日志過長被截斷的,建議優化下 /*/ruleanalysis.php:68 此處寫入日志的字符串長度為: int(25909)
腳本服務寫入日志代碼如下:
if ($this->isCli == true) { return file_put_contents($messageLogFile, $strLogMsg, FILE_APPEND); //在調用file_put_contents 方法寫入,為什么在寫入超長字符串是交叉寫呢? //跟進下file_put_contents函數的實現? }
查看file_put_contents 的源碼實現,最終寫文件會執行到_php_stream_write_buffer 函數,里面有這樣一處代碼:
明確幾個變量的含義:
count:需寫入文件的字符串長度
stream->chunk_size :默認為8192 (8k)
從上面代碼可以看出,當寫入的字符串長度 大于8192時,則拆為多次<=8192的字符串,然后調用php_stdiop_write函數寫入文件,php_stdiop_write函數實現如下:
static size_t php_stdiop_write(php_stream *stream, const char *buf, size_t count) { php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract; assert(data != NULL); if (data->fd >= 0) { #ifdef PHP_WIN32 int bytes_written; if (ZEND_SIZE_T_UINT_OVFL(count)) { count = UINT_MAX; } bytes_written = _write(data->fd, buf, (unsigned int)count); #else int bytes_written = write(data->fd, buf, count); #endif if (bytes_written < 0) return 0; return (size_t) bytes_written; } else { #if HAVE_FLUSHIO if (!data->is_pipe && data->last_op == "r") { zend_fseek(data->file, 0, SEEK_CUR); } data->last_op = "w"; #endif return fwrite(buf, 1, count, data->file); } }
php_stdiop_write 則調用的 write函數 寫入文件;write函數是能保證一次寫入的完整的。
所以日志寫串的原因也就能分析出來了,調用鏈接為:file_put_contents ->_php_stream_write_buffer ->php_stdiop_write(多次調用,每次最多寫入8192字節) ->write(),是在 多次調用php_stdiop_write 函數時出的問題;第一次寫完,緊接著在高并發的情況下,被其他進程的 write 函數追著寫,此時就出現寫串,也就是前面示例中日志;
為了證實此觀點,我對報錯的代碼 /**/ruleanalysis.php:68 寫了如下偽代碼:
public function run() { $this->mysqlConnect(); $sql = "select * from *** where ***=1"; $pidRet = $this->db->run($sql); UtilsLogger::notice("tiger_project_info:".json_encode($pidRet)); die; } vim ~/*****/logger.php if ($this->isCli == true) { var_dump(substr($strLogMsg ,16084 ,400 )); //在字符串的8192倍數的位置打出附近的字符串 16384 = 8192 * 2 return file_put_contents($messageLogFile, $strLogMsg, FILE_APPEND); }
執行代碼看打串日志的地方是否為8192倍數的位置,結果如下:
截斷的位置非常接近8192的倍數值;但因為定位時間不是當時的時間點,期間數據庫存在部分改動,所以出現偏移,那么也能驗證我們之前的猜想,正是file_put_contents 多次調用write函數的時候出現交叉打印。
問題解決:1、修改打日志處代碼,這么巨大的日志寫入文件是否合理?
2、需要寫入巨大日志又不希望不被交叉打印,加上LOCK_EX 標識;
FILE_APPEND文件追加的形式,這個是怎么實現的呢?
先標識 mode[0] =‘a’
緊接著轉換成:O_CREAT|O_APPEND
調用 open函數 ,fd = open(realpath, open_flags, 0666);
通過 C底層函數保證,寫入默認追加寫;
2、file_put_contents(filename,msg ,FILE_APPEND|LOCK_EX ) 中的 LOCK_EX實現?作用?file_put_contents在調用_php_stream_write_buffer 前加一個鎖 php_stream_supports_lock(stream) ->flock()
得到文件鎖定后 調用_php_stream_write_buffer->多次 write();
寫完后釋放文件鎖
php_stream_close(stream)->close(data->fd); //直接關閉
總結:LOCK_EX 保證了一個巨大字符串的完整,不會被寫串;
3、多進程,file_put_contents()數據覆蓋嗎?write調用路徑:file_put_contents ->_php_stream_write_buffer ->php_stdiop_write(多次調用,每次最多寫入8192字節) ->write()
file_put_contents($messageLogFile, $strLogMsg, FILE_APPEND);
write函數在O_APPEND模式下,偏移到文件末尾與寫文件是原子性的,不存在被覆蓋的情況;
還是寫在文件尾部,參考文章:https://blog.csdn.net/dog250/...
write函數代碼如下:
+static inline loff_t file_pos_read_lock(struct file *file) { + if (file->f_mode & FMODE_LSEEK) + mutex_lock(&file->f_pos_lock); return file->f_pos; } +static inline void file_pos_write_unlock(struct file *file, loff_t pos) { file->f_pos = pos; + if (file->f_mode & FMODE_LSEEK) + mutex_unlock(&file->f_pos_lock); } 修改sys_write系統調用: file = fget_light(fd, &fput_needed); if (file) { - loff_t pos = file_pos_read(file); + loff_t pos = file_pos_read_lock(file); ret = vfs_write(file, buf, count, &pos); - file_pos_write(file, pos); + file_pos_write_unlock(file, pos); fput_light(file, fput_needed); }5、進程內多次file_put_contents,open和close只有一次還是多次?
open調用路徑:file_put_contents->php_stream_open_wrapper_ex->php_plain_files_stream_opener->php_stream_fopen_rel->fd = open(realpath, open_flags, 0666);
close調用路徑:file_put_contents->php_stream_close->php_stdiop_close->ret = close(data->fd);
每次都會執行 open和close
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28905.html
摘要:順風車運營研發團隊黃桃問題現象某機器這段時間出現掉地的報警如圖原因分析查看當時的監控,等今天出現兩次突降,一次是點左右,一次是左右,查看整周也經常出現突降,如圖在分時突然升高也在時出現大量寫當時短暫出現降低,之后出現徒 順風車運營研發團隊 黃桃 問題現象某機器這段時間出現cpu-idle掉地的報警 如圖: showImg(https://segmentfault.com/img/bVb...
摘要:背景最近時運不佳,幾乎天天被線上問題騷擾。工具分析所以最好的方式就是不改動一行代碼把這個問題分析出來。我們選用了阿里以前開源的來使用。因為這個項目阿里多年沒有維護了,還殘留一些我在它原有的基礎上修復了個影響使用的,同時做了一些優化。 showImg(https://segmentfault.com/img/remote/1460000016978923?w=1920&h=1080); ...
摘要:本文介紹了企業互聯網開發及運維的一些實踐,深入剖析了互聯網項目開發及上線過程中的各種痛點及解決之道。線上出錯,我們通過收集服務器端應用性能數據的方式,實時展示應用的調用拓撲圖,并根據出現異常的請求,進行下鉆,定位出具體出現問題的代碼。 本文介紹了企業互聯網開發及運維的一些實踐,深入剖析了互聯網項目開發及上線過程中的各種痛點及解決之道。一個互聯網項目的上線并不是那么容易,需要經過很多的環...
摘要:很顯然對于不同規模,不同功能的系統,這個問題無法一概而論。生產事件上報客服上報此類問題往往來自用戶投訴,最重要的就是問題現象的復現。線上問題處理的核心是快速修復。以上說的都是問題發生后的消極應對措施。 前言一線程序員在工作中經常需要處理線上的問題或者故障,但工作幾年下來發現,有些同事其實并不知道該如何去分析和解決這些問題,毫無章法的猜測和嘗試,雖然在很多時候可以最終解決問題,但往往也會浪費大...
閱讀 3569·2021-11-15 11:36
閱讀 1060·2021-11-11 16:55
閱讀 695·2021-10-20 13:47
閱讀 2993·2021-09-29 09:35
閱讀 3428·2021-09-08 10:45
閱讀 2554·2019-08-30 15:44
閱讀 849·2019-08-30 11:10
閱讀 1428·2019-08-29 13:43