Szybki start: używanie Azure Cache for Redis w .NET Framework

W tym przewodniku Szybki start dołączasz aplikację Azure Cache for Redis do aplikacji .NET Framework, aby mieć dostęp do bezpiecznej, dedykowanej pamięci podręcznej, która jest dostępna z dowolnej aplikacji na platformie Azure. Klienta StackExchange.Redis należy używać z kodem C# w aplikacji konsolowej .NET.

Przejdź do kodu na stronie GitHub

Jeśli chcesz przejść bezpośrednio do kodu, zobacz przewodnik Szybki .NET Framework na GitHub.

Wymagania wstępne

Tworzenie pamięci podręcznej

  1. Aby utworzyć pamięć podręczną, zaloguj się do Azure Portal i wybierz pozycję Utwórz zasób.

    Create a resource is highlighted in the left navigation pane.

  2. Na stronie Nowy wybierz pozycję Bazy danych, a następnie wybierz pozycję Azure Cache for Redis.

    On New, Databases is highlighted, and Azure Cache for Redis is highlighted.

  3. Na stronie Nowy Redis Cache skonfiguruj ustawienia nowej pamięci podręcznej.

    Ustawienie Wybieranie wartości Opis
    Subskrypcja Rozwijaj i wybierz swoją subskrypcję. Subskrypcja, w ramach której ma być Azure Cache for Redis wystąpienie.
    Grupa zasobów Rozwijaj i wybierz grupę zasobów lub wybierz pozycję Utwórz nową i wprowadź nazwę nowej grupy zasobów. Nazwa grupy zasobów, w której ma być tworzyć pamięć podręczną i inne zasoby. Dzięki umieszczeniu wszystkich zasobów aplikacji w jednej grupie zasobów można łatwo nimi zarządzać lub usuwać je razem.
    Nazwa DNS Wprowadź unikatową nazwę. Nazwa pamięci podręcznej musi być ciągiem od 1 do 63 znaków, który zawiera tylko cyfry, litery lub łączniki. Nazwa musi zaczynać się i kończyć liczbą lub literą i nie może zawierać kolejnych łączników. Nazwa hosta wystąpienia pamięci podręcznej będzie nazwą DNS .redis.cache.windows.net.
    Lokalizacja Rozwijaj i wybierz lokalizację. Wybierz region w pobliżu innych usług, które będą używać Twojej pamięci podręcznej.
    Typ pamięci podręcznej Rozwijaj i wybierz warstwę. Warstwa określa rozmiar, wydajność i funkcje dostępne dla pamięci podręcznej. Aby uzyskać więcej informacji, zobacz Omówienie pamięci podręcznej Azure Cache for Redis.
  4. Wybierz kartę Sieć lub wybierz przycisk Sieć w dolnej części strony.

  5. Na karcie Sieć wybierz metodę łączności.

  6. Wybierz kartę Dalej: Zaawansowane lub wybierz przycisk Dalej: Zaawansowane w dolnej części strony.

  7. Na karcie Zaawansowane dla podstawowego lub standardowego wystąpienia pamięci podręcznej wybierz przełącznik włącz, jeśli chcesz włączyć port niebędący portem TLS. Możesz również wybrać wersję redis, której chcesz użyć, 4 lub 6.

    Redis version 4 or 6.

  8. Na karcie Zaawansowane dla wystąpienia pamięci podręcznej Premium skonfiguruj ustawienia dla portu, klastrowania i trwałości danych innych niż TLS. Możesz również wybrać wersję redis, której chcesz użyć, 4 lub 6.

  9. Wybierz kartę Dalej: Tagi lub wybierz przycisk Dalej: Tagi w dolnej części strony.

  10. Opcjonalnie na karcie Tagi wprowadź nazwę i wartość, jeśli chcesz skategoryzować zasób.

  11. Wybierz pozycję Przejrzyj i utwórz. Zostaniesz przekierowyny do karty Przeglądanie + tworzenie, na której platforma Azure zweryfikuje konfigurację.

  12. Po wyświetlonym zielonym komunikacie Weryfikacja pomyślnie wybierz pozycję Utwórz.

Utworzenie pamięci podręcznej zajmuje trochę czasu. Postęp można monitorować na Azure Cache for Redis Przegląd. Gdy stan ma stan Uruchomiona,pamięć podręczna jest gotowa do użycia.

Pobieranie nazwy hosta, portów i kluczy dostępu z Azure Portal

