国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

太極限了,JDK的這個Bug都能被我踩到

開發 前端
如果是在這個BUG的影響下,只要同一秒內有2次變更,且讀取文件最后時間戳位于這2次變更之間的時間,第2次變更就無法被程序感知了,同1秒這個概率比同一毫秒大的多的多,所以當然就被觸發了,導致了一次線上故障。

hello,大家好呀,我是小樓。

之前遇到個文件監聽變更的問題,剛好這周末有空研究了一番,整理出來分享給大家。

從一次故障說起

我們還是從故障說起,這樣更加貼近實際,也能讓大家更快速理解背景。

有一個下發配置的服務,這個配置服務的實現有點特殊,服務端下發配置到各個服務的本地文件,當然中間經過了一個agent,如果沒有agent也就無法寫本地文件,然后由client端的程序監聽這個配置文件,一旦文件有變更,就重新加載配置,畫個架構圖大概是這樣:

今天的重點是文件的變更該如何監聽(watch),我們當時的實現非常簡單:

  • 單獨起個線程,定時去獲取文件的最后更新時間戳(毫秒級)。
  • 記錄每個文件的最后更新時間戳,根據這個時間戳是否變化來判斷文件是否有變更。

從上述簡單的描述,我們能看出這樣實現有一些缺點:

  • 無法實時感知文件的變更,感知誤差在于輪詢文件最后更新時間的間隔。
  • 精確到毫秒級,如果同一毫秒內發生2次變更,且輪詢時剛好落在這2次變更的中間時,后一次變更將無法感知,但這概率很小。

還好,上述兩個缺點幾乎沒有什么大的影響。

但后來還是發生了一次比較嚴重的線上故障,這是為什么呢?因為一個JDK的BUG,這里直接貼出罪魁禍首:

BUG詳見:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8177809

在某些JDK版本下,獲取文件的最后更新時間戳會丟失毫秒精度,總是返回整秒的時間戳,為了直觀感受,寫了個demo分別在jdk1.8.0_261和jdk_11.0.6測試(均為MacOs):

  • jdk_1.8.0_261

  • jdk_11.0.6

如果是在這個BUG的影響下,只要同一秒內有2次變更,且讀取文件最后時間戳位于這2次變更之間的時間,第2次變更就無法被程序感知了,同1秒這個概率比同一毫秒大的多的多,所以當然就被觸發了,導致了一次線上故障。

這就好比之前是滄海一粟,現在變成了大海里摸到某條魚的概率。這也能被我們碰到,真是有點極限~

WatchService—JDK內置的文件變更監聽

當了解到之前的實現存在BUG后,我就去搜了一下Java下如何監聽文件變更,果然被我找到了WatchService。

說是WatchService可以監聽一個目錄,對目錄下的文件新增、變更、刪除進行監聽。于是我很快就寫了個demo進行測試:

