您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關通用插件系統ZStack是怎樣的,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
當前IaaS軟件更像云控制器軟件,要成為一個完整的云解決方案還缺少很多特性(features)。作為一個正在發展中的技術,預測一個完整的解決方案的必備的所有特性是非常困難的,所以一個IaaS軟件不可能在一開始就完成它所有的特性。基于以上事實,一個IaaS軟件的架構必須有能力,在添加新特性的同時保持核心結構穩定。ZStack的通用插件系統,使得特性可以像插件一樣實現(在線程內或在線程外),這樣不只能使ZStack的功能得到了拓展,也可以注入業務邏輯內部去改變默認的行為。
當前IaaS軟件的處境,除了一些構建的非常好的IaaS(像AWS),大多數IaaS(包括我們的ZStack)仍然不是一個完整的云解決方案。由于像Amazon這樣的已經探索多年的先驅,公有云已經有一個更成熟的模型,對于公共云解決方案應該是什么樣子而言。由于仍然處在開發階段,私有云目前還沒有經過驗證的完整的解決方案。不像專用的公有云軟件,可以專門為制造商的基礎設施和服務定制;開源的IaaS軟件必須同時考慮公有云和私有云的需求,使得創建一個完整的解決方案變得更加困難。因為我們沒有辦法預測一個完整的解決方案應該是什么樣,我們唯一的辦法是提供一個插件式的架構,它能在添加插件的同時,不影響核心業務穩定性。
許多軟件聲稱自己是插件式的,但是很多并不是真的插件式的,或至少不是完全插件式的。在解釋原因之前,讓我們看兩種主要的插件式架構的形式。雖然有很多文章討論過這個話題,以我們的經驗來看,我們把所有插件歸納成兩種結構,可以被準確的描述為GoF design patterns一書中的策略模式和觀察者模式。
源自策略者模式的插件
這種形式的插件通常是通過提供不同的實現,拓展軟件特定的功能;或者通過添加插件APIs去添加新的功能。我們熟悉的很多軟件都是通過這種模式搭建的,比如,操作系統的驅動,網頁瀏覽器的插件。這種插件組成的工作方式是,允許應用程序通過定義良好的協議去訪問插件 。
源自觀察者模式的插件
這種形式的插件通常注入應用程序的業務邏輯,針對特定的事件。一旦一個事件發生,掛在上面的插件將被調用,以執行一段甚至可能改變執行流的代碼,比如,當事件滿足某些條件,拋出異常去停止執行流。基于這種模式的插件通常對最終用戶是透明的、純內部實現的,例如,一個監聽器監聽數據庫插入事件。這種插件的工作方式是,允許插件通過定義良好的擴展點去訪問應用程序。
大多數軟件聲稱它們是插件式的,要么實現了這些組成方式中的一種,要么有一部分代碼實現這些組成方式。為了變得完全插件化,軟件必須設想到這么一個想法,即所有的業務邏輯都使用這兩種方式實現。這意味著整個軟件是由大量的小插件組成的,就像樂高玩具一樣。
一個重要的設計原則貫穿了所有ZStack的組件:每一個組件都應該被這么設計,信息最少、自包含、無關其他組件。 比如,為了創建一個虛擬機,分配磁盤、提供DHCP、建立SNAT都是非常必要的步驟,管理創建VM的組件應該非常清楚。但是它真的需要知道這么多嗎?為什么這個組件不能簡化為,分配VM的CPU/內存,然后給主機發送啟動請求,讓其他組件,像存儲、網絡來關心它們自己的事情。你可能已經猜到了這個答案:不,在ZStack中,組件并不需要知道那么多,沒錯!可以是那么簡單。我們充分意識到這么一個事實,你的組件知道的信息越多,你的應用程序耦合越緊密,最終你得到一個復雜的難以修改的軟件。 所以我們提供以下插件形式來保證我們的架構是松耦合的,并且使我們容易添加新特性,最終形成一個完整的云解決方案。
通常IaaS軟件中的插件是整合不同物理資源的驅動。例如,NFS主存儲,ISCSI主存儲,基于VLAN的L2網絡,基于Open vSwitch的L2網絡;這些插件都是我們剛剛提到的策略模式的形式。ZStack已經將云資源抽象成:虛擬機管理器、主存儲、備份存儲、L2網絡、L3網絡等等。每個資源都有一個相關的驅動程序,作為一個單獨的插件。要添加一個新的驅動程序,開發人員只需要實現三個組件:一個類型,一個工廠,和一個具體的資源實現,這些全部都被封裝在單一的插件中,通常被構建成一個jar文件。引用Open vSwitch舉一個例子,讓我們假定我們將創建一個使用Open vSwitch作為后臺的新L2網絡,然后開發者需要:
public static L2NetworkType type = new L2NetworkType("Openvswitch"); /* once the type is declared as above, there will be a new L2 network type called 'Openvswitch' that can be retrieved by API */(一旦類型被聲明,一個新的叫做“Openvswitch”的l2網絡類型可以被API檢索)
public class OpenvswitchL2NetworkFactory implements L2NetworkFactory { [@Override](https://my.oschina.net/u/1162528) public L2NetworkType getType() { /* return type defined in 1.1 */ return type; } [@Override](https://my.oschina.net/u/1162528) public L2NetworkInventory createL2Network(L2NetworkVO vo, APICreateL2NetworkMsg msg) { /* * new resource will normally have own creational API APICreateOpenvswitchL2NetworkMsg that * usually inherits APICreateL2NetworkMsg, and own database object OpenvswitchL2NetworkVO that * usually inherits L2NetworkVO, and a java bean OpenvswitchL2NetworkInventory that usually inherits * L2NetworkInventory representing all properties of Openvswitch L2 network. */(新的資源將通常有一個創建的API APICreateOpenvswitchL2NetworkMsg,通常繼承自APICreateL2NetworkMsg,還將有自己的數據庫對象,OpenvswitchL2NetworkVO,通常繼承自L2NetworkVO,和一個java bean OpenvswitchL2NetworkInventory,通常在L2NetworkInventory中表示Openvswitch L2網絡的所有屬性) APICreateOpenvswitchL2NetworkMsg cmsg = (APICreateOpenvswitchL2NetworkMsg)APICreateL2NetworkMsg; OpenvswitchL2NetworkVO cvo = new OpenvswitchL2NetworkVO(vo); evaluate_OpenvswitchL2NetworkVO_with_parameters_in_API(cvo, cmsg); save_to_database(cvo); return OpenvswitchL2NetworkInventory.valueOf(cvo); } [@Override](https://my.oschina.net/u/1162528) public L2Network getL2Network(L2NetworkVO vo) { /* return the concrete implementation defined in 1.3 */ return new OpenvswitchL2Network(vo); } }
public class OpenvswitchL2Network extends L2NoVlanNetwork { public OpenvswitchL2Network(L2NetworkVO self) { super(self); } [@Override](https://my.oschina.net/u/1162528) public void handleMessage(Message msg) { /* handle Openvswitch L2 network specific messages(both API and non API) and delegate * others to the base class L2NoVlanNetwork; so the implementation can focus on own business * logic and let the base class handle things like attaching cluster, detaching cluster; * of course, the implementation can override any message handler if it wants, for example, * override L2NetworkDeletionMsg to do some cleanup work before being deleted. (處理和Openvswitch L2網絡相關的特定消息(API消息或不是API的消息),并把其他消息交給L2NoVlanNetwork的基礎類處理;所以它的實現可以關注它自身的業務邏輯,并讓基礎類處理一些如綁定集群,解綁集群,的事情。當然,實現方法也可以覆蓋任何消息處理器,例如,覆蓋L2NetworkDeletionMsg在刪除前做一些清理工作。) */ if (msg instanceof OpenvswitchL2NetworkSpecificMsg1) { handle((OpenvswitchL2NetworkSpecificMsg1)msg); } else if (msg instanceof OpenvswitchL2NetworkSpecificMsg2) { handle((OpenvswitchL2NetworkSpecificMsg2)msg); } else { super.handleMessage(msg); } } }
讓三個組件一起放到一個Maven模塊中,添加一些必須的Spring配置文件,并編譯為JAR文件,你就在ZStack中創建了一個新的L2網絡類型。所有的Zstack資源驅動程序都是通過以上步驟實現的(類型,工廠,具體實現)。一旦你已經學會了怎么為一個資源創建驅動程序,你就學會了怎么為所有的資源這么做。正如我們在“ZStack--進程內的微服務架構”中提到的一樣,驅動可以有自己的API和配置方法。
策略模式的插件(驅動)允許你擴展現有的ZStack的功能;然而,為了使架構松耦合,插件必須能注入應用程序的業務邏輯,甚至是其他插件的業務邏輯;觀察模式插件的關鍵是拓展點,拓展點允許一段插件的代碼在一個代碼流運行的時候被調用。目前Zstack定義了大約100個擴展點,暴露了大量讓插件去接收事件或改變代碼流行為的場景。創建一個新的擴展點就是定義一個java接口,組件可以很容易地創建擴展點,以允許其他組件注入自己的業務邏輯。為了了解它是如何工作的,讓我們繼續我們的Open vSwitch的例子;假設Open vSwitch L2網絡需要鉤入創建VM的過程,以在VM創建之前準備好GRE隧道,該插件實現如下:
PreVmInstantiateResourceExtensionPoint: public class OpenvswitchL2NetworkCreateGRETunnel implements PreVmInstantiateResourceExtensionPoint { [@Override](https://my.oschina.net/u/1162528) public void preBeforeInstantiateVmResource(VmInstanceSpec spec) throws VmInstantiateResourceException { /* * you can do some check here; if any condition makes you think the VM should not be created/started, * you can throw VmInstantiateResourceException to stop it */ } @Override public void preInstantiateVmResource(VmInstanceSpec spec, Completion completion) { /* create the GRE tunnel, you can get all necessary information about the VM from VmInstanceSpec */ completion.success(); } @Override public void preReleaseVmResource(VmInstanceSpec spec, Completion completion) { /* *in case VM fails to create/start for some reason, for cleanup, you can delete the prior created GRE tunnel here */ completion.success(); } }
當ZStack連接到KVM主機,Open vSwitch L2網絡想要在主機上檢查并啟動Open vSwitch的守護進程,那么它實現KVMHostConnectExtensionPoint:
public class OpenvswitchL2NetworkKVMHostConnectedExtension implements KVMHostConnectExtensionPoint { @Override public void kvmHostConnected(KVMHostConnectedContext context) throws KVMHostConnectException { /* * you can use various methods like SSH login, HTTP call to KVM agent to check the Openvswitch daemon你可以使用很多方式(如SSH登錄,KVM代理的HTTP調用)通過使用在KVMHostConnectedContext上的信息,去檢查在主機上的Openvswitch的守護進程。如果任意狀態讓你認為主機不能提供Openvswitch L2網絡方法,你可以拋出KVMHostConnectExtensionPoint去阻止主機連接。 * on the host, using information in KVMHostConnectedContext. If any condition makes you think the * host cannot provide Openvswitch L2 network function, you can throw KVMHostConnectExtensionPoint to * stop the host from being connected. */ } }
最后,你需要宣傳你有兩個組件實現了這些擴展點,ZStack的插件系統將確保所有者在一個適當的時間調用你的組件。該通知是在插件的Spring配置文件中完成的:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:zstack="http://zstack.org/schema/zstack" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://zstack.org/schema/zstack http://zstack.org/schema/zstack/plugin.xsd" default-init-method="init" default-destroy-method="destroy"> <bean id="OpenvswitchL2NetworkCreateGRETunnel" class="org.zstack.network.l2.ovs.OpenvswitchL2NetworkCreateGRETunnel"> <zstack:plugin> <zstack:extension interface="org.zstack.header.vm.PreVmInstantiateResourceExtensionPoint" /> </zstack:plugin> </bean> <bean id="OpenvswitchL2NetworkKVMHostConnectedExtension" class="org.zstack.network.l2.ovs.OpenvswitchL2NetworkKVMHostConnectedExtension"> <zstack:plugin> <zstack:extension interface="org.zstack.kvm.KVMHostConnectExtensionPoint" /> </zstack:plugin> </bean> </beans>
這就是你所需要做的一切。創建一個新類型的L2網絡,卻不需要更改其他任意一個ZStack組件的甚至一行代碼。這是ZStack保持其核心業務流程穩定的基礎。
不要OSGI:熟悉Eclipse和OSGI的人可能已經注意到,我們的插件系統和eclipse、OSGI的非常相似。可能有人會問,為什么我們不直接使用OSGI,它可是為Java應用程序創建插件系統而專門設計的。實際上,我們花費了相當多的時間嘗試OSGI;然而,我們感覺它是用力過猛。我們不喜歡在我們的應用程序有另一個容器,不喜歡單獨的類裝載器,不喜歡它創建插件的復雜性。看起來OSGI正付出大量努力使插件相互隔離,但ZStack想讓插件扁平化。我們已經注意到,許多項目在代碼中引入了不必要的限制,以使整體架構明顯是分層的、隔離的,但由于設計糟糕的接口,插件必須寫很多丑陋的代碼來克服這些限制,反而打亂了真正的架構。ZStack將所有的插件作為自己核心的一部分來考慮,針對核心業務流程擁有一樣的特權。我們不是構建一個類似瀏覽器的消費級應用程序,用戶可能會錯誤地安裝惡意插件;我們是在構建一個企業級軟件,每一個角落都需要經過嚴格的測試。一個扁平的插件系統使我們的代碼變得簡單和健壯。
除了以上兩種方式外,開發人員確實有第三種方式擴展ZStack--進程外服務。雖然ZStack把所有的編排服務包裝成一個單一的進程,獨立于業務流程服務的功能可以被實現為獨立的服務,這些服務運行在不同的進程甚至不同的機器上。ZStack Web UI,一個通過RabbitMQ和ZStack編排服務進行交互的Python應用程序,是一個很好的例子。ZStack有一個定義良好的消息規范,進程外的服務可以用任何語言編寫,只要它們能通過RabbitMQ進行交互。ZStack也有稱為canonical event的機制,用于暴露一些內部事件給總線,比如VM創建,VM停止,磁盤創建。諸如計費系統的軟件完全可以通過監聽這些事件,建立一個進程外的服務。如果一個服務想要在進程外,但仍需要訪問一些還沒有暴露的核心業務流程的數據結構,或需要訪問數據庫,它可以使用一種混合的方式,即在管理節點上的一塊小插件負責采集數據并將它們發送給消息代理,在進程外的服務接受這些數據并完成自己的事情。
看完上述內容,你們對通用插件系統ZStack是怎樣的有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。