前言
最近一段時(shí)間一直在參與一些SaaS產(chǎn)品的設(shè)計(jì),發(fā)現(xiàn)SaaS主產(chǎn)品有90%的功能基本都是通用性,其中10%各個(gè)租戶都會(huì)出現(xiàn)定制化,比如一些頁(yè)面表單字段的差異化、業(yè)務(wù)規(guī)則差異化以及流程差異化等,這些差異化如果軟件架構(gòu)的可擴(kuò)展性不夠,很容易出現(xiàn)后期維護(hù)成本非常高。其實(shí)技術(shù)發(fā)展到今天,軟件架構(gòu)設(shè)計(jì)有一個(gè)核心的理念一直沒(méi)有不變,如何面的業(yè)務(wù)發(fā)展的不確定性快速實(shí)現(xiàn),比如Nosql的出現(xiàn)為了彌補(bǔ)傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)數(shù)據(jù)結(jié)構(gòu)的不確定性,規(guī)則引擎的出現(xiàn)為了解決業(yè)務(wù)規(guī)則的不確定性。今天同樣面對(duì)SaaS產(chǎn)品各個(gè)租戶的需求不確定性以及差異化,讓我很容易到微內(nèi)核插件架構(gòu)設(shè)計(jì),微內(nèi)核插件架構(gòu)設(shè)計(jì)是一種非常典型的架構(gòu)設(shè)計(jì)模式。
微內(nèi)核架構(gòu)本質(zhì)上是為了提高系統(tǒng)的擴(kuò)展性 。所謂擴(kuò)展性,是指系統(tǒng)在經(jīng)歷不可避免的變更時(shí)所具有的靈活性,以及針對(duì)提供這樣的靈活性所需要付出的成本間的平衡能力。也就是說(shuō),當(dāng)在往系統(tǒng)中添加新業(yè)務(wù)時(shí),不需要改變?cè)械母鱾€(gè)組件,只需把新業(yè)務(wù)封閉在一個(gè)新的組件中就能完成整體業(yè)務(wù)的升級(jí),我們認(rèn)為這樣的系統(tǒng)具有較好的可擴(kuò)展性。
就架構(gòu)設(shè)計(jì)而言,擴(kuò)展性是軟件設(shè)計(jì)的永恒話題。而要實(shí)現(xiàn)系統(tǒng)擴(kuò)展性,一種思路是提供可插拔式的機(jī)制來(lái)應(yīng)對(duì)所發(fā)生的變化。當(dāng)系統(tǒng)中現(xiàn)有的某個(gè)組件不滿足要求時(shí),我們可以實(shí)現(xiàn)一個(gè)新的組件來(lái)替換它,而整個(gè)過(guò)程對(duì)于系統(tǒng)的運(yùn)行而言應(yīng)該是無(wú)感知的,我們也可以根據(jù)需要隨時(shí)完成這種新舊組件的替換。
微內(nèi)核插件架構(gòu)
在正式介紹微內(nèi)核插件架構(gòu)之前,先介紹一下什么是內(nèi)核以及內(nèi)核分類。
百度百科是這樣介紹內(nèi)核:
內(nèi)核,是一個(gè)操作系統(tǒng)的核心。是基于硬件的第一層軟件擴(kuò)充,提供操作系統(tǒng)的最基本的功能,是操作系統(tǒng)工作的基礎(chǔ),它負(fù)責(zé)管理系統(tǒng)的進(jìn)程、內(nèi)存、設(shè)備驅(qū)動(dòng)程序、文件和網(wǎng)絡(luò)系統(tǒng),決定著系統(tǒng)的性能和穩(wěn)定性。內(nèi)核的分類可分為單內(nèi)核和雙內(nèi)核以及微內(nèi)核。
微內(nèi)核(Micro kernel)是提供操作系統(tǒng)核心功能的內(nèi)核的精簡(jiǎn)版本,它設(shè)計(jì)成在很小的內(nèi)存空間內(nèi)增加移植性,提供模塊化設(shè)計(jì),以使用戶安裝不同的接口,如 DOS、Workplace OS、Workplace UNIX 等。IBM、Microsoft、開(kāi)放軟件基金會(huì)(OSF)和 UNIX 系統(tǒng)實(shí)驗(yàn)室(USL)、鴻蒙 OS 等新操作系統(tǒng)都采用了這一研究成果的優(yōu)點(diǎn)。
與微內(nèi)核相對(duì)應(yīng)的一個(gè)概念是宏內(nèi)核,宏內(nèi)核是包含很多功能的底層程序,干的事情很多,且不可插拔;一點(diǎn)微小的修改都可能會(huì)影響到整個(gè)內(nèi)核,典型的”牽一發(fā)而動(dòng)全身“。linux 就是宏內(nèi)核,也因此被稱為 monolithic OS。Linux除了時(shí)鐘中斷、進(jìn)程創(chuàng)建與銷毀、進(jìn)程調(diào)度、進(jìn)程間通信外,其他的文件系統(tǒng)、內(nèi)存管理、輸入輸出、設(shè)備驅(qū)動(dòng)管理都需要內(nèi)核完成,其中的文件系統(tǒng)、內(nèi)存管理、設(shè)備驅(qū)動(dòng)等都被作為系統(tǒng)進(jìn)程放到了用戶態(tài)空間,屬于可擴(kuò)展插件部分。
微內(nèi)核只負(fù)責(zé)最核心的功能,其他功能都是通過(guò)用戶態(tài)獨(dú)立進(jìn)程以插件方式加入進(jìn)來(lái)的,微內(nèi)核負(fù)責(zé)進(jìn)程的管理、調(diào)度和進(jìn)程之間通訊,從而完成整個(gè)內(nèi)核需要的功能。當(dāng)某個(gè)功能出現(xiàn)問(wèn)題時(shí),由于該功能是以獨(dú)立進(jìn)程方式存在的,所以不會(huì)對(duì)其他進(jìn)程有什么影響從而導(dǎo)致內(nèi)核不可用,最多就是內(nèi)核某一功能現(xiàn)在不可用而已。
微內(nèi)核架構(gòu)(Microkernel Architecture),也被稱為插件式架構(gòu)(plug-in architecture),作為一個(gè)在幾十年前就被創(chuàng)建出來(lái)的架構(gòu)模式,它如今仍然被廣泛應(yīng)用在各個(gè)領(lǐng)域中。從組成結(jié)構(gòu)上講, 微內(nèi)核架構(gòu)包含兩部分組件:內(nèi)核系統(tǒng)和插件 。這里的內(nèi)核系統(tǒng)通常提供系統(tǒng)運(yùn)行所需的最小功能集,而插件是獨(dú)立的組件,包含自定義的各種業(yè)務(wù)代碼,用來(lái)向內(nèi)核系統(tǒng)增強(qiáng)或擴(kuò)展額外的業(yè)務(wù)能力。
微內(nèi)核架構(gòu)由以下兩部分組成:核心系統(tǒng)(core system)和插件(plug-in component),將應(yīng)用系統(tǒng)的業(yè)務(wù)邏輯拆分成核心系統(tǒng)和插件,能夠提供很好的可擴(kuò)展性和靈活性,極大地方便了后續(xù)需求的新增和修改。

