Dubboサービスの優雅な停止

6024 ワード

サービスプロバイダが停止した場合、新しいリクエストを受信しないとマークし、新しいリクエストが来たときに直接エラーを報告し、クライアントに他のマシンを再試行させる.次に、スレッドプール内のスレッドが実行されているかどうかを検出し、タイムアウトしない限り、すべてのスレッドの実行が完了するまで待機します.サービス消費者が停止すると、新しい呼び出し要求は開始されず、すべての新しい呼び出しはクライアントでエラーが発生します.その後、リクエストの有無を検出した応答はまだ返されず、タイムアウトしない限り強制的に閉じられます.
ここではまずフックプログラムとは何かを説明します.Javaプログラムでは、閉じるフックを追加することで、プログラムが終了したときにリソースを閉じ、スムーズに終了する機能を実現することができます.Runtimeを使う.addShutdownHook(Thread hook)メソッドでは、JVMが閉じたフックを登録できます.このフックは、以下のシーンで呼び出すことができます.
  • プログラム正常終了
  • はSystemを用いる.exit()
  • 端末は、Ctrl+Cを使用するトリガされた割り込み
  • を使用する.
  • システムは
  • を閉じる
  • Kill pidコマンドを使用してプロセス
  • を終了
    私たちはRuntime.getRuntime().addShutdownHook()を通じてフックを登録して、ApplicationShutdownHooks.add(hook)に呼び出されて、最後にHOOKSというIdentityHashMapに保存されていることを発見しました.それはいつフックプログラムをトリガーしたのですか.ApplicationShutdownHooksには静的ブロックがありました
       static {        try {
                Shutdown.add(1 /* shutdown hook invocation order */,
                    false /* not registered if shutdown in progress */,
                    new Runnable() {
                        public void run() {
                            runHooks();
                        }
                    }
                );
                hooks = new IdentityHashMap<>();
            } catch (IllegalStateException e) {
                hooks = null;
            }
        }
    

    最終的にはrunHooksメソッドが呼び出されます.System.exit()を確認しますが、最終的にはShutDown.exit()->sequence()を通じて入ってきて、runHooksを呼び出してフックプログラムを呼び出します.Javaはどのようにkill命令に応答したのでしょうか.SignalHandlerによって実現され、openjdkのwindowsディレクトリとsolarisディレクトリの下にTerminator.javaというコードがあります.
        SignalHandler sh = new SignalHandler() {            public void handle(Signal sig) {
                    Shutdown.exit(sig.getNumber() + 0200);
                }
       };
      Signal.handle(new Signal("HUP"), sh);
      Signal.handle(new Signal("INT"), sh);
      Signal.handle(new Signal("TERM"), sh);
    

    最後に、void* oldHandler = os::signal(sig, newHandler)によってlinuxシステムのsignal信号が取得される.
    振り返ってdubboを見ると、優雅なダウンタイムのタイムアウト時間を設定できます.デフォルトのタイムアウト時間は10秒です.(タイムアウトは強制的に閉じます)
        
         
    
    

    サービス側フックプログラムを見てみましょう.
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
                public void run() {
                    if (logger.isInfoEnabled()) {
                        logger.info("Run shutdown hook now.");
                    }
                    ProtocolConfig.destroyAll();
                }
       }, "DubboShutdownHook"));
    

    その結果、ProtocolConfig.destroyAll()メソッドが呼び出されました.
     public static void destroyAll() {
            AbstractRegistryFactory.destroyAll();
            ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Protocol.class);
            for (String protocolName : loader.getLoadedExtensions()) {
                try {
                    Protocol protocol = loader.getLoadedExtension(protocolName);
                    if (protocol != null) {
                        protocol.destroy();
                    }
                } catch (Throwable t) {
                    logger.warn(t.getMessage(), t);
                }
            }
        }
    

    すべてのProtocolプロトコルをロードし、destroyメソッドをループして呼び出します.次に、DubboProtocoldestroyメソッドを見てみましょう.
        public void destroy() {        for (String key : new ArrayList(serverMap.keySet())) {
                ExchangeServer server = serverMap.remove(key);
                if (server != null) {
                    try {
                        if (logger.isInfoEnabled()) {
                            logger.info("Close dubbo server: " + server.getLocalAddress());
                        }
                        server.close(getServerShutdownTimeout());
                    } catch (Throwable t) {
                        logger.warn(t.getMessage(), t);
                    }
                }
            }
            
            for (String key : new ArrayList(referenceClientMap.keySet())) {
                ExchangeClient client = referenceClientMap.remove(key);
                if (client != null) {
                    try {
                        if (logger.isInfoEnabled()) {
                            logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress());
                        }
                        client.close();
                    } catch (Throwable t) {
                        logger.warn(t.getMessage(), t);
                    }
                }
            }
            
            for (String key : new ArrayList(ghostClientMap.keySet())) {
                ExchangeClient client = ghostClientMap.remove(key);
                if (client != null) {
                    try {
                        if (logger.isInfoEnabled()) {
                            logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress());
                        }
                        client.close();
                    } catch (Throwable t) {
                        logger.warn(t.getMessage(), t);
                    }
                }
            }
            stubServiceMethodsMap.clear();
            super.destroy();
        }
    
  • serverは、サービス側がDubboProtocolopenServerを介してNettyを介してサービスを開始するため、serverMap.put(key, createServer(url))を閉じる.閉じるときは、ポートとシステムリソースを解放するために、サービスを閉じる必要があります.
  • reference client共有リンクを閉じ、ReferenceCountExchangeClient
  • ghost client(公式注釈では幽霊client)を閉じるこの操作は、プログラムバグがclientを誤って閉じることを防止するための防御措置
  • にすぎない.
  • クリアstubメソッドMap
  • suer.destroyはInvokerを閉じ、サービスを利用できないように設定します.そしてExporterを通ります.Uniexport()エクスポートされたサービスを閉じる
  • まとめ
    オンラインサービスは簡単なkill -9ではなく、killトリガアプリケーションのフックプログラムを行い、関連するリソースのクリーンアップを行い、ずっと閉じられなければ、最終的にkill -9を通じて実行することができます.