HybridCache-bibliotheek in ASP.NET Core
Belangrijk
HybridCache
is momenteel nog in preview, maar wordt volledig uitgebracht na .NET 9.0 in een toekomstige secundaire versie van .NET Extensions.
In dit artikel wordt uitgelegd hoe u de HybridCache
-bibliotheek in een ASP.NET Core-app configureert en gebruikt. Zie de sectie HybridCache
van het overzicht van cachingvoor een inleiding tot de bibliotheek.
De bibliotheek verkrijgen
Installeer het Microsoft.Extensions.Caching.Hybrid
-pakket.
dotnet add package Microsoft.Extensions.Caching.Hybrid --version "9.0.0-preview.7.24406.2"
De service registreren
Voeg de HybridCache
-service toe aan de afhankelijkheidsinjectie (DI) container door AddHybridCache
aan te roepen:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthorization();
builder.Services.AddHybridCache();
De voorgaande code registreert de HybridCache
-service met standaardopties. De registratie-API kan ook opties configureren en serialisatie.
Cachevermeldingen ophalen en opslaan
De HybridCache
-service biedt een GetOrCreateAsync
-methode met twee overloads, waarbij een sleutel wordt gebruikt en:
- Een fabrieksmethode.
- Staat en een fabrieksmethode.
De methode gebruikt de sleutel om het object op te halen uit de primaire cache. Als het item niet wordt gevonden in de primaire cache (een cache miss), wordt de secundaire cache gecontroleerd indien deze is geconfigureerd. Wanneer de gegevens daar niet worden gevonden (een andere cachemiss), wordt de factorymethode aangeroepen om het object uit de gegevensbron op te halen. Vervolgens wordt het object opgeslagen in zowel primaire als secundaire caches. De factory-methode wordt nooit aangeroepen als het object wordt gevonden in de primaire of secundaire cache (een cachetreffer).
De HybridCache
-service zorgt ervoor dat slechts één gelijktijdige beller voor een bepaalde sleutel de factory-methode aanroept en alle andere bellers wachten op het resultaat van die aanroep. De CancellationToken
die aan GetOrCreateAsync
is doorgegeven vertegenwoordigt de gezamenlijke annulering van alle gelijktijdige bellers.
De belangrijkste GetOrCreateAsync
overbelasting
De stateless overload van GetOrCreateAsync
wordt aanbevolen voor de meeste scenario's. De code die moet worden aangeroepen, is relatief eenvoudig. Hier volgt een voorbeeld:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
Richtlijnen voor cachesleutels
De key
doorgegeven aan GetOrCreateAsync
moeten de gegevens die in de cache worden opgeslagen, uniek identificeren:
- In termen van de id-waarden die worden gebruikt om die gegevens op te halen uit de bron.
- Wat betreft andere gegevens die in de cache van de toepassing zijn opgeslagen.
Beide typen uniekheid worden meestal gegarandeerd door tekenreekssamenvoeging te gebruiken om één sleuteltekenreeks te maken die bestaat uit verschillende onderdelen die zijn samengevoegd in één tekenreeks. Bijvoorbeeld:
cache.GetOrCreateAsync($"/orders/{region}/{orderId}", ...);
of
cache.GetOrCreateAsync($"user_prefs_{userId}", ...);
Het is de verantwoordelijkheid van de beller om ervoor te zorgen dat een sleutelschema geldig is en dat gegevens niet in de war raken.
U wordt aangeraden geen externe gebruikersinvoer in de cachesleutel te gebruiken. Gebruik bijvoorbeeld geen onbewerkte string
waarden uit een gebruikersinterface als onderdeel van een cachesleutel. Dergelijke sleutels kunnen schadelijke toegangspogingen toestaan of kunnen worden gebruikt bij een denial-of-service-aanval door uw cache te verzadiging met gegevens die betekenisloze sleutels hebben die zijn gegenereerd op basis van willekeurige tekenreeksen. In de voorgaande geldige voorbeelden zijn de volgorde gegevens en gebruikersvoorkeur gegevens duidelijk verschillend:
-
orderid
enuserId
zijn intern gegenereerde id's. -
region
kan een opsomming of tekenreeks zijn uit een vooraf gedefinieerde lijst met bekende regio's.
Er is geen significantie geplaatst op tokens zoals /
of _
. De volledige sleutelwaarde wordt behandeld als een ondoorzichtige identificatietekenreeks. In dit geval kunt u de /
en _
weglaten zonder dat de werking van de cache verandert, maar meestal wordt een scheidingsteken gebruikt om dubbelzinnigheid te voorkomen - bijvoorbeeld kan $"order{customerId}{orderId}"
verwarring veroorzaken tussen:
-
customerId
42 metorderId
123 -
customerId
421 metorderId
23
(beide zouden de cachesleutel order42123
genereren)
Deze richtlijnen zijn evenzeer van toepassing op elke string
-gebaseerde cache-API, zoals HybridCache
, IDistributedCache
en IMemoryCache
.
U ziet dat de inline geïnterpoleerde tekenreekssyntaxis ($"..."
in de voorgaande voorbeelden van geldige sleutels) zich rechtstreeks in de GetOrCreateAsync
aanroep bevindt. Deze syntaxis wordt aanbevolen bij het gebruik van HybridCache
, omdat geplande toekomstige verbeteringen mogelijk zijn die de noodzaak om een string
toe te wijzen voor de sleutel in veel scenario's overslaan.
Aanvullende belangrijke overwegingen
- Sleutels kunnen worden beperkt tot geldige maximumlengten. De standaard-
HybridCache
-implementatie (viaAddHybridCache(...)
) beperkt bijvoorbeeld sleutels standaard tot 1024 tekens. Dat getal kan worden geconfigureerd viaHybridCacheOptions.MaximumKeyLength
, waarbij langere sleutels de cachemechanismen omzeilen om verzadiging te voorkomen. - Sleutels moeten geldige Unicode-reeksen zijn. Als er ongeldige Unicode-reeksen worden doorgegeven, is het gedrag niet gedefinieerd.
- Wanneer u een niet-verwerkte secundaire cache gebruikt, zoals
IDistributedCache
, kan de specifieke back-end-implementatie aanvullende beperkingen opleggen. Als hypothetisch voorbeeld kan een bepaalde back-end hoofdletterongevoelige sleutellogica gebruiken. De standaard-HybridCache
(viaAddHybridCache(...)
) detecteert dit scenario om verwarringsaanvallen te voorkomen, maar dit kan er nog steeds toe leiden dat conflicterende sleutels sneller worden overschreven of verwijderd dan verwacht.
De alternatieve GetOrCreateAsync
overbelasting
De alternatieve overbelading kan enige overhead verminderen bij de vastgelegde variabelen en callbacks per instantie, maar gaat ten koste van complexere code. Voor de meeste scenario's weegt de prestatieverhoging niet op tegen de complexiteit van de code. Hier volgt een voorbeeld waarin gebruik wordt gemaakt van de alternatieve overbelasting:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
(name, id, obj: this),
static async (state, token) =>
await state.obj.GetDataFromTheSourceAsync(state.name, state.id, token),
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
De methode SetAsync
In veel scenario's is GetOrCreateAsync
de enige API die nodig is. Maar HybridCache
heeft ook SetAsync
om een object in de cache op te slaan zonder het eerst op te halen.
Cachevermeldingen per sleutel verwijderen
Wanneer de onderliggende gegevens voor een cachevermelding worden gewijzigd voordat deze verloopt, verwijdert u de vermelding expliciet door RemoveAsync
aan te roepen met de sleutel voor de vermelding. Met een overload kunt u een verzameling sleutelwaarden opgeven.
Wanneer een vermelding wordt verwijderd, wordt deze verwijderd uit zowel de primaire als de secundaire cache.
Cachevermeldingen per tag verwijderen
Belangrijk
Deze functie is nog in ontwikkeling. Als u items per tag probeert te verwijderen, zult u merken dat deze geen effect heeft.
Tags kunnen worden gebruikt om cachevermeldingen te groeperen en ze samen ongeldig te maken.
Stel tags in bij het aanroepen van GetOrCreateAsync
, zoals wordt weergegeven in het volgende voorbeeld:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
var tags = new List<string> { "tag1", "tag2", "tag3" };
var entryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(1),
LocalCacheExpiration = TimeSpan.FromMinutes(1)
};
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
entryOptions,
tags,
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
Verwijder alle vermeldingen voor een opgegeven tag door RemoveByTagAsync
aan te roepen met de tagwaarde. Met een overload kunt u een verzameling tag-waarden opgeven.
Wanneer een vermelding wordt verwijderd, wordt deze verwijderd uit zowel de primaire als de secundaire cache.
Opties
De methode AddHybridCache
kan worden gebruikt om globale standaardinstellingen te configureren. In het volgende voorbeeld ziet u hoe u enkele van de beschikbare opties configureert:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.MaximumPayloadBytes = 1024 * 1024;
options.MaximumKeyLength = 1024;
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(5),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
});
De methode GetOrCreateAsync
kan ook een HybridCacheEntryOptions
object gebruiken om de algemene standaardwaarden voor een specifieke cachevermelding te overschrijven. Hier volgt een voorbeeld:
public class SomeService(HybridCache cache)
{
private HybridCache _cache = cache;
public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
{
var tags = new List<string> { "tag1", "tag2", "tag3" };
var entryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(1),
LocalCacheExpiration = TimeSpan.FromMinutes(1)
};
return await _cache.GetOrCreateAsync(
$"{name}-{id}", // Unique key to the cache entry
async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
entryOptions,
tags,
cancellationToken: token
);
}
public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
{
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
Zie de broncode voor meer informatie over de opties:
Grenzen
Met de volgende eigenschappen van HybridCacheOptions
kunt u limieten configureren die van toepassing zijn op alle cachevermeldingen:
- MaximumPayloadBytes - Maximale grootte van een cachevermelding. De standaardwaarde is 1 MB. Pogingen om waarden over deze grootte op te slaan, worden geregistreerd en de waarde wordt niet opgeslagen in de cache.
- MaximumKeyLength - Maximale lengte van een cachesleutel. De standaardwaarde is 1024 tekens. Pogingen om waarden over deze grootte op te slaan, worden geregistreerd en de waarde wordt niet opgeslagen in de cache.
Serialisatie
Voor het gebruik van een secundaire out-of-process-cache is serialisatie vereist. Serialisatie wordt geconfigureerd als onderdeel van het registreren van de HybridCache
-service. Typespecifieke en algemeen bruikbare serializers kunnen worden geconfigureerd via de AddSerializer
en AddSerializerFactory
methodes, aangeroepen vanuit de AddHybridCache
-aanroep. De bibliotheek verwerkt standaard string
en byte[]
intern en gebruikt System.Text.Json
voor alles.
HybridCache
kan ook andere serializers gebruiken, zoals protobuf of XML.
In het volgende voorbeeld wordt de service geconfigureerd voor het gebruik van een typespecifieke protobuf-serializer:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromSeconds(10),
LocalCacheExpiration = TimeSpan.FromSeconds(5)
};
}).AddSerializer<SomeProtobufMessage,
GoogleProtobufSerializer<SomeProtobufMessage>>();
In het volgende voorbeeld wordt de service geconfigureerd voor het gebruik van een protobuf-serializer voor algemeen gebruik die veel protobuf-typen kan verwerken:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromSeconds(10),
LocalCacheExpiration = TimeSpan.FromSeconds(5)
};
}).AddSerializerFactory<GoogleProtobufSerializerFactory>();
Voor de secundaire cache is een gegevensarchief vereist, zoals Redis of SqlServer. Als u Azure Cache voor Redis-wilt gebruiken, bijvoorbeeld:
Installeer het
Microsoft.Extensions.Caching.StackExchangeRedis
-pakket.Maak een exemplaar van Azure Cache voor Redis.
Haal een verbindingsreeks op die verbinding maakt met het Redis-exemplaar. Zoek de verbindingsreeks door Toegangssleutels weergeven te selecteren op de pagina Overzicht in de Azure-portal.
Sla de verbindingsreeks op in de configuratie van de app. Gebruik bijvoorbeeld een gebruikersgeheimbestand dat eruitziet als de volgende JSON, met de verbindingsreeks in de sectie
ConnectionStrings
. Vervang<the connection string>
door de werkelijke verbindingsreeks:{ "ConnectionStrings": { "RedisConnectionString": "<the connection string>" } }
Registreer in DI de
IDistributedCache
-implementatie die het Redis-pakket biedt. Hiervoor roept uAddStackExchangeRedisCache
aan en geeft u de verbindingsreeks door. Bijvoorbeeld:builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = builder.Configuration.GetConnectionString("RedisConnectionString"); });
De Implementatie van Redis
IDistributedCache
is nu beschikbaar in de DI-container van de app.HybridCache
gebruikt deze als secundaire cache en maakt gebruik van de serialisatiefunctie die hiervoor is geconfigureerd.
Zie de voorbeeld-app HybridCache-serialisatievoor meer informatie.
Cacheopslag
Standaard gebruikt HybridCache
MemoryCache voor de primaire cacheopslag. Cachevermeldingen worden in het proces opgeslagen, dus elke server heeft een afzonderlijke cache die verloren gaat wanneer het serverproces opnieuw wordt opgestart. Voor secundaire out-of-process-opslag, zoals Redis of SQL Server, gebruikt HybridCache
de geconfigureerde IDistributedCache
implementatie, indien van toepassing. Maar zelfs zonder een IDistributedCache
-implementatie biedt de HybridCache
-service nog steeds plaatsgebonden caching en stampede-bescherming.
Notitie
Bij het ongeldig maken van cachevermeldingen per sleutel of tags, worden deze ongeldig gemaakt op de huidige server en in de secundaire out-of-process-opslag. De cache in het geheugen op andere servers wordt echter niet beïnvloed.
Prestaties optimaliseren
Als u de prestaties wilt optimaliseren, configureert u HybridCache
voor het hergebruik van objecten en voorkomt u byte[]
toewijzingen.
Objecten opnieuw gebruiken
Door exemplaren opnieuw te gebruiken, kan HybridCache
de overhead van CPU- en objecttoewijzingen verminderen die verbonden zijn met deserialisatie per aanroep. Dit kan leiden tot prestatieverbeteringen in scenario's waarbij de objecten in de cache groot zijn of regelmatig worden geopend.
In typische bestaande code die gebruikmaakt van IDistributedCache
, resulteert elk ophalen van een object uit de cache in de deserialisatie. Dit gedrag betekent dat elke gelijktijdige aanroeper een afzonderlijk exemplaar van het object krijgt, dat niet kan communiceren met andere exemplaren. Het resultaat is threadveiligheid, omdat er geen risico is op gelijktijdige wijzigingen in dezelfde objectinstanties.
Omdat veel HybridCache
gebruik wordt aangepast aan bestaande IDistributedCache
code, behoudt HybridCache
dit gedrag standaard om gelijktijdigheidsfouten te voorkomen. Objecten zijn echter inherent thread-safe als:
- Ze zijn onveranderbare typen.
- De code wijzigt deze niet.
In dergelijke gevallen informeert u HybridCache
dat het veilig is om exemplaren opnieuw te gebruiken door:
- Het type markeren als
sealed
. Hetsealed
trefwoord in C# betekent dat de klasse niet kan worden overgenomen. - Het
[ImmutableObject(true)]
-kenmerk toepassen op het type. Het kenmerk[ImmutableObject(true)]
geeft aan dat de status van het object niet kan worden gewijzigd nadat het is gemaakt.
Vermijd byte[]
-toewijzingen
HybridCache
biedt ook optionele API's voor IDistributedCache
implementaties om byte[]
toewijzingen te voorkomen. Deze functie wordt geïmplementeerd door de preview-versies van de Microsoft.Extensions.Caching.StackExchangeRedis
- en Microsoft.Extensions.Caching.SqlServer
-pakketten. Zie IBufferDistributedCache Hier vindt u de .NET CLI-opdrachten voor het installeren van de pakketten voor meer informatie:
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package Microsoft.Extensions.Caching.SqlServer
Aangepaste HybridCache-implementaties
Een concrete implementatie van de HybridCache
abstracte klasse wordt opgenomen in het gedeelde framework en wordt geleverd via afhankelijkheidsinjectie. Ontwikkelaars zijn echter welkom om aangepaste implementaties van de API te bieden.
Compatibiliteit
De HybridCache
-bibliotheek ondersteunt oudere .NET-runtimes, tot .NET Framework 4.7.2 en .NET Standard 2.0.
Aanvullende informatiebronnen
Zie de volgende bronnen voor meer informatie over HybridCache
:
- GitHub-probleem dotnet/aspnetcore #54647.
-
HybridCache
broncode