public static void watchDir(String dir) {
Path path = Paths.get(dir);
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.OVERFLOW);
while (true) {
WatchKey key = watchService.take();
for (WatchEvent<?> watchEvent : key.pollEvents()) {
if (watchEvent.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
System.out.println("create..." + System.currentTimeMillis());
} else if (watchEvent.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
System.out.println("modify..." + System.currentTimeMillis());
} else if (watchEvent.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
System.out.println("delete..." + System.currentTimeMillis());
} else if (watchEvent.kind() == StandardWatchEventKinds.OVERFLOW) {
System.out.println("overflow..." + System.currentTimeMillis());
}
}
if (!key.reset()) {
System.out.println("reset false");
return;
}
}
} catch (Exception e) {
e.printStackTrace();

先對/tmp/file_test目錄進行監聽,然后每隔5毫秒往文件寫數據,理論上來說,應該能收到3次事件,但實際上很奇怪,仔細看接收到modify事件的時間大概是第一次文件修改后的9.5s左右,很奇怪,先記著,我們讀一下WatchService源碼:

>>> 1652076266609 - 1652076257097
9512

WatchService原理

WatchService watchService = FileSystems.getDefault().newWatchService()

通過debug發現,這里的watchService實際上是PollingWatchService的實例,直接看PollingWatchService的實現:

PollingWatchService上來就起了個線程,這讓我隱隱不安。再找一下這個scheduledExecutor在哪里用到:

每隔一段時間(默認為10s)去poll下,這個poll干了什么?代碼太長,我截出關鍵部分:

果然,和我們的實現類似,也是去讀文件的最后更新時間,根據時間的變化來發出變更事件。

換句話說,在某些JDK版本下,他也是有BUG的!

這也就解釋了上文提到的事件監聽為什么是在第一個9.5s之后才發出,因為監聽注冊后,sleep了500ms后修改文件,10s輪詢,剛好9.5s后拿到第一輪事件。

inotify—Linux內核提供的文件監聽機制

至此,我想起了linux上的tail命令,tail 是在文件有變更的情況下輸出文件的末尾,理論上也是監聽了文件變更,這塊剛好在很久之前聽過一個技術大佬分享如何自己實現tail命令,用到的底層技術就是inotify。

簡單來說,inotify是linux內核提供的一種監控文件變更事件的系統調用。如果基于此來實現,不就可以規避JDK的BUG了嗎?

但奇怪的是為什么Java沒有用這個來實現呢?于是我又搜了搜,發現谷歌似乎有一個庫,但被刪了,看不到代碼:

github上又搜到一個:https://github.com/sunmingshi/Jinotify。

看起來是一個native的實現,需要自己編譯.so文件,這樣就比較蛋疼了。

記得上次這么蛋疼還是在折騰Java的unix domain socket,也是找到了一個google的庫,測試沒問題,放到線上就崩了~不得不說google還是厲害,JDK提供不了的庫,我們來提供!

于是我帶著這個疑問去問了一個搞JVM開發的朋友,結果他告訴我,Java也可以使用inotify!

瞬間斗志來了,難道是我測試的姿勢不對?

我又去翻了一遍Java文檔,發現在角落隱藏了這么一段話:

也就是說,不同的平臺下會使用不同的實現,PollingWatchService是在系統不支持inotify的情況下使用的兜底策略。

于是將watchService的類型打印出來,在Mac上打印為:

class sun.nio.fs.PollingWatchService

在Linux上是:

class sun.nio.fs.LinuxWatchService

LinuxWatchService在Mac上是找不到這個類,我猜測應該是Mac版的JDK壓根沒把這塊代碼打包進來。

原來我本地測試都走了兜底策略,看來是測了個寂寞。

于是我寫了個demo再測試一把:

public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> watchDir("/tmp/file_test"));
thread.setDaemon(false);
thread.start();

Thread.sleep(500L);

for (int i = 0; i < 3; i++) {
String path = "/tmp/file_test/test";
FileWriter fileWriter = new FileWriter(path);
fileWriter.write(i);
fileWriter.close();
File file = new File(path);
System.out.println(file.lastModified());
Thread.sleep(5);
}
}
  • 本地Mac

  • Linux

可以看出,Linux上能收到的事件比本地多的多,而且接收事件的時間明顯實時多了。

為了更加準確的驗證是inotify,用strace抓一下系統調用,由于JVM fork出的子進程較多,所以要加-f命令,輸出太多可以存入文件再分析:

strace -f -o s.txt java FileTime

果然是用到了inotify系統調用的,再次驗證了我們的猜想。

故障是如何修復的?

再次回到開頭的故障,我們是如何修復的呢?由于下發的文件和讀取文件的程序都是我們可控的,所以我們繞過了這個BUG,給每個文件寫一個version,可以用文件內容md5值作為version,寫入一個特殊文件,讀取時先讀version,當version變化時再重新載入文件。

