Java動(dòng)態(tài)修改配置即時(shí)生效的方式WatchService
這種方式僅適合于比較小的項(xiàng)目,例如只有一兩臺(tái)服務(wù)器,而且配置文件是可以直接修改的。例如 Spring mvc 以 war 包的形式部署,可以直接修改resources 中的配置文件。如果是 Spring boot 項(xiàng)目,還想用這種方式的話,就要引用一個(gè)外部可以編輯的文件,比如一個(gè)固定的目錄,因?yàn)?spring boot 大多數(shù)以 jar 包部署,打到包里的配置文件沒辦法直接修改。如果是比較大的項(xiàng)目,最好還是用配置中心,例如攜程的 Apollo、Consul 等。
原始方式
原始方式指的是每次要修改配置的時(shí)候,都要重新打包發(fā)布或者重啟服務(wù)器。
假設(shè)我們用 spring mvc 開發(fā),開發(fā)完成后打成 war 包部署到 tomcat 上,如果這時(shí)我們修改一個(gè)短信接口地址。
我們要做如下操作:
1、打開配置文件,修改配置信息;
2、編譯打包;
3、停止 tomcat ,刪除舊的項(xiàng)目目錄;
4、將新的 war 包放到 webapps ,啟動(dòng) tomcat。
當(dāng)然,可以直接在 tomcat 中找到這個(gè)項(xiàng)目的配置文件,然后修改,但同樣需要重啟 tomcat 。
如果只是單純做開發(fā)或者測(cè)試,除了有點(diǎn)浪費(fèi)時(shí)間外,當(dāng)然可以接受。那么,既不想浪費(fèi)時(shí)間又不想重啟 tomcat 呢,有沒有辦法呢。這就輪到本文介紹的這種方式了。
WatchService 方式
Java 提供了 WatchService 接口,這個(gè)接口是利用操作系統(tǒng)本身的文件監(jiān)控器對(duì)目錄和文件進(jìn)行監(jiān)控,當(dāng)被監(jiān)控對(duì)象發(fā)生變化時(shí),會(huì)有信號(hào)通知,從而可以高效的發(fā)現(xiàn)變化。
這種方式大致的原理:先根據(jù)操作系統(tǒng) new 一個(gè)監(jiān)控器( WatchService ),然后選擇要監(jiān)控的配置文件所在目錄或文件,然后訂閱要監(jiān)控的事件,例如創(chuàng)建、刪除、編輯,最后向被監(jiān)控位置注冊(cè)這個(gè)監(jiān)控器。一旦觸發(fā)對(duì)應(yīng)我們所訂閱的事件時(shí),執(zhí)行相應(yīng)的邏輯即可。
先上代碼吧,這是在一個(gè) spring mvc 項(xiàng)目里,監(jiān)控的是 resources 目錄。
@Repository
public class ConfigWatcher {
private static final Logger logger = LoggerFactory.getLogger(ConfigWatcher.class);
private static WatchService watchService;
@PostConstruct
public void init() {
logger.info("啟動(dòng)配置文件監(jiān)控器");
try {
watchService = FileSystems.getDefault().newWatchService();
URL url = ConfigWatcher.class.getResource("/");
Path path = Paths.get(url.toURI());
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_CREATE);
} catch (Exception e1) {
e1.printStackTrace();
}
/**
* 啟動(dòng)監(jiān)控線程
*/
Thread watchThread = new Thread(new WatchThread());
watchThread.setDaemon(true);
watchThread.start();
/**注冊(cè)關(guān)閉鉤子*/
Thread hook = new Thread(new Runnable() {
@Override
public void run() {
try {
watchService.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
Runtime.getRuntime().addShutdownHook(hook);
}
public class WatchThread implements Runnable {
@Override
public void run() {
while (true) {
try {
// 嘗試獲取監(jiān)控池的變化,如果沒有則一直等待
WatchKey watchKey = watchService.take();
for (WatchEvent<?> event : watchKey.pollEvents()) {
String editFileName = event.context().toString();
logger.info(editFileName);
/**
* 重新加載配置
*/
}
watchKey.reset();//完成一次監(jiān)控就需要重置監(jiān)控器一次
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
代碼非常簡(jiǎn)單,一看就懂,在項(xiàng)目啟動(dòng)的時(shí)候,用 FileSystems.getDefault().newWatchService() 創(chuàng)建一個(gè) WatchService,這是根據(jù)操作系統(tǒng)來(lái)的。然后獲取 resources 目錄的 URL,并由此獲取 Path,然后調(diào)用 Path 對(duì)象的 register 方法,注冊(cè)監(jiān)控器,訂閱了編輯和創(chuàng)建事件。事件在 StandardWatchEventKinds 類中定義,共有四種:
1、StandardWatchEventKinds#OVERFLOW
2、StandardWatchEventKinds#ENTRY_CREATE
3、StandardWatchEventKinds#ENTRY_DELETE
4、StandardWatchEventKinds#ENTRY_MODIFY
然后單獨(dú)啟動(dòng)了一個(gè) WatchThread 線程來(lái)處理變化邏輯,在一個(gè) while 無(wú)限循環(huán)中調(diào)用 take() 方法,直到有變化發(fā)生,一旦是我們監(jiān)控的配置文件發(fā)生了變化,則調(diào)用我們的邏輯重新加載配置。另外,每次有變化發(fā)生后,要調(diào)用 watchKey.reset() 方法來(lái)重置監(jiān)控器。
最后,還要注冊(cè)一個(gè) hook,在 jvm 關(guān)閉的時(shí)候可以關(guān)閉監(jiān)控器。
有了這種方式,當(dāng)我們有一些配置變化的時(shí)候,就可以直接到 tomcat 下修改配置文件,不用重啟就可以生效了。
本文主要介紹的是這種方式,上面也說(shuō)了,這種方式只適合非常簡(jiǎn)單的項(xiàng)目,對(duì)于大型項(xiàng)目,就需要用到更高級(jí)的方式了。
配置中心的方式
當(dāng)項(xiàng)目復(fù)雜度變高,配置修改后實(shí)時(shí)生效,灰度發(fā)布,分環(huán)境、分集群管理配置,完善的權(quán)限、審核機(jī)制可能都變成項(xiàng)目中要考慮的問(wèn)題,這個(gè)時(shí)候,單純依賴配置文件就顯得力不從心了。
目前比較用的比較多的配置中心有etcd、zookeeper、disconf、Apollo 等。disconf、Apollo 都是屬于拿來(lái)即用的,功能完善,而且有配套的 UI。而 etcd 和 zookeeper 需要一些定制開發(fā)。
各位同學(xué)可以根據(jù)需要自行選擇,更詳細(xì)的內(nèi)容可以自行搜索和實(shí)踐。
相關(guān)文章
Java?詳細(xì)分析四個(gè)經(jīng)典鏈表面試題
兄弟們,編程,當(dāng)我們學(xué)習(xí)完數(shù)據(jù)結(jié)構(gòu)的時(shí)候,你就會(huì)有一種豁然開朗的感覺。算是真正的入了編程的門,所以打好數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)是特別特別重要的2022-03-03
Java參數(shù)校驗(yàn)中validation和validator的區(qū)別詳解
這篇文章主要介紹了Java參數(shù)校驗(yàn)中validation和validator的區(qū)別詳解,一般對(duì)于復(fù)雜的業(yè)務(wù)參數(shù)校驗(yàn),可以通過(guò)校驗(yàn)類單獨(dú)的校驗(yàn)方法進(jìn)行處理,通常對(duì)于一些與業(yè)務(wù)無(wú)關(guān)簡(jiǎn)單的參數(shù)校驗(yàn)可以采用validation和 validator通過(guò)注解的方式實(shí)現(xiàn)校驗(yàn),需要的朋友可以參考下2023-10-10
commons fileupload實(shí)現(xiàn)文件上傳的實(shí)例代碼
這篇文章主要介紹了commons fileupload實(shí)現(xiàn)文件上傳的實(shí)例代碼,包括文件上傳的原理分析等相關(guān)知識(shí)點(diǎn),本文給大家介紹的非常詳細(xì),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧2016-10-10
SpringMVC整合websocket實(shí)現(xiàn)消息推送及觸發(fā)功能
這篇文章主要為大家詳細(xì)介紹了SpringMVC整合websocket實(shí)現(xiàn)消息推送及觸發(fā)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03

