- Published on
เพิ่มประสิทธิภาพเว็บแบบง่ายๆด้วย HybridCache ใน .NET9

การทำ caching ใน .NET มีอยู่ 2 แบบคือ In-Memory cache กับ Distributed caching เป็นการใช้ฐานข้อมูลเช่น Redis เพื่อเก็บ cache ทำให้เราต้องเขียนโค้ดแยกกันสำหรับแต่ละแบบ
ใน .NET 9 ได้เพิ่ม HybridCache มาแก้ปัญหานี้โดยการใช้โค้ดชุดเดียวแต่ทำงานได้ทั้งสองแบบ
ลองสร้าง API ที่ใช้เวลาในการคืนข้อมูลประมาณ 1-5 วินาที
app.MapGet(
"/posts",
async () =>
{
List<Post> posts =
[
new()
{
Id = 1,
Title = "Post 1",
Content = "Content 1",
},
new()
{
Id = 2,
Title = "Post 2",
Content = "Content 2",
},
new()
{
Id = 3,
Title = "Post 3",
Content = "Content 3",
},
];
Random random = new();
int delay = random.Next(1000, 5000);
await Task.Delay(delay);
return Results.Ok(posts);
}
);
มาเริ่มใช้ HybridCache โดยการติดตั้ง Nuget Microsoft.Extensions.Caching.Hybrid
แล้วเพิ่ม
builder.Services.AddHybridCache();
แค่นี้เราก็พร้อมใช้ cache แบบ In-Memory แล้ว
มาเพิ่ม API อีกตัวเพื่อเรียก API ที่ไว้ก่อนหน้านี้
app.MapGet(
// argument แรกเป็นชื่อของ API
// ตั้งชื่อ posts ใน path รับ id ที่เป็น int
"/posts/{id:int}",
// argument ที่สองเป็น function เพื่อคืนค่า
// มี parameter int id ที่รับมาจาก path แล้วก็ inject HybridCache เข้ามา
async (int id, HybridCache cache) =>
{
// ใช้คำสั่ง GetOrCreateAsync ของ HybridCache เพื่อดึงหรือสร้าง cache
List<Post>? posts = await cache.GetOrCreateAsync(
// argument แรกเป็น key ของ cache
"hybrid:posts",
// arguemnt ที่สองเป็น function factory ในกรณีที่ถ้ายังไม่มีข้อมูลใน cache function จะถูกเรียกแล้วเก็บข้อมูลที่ได้ไว้ใน cache ต้องใส่ parameter CancellationToken ด้วย ให้ชื่อ cancel
async (cancel) =>
{
// แล้วก็เรียกใช้ API ที่เตรียมไว้โดยใช้ HttpClient
HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(
"http://localhost:5228/posts",
cancel
);
// ถ้ามี error ก็ให้คืนเป็น array เปล่า
if (!response.IsSuccessStatusCode)
{
return [];
}
// ไม่มี error คืนค่าที่ได้มาจาก API
return await response.Content.ReadFromJsonAsync<List<Post>>(cancel);
// จากตรงนี้ข้อมูลจะถูกเก็บลง cache อัตโนมัติโดยที่เราไม่ต้องทำอะไรเพิ่ม
}
);
// ใช้ id จาก path เพื่อหาข้อมูลจาก list
Post? post = posts?.FirstOrDefault(x => x.Id == id);
// ไม่เจอก็ NotFound
if (post == null)
{
return Results.NotFound();
}
// เจอก็ Ok พร้อมข้อมูล post
return Results.Ok(post);
}
);
เตรียม http request แล้วลองรันดู
@HyBridCacheDemo_HostAddress = http://localhost:8080
GET {{HyBridCacheDemo_HostAddress}}/posts

แน่นอนว่าใช้เวลาไม่ต่ำกว่า 1-5 วินาที แล้วลองเรียก API ที่มีการเก็บ cache
GET {{HyBridCacheDemo_HostAddress}}/posts/1
เรียกครั้งแรกจะให้เวลาไม่ต่ำกว่า 1-5 วินาทีเหมือนกัน

ลองเรียกอีกครั้งจะใช้เวลาแค่เสี้ยววินาทีเท่านั้น

ตอนนี้ cache ถูกเก็บไว้ใน memory เมื่อ restart แล้วเรียก GET /posts/1 อีกทีก็จะใช้เวลา 1-5 วินาทีเพื่อเก็บข้อมูลเข้า cache ใหม่
คราวนี้มาทำให้ข้อมูล cache ถูกเก็บไว้ในฐานข้อมูลบ้าง โดยใช้ Redis เป็นฐานข้อมูล
ติดตั้ง Nuget Microsoft.Extensions.Caching.StackExchangeRedis
แล้วเพิ่ม code
// เรียก AddStackExchangeRedisCache เพื่อเชื่อมต่อ Redis
builder.Services.AddStackExchangeRedisCache(options =>
{
// ใช้ Redis connection ที่อยู่ใน appSettings
options.Configuration = builder
.Configuration.GetConnectionString("RedisConnection");
});
มาดูวีธีเรียกใช้ Distributed caching เพื่อเก็บ cache ไว้ในฐานข้อมูลแบบเก่ากันก่อน
app.MapGet(
"/dist-posts",
async (IDistributedCache cache) =>
{
string key = $"dist:posts";
// ทำการเช็คก่อนว่ามี cache เก็บอยู่รึป่าว
string? json = await cache.GetStringAsync(key);
List<Post>? posts = [];
// ถ้าไม่มีก็ให้ดึงข้อมูลเพื่อทำ cache
if (string.IsNullOrEmpty(json))
{
HttpClient client = new();
HttpResponseMessage response = await client.GetAsync("http://localhost:5228/posts");
if (response.IsSuccessStatusCode)
{
posts = await response.Content.ReadFromJsonAsync<List<Post>>();
// ได้ข้อมูลเสร็จต้อง serialize แล้วเก็บลอง cache
json = JsonSerializer.Serialize(posts);
await cache.SetStringAsync(key, json);
}
}
// ถ้ามีก็ deserialize เป็นค่าที่ต้องการ
else
{
posts = JsonSerializer.Deserialize<List<Post>>(json) ?? [];
}
return posts;
}
);
จะเห็นว่าการใช้ Distributed caching มีขั้นตอนที่เยอะกว่า HybridCache อยู่ เพราะต้อง Serialize และ Deserialize เอง
HybridCache เราสามารถใช้ GetOrCreateAsync
โดยที่ไม่ได้เช็คอะไรเลย ลอง request /posts/1 อีกที
แล้วไปดูที่ Redis ก็จะมีข้อมูลที่เราส่งไปทำ cache

การทำงานของ HybridCache จะเช็คว่ามีการตั้งค่าใช้งาน Distributed caching อยู่รึป่าว ถ้ามีก็จะทำการเก็บ cache ไว้ใน memory ก่อนจากนั้นก็เก็บลงฐานข้อมูล
HybridCache ทำให้การ caching ใน .NET ง่ายขึ้นมาก ที่เด็ดกว่านั้นคือ แต่ไม่ใช่แค่ .NET 9 เท่านั้นที่สามารถใช้งานได้ .NET Framework ตั้งแต่ 4.7.2 ก็สามารถใช้งานความสามารถนี้ได้เช่นกัน