Hero Image
[架構] 多層式架構(Multitier Architecture)

多層式架構 Multitier Architecture 或稱 N-Tier Architecture, 是 Client–server architecture 的一種, 多層架構的層可以是 layer 或 tier,這兩者之間主要的差別在於 layer 指程式邏輯在應用程式的位置; 而 tier 指 layer 在系統上實際部屬執行的位址,屬於物理層級的指涉。 這一篇的層指的是 tier。 N-tier model N-tier model,層與層之間的邊界有 N-1 個,而程式跨邊界會造成巨大的效能損失, 一說為光是跨越同一台機器上不同進程(process)邊界存取資源損失就大約1000倍, 如果透過網路進行遠端呼叫勢必損失更多, 因此每跨越一個邊界進行資源存取效能就會以幾何級數損失。 且增加邊界在軟體設計上會增加複雜度,簡單的應用程式使用多層式架構很容易造成過度設計(over design), 因此如何適當添加層級(tier)也是一門學問,添加層級時必需考量如何在應用程式所部屬的環境獲取最大的成本效益。 軟體是否採用多層式架構必須以多層式架構的優缺點進行取捨(尤其是可擴展性與效能之間)。 提高可擴展性(scalability)。 提高效能(performance)。 提高容錯率(fault tolerance)。 提高安全性(security)。 1-tier model 所有的 layer 都在同一機器、同一記憶體空間內運行,因此不需考慮網路造成的性能損失。 2-tier model layer 分配至兩個不同的記憶體空間運行,記憶體空間可能位於相同或兩台不同的機器上(通常是不同機器),典型的例子是分配到 client、server 上運行。 3-tier model 多層式架構當中最常使用的就是三層架構(three-tier architecture)。 三層架構與Web應用程式來說明如下: 表現層(Presentation):又稱為 UI 層,以Web來說就是呈現出來的前端網頁。 應用程式層(Application):以 Web 來說相當於伺服器上執行的應用程式。 資料層(Data):這一層包含資料儲存、呈現機制,以 Web 來說通常指 Database。 Reference wikipedia - Multitier architecture Rockford Lhotka - Should all apps be n-tier?

Hero Image
[.NET Core] 不阻塞的非同步控制器(Non-Blocking Asynchronous Controllers)

ASP .NET Core 當中的 Web 控制器屬於IO密集的應用程式,當中主要使用的 TAP 是一種簡易使用、語言層級的非同步設計模式。 透過 TAP 可設計出非同步(Asynchronous)/非阻塞(Non-Blocking)的 Web API,大幅提高 Web 應用程式的併發性(Concurrency)。 非同步方法 C# 當中基於 TAP 設計的的非同步方法 (TAP method) 有幾個特性: 產生可等待 awaitable 型別 (Task, Task<TResult>, ValueTask, 和 ValueTask<TResult>), 其中以 Task、Task<TResult>最常見。 非同步方法的參數順序通常跟同步版本的方法相同,但方法名稱以 Async 結尾。 async、await await 運算子用來等待非同步行為完成, 或等待非同步行為完成後解析回傳值, await 運算子只能用在非同步方法中, 因此 await 運算子的外層方法必須套用 async 修飾, 否則會出現錯誤。 非同步 Action 設計原則: 總是加上 async 關鍵字 async 的方法裡面可以等待非同步方法。 action 前加上 async 的作用在於建立一個管理回傳任務的狀態機(state machine), 當 async 方法擲出例外時會被狀態機捕獲並放到任務中回傳, 而這也是以 Task 作為回傳值的方法的預期行為。 如果沒有 async 關鍵字則擲出的例外會被直接傳遞到呼叫者(caller), 因此除非確定該 aciton 不會擲出任何例外,否則一律加上 async。

Hero Image
[JS] 提升(Hoisting)與暫時性死區(Temporal Dead Zone)

網路上時常見到充滿 function 與 var 的 JavaScript 求值題目, 筆者在釐清 Hoisting 和 TDZ 的觀念前時常覺得答案出乎意料, 雖然現在撰寫程式碼都已經避免使用 var,但是維護 legacy code 還是會用到,因此在這裡做個筆記。 Hoisting 對學過伺服器端語言(C#、C/C++…)的人來說,預期試圖對未宣告的變數取值會出現 ReferenceError是很正常的事, 在 JavaScript 中也是如此: console.log(x) // ReferenceError: x is not defined 但 JavaScript 把 var 宣告變數放在後面,x 前面對 x 取值就變成 undefine, 在 JavaScript 當中對變數取值獲得 undefine 代表變數處於宣告後已分配記憶體空間(初始化、initiation)但尚未賦值的狀態, 但是明明 JavaScript 在宣告前就取值,怎麼能夠認得 x、而且知道 x 被初始化為 undefined 呢? console.log(x) // undefined var x 原因在於 javascript 會先程式中的蒐集 var(let/const/function) 宣告並釐清對應的作用域, 最後再執行程式碼,這個行為就如同宣告被提升(Hoisting)到前面行數的程式碼中一樣。 JavaScript 跟傳統 OOP 語言一樣,在變數提升後、宣告初始化、賦值等等時機取用變數會獲得不同輸出結果, 這篇文章(link)中提到, 稱 JavaScript 為直譯式(解釋型)語言實際上是通俗的說法,語言本身沒有規定實現的方式(編/直譯), 舉例來說 Chrome 瀏覽器所使用的 Google V8 JavaScript engine, 所帶的解釋器 (interpreter) 會將程式編譯 (compile) 成字節碼 (bytecode), 最後再由編譯器 (compiler) 即時編譯 (JIT;just-in-time compilation) 編譯成機器碼執行, 而 JavaScript 被編譯時宣告會被蒐集到最頂端進行定義並區分作用域(scope),這個行為就是 Hoisting ,記住重點在於JS編譯後會先定義變數及區分作用域,編譯後的程式看起來就像這樣: