Rychlý start: Použití Azure Cache for Redis v .NET Framework

V tomto rychlém startu začleníte Azure Cache for Redis do .NET Framework, abyste měli přístup k zabezpečené vyhrazené mezipaměti, která je přístupná z libovolné aplikace v Azure. Konkrétně používáte klienta StackExchange.Redis s kódem jazyka C# v konzolové aplikaci .NET.

Přeskočte na kód na GitHub

Pokud chcete přeskočit přímo na kód, podívejte se na rychlý .NET Framework start na GitHub.

Požadavky

Vytvoření mezipaměti

  1. Pokud chcete vytvořit mezipaměť, přihlaste se k Azure Portal a vyberte Vytvořit prostředek.

    V levém navigačním podokně je zvýrazněná možnost Vytvořit prostředek.

  2. Na stránce Nový vyberte Databáze a pak vyberte Azure Cache for Redis.

    V okně Nový je zvýrazněná možnost Databáze a Azure Cache for Redis je zvýrazněná.

  3. Na stránce Nový Redis Cache nakonfigurujte nastavení nové mezipaměti.

    Nastavení Volba hodnoty Popis
    Předplatné Rozevírací seznam a vyberte své předplatné. Předplatné, ve kterém chcete vytvořit tuto novou Azure Cache for Redis instance.
    Skupina prostředků Rozevírací seznam a vyberte skupinu prostředků nebo vyberte Vytvořit novou a zadejte název nové skupiny prostředků. Název skupiny prostředků, ve které chcete vytvořit mezipaměť a další prostředky. Když všechny prostředky aplikace dáte do jedné skupiny prostředků, můžete je snadno spravovat nebo odstraňovat společně.
    Název DNS Zadejte jedinečný název. Název mezipaměti musí být řetězec o 1 až 63 znacích, který obsahuje pouze číslice, písmena nebo pomlčky. Název musí začínět a končovat číslem nebo písmenem a nesmí obsahovat po sobě jdoucí pomlčky. Název hostitele instance mezipaměti bude <DNS name> .redis.cache.windows.net.
    Umístění Rozevírací seznam a vyberte umístění. Vyberte oblast blízko jiných služeb, které budou používat vaši mezipaměť.
    Typ mezipaměti Rozevírací seznam a vyberte vrstvu. Úroveň určuje velikost, výkon a funkce, které jsou pro mezipaměť k dispozici. Další informace najdete v tématu Azure Cache for Redis .
  4. Vyberte kartu Sítě nebo vyberte tlačítko Sítě v dolní části stránky.

  5. Na kartě Sítě vyberte metodu připojení.

  6. Vyberte kartu Další: Upřesnit nebo vyberte tlačítko Další: Upřesnit v dolní části stránky.

  7. Na kartě Upřesnit instance základní nebo standardní mezipaměti vyberte přepínač Povolit, pokud chcete povolit port, který není TLS. Můžete také vybrat, kterou verzi Redis chcete použít, a to buď 4, nebo 6.

    Redis verze 4 nebo 6.

  8. Na kartě Upřesnit pro instanci služby Premium Cache nakonfigurujte nastavení pro porty bez TLS, clustering a trvalost dat. Můžete také vybrat, kterou verzi Redis chcete použít, a to buď 4, nebo 6.

  9. Vyberte kartu Další: Značky nebo vyberte tlačítko Další: Značky v dolní části stránky.

  10. Volitelně můžete na kartě Značky zadat název a hodnotu, pokud chcete prostředek kategorizovat.

  11. Vyberte Zkontrolovat a vytvořit. Budete na kartě Zkontrolovat a vytvořit, kde Azure ověří vaši konfiguraci.

  12. Jakmile se zobrazí zelená zpráva Ověření bylo předáno, vyberte Vytvořit.

Vytvoření mezipaměti chvíli trvá. Průběh můžete sledovat na Azure Cache for Redis přehledu. Když se stav zobrazí jako Spuštěno, je mezipaměť připravená k použití.

