日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

在 JAVA 的世界里遨游,如果能擁有一雙善于發現的眼睛,有很多東西留心去看,外加耐心助力,仔細去品,往往會品出不一樣的味道。

通過本次分享,能讓你輕松 get 如下幾點,絕對收獲滿滿。

a)如何讓 Java 程序實現優雅停服?有思想才是硬道理!

 

b)addShutdownHook 的使用場景?會用才是王道!

 

c)addShutdownHook 鉤子函數到底是個啥?刨根問底!

1. 如何讓 Java 程序實現優雅停服?

無論是自研基礎服務框架,還是分析開源項目源碼,細心的 Java 開發同學,都會發現 Runtime.getRuntime().addShutdownHook 這么一句代碼的身影,這句到底是干什么用的?

接下來就一起細品,看看它香不香?

阿里開源的數據同步神器 Canal 啟動時的部分源碼:

Java 如何利用鉤子函數實現優雅停服?刨根問底

 

Apache 麾下的用于海量日志收集的 Flume 啟動時的部分源碼:

Java 如何利用鉤子函數實現優雅停服?刨根問底

 

仰望了一下開源的項目,不妨從中提煉一下共性(同樣的代碼遇到多次,勢必會品出味道),寫段代碼跑跑看(站在 flume 源碼的肩膀上,起飛)。

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 體驗 Java 優雅停服
 *
 * @author 一猿小講
 */
public class Application {

    /**
     * 監控服務
     */
    private ScheduledThreadPoolExecutor monitorService;

    public Application() {
        monitorService = new ScheduledThreadPoolExecutor(1);
    }

    /**
     * 啟動監控服務,監控一下內存信息
     */
    public void start() {
        System.out.println(String.format("啟動監控服務 %s", Thread.currentThread().getId()));
        monitorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println(String.format("最大內存: %dm  已分配內存: %dm  已分配內存中的剩余空間: %dm  最大可用內存: %dm",
                        Runtime.getRuntime().maxMemory() / 1024 / 1024,
                        Runtime.getRuntime().totalMemory() / 1024 / 1024,
                        Runtime.getRuntime().freeMemory() / 1024 / 1024,
                        (Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory() +
                                Runtime.getRuntime().freeMemory()) / 1024 / 1024));
            }
        }, 2, 2, TimeUnit.SECONDS);
    }

    /**
     * 釋放資源(代碼來源于 flume 源碼)
     * 主要用于關閉線程池(看不懂的同學莫糾結,當做黑盒去對待)
     */
    public void stop() {
        System.out.println(String.format("開始關閉線程池 %s", Thread.currentThread().getId()));
        if (monitorService != null) {
            monitorService.shutdown();
            try {
                monitorService.awaitTermination(10, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                System.err.println("Interrupted while waiting for monitor service to stop");
            }
            if (!monitorService.isTerminated()) {
                monitorService.shutdownNow();
                try {
                    while (!monitorService.isTerminated()) {
                        monitorService.awaitTermination(10, TimeUnit.SECONDS);
                    }
                } catch (InterruptedException e) {
                    System.err.println("Interrupted while waiting for monitor service to stop");
                }
            }
        }
        System.out.println(String.format("線程池關閉完成 %s", Thread.currentThread().getId()));
    }

    /**
     * 應用入口
     */
    public static void main(String[] args) {
        Application application = new Application();
        // 啟動服務(每隔一段時間監控輸出一下內存信息)
        application.start();

        // 添加鉤子,實現優雅停服(主要驗證鉤子的作用)
        final Application appReference = application;
        Runtime.getRuntime().addShutdownHook(new Thread("shutdown-hook") {
            @Override
            public void run() {
                System.out.println("接收到退出的訊號,開始打掃戰場,釋放資源,完成優雅停服");
                appReference.stop();
            }
        });
        System.out.println("服務啟動完成");
    }
}

經常讀文的我很清楚,耐心讀文章中源碼的同學應該很少,所以我還是用圖給你簡單捋一捋。

Java 如何利用鉤子函數實現優雅停服?刨根問底

 

標注1:start 方法利用線程池啟動一個線程去定時監控內存信息;

標注2:stop 方法用于在退出程序之前,進行關閉線程池進而釋放資源。

