只有累積,沒有奇蹟

2020年8月12日 星期三

[NETCore] 使用 BenchmarkDotNet 測試程式碼效能

前言
在開發或是 POC 時為了記錄代碼程式執行的時間,都會採用在代碼上加上  stopWatch  紀錄花費時間,使用方式不外乎就是 new Stopwatch、Start、Stop、Reset、ElapsedMilliseconds,以詳細記錄代碼所花費的時間,接下來為了要顯示在 console 上可能還要針對 log 在輸出內容做簡單排版方便記錄,最近發現上述惱人的事情都可以使用  BenchmarkDotNet  套件來幫你完成,不僅可以完成上述提到的事情,還會自動將結果輸出成 report html 檔案,並會記錄執行時的 Runtime 版本與硬體資訊,這篇就針對 benchmarkDotNet 做簡單的介紹與說明若有問題或是有更推薦的工具歡迎提出一起討論或是給予指導。

BenchmarkDotNet 安裝
套件支援下列項目
  • Runtimes: Full .NET Framework (4.6+), .NET Core (2.0+), Mono, CoreRT
  • OS: Windows, Linux, MacOS
  • Languages: C#, F#, VB
Manage Nuget Package 安裝
Step 1 : 在 Visual Studio IDE 按下快捷鍵  Ctrl + Q ,滑鼠指標會移到右上角搜尋框輸入 nuget 按下 enter
Step 2 : 在搜尋框輸入 benchmarkDotNet,點擊 Install
由於此套件要下載的 dll 很多因此等待時間會比較長,接著就是無止境的下一步

Package Manager Console 安裝
不愛 GUI 介面的人可以選擇輸入以下指令進行安裝 
Install-Package BenchmarkDotNet -Version 0.11.4

使用方式
接著參考官網的範例(沒梗)比較 C# 裡 MD5 和 SHA256 兩個方法在新增特定筆數時,哪一個會花費比較少的時間完成(效能)會比較好,也順便練習在 Benchmarkdotnet 套件是如何實作及 Report 內容是如何呈現,新增一個 Console 專案為 BenchmarkDotnet,並在專案中加入下列代碼
using System;
using System.Security.Cryptography;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace benchmarkLab
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<Md5VsSha256>();
            Console.ReadKey();
        }
    }

    public class Md5VsSha256
    {
        private const int N = 10000;
        private readonly byte[] data;

        private readonly SHA256 sha256 = SHA256.Create();
        private readonly MD5 md5 = MD5.Create();

        public Md5VsSha256()
        {
            data = new byte[N];
            new Random(42).NextBytes(data);
        }

        [Benchmark]
        public byte[] Sha256() => sha256.ComputeHash(data);

        [Benchmark]
        public byte[] Md5() => md5.ComputeHash(data);
    }
}