核心模塊只擁有能使應(yīng)用運(yùn)行的最小功能邏輯。許多操作系統(tǒng)使用微內(nèi)核系統(tǒng)架構(gòu),這就是該結(jié)構(gòu)的名字由來(lái)。從業(yè)務(wù)應(yīng)用的角度,核心系統(tǒng)通常定義了一般商務(wù)邏輯,不包含特殊情況、特殊規(guī)則、復(fù)雜的條件的特定處理邏輯。
插件模塊是獨(dú)立存在的模塊,包含特殊的處理邏輯、額外的功能和定制的代碼,能拓展核心系統(tǒng)業(yè)務(wù)功能。通常,不同的插件模塊互相之間獨(dú)立,但是你可以設(shè)計(jì)成一個(gè)插件依賴于另外一個(gè)插件的情況。最重要的是,你需要讓插件之間的互依賴關(guān)系降低到最小,為避免繁雜的依賴問(wèn)題。
常見(jiàn)的微內(nèi)核插件架構(gòu)案例
在Web瀏覽器領(lǐng)域,谷歌的Chrome瀏覽器之所以被認(rèn)為功能強(qiáng)大,一個(gè)很重要的原因是它有著豐富的插件類型;在開(kāi)發(fā)工具領(lǐng)域,微軟的VS Code初始安裝后還只是個(gè)簡(jiǎn)單的文本編輯器,但用戶可以安裝各種插件,從而讓它搖身一變成為功能強(qiáng)大的IDE。
Chrome和VS Code以及Eclipse、IDEA都是微內(nèi)核架構(gòu)的典型應(yīng)用例子,它們提供一個(gè)具備最基礎(chǔ)能力的核心系統(tǒng),并定義好插件的開(kāi)發(fā)接口。至于需要開(kāi)發(fā)或安裝哪種類型的插件,則完全由普通開(kāi)發(fā)者和用戶決定,這樣的設(shè)計(jì)讓系統(tǒng)具備了極強(qiáng)的可定制化和可擴(kuò)展能力。
常見(jiàn)的一些開(kāi)源框架比如Dubbo、ShardingSphere、Skywalking以及Apache ShenYU網(wǎng)關(guān)都支持插件化架構(gòu),每個(gè)開(kāi)源軟件的微內(nèi)核插件設(shè)計(jì)核心思想都是一致的,其具體技術(shù)實(shí)現(xiàn)略有不同。在dubbo中,SPI的使用幾乎是瘋狂。dubbo針對(duì)JAVA SPI的局限性,自己重寫(xiě)了一套加載機(jī)制。可以針對(duì)不同的需求使用不同的實(shí)現(xiàn)類以及提供一些高級(jí)特性。
阿里巴巴的星環(huán)TMF框架其核心思想也是基于微內(nèi)核插件架構(gòu),TMF2.0框架改造的交易平臺(tái)支持了淘寶、天貓、聚劃、盒馬、大潤(rùn)發(fā)等一系列集團(tuán)交易業(yè)務(wù),通過(guò)業(yè)務(wù)管理域與運(yùn)行域分離、業(yè)務(wù)與業(yè)務(wù)的隔離架構(gòu),大幅度提高了業(yè)務(wù)在可擴(kuò)展性、研發(fā)效率以及可維護(hù)性問(wèn)題,同時(shí)以更好的開(kāi)放模式,讓業(yè)務(wù)方能自助進(jìn)行無(wú)侵入的需求開(kāi)發(fā)。
微內(nèi)核插件架構(gòu)設(shè)計(jì)
微內(nèi)核插件架構(gòu)包含兩個(gè)核心組件:系統(tǒng)核心(Core System)和插件化組件(Plug-in component)。Core System負(fù)責(zé)管理各種插件,當(dāng)然Core System也會(huì)包含一些重要功能,如插件注冊(cè)管理、插件生命周期管理、插件之間的通訊、插件動(dòng)態(tài)替換等。整體結(jié)構(gòu)如下:

