簡介: 作為一名JAVA程序員,相信同學(xué)們都聽說過微內(nèi)核架構(gòu)設(shè)計,也有自己的理解。那么微內(nèi)核是如何被提出來的?微內(nèi)核在操作系統(tǒng)內(nèi)核的設(shè)計中又有什么作用?本文從插件化(Plug-in)架構(gòu)的角度來詮釋微內(nèi)核架構(gòu)設(shè)計,通過微內(nèi)核架構(gòu)和微服務(wù)架構(gòu)的對比,分享其對微服務(wù)設(shè)計的參考意義。
關(guān)于微內(nèi)核架構(gòu)設(shè)計現(xiàn)在比較熱,聽起來好像是操作系統(tǒng)內(nèi)核相關(guān)的,作為Java程序員,操作系統(tǒng)內(nèi)核那么遙遠的事情,好像和我們沒有什么關(guān)系。但是如果我說微內(nèi)核其實就是插件化(Plug-in)架構(gòu),你一定會一臉疑惑,“你居然向Java程序員解釋什么是插件化架構(gòu)?我每天都在用啊,Eclipse、IntelliJ IDEA、OSGi、Spring Plugin、SPI等,哪個不是插件化架構(gòu)。我的一些項目也是采用插件化設(shè)計的,如使用插件實現(xiàn)流程控制定制等等”。但是別著急,即便是我們每天都在使用的技術(shù),而且大多數(shù)人也都知道,如果我們能將其闡述得更清楚,并且能從中發(fā)現(xiàn)一些問題,做出一些優(yōu)化有助于以后的架構(gòu)設(shè)計,那么大多數(shù)人在日常的設(shè)計和開發(fā)中都能受益,豈不是更好。現(xiàn)在我們就來聊一聊微內(nèi)核架構(gòu)設(shè)計。
一、 微內(nèi)核設(shè)計之操作系統(tǒng)內(nèi)核
微內(nèi)核設(shè)計其實就是插件體系。我們都知道,操作系統(tǒng)內(nèi)核誕生得比較早,所以插件化最早被用在內(nèi)核設(shè)計上,于是就有了微內(nèi)核設(shè)計這一稱呼。
微內(nèi)核是這樣一種內(nèi)核:它只完成內(nèi)核不得不完成的功能,包括時鐘中斷、進程創(chuàng)建與銷毀、進程調(diào)度、進程間通信,而其他的諸如文件系統(tǒng)、內(nèi)存管理、設(shè)備驅(qū)動等都被作為系統(tǒng)進程放到了用戶態(tài)空間。說白了,微內(nèi)核是相對于宏內(nèi)核而言的,像linux就是典型的宏內(nèi)核,它除了時鐘中斷、進程創(chuàng)建與銷毀、進程調(diào)度、進程間通信外,其他的文件系統(tǒng)、內(nèi)存管理、輸入輸出、設(shè)備驅(qū)動管理都需要內(nèi)核完成。
也就是說,微內(nèi)核是相對宏內(nèi)核而言的,宏內(nèi)核是一個包含非常多功能的底層程序,也就是我們現(xiàn)在講的Monolith。它干的事情非常多,而且不是可插拔的,修改一些小的功能,都會涉及到整個程序的重新編譯等,比如一個功能出現(xiàn)了一個小bug,可能導(dǎo)致整個內(nèi)核都出問題。這也是很多人將Linux稱為monolithic OS的原因。而微內(nèi)核只負責(zé)最核心的功能,其他功能都是通過用戶態(tài)獨立進程以插件方式加入進來,然后微內(nèi)核負責(zé)進程的管理、調(diào)度和進程之間通訊,從而完成整個內(nèi)核需要的功能?;疽粋€功能出現(xiàn)問題,但是該功能是以獨立進程方式存在的,不會對其他進程有什么影響從而導(dǎo)致內(nèi)核不可用,最多就是內(nèi)核某一功能現(xiàn)在不可用而已。
微內(nèi)核就是一個運行在最高級別的程序片段,它能完成用戶態(tài)程序不能完成的一些功能。微內(nèi)核通過進程間通信來協(xié)調(diào)各個系統(tǒng)進程間的合作,這就需要系統(tǒng)調(diào)用,而系統(tǒng)調(diào)用需要切換堆棧以及保護進程現(xiàn)場,比較耗費時間;而宏內(nèi)核則是通過簡單的函數(shù)調(diào)用來完成各個模塊之間的合作,所以理論上宏內(nèi)核效率要比微內(nèi)核高。這個和微服務(wù)的架構(gòu)設(shè)計一樣,我們將Monolith應(yīng)用劃分為多個小應(yīng)用后,系統(tǒng)的設(shè)計就變得比較復(fù)雜了,之前都是應(yīng)用內(nèi)部函數(shù)調(diào)用,現(xiàn)在要涉及網(wǎng)絡(luò)通訊、超時等問題,同時響應(yīng)時間會被拉長。
聊到這里,相信大家對微內(nèi)核和宏內(nèi)核已經(jīng)有了一個大致的了解,看起來各有千秋。但是宏內(nèi)核有一個最大的問題就是定制和維護陳本?,F(xiàn)在的移動設(shè)備和IoT設(shè)備越來越多,如果要把一個龐大復(fù)雜的內(nèi)核適配到某一設(shè)備上,是一件非常復(fù)雜的事情,如果很簡單的話,那么把Linux內(nèi)核適配到Android內(nèi)核,甚至到Tesla等車載系統(tǒng),基本上人人都可以做了。
因此我們更需要一個微內(nèi)核的架構(gòu)設(shè)計,方便定制,而且非常小,可以實現(xiàn)功能的熱替換或者在線更新等,這就是微內(nèi)核被提出來的核心需求。但是微內(nèi)核有一個運行的效率問題,所以在微內(nèi)核和宏內(nèi)核之間,又有了Hybrid內(nèi)核,主要是想擁有微內(nèi)核的靈活性,同時在關(guān)鍵點上有宏內(nèi)核的性能。微內(nèi)核設(shè)計在理論上確實有效率問題,但是隨著芯片設(shè)計、硬件性能提升等,這方面或許已經(jīng)有了非常大的提升,已經(jīng)不再是最關(guān)鍵的問題。
總體下來,內(nèi)核設(shè)計有三個形式,如下:
二、 插件化(Plug-in)架構(gòu)設(shè)計
上面聊了微內(nèi)核在操作系統(tǒng)內(nèi)核設(shè)計中的作用,接下來我們就開始討論更通用的插件化架構(gòu)設(shè)計,畢竟這個詞大家都明白。
插件化架構(gòu)非常簡單,就兩個核心組件:系統(tǒng)核心(Core System)和插件化組件(Plug-in component)。Core System負責(zé)管理各種插件,當然Core System也會包含一些重要功能,如插件注冊管理、插件生命周期管理、插件之間的通訊、插件動態(tài)替換等。整體結(jié)構(gòu)如下:
插件化架構(gòu)對微服務(wù)架構(gòu)設(shè)計幫助非常大,考慮到隔離性,插件可能是以獨立進程方式運行的,那么這些進程如果擴展到網(wǎng)絡(luò)上,分布在眾多的服務(wù)器上,這個就是微服務(wù)架構(gòu)的原型,所以了解微內(nèi)核的同學(xué)都不屑于和你討論微服務(wù)架構(gòu),相信你也明白了,除了IT傳統(tǒng)的鄙視鏈因素,原理上確實就是這么回事。
回到微服務(wù)架構(gòu)設(shè)計場景,我們將Plug-in component重新命名為服務(wù)(Service),這個和微內(nèi)核設(shè)計中的服務(wù)也差不多,這個時候微服務(wù)和微內(nèi)核就差不多了,都涉及到服務(wù)注冊、管理和服務(wù)之間的通訊等。那我們看一下微內(nèi)核是如何解決服務(wù)之間的通訊問題的?以下摘自維基百科:
因為所有服務(wù)行程都各自在不同地址空間運行,因此在微核心架構(gòu)下,不能像宏內(nèi)核一樣直接進行函數(shù)調(diào)用。在微核心架構(gòu)下,要創(chuàng)建一個進程間通信機制,通過消息傳遞的機制來讓服務(wù)進程間相互交換消息,調(diào)用彼此的服務(wù),以及完成同步。采用主從式架構(gòu),使得它在分布式系統(tǒng)中有特別的優(yōu)勢,因為遠程系統(tǒng)與本地進程間,可以采用同一套進程間通信機制。
也就是說,采取的是基于消息的進程間通訊機制。消息最簡單,就兩個接口:send和receive,消息發(fā)送出去,然后等著收消息,處理后再發(fā)消息就可以了,這里大家應(yīng)該也知道了,這個是異步的?;氐讲寮軜?gòu)設(shè)計中,Plug-in組件設(shè)計包含交互規(guī)范,也就是和外界相互通訊的接口,如果是基于消息通訊的話,就是send和receive接口,可以說是非常簡單的。
但是這里還有一個問題,那就是進程間通訊。你可能會問,這個有什么好疑問的,就是兩個進程之間相互發(fā)消息唄。但是這里有一個最大的疑問,那就是進程間通訊是否有第三者介入?如下圖:
當然在操作系統(tǒng)的內(nèi)核設(shè)計中,一定是通過內(nèi)核進行轉(zhuǎn)發(fā)的,就是我們理解的總線架構(gòu),內(nèi)核負責(zé)協(xié)調(diào)各個進程間的通訊。這個大家也能理解,如果進程A直接發(fā)給另外一個進程B,必然要了解對應(yīng)的內(nèi)存地址,微內(nèi)核中的服務(wù)是可以被隨時替換的,如果服務(wù)不可用或者被替換,這個時候要通知和其通訊的其他進程,是不是太復(fù)雜?剛才已經(jīng)提到,只有send和receive接口,沒有其他通知下線、服務(wù)不可用的接口。在微內(nèi)核的設(shè)計中,一定是通過總線結(jié)構(gòu),進程向Kernel發(fā)送消息,然后kernel再發(fā)送給對應(yīng)的進程,這樣的一個總線設(shè)計。實際上很多應(yīng)用內(nèi)部在做Plug-in組件解耦時,都會使用EventBus的結(jié)構(gòu),其實就是總線的設(shè)計機制。
為何婆婆媽媽說這些?因為非常關(guān)鍵。分布式的進程通訊是微服務(wù)的核心,我們理解的服務(wù)到服務(wù)的通訊,就是服務(wù)A啟動監(jiān)聽端口,服務(wù)B會和服務(wù)A建立連接,然后兩者通訊即可。這個方式和微內(nèi)核設(shè)計中內(nèi)核負責(zé)消息接收和轉(zhuǎn)發(fā)的總線架構(gòu)設(shè)計是不一樣的。如采用HTTP,HSF等通訊協(xié)議時,相當于kernel告知通訊的雙方各自的地址,然后它們之間就可以通訊了。然后就沒有Kernel什么事情了,也不會用到什么總線的結(jié)構(gòu)設(shè)計,這個就是傳統(tǒng)的服務(wù)發(fā)現(xiàn)機制。
但是還有一種模式,就是完全透明的插件化通訊機制,如下圖:
Plug-in組件,也就是微服務(wù)架構(gòu)中的服務(wù),是不能直接通訊的,而是需要Core System進行轉(zhuǎn)發(fā)。這樣做的好處和微內(nèi)核架構(gòu)一樣,插件相互之間無直接聯(lián)系,彼此之間非常透明,例如服務(wù)A下線后,完全不需要通知其他服務(wù);服務(wù)A被替換,也不需要通知其他服務(wù);服務(wù)A從數(shù)據(jù)中心1到數(shù)據(jù)中心2,也不用通知其他服務(wù);即便服務(wù)N和服務(wù)A之間網(wǎng)絡(luò)不互通,兩者之間也能通訊。
這里有個問題:性能問題。我們都知道,兩點之間,直線段最短。為何要多繞一下到Core System呢?這就是微內(nèi)核和宏內(nèi)核之間的爭論之處,使用函數(shù)調(diào)用非???,而進程間的消息通訊則是非常慢的,但是這種通過中介進行通訊機制的好處也是非常明顯的。那么如何提升這種基于總線的通訊性能呢?當然有,比如選擇高性能的二進制協(xié)議,HTTP 1.1這種文本協(xié)議就不需要了;采用Zero Copy機制,可以快速進行網(wǎng)絡(luò)包轉(zhuǎn)發(fā);好的網(wǎng)絡(luò)硬件,如RDMA;好的協(xié)議,如基于UDP的QUIC等??偨Y(jié)下來,和微內(nèi)核一樣,這種微服務(wù)通訊的性能是可以提升的。當然如果實在受不了這種性能,在關(guān)鍵場景,你可以采用Hybrid模式,混入一些服務(wù)之間直接通訊的設(shè)計,但只能在性能極致的場景中使用。
此外,插件化架構(gòu)中的插件組件是各種各樣的,通訊的機制也各不一樣,一些是RPC的,一些是Pub/Sub的,一些是無需ACK的(如Beacon接口),還有一些是雙向通訊的等等。當然你可以選擇不同的通訊協(xié)議,但是這里有一個問題,就是Core System需要理解這個協(xié)議,然后才能進行消息路由。這個時候Core System需要編寫大量的Adapter來解析這些協(xié)議,例如Envoy包含各種filter來支持不同的協(xié)議,如HTTP、MySQL、ZooKeeper等,但是因此Core System就會變得非常復(fù)雜且不穩(wěn)定。
另外可以選一種通用的協(xié)議,Core System只支持這一種協(xié)議,各個插件之間都基于該協(xié)議通訊,至于服務(wù)和其他外部系統(tǒng)如何通訊,如數(shù)據(jù)庫、github集成等,這些Core System并不關(guān)心,那只是Service內(nèi)部的事情。目前比較通用的協(xié)議是gRPC,如K8s內(nèi)部都會采用該協(xié)議,另外Dapr也采用gRPC協(xié)議做服務(wù)集成,因為gRPC提供的通訊模型基本可以滿足大多數(shù)的通訊場景。當然另外一個就是RSocket,提供更豐富的通訊模型,也適用于Core System這種服務(wù)間通訊場景。對比gRPC,RSocket可以運行在各種傳輸層上,如TCP、UDP、WebSocket、RDMA等,相反的,gRPC目前只能運行在HTTP 2之上。
三、 服務(wù)通訊的延伸
前面說到,最好由插件化架構(gòu)設(shè)計的Core System作為服務(wù)之間消息通訊的路由,如果是這樣的話,就會產(chǎn)生一種Broker模式,當然也有可能是Agent。這里大家一定會想到Service Mesh,沒錯。當然你可以選擇Agent Sidecar模式,也可以選擇中心化的Broker模式,這兩者的功能都是一樣的,只是處理的方式不一樣而已。Agent基于服務(wù)注冊和發(fā)現(xiàn)機制,然后找到對方服務(wù)的Agent,再進行兩個Agent之間的通訊,只是省掉服務(wù)之間的調(diào)用的開銷。但是Broker是集中式的,大家都向Broker發(fā)送和接收消息,不涉及服務(wù)注冊發(fā)現(xiàn)機制,不涉及服務(wù)元信息推送,就是總線結(jié)構(gòu)。
我現(xiàn)在做的就是基于這種Broker的總線的架構(gòu)設(shè)計,在RSocket Broker中,也是采用微內(nèi)核架構(gòu)設(shè)計,當然未必做得最好 。RSocket Broker核心就是管理注冊的服務(wù)、路由管理、數(shù)據(jù)采集等,而不會添加過多的功能,和Core System的設(shè)計理念一樣,只添加必須的功能。如果你要擴展整個系統(tǒng)更多的功能,如發(fā)短信、發(fā)郵件、對接云存儲服務(wù)等,需要編寫一個Service ,然后和Broker對接一下,再從broker那里收消息(receive),處理完畢后再發(fā)送(send)給Broker就可以了??傮w結(jié)構(gòu)如下:
有不少同學(xué)會問,當服務(wù)實例的負載太高的時候,Broker如何實現(xiàn)動態(tài)擴容呢?Broker會給你提供數(shù)據(jù),如一個服務(wù)實例QPS,至于是否擴展,你只需要寫一個服務(wù),從Broker上采集數(shù)據(jù),分析后,調(diào)用K8s API進行擴容即可,Broker并不負載這些業(yè)務(wù)功能,它只會添加非常必要的功能,這個和Core System設(shè)計是一樣的。
回到插件化架構(gòu)的靈活性上,如果系統(tǒng)中有一個KV存儲的插件,你只要遵循消息格式或者通訊接口,就可以保存KV數(shù)據(jù)。但是你并不太關(guān)心是redis存儲的,還是Tair存儲的,或者是云端的KV服務(wù),這就為服務(wù)標準化和可替換性提供了很好的基礎(chǔ),這對應(yīng)用上云或云原生化幫助非常大,整個系統(tǒng)有非常大的靈活性。
四 、總結(jié)
其實有非常多的書有關(guān)于微內(nèi)核的介紹,操作系統(tǒng)的圖書就不用說了,另外兩本書也非常不錯,對通用架構(gòu)設(shè)計幫助也非常大,尤其是微服務(wù)的場景,我也是參考這兩本書寫這篇文章的。
微內(nèi)核架構(gòu)設(shè)計對微服務(wù)設(shè)計有非常好的參考意義,但是微服務(wù)有一個非常大的問題就是服務(wù)邊界的劃分,對比操作系統(tǒng),已經(jīng)發(fā)展幾十年,而且非常穩(wěn)定,功能劃分非常容易。而微服務(wù)架構(gòu)是為業(yè)務(wù)服務(wù)的,雖然面對的業(yè)務(wù)可能已經(jīng)存在上百年,但是軟件化、數(shù)字化和流程化并沒有多少年,加上現(xiàn)實業(yè)務(wù)的復(fù)雜性,還有各種妥協(xié),個人認為微服務(wù)架構(gòu)會更復(fù)雜一些。
作者:開發(fā)者小助手_LS
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載