Načtení názvu hostitele, portů a přístupových klíčů z Azure Portal

Pro připojení k instanci Azure Cache for Redis potřebují klienti mezipaměti název hostitele, porty a klíč pro mezipaměť. Někteří klienti mohou tyto položky označovat trochu odlišnými názvy. Název hostitele, porty a klíče můžete získat z Azure Portal .

  • Přístupové klíče získáte tak, že v levé navigaci v mezipaměti vyberete Přístupové klíče.

    Azure Cache for Redis klíče

  • Pokud chcete získat název hostitele a porty, v levém navigačním panelu mezipaměti vyberte Vlastnosti. Název hostitele má tvar <DNS name> .redis.cache.windows.net.

    Azure Cache for Redis vlastnosti

Vytvořte v počítači soubor s názvem TajnéKódyMezipaměti.config a uložte ho do umístění, které se nebude vracet se zdrojovým kódem této ukázkové aplikace. V tomto rychlém startu je soubor TajnéKódyMezipaměti.config umístěný zde: C:\AppSecrets\TajnéKódyMezipaměti.config.

Upravte soubor TajnéKódyMezipaměti.config a přidejte do něj následující obsah:

<appSettings>
    <add key="CacheConnection" value="<host-name>,abortConnect=false,ssl=true,allowAdmin=true,password=<access-key>"/>
</appSettings>

<host-name> nahraďte názvem hostitele mezipaměti.

<access-key> nahraďte primárním klíčem mezipaměti.

Vytvoření konzolové aplikace

V Visual Studio vyberte Soubor > Nový Project > .

Vyberte Konzolová aplikace (.NET Framework) a Další a nakonfigurujte aplikaci. Zadejte název Project, ověřte, že je vybraná možnost .NET Framework 4.6.1 nebo vyšší, a pak výběrem možnosti Vytvořit vytvořte novou konzolovou aplikaci.

Konfigurace klienta mezipaměti

V této části nakonfigurujete konzolovou aplikaci, aby pro .NET používala klienta StackExchange.Redis.

V Visual Studio vyberte Nástroje NuGet Správce balíčků Správce balíčků Console a v okně konzoly Správce balíčků > > spusťte následující příkaz.

Install-Package StackExchange.Redis

Po dokončení instalace budete moct se svým projektem používat klienta mezipaměti StackExchange.Redis.

Připojení k mezipaměti

V sadě Visual Studio otevřete soubor App.config a aktualizujte ho tak, aby obsahoval atribut appSettings file odkazující na soubor CacheSecrets.config.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>

    <appSettings file="C:\AppSecrets\CacheSecrets.config"></appSettings>
</configuration>

V Průzkumník řešení klikněte pravým tlačítkem na Odkazy a vyberte Přidat odkaz. Přidejte odkaz na sestavení System.Configuration.

Do souboru Program.cs přidejte následující příkazy using:

using StackExchange.Redis;
using System.Configuration;

Připojení k Azure Cache for Redis spravuje ConnectionMultiplexer třída . V klientské aplikaci byste měli tuto třídu sdílet a opakovaně používat. Nevytvářejte pro každou operaci nové připojení.

Neuchovávejte přihlašovací údaje ve zdrojovém kódu. Pro zjednodušení této ukázky používám pouze konfigurační soubor externích tajných kódů. Lepším přístupem může být použití řešení Azure Key Vault s certifikáty.

V souboru Program.cs přidejte k třídě Program konzolové aplikace následující členy:

private static Lazy<ConnectionMultiplexer> lazyConnection = CreateConnection();

public static ConnectionMultiplexer Connection
{
    get
    {
        return lazyConnection.Value;
    }
}

private static Lazy<ConnectionMultiplexer> CreateConnection()
{
    return new Lazy<ConnectionMultiplexer>(() =>
    {
        string cacheConnection = ConfigurationManager.AppSettings["CacheConnection"].ToString();
        return ConnectionMultiplexer.Connect(cacheConnection);
    });
}

