練習 - 將應用程式連線至快取

已完成

既然我們已在 Azure 中建立 Redis 快取,讓我們建立應用程式來使用它。 請確定您有 Azure 入口網站的連接字串資訊。

注意

在右側有可用的整合式 Cloud Shell。 您可以使用該命令提示字元,以建立及執行我們在這裡建置的範例程式碼,或者,如果您已設定 .NET Core 開發環境,也可以在本機執行這些步驟。

建立主控台應用程式

我們會使用簡單的主控台應用程式,以便專注於 Redis 實作。

  1. 在 Cloud Shell 中,建立新的 .NET Core 主控台應用程式,將其命名為 SportsStatsTracker

    dotnet new console --name SportsStatsTracker
    
  2. 將目前的目錄變更為新專案的資料夾。

    cd SportsStatsTracker
    

新增連接字串

將從 Azure 入口網站取得的連接字串新增至程式碼。 請勿將此類認證儲存在原始程式碼中。 為了讓此範例保持簡單,我們要使用組態檔。 對於 Azure 中的伺服器端應用程式,更理想的方法是使用 Azure Key Vault 與憑證。

  1. 建立新的 appsettings.json 檔案以新增至專案。

    touch appsettings.json
    
  2. 透過在專案資料夾中輸入 code . 以開啟程式碼編輯器。 如果您是在本機工作,我們建議您使用 Visual Studio Code。 這裡的步驟大致上與其使用方式一致。

  3. 在編輯器中選取 appsettings.json 檔案,然後新增下列文字。 將您的連接字串貼到設定的 [值] 中。

    {
      "CacheConnection": "[value-goes-here]"
    }
    
  4. 儲存變更。

    重要

    每當將程式碼貼上或變更為編輯器中的檔案時,請務必使用 [...] 功能表或快速鍵儲存 (Ctrl + S 適用於Windows 與 Linux、Cmd + S 適用於 macOS )。

  5. 在編輯器中選取 SportsStatsTracker.csproj 檔案將其開啟。

  6. 請將下列 <ItemGroup> 設定區塊新增到根 <Project> 元素,以在專案中包含新檔案,並將其複製到輸出資料夾。 這會確保應用程式組態檔在編譯/建置應用程式時,位於輸出目錄中。

    <Project Sdk="Microsoft.NET.Sdk">
       ...
        <ItemGroup>
            <None Update="appsettings.json">
              <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
            </None>
        </ItemGroup>
    </Project>
    
  7. 儲存檔案。 (請確定您執行了這項操作,否則當您在稍後新增套件時,將會遺失變更!)

新增 JSON 組態檔讀取支援

.NET Core 應用程式需要其他 NuGet 套件,才能讀取 JSON 組態檔。

在視窗的命令提示字元區段中,將參考 Microsoft.Extensions.Configuration.Json 新增至 NuGet 套件。

 dotnet add package Microsoft.Extensions.Configuration.Json

新增程式碼來讀取組態檔

既然我們已新增必要程式庫來啟用讀取組態,因此需要在主控台應用程式內啟用該功能。

  1. 在編輯器中選取 Program.cs

  2. 在檔案頂端,有一行 using System。 請在該行下方,新增下列幾行程式碼。

     using Microsoft.Extensions.Configuration;
     using System.IO;
    
  3. Main 方法的內容取代為下列程式碼。 此程式碼會初始化設定系統,以從 appsettings.json 檔案進行讀取。

    var config = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json")
        .Build();
    

Program.cs 檔案現在看起來應該如以下範例所示。

 using System;
 using Microsoft.Extensions.Configuration;
 using System.IO;

 namespace SportsStatsTracker
 {
     class Program
     {
         static void Main(string[] args)
         {
             var config = new ConfigurationBuilder()
                 .SetBasePath(Directory.GetCurrentDirectory())
                 .AddJsonFile("appsettings.json")
                 .Build();
         }
     }
 }

從組態取得連接字串

Program.cs 中於 Main 方法結尾,使用新的 config 變數擷取連接字串,並且儲存在名為 connectionString 的新變數中。

Config 變數具有索引子,您可以在其中傳入字串,以便從 appSettings.json 檔案進行擷取。

 string connectionString = config["CacheConnection"];

新增 Redis 快取 .NET 用戶端的支援

接下來,讓我們設定主控台應用程式,以使用適用於 .NET 的 StackExchange.Redis 用戶端。

  1. 使用 Cloud Shell 編輯器底部的命令提示字元,將 StackExchange.Redis NuGet 套件新增至專案。

     dotnet add package StackExchange.Redis
    
  2. 在編輯器中選取 Program.cs,然後為命名空間 StackExchange.Redis 新增 using

     using StackExchange.Redis;
    

安裝完成後,Redis 快取用戶端即可與專案搭配使用。