Aby nawiązać połączenie z Azure Cache for Redis pamięci podręcznej, klienci pamięci podręcznej potrzebują nazwy hosta, portów i klucza pamięci podręcznej. Niektórzy klienci mogą odwoływać się do tych elementów przy użyciu nieco innych nazw. Nazwę hosta, porty i klucze można uzyskać z Azure Portal .

  • Aby uzyskać klucze dostępu, w lewym okienku nawigacji pamięci podręcznej wybierz pozycję Klucze dostępu.

    Azure Cache for Redis keys

  • Aby uzyskać nazwę hosta i porty, w lewym okienku nawigacji pamięci podręcznej wybierz pozycję Właściwości. Nazwa hosta ma postać nazwa DNS > .redis.cache.windows.net.

    Azure Cache for Redis properties

Utwórz na swoim komputerze plik o nazwie CacheSecrets.config i umieść go w lokalizacji, gdzie nie zostanie zaewidencjonowany za pomocą kodu źródłowego przykładowej aplikacji. W tym przewodniku Szybki start plik CacheSecrets.config znajduje się tu: C:\AppSecrets\CacheSecrets.config.

Edytuj plik CacheSecrets.config i dodaj następującą zawartość:

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

Zastąp element <host-name> nazwą hosta pamięci podręcznej.

Zastąp element <access-key> kluczem podstawowym pamięci podręcznej.

tworzenie aplikacji konsoli

W programie Visual Studio wybierz pozycje PlikNowyProjekt.

Wybierz pozycję Aplikacja konsolowa (.NET Framework)i Dalej, aby skonfigurować aplikację. Wpisz nazwę Project, sprawdź, czy wybrano .NET Framework 4.6.1 lub nowszą, a następnie wybierz pozycję Utwórz, aby utworzyć nową aplikację konsoli.

Konfigurowanie klienta pamięci podręcznej

W tej sekcji skonfigurujesz aplikację konsolową umożliwiającą korzystanie z klienta StackExchange.Redis na platformie .NET.

W Visual Studio wybierz pozycjęNarzędzia NuGet Menedżer pakietów Menedżer pakietów Console i uruchom następujące polecenie w oknie Menedżer pakietów Konsoli.

Install-Package StackExchange.Redis

Po ukończeniu instalacji klient pamięci podręcznej StackExchange.Redis będzie dostępny do użycia z projektem.

Łączenie z pamięcią podręczną

W Visual Studio otwórz plik App.config zaktualizuj go, aby uwzględnić atrybut, który odwołuje się do filefile pliku.

<?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>

W Eksplorator rozwiązań kliknij prawym przyciskiem myszy pozycję Odwołania i wybierz polecenie Dodaj odwołanie. Dodaj odwołanie do zestawu System.Configuration.

Dodaj następujące instrukcje using do pliku using:

using StackExchange.Redis;
using System.Configuration;

Połączenie z usługą Azure Cache for Redis jest zarządzane przez klasę ConnectionMultiplexer. Ta klasa powinna być udostępniana i wielokrotnie używana w aplikacji klienta. Nie należy tworzyć nowego połączenia dla każdej operacji.

Nigdy nie należy przechowywać poświadczeń w kodzie źródłowym. Aby przykład był prosty, używam tylko pliku konfiguracji zewnętrznych wpisów tajnych. Lepszym rozwiązaniem byłoby użycie usługi Azure Key Vault z certyfikatami.

W pliku Program.cs dodaj następujące elementy członkowskie do klasy aplikacji konsolowej:

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);
    });
}

W tym podejściu do udostępniania wystąpienia klasy ConnectionMultiplexer w aplikacji użyto właściwości statycznej, która zwraca połączone wystąpienie. Ten kod zapewnia bezpieczny wątkowo sposób inicjowania tylko jednego połączonego wystąpienia klasy ConnectionMultiplexer. Parametr abortConnect ma wartość false, co oznacza, że wywołanie zostanie wykonane pomyślnie, nawet jeśli połączenie z usługą Azure Cache for Redis nie zostanie nawiązane. Kluczowa funkcja klasy ConnectionMultiplexer polega na automatycznym przywracaniu łączności z pamięcią podręczną po rozwiązaniu problemu z siecią lub usunięciu innych przyczyn.

Wartość ustawienia appSetting CacheConnection jest używana do odwoływania się do parametrów połączenia pamięci podręcznej z witryny Azure Portal jako parametru hasła.

Obsługuj błędy RedisConnectionException i SocketException przez ponowne nawiązanie połączenia

Zalecanym najlepszym rozwiązaniem podczas wywoływania metod na platformie jest próba automatycznego rozwiązania wyjątków i przez zamknięcie i ponowne ConnectionMultiplexerRedisConnectionExceptionSocketException połączenie.

Dodaj następujące instrukcje using do pliku using:

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

W programie Program.csdodaj następujące składowe do klasy :

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));
}

Wykonywanie poleceń pamięci podręcznej

Dodaj następujący kod do procedury Main klasy Program aplikacji konsolowej:

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);
}

