如何快速啓動服務

在軟件開發中,一般的作法是將一些基礎,簡單的服務組合在一塊兒而造成一個具備某一功能的特定服務。這種搭積木的結構,或者說自下而上的組合更有利於程序的資源隔離以及維護與拓展。高層的服務依賴底層服務提供業務計算,低層的服務提供諸如數據存儲,網絡傳輸等基礎操做。java

這些低層的服務就如現實世界中的城市基礎設施,沒有道路車輛就無處行駛,沒有發電廠提供的電力全部的電力設備就沒法工做。因此,在程序世界構建的時候,這些基礎服務必須預先完成加載(初始化)。當低層接口與高層接口變得比較多時,它們的關係錯綜複雜。因此,合理地管理它們變得很是有必要!下面咱們將討論幾種常見的模式:git

有序加載

一種很是簡單的方式是:將全部的服務按照依賴層級逐個地加載它們。以下圖, C 分別依賴了 A 與 B 。在程序啓動的時候能夠按照 A -> B -> C 的方式依次加載它們。github

這種方式可以有效地解決服務依賴,在服務不是特別複雜,依賴的鏈不是很長的狀況下它會是一個很不錯的選擇。可是這種方式會將整個服務啓動的時候變得很長,由於它們的加載是逐個進行的。所以在大型的服務中這種方式是不被推薦的。網絡

按需加載

另外一種方式是按需加載,在服務被使用(調用)時,再去加載它所依賴的服務。以下圖,有 2 個高層服務 C 與 E,它們分別依賴了基礎服務 A,B 與 B,D。app

當訪問 C 服務的時候,會按需加載 C <-> B <-> A。C 去嘗試找到並加載 B,B 也會嘗試找到並加載 A。這是一個有返回的過程,全部使用符號 <-> 表示。同理,E 服務的加載過程也是按照 E <-> B <-> D。maven

這種模式最大的優勢是能作到按需加載,當高層服務被使用的時候纔會去加載依賴的服務。在容器技術中也稱之爲 依賴注入。爲避免重複加載的過程,實際上咱們會把這些服務放在一個服務容器裏面,服務的加載會有容器處理,咱們要作的僅僅是從一個容器裏面找到它們。例如上圖中 C, E 都依賴了 B。ide

大多的應用服務都會選擇按需加載的模式,它不只能避免有序加載的不能同時加載多個服務的問題,按需加載實際上由於不會加載那些無訪問的服務,因此能有效地節省一部分資源。svg

但這就是終極方案嗎?顯然不是!下面咱們來了解第三種模式。性能

分組加載

分組加載是將服務按照依賴的層級劃分稱不一樣的 ServiceGroup。ServiceGroup 之間是按照順序加載的,但 ServiceGroup 內的服務是並行加載的。這種方式可以快速地將全部的服務一次性加載。與按需加載不一樣,分組加載能夠在應用啓動成功之時就能夠當即服務;同時對於按序加載能過作到更加快速地啓動服務。以下圖,ServiceGroup 1 中的 A, B, 'C' 是並行加載的,ServiceGroup 2ServiceGroup 1 加載完成以後纔開始加載。this

服務 A, B, D 之間沒有依賴,因此它們的加載能夠是無序的。當 ServiceGroup 1 加載完成以後,ServiceGroup 2 在加載以前就已經加載了必要的服務。這時候不會出現 ServiceNotFound 的問題。下面的代碼使用了 DSL 來描述整個加載的過程:

GroupedServiceBus serviceBus = ...
serviceBus.start(serviceA, serviceB, serviceD)
          .then(serviceC, serviceE)
          .awaitStarted();

銷燬

銷燬的順序必須是加載順序的反序。這樣你才能保證不回出現必要服務的丟失。

分組加載實現

使用 Gauva 的 Service 做爲服務的基礎接口,下面給出了一個分組加載的簡單實現。
Current Version

import com.google.common.util.concurrent.Service;

public interface GroupedServiceBus {
    void awaitStarted();

    void awaitStopped();

    GroupedServiceBus start(Service... services);

    GroupedServiceBus then(Service... services);

    GroupedServiceBus awaitServiceGroupStarted(Service... services);

}

使用 DSL 的方式設計 API 能過讓它變得更加容易使用跟理解。

public interface KernelService {

}

可使用 KernelService 來指定哪些服務是必須正確加載的,當 KernelService 加載失敗應用並不能提供一個正確的服務,這時你也許能夠將整個程序退出。

public class GroupedServiceBusImpl implements GroupedServiceBus {

    private final List<ServiceManager> serviceCluster = new ArrayList<>();
    private final ServiceManager.Listener listener = new ServiceManager.Listener() {
        @Override
        public void failure(Service service) {
            if (service instanceof KernelService) {
                logger.error("KernelService [{}] start failure, system will be exit 1.", service.getClass());
                System.exit(1);
            } else {
                logger.error("Service [{}] start failure.", service.getClass());
            }
        }
    };

    @Override
    public void awaitStarted() {
        if (!serviceCluster.isEmpty()) {
            for (final ServiceManager serviceManager : serviceCluster) {
                awaitStartedServiceManager(serviceManager);
            }
        }
    }

    @Override
    public void awaitStopped() {
        if (!serviceCluster.isEmpty()) {
            for (int i = serviceCluster.size() - 1; i > -1; i--) {
                final ServiceManager serviceManager = serviceCluster.get(i);
                serviceManager.stopAsync();
                serviceManager.awaitStopped();
            }
            serviceCluster.clear();
        }
    }

    @Override
    public GroupedServiceBus start(final Service... services) {
        then(services);
        return this;
    }

    @Override
    public GroupedServiceBus then(Service... services) {
        final ServiceManager serviceManager = new ServiceManager(ImmutableList.copyOf(services));
        serviceManager.addListener(listener);
        serviceCluster.add(serviceManager);
        return this;
    }

    private void awaitStartedServiceManager(final ServiceManager serviceManager) {
        if (!serviceManager.isHealthy()) {
            serviceManager.startAsync();
            serviceManager.awaitHealthy();
        }
    }
}

上面的 serviceCluster 是一個 ServiceManager 的集合,在這裏 ServiceManager 就是咱們上面講到的 ServiceGroup。在 ServiceGroup 的全部服務均可以並行加載。

總結

這三種模式沒有哪種是絕對正確跟優秀的。在程序設計中,除了要考慮性能,穩定性等同時還要避免陷入過分設計的陷阱。你的選擇須要適應你的環境!

相關文章
相關標籤/搜索