ZStack源碼剖析之設計模式鑑賞——三駕馬車

本文首發於泊浮目的專欄: https://segmentfault.com/blog...

前言

隨着ZStack的版本迭代,其能夠掌管的資源也愈來愈多。但新增模塊的結構卻仍是大體相同,此便是ZStack的經典設計模式——這套模式也被開發者稱爲ZStack三駕馬車java

實例分析

以PrimaryStorage爲例,其APIMsg的真正邏輯處理第一站就是PrimaryStorageManagerImplgit

若是是對特定的PrimaryStorage進行操做,將會經過相應的Factory得出對應類型並轉發至相應的Base:github

private void passThrough(PrimaryStorageMessage pmsg) {
        PrimaryStorageVO vo =  dbf.findByUuid(pmsg.getPrimaryStorageUuid(), PrimaryStorageVO.class);
        if (vo == null && allowedMessageAfterSoftDeletion.contains(pmsg.getClass())) {
            PrimaryStorageEO eo = dbf.findByUuid(pmsg.getPrimaryStorageUuid(), PrimaryStorageEO.class);
            vo = ObjectUtils.newAndCopy(eo, PrimaryStorageVO.class);
        }

        Message msg = (Message) pmsg;
        if (vo == null) {
            String err = String.format("Cannot find primary storage[uuid:%s], it may have been deleted", pmsg.getPrimaryStorageUuid());
            bus.replyErrorByMessageType(msg, errf.instantiateErrorCode(SysErrors.RESOURCE_NOT_FOUND, err));
            return;
        }

        PrimaryStorageFactory factory = getPrimaryStorageFactory(PrimaryStorageType.valueOf(vo.getType()));
        PrimaryStorage ps = factory.getPrimaryStorage(vo);
        ps.handleMessage(msg);
    }

PrimaryStorageFactory是一個接口,在此基礎上有各式各樣的實現:如LocalCephNFS等。segmentfault

public interface PrimaryStorageFactory {
    PrimaryStorageType getPrimaryStorageType();

    PrimaryStorageInventory createPrimaryStorage(PrimaryStorageVO vo, APIAddPrimaryStorageMsg msg);

    PrimaryStorage getPrimaryStorage(PrimaryStorageVO vo);

    PrimaryStorageInventory getInventory(String uuid);
}

這就像現實中的模型同樣——在ZStack中能夠有PrimaryStorage,並且能夠有不一樣類型的PrimaryStorage:設計模式

PrimaryStorage:架構

  • Local
  • Ceph
  • NFS

這在軟件工程中便是一種分離領域(Layered Architecture)的具象。應用層對應ZStack的ManagerImpl,而Base更像是領域層。ide

應用層

應用層的定義應該是:ui

  • 定義軟件要完成的任務,而且指揮表達領域概念的對象來解決問題。這一層負責的工做對業務來講意義重大,也是與其餘系統的應用層進行交互的必要渠道。
  • 應用層要儘可能簡單,不包含業務規則或者知識,而只爲下一次的領域對象協調任務,分配工做,使它們互相協做。它沒有反映業務狀況的狀態,可是卻能夠具備另一種狀態,爲用戶或程序顯示某個任務的進度。

而在ZStack中,的確也像上面說的如此。在源碼中咱們能夠看到,對實例操做的API所有被轉發到了Base層去,而Manager這裏handle的每每是一些過濾性、Get型API,如APIListPrimaryStorageMsgAPIGetPrimaryStorageMsgAPIGetPrimaryStorageTypesMsg等。spa

Manager便是API(這裏API不單單是APIxxxMsg,同時包含用於通訊的內部Msg。注意,它們都implements自Message這個接口)的入口,以及用於管理服務的生命週期。debug

領域層

定義:

  • 負責表達業務概念,業務狀態信息以及業務規則。儘管保存業務狀態的技術細節由基礎設施層(在ZStack如DataBaseFacade便是),可是反映業務狀況的狀態是由本層控制而且使用的。注意,領域層是業務軟件的核心

PrimaryStorageBase爲例,其自己對應了DB中的一條記錄,而且在改變狀態後也Refresh本身。並對操做單獨實例的Msg進行handle。

通訊

雖然分了層,而且關係是鬆散的。可是各個層之間也是須要通訊的,那麼層與層之間只能是單向的。上層能夠直接使用或操做下層元素,方法是經過調用下層元素的公共接口,保持對下層元素的引用(至少是暫時的),以及採用常規的交互手段。而若是下層元素須要與上層元素通訊,則須要採用另外一種通訊機制——好比回調或者Observers模式(在ZStack中便是ExtensionPoint)。

回調

咱們仍是以PrimaryStorageBase爲例。在其作連接操做時,邏輯以下:

private void doConnect(ConnectParam param, final Completion completion) {
        thdf.chainSubmit(new ChainTask(completion) {
            @Override
            public String getSyncSignature() {
                return String.format("reconnect-primary-storage-%s", self.getUuid());
            }

            @Override
            public void run(SyncTaskChain chain) {
                changeStatus(PrimaryStorageStatus.Connecting);

                connectHook(param, new Completion(chain, completion) {
                    @Override
                    public void success() {
                        self = dbf.reload(self);
                        changeStatus(PrimaryStorageStatus.Connected);
                        logger.debug(String.format("successfully connected primary storage[uuid:%s]", self.getUuid()));

                        RecalculatePrimaryStorageCapacityMsg rmsg = new RecalculatePrimaryStorageCapacityMsg();
                        rmsg.setPrimaryStorageUuid(self.getUuid());
                        bus.makeLocalServiceId(rmsg, PrimaryStorageConstant.SERVICE_ID);
                        bus.send(rmsg);

                        tracker.track(self.getUuid());

                        completion.success();
                        chain.next();
                    }

                    @Override
                    public void fail(ErrorCode errorCode) {
                        tracker.track(self.getUuid());

                        self = dbf.reload(self);
                        changeStatus(PrimaryStorageStatus.Disconnected);
                        logger.debug(String.format("failed to connect primary storage[uuid:%s], %s", self.getUuid(), errorCode));
                        completion.fail(errorCode);
                        chain.next();
                    }
                });
            }

            @Override
            public String getName() {
                return getSyncSignature();
            }
        });
    }

而不一樣的connectHook都有不一樣的實現。

在抽象等級上,PrimaryStorageBase是比圖中的這些Base高的。而這類具象Base能夠Message返回Success或者Fail使高層Base作出不一樣的決策。

Observers

繼續,在PrimaryStorageBase中,其中handle APIAttachPrimaryStorageToClusterMsg的地方會作事件發送:

extpEmitter.preAttach(self, msg.getClusterUuid());

其會發送向:

在這裏,一個Base經過了Observers模式向某個ManagerImpl發送了事件,實現了下層往上層的通訊。

小結

在大型軟件工程中,咱們一般會給這樣的應用劃分層次。分別在每層中進行設計,使其具備內聚性而且只依賴於它的下層,而下層與上層也只有鬆散的耦合。這使得模型含義豐富,結構清晰。也使得整個應用架構更加茁壯。

相關文章
相關標籤/搜索