ZStack源码剖析之设计模式鉴赏——三驾马车

简介: 随着ZStack的版本迭代,其可以掌管的资源也越来越多。但新增模块的结构却还是大致相同,此即是ZStack的经典设计模式——这套模式也被开发者称为ZStack三驾马车。

前言

随着ZStack的版本迭代,其可以掌管的资源也越来越多。但新增模块的结构却还是大致相同,此即是ZStack的经典设计模式——这套模式也被开发者称为ZStack三驾马车

实例分析

以PrimaryStorage为例,其APIMsg的真正逻辑处理第一站就是PrimaryStorageManagerImpl

如果是对特定的PrimaryStorage进行操作,将会通过相应的Factory得出对应类型并转发至相应的Base:

    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等。

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更像是领域层。

应用层

应用层的定义应该是:

  • 定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。这一层负责的工作对业务来说意义重大,也是与其他系统的应用层进行交互的必要渠道。
  • 应用层要尽量简单,不包含业务规则或者知识,而只为下一次的领域对象协调任务,分配工作,使它们互相协作。它没有反映业务情况的状态,但是却可以具有另外一种状态,为用户或程序显示某个任务的进度。

而在ZStack中,的确也像上面说的如此。在源码中我们可以看到,对实例操作的API全部被转发到了Base层去,而Manager这里handle的往往是一些过滤性、Get型API,如APIListPrimaryStorageMsgAPIGetPrimaryStorageMsgAPIGetPrimaryStorageTypesMsg等。

Manager即是API(这里API不仅仅是APIxxxMsg,同时包含用于通信的内部Msg。注意,它们都implements自Message这个接口)的入口,以及用于管理服务的生命周期。

领域层

定义:

  • 负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节由基础设施层(在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发送了事件,实现了下层往上层的通信。

小结

在大型软件工程中,我们通常会给这样的应用划分层次。分别在每层中进行设计,使其具有内聚性并且只依赖于它的下层,而下层与上层也只有松散的耦合。这使得模型含义丰富,结构清晰。也使得整个应用架构更加茁壮。

目录
相关文章
设计模式 存储 人工智能
693 0
|
12月前
|
设计模式 存储 缓存
Netty源码—9.性能优化和设计模式
本文主要介绍了Netty的两大性能优化工具、FastThreadLocal的源码和总结、Recycler的设计理念/使用/四个核心组件/初始化/对象获取/对象回收/异线程收割对象和总结,以及Netty设计模式的单例模式和策略模式。
310 53
|
设计模式 存储 Java
「全网最细 + 实战源码案例」设计模式——责任链模式
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,允许将请求沿着处理者链进行发送。每个处理者可以处理请求或将其传递给下一个处理者,从而实现解耦和灵活性。其结构包括抽象处理者(Handler)、具体处理者(ConcreteHandler)和客户端(Client)。适用于不同方式处理不同种类请求、按顺序执行多个处理者、以及运行时改变处理者及其顺序的场景。典型应用包括日志处理、Java Web过滤器、权限认证等。
364 13
「全网最细 + 实战源码案例」设计模式——责任链模式
|
设计模式 存储 算法
「全网最细 + 实战源码案例」设计模式——命令模式
命令模式(Command Pattern)是一种行为型设计模式,将请求封装成独立对象,从而解耦请求方与接收方。其核心结构包括:Command(命令接口)、ConcreteCommand(具体命令)、Receiver(接收者)和Invoker(调用者)。通过这种方式,命令的执行、撤销、排队等操作更易扩展和灵活。 适用场景: 1. 参数化对象以操作。 2. 操作放入队列或远程执行。 3. 实现回滚功能。 4. 解耦调用者与接收者。 优点: - 遵循单一职责和开闭原则。 - 支持命令组合和延迟执行。 - 可实现撤销、恢复功能。 缺点: - 增加复杂性和类数量。
510 14
「全网最细 + 实战源码案例」设计模式——命令模式
|
设计模式 算法 开发者
「全网最细 + 实战源码案例」设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列可替换的算法或行为,并将它们封装成独立的类。通过上下文持有策略对象,在运行时动态切换算法,提高代码的可维护性和扩展性。适用于需要动态切换算法、避免条件语句、经常扩展算法或保持算法独立性的场景。优点包括符合开闭原则、运行时切换算法、解耦上下文与策略实现、减少条件判断;缺点是增加类数量和策略切换成本。示例中通过定义抽象策略接口和具体策略类,结合上下文类实现动态算法选择。
566 8
「全网最细 + 实战源码案例」设计模式——策略模式
|
设计模式 SQL 算法
「全网最细 + 实战源码案例」设计模式——模板方法模式
模板方法模式是一种行为型设计模式,定义了算法的骨架并在父类中实现不变部分,将可变部分延迟到子类实现。通过这种方式,它避免了代码重复,提高了复用性和扩展性。具体步骤由抽象类定义,子类实现特定逻辑。适用于框架设计、工作流和相似算法结构的场景。优点包括代码复用和符合开闭原则,缺点是可能违反里氏替换原则且灵活性较低。
466 7
「全网最细 + 实战源码案例」设计模式——模板方法模式
|
设计模式 存储 安全
「全网最细 + 实战源码案例」设计模式——组合模式
组合模式(Composite Pattern)是一种结构型设计模式,用于将对象组合成树形结构以表示“部分-整体”的层次结构。它允许客户端以一致的方式对待单个对象和对象集合,简化了复杂结构的处理。组合模式包含三个主要组件:抽象组件(Component)、叶子节点(Leaf)和组合节点(Composite)。通过这种模式,客户端可以统一处理简单元素和复杂元素,而无需关心其内部结构。适用于需要实现树状对象结构或希望以相同方式处理简单和复杂元素的场景。优点包括支持树形结构、透明性和遵循开闭原则;缺点是可能引入不必要的复杂性和过度抽象。
502 22
|
设计模式 存储 缓存
「全网最细 + 实战源码案例」设计模式——享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,旨在减少大量相似对象的内存消耗。通过分离对象的内部状态(可共享、不变)和外部状态(依赖环境、变化),它有效减少了内存使用。适用于存在大量相似对象且需节省内存的场景。模式优点包括节省内存和提高性能,但会增加系统复杂性。实现时需将对象成员变量拆分为内在和外在状态,并通过工厂类管理享元对象。
498 92
|
设计模式 前端开发 数据库
「全网最细 + 实战源码案例」设计模式——桥接模式
桥接模式(Bridge Pattern)是一种结构型设计模式,通过将抽象部分与实现部分分离,使它们可以独立变化,从而降低代码耦合度,避免类爆炸,提高可扩展性。其结构包括实现类接口、具体实现类、抽象类和精确抽象类。适用于多维度扩展类、隐藏实现细节、简化庞杂类以及运行时切换实现方法的场景。优点包括高扩展性、隐藏实现细节、遵循开闭原则和单一职责原则;缺点是可能增加代码复杂度。示例中展示了不同操作系统播放不同格式视频文件的实现。
428 19