<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    我對軟件分層設(shè)計的思考

    共 6263字,需瀏覽 13分鐘

     ·

    2021-08-06 01:23

    不點藍字,我們哪來故事?

    每天 11 點更新文章,餓了點外賣,點擊 ??《無門檻外賣優(yōu)惠券,每天免費領(lǐng)!》

    • 1. 什么是分層設(shè)計?它有何好處?
    • 2. 計算機語言的發(fā)展
    • 3. Linux 內(nèi)核
    • 4. TCP/IP 網(wǎng)絡(luò)協(xié)議堆棧
    • 5. Netty
    • 6. 微服務(wù)分層
    • 7. Rails On Rack
    • 8. 總結(jié)

    在日常開發(fā)中,經(jīng)常聽到大家說一句話“任何需求都可以通過一個間接的的中間層來解決”。今天,通過幾個 case 就“分層”話題梳理下自己的思考,其中,有些 case 比較直觀,而有些不那么直觀,甚至有些微妙,需要我們自己多品味。這意味著學(xué)習(xí)過程需要我們不斷將新知識與舊知識進行關(guān)聯(lián),形成自己的知識體系,而非一個個知識孤島。

    1. 什么是分層設(shè)計?它有何好處?

    圖片

    分層設(shè)計將軟件劃分成若干層,每一層只解決一部分問題,通過所有層的協(xié)作來完成整體目標(biāo)。一個復(fù)雜問題通過分解成一個個系統(tǒng)子問題,這樣就有效的降低了每個子問題的規(guī)模與復(fù)雜度。

    分層設(shè)計帶來的好處:

    • 降低了系統(tǒng)軟件的復(fù)雜度,將一個復(fù)雜問題通過分解,分而治之
    • 功能的復(fù)用和封裝

    2. 計算機語言的發(fā)展

    圖片

    機器語言

    早期,軟件開發(fā)是機器語言,直接用二進制 0 和 1 表示機器可以識別的指令和數(shù)據(jù),看起來像這樣:

    0010000100100011

    這就是計算機 CPU 唯一可以理解的語言。對人類為說,二進制的程序是不可讀的。

    匯編語言

    為了解決語言可讀性的問題,匯編程序誕生了。匯編程序是人類可讀的機器代碼。它又被稱為“符號語言”,使用助記符來代替機器的操作碼。

    匯編語言是二進制的文本形式,與 CPU 的指令是一一對應(yīng)的關(guān)系。而我們不同的 CPU 體系結(jié)構(gòu)(比如 PC 的 X86、嵌入式的 ARM) 是不同的,面向機器的語言帶來的問題就是:對于不同的 CPU 體系架構(gòu),就需要不同的匯編語言。

    高級語言

    為了解決語言對機器的無關(guān)性,高級語言誕生了。一條高級語言通常由若干條機器語言實現(xiàn)的,并且不具有對應(yīng)性。

    高級語言讓開發(fā)者不需要關(guān)注底層 CPU 體系結(jié)構(gòu)與指令,只關(guān)注業(yè)務(wù)即可。

    計算機語言的發(fā)展就是不斷的抽象,只有通過抽象,將一個復(fù)雜的的系統(tǒng)變成一層層的接口集合,讓我們每次只需要考慮關(guān)注當(dāng)前層集合內(nèi)的邏輯,而不用去考慮當(dāng)前層次以上或者以下的復(fù)雜度,才有可能讓我們從復(fù)雜系統(tǒng)中解放出來,逐步理解以及構(gòu)造一個復(fù)雜系統(tǒng)。

    3. Linux 內(nèi)核

    內(nèi)核功能層與內(nèi)核硬件層

    圖片

    操作系統(tǒng)內(nèi)核,可以簡化理解成三大層:

    • 內(nèi)核接口層 :向上對用戶態(tài)應(yīng)用程序提供一套接口子集,開發(fā)者使用的系統(tǒng)調(diào)用 APIs。
    • 內(nèi)核功能層 :這一層完成各種實際的功能,我們知道 OS 主要負責(zé)資源管理、內(nèi)存、進程這些資源,物理內(nèi)存如何申請、釋放,進程如何調(diào)度。具體來說進程管理、內(nèi)存管理、中斷管理、設(shè)備管理。
    • 內(nèi)核硬件層 :分離硬件的相關(guān)性,我們知道一個 OS 可以運行不同的指令集,也就是運行在不同的硬件平臺。

    不管是 ARM 體系結(jié)構(gòu),還是 X86,選擇一個進程調(diào)度的算法是可以相同的,需要改變的進程切換相關(guān)代碼,因為不同的硬件平臺的上下文是不同的,CPU 的寄存器也不同。這時候最好的設(shè)計是分層,當(dāng)操作系統(tǒng)運行在不同的硬件平臺時,就只需要修改硬件平臺相關(guān)層代碼,實現(xiàn)操作系統(tǒng)的高可移植性。

    操作系統(tǒng)有兩個關(guān)鍵設(shè)計:

    • 內(nèi)核接口層區(qū)分用戶態(tài)與內(nèi)核態(tài),來保護硬件資源受限訪問。
    • 內(nèi)核硬件層分離多種硬件平臺相關(guān)性。這種分層的架構(gòu),極大提升了系統(tǒng)的穩(wěn)定性和擴展性。

    MMU 抽象層

    操作系統(tǒng)負責(zé)管理物理內(nèi)存,而用戶進程使用虛擬內(nèi)存。操作系統(tǒng)呈現(xiàn)給用戶進程的是連續(xù)的虛擬空間,但不一定是連續(xù)的物理空間。因為物理內(nèi)存被整個 OS 共享。

    什么是 MMU 呢?它是硬件,即內(nèi)存管理單元,它對 CPU 發(fā)出的訪存地址進行映射與檢查,可以讓處理器發(fā)出的訪存地址訪問不同的物理內(nèi)存單元。

    如果將計算機上有限的物理內(nèi)存分配給多個應(yīng)用程序使用,如果讓應(yīng)用程序直接訪問物理內(nèi)存,如果沒有 MMU 這層抽象呢?帶來的問題是每個應(yīng)用程序地址空間不隔離,內(nèi)存使用率低,程序運行地址也無法固定。

    圖片

    解決的問題:虛擬內(nèi)存 VA 與物理內(nèi)存 PA 的映射——通過在 CPU 與內(nèi)存之間加入 MMU 抽象層,讓 CPU 在運行指令時發(fā)出的 VA 虛擬地址通過 MMU 轉(zhuǎn)換后變成 PA 物理地址,然后再去訪問物理內(nèi)存。

    圖片

    MMU 引入帶來的好處:

    • 權(quán)限控制。可以對一些虛擬地址進行訪問控制,比較代碼段為只讀,用戶程序代可寫。
    • 提升內(nèi)存使用率:物理內(nèi)存按需申請。fork 子進程的對應(yīng)的物理空間是能過寫時復(fù)制才進行真正的物理內(nèi)存分配。
    • 不同進程之間可以使用相同的虛擬內(nèi)存地址空間,而進程的物理內(nèi)存又可以隔離。
    • 系統(tǒng)運行多個進程,所分配的內(nèi)存之和可以大于實際物理內(nèi)存大小。

    這是我認為最經(jīng)典、最本質(zhì)、最受啟發(fā)的中間抽象層的設(shè)計。

    CPU 與外設(shè)的通信

    CPU 訪問外設(shè)有兩種方法;

    • IO 與內(nèi)存統(tǒng)一編址
    • IO 與內(nèi)存的獨立編址
    圖片

    外設(shè)接口中的 IO 寄存器(即 IO 端口)與主存單元一樣看待,每個端口占用一個存儲單元的地址,將主存的一部分劃分出來用作 IO 的地址空間。

    把外設(shè)的寄存器當(dāng)做是一個內(nèi)存地址,從而 CPU 以類似訪問內(nèi)存相同的方式來操作外設(shè)。

    對 IO 外設(shè)的端口映射到一個物理內(nèi)存單元地址,在 CPU 與外設(shè)之間的“內(nèi)存”抽象層,帶來好處是訪問內(nèi)存一樣去訪問外設(shè)。

    小結(jié)

    Linux 中的內(nèi)核硬件層設(shè)計、MMU、CPU 與 IO 外設(shè)通信設(shè)計處處體現(xiàn)了分層 / 中間層的設(shè)計思想。

    4. TCP/IP 網(wǎng)絡(luò)協(xié)議堆棧

    從最底層的物理鏈路層層層向上封裝抽象,解決了復(fù)雜的網(wǎng)絡(luò)通信的問題。同樣的,任何復(fù)雜的問題,通過分層最終總能夠回歸最本質(zhì)、最簡單。這個分層架構(gòu),對所有開發(fā)者而言,再熟悉不過,它的引入是想與后續(xù)介紹的 Netty 形成對比。這里先賣個關(guān)子,后面解開謎底。

    圖片

    舉例說明::

    來自杭州西湖區(qū)某個小區(qū)的商務(wù)人士來京出差后,被確診新冠肺炎,實施在京隔離措施,同時北京將此報告先發(fā)給浙江省,接著浙江省發(fā)給杭州市政府,然后市政府再向西湖區(qū)發(fā)送,最后到達某小區(qū)。這個發(fā)送報告過程也是分層報告思想。

    DNS 中間層

    圖片

    DNS (domain name system) 是域名系統(tǒng),是用來將主機轉(zhuǎn)換為 IP 地址的服務(wù)。我們有至少三種方式在互聯(lián)網(wǎng)上標(biāo)識一臺主機、主機名、IP 地址以及 MAC 地址。為什么有引入 DNS 中間抽象層呢? 主要是主機名便于記憶,而 IP 地址方便于在計算機網(wǎng)絡(luò)設(shè)備的處理,因此需要設(shè)計出一個 DNS 協(xié)議 (中間層) 來做主機名到 IP 地址的轉(zhuǎn)換。

    ARP 中間層

    圖片

    ARP(address resolution protocol) 是地址解析協(xié)議,它根據(jù) IP 地址來獲取物理地址。上面也談到,MAC 與 IP 都可以用來標(biāo)識一臺主機。那二者區(qū)別是什么?

    同一個局域網(wǎng)中的一臺主機和另一臺主機通信的時候,需要通過 MAC 地址進行定位,之后才能進行數(shù)據(jù)包的傳送。

    而在網(wǎng)絡(luò)層和傳輸層中,主機之間是通過 IP 地址來定位的,對應(yīng)的數(shù)據(jù)包中必須攜帶目標(biāo)主機的 IP 地址, 而沒有 MAC 地址。

    因此,ARP 協(xié)議 (中間層) 用來實現(xiàn)從 IP 到 MAC 地址的轉(zhuǎn)換。

    5. Netty

    Netty 提供了異步的,基于事件驅(qū)動的網(wǎng)絡(luò)應(yīng)用程序框架。目前分布式搜索引擎,Spark 框架底層是擴展使用 Netty 框架。Netty 本身的架構(gòu)理解有些曲線,為了講清楚,我還是希望循序漸進方式,通過它的發(fā)展歷史來一步步介紹。先鋪墊再介紹,大家需要一些耐心。

    傳統(tǒng)阻塞 IO 服務(wù)模型

    圖片

    思路:

    • 采用阻塞 IO 模式獲取輸入數(shù)據(jù)
    • 每個連接都需要獨立的線程完成數(shù)據(jù)的輸入,業(yè)務(wù)的處理和數(shù)據(jù)返回

    問題:

    • 當(dāng)并發(fā)數(shù)很大時,就會創(chuàng)建大量的線程,占用了很大的系統(tǒng)資源。
    • 連接創(chuàng)建后,如果當(dāng)前線程沒有數(shù)據(jù)可讀,這個線程會阻塞在 read 方法上,造成資源浪費。

    單 Reactor 單線程

    圖片

    思路:

    • 通過引入 selector 事件選擇器來監(jiān)聽多路連接的請求。
    • Reactor 對象通過 selector 監(jiān)控客戶端請求事件后,通過 Dispatch 進行分發(fā)。
    • 如果建立連接請求事件,則由 Acceptor 負責(zé)建立一個連接,然后創(chuàng)建一個 Handler 對象處理連接完成后的業(yè)務(wù)處理。

    問題:

    • 模型簡單,沒有多線程,資源競爭的問題。所以工作在一個線程完成。
    • 性能問題,一個線程,無法發(fā)揮多核 CPU 的性能。
    • 可靠性問題,線程 crash,會導(dǎo)致整個系統(tǒng)不可用。

    主從 Reactor 多線程

    主 React 處理所有 socket 連接事件的監(jiān)聽和響應(yīng),而從 React 處理所有 socket 的讀寫事件的監(jiān)聽與響應(yīng)。主從 React 都在多線程中運行。

    圖片

    Netty 模型

    Netty 主要基于主從 Reactor 多線程模型發(fā)展出來的。

    圖片

    Netty 邏輯架構(gòu)

    前面 Netty 的發(fā)展階段都是鋪墊,Nettty 邏輯架構(gòu)為典型網(wǎng)絡(luò)分層架構(gòu)設(shè)計,從下到上分別為網(wǎng)絡(luò)通信層、事件調(diào)度層、服務(wù)編排層。

    圖片

    網(wǎng)絡(luò)通信層 :它執(zhí)行網(wǎng)絡(luò) I/O 操作,核心組件包含 BootStrap、ServerBootStrap、Channel?!狢hannel 通道,提供了基礎(chǔ)的 API 用于操作網(wǎng)絡(luò) IO,比如 bind、connect、read、write、flush 等等。它以 JDK NIO Channel 為基礎(chǔ),提供了更高層次的抽象,同時屏蔽了底層 Socket 的復(fù)雜性。Channel 有多種狀態(tài),比如連接建立、數(shù)據(jù)讀寫、連接斷開。隨著狀態(tài)的變化,Channel 處于不同的生命周期,背后綁定相應(yīng)的事件回調(diào)函數(shù)。

    事件調(diào)度層 :它的核心組件包含 EventLoopGroup、EventLoop?!狤ventLoop 本質(zhì)是一個線程池,主要負責(zé)接收 Socket I/O 請求,并分配事件循環(huán)器來處理連接生命周期中所發(fā)生的各種事件。

    服務(wù)編排層 :它的職責(zé)實現(xiàn)網(wǎng)絡(luò)事件的動態(tài)編排和有序傳播——ChannelPipeline 基于責(zé)任鏈模式,方便業(yè)務(wù)邏輯的攔截和擴展;本質(zhì)上它是一個雙向鏈表將不同的 ChannelHandler 鏈接在一塊,當(dāng) I/O 讀寫事件發(fā)生時, 會依次調(diào)用 ChannelHandler 對 Channel(Socket) 讀取的數(shù)據(jù)進行處理。

    ChannelPipeline 私有協(xié)議棧 vs. TCP/IP 協(xié)議棧

    圖片

    前面鋪墊這么久,就是為了自然過渡到上面的圖,請務(wù)必與 TCP/IP 協(xié)議棧進行對比。

    socket。read 經(jīng)過 TCP/IP 協(xié)議棧后,進入 netty 的網(wǎng)絡(luò)通信層,事件調(diào)度層,最后來到服務(wù)編排層。而服務(wù)編排層的 channelPipeline 的設(shè)計也是一個 upstream/downstream 的 stack,一進一出的二個 pipeline。負責(zé)處理流入 / 流出的數(shù)據(jù)包。

    上面的 stack 就非常類似 TCP/IP 協(xié)議棧。根據(jù)公司組織的需要可以定制分層的私有協(xié)議棧,比如從 authentication-handler、message-validation-handler、message-encode-handler、message-decoder-handler。

    6. 微服務(wù)分層

    圖片

    grpc-gateway ——它是一個開源框架, 讀取 protobuf 接口定義并生成一個反向代理服務(wù)器, 此服務(wù)器時一步將 restful http API 轉(zhuǎn)換成 grpc 服務(wù).

    middleware ——實現(xiàn)鑒權(quán)功能, 比如哪些 URL 需要權(quán)限檢驗

    handler 通用處理層 ——參數(shù)檢驗: handler 層負責(zé)執(zhí)行與客戶端約定參數(shù)的檢驗, 檢驗通過后再組裝成后端服務(wù)需要的數(shù)據(jù)結(jié)構(gòu)發(fā)往后端;接口聚合 / 組合服務(wù): handler 層可以根據(jù)業(yè)務(wù)需要, 調(diào)用多個后端服務(wù)的 endpoint 來組合實現(xiàn)一個新的接口, 同時將下層返回的數(shù)據(jù)進行聚合處理.

    service/model 業(yè)務(wù)邏輯層 ——對業(yè)務(wù)邏輯的封裝, 負責(zé)將多個 DAO 數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換和封裝成一個有邏輯意義的模型;可以引入緩存策略, 優(yōu)化數(shù)據(jù)存取效率.

    DAO 層 ——數(shù)據(jù)訪問層, 主要負責(zé)操作 DB 中某張表并映射到內(nèi)存中某個 DAO 模型;與數(shù)據(jù)表結(jié)構(gòu)一一對應(yīng), 通過 DAO 內(nèi)存模型向上層傳遞數(shù)據(jù)源的對象.

    數(shù)據(jù)訪問層 DAL ——對底層的數(shù)據(jù)源做統(tǒng)一的抽象, 屏蔽數(shù)據(jù)庫. 如果沒有 DAL 的存在, 那么向乎所有的業(yè)務(wù)邏輯層都會去與具體的數(shù)據(jù)庫存儲強挷定. 耦合性就很高.

    還有一個補充點:

    業(yè)務(wù)邏輯層中的服務(wù)在實際場景中不可避免的會出現(xiàn)互相調(diào)用的場景,這種情況往往需要將耦合 / 公共的功能進行下沉,比如數(shù)據(jù)請求下沉為數(shù)據(jù)訪問層服務(wù),而業(yè)務(wù)下沉為穩(wěn)定的通用業(yè)務(wù)服務(wù),被其它服務(wù)穩(wěn)定依賴。

    7. Rails On Rack

    熟悉 Ruby On Rails Web 應(yīng)用框架的開發(fā)者,肯定知道 Rack 是如何成為應(yīng)用容器 (webserver) 和應(yīng)用框架之間的橋梁的。

    圖片

    Rack 在 webserver 和應(yīng)用框架之間提供了一套最小的 API 接口,如果 webserver 都遵循 Rack 提供的這套規(guī)則,那么所有的框架都能通過協(xié)議任意地改變底層使用 webserver。

    圖片

    Rack 分層設(shè)計非常類似 Decorate Pattern 或者 Chain of Responsibility Pattern。

    8. 總結(jié)

    本文作者結(jié)合自身工作經(jīng)驗, 總結(jié)一些典型分層設(shè)計案例

    • 計算機語言的發(fā)展
    • Linux 內(nèi)核設(shè)計 (內(nèi)核功能層與內(nèi)核硬件層,MMU 抽象層,CPU 與外設(shè)的通信)
    • TCP/IP 網(wǎng)絡(luò)協(xié)議堆棧 (DNS 和 ARP 協(xié)議)
    • Netty 框架發(fā)展以及分層私有協(xié)議棧分析
    • 微服務(wù)分層
    • 應(yīng)用框架 Rails On Rack

    這些案例充分說明了計算機系統(tǒng)本身就是通過一層一層抽象構(gòu)造出來的。

    • 硬件方面是從一個個小的晶體管,抽象成一個個門電路,再到 CPU 器件,最后抽象組成計算機。
    • 軟件設(shè)計也是一個層次一個層次功能完善疊加的,無論是自頂向下還是自底向上。

    楊敏,F(xiàn)reewheel 首席工程師,負責(zé) SFX 團隊的整體工作。目前從事服務(wù)化框架、容器化平臺相關(guān)。關(guān)注與感興趣的技術(shù)主要有 Python/Java 虛擬機、Golang、K8s、分布式數(shù)據(jù)庫、分布式搜索引擎 ElasticSearch。

    - END -

    往期推薦

    Lombok!代碼簡潔神器還是代碼“亞健康”元兇?

    Spring為什么建議使用構(gòu)造器來注入?

    關(guān)于虛擬內(nèi)存,你需要了解的一些概念

    Facebook 分享 MySQL 5.6 到 8.0 的遷移經(jīng)驗

    下方二維碼關(guān)注我

    技術(shù)草根,堅持分享 編程,算法,架構(gòu)

    看完文章,餓了點外賣,點擊 ??《無門檻外賣優(yōu)惠券,每天免費領(lǐng)!》

    朋友,助攻一把!點個在看!
    瀏覽 37
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    蜜桃在线码无精品秘 入口九色 | 美女黄频免费 | 操逼视频看看 | www.日本 爽久久.cou | 精品乱子伦一区二区三区免费播放 |