Tento přístup ke sdílení instance ConnectionMultiplexer v aplikaci používá statickou vlastnost, která vrací připojenou instanci. Tento kód poskytuje způsob inicializace pouze jedné připojené instance ConnectionMultiplexer, který je bezpečný pro přístup z více vláken. abortConnect hodnota je nastavená na hodnotu false, což znamená, že volání bude úspěšné i v případě, že Azure Cache for Redis není navázáno. Klíčovou vlastností ConnectionMultiplexer je automatické obnovení připojení k mezipaměti po vyřešení problémů se sítí nebo jiných příčin.

Hodnota CacheConnection appSetting se používá k odkazování na připojovací řetězec mezipaměti z webu Azure Portal ve formě parametru hesla.

Zpracování redisConnectionException a SocketException opětovným připojením

Doporučeným postupem při volání metod na je pokus o vyřešení a automatické výjimky uzavřením a ConnectionMultiplexer RedisConnectionException SocketException obnovením připojení.

Do souboru Program.cs přidejte následující příkazy using:

using System.Net.Sockets;
using System.Threading;

V souboru Program.cs přidejte do třídy následující Program členy:

private static long _lastReconnectTicks = DateTimeOffset.MinValue.UtcTicks;
private static DateTimeOffset _firstErrorTime = DateTimeOffset.MinValue;
private static DateTimeOffset _previousErrorTime = DateTimeOffset.MinValue;
private static SemaphoreSlim _reconnectSemaphore = new SemaphoreSlim(initialCount: 1, maxCount: 1);
private static SemaphoreSlim _initSemaphore = new SemaphoreSlim(initialCount: 1, maxCount: 1);
private static ConnectionMultiplexer _connection;
private static bool _didInitialize = false;
// In general, let StackExchange.Redis handle most reconnects,
// so limit the frequency of how often ForceReconnect() will
// actually reconnect.
public static TimeSpan ReconnectMinInterval => TimeSpan.FromSeconds(60);
// If errors continue for longer than the below threshold, then the
// multiplexer seems to not be reconnecting, so ForceReconnect() will
// re-create the multiplexer.
public static TimeSpan ReconnectErrorThreshold => TimeSpan.FromSeconds(30);
public static TimeSpan RestartConnectionTimeout => TimeSpan.FromSeconds(15);
public static int RetryMaxAttempts => 5;
public static ConnectionMultiplexer Connection { get { return _connection; } }
private static async Task InitializeAsync()
{
    if (_didInitialize)
    {
        throw new InvalidOperationException("Cannot initialize more than once.");
    }
    _connection = await CreateConnectionAsync();
    _didInitialize = true;
}
// This method may return null if it fails to acquire the semaphore in time.
// Use the return value to update the "connection" field
private static async Task<ConnectionMultiplexer> CreateConnectionAsync()
{
    if (_connection != null)
    {
        // If we already have a good connection, let's re-use it
        return _connection;
    }
    try
    {
        await _initSemaphore.WaitAsync(RestartConnectionTimeout);
    }
    catch
    {
        // We failed to enter the semaphore in the given amount of time. Connection will either be null, or have a value that was created by another thread.
        return _connection;
    }
    // We entered the semaphore successfully.
    try
    {
        if (_connection != null)
        {
            // Another thread must have finished creating a new connection while we were waiting to enter the semaphore. Let's use it
            return _connection;
        }
        // Otherwise, we really need to create a new connection.
        string cacheConnection = ConfigurationManager.AppSettings["CacheConnection"].ToString();
        return await ConnectionMultiplexer.ConnectAsync(cacheConnection);
    }
    finally
    {
        _initSemaphore.Release();
    }
}
private static async Task CloseConnectionAsync(ConnectionMultiplexer oldConnection)
{
    if (oldConnection == null)
    {
        return;
    }
    try
    {
        await oldConnection.CloseAsync();
    }
    catch (Exception)
    {
        // Ignore any errors from the oldConnection
    }
}
/// <summary>
/// Force a new ConnectionMultiplexer to be created.
/// NOTES:
///     1. Users of the ConnectionMultiplexer MUST handle ObjectDisposedExceptions, which can now happen as a result of calling ForceReconnectAsync().
///     2. Call ForceReconnectAsync() for RedisConnectionExceptions and RedisSocketExceptions. You can also call it for RedisTimeoutExceptions,
///         but only if you're using generous ReconnectMinInterval and ReconnectErrorThreshold. Otherwise, establishing new connections can cause
///         a cascade failure on a server that's timing out because it's already overloaded.
///     3. The code will:
///         a. wait to reconnect for at least the "ReconnectErrorThreshold" time of repeated errors before actually reconnecting
///         b. not reconnect more frequently than configured in "ReconnectMinInterval"
/// </summary>
public static async Task ForceReconnectAsync()
{
    var utcNow = DateTimeOffset.UtcNow;
    long previousTicks = Interlocked.Read(ref _lastReconnectTicks);
    var previousReconnectTime = new DateTimeOffset(previousTicks, TimeSpan.Zero);
    TimeSpan elapsedSinceLastReconnect = utcNow - previousReconnectTime;
    // If multiple threads call ForceReconnectAsync at the same time, we only want to honor one of them.
    if (elapsedSinceLastReconnect < ReconnectMinInterval)
    {
        return;
    }
    try
    {
        await _reconnectSemaphore.WaitAsync(RestartConnectionTimeout);
    }
    catch
    {
        // If we fail to enter the semaphore, then it is possible that another thread has already done so.
        // ForceReconnectAsync() can be retried while connectivity problems persist.
        return;
    }
    try
    {
        utcNow = DateTimeOffset.UtcNow;
        elapsedSinceLastReconnect = utcNow - previousReconnectTime;
        if (_firstErrorTime == DateTimeOffset.MinValue)
        {
            // We haven't seen an error since last reconnect, so set initial values.
            _firstErrorTime = utcNow;
            _previousErrorTime = utcNow;
            return;
        }
        if (elapsedSinceLastReconnect < ReconnectMinInterval)
        {
            return; // Some other thread made it through the check and the lock, so nothing to do.
        }
        TimeSpan elapsedSinceFirstError = utcNow - _firstErrorTime;
        TimeSpan elapsedSinceMostRecentError = utcNow - _previousErrorTime;
        bool shouldReconnect =
            elapsedSinceFirstError >= ReconnectErrorThreshold // Make sure we gave the multiplexer enough time to reconnect on its own if it could.
            && elapsedSinceMostRecentError <= ReconnectErrorThreshold; // Make sure we aren't working on stale data (e.g. if there was a gap in errors, don't reconnect yet).
        // Update the previousErrorTime timestamp to be now (e.g. this reconnect request).
        _previousErrorTime = utcNow;
        if (!shouldReconnect)
        {
            return;
        }
        _firstErrorTime = DateTimeOffset.MinValue;
        _previousErrorTime = DateTimeOffset.MinValue;
        ConnectionMultiplexer oldConnection = _connection;
        await CloseConnectionAsync(oldConnection);
        _connection = null;
        _connection = await CreateConnectionAsync();
        Interlocked.Exchange(ref _lastReconnectTicks, utcNow.UtcTicks);
    }
    finally
    {
        _reconnectSemaphore.Release();
    }
}
// In real applications, consider using a framework such as
// Polly to make it easier to customize the retry approach.
private static async Task<T> BasicRetryAsync<T>(Func<T> func)
{
    int reconnectRetry = 0;
    int disposedRetry = 0;
    while (true)
    {
        try
        {
            return func();
        }
        catch (Exception ex) when (ex is RedisConnectionException || ex is SocketException)
        {
            reconnectRetry++;
            if (reconnectRetry > RetryMaxAttempts)
                throw;
            await ForceReconnectAsync();
        }
        catch (ObjectDisposedException)
        {
            disposedRetry++;
            if (disposedRetry > RetryMaxAttempts)
                throw;
        }
    }
}
public static Task<IDatabase> GetDatabaseAsync()
{
    return BasicRetryAsync(() => Connection.GetDatabase());
}
public static Task<System.Net.EndPoint[]> GetEndPointsAsync()
{
    return BasicRetryAsync(() => Connection.GetEndPoints());
}
public static Task<IServer> GetServerAsync(string host, int port)
{
    return BasicRetryAsync(() => Connection.GetServer(host, port));
}

Provádění příkazů mezipaměti

Pro proceduru Main třídy Program konzolové aplikace přidejte následující kód:

static void Main(string[] args)
{
    IDatabase cache = GetDatabase();

    // Perform cache operations using the cache object...

    // Simple PING command
    string cacheCommand = "PING";
    Console.WriteLine("\nCache command  : " + cacheCommand);
    Console.WriteLine("Cache response : " + cache.Execute(cacheCommand).ToString());

    // Simple get and put of integral data types into the cache
    cacheCommand = "GET Message";
    Console.WriteLine("\nCache command  : " + cacheCommand + " or StringGet()");
    Console.WriteLine("Cache response : " + cache.StringGet("Message").ToString());

    cacheCommand = "SET Message \"Hello! The cache is working from a .NET console app!\"";
    Console.WriteLine("\nCache command  : " + cacheCommand + " or StringSet()");
    Console.WriteLine("Cache response : " + cache.StringSet("Message", "Hello! The cache is working from a .NET console app!").ToString());

    // Demonstrate "SET Message" executed as expected...
    cacheCommand = "GET Message";
    Console.WriteLine("\nCache command  : " + cacheCommand + " or StringGet()");
    Console.WriteLine("Cache response : " + cache.StringGet("Message").ToString());

    // Get the client list, useful to see if connection list is growing...
    // Note that this requires allowAdmin=true in the connection string
    cacheCommand = "CLIENT LIST";
    Console.WriteLine("\nCache command  : " + cacheCommand);
    var endpoint = (System.Net.DnsEndPoint)GetEndPoints()[0];
    IServer server = GetServer(endpoint.Host, endpoint.Port);
    ClientInfo[] clients = server.ClientList();

    Console.WriteLine("Cache response :");
    foreach (ClientInfo client in clients)
    {
        Console.WriteLine(client.Raw);
    }

    CloseConnection(lazyConnection);
}

Azure Cache for Redis má konfigurovatelný počet databází (výchozí hodnota je 16), které lze použít k logickému oddělení dat v rámci Azure Cache for Redis. Kód se připojí k výchozí databázi DB 0. Další informace najdete v tématu Co jsou databáze Redis? a Výchozí konfigurace serveru Redis.

Položky v mezipaměti lze ukládat a načítat pomocí metod StringSet a StringGet.

Redis ukládá většinu dat jako řetězce Redis, ale tyto řetězce mohou obsahovat mnoho typů dat, včetně serializovaných binárních dat, která lze použít při ukládání objektů .NET v mezipaměti.

Stisknutím kombinace kláves Ctrl+F5 sestavíte a spustíte konzolovou aplikaci.

V níže uvedeném příkladu můžete vidět, že klíč Message měl předtím hodnotu z mezipaměti, která byla nastavena pomocí konzoly Redis na webu Azure Portal. Aplikace tuto hodnotu z mezipaměti aktualizovala. Aplikace rovněž spustila příkazy PING a CLIENT LIST.

Částečně provedený kód konzolové aplikace

Práce s objekty .NET v mezipaměti

Azure Cache for Redis do mezipaměti objekty .NET i primitivní datové typy, ale před objektem .NET lze ukládat do mezipaměti, musí být serializován. Serializaci objektů .NET má na starosti vývojář aplikace, kterému je tak poskytnuta flexibilita při výběru serializátoru.

