1. 程式人生 > >Android系統架構之微服務架構

Android系統架構之微服務架構

目錄

前段時間我們翻譯的《軟體架構模式》( 完整書籍的地址 ) 對外發布之後得到了大家的一致好評,書中講述了五種經典、流行的軟體架構模式,同時分析了五種模式的實現、優缺點等,為我們的開發工作提供了很有價值的指導。但是《軟體架構模式》的問題在於沒有結合具體的示例來讓這些理論知識更易於吸收,因此有些同學在我的開發群反饋: 書看起來是挺好的,但是沒有具體的示例感覺看得迷迷糊糊的。因此在下打算寫一些結合Android原始碼或者開發的文章來更深入的講述這些架構模式,理論與實踐相結合,讓大家更深刻、更具體的學習到這些架構的魅力所在。

一、微服務架構模式

由於微服務架構模式的高度靈活性、伸縮性等因特性,近年來在業內發展迅猛。但由於這個架構模式仍然在不斷的發展中,業內人士對這個模式也存在很多困惑,例如這個模式是關於什麼的?它是如何實現的?本文首先為講述這個模式的關鍵概念、基礎知識以及這個架構模式的優缺點,因為只有在對它有深入的瞭解之後你才能根據實際情況來判斷你的應用是否適合這種架構。

1.1 模式描述

不管你選擇哪種實現,有幾個常見的核心概念都需要進行了解。第一個概念是獨立部署單元。如圖4-1所示,微服務架構的每個元件都作為一個獨立單元進行部署,讓每個單元可以通過有效、簡化的傳輸管道進行通訊,同時它還有很強的擴充套件性,應用和元件之間高度解耦,使得部署更為簡單。

也許要理解這種模式,最重要的概念就是服務元件。不要考慮微服務架構內部的服務,最好是考慮服務元件,從粒度上講它可以小到單一的模組,或者大至一個應用程式。服務元件包含一個或多個模組(如Java類),這些模組可以提供一個單一功能,例如為特定的城市或城鎮提供天氣情況,或也可以作為一個大型商業應用的一個獨立部分,例如火車票的餘票查詢系統。在微服務架構中,正確設計服務元件的粒度也是一個很大的挑戰。在接下來的服務元件部分對這一挑戰進行了詳細的討論。

4-1
微服務架構模式的另一個關鍵概念是它可能是一個分散式的架構,這意味著架構內部的所有元件之間是完全解耦的,並通過某種遠端訪問協議(例如, JMS, AMQP, REST, SOAP, RMI等)進行訪問。這種架構的分散式特性是它實現一些優越的可擴充套件性和部署特性的關鍵所在。

微服務架構另一個令人興奮的特性是它是由其他常見架構模式存在的問題演化來的,而不是作為一個解決方案被創造出來等待問題出現。微服務架構的演化有兩個主要來源:使用分層架構模式的單體應用和使用面向服務架構的分散式應用。

提示 : 單體應用, 即一個應用就是一個整體。

從單體應用到微服務的發展過程主要是由持續交付開發促成的。單體應用通常是由緊耦合的元件組成,這些元件同時又是另一個單一可部署單元的一部分,這使得它繁瑣,難以改變、測試和部署應用。這些因素通常會導致應用變得脆弱,以至於每次有一點新功能部署後,如果由於這些新功能引發了異常,那麼整個應用就不能執行。微服務架構模式通過將應用分隔成多個可部署的單元(服務元件)的方法來解決這一問題,這些服務元件可以獨立於其他服務元件進行單獨開發、測試和部署。

另一個導致微服務架構模式產生的演化過程是由面向服務架構模式(SOA)應用程式存在的問題引起的。雖然SOA模式非常強大,提供了無與倫比的抽象級別、異構連線、服務排程,並保證通過IT能力調整業務目標,但它仍然是複雜的、昂貴的,它很難理解和實現,對大多數應用程式來說它過於重量級。微服務架構通過簡化服務概念,消除排程需求、簡化服務元件連線和訪問來解決複雜度問題。

1.2 模式拓撲

