本文說明如何在 ASP.NET Core 應用程式中設定和使用 HybridCache 程式庫。 如需程式庫的簡介,請參閱快取概觀的 HybridCache 區段。
取得程式庫
安裝 Microsoft.Extensions.Caching.Hybrid 套件。
dotnet add package Microsoft.Extensions.Caching.Hybrid
註冊服務
藉由呼叫 HybridCache,將 AddHybridCache 服務新增至容器中:
// 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 執行個體可確保對於特定金鑰,只有一個並行呼叫者會呼叫工廠方法,而其餘使用相同執行個體的呼叫者都會等待該次呼叫的結果。 當所有等待共享操作的呼叫者都已取消時,傳送到工廠的取消令牌即被取消。 這種協調不會延伸到其他 HybridCache 實例,即使它們使用相同的次級分散快取。
主要 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)
{
// Placeholder for retrieving data from a real backing store (database, HTTP API, etc.).
// A real implementation would await that call; this stub returns synchronously,
// so it intentionally has no await (and produces compiler warning CS1998).
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
快取金鑰指引
傳遞至 key 的 GetOrCreateAsync 必須唯一識別要快取的數據:
- 就用來從其來源擷取該數據的標識碼值而言。
- 就應用程式中快取的其他資料而言。
這兩種類型的唯一性通常透過串連字串的方式來確保,將不同部分合併成單一鍵字串。 例如:
cache.GetOrCreateAsync($"/orders/{region}/{orderId}", ...);
或
cache.GetOrCreateAsync($"user_prefs_{userId}", ...);
呼叫者有責任確保密鑰配置有效,而且無法造成數據變得混淆。
避免直接在快取索引鍵中使用外部用戶輸入。 例如,請勿使用使用者介面的原始字串作為快取索引鍵。 這麼做可能會讓您的應用程式面臨安全性風險,例如未經授權的存取或拒絕服務攻擊,因為快取中含有隨機或無意義的密鑰。 在上述有效範例中, 順序 和 用戶喜好設定 數據會清楚分隔並使用受信任的標識碼:
-
orderid和userId是內部產生的標識碼。 -
region可能是已知區域預先定義清單中的枚舉或字串。
標記如/或_沒有被當作重要的。 整個索引鍵值會被視為不透明的識別字串。 在此情況下,您可以省略 / 和 _,而不會改變快取的功能方式,但通常會使用分隔符來避免模稜兩可。例如,$"order{customerId}{orderId}" 可能會導致混淆:
-
customerId42,orderId123 -
customerId421 與orderId23
此兩個範例都會產生快取索引鍵 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)
{
// Placeholder for retrieving data from a real backing store (database, HTTP API, etc.).
// A real implementation would await that call. This stub returns synchronously and
// intentionally has no await, so the compiler emits warning CS1998 for this method.
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)
{
// Placeholder for retrieving data from a real backing store (database, HTTP API, etc.).
// A real implementation would await that call. This stub returns synchronously and
// intentionally has no await, so the compiler emits warning CS1998 for this method.
string someInfo = $"someinfo-{name}-{id}";
return someInfo;
}
}
使用標籤值呼叫 RemoveByTagAsync,以移除指定標籤的所有項目。 函式多載允許您指定一組標籤值。
IMemoryCache 和 IDistributedCache 都不直接支援標籤的概念,因此,基於標籤的失效是一種邏輯操作,而非實體刪除。 呼叫 RemoveByTagAsync 不會移除本地或分散快取的值。 取而代之的是,它為與該標籤相關的條目建立「忽略此點之前建立的任何內容」規則。
當 GetOrCreateAsync Later 取得該標籤的快取值時, HybridCache 會檢查該值是否在標籤失效前就已建立。 若是如此,該值將視為快取未命中,並呼叫工廠方法來取得新資料。 舊值會保留在 IMemoryCache 和 IDistributedCache 中,直到依其設定的存留期間以一般方式到期。
移除所有快取項目
星號標記 (*) 會保留為通配符,不允許針對個別值。 呼叫 RemoveByTagAsync("*") 會讓 所有數據HybridCache 失效,甚至是沒有任何標籤的數據。 如同個別標記,這是 邏輯 作業,而個別值會繼續存在,直到它們自然過期為止。 不支援 Glob 樣式匹配。 例如,您無法使用 RemoveByTagAsync("foo*") 來移除所有以 foo 開頭的內容。
其他標記考慮
- 系統不會限制您可以使用的標籤,但大量標記可能會對效能造成負面影響。
- 標記不能是空的、只有空格符或保留的值
*。
選項
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)
{
// Placeholder for retrieving data from a real backing store (database, HTTP API, etc.).
// A real implementation would await that call. This stub returns synchronously and
// intentionally has no await, so the compiler emits warning CS1998 for this method.
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、SQL Server 或 Postgres。 若要使用 Azure Cache for Redis,例如:
安裝
Microsoft.Extensions.Caching.StackExchangeRedis套件。建立 Azure Cache for Redis 的執行個體。
取得連線至 Redis 實體的連接字串。 在 Azure 入口網站的 [概觀] 頁面上選取 [顯示存取金鑰],以尋找連接字串。
將連接字串儲存在應用程式的組態中。 例如,使用類似下列 JSON 的使用者祕密檔案,以及
ConnectionStrings區段中的連接字串。 將<the 連接字串>取代為實際的連接字串:{ "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 或 Postgres,HybridCache 會使用 已設定的 IDistributedCache 實作(若有配置)。 但即使沒有 IDistributedCache 實作,該 HybridCache 服務仍提供程序內快取與 快取雪崩保護。
注意
依索引鍵或標記使快取項目失效時,它們會在目前伺服器和次要的進程外儲存中失效。 不過,其他伺服器中的記憶體內部快取不會受到影響。
注意
當主要快取是預設的 MemoryCache 時,HybridCache 並不會使用由 AddDistributedMemoryCache 註冊的 IDistributedCache 實作作為其次要快取。 該實作將條目存放在同一程序中,因此只會重複儲存主快取的內容。 如果它是唯一已註冊的 IDistributedCache,HybridCache 僅在處理序內快取,且快取項目不會在伺服器之間共用。 若要在伺服器間共享快取條目,請註冊一個處理程序外的實作,例如 Redis。
最佳化效能
若要將效能最佳化,請將 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 和 Microsoft.Extensions.Caching.Postgres 套件的預覽版本實作的。 如需詳細資訊,請參閱IBufferDistributedCache。
以下是安裝套件的 .NET CLI 命令:
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
dotnet add package Microsoft.Extensions.Caching.SqlServer
dotnet add package Microsoft.Extensions.Caching.Postgres
自訂 HybridCache 實作
HybridCache 抽象類別的具體實作包含在共用架構中,並透過相依性插入來提供。 但開發人員歡迎提供或取用 API 的自定義實作,例如 FusionCache。
搭配原生 AOT 使用混合式快取
下列原生 AOT 特定考慮適用於 HybridCache:
串行化
原生 AOT 不支援以運行時間反映為基礎的串行化。 如果您快取自定義類型,則必須使用來源產生器,或明確設定與 AOT 相容的序列化器,例如
System.Text.Json的來源產生器。HybridCache仍在開發中,並簡化與 AOT 搭配使用的方式,是該開發的首要任務。 如需詳細資訊,請參閱提取要求 dotnet/extensions#6475修剪
請確保您快取的所有類型,都已被設定為防止 AOT 編譯器修剪。 使用來源產生器進行串行化有助於符合這項需求。 如需詳細資訊,請參閱 ASP.NET 原生 AOT 的核心支援。
如果您正確設定串行化和修剪, HybridCache 在原生 AOT 中的行為方式與一般 ASP.NET Core 應用程式相同。
相容性
HybridCache 程式庫支援舊版 .NET 執行階段,向下至 .NET Framework 4.7.2 和 .NET Standard 2.0。
相關內容
如需詳細資訊,請參閱HybridCache原始程式碼