連線到快取

讓我們新增程式碼以連線到快取。

  1. 在編輯器中選取 Program.cs

  2. 藉由將連接字串傳遞給 ConnectionMultiplexer.Connect,使用它來建立 ConnectionMultiplexer。 將傳回值命名為 快取

  3. 因為建立的連線是「可處置」的,請將其包裝於 using 區塊中。 您的程式碼應該看似如下。

     string connectionString = config["CacheConnection"];
    
     using (var cache = ConnectionMultiplexer.Connect(connectionString))
     {
    
     }
    

注意

與 Azure Cache for Redis 的連線會由 ConnectionMultiplexer 類別所管理。 整個用戶端應用程式中都應該共用和重複使用此類別。 我們 想要對每個作業建立新連線。 相反地,我們想要將其儲存為類別中的欄位,對每個作業重複使用。 這裡我們只會在 Main 方法中使用該連線,但在生產應用程式中,該連線應該儲存於類別欄位或 singleton 中。

將值新增至快取

既然我們已經有連線,讓我們將值新增至快取。

  1. 在建立連線之後,請於 using 區塊內,使用 GetDatabase 方法來擷取 IDatabase 執行個體。

     IDatabase db = cache.GetDatabase();
    
  2. 呼叫 IDatabase 物件上的 StringSet,將金鑰 "test:key" 設為值 "some value"。

StringSet 的傳回值是 bool,會指出是否已新增金鑰。

  1. StringSet 的傳回值顯示在主控台上。

     bool setValue = db.StringSet("test:key", "some value");
     Console.WriteLine($"SET: {setValue}");
    

從快取中取得值

  1. 接下來,請使用 StringGet 來擷取值。 這樣會採用金鑰來擷取並傳回值。

  2. 輸出傳回值。

     string getValue = db.StringGet("test:key");
     Console.WriteLine($"GET: {getValue}");
    
  3. 您的程式碼應該如下所示。

     using System;
     using Microsoft.Extensions.Configuration;
     using System.IO;
     using StackExchange.Redis;
    
     namespace SportsStatsTracker
     {
         class Program
         {
             static void Main(string[] args)
             {
                 var config = new ConfigurationBuilder()
                     .SetBasePath(Directory.GetCurrentDirectory())
                     .AddJsonFile("appsettings.json")
                     .Build();
    
                 string connectionString = config["CacheConnection"];
    
                 using (var cache = ConnectionMultiplexer.Connect(connectionString))
                 {
                     IDatabase db = cache.GetDatabase();
    
                     bool setValue = db.StringSet("test:key", "some value");
                     Console.WriteLine($"SET: {setValue}");
    
                     string getValue = db.StringGet("test:key");
                     Console.WriteLine($"GET: {getValue}");
                 }
             }
         }
     }
    
  4. 請執行應用程式以查看結果。 將 dotnet run 輸入編輯器下方的終端機視窗。 請確定您是在專案資料夾中,否則它會找不到您的程式碼來進行建置及執行。

     dotnet run
    

提示

若程式未執行您想要的動作而是進行編譯,可能是因為您還沒有在編輯器中儲存變更。 在終端機與編輯器視窗之間切換之前,請務必先儲存變更。

使用方法的非同步版本

我們已經能夠從快取中取得和設定值,但是我們使用的是較舊的同步版本。 在伺服器端應用程式中,這不是對於執行緒的有效率使用方式。 相反地,我們想要使用方法的 非同步 版本。 您可以輕鬆地找出它們 - 它們的結尾都是 Async

若要讓這些方法方便使用,我們可以使用 C# asyncawait 關鍵字。 不過,我們必須 至少 使用 C# 7.1,才能將這些關鍵字套用至我們的 Main 方法。

切換至 C# 7.1

在 C# 7.1 以前,C# 的 asyncawait 關鍵字不是 Main 方法中的有效關鍵字。 我們可以透過 .csproj 檔案中的旗標切換至該編譯器。

  1. 在編輯器中開啟 SportsStatsTracker.csproj 檔案。

  2. <LangVersion>7.1</LangVersion> 新增至組建檔案中的第一個 PropertyGroup。 完成時,看起來應該如下所示。

     <Project Sdk="Microsoft.NET.Sdk">
    
       <PropertyGroup>
         <OutputType>Exe</OutputType>
         <TargetFramework>netcoreapp2.0</TargetFramework>
         <LangVersion>7.1</LangVersion> 
       </PropertyGroup>
     ...
    

套用非同步關鍵字

