1、項目介紹
將接入采集站的執(zhí)法記錄儀設(shè)備中的視頻、圖片、音頻等文件,采集到采集站本地,融合了管理平臺功能,提供用戶查看編輯視音頻等文件,也提供了對當(dāng)前組織人員設(shè)備等信息的管理。同時還有同步服務(wù),定時從上層平臺同步相應(yīng)的組織人員等信息,并上報采集站上的視音頻等文件到上層平臺。
2、說說工廠模式
由于該項目會接入不同廠商的設(shè)備,對不同廠商設(shè)備的通訊和處理方式有很大區(qū)別,而其中又有一些共同點(例如文件上傳邏輯),所以定義了抽象類作為父類,實現(xiàn)共有方法,各種設(shè)備對應(yīng)一個子類實現(xiàn),并由一個工廠類去根據(jù)設(shè)備類型新建對應(yīng)的子類。增加了系統(tǒng)對不同設(shè)備的擴展性。
3、有高并發(fā)場景嗎
對于設(shè)備插拔過程,沒有高并發(fā)場景,但需要嚴(yán)格控制設(shè)備插入行為,同一時間只處理一臺設(shè)備,因為可能出現(xiàn)人為的同時插入兩臺設(shè)備的情況。
有兩個版本(動態(tài)庫單設(shè)備通訊和多設(shè)備通訊,最新版本是多設(shè)備通訊)
①單設(shè)備通訊:采用synchronized鎖+redis鎖(也可以只說redis鎖),設(shè)備插入后,事件和設(shè)備信息會進(jìn)入隊列等待處理,只有獲取鎖成功的設(shè)備才能進(jìn)行后續(xù)處理。
等待隊列實現(xiàn):線程輪詢隊列是否為空,不為空則隊首出隊列,進(jìn)行后續(xù)處理。
redis鎖:輪詢過程會嘗試加鎖,redis中存放了key為鎖名,value為當(dāng)前時間+存活時間的數(shù)據(jù);
當(dāng)線程嘗試加鎖時發(fā)現(xiàn)該key已存在,則判斷該key對應(yīng)的value是否已到過期時間,未過期則視為線程加鎖失敗,已過期則刪除該key(防止死鎖),并由該線程重新寫入新的key-value,線程加鎖成功。
②多設(shè)備通訊:
僅采用synchronized鎖,鎖住設(shè)備插入的識別事件方法,避免識別到設(shè)備接入的hub標(biāo)識與設(shè)備不匹配。
(此時大概率會問你synchronized鎖原理之類的)
4、如何控制大批量文件的拷貝及大文件拷貝
滿載使用時,插入設(shè)備22臺,假設(shè)每臺設(shè)備1000個文件需采集,則同時需要采集2萬多個文件。
每個文件開啟一條線程去拷貝,這種方式不實際,會在一瞬間線程數(shù)量暴漲。
因此項目中采用一臺設(shè)備開啟一條線程專注于拷貝當(dāng)前設(shè)備的所有文件。每次拷貝文件前會根據(jù)文件大小進(jìn)行分段處理,通過NIO方式分段拷貝,但沒有采用一段一線程的方式,因為這樣也會導(dǎo)致線程數(shù)量暴增(例如由22個文件,每個文件分為5段,那么就會新增110條線程處理文件)。因此,做分段的目的在于:通過測試,大文件整個拷貝耗時會相對于一小段一小段傳稍慢些,因此將大文件分段傳輸。
后來為了充分利用系統(tǒng)IO資源,借鑒信號量機制,實現(xiàn)對同一時間文件上傳總數(shù)的控制(當(dāng)由22個文件同時傳輸時,系統(tǒng)總IO到達(dá)頂峰,且單個文件的IO很小,而假設(shè)僅有5個文件同時上傳,系統(tǒng)總IO也是頂峰,但單個文件的IO會相對更高)。定義一個允許同一時間內(nèi)文件上傳總數(shù)限制,假設(shè)為5,當(dāng)當(dāng)前正在上傳文件的線程數(shù)量達(dá)到5個時(信號量為5),后續(xù)即將上傳文件的線程只能進(jìn)入等待狀態(tài),直到5個文件中有完成拷貝的線程(此時信號量變?yōu)?),則正在等待上傳文件的線程進(jìn)行爭搶,哪個文件所在的線程成功修改信號量(修改為5),說明取得上傳文件的權(quán)利,線程開始上傳文件。
5、為什么使用LongAdder
由于需要統(tǒng)計上傳進(jìn)度,且該進(jìn)度受多個線程影響,線程中會對該值進(jìn)行修改,各個線程共享唯一一個該變量。
假設(shè)使用long類型,由于不是線程安全的類型,當(dāng)傳遞給各個線程時,線程會對該變量存儲為自己的副本,且需要定義為final類型傳遞,線程中對該值的修改,對其他線程來說并不可見。
為解決以上問題,使得某個線程對進(jìn)度的修改對其他線程可見,且保證線程安全,于是采用LongAdder。LongAdder提供api,支持在各自線程中對數(shù)值的累加等計算(原理沒有很了解,只記得是維護(hù)一個數(shù)組,累加就是在數(shù)組中插入一個值,最后總數(shù)就是數(shù)組內(nèi)所有數(shù)值的總和)。
Atomiclong對比:也是實現(xiàn)了多線程下對一個數(shù)值的讀寫,也是線程安全,和LongAdder不同的是,Atomiclong通過維護(hù)一個volatile變量,通過對volatile變量的值的修改來做到多線程下的可見性。但由于采用的是volatile實現(xiàn),底層采用CAS實現(xiàn),于是在大量線程的情況下效率會急劇驟減,大量沒有CAS成功的線程會進(jìn)入自旋等待,不斷嘗試CAS,消耗CPU。
(這里會問volatile原理,還有CAS)
6、為什么用隊列
MD5的計算,對于大文件來說十分耗時,于是通過全局的一個隊列來存儲準(zhǔn)備計算MD5的文件信息,通過線程輪詢判斷隊列是否有文件信息存在,若有,則出隊列并計算MD5。
在證據(jù)文件同步到平臺的過程中,也是采用隊列方式,將準(zhǔn)備上報平臺的證據(jù)文件入隊列,線程輪詢隊列進(jìn)行文件上報。
7、redis在項目中的作用
數(shù)據(jù)庫數(shù)據(jù)緩存、服務(wù)間共享數(shù)據(jù)的緩存(如系統(tǒng)配置)、同步鎖。
8、nginx在項目中的作用
映射靜態(tài)資源,包括證據(jù)文件。
9、JVM調(diào)優(yōu)經(jīng)歷(這個涉及JVM,要深入理解)
現(xiàn)象:面板上設(shè)備信息閃爍
問題定位:第一反應(yīng)是系統(tǒng)線程調(diào)度出了問題,查看系統(tǒng)CPU情況,CPU占用100%
問題排查:CPU占用100%極大可能是JVM的GC活動頻繁,尤其是full GC。于是打開jdk自帶的JVM監(jiān)控工具jvisualvm,發(fā)現(xiàn)新生代每分鐘上百次GC,老年代每分鐘幾十次GC,且新生代每次GC都有大量對象存活,被移動到老年代,而老年代GC時又回收了大量對象。這個現(xiàn)象說明,新生代有大量無用對象在GC時幸存下來。于是要找到該對象,利用抽樣器對線程及內(nèi)存進(jìn)行抽樣,發(fā)現(xiàn)有兩個線程分配了大量內(nèi)存且不斷增加:一個是文件拷貝線程,一個是視頻縮略圖截取線程。初步分析:文件拷貝采用的工具類完成任務(wù)后,沒有被即時回收,導(dǎo)致堆中大量該類存在;在大批量小文件上傳的場景下,每個視頻都要開一個單獨的線程去截取縮略圖,這也導(dǎo)致線程大量增長,且該方法較耗時。
問題解決:將文件拷貝工具類定義為bean,單例模式,大大減少了GC頻率;優(yōu)化縮略圖截取方法,介紹不必要的處理,并取消線程,串行處理,提高性能。
問題分析:在大批量小文件傳輸過程中,假設(shè)單臺設(shè)備需處理2000個文件,當(dāng)設(shè)備中途拔出,設(shè)備服務(wù)類會對所有失敗文件進(jìn)行日志記錄,在這個過程中文件拷貝工具類依然被設(shè)備服務(wù)類所引用,不會被回收。所以當(dāng)22臺這樣的設(shè)備接入,并全部中途拔出,造成大量無用的文件拷貝工具類存在堆內(nèi)存中。
(這一塊一定要理解JVM內(nèi)存模型,還有堆的GC原理,包括新生代具體的GC流程)
10、SQL優(yōu)化
只說了對頻繁查詢的字段加索引
(這里就要知道索引的原理,最左匹配原則等)