雖然有很多方法來實現微服務架構模式,但三個主要的拓撲結構脫穎而出,最常見和流行的有:基於REST API的拓撲結構,基於REST的應用拓撲結構和集中式訊息拓撲結構。

  • 基於REST的API拓撲
    基於REST的API拓撲適用於網站,通過某些API對外提供小型的、自包含的服務。這種拓撲結構,如圖4 - 2所示,由粒度非常細的服務元件(因此得名微服務)組成,這些服務元件包含一個或兩個模組並獨立於其他服務來執行特定業務功能。在這種拓結構撲中,這些細粒度的服務元件通常被REST-based的介面訪問,而這個介面是通過一個單獨部署的web API層實現的。這種拓撲的例子包含一些常見的專用的、基於雲的RESTful web service,大型網站像Yahoo, Google, and Amazon都在使用。

4-2
圖 4-2

  • 基於REST的應用拓撲結構
    基於REST的應用拓撲結構與基於REST API有所不同,它通過傳統的基於web的應用或者客戶端應用來接收客戶端請求,而不是通過一個簡單的API層。如圖4-3所示,應用的UI層是一個web應用,可以通過簡單的基於REST的介面訪問單獨部署的服務元件。該拓撲結構中的服務元件與基於REST API拓撲結構中的不同,這些服務元件往往會更大、粒度更粗、代表整個業務應用程式的一小部分,而不是細粒度的、單一操作的服務。這種拓撲結構常見於中小型企業等復程度相對較低的應用程式。

4-3
圖 4-3

  • 集中式訊息拓撲
    微服務架構模式中另一個常見的方法是集中式訊息拓撲,如圖4-4所示。該拓撲與前面提到的基於REST的應用拓撲類似,不同的是基於REST的應用拓撲結構使用REST進行遠端訪問,而該拓撲結構則使用一個輕量級的集中式訊息中介軟體(如,ActiveMQ, HornetQ等等)。不要將該拓撲與面向服務的架構模式混淆或將其當做SOA簡化版,這點是極其重要的。該拓撲中的輕量級訊息中介軟體(Lightweight Message Broker)不執行任何排程,轉換,或複雜的路由;相反,它只是一個輕量級訪問遠端服務元件的傳輸工具。
    集中式訊息拓撲結構通常應用在較大的業務應用程式中,或對於某些對傳輸層到使用者介面層或者到服務元件層有較複雜的控制邏輯的應用程式中。該拓撲較之先前討論的簡單基於REST的拓撲結構,其好處是有先進的排隊機制、非同步訊息傳遞、監控、錯誤處理和更好的負載均衡和可擴充套件性。與集中式代理相關的單點故障和架構瓶頸問題已通過代理叢集和代理聯盟(將一個代理例項為分多個代理例項,把基於系統功能區域的吞吐量負載劃分開處理)解決。

4-4
圖 4-4

1.3 避免依賴和排程

微服務架構模式的主要挑戰之一就是決定服務元件的粒度級別。如果服務元件粒度過粗,那你可能不會意識到這個架構模式帶來的好處(部署、可擴充套件性、可測試性和鬆耦合)。然而,服務元件粒度過細將導致額外的服務排程,這可能會導致將微服務架構模式變成一個複雜、容易混淆、代價昂貴並易於出錯的、重量級的面向服務架構。

如果你發現需要從應用內部的使用者介面或API層排程服務元件,那麼很有可能你服務元件的粒度太細了。同樣的,如果你發現你需要在服務元件之間執行服務間通訊來處理單個請求,要麼是你服務元件的粒度太細了,要麼是沒有從業務功能角度正確劃分服務元件。

服務間通訊,可能導致元件之間產生耦合,但可以通過共享資料庫進行處理。例如,若一個服務元件處理網路訂單而需要使用者資訊時,它可以去資料庫檢索必要的資料,而不是呼叫客戶服務元件的功能。

共享資料庫可以處理資訊需求,但是共享功能呢?如果一個服務元件需要的功能包含在另一個服務元件內,或是一個公共的功能,那麼有時你可以將服務元件的共享功能複製一份,因此違反了DRY規則。為了保持服務元件獨立和部署分離,微服務架構模式實現中會存在一小部分由重複的業務邏輯而造成的冗餘,這在大多數業務應用程式中是一個相當常見的問題。小工具類可能屬於這一類重複的程式碼。

提示 : DRY,即don’t repeat yourself.

如果你發現就算不考慮服務元件粒度的級別,你仍不能避免服務元件排程,這是一個好跡象,可能此架構模式不適用於你的應用。由於這種模式的分散式特性,很難維護服務元件之間的單一工作事務單元。這種做法需要某種事務補償框架回滾事務,這對此相對簡單而優雅的架構模式來說,顯著增加了複雜性。