Jeden snadný způsob, jak serializovat objekty, je použít metody serializace JsonConvert v balíčku Newtonsoft.Json a serializovat a deserializovat tak objekty do a z formátu JSON. V této části přidáte do mezipaměti objekt .NET.

V Visual Studio vyberte Nástroje NuGet Správce balíčků Správce balíčků Console a v okně konzoly Správce balíčků > > spusťte následující příkaz.

Install-Package Newtonsoft.Json

Na začátek souboru Program.cs přidejte následující příkaz using:

using Newtonsoft.Json;

Do souboru Program.cs přidejte následující definici třídy Employee:

class Employee
{
    public string Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }

    public Employee(string employeeId, string name, int age)
    {
        Id = employeeId;
        Name = name;
        Age = age;
    }
}

Na konec procedury Main() a před volání metody CloseConnection() v souboru Program.cs přidejte do mezipaměti následující řádky kódu a získejte serializovaný objekt .NET:

    // Store .NET object to cache
    Employee e007 = new Employee("007", "Davide Columbo", 100);
    Console.WriteLine("Cache response from storing Employee .NET object : " + 
    cache.StringSet("e007", JsonConvert.SerializeObject(e007)));

    // Retrieve .NET object from cache
    Employee e007FromCache = JsonConvert.DeserializeObject<Employee>(cache.StringGet("e007"));
    Console.WriteLine("Deserialized Employee .NET object :\n");
    Console.WriteLine("\tEmployee.Name : " + e007FromCache.Name);
    Console.WriteLine("\tEmployee.Id   : " + e007FromCache.Id);
    Console.WriteLine("\tEmployee.Age  : " + e007FromCache.Age + "\n");

Stisknutím kombinace kláves Ctrl+F5 sestavte a spusťte konzolovou aplikaci a otestujte serializaci objektů .NET.

Dokončená konzolová aplikace

Vyčištění prostředků

Pokud budete pokračovat k dalšímu kurzu, můžete prostředky vytvořené v tomto rychlém startu zachovat a znovu je použít.

V opačném případě, pokud jste už s ukázkovou aplikací v tomto rychlém startu skončili, můžete prostředky Azure vytvořené v tomto rychlém startu odstranit, abyste se vyhnuli poplatkům.

Důležité

Odstranění skupiny prostředků je nevratné a skupina prostředků včetně všech v ní obsažených prostředků bude trvale odstraněna. Ujistěte se, že nechtěně neodstraníte nesprávnou skupinu prostředků nebo prostředky. Pokud jste vytvořili prostředky pro hostování této ukázky v existující skupině prostředků, která obsahuje prostředky, které chcete zachovat, můžete místo odstranění skupiny prostředků odstranit každý prostředek jednotlivě na levé straně.

Přihlaste se k webu Azure Portal a potom vyberte Skupiny prostředků.

Do textového pole Filtrovat podle názvu zadejte název vaší skupiny prostředků. V pokynech v tomto článku se používala skupina prostředků TestResources. V seznamu výsledků ve vaší skupině prostředků vyberte ... a pak Odstranit skupinu prostředků.

Odstranit

Zobrazí se výzva k potvrzení odstranění skupiny prostředků. Potvrďte to tak, že zadejte název vaší skupiny prostředků, a vyberte Odstranit.

Po chvíli bude skupina prostředků včetně všech obsažených prostředků odstraněná.

Další kroky

V tomto rychlém startu jste zjistili, jak používat Azure Cache for Redis z aplikace .NET. Pokračujte k dalšímu rychlému startu, ve Azure Cache for Redis s ASP.NET aplikací.

Chcete optimalizovat útratu v cloudu a ušetřit ji?