接下來,請將 async 關鍵字套用至 Main 方法。 我們必須執行三件事。

  1. async 關鍵字新增至 Main 方法簽章。

  2. 將傳回型別從 void 變更為 Task

  3. 新增 using 陳述式以包含 System.Threading.Tasks

     using System;
     using Microsoft.Extensions.Configuration;
     using System.IO;
     using StackExchange.Redis;
     using System.Threading.Tasks;
    
     namespace SportsStatsTracker
     {
         class Program
         {
             static async Task Main(string[] args)
             {
             ...
    

以非同步的方式取得及設定值

我們可以保留同步方法, 並新增對 StringSetAsyncStringGetAsync 方法的呼叫以將另一個值新增到快取。 將 "counter" 設定為 "100" 的值。

  1. 使用 StringSetAsyncStringGetAsync 方法來設定及擷取名為 "counter" 的金鑰。 將值設定為 "100"。

  2. 套用 await 關鍵字,以從每個方法取得結果。

  3. 將結果輸出至主控台視窗,就像您對於同步版本所執行的操作一樣。

     // Simple get and put of integral data types into the cache
     setValue = await db.StringSetAsync("test", "100");
     Console.WriteLine($"SET: {setValue}");
    
     getValue = await db.StringGetAsync("test");
     Console.WriteLine($"GET: {getValue}");
    
  4. 再次執行應用程式。 應用程式應該仍可運作,並且現在有兩個值。

遞增值

  1. 使用 StringIncrementAsync 方法以遞增您的 計數器 值。 傳遞數字 50 以新增至計數器:

    • 請注意,此方法會採用金鑰 以及longdouble

    • 根據傳遞的參數,它會傳回 longdouble

  2. 將方法的結果輸出至主控台。

    long newValue = await db.StringIncrementAsync("counter", 50);
    Console.WriteLine($"INCR new value = {newValue}");
    

其他作業

最後,讓我們使用 ExecuteAsync 支援,嘗試執行一些額外的方法。

  1. 執行 "PING" 來測試伺服器連線。 它應該會回應 "PONG"。

  2. 執行 "FLUSHDB" 以清除資料庫值。 它應該會回應 "OK"。

     var result = await db.ExecuteAsync("ping");
     Console.WriteLine($"PING = {result.Type} : {result}");
    
     result = await db.ExecuteAsync("flushdb");
     Console.WriteLine($"FLUSHDB = {result.Type} : {result}");
    

最終程式碼應該看似如下。

 using System;
 using Microsoft.Extensions.Configuration;
 using System.IO;
 using StackExchange.Redis;
 using System.Threading.Tasks;

 namespace SportsStatsTracker
 {
     class Program
     {
         static async Task Main(string[] args)
         {
             var config = new ConfigurationBuilder()
                 .SetBasePath(Directory.GetCurrentDirectory())
                 .AddJsonFile("appsettings.json")
                 .Build();

             string connectionString = config["CacheConnection"];

             using (var cache = ConnectionMultiplexer.Connect(connectionString))
             {
                 IDatabase db = cache.GetDatabase();

                 bool setValue = db.StringSet("test:key", "some value");
                 Console.WriteLine($"SET: {setValue}");

                 string getValue = db.StringGet("test:key");
                 Console.WriteLine($"GET: {getValue}");

                 setValue = await db.StringSetAsync("test", "100");
                 Console.WriteLine($"SET: {setValue}");

                 getValue = await db.StringGetAsync("test");
                 Console.WriteLine($"GET: {getValue}");

                 var result = await db.ExecuteAsync("ping");
                 Console.WriteLine($"PING = {result.Type} : {result}");

                 result = await db.ExecuteAsync("flushdb");
                 Console.WriteLine($"FLUSHDB = {result.Type} : {result}");
             }
         }
     }
 }

挑戰

挑戰嘗試將物件類型序列化至快取。 以下是基本步驟。

  1. 建立新的 class 與一些公用屬性。 您可以發明自己的項目 ("Person" 或 "Car" 很熱門),或者使用上一個單元提供的 "GameStats" 範例。

  2. 使用 dotnet add package 來新增 Newtonsoft.Json NuGet 套件的支援。

  3. Newtonsoft.Json 命名空間新增 using

  4. 建立其中一個物件。

  5. 使用 JsonConvert.SerializeObject 將該物件序列化,然後使用 StringSetAsync 將其推送至快取。

  6. 使用 StringGetAsync 從快取中取回該物件,然後使用 JsonConvert.DeserializeObject<T> 將它還原序列化。

既然我們已在 Azure 中建立 Redis 快取,讓我們建立應用程式來使用它。 請確定您有 Azure 入口網站的連線資訊。

注意

在右側有可用的整合式 Cloud Shell。 您可以使用該命令提示字元來建立及執行我們在這裡建置的範例程式碼;若您已設定 Node.js 開發環境,也可以在本機執行這些步驟。

建立主控台應用程式

我們會使用簡單的主控台應用程式,以便專注於 Redis 實作。

  1. 在 Cloud Shell 中,建立名為 redisapp 的新目錄並在該處將新的 Node.js 應用程式初始化。

     mkdir redisapp
     cd redisapp
     npm init -y
     touch app.js
    
  2. 我們的應用程式將使用下列 npm 套件:

    • redis:用於連線到 redis 的最常用 JavaScript 套件。
    • bluebird:用來將封裝中的回呼樣式方法轉換 redis 成可等候保證。
    • dotenv:從 .env 檔案載入環境變數,那是我們將儲存 Redis 連線資訊的地方。

    現在讓我們安裝它們。 執行此命令以將這些套件新增到我們的應用程式。

     npm install redis bluebird dotenv
    

新增設定

讓我們將從 Azure 入口網站取得的連線資訊新增到 .env 設定檔。

  1. 在專案中建立新 .env 檔案。

     touch .env
    
  2. 透過在專案資料夾中輸入 code . 以開啟程式碼編輯器。 如果您是在本機工作,我們建議您使用 Visual Studio Code。 這裡的步驟大致上與其使用方式一致。

  3. 在編輯器中選取 .env 檔案,然後貼上下列文字。

     REDISHOSTNAME=
     REDISKEY=
     REDISPORT=
    
  4. 在每個對應行的等號後面貼上主機名稱、主要金鑰與連接埠。 完成的檔案看起來如下列範例所示。

     REDISHOSTNAME=myredishost.redis.cache.windows.net
     REDISKEY=K21mLSMN++z8d1FvIeMGy3VOAgoOmqaNYCqeE44eMDc=
     REDISPORT=6380
    
  5. 使用 Ctrl+S (Windows 與 Linux) 或 Cmd+S (macOS)儲存檔案。

設定實作

現在可以為我們的應用程式撰寫程式碼了。

  1. 在編輯器中選取 app.js

  2. 首先,新增我們的 require 陳述式。 在檔案頂端貼上下列程式碼。

     var Promise = require("bluebird");
     var redis = require("redis");
    
  3. 接下來,我們將載入我們的設定 .env ,並使用 bluebird 的函式將 promisifyAll redis 封裝的函式和方法轉換為可等候承諾。 貼入下列程式碼。

     require("dotenv").config();
     Promise.promisifyAll(redis);
    
  4. 現在我們會將 Redis 用戶端初始化。 貼上先前單元中的未定案程式碼 (使用 process.env 存取我們的主機名稱、連接埠與金鑰) 以建立用戶端。

     const client = redis.createClient(
       process.env.REDISPORT,
       process.env.REDISHOSTNAME,
       {
         password: process.env.REDISKEY,
         tls: { servername: process.env.REDISHOSTNAME }
       }
     );
    

使用用戶端來使用快取

我們可以開始撰寫程式碼以與我們的 Redis 快取互動了。

  1. 首先,在檔案底部新增 async 函式包裝函式以包含我們的主程式碼。 我們需要這樣做才能 await 我們正在使用的非同步函式呼叫。 您將在此單元中新增之程式碼的其餘部分將在此包裝函式中。

     (async () => {
    
       // The rest of the code you'll paste in goes here.
    
     })();
    
  2. 使用 setAsync 方法新增值到快取,並使用 getAsync 讀回該值。

     console.log("Adding value to the cache");
     await client.setAsync("myKey", "myValue");
    
     console.log("Reading value back:");
     console.log(await client.getAsync("myKey"));
    
  3. 使用 pingAsync 傳送 ping 到快取。

     console.log("Pinging the cache");
     console.log(await client.pingAsync());
    
  4. 使用 flushdbAsync 刪除快取中的所有金鑰。

     await client.flushdbAsync();
    
  5. 最後,使用 quitAsync 關閉連線。

     await client.quitAsync();
    
  6. 儲存檔案。 已完成的應用程式應該看似如下。

     var Promise = require("bluebird");
     var redis = require("redis");
    
     require("dotenv").config();
    
     Promise.promisifyAll(redis);
    
     const client = redis.createClient(
     process.env.REDISPORT,
     process.env.REDISHOSTNAME,
     {
       password: process.env.REDISKEY,
       tls: { servername: process.env.REDISHOSTNAME }
     }
     );
    
     (async () => {
       console.log("Adding value to the cache");
       await client.setAsync("myKey", "myValue");
       console.log("Reading value back:");
       console.log(await client.getAsync("myKey"));
       console.log("Pinging the cache");
       console.log(await client.pingAsync());
       await client.flushdbAsync();
       await client.quitAsync();
     })();
    
  7. 執行應用程式。 在 Cloud Shell 中,執行下列命令。

     node app.js
    

    您會看見下列結果。

     Adding value to the cache
     Reading value back:
     myValue
     Pinging the cache
     PONG