JD hotkey是一個(gè)非常好用的京東熱點(diǎn)數(shù)據(jù)分析軟件,對(duì)任意突發(fā)性的無(wú)法預(yù)先感知的熱點(diǎn)請(qǐng)求,都可以進(jìn)行毫秒級(jí)精準(zhǔn)探測(cè)到。 然后對(duì)這些熱數(shù)據(jù)、熱用戶等,推送到該應(yīng)用部署的所有機(jī)器JVM內(nèi)存中,以大幅減輕對(duì)后端數(shù)據(jù)存儲(chǔ)層的沖擊。熱數(shù)據(jù)探測(cè)并推送至集群各個(gè)服務(wù)器, 這些熱key在整個(gè)應(yīng)用集群內(nèi)保持一致性。
軟件介紹
JD-hotkey是京東APP后臺(tái)熱數(shù)據(jù)探測(cè)框架,歷經(jīng)多次高壓壓測(cè)和2020年京東618大促考驗(yàn)。在上線運(yùn)行的這段時(shí)間內(nèi),每天探測(cè)的key數(shù)量數(shù)十億計(jì),精準(zhǔn)捕獲了大量爬蟲(chóng)、刷子用戶,另準(zhǔn)確探測(cè)大量熱門商品并毫秒級(jí)推送到各個(gè)服務(wù)端內(nèi)存,大幅降低了熱數(shù)據(jù)對(duì)數(shù)據(jù)層的查詢壓力,提升了應(yīng)用性能。
對(duì)任意突發(fā)性的無(wú)法預(yù)先感知的熱點(diǎn)請(qǐng)求,包括并不限于熱點(diǎn)數(shù)據(jù)(如突發(fā)大量請(qǐng)求同一個(gè)商品)、熱用戶(如爬蟲(chóng)、刷子)、熱接口(突發(fā)海量請(qǐng)求同一個(gè)接口)等,進(jìn)行毫秒級(jí)精準(zhǔn)探測(cè)到。 然后對(duì)這些熱數(shù)據(jù)、熱用戶等,推送到該應(yīng)用部署的所有機(jī)器JVM內(nèi)存中,以大幅減輕對(duì)后端數(shù)據(jù)存儲(chǔ)層的沖擊,并可以由客戶端決定如何使用這些熱key(譬如對(duì)熱商品做本地緩存、對(duì)熱用戶進(jìn)行拒絕訪問(wèn)、對(duì)熱接口進(jìn)行熔斷或返回默認(rèn)值)。 這些熱key在整個(gè)應(yīng)用集群內(nèi)保持一致性。
核心功能:熱數(shù)據(jù)探測(cè)并推送至集群各個(gè)服務(wù)器
應(yīng)用場(chǎng)景
JD-hotkey適用場(chǎng)景:
1、mysql熱數(shù)據(jù)本地緩存
2、redis熱數(shù)據(jù)本地緩存
3、黑名單用戶本地緩存
4、爬蟲(chóng)用戶限流
5、接口、用戶維度限流
6、單機(jī)接口、用戶維度限流限流
7、集群用戶維度限流
8、集群接口維度限流
安裝方法
1、安裝etcd
在etcd下載頁(yè)面下載對(duì)應(yīng)操作系統(tǒng)的etcd
2、啟動(dòng)worker(集群) 下載并編譯好代碼,將worker打包為jar,啟動(dòng)即可。如:
java -jar $JAVA_OPTS worker-0.0.1-SNAPSHOT.jar --etcd.server=${etcdServer}
worker可供配置項(xiàng)如下:
etcdServer為etcd集群的地址,用逗號(hào)分隔
JAVA_OPTS是配置的JVM相關(guān),可根據(jù)實(shí)際情況配置
threadCount為處理key的線程數(shù),不指定時(shí)由程序來(lái)計(jì)算。
workerPath代表該worker為哪個(gè)應(yīng)用提供計(jì)算服務(wù),譬如不同的應(yīng)用appName需要用不同的worker進(jìn)行隔離,以避免資源競(jìng)爭(zhēng)。
3、啟動(dòng)控制臺(tái)
下載并編譯好dashboard項(xiàng)目,創(chuàng)建數(shù)據(jù)庫(kù)并導(dǎo)入resource下db.sql文件。 配置一下application.yml里的數(shù)據(jù)庫(kù)相關(guān)和etcdServer地址。
啟動(dòng)dashboard項(xiàng)目,訪問(wèn)ip:8081,即可看到界面。
其中節(jié)點(diǎn)信息里,即是當(dāng)前已啟動(dòng)的worker列表。
規(guī)則配置就是為各app設(shè)置規(guī)則的地方,初次使用時(shí)需要先添加APP。在用戶管理菜單中,添加一個(gè)新用戶,設(shè)置他的APP名字,如sample。之后新添加的這個(gè)用戶就可以登錄dashboard給自己的APP設(shè)置規(guī)則了,登錄密碼默認(rèn)123456。
如圖就是一組規(guī)則,譬如其中as__開(kāi)頭的熱key的規(guī)則就是interval-2秒內(nèi)出現(xiàn)了threshold-10次就認(rèn)為它是熱key,它就會(huì)被推送到j(luò)vm內(nèi)存中,并緩存60秒,prefix-true代表前綴匹配。那么在應(yīng)用中,就可以把一組key,都用as__開(kāi)頭,用來(lái)探測(cè)。
4、client端接入使用
引入client的pom依賴。
在應(yīng)用啟動(dòng)的地方初始化HotKey,譬如
@PostConstruct public void initHotkey() { ClientStarter.Builder builder = new ClientStarter.Builder(); ClientStarter starter = builder.setAppName("appName").setEtcdServer("http://1.8.8.4:2379,http://1.1.4.4:2379,http://1.1.1.1:2379").build(); starter.startPipeline(); }
其中還可以setCaffeineSize(int size)設(shè)置本地緩存最大數(shù)量,默認(rèn)5萬(wàn),setPushPeriod(Long period)設(shè)置批量推送key的間隔時(shí)間,默認(rèn)500ms,該值越小,上報(bào)熱key越頻繁,相應(yīng)越及時(shí),建議根據(jù)實(shí)際情況調(diào)整,如單機(jī)每秒qps10個(gè),那么0.5秒上報(bào)一次即可,否則是空跑。該值最小為1,即1ms上報(bào)一次。
注意:
如果原有項(xiàng)目里使用了guava,需要升級(jí)guava為以下版本,否則過(guò)低的guava版本可能發(fā)生jar包沖突。或者刪除自己項(xiàng)目里的guava的maven依賴,guava升級(jí)不會(huì)影響原有任何邏輯。
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.2-jre</version> <scope>compile</scope> </dependency>
有時(shí)可能項(xiàng)目里沒(méi)有直接依賴guava,但是引入的某個(gè)pom里引了guava,也需要將guava排除掉。
常見(jiàn)問(wèn)題
1、worker掛了怎么辦
client根據(jù)worker的數(shù)量對(duì)key進(jìn)行hash后分發(fā),同一個(gè)key一定會(huì)被發(fā)往同一個(gè)worker。譬如4臺(tái),掛了一臺(tái),key就自動(dòng)hash到另外3臺(tái)。那么這個(gè)過(guò)程中,就會(huì)丟失最多一個(gè)探測(cè)周期內(nèi)的所有發(fā)來(lái)的key,譬如2秒10次算熱,那么就可能全部被rehash,丟失這2秒的數(shù)據(jù)。
它的影響是什么呢?我要不要去存下來(lái)所有發(fā)來(lái)的key呢?很多人都會(huì)問(wèn)的問(wèn)題。
首先掛機(jī),那是極其罕見(jiàn)的事件,即便掛了,對(duì)于特別熱的key,完全不影響,hash丟幾秒,不影響它繼續(xù)瞬間變熱。對(duì)于不熱的key,它掛不掛,它也熱不了。對(duì)于那些將熱未熱的,可能會(huì)這次讓它熱不起來(lái),但沒(méi)有什么影響,業(yè)務(wù)服務(wù)完全可以吃下這個(gè)熱key。而加上一堆別的組件如存儲(chǔ)、worker間通信傳輸key等,它的復(fù)雜度,性能都會(huì)影響很大。
所以它掛了對(duì)系統(tǒng)沒(méi)有任何影響
2、為什么全部要worker匯總計(jì)算,而不是客戶端自己計(jì)算
首先,客戶端是會(huì)本地累加的,在固定的上報(bào)周期內(nèi),如500ms內(nèi),本地就是在累加,每500ms批量上報(bào)給worker一次。如果上報(bào)頻率很高,如10ms一次,那么大概率本地同一個(gè)key是沒(méi)有累加。
有人會(huì)說(shuō),把這個(gè)間隔拉長(zhǎng),譬如本地計(jì)算3秒后,本地判定熱key,再上報(bào)給其他機(jī)器。那么這種場(chǎng)景首先對(duì)于京東是不可行的,哪怕1秒都不行。譬如一個(gè)用戶刷子,它在非常頻繁地刷接口,一秒刷了500次,而正常用戶一秒最多點(diǎn)5次,它已經(jīng)是非常嚴(yán)重的刷子了。但我們本地還是判斷不出來(lái)它是不是刷子。為什么?機(jī)器多。
隨便一個(gè)app小組都有數(shù)千臺(tái)機(jī)器,一秒500次請(qǐng)求,一個(gè)機(jī)器連1次都平均不到,大部分是0次,本地如何判斷它是刷子呢?總不能訪問(wèn)我一次就算它刷吧。
然后搶購(gòu)場(chǎng)景,有些秒殺商品,1-2秒就沒(méi)了,流量就停了,你本地計(jì)算了3秒,才去上報(bào),那活動(dòng)已經(jīng)結(jié)束了,你的熱key再推送已經(jīng)沒(méi)價(jià)值了。我們就要在活動(dòng)即將開(kāi)始之前的可能在10ms內(nèi),就要該商品被推送到所有client的jvm里去,根本等不了1秒。
3、為什么是worker推送,而不是worker發(fā)送熱key到etcd,客戶端直接監(jiān) 聽(tīng)etcd獲取熱key
(1) worker和client是長(zhǎng)連接,產(chǎn)生熱key后,直接推送過(guò)去,鏈路短,耗時(shí)少。如果是發(fā)到etcd,客戶端再通過(guò)etcd獲取,多了一層中轉(zhuǎn),耗時(shí)明顯增加。
(2) etcd性能不夠,存在單點(diǎn)風(fēng)險(xiǎn)。譬如我有5000臺(tái)client,每秒產(chǎn)生100個(gè)熱key,那么每秒就對(duì)應(yīng)50萬(wàn)次推送。我用2臺(tái)worker即可輕松完成,隨著worker的橫向擴(kuò)展,每秒的推送上限線性增加。但無(wú)論是etcd、redis等等任何組件,都不可能做到1秒50萬(wàn)次拉取或推送,會(huì)瞬間cpu爆滿卡死。因?yàn)閣orker是各自隔離的,而etcd是單點(diǎn)的。實(shí)際情況下,也不止5000臺(tái)client,每秒也不止100個(gè)熱key,只有當(dāng)前的架構(gòu)能支撐。
4、為什么是etcd,不是zookeeper之類的
etcd里面具備一個(gè)過(guò)期刪除的功能,你可以設(shè)置一個(gè)key幾秒過(guò)期,etcd會(huì)自動(dòng)刪除它,刪除時(shí)還會(huì)給所有監(jiān) 聽(tīng)的client回調(diào),這個(gè)功能在框架里是在用的,別的配置中心沒(méi)有這個(gè)功能。
etcd的性能和穩(wěn)定性、低負(fù)載等各項(xiàng)指標(biāo)非常優(yōu)異,完全滿足我們的需求。而zk在很多暴漲流量前和高負(fù)載下,并不是那么穩(wěn)定,性能也差的遠(yuǎn)。
標(biāo)簽: 源碼相關(guān)
下載地址
裝機(jī)必備軟件
網(wǎng)友評(píng)論