Usługa Azure Cache for Redis ma konfigurowalną liczbę baz danych (domyślnie 16), których można użyć do logicznego odseparowania danych w tej usłudze. Kod łączy się z domyślną bazą danych DB 0. Aby uzyskać więcej informacji, zobacz What are Redis databases? (Co to są bazy danych Redis?) i Default Redis server configuration (Domyślna konfiguracja serwera Redis).

Elementy pamięci podręcznej można zapisywać i pobierać za pomocą metod StringSet i StringGet.

Usługa Redis przechowuje większość danych w formie ciągów Redis, ale ciągi te mogą zawierać wiele typów danych, w tym serializowane dane binarne, które mogą być używane podczas przechowywania obiektów platformy .NET w pamięci podręcznej.

Naciśnij klawisze Ctrl+F5, aby skompilować i uruchomić aplikację konsolową.

W poniższym przykładzie widać, że klucz Message miał już w pamięci podręcznej wartość, która została ustawiona za pomocą konsoli Redis w witrynie Azure Portal. Aplikacja zaktualizowała tę wartość w pamięci podręcznej. Aplikacja również wykonała polecenia PING i CLIENT LIST.

Console app partial

Praca z obiektami platformy .NET w pamięci podręcznej

Usługa Azure Cache for Redis może buforować obiekty platformy .NET oraz pierwotne typy danych, ale zanim będzie możliwe buforowanie obiektu platformy .NET, trzeba go serializować. Odpowiedzialność za serializację obiektu .NET spoczywa na deweloperze aplikacji, który ma możliwość wybrania serializatora.

Prostym sposobem na wykonanie serializacji obiektów jest użycie metod serializacji JsonConvert w środowisku JsonConvert oraz serializacja do i z formatu JSON. W tej sekcji dodasz obiekt platformy .NET do pamięci podręcznej.

W Visual Studio wybierz pozycjęNarzędzia NuGet Menedżer pakietów Menedżer pakietów Console i uruchom następujące polecenie w oknie Menedżer pakietów Konsoli.

Install-Package Newtonsoft.Json

Dodaj następującą instrukcję using na początku pliku using:

using Newtonsoft.Json;

Dodaj następującą definicję klasy Employee do pliku 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;
    }
}

W dolnej części procedury Main() w pliku Main() i przed wywołaniem do CloseConnection() dodaj następujące wiersze kodu do pamięci podręcznej i pobierz serializowany obiekt platformy .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");

Naciśnij klawisze Ctrl+F5, aby skompilować i uruchomić aplikację konsolową na potrzeby testowania serializacji obiektów platformy .NET.

Console app completed

Czyszczenie zasobów

Jeśli zamierzasz przejść do kolejnego samouczka, możesz zachować zasoby utworzone w tym przewodniku Szybki start i użyć ich ponownie.

W przeciwnym razie po zakończeniu pracy z przykładową aplikacją poradnika Szybki start możesz usunąć zasoby platformy Azure utworzone w tym poradniku Szybki start, aby uniknąć naliczania opłat.

Ważne

Usunięcie grupy zasobów jest nieodwracalne i grupa zasobów oraz wszystkie zawarte w niej zasoby zostaną trwale usunięte. Uważaj, aby nie usunąć przypadkowo niewłaściwych zasobów lub grupy zasobów. Jeśli zasoby do hostowania tego przykładu zostały utworzone wewnątrz istniejącej grupy zasobów zawierającej zasoby, które chcesz zachować, możesz usunąć każdy zasób indywidualnie po lewej stronie zamiast usuwać grupę zasobów.

Zaloguj się do witryny Azure Portal i wybierz pozycję Grupy zasobów.

W polu tekstowym Filtruj według nazwy... wpisz nazwę grupy zasobów. Instrukcje w tym artykule używają grupy zasobów o nazwie TestResources. W grupie zasobów na liście wyników wybierz pozycję ... , a następnie pozycję Usuń grupę zasobów.

Delete

Zobaczysz prośbę o potwierdzenie usunięcia grupy zasobów. Wpisz nazwę grupy zasobów, aby potwierdzić, a następnie wybierz pozycję Usuń.

Po krótkim czasie grupa zasobów i wszystkie zawarte w niej zasoby zostaną usunięte.

Następne kroki

W tym przewodniku Szybki start przedstawiono sposób użycia usługi Azure Cache for Redis z poziomu aplikacji platformy .NET. Przejdź do kolejnego Szybkiego startu, w którym wyjaśniono, jak używać usługi Microsoft Azure Cache for Redis z poziomu aplikacji internetowej ASP.NET.

Chcesz zoptymalizować wydatki na chmurę i zaoszczędzić na tych wydatkach?