最近工作上碰到個案子,其中一個環節需要開發 CLI (Command Line Interface) 的工具,用來處理上百萬筆的資料,處理的步驟有好幾步,希望能按照步驟獨立成數個 CLI ..。資料筆數跟處理步驟是兩個不同的維度,以開發角度當然是按照步驟來區隔 CLI,但是以執行的效能考量則是希望處理好一筆資料的所有步驟,然後再處理下一筆。我跟同事提了 PIPELINE 的作法,也簡單做了些 POC 來說明可行性,所以才起了寫這篇文章的念頭。
如果你熟悉 linux 的 shell script, 你會發現很多內建的指令都能做到這些需求,大量資料的 input / output 都透過 stdio 來進行,shell 也提供 pipe(line) 的指令,讓前一個 CLI 的 stdout, 能夠直接接上下一個 CLI 的 stdin, 就能做到像接火車這樣的串接機制。不過使用的時候很簡單,這彈性的背後,有很多平時不會留意的基礎知識,所以寫了這篇打算來介紹一下 CLI 如何透過 PIPELINE 串流(平行)處理大量資料的開發技巧 (C#)。
圖片來源: https://pixabay.com/photos/tree-landscape-field-branch-696839/
架構面試題,這系列好久沒寫了。這次來探討一下,怎麼妥善的用 RDBMS 來處理樹狀結構的做法吧。
即使 NOSQL 風行了這麼多年,現在還是有不少服務背後是用 RDBMS 在處理資料的。RDBMS + SQL 是個絕配,將資料轉成表格,加上結構化的查詢就能解決過去 80% 的問題了。不過 Tree 這種資料結構,在 RDBMS 裡面就是格格不入。幾種 Tree 的基本操作,在 RDBMS 裡面都要花些心思轉換才能夠妥善處理。例如不限定階層的儲存、查詢某個節點以下 (不限定階層) 的所有子節點,或是 Tree 的搬移、刪除等等操作都是。如果用 RDBMS 的關聯性來處理 Tree Node 跟 Node 之間的關聯,就會衍生幾個階層就需要 Join 幾次的困境 (我不可能無限制的一直 Join 下去啊)。如果搭配特殊語法 (如 T-SQL CTE), 或是用 programming language 搭配或許能夠解決, 不過你就必須在複雜度與效能間做取捨了。
我覺得這是個不錯的題目,能鑑別出你的基礎知識夠不夠札實,能否靈活運用;也能看出你是否容易被既有的工具或是框架限制住做法的測驗。這邊先不討論 tree 到底該不該擺在 RDBMS 上面處理這種議題,那是另一個層次的討論。如果背後的 storage 就限定在 RDBMS 的話,我想探討的是我們有哪些方法能夠妥善的處理他?
在今年 .NET Conf 2018, 我講了一個場次: Message Queue Based RPC, 內容就是這篇文章的前半段… Message Queue 本身的架構就容許部分服務掛掉也還能正常運作,是高可靠度 / 非同步通訊不可或缺的一環啊! 這個場次我說明了如何用 C# 把 Message Queue 封裝成 RPC, 同時支援 C# async / await 的整個過程跟細節。不過時間只有短短的 50 分鐘,要交代完整的來龍去脈實在有點困難啊,剩下的部分我就在這篇文章補完。
這類主題,拉得更高一點來看,目的其實是整合啊! “架構師” 在這幾年變成技術職的終極目標,不過軟體業的市場還很年輕,市場上有經驗的架構師也不多啊,很多文章都在談論架構師到底該做些什麼事? 其中有一點很多文章都有提到,就是 “技術選型”。架構師應該用他的專業及經驗,替團隊挑選一個符合現況及將來發展的技術。這句話並沒有錯,不過我看過幾個例子,都沒有做到位;因為選完之後,下一步就是要應用啊! 成熟的服務或是框架,在現在的時空背景下其實不是個大問題,但是每個團隊面臨的商業需求,歷史包袱,團隊組成狀況等等都不同,很難有哪一套服務或是框架可以適應所有情況的。因此你做完技術選型後,下一個難題是將這些技術 “整合” 起來給團隊使用。
這就是我這篇文章想要表達的;我們挑選了 RabbitMQ 當作我們服務背後的 Message Queue, Message Queue 掌控了可靠的通訊核心, 非同步的通訊,到 CQRS, 到 Event Driven 事件驅動, 訂閱 / 通知 等類型的通訊都需要靠他,我期望內部團隊能發展出一套體系,能將這些機制串在一起。這些機制整合的好,團隊的能力就能往上提升一個檔次。既然整個體系,是架構師規劃出來的,該如何 “整合”,自然是架構師最清楚其中的關鍵了。我一直期望我對團隊的貢獻不只是紙上的規劃而已,而是能真正融入開發,從 code 層面就能幫助團隊提升。因此這篇我就打算從 Message Queue 幾個應用場景切入,示範一下從架構的角度,該如何看待 Message Queue 的整合這件事?
轉到電商後,經歷過第一次雙十一的洗禮,總算是親身體驗大流量的刺激與震撼了 :D,其中的辛苦我就不多說了,這次都還是仰賴經驗老到的同事們渡過難關的。不過這次我們能安然渡過,先前開發的排隊機制功不可沒。但是這次我們也發現到排隊機制其實還有些優化改善的空間,可以讓排隊中的消費者有更好的體驗。
圖片出處: https://tw.appledaily.com/new/realtime/20181124/1472338/
雙十一過後,我試著在思考如何改善排隊的做法… 不過很少看到有文章在探討這類機制該如何開發的細節? (大多是高流量高併發,淘寶雙十一超大規模架構之類的) 於是我試著研究這個議題,也簡單的寫了 POC 來驗證想法,就順手寫了這篇…。
這篇跟前一篇 微服務基礎建設: 斷路器 #1, 服務負載的控制 的出發點是一樣的,都是要透過某些程序,限制同時取用服務的請求,確保服務端有足夠的資源來完成任務。只是上一篇控制的是 server 能 “同時” 受理多少 request 的管控機制,控制的是處理交易的 “速度”。而這篇我要探討的是這些 request 應該要用什麼樣的規則與順序,決定那些 request 可以被服務? 換個角度說,排隊機制決定那些人 (request) 可以進入店面消費,而流量管控則決定櫃台一分鐘能服務多少人 (request)。
這兩件事情都做到位,你就能掌握好服務對使用者的 QoS (Quality Of Service) 了,可以兼顧服務的質跟量。質是指還未能被服務到的客戶是否仍能有良好的體驗,而量則是指交易處理的速度 (TPS, transaction per second)。這兩者都是線上交易很關鍵的一環,不過往往 developer 都不會把它擺在第一順位 (一般情況是: 規格就沒這項啊,通常都是上線了有人來抱怨再說…)
有時候,無知就是福啊… 沒想到我也默默忍受了這個地雷一年多了都沒發現。Container 透過 volume 掛載儲存空間到容器內部,會有一定的效能折損是一定的。然而 container 本身透過 AUFS 也有一定的效能折損啊。原本很天真的想: 我都在本機使用,想想應該不會差到哪裡去,直到膝蓋中了一箭…
事情要回顧到兩年前,我開始把 Blog 搬到 GitHub Pages 上面來開始說起… 當時為了避開 Ruby for Windows 地獄,我一直用 Docker for Windows 跑 Jekyll 在我自己 PC build website 測試,直到前一陣子突然想用 LCOW 來替代,才意外發現 volume 跟 container 內部的 I/O 效能有天跟地的差別…
不過,不論你對測試的數據有沒有興趣,請務必看一下 結論 這個章節。雖然 (先雷一下結果好了) 透過 LCOW 掛上 volume 的 I/O 效能不好看,但是別急著否定他。搞清楚它的定位,搞清楚什麼應用場景才是適合 LCOW 發揮的領域,善用他才是正途。