微內(nèi)核插件架構(gòu)設(shè)計(jì)需要考慮如下四點(diǎn):
插件管理
- 核心系統(tǒng)需要知道當(dāng)前有哪些插件可用,如何加載這些插件,什么時(shí)候加載插件。常見(jiàn)的實(shí)現(xiàn)方法是插件注冊(cè)表機(jī)制。
- 核心系統(tǒng)提供插件注冊(cè)表(可以是配置文件,也可以是代碼,還可以是數(shù)據(jù)庫(kù)),插件注冊(cè)表含有每個(gè)插件模塊的信息,包括它的名字、位置、加載時(shí)機(jī)(啟動(dòng)就加載,還是按需加載)等。
插件連接
- 插件連接指插件如何連接到核心系統(tǒng)。通常來(lái)說(shuō),核心系統(tǒng)必須制定插件和核心系統(tǒng)的連接規(guī)范,然后插件按照規(guī)范實(shí)現(xiàn),核心系統(tǒng)按照規(guī)范加載即可。
- 常見(jiàn)的連接機(jī)制有 OSGi(Eclipse 使用)、消息模式、依賴注入(Spring 使用),甚至使用分布式的協(xié)議都是可以的,比如 RPC 或者 HTTP Web 的方式。
插件通信
- 插件通信指插件間的通信。雖然設(shè)計(jì)的時(shí)候插件間是完全解耦的,但實(shí)際業(yè)務(wù)運(yùn)行過(guò)程中,必然會(huì)出現(xiàn)某個(gè)業(yè)務(wù)流程需要多個(gè)插件協(xié)作,這就要求兩個(gè)插件間進(jìn)行通信。
- 微內(nèi)核的核心系統(tǒng)也必須提供類似的通信機(jī)制,各個(gè)插件之間才能進(jìn)行正常的通信。
事實(shí)上, Java中已經(jīng)為我們提供了一種微內(nèi)核架構(gòu)的實(shí)現(xiàn)方式,就是JDK SPI。這種實(shí)現(xiàn)方式針對(duì)如何設(shè)計(jì)和實(shí)現(xiàn) SPI 提出了一些開(kāi)發(fā)和配置上的規(guī)范,ShardingSphere、Dubbo 使用的就是這種規(guī)范,只不過(guò)在這基礎(chǔ)上進(jìn)行了增強(qiáng)和優(yōu)化。另外Java中提供了OSGI,也可以作為插件化架構(gòu)設(shè)計(jì),早期淘寶HSF組件中間版本做過(guò)OSGi的嘗試,最終還是因?yàn)樗氖褂盟拇鷥r(jià)大于好處而放棄。
SPI
SPI主要用于框架設(shè)計(jì)。在框架設(shè)計(jì)中可能會(huì)有不同的實(shí)現(xiàn)類。如果所有的實(shí)現(xiàn)類都放入代碼中的話,代碼的耦合程度就太高了。SPI就是為了降低代碼耦合度的,但是如果不知道SPI機(jī)制在看一些源碼的時(shí)候就感覺(jué)云里霧里的。SPI簡(jiǎn)單來(lái)說(shuō)就是使用配置文件來(lái)決定使用哪個(gè)實(shí)現(xiàn)類。將原來(lái)可能要在代碼里面直接引用的實(shí)現(xiàn)類,寫(xiě)入到配置文件中,然后通過(guò)一個(gè)加載器去加載。
SPI優(yōu)勢(shì)很明顯就是簡(jiǎn)單易用,如果對(duì)于只有一個(gè)實(shí)現(xiàn)類,比如JDBC。每個(gè)廠商都去實(shí)現(xiàn)但是每個(gè)廠商都只有自己一個(gè)實(shí)現(xiàn)類。針對(duì)這種上游接口Java SPI是合理的。
可以看到Java SPI是將文件里面的所有接口都去實(shí)現(xiàn)。在系統(tǒng)的實(shí)際開(kāi)發(fā)中一個(gè)接口的實(shí)現(xiàn)類可能很多,在不同的場(chǎng)景下使用的也不一樣。Java SPI這個(gè)時(shí)候就不太適用了。
如果有些實(shí)現(xiàn)類在運(yùn)行時(shí)沒(méi)有使用,并且加載比較繁瑣,必然會(huì)耗費(fèi)整個(gè)系統(tǒng)的資源。
OSGI
OSGI的全稱是open services gateway initiative,是一個(gè)插件化的標(biāo)準(zhǔn),而不是一個(gè)可運(yùn)行的框架。

OSGI的優(yōu)勢(shì)主要如下幾點(diǎn):
1.可復(fù)用性強(qiáng)
OSGI框架本身可復(fù)用性極強(qiáng),很容易構(gòu)建真正面向接口的程序架構(gòu),每一個(gè)Bundle 都是一個(gè)獨(dú)立可復(fù)用的單元。
2.基于OSGI的應(yīng)用程序可動(dòng)態(tài)更改運(yùn)行狀態(tài)和行為。
在OSGI框架中,每一個(gè)Bundle實(shí)際上都是可熱插拔的,因此,對(duì)一個(gè)特定的Bundle進(jìn)行修改不會(huì)影響到容器中的所有應(yīng)用,運(yùn)行的大部分應(yīng)用還是可以照常工作。當(dāng)你將修改后的Bundle再部署上去的時(shí)候,容器從來(lái)沒(méi)有重新啟過(guò)。這種可動(dòng)態(tài)更改狀態(tài)的特性在一些及時(shí)性很強(qiáng)的系統(tǒng)中比較重要,尤其是在Java Web項(xiàng)目中,無(wú)需重啟應(yīng)用服務(wù)器就可以做到應(yīng)用的更新。
3.職責(zé)分離
基于OSGI框架的系統(tǒng)可分可合,其結(jié)構(gòu)的優(yōu)勢(shì)性導(dǎo)致具體的Bundle不至于影響到全局,不會(huì)因?yàn)榫植康腻e(cuò)誤導(dǎo)致全局系統(tǒng)的崩潰。例如Java EE項(xiàng)目中可能會(huì)因?yàn)槟硞€(gè)Bean的定義或注入有問(wèn)題,而導(dǎo)致整個(gè)應(yīng)用跑不起來(lái),而使用OSGI則不會(huì)有這種問(wèn)題,頂多相關(guān)的幾個(gè)Bundle無(wú)法啟動(dòng)。
OSGI同樣也有一些缺點(diǎn):
1.類加載機(jī)制
每個(gè)Bundle都由單獨(dú)的類加載器加載,與一些Java EE項(xiàng)目中使用比較多的框架整合比較困難,如Spring MVC、Struts2等,例如筆者嘗試在OSGI應(yīng)用中整合Spring MVC時(shí),通過(guò)DispatcherServlet啟動(dòng)的Bean與OSGI Bundle啟動(dòng)的Bean無(wú)法相互依賴,需要做特殊處理,后面文章中會(huì)有介紹。
2.OSGI框架提供的管理端不夠強(qiáng)大
現(xiàn)在的管理端中僅提供了基本的Bundle狀態(tài)管理、日志查看等功能,像動(dòng)態(tài)修改系統(tǒng)級(jí)別的配置(config.ini)、動(dòng)態(tài)修改Bundle的配置(Manifest.mf)、啟動(dòng)級(jí)別等功能都尚未提供,而這些在實(shí)際的項(xiàng)目或產(chǎn)品中都是非常有必要的。
3.使用成本高
采用OSGI作為規(guī)范的模塊開(kāi)發(fā)、部署方式自然給現(xiàn)有開(kāi)發(fā)人員提出了新的要求,需要學(xué)習(xí)新的基于OSGI的開(kāi)發(fā)方式。
微內(nèi)核插件架構(gòu)設(shè)計(jì)
1.定義擴(kuò)展點(diǎn)注解
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Extension {
String tenantId() default "0";//根據(jù)SaaS租戶ID進(jìn)行隔離
int ordinal() default 0;
Class<? extends ExtensionPoint>[] points() default {};
String[] plugins() default {};
}
2.定義業(yè)務(wù)擴(kuò)展點(diǎn)
擴(kuò)展點(diǎn)表示一塊邏輯在不同的業(yè)務(wù)有不同的實(shí)現(xiàn),使用擴(kuò)展點(diǎn)做接口申明。
public interface ExtensionPoint {
}
3.插件實(shí)體定義
主程序定義插件類Plugin,用于封裝從插件配置文件讀取業(yè)務(wù)擴(kuò)展實(shí)現(xiàn),具體定義如下:
@Data
public class Plugin {
/**
* 插件名稱
*/
private String pluginId;
/**
* 插件版本
*/
private String version;
/**
* 插件路徑
*/
private String path;
/**
* 插件類全路徑
*/
private String className;
}
4.插件管理
創(chuàng)建插件管理類,初始化插件。
/**
* 使用URLClassLoader動(dòng)態(tài)加載jar文件,實(shí)例化插件中的對(duì)象
*
*/
public abstract class PluginManager {
private Map<String, Class> clazzMap = new HashMap<>();
public PluginManager(List<Plugin> plugins) throws PluginException {
init(plugins);
}
/**
* 插件初始化方法
*/
private void init(List<Plugin> plugins) throws MalformedURLException {
try{
int size = plugins.size();
for(int i = 0; i < size; i++) {
Plugin plugin = plugins.get(i);
String filePath = plugin.getPath();
// URL url = new URL("file:" + filePath);
URL url = new File(plugin.getPath()).toURI().toURL();
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
Class<?> clazz = urlClassLoader.loadClass(plugin.getClassName());
clazzMap.put(plugin.getClassName(), clazz);
}
}catch (Exception e) {
throw new PluginException("plugin " + plugin.getPluginName() + " init error," + e.getMessage());
}
}
/**
* 獲得插件
* @param className 插件類全路徑
* @return
* @throws PluginException
*/
public ExtensionPoint getInstance(String className) throws PluginException {
// 插件實(shí)例化對(duì)象,插件都是實(shí)現(xiàn)ExtensionPoint接口
Class clazz = clazzMap.get(className);
Object instance = null;
try {
instance = clazz.newInstance();
} catch (Exception e) {
throw new PluginException("plugin " + className + " instantiate error," + e.getMessage());
}
return (ExtensionPoint)instance;
}
PluginState startPlugin(String pluginId);
/**
* 停止所有插件
*/
void stopPlugins();
/**
* 停止插件
*/
PluginState stopPlugin(String pluginId);
/**
* 卸載所有插件
*/
void unloadPlugins();
/**
*卸載插件
*/
boolean unloadPlugin(String pluginId);
}
5.測(cè)試
public class Main {
public static void main(String[] args) {
try {
// 從配置文件加載插件
List<Plugin> pluginList = PluginLoader.load();
// 初始化插件管理類
PluginManager pluginManager = new PluginManager(pluginList);
// 循環(huán)調(diào)用所有插件
for(Plugin plugin : pluginList) {
ExtensionPoint extensionPoint = pluginManager.getInstance(plugin.getClassName());
System.out.println("開(kāi)始執(zhí)行[" + plugin.getName() + "]插件...");
// 調(diào)用插件
extensionPoint.xxx();
System.out.println("[" + plugin.getName() + "]插件執(zhí)行完成");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
以上就是一個(gè)非常簡(jiǎn)版的基于擴(kuò)展點(diǎn)的微內(nèi)核插件,其設(shè)計(jì)思想和多數(shù)微內(nèi)核插件架構(gòu)相差無(wú)幾,當(dāng)然如果要用到生產(chǎn)還需要考慮很多問(wèn)題,比如熱部署問(wèn)題、類隔離機(jī)制以及對(duì)于多語(yǔ)言的支持等。
關(guān)于Java的一些開(kāi)源微內(nèi)核插件架構(gòu)也有一些開(kāi)源實(shí)現(xiàn):
SpringPlugin:https://github.com/spring-projects/spring-plugin
p4fj:https://github.com/pf4j/pf4j
jspf:https://code.google.com/archive/p/jspf/
結(jié)合我們自己的一些業(yè)務(wù)場(chǎng)景,參考p4fj設(shè)計(jì)思想我也開(kāi)發(fā)了一個(gè)微內(nèi)核插件框架,解決了熱部署問(wèn)題、類隔離機(jī)制以及對(duì)于多語(yǔ)言等問(wèn)題,主要應(yīng)用于SaaS一些業(yè)務(wù)租戶定制化需求,后面我也會(huì)考慮將其開(kāi)源出來(lái)放到GitHub上,大家敬請(qǐng)關(guān)注。
總結(jié)
Robert C.Martin曾經(jīng)說(shuō)過(guò),軟件開(kāi)發(fā)技術(shù)發(fā)展的歷史就是一個(gè)如何想方設(shè)法方便地增加插件,從而構(gòu)建一個(gè)可擴(kuò)展、可維護(hù)的系統(tǒng)架構(gòu)的故事。在敏捷開(kāi)發(fā)的潮流之下,需求的變更如同家常便飯,系統(tǒng)不應(yīng)該因?yàn)槟骋徊糠职l(fā)生變更從而導(dǎo)致其他不相關(guān)的部分出現(xiàn)問(wèn)題。將系統(tǒng)設(shè)計(jì)為微內(nèi)核架構(gòu),就等于構(gòu)建起了一面變更無(wú)法逾越的防火墻,插件發(fā)生的變更就不會(huì)影響系統(tǒng)的核心業(yè)務(wù)邏輯。
微內(nèi)核架構(gòu)的設(shè)計(jì)思想,能夠極大提升系統(tǒng)的可擴(kuò)展性和健壯性,在其他的一些軟件方法論里,我們也隱約能看到它的影子。比如在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中,領(lǐng)域?qū)泳拖喈?dāng)于核心系統(tǒng),它定義了系統(tǒng)的核心業(yè)務(wù)邏輯;基礎(chǔ)設(shè)施層則相當(dāng)于插件,切換不同的基礎(chǔ)設(shè)施并不會(huì)影響系統(tǒng)的業(yè)務(wù)邏輯,這得益于基礎(chǔ)設(shè)施層依賴倒置的設(shè)計(jì)原則。
當(dāng)然,作為微內(nèi)核架構(gòu)也有著一些缺點(diǎn),它天然具備了單體架構(gòu)的一些劣勢(shì),比如核心系統(tǒng)作為架構(gòu)的中心節(jié)點(diǎn)并不具備Fault tolerance能力。因此,該架構(gòu)模式往往被廣泛應(yīng)用于一些著重提供很強(qiáng)的用戶定制化功能的小型產(chǎn)品,如VS Code等,它們對(duì)系統(tǒng)的Elasticity、Fault tolerance和Scalability并沒(méi)有很高的要求。