In this paper, starting from purpose of moored floating column: segmentfault.com/blog/camile
This article is participating in “Java Theme Month – Java Development in Action”, see the activity link for details
preface
As versions of ZStack iterate, the number of resources it can manage grows. But the structure of the new modules remains much the same, a classic ZStack design pattern known to developers as the ZStack Troika.
The example analysis
PrimaryStorage, for example, the real logic to handle the first stop is PrimaryStorageManagerImpl APIMsg.
If the operation is performed on a specific PrimaryStorage, the corresponding type will be derived from the corresponding Factory and forwarded to the corresponding 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);
}
Copy the code
PrimaryStorageFactory is an interface on which there are various implementations: Local, Ceph, NFS, etc.
public interface PrimaryStorageFactory {
PrimaryStorageType getPrimaryStorageType(a);
PrimaryStorageInventory createPrimaryStorage(PrimaryStorageVO vo, APIAddPrimaryStorageMsg msg);
PrimaryStorage getPrimaryStorage(PrimaryStorageVO vo);
PrimaryStorageInventory getInventory(String uuid);
}
Copy the code
This is just like a real model — there can be primary storages in ZStack, and there can be different types of primary storages:
PrimaryStorage:
- Local
- Ceph
- NFS
This is a representation of Layered Architecture in software engineering. The application layer corresponds to the ManagerImpl of ZStack, while Base is more like the domain layer.
The application layer
The definition of the application layer should be:
- Define the tasks the software is supposed to accomplish and direct objects that express domain concepts to solve the problem. This layer is responsible for work that is significant to the business and a necessary channel for interaction with the application layers of other systems.
- The application layer should be as simple as possible, containing no business rules or knowledge, but only coordinating tasks and assigning work to the next domain object to make them work together. It does not reflect the state of the business situation, but it can have another state that shows the progress of a task to the user or program.
And in ZStack, it does. In the source code, we can see that all the APIS for instance operation are forwarded to the Base layer, while the handle in Manager is usually some filter and Get API. Such as APIListPrimaryStorageMsg, APIGetPrimaryStorageMsg, APIGetPrimaryStorageTypesMsg, etc.
Manager is the API (here the API is not just APIxxxMsg, but also contains the internal Msg for communication. Note that they both implements the entry from the Message interface and are used to manage the life cycle of the service.
Domain layer
Definition:
- Responsible for expressing business concepts, business state information, and business rules. Although the technical details of holding the business state are held by the infrastructure layer (such as DataBaseFacade in ZStack), the state that reflects the business state is controlled and used by this layer. Note that the domain layer is the core of the business software.
Take PrimaryStorageBase as an example, which itself corresponds to a record in the DB and also refreshes itself after changing state. Handle the Msg that operates on a single instance.
communication
Although there are layers, and the relationship is loose. However, each layer also needs to communicate with each other, so it can only be one-way between layers. The upper layer can directly use or manipulate the lower element by calling its public interface, keeping references to the lower element (at least temporarily), and using normal interaction methods. If the lower-level elements need to communicate with the upper-level, another communication mechanism is required — such as a callback or Observers mode (ExtensionPoint in ZStack).
The callback
Let’s take PrimaryStorageBase as an example. When it links, the logic is as follows:
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(); }}); }Copy the code
And differentconnectHook
They all have different implementations.At the level of abstraction, primary StorageBases are higher than those in the figure. This kind of concrete Base can return Success or Fail to the higher level Base to make a different decision.
Observers
Continue, in PrimaryStorageBase, which handle APIAttachPrimaryStorageToClusterMsg place do event to send:
extpEmitter.preAttach(self, msg.getClusterUuid());
Copy the code
It will be sent to:
Here, a Base sends events to a ManagerImpl through Observers mode, enabling bottom-to-top communication.
summary
In large software projects, we typically have hierarchies for such applications. Design each layer separately so that it is cohesive and dependent only on its lower layers, which are loosely coupled to the upper layers. This makes the model rich in meaning and clear in structure. It also makes the entire application architecture stronger.