大綱:
- 前言
- 日志系統(tǒng)架構(gòu)是怎樣的
- 游戲分析有什么內(nèi)容
- 為什么要自己架一個系統(tǒng)
- FEN架構(gòu)
- 架構(gòu)圖
- Fluentd
- ElasticSearch
- NodeJS
- pusher
- logger
- analyser
- 用戶界面
- 總結(jié)
前言
最近我司需要做一個統(tǒng)一的游戲日志系統(tǒng)脖祈,要求有一定的通用性,能應(yīng)對公司所有的游戲業(yè)務(wù)。接下來分享一下這次日志系統(tǒng)的項目經(jīng)驗蝎毡。
日志系統(tǒng)架構(gòu)是怎樣的
目前流行的日志系統(tǒng)為ELK笛钝,由Beats壶熏、Logstash容客、Elasticsearch派昧、Kibana等組件共同實現(xiàn)特笋,但萬變不離其宗剃浇,一個基本的日志系統(tǒng)架構(gòu)類似如下:
游戲分析有什么內(nèi)容
游戲分析,與其它服務(wù)系統(tǒng)不同的是猎物,游戲內(nèi)的系統(tǒng)可能是天馬行空的虎囚,數(shù)據(jù)類型是多樣的,甚至頻繁變化的蔫磨。我們要在變化中總結(jié)到不變的內(nèi)容淘讥,例如系統(tǒng)經(jīng)濟(jì)產(chǎn)出,玩家物品消耗堤如,商店購買等進(jìn)行分析蒲列。所以這次的游戲日志系統(tǒng)要滿足以下需求:
- 記錄游戲日志窒朋,并隨時檢索日志;
- 分析玩家行為:玩家留存相關(guān)蝗岖,玩家物品消耗侥猩,商店消耗等有一定復(fù)雜度的分析;
- 能建立一個統(tǒng)一的日志系統(tǒng):一次性滿足未來游戲運營多樣性抵赢。
為什么要自己架一個系統(tǒng)
雖然ELK在安裝配置方面不算困難欺劳,插件眾多,例如Filebeat铅鲤,讀log文件划提,過濾格式,轉(zhuǎn)發(fā)邢享,但誰來生產(chǎn)這些log文件鹏往,沒有提及。實際上骇塘,業(yè)務(wù)具有多樣性伊履,只要有日志文件的地方,它就可以用绪爸。例如多數(shù)會使用Nginx進(jìn)行日志收集湾碎。我們也需要考慮到日志生產(chǎn)者的問題,責(zé)權(quán)分離奠货,需要單獨一臺機(jī)子進(jìn)行日志采集介褥。
游戲是一種技術(shù)與藝術(shù)結(jié)合的產(chǎn)品,數(shù)據(jù)龐雜递惋,形態(tài)各異柔滔,光日志埋點也花不少功夫復(fù)雜,但不能因此放棄治療萍虽。好的游戲日志睛廊,還可以幫我們還原玩家玩家畫像。游戲更新周期短杉编,數(shù)據(jù)變化大超全,需要提供更實時參照報表,為非技術(shù)人員更好友的查詢界面邓馒,才能更好的服務(wù)于游戲數(shù)據(jù)分析嘶朱。ELK 在這方面,基本解決了采集和儲存的問題光酣,但實現(xiàn)分析方面還不能滿足我們的需求疏遏。
經(jīng)過一翻思索,我們可以用現(xiàn)有工具,粘合多個套件财异,所以倘零,我們有了以下思路:
-
日志采集器:
利用Fluented作為日志文件采集器,生產(chǎn)者通過內(nèi)網(wǎng)HTTP發(fā)送到采集器上戳寸,那每個生產(chǎn)者同一內(nèi)網(wǎng)只要部署一個采集器即可呈驶,如果量特別大,可以多個庆揩,游戲的功能埋點可以統(tǒng)一俐东; -
轉(zhuǎn)發(fā)器:
利用NodeJS進(jìn)行 HTTP 轉(zhuǎn)發(fā)即可,前提是能按順序和分段讀取日志文件订晌,結(jié)合Fluented間接實現(xiàn); -
接收器與實時分析:
接收器可以用Koa實現(xiàn)蚌吸,Redis進(jìn)行緩存锈拨;同時用NodeJS另外一個進(jìn)程分析和日志入庫,分析行為羹唠,玩家畫像奕枢,得出報表,這些非日志源的數(shù)據(jù)佩微,可以放到MongoDB上缝彬,因為這些數(shù)據(jù)是修改性增長緩慢數(shù)據(jù),占用空間不大哺眯; -
儲存?zhèn)}庫:
ElasticSearch是個很好的選擇谷浅,能集群,可熱增減節(jié)點奶卓,擴(kuò)容一疯,還可以全文檢索,分詞夺姑; -
用戶界面:
Kibana針對 ElasticSearch提供良好的分析墩邀,結(jié)合原有的管理后臺系統(tǒng),我們自己實現(xiàn)了一套用戶界面盏浙。
FEN架構(gòu)
這個框架主要使用到了Fluentd眉睹,ElasticSearch,以及NodeJS废膘,我就稱它為 FEN 架構(gòu)吧竹海,如下圖。
架構(gòu)圖
上圖看出殖卑,這樣的日志架構(gòu)和第一個圖基本沒什么不同站削,只是多了后面的分析與分批入庫處理,并且大量使用了NodeJS孵稽。
注:在這里不會介紹各組件的詳細(xì)的安裝配置方法许起,網(wǎng)上有太多了十偶,怎樣使用好每一個組件才是關(guān)鍵。
先介紹我們用到的工具:
Fluentd
Fluentd是一個完全開源免費的log信息收集軟件园细,支持超過125個系統(tǒng)的log信息收集惦积。Fluentd在收集源日志方面非常方便而且高性能,通過HTTP GET就可以猛频,這類似于Nginx的日志記錄行為狮崩。它的優(yōu)點是,日志文件可以高度定制化鹿寻,例如我們這里每5秒生成一個文件睦柴,這樣每分鐘有12個文件,每個文件體積非常小毡熏。為什么要這樣做坦敌?下面會介紹。Fluentd還有非常多的插件痢法,例如直接存入MongoDB狱窘,亞馬遜云等,要是熟悉Ruby财搁,也可以自己寫插件蘸炸。
ElasticSearch
有人使用MongoDB進(jìn)行日志收集,是非常不明智的尖奔,只有幾千萬條還可以搭儒,如果半個月生產(chǎn)10億條日志呢?日志文件需要保存一個月甚至更長越锈,那么集群和硬盤維護(hù)就非常重要仗嗦。使用便利性也很重要,例如分詞檢索甘凭,在客服回溯玩家日志稀拐,分析游戲 BUG 的時候非常有用。下文的 ES 也是該組件的簡稱丹弱。
NodeJS
NodeJS不適合做 CPU 密集型任務(wù)德撬,但在網(wǎng)絡(luò)應(yīng)用方面還不錯,并且是我們正好熟悉的躲胳。日志系統(tǒng)并不對實時性要求并不高蜓洪,延時半小時以內(nèi)都是允許的,事實上坯苹,正常情況延時也就10來秒隆檀。下面的讀與轉(zhuǎn)發(fā)日志的Pusher,收集日志的logger,分析日志并數(shù)據(jù)落袋為安的的analyser恐仑,都是由NodeJS實現(xiàn)的泉坐。
下面繼續(xù)介紹用 NodeJS實現(xiàn)的每一個部分:
轉(zhuǎn)發(fā)器Pusher
上面說到,為什么Fluentd使用分割成多個小文件的方式裳仆,因為NodeJS在大文件處理方面并不友好腕让,并且要考慮到通過網(wǎng)絡(luò)發(fā)送到另一臺機(jī),轉(zhuǎn)發(fā)速度比讀慢太多了歧斟,所以必須實現(xiàn)續(xù)傳與斷點記錄功能纯丸。想想,如果讀幾百 M 的文件静袖,出現(xiàn)中斷后觉鼻,需要永久記錄上次位置,下次再從此處讀起队橙,這就增加了程序復(fù)雜度滑凉。NodeJS雖然有readline模塊,但測過發(fā)現(xiàn)并不如文件流那樣可控喘帚,訪模塊用于交互界面尚可。相反咒钟,如果日志分割成多個小文件吹由,則讀的速度非常高效,并且每5秒一個文件朱嘴,哪怕有上萬條記錄倾鲫,文件也大不到哪里去,內(nèi)存也不會占用太多萍嬉,在斷點續(xù)傳與出錯重試方面都能自如應(yīng)對乌昔。如果游戲日志增多,可以增加節(jié)點來緩解文件過大的壓力壤追。
為什么不直接讓日志生產(chǎn)者直接發(fā)到Koa上磕道?因為效率與帶寬。NodeJS的適合做網(wǎng)站行冰,但比專業(yè)的HTTP服務(wù)器要弱太多溺蕉,4核心主機(jī)面對3000QPS就吃力,更多的關(guān)于NodeJS的性能問題悼做,可以參考網(wǎng)絡(luò)文章疯特。在高并發(fā)量下,帶寬是個很大的問題肛走,尤其是需要做統(tǒng)一服務(wù)漓雅,面對的情況是日志機(jī)器與游戲并不在同一內(nèi)網(wǎng)中。在10萬日活下,帶寬超過了50M邻吞,非常嚇人组题,帶寬可是很貴的,過高的帶寬費用在這里性價比太低了吃衅。
Pusher的注意點:
- 批量轉(zhuǎn)發(fā):不要一條條日志發(fā)往踢,采用批量發(fā)送。根據(jù)單條日志文件大小徘层,如果是 JSON 數(shù)據(jù)峻呕,有10多個字段,那么每次請求發(fā)送50~100條發(fā)送都是沒問題的趣效,也就幾十 KB瘦癌;
- 串行序順發(fā)送:從時間小的文件,從文件關(guān)開始發(fā)跷敬,等待上一次發(fā)送請求完成再執(zhí)行下一次讯私;
- 發(fā)送失敗保存重試:如果某一次請求失敗,則保存到另外一個文件目錄西傀,以時間戳作為文件名斤寇,下次重試,盡可能保證數(shù)據(jù)完整性拥褂;
- 每100毫秒讀一次文件列表娘锁,檢查有沒有新的日志文件。雖然是每5秒產(chǎn)生一次日志文件饺鹃,但有可能出現(xiàn)效率下降導(dǎo)致發(fā)送速度跟不上而產(chǎn)生文件積壓莫秆,即使是空讀也是允許的,這不基本不占什么CPU悔详。第100毫秒的間隔不要使用setInterval镊屎,應(yīng)該在上一次文件發(fā)送完畢再setTimeout來執(zhí)行;
- 發(fā)送速度提供可變性茄螃,如果下面的logger效率低下缝驳,上面的100毫秒可以適當(dāng)放緩一些。
日志收集器logger
這里我們使用Koa作為日志采集器责蝠。使用Koa党巾,無論在性能還是開發(fā)效率上,都比expressJS高效霜医。我們還用到了Redis作為緩存齿拂,而不是直接在這里做分析任務(wù),是為了盡量提高與Pusher的對接效率肴敛,畢竟日志的生產(chǎn)速度是很快的署海,但網(wǎng)絡(luò)傳送是相對低效的吗购。
logger的注意點:
- 使用緩存緩存數(shù)據(jù),如Redis砸狞;
- 關(guān)注內(nèi)存:logger與pusher是兩臺機(jī)子捻勉,當(dāng)logger的緩存提升太快,也就是后面的分析與入庫速度跟不上了刀森,需要返回消息告知pusher放慢發(fā)送速度踱启;
- 安全驗證:簡單的方式是pusher發(fā)送時可以進(jìn)行md5驗證,logger驗簽研底;
- 如果使用Redis埠偿,在Redis 4.0以下,使用list記錄每條日志 ID榜晦,日志使用hash節(jié)省內(nèi)存冠蒋。在Redis 3.x不要使用Scan,它有BUG乾胶,就是Scan出的數(shù)量是無法確定的抖剿,就算明確指定了條數(shù),但有可能出現(xiàn)一次讀數(shù)萬條识窿,也有可能一次讀幾十條斩郎,這對后面的分析器非常不利;
- Redis記得開啟 RDB喻频,以及maxmemory設(shè)置孽拷,前者可以在出問題時還原狀態(tài),后者可以防止出現(xiàn)災(zāi)難時資源暴掉半抱,搞崩其它服務(wù);
- 無論是不是使用Redis膜宋,應(yīng)該使用支持管道窿侈,或者批量的方法,如redisio秋茫,根據(jù)機(jī)器效率史简,如每次滿500條就入緩存,不滿就100毫秒入一次肛著,減少緩存操作次數(shù)可以提高效率圆兵;
- logger可以用pm2的集群模式,提高效率枢贿。
注:pm2 3.2.2的集群可能出現(xiàn)集群內(nèi)端口沖突的吊詭問題殉农,建議用3.0.3
分析器analyser:
分析器讀取Redis的內(nèi)容,這里就是單進(jìn)程的隊列操作局荚。到這一步超凳,日志怎么分析愈污,就可以很自由了。
分析器analyser的注意點:
- 單線程可以確保每個玩家的日志時間序列轮傍;
- Redis的讀取使用管道暂雹,一次讀取數(shù)千條進(jìn)行分析。參考值:目前每次讀3000條進(jìn)行處理创夜,在4核心中低配置云主機(jī)下單線程占用僅為35%左右杭跪;
- 日志存ES:源日志文件可以進(jìn)行進(jìn)一步分析或者格式優(yōu)化,處理后的放ES驰吓,ES 就是為集群而生涧尿,通過加入子節(jié)點可以熱擴(kuò)容,硬盤便宜棚瘟,所以先做3個節(jié)點的集群吧现斋;
- 配置好ES的索引(mapping),仔細(xì)考慮各字段類型偎蘸,凡是要與搜索條件有關(guān)的庄蹋,例如要查元寶大于多少的,那么元寶字段必須有索引迷雪,否則將無法根據(jù)該字段查找日志限书。還有,想要分詞的必須使用text類型章咧。日志一般不會進(jìn)行匯總倦西,因為我們已經(jīng)統(tǒng)計大部分內(nèi)容了,所以可以適當(dāng)減少doc_value赁严,壓縮率等扰柠,否則一千萬條日志半小時內(nèi)就吃掉1G硬盤。這需要你好好研究 ES 的索引配置了疼约,后面還得研究 ES 的搜索卤档,因為它比MongoDB的復(fù)雜得多,但這很值得程剥;
- ES和MongoDB的入庫劝枣,使用批量處理,根據(jù)機(jī)器性能和系統(tǒng)資源找到合適的批處理數(shù)量织鲸。參考值舔腾,4核下 ES 批量入庫1000條效率300ms 左右;
- ES 配置好內(nèi)存搂擦,默認(rèn)是1G JVM內(nèi)存稳诚,經(jīng)常不夠用就會崩潰。在配置文件同目錄下有個jvm option文件瀑踢,可以加大JVM采桃,建議至少分配一半以上內(nèi)存懒熙;
- ES 的寫入效率:不要以為 ES 的輸入速度很快,默認(rèn)它是寫一條更新一條索引普办,也就是必須等把數(shù)據(jù)更新到索引才會返回工扎,無論使用批量處理還是單個,日志量大的時候衔蹲,批處理僅100條也會超過500ms肢娘。設(shè)置durability為async,不要馬上更新到索引舆驶;
- ES使用別名索引橱健,好處是當(dāng)你需要重建索引時,可以通過另外重新指向到新的索引沙廉,因為 ES 不能修改索引拘荡,只能重建;
- 在分析的時候撬陵,先還原玩家畫像珊皿,對其它數(shù)據(jù)報表,組織好你的數(shù)據(jù)結(jié)構(gòu)巨税,數(shù)據(jù)量小蟋定、簡單的可以同時放內(nèi)存中進(jìn)行計數(shù),并定期條件清理草添,大的如玩家畫像放redis中驶兜,定期更新入庫。這些數(shù)據(jù)的緩存方式可以使用完整版本远寸,簡化問題抄淑,減少出現(xiàn)臟數(shù)據(jù)的可能;同時分析也要注意效率的問題驰后,例如有Mongodb數(shù)據(jù)的讀寫蝇狼,要務(wù)必配置到index,否則將引起災(zāi)難性效率下降倡怎。
用戶界面
因為我們本身有后臺管理系統(tǒng),所以我們很方便的把用戶畫像與其它分析點接了入去贱枣,在查詢玩家行為時监署,我們搜索ES,在查詢分析報表時纽哥,我們查詢MongoDB中的數(shù)據(jù)钠乏。當(dāng)然我們也使用了Kibana來滿足可能的需求。
總結(jié)
目前該日志系統(tǒng)運行1個半月春塌,由純MongoDB到結(jié)合 ES晓避,走了不少彎路簇捍,還好現(xiàn)在終于穩(wěn)定下來。目前在性能方面俏拱,logger 與 analyser都在同一臺機(jī)暑塑,平均 CPU 為23%左右,高峰47%左右锅必,說明還有更大的機(jī)器壓榨空間事格。
內(nèi)存方面,在高峰期5G 以內(nèi)搞隐,總體非常平穩(wěn)沒多大波動驹愚,其中redis內(nèi)存使用為800MB以內(nèi),但機(jī)器是16G劣纲,還有很大余量保障逢捺。
NodeJS 的腳本中,logger的CPU占用更小癞季,3條進(jìn)程劫瞳,每條才3%,每條內(nèi)存占用不到100MB余佛。analyser 的 CPU 與內(nèi)存占用多一點柠新,這一點可以通過腳本內(nèi)的參數(shù)調(diào)整,例如內(nèi)存計數(shù)的內(nèi)容清理得更快辉巡,使用pm2的話設(shè)置max_memory_restart : '4G' 都可以提高穩(wěn)定性恨憎。
以上是我在游戲日志系統(tǒng)中的經(jīng)驗總結(jié)。
參考文獻(xiàn):