ASP.NET Core 中的 HybridCache 程式庫
本文說明如何在 ASP.NET Core 應用程式中設定和使用 HybridCache
程式庫。 如需程式庫的簡介,請參閱快取概觀的 HybridCache
區段。
取得程式庫
安裝 Microsoft.Extensions.Caching.Hybrid
套件。
dotnet add package Microsoft.Extensions.Caching.Hybrid
註冊服務
藉由呼叫 AddHybridCacheHybridCache
,將 相依性注入 (DI) 服務新增至容器中:
// Add services to the container.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthorization();
builder.Services.AddHybridCache();
上述程式碼會使用預設選項註冊 HybridCache
服務。 註冊 API 也可以設定選項和序列化。
取得和儲存快取項目
此 HybridCache
服務提供兩個多載的 GetOrCreateAsync 方法,接受索引鍵及:
- 工廠方法。
- 狀態和工廠方法。
方法會使用索引鍵嘗試從主要快取擷取物件。 如果在主要快取中找不到項目 (快取遺失),則會檢查是否已設定次要快取。 如果找不到該處的資料 (另一個快取遺失),它會呼叫 Factory 方法,以從資料來源取得物件。 然後,它會將物件儲存在主要和次要快取中。 如果在主要或次要快取中找到物件 (快取命中),則永遠不會呼叫 Factory 方法。
HybridCache
服務可確保只有一個指定機碼的同時呼叫端會呼叫 Factory 方法,而所有其他呼叫端都會等候該呼叫的結果。 傳遞到 GetOrCreateAsync
的 CancellationToken
代表著所有同時呼叫者的合併取消。
主要 GetOrCreateAsync
超載
在大部分情況下,建議使用 GetOrCreateAsync
的無狀態重載。 要呼叫的程式碼相對簡單。 以下是範例:
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;
}
}
快取金鑰指引
傳遞至 key
的 GetOrCreateAsync
必須唯一識別要快取的數據:
- 就用來從其來源擷取該數據的標識碼值而言。
- 就應用程式中快取的其他資料而言。
這兩種類型的唯一性通常透過串連字串的方式來確保,將不同部分合併成單一鍵字串。 例如:
cache.GetOrCreateAsync($"/orders/{region}/{orderId}", ...);
或
cache.GetOrCreateAsync($"user_prefs_{userId}", ...);
呼叫者有責任確保密鑰配置有效,而且無法造成數據變得混淆。
建議您不要在快取索引鍵中使用外部用戶輸入。 例如,請勿使用來自UI的原始 string
值作為快取索引鍵的一部分。 這類密鑰可能會允許惡意存取嘗試,或在拒絕服務攻擊中使用,藉由隨機字串產生的無意義索引鍵將資料填滿,進而讓快取飽和。 在上述有效範例中,順序 數據和 用戶喜好設定 數據明顯不同:
-
orderid
和userId
是內部產生的標識碼。 -
region
可能是已知區域預先定義清單中的枚舉或字串。
對於像 /
或 _
這樣的標記來說,沒有特別的意義。 整個索引鍵值會被視為不透明的識別字串。 在此情況下,您可以省略 /
和 _
,而不會改變快取的功能方式,但通常會使用分隔符來避免模稜兩可。例如,$"order{customerId}{orderId}"
可能會導致混淆:
-
customerId
42,orderId
123 -
customerId
421 與orderId
23
(這兩者都會產生快取金鑰 order42123
)
本指南同樣適用於任何以 string
為基礎的快取 API,例如 HybridCache
、IDistributedCache
和 IMemoryCache
。
請注意,內嵌的內插字串語法($"..."
,位於上述有效索引鍵的範例中)直接在 GetOrCreateAsync
調用中。 使用 HybridCache
時,建議使用這個語法,因為它允許規劃的未來改善,以略過在許多案例中為密鑰配置 string
的需求。
其他重要考慮
- 金鑰可以限制為有效的最大長度。 例如,預設
HybridCache
實作(透過AddHybridCache(...)
)預設會將索引鍵限製為1024個字元。 該數目可透過HybridCacheOptions.MaximumKeyLength
來設定,較長的鍵會略過快取機制,以避免飽和。 - 索引鍵必須是有效的 Unicode 序列。 如果傳遞無效的 Unicode 序列,則行為是未定義的。
- 使用進程外次要快取,例如
IDistributedCache
時,特定後端實作可能會施加額外的限制。 作為假設的範例,特定後端可能會使用不區分大小寫的索引鍵邏輯。 預設HybridCache
(透過AddHybridCache(...)
)偵測此情況以防止混淆攻擊,不過它仍然可能會導致衝突的密鑰遭覆寫或被較預期提前淘汰。
替代的 GetOrCreateAsync
多載
替代多載可能會降低擷取到的變數和個別執行個體回呼的一些額外負荷,但代價是更複雜的程式碼。 在大部分情況下,效能提升不會比程式碼的複雜度更重要。 以下是使用替代重載的範例:
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;
}
}
SetAsync
方法
在許多情況下,GetOrCreateAsync
是唯一需要的 API。 但 HybridCache
也必須具有 SetAsync 以將物件儲存在快取中,而不需要先嘗試擷取它。
依機碼移除快取項目
當快取項目的基礎資料在到期前變更時,可透過使用機碼呼叫RemoveAsync 來明確移除此項目。 過載允許您指定一組鍵值集合。
移除項目時,它會從主要和次要快取中移除。
依標籤移除快取項目
標籤可用來將快取項目分組,並使其一起失效。
呼叫 GetOrCreateAsync
時設定標籤,如下列範例所示:
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;
}
}
使用標籤值呼叫 RemoveByTagAsync,以移除指定標籤的所有項目。 函式多載允許您指定一組標籤值。
移除項目時,它會從主要和次要快取中移除。
選項。
AddHybridCache
方法可用來設定全域預設值。 下列範例示範如何設定一些可用的選項:
// 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)
};
});
GetOrCreateAsync
方法也可以採用 HybridCacheEntryOptions
物件來覆寫特定快取項目的全域預設值。 以下是範例:
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;
}
}
如需選項的詳細資訊,請參閱原始程式碼:
限制
下列 HybridCacheOptions
屬性可讓您設定適用於所有快取項目的限制:
- MaximumPayloadBytes - 快取項目的大小上限。 預設值為 1 MB。 系統會記錄嘗試儲存超過此大小的值,且該值不會儲存在快取中。
- MaximumKeyLength - 快取索引鍵的最大長度。 預設值為 1024 個字元。 系統會記錄嘗試儲存超過此大小的值,且該值不會儲存在快取中。
序列化
使用次要的外部快取需要序列化。 序列化設定為註冊 HybridCache
服務的一部分。 特定類型和一般用途的序列化器可以透過 AddSerializer 和 AddSerializerFactory 方法來進行設定,並從 AddHybridCache
呼叫鏈結。 根據預設,程式庫會在內部處理 string
和 byte[]
,並針對其他所有項目使用 System.Text.Json
。
HybridCache
也可以使用其他序列化程式,例如 protobuf 或 XML。
下列範例會將服務設定為使用類型特定的 protobuf 序列化程式:
// 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>>();
下列範例會將服務設定為使用可處理許多 protobuf 類型的一般用途 protobuf 序列化程式:
// 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>();
次要快取需要資料存放區,例如 Redis 或 SqlServer。 若要使用 Azure Cache for Redis,例如:
安裝
Microsoft.Extensions.Caching.StackExchangeRedis
套件。建立 Azure Cache for Redis 的執行個體。
取得連線至 Redis 實體的連接字串。 在 Azure 入口網站的 [概觀] 頁面上選取 [顯示存取金鑰],以尋找連接字串。
將連接字串儲存在應用程式的組態中。 例如,使用類似下列 JSON 的使用者祕密檔案,以及
ConnectionStrings
區段中的連接字串。 將<the connection string>
取代為實際的連接字串:{ "ConnectionStrings": { "RedisConnectionString": "<the connection string>" } }
在 DI 中註冊 Redis 套件所提供的
IDistributedCache
實作。 若要這樣做,請呼叫AddStackExchangeRedisCache
,並傳入連接字串。 例如:builder.Services.AddStackExchangeRedisCache(options => { options.Configuration = builder.Configuration.GetConnectionString("RedisConnectionString"); });
Redis
IDistributedCache
實作現在可從應用程式的 DI 容器取得。HybridCache
會使用它作為次要快取,並使用為其設定的序列化程式。
如需詳細資訊,請參閱 HybridCache 序列化範例應用程式。
快取儲存體
根據預設,HybridCache
會針對其主要快取儲存體使用 MemoryCache。 快取項目儲存在進程內,因此每部伺服器都有個別的快取,每當伺服器程序重新啟動時就會清除。 如果是次要跨進程儲存體,例如 Redis 或 SQL Server,HybridCache
會使用已設定的 IDistributedCache
實作 (如果有的話)。 但是,即使沒有 IDistributedCache
實作,HybridCache
服務仍會提供過程中快取和 突發流量保護。
注意
依索引鍵或標記使快取項目失效時,它們會在目前伺服器和次要的跨進程儲存空間中失效。 不過,其他伺服器中的記憶體內部快取不會受到影響。
最佳化效能
若要將效能最佳化,請將 HybridCache
設定為重用物件,並避免 byte[]
分配。
重複利用物件
藉由重複使用執行個體,HybridCache
即可降低與每次呼叫的還原序列化相關聯的 CPU 和物件配置額外負荷。 在快取物件很大或受到頻繁存取的案例中,這可以提高效能。
在使用 IDistributedCache
的一般現有程式碼中,每次從快取中擷取物件都會導致還原序列化。 此行為表示每個並行呼叫者都會取得物件的不同執行個體,而該執行個體無法與其他執行個體互動。 結果是執行緒安全,因為不會有對相同物件執行個體進行並行修改的風險。
由於會根據現有的 HybridCache
程式碼來調整許多 IDistributedCache
使用方式,因此 HybridCache
預設會保留此行為,以避免引進並行錯誤 (bug)。 不過,若符合下列條件,物件本質上就是執行緒安全的:
- 它們具有不變的類型。
- 程式碼不會修改它們。
在這種情況下,請通知 HybridCache
透過下列方式可安全地重複使用執行個體:
- 將類型標示為
sealed
。 C# 中的sealed
關鍵字表示無法繼承該類別。 - 將
[ImmutableObject(true)]
屬性套用至型別。[ImmutableObject(true)]
屬性表示物件的狀態在建立之後即無法變更。
避免 byte[]
分配
HybridCache
也提供選擇性的 IDistributedCache
實作 API,以避免 byte[]
配置。 這項功能由 Microsoft.Extensions.Caching.StackExchangeRedis
和 Microsoft.Extensions.Caching.SqlServer
套件的預覽版本實作。 如需詳細資訊,請參閱IBufferDistributedCache。
以下是安裝套件的 .NET CLI 命令:
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package Microsoft.Extensions.Caching.SqlServer
自訂 HybridCache 實作
HybridCache
抽象類別的具體實作包含在共用架構中,並透過相依性插入來提供。 但開發人員歡迎提供或取用 API 的自定義實作,例如 FusionCache。
相容性
HybridCache
程式庫支援舊版 .NET 執行階段,向下至 .NET Framework 4.7.2 和 .NET Standard 2.0。
其他資源
如需 HybridCache
的詳細資訊,請參閱下列資源:
- GitHub 問題 dotnet/aspnetcore #54647。
-
HybridCache
原始程式碼