程式說明
  • Md5VsSha256 類別 : 產生 Md5 與 sha256 方法上加上  Benchmark  attribute
  • Main 類別 : 使用  BenchmarkRunner.Ren<T>  T是你要驗證的 classname
  • 下一步將執行方式改為  Release  ,如果你的Visual Studio 有開啟 Just my code 也會提示你關閉此功能
    按下 Ctrl + F5 執行專案,接著可以觀察到 Benchmark 開始針對 release 資料夾底下 projectName.exe 檔案執行測試,並列出測試環境的 runtime 與 framework 資訊
    測試跑完之後我們直接看結果 
    // * Summary *
    
    BenchmarkDotNet=v0.11.4, OS=Windows 10.0.17763.379 (1809/October2018Update/Redstone5)
    Intel Core i7-8550U CPU 1.80GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
      [Host]     : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3324.0
      DefaultJob : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.7.3324.0
    
    
    | Method |      Mean |     Error |    StdDev |
    |------- |----------:|----------:|----------:|
    | Sha256 | 151.71 us | 1.2058 us | 0.9414 us |
    |    Md5 |  30.46 us | 0.6038 us | 0.5930 us |
    
    // * Hints *
    Outliers
      Md5VsSha256.Sha256: Default -> 3 outliers were removed
      Md5VsSha256.Md5: Default    -> 2 outliers were removed, 3 outliers were detected
    
    // * Legends *
      Mean   : Arithmetic mean of all measurements
      Error  : Half of 99.9% confidence interval
      StdDev : Standard deviation of all measurements
      1 us   : 1 Microsecond (0.000001 sec)
    
    // ***** BenchmarkRunner: End *****
    // ** Remained 0 benchmark(s) to run **
    Run time: 00:00:39 (39.09 sec), executed benchmarks: 2
    
    Global total time: 00:00:45 (45.68 sec), executed benchmarks: 2
    // * Artifacts cleanup *
    從報告中可以看出來,執行時間 sha256 為 151.71 us 與 md5 的 30.46 us 較久,1 us : 1 Microsecond (0.000001 sec),因此可以判定在 .Net Framework 環境下 md5 比 sha256 來的快 

    Report
    前面有提到案下執行後,會針對專案 release 資料夾底下 backmarklab.exe 檔案執行測試,測試完畢後除了會 console 出測試結果之外,如果注意 console 內容來可以發現內容有提到結果輸出成 report,輸出路徑在  release\BenchmarkDotNet.Artifacts\results  目錄底下,如下圖所示
    // * Export *
    BenchmarkDotNet.Artifacts\results\benchmarkLab.Md5VsSha256-report.csv
    BenchmarkDotNet.Artifacts\results\benchmarkLab.Md5VsSha256-report-github.md
    BenchmarkDotNet.Artifacts\results\benchmarkLab.Md5VsSha256-report.html
    Repost 提供三種呈現方式,分別是 : csv、html 與 html,都記錄測試時間結果還有環境變數資訊
    HTML 呈現方式
    MD 呈現方式

    設定環境變數 - Job

    剛剛的測試數據證明了在 .NET Framework 下是 Md5 比 sha256 快,但如果今天程式是執行在 .NET Core 環境下是否還是一樣呢 ? 在一開始有提到,benchmarkdotnet 支援 .Net Framework 與 .NET Core 以及 mono,可以執行一次同時確認多個環境變數,舉例來說想同時驗證 .NET Framework、.NET Core 環境,只需要在 attribute 上加上  [clrJob]  與  coreJob  ,如下範例
    [ClrJob, CoreJob]
    public class Md5VsSha256
    可以看到輸出結果多了 clr 部分,但是其結果卻是 NA,這時需要開啟專案 csproj 檔案,調整下列內容
    詳細可以參考官網常見問題 : 傳送門
    <TargetFrameworks>netcoreapp2.2;net461</TargetFrameworks>
    <PlatformTarget>AnyCPU</PlatformTarget>
    
    重新執行一次,可以發現 .NET Framework 與 .NET Core 數據有正常呈現
    加入了在 .NET Core 環境測試,從結果來看 md5 速度上還是優於 sha256,另外在測試時建議將測試環境不要用到的程式關閉,避免影響到測試時的效能,才可以更呈現結果更為準確。

    GitHub Sample Code 傳送門 :  BenchmarkDotNet Lab

    感想
    透過以上簡單的應用可以看出來 Banchmarkdotnet 不僅可以協助測試,還可以將測試數據輸出成報表呈現,只需要在測試的類別上加上 [Benchmark] 即可,並在測試時考慮到 cold start & warm state、手動調整執行次數與執行時 GC 資訊,相關文件與更多的功能都可以在官網看到,想了解更多的朋友去 benchmarkdotnet.org 尋寶吧 ! 
      參考
      benchmarkdotnet
      使用 BenchmarkDotnet 测试代码性能

      2020年7月11日 星期六

      [VisualStudio] PlantUML - 在 Visual Studio Code 繪製 UML 圖

      前言
      最近團隊主管開始要求團隊的文件,對於既有的系統的或是代碼,一直沒有比較清楚的架構圖或是說明的文件來定義各模組之間依賴關係,都是透過較資深的同事口耳相傳或是有看過代碼的人做傳承,但這種方式還是有比較大的風險,可能每個人理解的內容經過幾個世代口耳相傳後認知可能會有所差異,因此最近花了蠻多時間在整理既有系統相關文件,整理中可能會用架構圖循序圖、流程圖加上文字來說明。自己過去在畫循序圖可能會用 Visual Paradigm Online 或是 websequencediagrams 等工具,PlantUML 是 open source 免費且使用文字來定義互動的 UML 模型溝通方式,個人使用上覺得挺好用的,省掉了使用軟體畫圖調整位置的時間,今天這篇文章就來推坑這款好用的工具,若有問題歡迎提出來一起討論。


      安裝
      首先,官方說明文件有提到可以使用 PlantUml Demo Site 在線上 Render 需要的 UML 圖,如果希望在本機使用的話,可以從 Visual Studio Code 中的 Extensions 進行安裝, 在 Extensions 搜尋框輸入關鍵字 PlantUML,接著按下旁邊的 install 即可進行安裝的動作,
      接著要在本機環境 Render,需要先安裝 Java,如果是 Mac 環境輸入以下指令
      • brew cask install java
      • brew install graphviz
      如果在 Windows 請開啟 cmd.exe 輸入下列指令
      • @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
      • choco install plantuml
      備註:如果電腦環境沒有安裝 Java 的話,可以到 這裡 下載 Java 安裝檔
      透過以上步驟,即可完成 PlantUML 在 Visual Studio Code 的安裝。


      使用
      Sequence Diagram
      安裝完畢之後可以開始使用 PlantUML,在 Visual Studio Code 新增 demo.puml 檔案,檔案內容參考 demo site 中提供的範例內文,貼在新增的檔案中
      @startuml
      Bob -> Alice : hello world
      @enduml
      按下右鍵選擇 Preview Current Diagram 選項,右邊就會出現所定義範例 UML Sequence Diagram
      UseCase Diagram
      @startuml
      
      (First usecase)
      (Another usecase) as (UC2)
      usecase UC3
      usecase (Last\nusecase) as UC4
      
      @enduml
      在 Visual Studio Code 中 Render 之後就會像下方的方式呈現
      除了以上的 Demo 的兩種常見的 UML 圖之外,PlantUML 還另外支援以下 UML diagram
      • Sequence diagram
      • Usecase diagram
      • Class diagram
      • Activity diagram
      • Component diagram
      • State diagram
      若對以上不同的 Diagram 有興趣的大大們,歡迎參考官網說明 自行試試看 :)

      貼心功能
      另外花了很多時間整理好 Diagram 之後,接下來要處理的是將辛苦畫好的圖片產出,PlantUML 提供一個很方便的功能自動將產出物 Export 到 url,個人覺得此功能十分貼心可以省下很多時間,這裡也就直接使用官方示意來說明
      輸出圖片
      產生 URL

      感想
      以上針對 PlantUML 套件做一些基本的介紹,整體使用上感覺算是挺方便的,也謝謝強者同事賈米斯推薦又學到一套好用的 Tool,如果有不清楚的地方歡迎一起討論,hope it helps !

      參考
      官方網站
      marketplace
      用筆記也可以管理專案(一):PlantUML



      2020年7月1日 星期三

      [NETCore] ASP.NET Core 靜態檔案設定 - UseStaticFiles

      前言
      在過去開發 .NET Framework 時代在專案使用靜態檔案是一塊小蛋糕,只需要在專案指定 Folder 加入需要的靜態檔案內容即可,像是圖片就放在 image、javascript 內容就放在 js 資料底下...等等,但同樣事情在 ASP.NET Core 專案可就完全不同,在 .NET Core 專案中是無法直接瀏覽靜態檔案的,需要透過一些設定方式才可以在專案看到像是 HTML、CSS、Image 或是 javascript 靜態檔案,這篇文章就是簡單說明在 ASP.NET Core 專案中如何設定才可以看到靜態檔案內容,若有問題歡迎提出一起討論或是給予指導。

      靜態檔案
      在 ASP.NET Core 中 Web 檔案會存放在專案根目錄中,預設存放位置是專案根目錄  project / wwwroot  底下,其資料夾底下可以存放多個資料夾如下,如果要存取 image 子資料夾底下的 test.jpg,其 URL 格式為 http:// yourApplicaitonName:9487/image/test.jpg
      以下就透過簡單的範例,在 ASP.NET Core 專案簡單示範如何加上靜態檔案
      簡單來說兩個步驟可以完成
      1. 加入靜態實體檔案
      2. 加入 UseStaticFiles 方法
      Step 1 : 前面提到預設靜態檔案目錄是 wwwroot,在 wwwroot 目錄底下新增一個 helloworrld.html 檔案
      其 html 內容就簡單輸入 Hello World ASP.NET Core !!!
      <!DOCTYPE html>
      <html>
      <head>
          <meta charset="utf-8" />
          <title></title>
      </head>
      <body>
          Hello World ! ASP.NET Core 
      </body>
      </html>
      Step 2 : 在 Startup 中 Configure 新增  UseStaticFiles  方法,UseStaticFile 是  IApplicationBuilder  的擴充方法,主要是啟用靜態文件服務,背後其實是呼叫另一個 middleware 幫妳進行靜態檔案的設定
      app.UseStaticFiles();
      好的,經過設定完畢之後在開啟網站並在網頁 url 後面加上 helloworld.html,即可在網頁上看到 Hello world ! ASP.NET Core 字眼,新增成功 !!
      未設定 UseStaticFiles
      如果未設定 UseStaticFiles 或是未將檔案放置在預設路徑底下,則會出現 Http Error 404 not found 錯誤畫面,如下圖所示
      透過以上步驟,就可以輕鬆解決在 ASP.NET Core 無法顯示靜態檔案問題,讓靜態檔案找到回家的路(咦

      UseStaticFiles 
      為何透過 UseStaticFiles 方法就可以啟用靜態檔案呢? 接下來我們在往下追看看方法做了什麼事情以及實作細節,在  UseStaticFiles  方法提供三種多載情境給開發者使用,三種方法分別是
      • 預設路徑 : 無任何參數
      • 指定靜態檔案路徑 : requestPath
      • 指定 StaticFileOptions 物件
      UseStaticFiles
      無參數的方法如下
      /// <summary>
      /// Enables static file serving for the current request path
      /// </summary>
      /// <param name="app"></param>
      /// <returns></returns>
      public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app)
      {
        if (app == null)
          throw new ArgumentNullException(nameof (app));
        return app.UseMiddleware<StaticFileMiddleware>(Array.Empty<object>());
      }
      從上述代碼可以看到,無參數的方法主要是透過設定  StaticFileMiddleware  來做靜態檔案的設定,在其 middleware invoke 方法中主要是處理已知靜態檔案 Request 請求,也就是確認 MIME 的 Content Type 是可以被識別的才會處理其靜態檔案請求內容,路徑則是透過預設的 wwwroot 路徑位置來讀取檔案位置,

      UseStaticFiles 指定靜態檔案路徑
      此方法是指定檔案路徑位置作為參數,接著再透過 UseStaticFiles  指定其 StaticFileOptions 作為參數,相關代碼如下
      /// <summary>
      /// Enables static file serving for the given request path
      /// </summary>
      /// <param name="app"></param>
      /// <param name="requestPath">The relative request path.</param>
      /// <returns></returns>
      public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app, string requestPath)
      {
        if (app == null)
          throw new ArgumentNullException(nameof (app));
        IApplicationBuilder app1 = app;
        StaticFileOptions options = new StaticFileOptions();
        options.RequestPath = new PathString(requestPath);
        return app1.UseStaticFiles(options);
      }
      或者這樣講有點抽象,這就簡單的舉個範例讓大家更容易了解,下面的範例是透過設定 StaticFileOptions 公開 MyStaticFiles 目錄資料夾作為存放靜態檔案的位置
      public void Configure(IApplicationBuilder app)
      {
          app.UseStaticFiles(); // 預設公開位置 wwwroot 
          app.UseStaticFiles(new StaticFileOptions
          {
              FileProvider = new PhysicalFileProvider(
                  Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
              RequestPath = "/StaticFiles"
          });
      }

      UseStaticFiles 指定 StaticOptions
      UseStaticFiles 指定靜態檔案路徑與此方法有點類似,都是設定其 StaticOptions 參數資料,差異是上面的方法僅提供設定路徑位置 RequestPath,此方法提供設定範圍比較廣,可以定義 StaticOptions 類別更多的細節,先看看 API 簽章
      /// <summary>Enables static file serving with the given options</summary>
      /// <param name="app"></param>
      /// <param name="options"></param>
      /// <returns></returns>
      public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app, StaticFileOptions options)
      {
        if (app == null)
          throw new ArgumentNullException(nameof (app));
        if (options == null)
          throw new ArgumentNullException(nameof (options));
        return app.UseMiddleware<StaticFileMiddleware>((object) Microsoft.Extensions.Options.Options.Create<StaticFileOptions>(options));
      }
      甚麼時候有可能會用到呢? 舉例來說有時希望在回傳靜態檔案時加上 cache 機器,讓瀏覽器的 Browser 幫忙做點事不用相同檔案的 Request 重複跟 Server 要資料,這時就可以透過 OnPrepareResponse 方法在回傳前做點小加工,實作方式如下
      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
      {
          var cachePeriod = "600";
          app.UseStaticFiles(new StaticFileOptions
          {
              OnPrepareResponse = ctx =>
              {            
                  ctx.Context.Response.Headers.Append("Cache-Control", $"public, max-age={cachePeriod}");
              }
          });
      }
      在回傳的 Head 上加上 cache 有效時間為 600 秒,從網頁 Response 結果就可以看出設定成功

      後記
      以上簡單介紹關於 UseStaticFiles 的基本應用與說明,在其中有更多進階的應用像是檔案授權、如何開啟靜態檔案目錄方式瀏覽、提供預設文件的方式等更多情境的應用,如果有需要可以參考官方網站的說明與 sample code : 傳送門在此,當然對以上的內容說是有疑問的地方歡迎隨時提出來,謝謝

      參考
      ASP.NET Core 中的靜態檔案

      2020年5月17日 星期日

      [筆記] TGONext 架構組 - 資料庫 Migration 及軟體架構的演進

      前言
      這一篇是紀錄參加 TGONext - TGONetworks 導師計畫架構一組第三次聚會的旁聽筆記,導師計畫歡迎小組間跨組討論因此自己只要時間允許會參加其他組的討論,參加完後也會整理討論的筆記方便日後回顧,在過去兩個月有參加的架構組筆記分別如下 為了日後讓自己可以更快理解因此整理筆記時有加上自己理解的說明與圖片,其中若是自己有理解不正確的地方是錯誤的部分歡迎各位高手一同討論或給予指導

      Database Migration
      在這次聚會的一開始同學拋出 Database Migration 的議題,程式碼有版本控制系統,遇到異常狀況時可以找到原本執行正常的程式碼快速進行 Rollback 的動作,那麼資料庫遇到類似狀況要如何 rollback 呢? Ant 請同學分享針對過去的經驗或是解法,可能的解決方案如下
      • 不刪除資料及欄位
      • 保存刪除資料,使用 Trigger 將資料複製到另一個 Table
      • 將要異動的 Table 建立一份 snapshot 快照
      • Rename & Copy
      這問題沒有一定標準的答案,導師也提醒需要考慮在特定的法規中需要保留資料特定年限才能刪除,像是在金融業政府就規定交易紀錄須保存五年以上,方便稽核單位進行查核的動作。
      導師 : 如果不刪除資料的話,大家覺得會有什麼問題?
      如果資料庫有很多非必要資料或是欄位時,如果不刪除的話會占用 disk 或是 memory,在傳輸時會多占用網路頻寬;存放過多 Dirty data,後人在維護時也有可能因為不敢亂刪除資料或是欄位,因而繼續存放演變為技術債,也可能會讓 db 的 loading 越來越高,因此可能會影響到資料庫的效能,為了不影響資料庫的效能處理方式有很多種,常見有兩種方式處理
      • 將沒用到的放置在另一個 Table,如果之後 Database 需要 Migration 時可以將所需要資料移回來
      • 將不需要用到的資料 snapshot,有需要 Rollback 時再將 snapshot 資料回復到原本的資料庫中
      Add New Columns
      RDBMS 是 row base database,新增一個欄位所需要的時間取決於目前資料量的多寡,如果目前異動的 Table 有兩千萬筆 row data 時,這兩千萬筆資料都需要新增 columns,在 Production 的 Table 新增欄位會有 lock 的情況發生,也就是說可能會有 downtime 的狀況產生,在過去的經驗如果要在核心 Table 進行異動或是 index rebuild 的話,會在網站者使用者相對較少的時間進行停機告知用戶網站正在維修中(Under Maintenance),在交易量大的網站中對於 downtime 是格外重視的,在 PostgreSQL 11 與 MySQL 8 分別都有支援新增欄位不會 Table lock 的方法。
      那麼不停機要新增欄位,要怎麼做會比較好呢? 
      在 MySQL 有工具可以協助達到此目的 : gh-ost,原理如下 了解運作原理之後,那麼它可能的缺點會是什麼?舉例來說目前資料庫中有些 Table 近 2G 的空間,會進行 COPY Table 資料,可能問題會是 COPY 原有 Table 資料過程中,由於資料量非常龐大此時 Disk IO 會飆高,cpu high或是既有空間會不足的情況發生,這都是使用前需要留意的地方。

      Row Based Database vs Column Based Database
      在比較 RDBMS 與 NoSQL 的特性時,導師也提到可以從 Row Based Database 與 Column Based Database 的設計進行研究,這方面比較不熟因此列出網路上文章日後有時間拖稿再繼續研究

      資料結構 : Tree
      接著討論有關 RDBMS 資料庫與 NoSQL 本質上的差異,以及資料庫中的重要觀念 B Tree & B+ Tree,內容相信大家都了解這裡就不在說明,這裡列出幾個自己覺得說明蠻清楚的網站讓大家了解其原理

      軟體架構演進
      接著回到軟體架構的討論,在上次的討論中有提到 架構的演進,在討論的一開始導師又拋出問題給同學
      導師 : 當單機承受不住的時候,為了支持更大的量,你會怎麼做 ? 

      • 機器硬體加大
      • 橫向擴充
      • 服務拆分
      • Cache Server
      • Queue
      導師強調演進沒有一定的答案,每個解決方案都有適合他的情境,架構也有各自的瓶頸點,舉例來說選擇使用垂直擴充加強機器的硬體,但升級並不是無限擴充的會有物理的上限問題;如果選擇橫向擴充了100台機器,機器的管理就是一個問題,像是如何將新功能在最短時間佈署至100台機器,佈署策略要如何去定義,分散式系統的問題開始浮上檯面不得不面對;在上次聚會中提到 cache server 可能是其中的一種方法,但是團隊是否有能力可以處理 cache 帶來的問題,要如何處理因為 cache 異常引起的雪崩效應,常用的 cache 有 Redis 與 Memcached 兩種,Redis 相信大家都不陌生,在 Facebook 與 google 仍然在使用 Memcached,導師也請同學們思考看看背後的原因。

      擴充方式
    • Scale up : 垂直擴充 (Scale vertically),提升單體 Server 硬體效能,像是增加 CPU 核心數、硬碟換為SSD、升級記憶體 Memory、升及網路卡速度,硬體升好升滿的概念。
    • Scale out : 水平擴充 (Scale horizontally),增加伺服器數量,使用更多機器一起來負擔更多的用戶與 Request 請求數量
    • 但在垂直擴充到某個程度可能會有硬體的物理上限,相對的硬體資源更好的伺服器費用也較為昂貴,
      使用大量硬體較便宜伺服器來實現提升整體效能成本上會來的較為便宜,機器異常時造成服務中斷的風險也比較低。

      Database 擴充
      在 RDBMS Database 相對於 Application 來說,擴充上是較不容易的(不考慮分散式系統或是讀寫分離的情況),如下圖所示

      此是一個簡易的前後端分離架構圖,當用戶透過瀏覽器發送請求到資料庫處理的流程來區分,網路可以透過 CDN 快取加速,前端 FrondEnd 網站與 Backend API 可以透過 local cache 或是 Redis 等快取機制加快處理速度,當 Server 機器數量不夠時可以透過 Scale out 擴充承接更大量的 Request 請求,前面假設機器加到 50 或 100 台 server 能夠應付更大的流量,最後在做資料異動還是需要透過 Database 來處理,由於 RDBMS Database 的特性(Atomicity、Consistency、Isolation、Durability) 注重資料的一致性,因此在擴充上是較難分為多台水平擴充,如果遇到效能的問題需要做擴充大多都會選擇硬體升級方向出發,但是升級都會遇到機器物理的上限無法無止境的提升,因此保護好資料庫變成一件很重要的事情。

      資源的限制
      在架構的選擇中,資源(Resource)也是考量的重點之一,常見的資源有 CPU、Memory、Storage space、Disk IO、Network bandwidth、Network latency 等,請大家思考哪一個項目是覺得比較難擴充的,有人回答 Disk IO,因為 Network 的問題或許可以靠 CDN 去解決,但 Disk IO 可能跟不上 CPU 的速度,大家都提出自己的觀點與看法,接著導師請大家思考
      導師 : 在雲端服務廠商裡面,上述提到的(Resource)有什麼服務是不能選擇的 ? 
      導師提出他的觀點 Network 是無法選擇的,Google 推出 Protocol Buffers 以及 MessagePack 等新的 format 就是為了解決網路傳輸的問題,主要是透過(使用CPU)壓縮的方式讓傳輸資料輕量化,以加快資料網路傳輸的速度,也就是用 CPU 去換傳輸資料輕量化。

      Queue
      導師 : 甚麼時候應該要用 Queue ? 它的特性應該是什麼 ? 
      • 有順序性,存放暫存的請求內容(狀態state)資料
      • 在架構設計上常會用於系統與系統間解偶(Loosely Coupled)
      • 像是非同步,處理完畢的時間不確定 (asynchronous 的缺點都有)
      使用上要注意的是要有(限制) 容量 (memory) 的上限,不能是無限制的使用;另一點要注意的是當 producer 要將請求內容放置到 Queue 的時候,如果遇到 Queue 已經滿了放不進去時該如何處理 ? 遇到 Queue 滿了是否有將 producer 的內容 block 住,保證內容不會因為 Queue 滿了而遺失,但也可能會因此造成連鎖效應,如果發生異常 block 住的數量過多要如何快速地消耗,是否團隊有建立監控機制來預防此狀況發生,與上次討論提到的 cache 相似,團隊在選用前應該了解會遇到的問題及如何解決,才不會在上線後踩雷後無法處理造成業務上的損失。

      Kafka & RabbitMQ
      接著討論到常見的 Queue service 有 Kafka 及 RabbitMQ,分別是使用 Java 與 Erlang 不同的程式語言所開發,分別有各自程式語言的特性,像是 RabbitMQ 預設記憶體超過 40% 時會發出 memory 不足的警告,當 GC (Garbage Collection) 機制回收時會消耗兩倍記憶體量為 80% (50% used memory + 50% Crach memory,確保失敗的記憶體可以復原),這是 Erlang 程式語言的 safe to crash 的特性也是可能在使用 RabbitMQ 必須要注意的地方(缺點);接著談到 Kafka,Kafka 是 Linkedin 開發的 open source,Kafka 快的原因是 Java 有使用 off-heap,off-heap 是 GC 的一種技術,在 Java 有提供 unsafe 基礎類別 API 可以自己去控制記憶體或回收記憶體 (備註1),從效能鐵三角的角度來看 Kafka 是追求高 throughput。

      備註1 : 當 GC 啟動在執行時記憶體回收時,正在進行的執行緒會將暫停直到 GC 回收完畢為止,為了解決內存記憶體過大造成 GC 回收時間過長的問題, JAVA 提供 off-heap memory,將記憶體放在 heap 中將控制權交由作業系統處理,(感謝 Ant 大協助 Review & 糾正)

      心得
      過去曾經在面試中被問到 RDBMS 與 NoSQL 如何選擇,自己當時回答得七零八落,但經過這兩次小組的討論陸續有講到 RDBMS、NoSQL與 NewSQL 的本質設計,對於這問題的答案自己也開始有些頭緒。如同於程式架構的演進一樣,從 RDBMS 到 NoSQL 到最近的 NewSQL,各有適用的強項(解決的問題)與適用的場景,沒有一招打遍天下的解決方案。另外在討論架構時也提到不同解決方案的差異,也提醒同學必須思考什麼情況下會從一種模式轉為另一種架構模式,考量的點會是甚麼,舉例來說如果架構中某個服務loading 較高時,可以選擇抽出來提高其可用性與吞吐量,其餘沒有問題的研究既有架構繼續使用,像是積木一樣做組合與分配,選擇可以解決問題的方案才是真正的王道。

      參考
      Elasticache : 比較 Memcached 和 Redis 的差別
      Queue 的應用(4) - ManualResetEvent 與 Lock 的 BlockQueue 補完
      堆外內存(off-heap),堆內存(on-heap)
      Java魔法类:Unsafe应用解析
      Apache Kafka 介紹
      Blocking Queue
      RabbitMQ性能优化
      Difference between Row oriented and Column oriented data stores in DBMS
      Rowise vs Columnar Database? Theory and in Practice
      關聯式與NoSQL 資料
      Multi-Tenancy Application #2, 資料層的選擇
      Protocol Buffers
      MessagePack

      Copyright © m@rcus 學習筆記 | Powered by Blogger

      Design by Anders Noren | Blogger Theme by NewBloggerThemes.com