可能你要問了,為什么不用WatchService呢?

我也問了負責人,據說inotify在docker上運行的不是很好,經常會丟失事件,不是Java的問題,所有語言都存在這個問題,所以一直沒有使用。不過這塊找不到相關的資料,也無法證明,所以暫時擱置。

最后說幾句

有些BUG,不踩過就很難避免,代碼只要存在BUG的可能性,就一定會暴露出來,只是時間問題。

我們要在技術上深入探究,小心求證,但產品上不必執著,可另辟蹊徑。

責任編輯:武曉燕 來源: 捉蟲大師
相關推薦

2024-10-09 09:07:10

JVM優化String類JDK1.6

2020-07-20 09:40:49

MySQLBUG數據庫

2020-04-02 14:33:42

MySQLBUG解決方案

2022-05-27 07:03:04

JDK場景線程

2020-12-17 07:39:30

HashMap死循環數據

2022-11-11 15:49:41

MySQL隔離

2022-02-23 11:47:57

CharlesFiddler抓包

2021-10-13 06:49:14

事故復盤ID

2016-04-15 17:45:59

HPE存儲閃存

2021-08-20 08:22:12

Tomcat原生線程池

2021-09-25 13:05:10

MYSQL開發數據庫

2022-11-30 07:16:18

2020-04-27 09:40:43

開源項目 Bug

2019-05-13 09:01:13

程序員職責產品經理

2025-04-02 04:55:00

2020-04-21 15:22:35

ChromeFirefox瀏覽器

2021-08-31 15:19:16

美團面試快排

2022-06-21 11:24:05

多線程運維

2018-05-03 16:49:13

魅藍

2021-12-27 18:28:28

Spring設計配置
點贊
收藏

51CTO技術棧公眾號

91九色国产ts另类人妖| 午夜av在线免费观看| 欧美午夜一区| 久久精品免费播放| 成人免费网站观看| 欧美片在线播放| 性感av在线播放| 亚洲欧洲精品一区二区三区不卡| 亚洲色欲久久久综合网东京热| 亚洲欧美日韩国产综合精品二区 | 精品午夜av| 亚洲黄色www网站| av官网在线播放| 欧美日韩五月天| 国产区视频在线播放| 偷拍亚洲欧洲综合| 中文字幕在线二区| 欧美日韩国产一区二区三区| 在线观看视频免费| 精品久久久一区二区| 一区二区电影网| 色欧美乱欧美15图片| 九色在线免费| 69堂国产成人免费视频| 黄视频网站在线| 日韩视频免费观看高清完整版 | 91国内精品| 九九精品在线播放| 奇米一区二区| 久久69精品久久久久久国产越南| 麻豆一区在线| 高清欧美性猛交| 久久99国产成人小视频| 国产精品福利小视频| 91精品一区二区三区综合| 亚洲淫片在线视频| 亚洲经典三级| 一区二区三区四区不卡| 国产超碰在线一区| 色片在线免费观看| 欧美日韩亚洲91| 自拍视频在线| 亚洲精品国产精品国自产在线| 日韩免费福利视频| 国内精品久久影院| 国产精品久久久久久影院8一贰佰| 亚洲最大福利视频| 六月丁香综合在线视频| 国产在线青青草| 最新欧美精品一区二区三区| 亚洲一区二区三区成人| 欧美日韩日本视频| 97久久香蕉国产线看观看| 久久成人精品视频| 欧美日韩有码| 亚洲视频在线二区| 亚洲欧洲日韩在线| 麻豆av免费在线观看| 中文字幕日韩欧美| 欧美日韩国产一区二区三区不卡 | 91国内精品野花午夜精品| 黄页网站在线观看免费| 欧美超级免费视 在线| 精品国产一级毛片| 日韩精品一区二区三区丰满| 99在线精品观看| 美丽的姑娘在线观看免费动漫| 欧美一区二区精品久久911| 日韩高清成人| 91精品久久久久| 精品一区二区在线免费观看| 免费女人黄页| 亚洲国产免费av| 免费观看久久av| 台湾成人av| 国产精品高潮久久久久无| 1区2区3区在线观看| 综合网中文字幕| 综合久久十次| 密臀av一区二区三区| 91精品国产品国语在线不卡| 97视频一区| 亚洲视频在线观看日本a| 亚洲精品免费看| 亚洲精品88| 91青青草免费在线看| 99久久精品免费看| 黄色成人在线| 欧美专区第一页| 国产高清精品在线| 久久日韩视频| 国产精品成人国产乱一区| 国精产品一区一区三区mba桃花| 在线一区观看| 欧美成人精品在线视频| 在线亚洲自拍| 免费观影入口看日本视频| 亚洲视频综合网| 亚洲一区二区成人| h精品动漫在线观看| 精品国产一区久久久| 久久在线精品| 青青操在线视频| 久久久免费精品| 国产成人免费视频| 中日韩高清电影网| av色综合网| 亚洲成人久久影院| 成人春色在线观看免费网站| 中文字幕黄色大片| 欧美一区日韩一区| 一区二区在线影院| 91嫩草在线播放| 97**国产露脸精品国产| 成人午夜电影久久影院| 成人在线免费观看黄色| 国产精品一区二区a| 亚洲福利视频导航| 亚州精品视频| 自拍偷拍一区二区三区四区| 最近2019中文字幕第三页视频| 麻豆精品国产传媒mv男同| 免费在线观看黄| 激情视频一区二区| 欧美日韩在线播放三区| 亚洲色图国产| 国产精品一二三区视频| 亚洲a中文字幕| 日本黄色一区二区| 亚洲黄色av| 国产最新在线| 欧美一区二区在线| 亚洲成人av在线播放| 国产亚洲在线观看| 色呦呦视频在线观看| 色播亚洲婷婷| 日韩久久精品成人| 久久精品国产一区二区三| 第一中文字幕在线| 看一级黄色录像| 亚洲一级片在线看| 国产一区二区在线影院| 怡红院成人在线| 91国视频在线| 国内精品久久久久久影视8| 国产欧美日本一区二区三区| 亚洲专区**| 一级毛片免费在线| 99久久久久国产精品免费| 欧美在线你懂的| 麻豆精品网站| 三级中文字幕在线观看| 在线视频亚洲自拍| 日韩在线精品视频| 国产色综合一区| 西瓜成人精品人成网站| 美女黄视频在线播放 | 日韩欧美亚洲成人| 亚洲欧美大片| 忘忧草在线日韩www影院| 男女日批视频在线观看| 久久久久久久一区二区| 亚洲精品成人在线| 欧美精品啪啪| 黄色视屏在线免费观看| 久无码久无码av无码| 欧美激情视频播放| 欧美性猛交xxxx久久久| 新狼窝色av性久久久久久| 92国产精品| www.99av.com| 欧美成人免费网| 色综合天天性综合| 国产在线视视频有精品| 久久久久观看| youjizz在线播放| 国产美女网站在线观看| 国产精品欧美日韩久久| 91精品国产麻豆| 337p粉嫩大胆噜噜噜噜噜91av| 欧美美女在线观看| 影院在线观看全集免费观看| 欧美性久久久久| 高清视频一区二区三区| 日韩精品福利网站| 一区二区三区久久| 美女免费视频一区二区| 亚洲瘦老头同性70tv| 制服丝袜在线播放| 成人au免费视频影院| 久久久久久久久久久久久久久久av | 国产无人区一区二区三区| 亚洲天堂一区二区三区四区| av免费不卡国产观看| 性直播在线观看| 日韩精品久久久免费观看| 国模极品一区二区三区| 精品黑人一区二区三区久久 | 人人精品亚洲| 黄网页在线观看|