1.4 注意事項

微服務架構模式解決了很多單體應用和麵向服務架構應用存在的問題。由於主要應用元件被分成更小的、單獨部署單元,使用微服務架構模式構建的應用程式通常更健壯,並提供更好的可擴充套件性,支援持續交付也更容易。

該模式的另一個優點是,它提供了實時生產部署能力,從而大大減少了傳統的月度或週末“大爆炸”生產部署的需求。因為變化通常被隔離成特定的服務元件,只有變化的服務元件才需要部署。如果你的服務元件只有一個例項,你可以在使用者介面程式編寫專門的程式碼用於檢測一個活躍的熱部署,一旦檢測到就將使用者重定向到一個錯誤頁面或等待頁面。你也可以在實時部署期間,將服務元件的多個例項進行交換,允許應用程式在部署期間保持持續可用性(分層架構模式很難做到這點)。

最後一個要重視的考慮是,由於微服務架構模式可能是分散式的架構,他與事件驅動架構模式具有一些共同的複雜的問題,包括約定的建立、維護,和管理、遠端系統的可用性、遠端訪問身份驗證和授權等。

1.5 模式分析

下面這個表中包含了微服務架構模式的特點分析和評級,每個特性的評級是基於其自身特點,基於典型模式實現的能力特性,以及該模式是以什麼聞名的。

特性 評級 分析
整體靈活性 整體的靈活性是能夠快速響應不斷變化的環境。由於服務是獨立部署單元,因此變化通常被隔離成單獨的服務元件,使得部署變得快捷、簡單。同時,使用這種模式構建的應用往往是鬆耦合的,也有助於促進改變。
易於部署 每個服務元件都是一個獨立的部署單元,使得每個部署單元都相對比較簡單,也降低了部署的複雜度。易於部署成為了微服務架構的一大優勢。
可測試性 由於業務功能被分離成獨立的應用模組,可以在區域性範圍內進行測試,這樣測試工作就更有針對性。對一個特定的服務元件進行迴歸測試比對整個單體應用程式進行迴歸測試更簡單、更可行。而且,由於這種模式的服務元件是鬆散耦合的,從開發角度來看,由一個變化導致應用其他部分也跟著變化的機率很小,也不會出現由於一個微小的變化而對整個應用程式進行測試的蛋疼情況。
效能 較低 雖然你該模式有很多的優勢,但由於微服務架構模式的分散式或者跨程序特性,客戶程式與服務之間的通訊會降低效率,因此它並不適用於高效能的應用程式。
伸縮性 由於應用程式被分為單獨的部署單元,每個服務元件可以單獨擴充套件,並允許對應用程式進行擴充套件調整。例如,股票交易的管理員功能模組可能不需要擴充套件,因為使用該功能的使用者比較有限。但是交易資料請求服務元件可能需要擴充套件,因為這裡每時每刻可能都有無數的人在請求,因此需要較高的擴充套件性。
易於開發 由於功能被分隔成不同的服務元件,由於開發範圍更小且被隔離,開發變得更簡單。程式設計師在一個服務元件做出一個變化影響其他服務元件的機率是很小的,從而減少開發人員或開發團隊之間的協調。

Android中的微服務架構

在Android領域中,我們最常看到的就是如圖4-5的Android體系架構。

image-4-5
圖4-5

這是一個典型的分層架構,分為應用層、Framework層、Native層、核心層。這似乎與我們今天要說的微服務架構沒有任何關係!大家需要注意的是這是一個更為巨集觀的架構,在這個分層架構之下還有其他的架構模式,微服務架構就是其中最為明顯的一個。Android系統按照職責分為不同的層次,但是在Java層( Java應用程式和應用程式框架)與系統服務層( Android執行環境 )這兩個層之間則是通過本地C/S模式進行通訊,也就是我們的微服務架構。

我們知道在Android系統啟動時,大致會執行如下四步:

  1. init程序啟動;;
  2. System Server啟動;
  3. Android系統服務啟動,將服務註冊到ServiceManager中;
  4. Android執行環境建立,並且啟動Launcher程式。

在init程序啟動後,會呼叫init_parse_config_file方法解析init.rc檔案,然後將init.rc中指定的命令、服務等啟動起來。