程序跑起來,效果如下。

Java 如何利用鉤子函數實現優雅停服?刨根問底

 

當進行 kill 操作時,程序確實進行了資源釋放,效果確實很優雅。

Java 如何利用鉤子函數實現優雅停服?刨根問底

 

一切看似那么自然,一切又是那么完美,這是真的嗎?殺進程時候如果用 kill -9,這種情況下會發生什么現象呢?

Java 如何利用鉤子函數實現優雅停服?刨根問底

 

嗚呼!結果不會騙人的,當用 kill -9 的時候,就顯得很粗暴了,壓根不管什么資源釋放,不管三七二十一,就是終止程序。

估計很多同學,都擅長用 kill -9 進行殺進程,為了線上的應用安全,還是用 kill -15 命令殺進程吧,這樣會給應用留點時間去打掃一下戰場,釋放一下資源。

好了,通過仔細品味,借助 JDK 自帶的 addShutdownHook 來助力應用,確實能讓線上服務跑起來很優雅。

有思想才是硬道理!

2. addShutdownHook 的使用場景?

通過代碼試驗,能夠感知 addShutdownHook(new Thread(){}) 是 JVM 銷毀前要執行的一個線程,那么只要是涉及到資源回收的場景,應該都可以滿足,下面簡單列舉幾個。

a)數據同步神器 Canal 借助它,來進行關閉 socket 鏈接、釋放 canal 的工作節點、清理緩存信息等;

 

b)海量日志收集 Flume 借助它,來實現線程池資源關閉、工作線程停止等;

 

c)在應用正常退出時,執行特定的業務邏輯、關閉資源等操作。

 

d)在 OOM 宕機、 CTRL+C、或執行 kill pid,導致 JVM 非正常退出時,加入必要的挽救措施成為可能。

其實,在 Java 的世界里遨游,只有想不到的,沒有做不到的!

3. addShutdownHook 鉤子函數是個啥?

刨根還要問到底!

Java 如何利用鉤子函數實現優雅停服?刨根問底

 

Hook 翻譯過來是「鉤子」的意思,那顧名思義就是用來掛東西的。

Java 如何利用鉤子函數實現優雅停服?刨根問底

 

如圖所示,在現實生活中,要制作臘肉,首先用鉤子把肉勾住,然后掛在竹竿上,這應該是鉤子的作用。

生活如此,一切設計理念都源于生活,在 Java 的世界里,亦是如此。

Java 如何利用鉤子函數實現優雅停服?刨根問底

 

如上圖 Runtime 的源碼所示,遵循 Java 的核心思想「一切皆是對象」,那么可以把 addShutdownHook 方法可以視作掛鉤子,其實稱之為鉤子函數會好一些,而現實生活中的肉就可以抽象為釋放資源的線程。

只要有這個鉤子函數,對外就提供了擴展能力,研發人員就可以往鉤子上掛各種自定義的場景實現,這種設計你細品那絕對是香!這也就是 Canal、Flume、Tomcat 等不同應用,在優雅停服時有著不同的實現的原因吧。

大白話,鉤子函數有了,想掛什么東西,根據心情自己定就好了。

再深入去刨會發現,由于底層數據結構采用 Map 來進行存儲,那么就支持研發人員掛多個 shutdownHook 的實現,又帶來了無限的可能性(又帶來了無限的「刺激」,自己好好去體會)。

Java 如何利用鉤子函數實現優雅停服?刨根問底

 

好了,避免頭大,就刨到這兒吧,感興趣的可自行順著思路繼續刨下去。

4. 寄語,寫在最后

作為研發人員:要擁有一雙善于發現的眼睛,要善于發現代碼之美。

作為研發人員:要時常思考面對當前的項目,是否能夠簡單重構讓程序跑的更順溜。

作為研發人員:要多看、多悟、多提煉、多實踐。

作為研發人員:請不要放棄代碼,因為程序終會鑄就人生。

本次分享就到這里,希望對你有所幫助吧。

一起聊技術、談業務、噴架構,少走彎路,不踩大坑。歡迎關注「一猿小講」,會持續輸出原創精彩分享,敬請期待!

分享到:
標簽:鉤子 函數 Java
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定