• 15 分鐘將 ASP.NET MVC Movie 範例程式導入 Azure Redis Cache

    原始文章發佈於 MVC movie app with Azure Redis Cache in 15 minutes

    最近公開預覽的 Azure Redis Cache 很容易就能整合進您的 Azure 網站應用程式中,這裡我將 MVC Movie 範例程式整合 Azure Redis Cache 然後部署到 Azure 網站服務(Websites)上,大約只花了 15 分鐘左右。

    在導入快取(cache)之後,程式的速度比起純用資料庫來說快了將近 100 倍,因為經常被存取的資料就可以直接從快取中取得,而不必再到資料庫中撈資料,如此一來你也可以降低資料庫的存取次數,讓資料查詢的動作變得更迅速。

    整合完畢的範例程式碼可以在這裡下載

    接下來我會說明我是如何把 Azure Redis Cache 整合進我的 ASP.NET MVC Movie 範例之中。

    1. 在預覽中新的 Azure 管理界面,我們可以如圖所示建立一個新的 Redis Cache 服務。

      建立 Cache 服務大概會花個 15 分鐘左右,不過同一時間我不會在這裡等著,而是去修改我的程式。如果你需要完整的參考手冊,可以參考 How to Use Azure Redis Cache 這篇文章,不過要記得的是,Redis Cache 服務必須要跟你的網站服務是在同樣的資料中心,不同的資料中心可能速度會相差到 25 倍左右,建立的步驟可以參考 Create a Redis Cache 這一頁,你可以下載原始的 MvcMovie 範例程來改,或是直接下載我修改後的範例,但是要記得修改 cache 服務的 URL 及認證資料才會正常運作。
    2. 當 Cache 服務建立完成後,記下 Cache 名稱像是 <yourName>.redis.cache.windows.net 以及密碼(點擊 keys 按鈕就可以取得名稱及密碼)。
    3. 在你的 MVC 專案中,使用 NuGet 套件管理工具安裝 StackExchange.Redis 這個套件,如果您下載的是修改後的範例,那專案中已經安裝並參考了這個套件。
    4. 打開套件管理主控台(package manager console)中輸入 Update-Database 的指令。
    5. 在相關的 controller 中加入連結快取服務的程式碼:
      public class MoviesController : Controller
         {
            private MovieDBContext db = newMovieDBContext();
            private static ConnectionMultiplexer connection;
            private static ConnectionMultiplexer Connection
            {
               get
               {
                  if (connection == null || !connection.IsConnected)
                  {
                     connection = ConnectionMultiplexer.Connect(
                     ".redis.cache.windows.net,ssl=true," +
                     "password=");
                  }
                  return connection;
               }
            }

    注意:一般來說都不建議你把帳號密碼的資料直接寫在程式碼中,這裡只是為了方便瞭解程式碼才這麼做。請參考 Windows Azure Web Sites: How Application Strings and Connection Strings Work 這篇文章介紹的方法來處理帳號密碼這些敏感資料。

    在上述的程式碼中,我已經將連結快取服務的部份用靜態成員的方式儲存,所以你不必在每一次 request 都重新建立一個快取服務的連結,你只需要在使用時檢查連結是否還在,若已經失去連結再重新建立連結就好。

    新增一個類別,並包含這個 SampleStackExchangeRedisExtension 類別:

    public static class SampleStackExchangeRedisExtensions
    {
       public static T Get<T>(this IDatabase cache, string key)
       {
          return Deserialize<T>(cache.StringGet(key));
       }
    
       public static object Get(this IDatabase cache, string key)
       {
          return Deserialize<object>(cache.StringGet(key));
       }
    
       public static void Set(this IDatabase cache, string key, object value)
       {
          cache.StringSet(key, Serialize(value));
       }
    
       static byte[] Serialize(object o)
       {
          if (o == null)
          {
             return null;
          }
          BinaryFormatter binaryFormatter = new BinaryFormatter();
          using (MemoryStream memoryStream = new MemoryStream())
          {
             binaryFormatter.Serialize(memoryStream, o);
             byte[] objectDataAsStream = memoryStream.ToArray();
             return objectDataAsStream;
          }
       }
    
       static T Deserialize<T>(byte[] stream)
       {
          BinaryFormatter binaryFormatter = new BinaryFormatter();
          if (stream == null)
             return default(T);
    
          using (MemoryStream memoryStream = new MemoryStream(stream))
          {
             T result = (T)binaryFormatter.Deserialize(memoryStream);
             return result;
          }
       }
    }

    SampleStackExchangeRedisExtensions 類別可以讓你很輕易就將任何可序列化(serializable)的類別做快取。你可以在你的模型(model)上加上 [Serializable] 屬性。

    [Serializable]
    public class Movie

    然後將所有 Movie movie = db.Movies.Find(id); 的部份都修改成:

    //Movie movie = db.Movies.Find(id);
    Movie movie = getMovie((int)id);

    在 POST 呼叫的 EditDelete 方法中,記得要清除快取。

    ClearMovieCache(movie.ID);

    在 controller 中加入下面這段程式碼,其中 getMovie 是一個很標準的快取操作:

    Movie getMovie(int id)
    {
       Stopwatch sw = Stopwatch.StartNew();
       IDatabase cache = Connection.GetDatabase();
       Movie m = (Movie)cache.Get(id.ToString()); 
    
       if (m == null)
       {
          Movie movie = db.Movies.Find(id);
          cache.Set(id.ToString(), movie);
          StopWatchMiss(sw);
          return movie;
       }
       StopWatchHit(sw); 
    
       return m;
    } 
    
    private void ClearMovieCache(int p)
    {
       IDatabase cache = connection.GetDatabase();
       if (cache.KeyExists(p.ToString()))
          cache.KeyDelete(p.ToString());
    } 
    
    void StopWatchEnd(Stopwatch sw, string msg)
    {
       sw.Stop();
       double ms = sw.ElapsedTicks / (Stopwatch.Frequency / (1000.0));
       ViewBag.cacheMsg = msg + ms.ToString() +
           ” PID: ” + Process.GetCurrentProcess().Id.ToString();
    } 
    
    void StopWatchMiss(Stopwatch sw)
    {
       StopWatchEnd(sw, “Miss – MS:”);
    } 
    
    void StopWatchHit(Stopwatch sw)
    {
       StopWatchEnd(sw, “Hit – MS:”);
    }

    另外,在 Views\Shared\_Layout.cshtml 檔案中加入 ViewBag.cacheMsg 的程式碼,這是為了在頁面上顯示快取的資訊。

    <div class="container body-content">
      @RenderBody()
      <hr />
      <footer>
        <h2>@ViewBag.cacheMsg</h2>
      </footer>
    </div>
    
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
    </body>
    </html>

    現在你可以在開發環境中測試快取的效果了,不過如果你的資料庫很小,而快取服務又是在雲端,效果可能不會太明顯,部署到 Azure 上應該就可以感受到明顯的差異。

    在管理介面中監控快取服務

    在管理介面中,你可以看到快取服務的 hit/miss 的統計資料:

    你可以加上其它的相關資訊來監控,像是自訂時間範圍、被清除的鍵值、過期的鍵值、使用的 CPU 或記憶體等。

    當然,你也可以加入警告通知(Add Alert)來幫助你監控快取服務的使用狀況,像是下圖我就加入了一個警告通知,在 15 分鐘內,清除的鍵值過多時,可能要使用更大的快取。

    從 Visual Studio 將網站部署到 Azure 是十分容易的,只要在 Web 專案上按右鍵,選擇發行就可以了,再次提醒,網站服務跟快取服務一定要在同一個資料中心,否則網路傳輸的延遲會拖垮快取的效能。而在發行時別忘了勾選 Execute Code First Migrations

    部署完成後,你就可以試試看有沒有快取的效能是不是有明顯的差異。

    壓力測試快取服務

    預設的快取操作時間是 1000ms (1秒),你可以試著用下面這段程式碼,將 time out (強制清除快取)改成更長或更短的時間,測試你的快取服務是否正常。當 #define NotTestingTimeOut 這段程式碼被註解掉時,timeout 便會被設定為 150ms,讓快取在很短的時間被清掉。

    #else
          #region StressTest
          private static ConnectionMultiplexer Connection
          {
    
             get
             {
                if (connection != null && connection.IsConnected)
                {
                   return connection;
                }
                var config = new ConfigurationOptions();
                config.EndPoints.Add(Keys.URL);
                config.Password = Keys.passwd;
                config.Ssl = true;
                config.SyncTimeout = 150;
    
                connection = ConnectionMultiplexer.Connect(config);
                return connection;
             }
          }
          #endregion
    #endif

    在壓力測試時,最好將 session 快取關閉,簡單的作法是到 web.config 檔案中把整個應用程式的 session 快取關閉。

    <sessionState mode="Off" />

    或是在你的 controller 中使用 [SessionState(SessionStateBehavior.Disabled)] 來做。以下這個更新後的 getMovie 方法可以運作得更穩定,因為當 time out 例外發生時會重試 3 次。

          Movie getMovie(int id, int retryAttempts = 0)
          {
             IDatabase cache = Connection.GetDatabase();
             if (retryAttempts > 3)
             {
                string error = "getMovie timeout with " + retryAttempts.ToString()
                   + " retry attempts. Movie ID = " + id.ToString();
                Logger(error);
    
                ViewBag.cacheMsg = error + " Fetch from DB";
                // Cache unavailable, get data from DB
                return db.Movies.Find(id);
             }
             Stopwatch sw = Stopwatch.StartNew();
             Movie m;
    
             try
             {
                m = (Movie)cache.Get(id.ToString());
             }
    
             catch (TimeoutException tx)
             {
                Logger("getMovie fail, ID = " + id.ToString(), tx);
                return getMovie(id, ++retryAttempts);
             }
    
             if (m == null)
             {
                Movie movie = db.Movies.Find(id);
                cache.Set(id.ToString(), movie);
                StopWatchMiss(sw);
                return movie;
             }
             StopWatchHit(sw);
    
             return m;
          }

    這個範例程式中還��很多方法可以測試快取服務。

    像 WriteCache 或 ReadCache 方法會預設寫入或讀取 1,000 筆資料,你可以在 URL 後加上 "/n" 讓它們變成讀寫 n * 1000 筆的資料,像上圖的例子 http://<your site>.azurewebsites.net/Movies/ReadCache/3 就會讀取 3,000 筆快取的資料。

    在這個 150ms timeout 的環境下,我的程式就會很容易碰到 timeout 的狀況而去存取資料庫,這是因為我的程式有正確處理這個 timeout 的例外才能順利去讀取資料庫。所以建議您上線的應用程式也要能處理好這個例外,因為根據雲端平台的服務水準,如果您選擇的是基本方案,那一個月中可能會有幾分鐘無法存取(比如正在更新 VM),除非選擇了標準方案,並且建立好 master-slave 的架構備援,不過還是建議您在程式碼中預先處理這個可能發生的例外狀況。

    Azure Redis Cache (Preview) ASP.NET Session State Provider

    ASP.NET 預設的 In-memory Session State Provider 無法同時被多個網站實體使用,而 SQL Server session state 雖然可以同時讓不同的網站使用相同的 session state,但這會受限於資料庫查詢的延遲時間,進而影響效能。而 Redis session state cache provider 則是另一個選擇,如果你的網站只會用到不是很大的 session state,則可以利用 Redis Cache 來快取這些 session state data。

    您可以參考這篇文章,在你的網站應用程式中加入 RedisSessionStateProvider,然後修改 Web.config 檔案來設定 Redis Cache 服務:

     <system.web>
         <customErrors mode="Off" />
         <!--<sessionState mode="Off" />-->
        <authentication mode="None" />
        <compilation debug="true" targetFramework="4.5" />
        <httpRuntime targetFramework="4.5" />
      <sessionState mode="Custom" customProvider="RedisSessionProvider">
                   <add name="RedisSessionProvider" 
                  type="Microsoft.Web.Redis.RedisSessionStateProvider" 
                  port="6380"
                  host="movie2.redis.cache.windows.net" 
                  accessKey="m7PNV60CrvKpLqMUxosC3dSe6kx9nQ6jP5del8TmADk=" 
                  ssl="true" />
          <!--<add name="MySessionStateStore" type="Microsoft.Web.Redis.RedisSessionStateProvider" host="127.0.0.1" accessKey="" ssl="false" />-->
          </providers>
        </sessionState>
      </system.web>
      <system.webServer>

    如此一來你就可以在你的網站應用程式中使用 Redis Cache 來處理 session state。在範例程式中也提供了測試的方法,你可以透過 http://<your site>.azurewebsites.net/SessionTest/WriteSession/Hello_joe 來存取,如此便會將 "Hello_joe" 寫入 session state,你可以試著增加網站服務實體,看看這個 session state 是否會在多個實體間共用。

    如果你對於 Redis Cache 有任何問題,可以在 twitter 上跟隨 @RickAndMSFT 並且向他發問。

    參考資料

  • Microsoft Azure 與 OpenSSL 資安漏洞

    自 2014 年4月7日 OpenSSL 代號 Heartbleed (CVE-2014-0160) 資安漏洞公告後,陸續又發布了數項其它資安漏洞 (http://www.openssl.org/news/vulnerabilities.html),許多客戶都欲了解這幾次資安漏洞是否會影響微軟的產品,特別是 Microsoft Azure。Microsoft Azure 與大多數 Microsoft 服務是使用微軟公司實作之 Windows SSL/TLS,因此不會受到 OpenSSL 相關資安漏洞的影響。Microsoft Azure Websites 以及 Microsoft Azure Cloud Services Web Role 都未使用 OpenSSL 來實作 SSL 協定,微軟公司使用自己實作之加密元件 Secure Channel (常縮寫為 SChannel) 來支援 SSL 所需的加解密機制。然而 Microsoft Azure Virtual Machines 的用戶可以選擇多種版本之 Linux 作業系統,多數 Linux 作業系統採用 OpenSSL 來提供加密功能,由於 Microsoft Azure Virtual Machines 用戶需要自行維護作業系統相關修補,請用戶關注各 Linux distribution 所發布之相關資安訊息。

  • Azure Traffic Manager 偵測端點 (Endpoint) 之機制

    Microsoft Azure Traffic Manager 可以提供用戶跨資料中心之負載平衡或失敗回復 (Failover),用戶經常詢問 Azure Traffic Manager 失敗回復時偵測端點 (End Point) 的運作方式,到底所設定的主要資料中心端點失效多久後才會切換至備援的資料中心端點? DNS TTL (Time-To-Live) 時間設定成很短有助於縮短停機時間嗎?

    image

    針對此類問題 http://msdn.microsoft.com/en-us/library/azure/dn339010.aspx 提供了詳細描述,在此簡述如後。Microsoft Azure Traffic Manager 偵測端點的步驟如下圖:

    IC697947

    1. GET – 依據用戶的設定,Azure Traffic Manager 偵測系統會發出 HTTP GET 去偵測端點。
    2. 200 OK – 倘若 10 秒鐘內偵測系統收到 HTTP 200 OK 之訊息回覆,代表此一 Azure Cloud Services 或 Azure Websites 端點運作正常。只要是回傳非 HTTP 200 OK 之訊息或超過 10 秒未回應都視為端點偵測到問題。
    3. 間隔 30 秒檢查一次 – 偵測系統的檢查動作每 30 秒會執行一次。
    4. 系統發生問題,端點無回應 – 倘若在 30 秒偵測間隔中端點發生問題,Azure Traffic Manager 是無法得知的,必須等到下一次偵測檢查才會發現。
    5. 確認端點出現問題 (4 次嘗試) – 當 Azure Traffic Manager 偵測系統發送 HTTP GET 而超過 10 秒未獲回應,偵測系統將持續以 30 秒為間格偵測三次,確認端點是否真的停止運作,這意味著 Azure Traffic Manager 至少要花費 1.5 分鐘來確認端點真的已經停止運作, 在連續三次的偵測中若有一次順利在 10 秒內收到 HTTP 200 OK 回應,就視為監測端點已經恢復正常。
    6. 標註端點發生問題 – 當連續四次偵測回應都失敗後,Azure Traffic Manager 偵測系統會標註該端點失效。Azure Traffic Manager 對外服務的 domain name 開始啟用備援資料中的端點取代原本這個已經無法運作的端點。
    7. 存取出問題端點的用戶流量開始降低 – 終端用戶的流量仍持續前往這個已經無法運作的端點,終端用戶開始感受此服務無法正常運作,由於終端用戶端與輔助 DNS Server 已經緩存 (cached) 了這個無法運作的端點 IP Address,隨著時間推進,DNS Server 逐步更新 domain name 的 IP Address,繼續流向這個無法運作端點的流量會逐漸減少。而 Azure Traffic Manager 監測系統繼續以 30 秒為間隔進行檢查。
    8. 出問題端點已經沒有用戶流量 – Azure Traffic Manager DNS TTL 預設時間是 5 分鐘 (300 秒),因次當超過 DNS TTL 時間後,所有用戶會取得服務端點的新 IP Address,也就是備援資料中心的端點。原本出問題的資料中心端點已經沒有來自終端用戶的流量了。 而 Azure Traffic Manager 監測系統繼續以 30 秒為間隔進行檢查。
    9. 出問題端點已經恢復正常,用戶流量開始逐漸增加 – 原本出問題的端點可能在這個時間點恢復正常,但是 Azure Traffic Manager 仍不知道,要等到每 30 秒偵測程序完成後,才會標註系統恢復正常。
    10. 用戶流量回到原本資料中心的端點 - 當 Azure Traffic Manager 送出 HTTP GET 並在 10 秒內收到 200 OK 回應後,開始將對外端點切回原本的 DNS name ,當 DNS TTL 時間過後,所有用戶 DNS 緩存內容都會更新回原本主要資料中心的 IP Address, 所有用戶的流量會回到原本的資料中心。

    了解 Microsoft Azure Traffic Manager 失敗回復 (Failover) 偵測機制之後,我們可以知道利用此失敗回復的機制而主要資料中心發生問題時,如果使用 DNS TTL 300 秒的預設值,終端用戶至多會感受到 5 分鐘的停機時間,因為 DNS 會緩存已經出問題的 IP Address 直到 DNS TTL 時間超過,而縮短 DNS TTL 時間到 1.5 分鐘以內的意義並不大,因為 Azure Traffic Manager 需要至少 1.5 分鐘的時間連續三次 30 秒間隔的偵測動作來確認端點失效,DNS TTL 過短也會讓 DNS 查詢變得頻繁,而影響系統回應時間。

  • Azure HDInsight 公開預覽 HBase 叢集做為 Azure Blobs 上的 NoSQL 資料庫

    原文發表於 Azure HDInsight previewing HBase clusters as a NoSQL database on Azure Blobs

    2014 年 6 月 3 日,Azure HDInsight 公開了一項更新消息,Azure HDInsight 將支援 Hadoop 2.4,並提升查詢資料的效能 100 倍。而今天,我們宣佈在 HDInsight 產品中,開始預覽 Apache HBase 叢集(cluster)。

    HBase 是一個低延遲的 NoSQL 資料庫,適合用來做大數據的線上交易處理(OLTP, online transactional processing)。我們在 Azure 中提供 HBase 叢集,而叢集的資料直接儲存於 Azure Blob 之中,如此一來便能在成本及效能的考量上,取得最大的存取效能以及擴充彈性。這個產品讓我們的客戶能擁有一個大型資料庫來建立高互動性的網站、或是儲存從百萬個感測器回傳的遙測資料等等,並且能在 Hadoop 中分析這些資料。

    如何建立 HBase 叢集

    由於目前 Azure HDInsight 上的 HBase 還在預覽階段,所以操作上需要利用 PowerShell。

    1. 安裝為 Azure 環境優化的 PowerShell
    2. 按照文章步驟設定環境
    3. 利用下列指令將認證資料存在變數中:
      PS C:\> $creds = Get-Credential
    4. 建立 HBase 叢集(可根據需求修改資料中心位置,以及修改 Blob 帳號的資料):
      PS C:\> New-AzureHDInsightCluster -Name yourclustername -ClusterType HBase -Version 3.0 -Location "West US" -DefaultStorageAccountName yourstorageaccount.blob.core.windows.net -DefaultStorageAccountKey "yourstorageaccountkey" -DefaultStorageContainerName hbasecontainername -Credential $creds -ClusterSizeInNodes 4

    在 HBase 叢集中操作資料

    應用程式開發人員可以透過 REST APIs 、HBase shell 或是不同的 map/reduce 工具如 Hive 及 Pig 來存取 HBase 的資料,HBase shell 提供了一個互動式的主控台(console)讓您可以管理 HBase 叢集、建立或刪除資料表(table)、以及操作資料。

    1. 要使用 HBase shell,首先您必須開啟建立的 HBase 叢集的遠端桌面連線(RDP)來連上它。
      在叢集建立完成時,你可以在 Azure 管理介面上設定(CONFIGURATION)的頁面中,在最下方按下 ENABLE REMOTE 的按鈕來開啟 RDP,最後可以利用 CONNECT 按鈕來進行連線。
    2. 連結進叢集後,按下桌面上 Hadoop command prompt的捷徑,然後輸入下列指令來開啟 HBase shell:
      cd %HBASE_HOME%\bin
      hbase shell
    3. 下面的指令則是建立一個範例資料表,並且加入一列資料、以及列出資料表的所有資料:
      create ‘sampletable’, ‘cf1′
      put ‘sampletable’, ‘row1′, ‘cf1:col1′, ‘value1′
      scan ‘sampletable’

    如果您想瞭解更多關於 Azure HDInsight 上的 HBase,下面是一些參考資料:

  • Azure 網站服務已經搭載下一代加密技術

    原文發表於 Next Generation Cryptography now available with Azure Web Sites

    沒有什麼事情,比使用 SSL 來提升您網站的安全性來得更重要了,然而,即使加密不是什麼新的技術,密碼學家仍然不斷地尋找能讓加密技術更安全的作法。而我們 Azure 網站服務團隊也一直不斷地支援最新最安全的加密運算,今天要跟各位介紹我們已經支援很棒、使用橢圓曲線加密法(ECC, Elliptic Curve Cryptography)的憑證。

    橢圓曲線加密技術,是基於在有限場(finite field)中橢圓曲線的代數結構,聽不懂了嗎?別擔心,很多人都聽不懂這玩意兒。要畫出橢圓曲線的圖,都是根據 y2 = x3 + ax + b 這個代數方程式,當你將 X 及 Y 對應的值畫出來時,你會發現它是一個對稱的橢圓曲線,舉例來說,若是畫出 y2=4x3-4x+4 這個代數方程式,就會像下面這張圖一樣:

    為了不讓這篇文章變成艱深的數學課,我們簡單地說,當一個代數方程式有這樣複雜且對稱性質,那就可以用來產生一個函數,有效地產生不容易攻破的公鑰(public key)及私鑰(private key)來做加解密。我們先產生一把私鑰,選定一個橢圓曲線來產生出對應的公鑰,要從公鑰反推回私鑰(也就是我們俗稱的攻擊、破解)是出了名地難,而且運用當今科技也無法在合理的時間內破解成功,這個方式相較於現在常見的一些公鑰私鑰加解密系統大概可以提升 10 倍以上的難度(確定你真的想瞭解為什麼,再讀這篇文章),換句話說,使用 256 位元的橢圓曲線加密法,它的安全指數相當於使用 2600 位元的 RSA 加密法,而現今業界常使用的標準不過是 2048 位元的 RSA 加密法,這已經是很難攻破的加密法了,由此可知,若使用相同位元的橢圓曲線加密法,要用現在的超級電腦破解它簡直是天方夜譚。

    說了這麼多,我們只是想讓您知道,現在您不只可以購買市面上常見的 SSL 憑證,還可以購買更安全、使用 ECC 的 SSL 憑證來確保您網站服務的安全性。ECC 憑證目前還是一項很新的技術,市場上還沒有很多家業者有提供,不過若您有需要,現在可以向 Symantec 或是 Entrust 來購買,不過相信很快很多憑證業者就會跟進了。

    你知道嗎?Azure 網站服務最近已經通過 ECC 憑證的測試了!所以您也可以在 Azure 網站服務上安裝 ECC 憑證,就跟過去安裝憑證一樣,上傳您購買的 .pfx 憑證檔案就可以立即���作了!不過要注意的是,不是所有的用戶端都支援 ECC 憑證,但如果您是使用 Windows Vista 之後的電腦,那就可以不必經過特殊設定,也能使用瀏覽器來瀏覽使用 ECC 憑證做 SSL 加密的網站!