int main(int argc, char **argv)
{
    // 程式碼省略

    // 建立系統資料夾
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);
    // 程式碼省略

    // 初始化核心
    open_devnull_stdio();
    klog_init();
    property_init();
    process_kernel_cmdline();
    // 程式碼省略

    // 解析init.rc檔案
    init_parse_config_file("/init.rc");

    // 程式碼省略
    return 0;
}

init.rc是由一種被稱為“Android初始化語言”的指令碼寫成的檔案。在該檔案中描述了一些動作、命令、服務、選項等,我們這裡只關心服務這一項。init.rc中的一個服務描述大致是這樣的。

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

在上述程式碼中指定了一個zygote服務,這個服務會啟動一個叫做zygote的程序,zygote是Android世界的萬物之源,所以的程序都有它孵化。在啟動zygote時又會啟動System Server程序,System Server是所有系統服務的棲息地,也是應用與Zygote程序通訊的中樞,例如需要啟動某個應用時會通過System Server通知zygote fork一個新的程序。在System Server啟動之後會呼叫com_ android_ server_ SystemServer. cpp類中的android_server_SystemServer_nativeInit函式,在該函式中會獲取ServiceManager例項以及啟動一些Native服務。最後會呼叫SystemServer內部類ServerThread的initAndLoop函式將WindowManagerService、ActivityManagerService等系統服務註冊到ServiceManager中,這些服務為系統提供各種各樣的功能,最後啟動系統訊息迴圈,此時Android的執行環境基本構建起來了。

public class SystemServer {
// 主函式
public static void main(String[] args) {
       // 程式碼省略

        // Initialize native services.
        nativeInit();

        // This used to be its own separate thread, but now it is
        // just the loop we run on the main thread.
        ServerThread thr = new ServerThread();
        thr.initAndLoop();
    }
}

// 內部類
class ServerThread {

    public void initAndLoop() {

        // 1、啟動主執行緒訊息迴圈
        Looper.prepareMainLooper();
        // 程式碼省略
        try {
            // 2、將各個系統服務註冊到ServiceManager中
            // 新增PackageManagerService
            pm = PackageManagerService.main(context, installer,
            wm = WindowManagerService.main(context, power, display, inputManager,
                    wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
                    !firstBoot, onlyCore);
            // 新增 WindowManagerService
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);

            // 數十種服務的註冊,程式碼省略
        } catch (RuntimeException e) {
        }
        // 3、啟動訊息迴圈
        Looper.loop();
    }
} // end of ServiceThread

} // end of SystemServer

Framework層(客戶端)與Native系統服務(服務端)之間並不是直接呼叫的,而是通過Binder機制,客戶端程式碼通過Java端的服務代理與Bidner機制向Native服務發起請求,具體的工作交給Native系統服務來實現。因此Framework和Native層的架構是如圖 4-6 所示。

4-6

這種架構就是類似上文所說的基於API的微服務架構,Native層系統服務完成具體的功能,Java層提供訪問Native系統服務的介面,它們之間通過Binder機制進行通訊,Java層與Native層就是一個本地的C/S架構。如果以一個有網路請求的App做比喻的話,App客戶端就扮演了Java層的角色,Native層系統服務對應的是服務端,而通訊機制則由http變成了Binder。Native層的系統服務多達數十種,每個系統服務的職責明確、單一,具有良好的內聚性。例如WindowManagerService只負責管理螢幕視窗相關的操作。通過這種微服務架構,使得Java層與Native層耦合性很低、伸縮性很高,也對Java層隱藏了複雜的實現,使得整個系統更為清晰、靈活。

總結

微服務架構以其優秀的靈活性、伸縮性在各個架構模式中脫穎而出,但是它的一個弱點是效能相對較低。因為客戶端與服務提供端需要進行IPC甚至是網路請求,這就通訊成本較高。在Android中客戶端和服務端都在本地,因此需要IPC機制。Android並沒有選擇像Socket、管道等傳統的IPC機制,而是選擇了更為靈活、簡潔和快速、低記憶體消耗的Binder,這也在很大程度上提升了微服務架構在Android系統中效能以及開銷。因此,如果在普通應用中運用這種架構時,首先需要考慮的就是效能的開銷,靈活度、記憶體帶來的益處是否大於效能降低帶來的弊端,這些就需要大家以自身情況而定了。