Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

สร้าง Hybrid Cache ใน .NET 9 ด้วย Redis สำหรับ Unified In-Memory และ Distributed Caching

Hybrid Cache (Hybrid Cache)

Hybrid Cache เป็น Unified Library สำหรับ Caching Data ใน In-memory และ External Sources ซึ่งอาจเรียกได้ว่า Multi-tier Caching กล่าวคือ Multi-tier Caching นี้เป็น Replacement สำหรับ IDistributedCache และ IMemoryCache มันยัง Intended (ตั้งใจ) ให้ Simplify การใช้ Caching ใน .NET วิธีเก่าสำหรับ Distributed Caching ต้องเขียน Code เพิ่มเพื่อ Ensure Things ถูก Cache และ Retrieve ได้ถูกต้อง Package นี้เป็น Amazing Addition สำหรับ Simplify Caching ใน .NET ทำให้เป็น Robust Caching Solution เราใช้ Microsoft.Extensions.Caching.Hybrid NuGet Package ในบทความนี้ เราจะครอบคลุม Features ทั้งหมดของ Hybrid Cache Package

Package Features

  • Extensible Code: Code ที่เขียนสำหรับ In-memory Cache ใช้ As-is ได้โดยไม่ต้อง Modify สำหรับ Integration กับ External Caching Servers เช่น Redis, SQL Server, etc.
  • Concurrency Management: เพื่อใช้ Hybrid Cache เราต้องใช้ HybridCache Class เป็น Dependency Service Class นี้ Ensure ว่า Only One Instance ใช้ Get Data สำหรับ Key ใดๆ และ No Concurrent Requests ทำสำหรับ Entry เดียวกัน All Concurrent Requests Wait สำหรับ Request นี้ให้ Complete
  • Multi-source Caching/Primary-secondary Source Caching: ถ้า Application Configure Multiple Sources สำหรับ Caching Data จะ Stored ที่ทุก Location

เพื่อ Get Data จะ Check ใน Primary Source ก่อน ถ้า Data ไม่ Available Request จะ Forward ไป Secondary Source ตาม Diagram ด้านล่าง Scenario 1 ในสีน้ำเงิน และ Scenario 2 ในสีแดง

Code Setup

เพื่อเริ่มต้น เราจะ Install Microsoft.Extensions.Caching.Hybrid Package ก่อน

ใน Program.cs เราจะเขียน Code ด้านล่างเพื่อ Add Hybrid Cache

builder.Services.AddHybridCache();

เพื่อ Configure Hybrid Cache เราเขียน Code ด้านล่างใน AddHybridCache เป็น Setup Action ตัวอย่างด้านล่าง

builder.Services.AddHybridCache(options => {
    options.ReportTagMetrics = true;
    options.DefaultEntryOptions = new Microsoft.Extensions.Caching.Hybrid.HybridCacheEntryOptions {
        Expiration = TimeSpan.FromSeconds(30),
        LocalCacheExpiration = TimeSpan.FromSeconds(30),
    };
});

Implement In-memory Cache

Dependency Service Configuration ด้านบนเพียงพอสำหรับ In-memory Caching ด้านล่างคือ Sample Code ใน API Controller สำหรับ Set และ Get Key และ Value ใน Application Memory

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Hybrid;
namespace HybridCacheDemoApplication.Controllers {
    [ApiController]
    [Route("[controller]")]
    public class CacheController : ControllerBase {
        private readonly HybridCache _cache;
        const string cacheKey = "ping";
        public CacheController(HybridCache cache) {
            _cache = cache;
        }
        [HttpGet(Name = "GetPingKey")]
        public async Task<IActionResult> Get() {
            var keydata = await _cache.GetOrCreateAsync<string>(
                cacheKey, // Unique key to the cache entry
                async cancel => await Task.FromResult("pong from get")
            );
            return Ok(new {message = $"{cacheKey} data: {keydata}"});
        }
        [HttpGet("set",Name = "SetPingKey")]
        public async Task<IActionResult> Set() {
            await _cache.SetAsync<string>(cacheKey, "ping");
            return Ok(new { message = $"{cacheKey} is set" });
        }
    }
}

เพื่อเข้าใจ Code นี้ทำงานอย่างไร ดู Table ด้านล่างตาม Sequence

Cache Execution Demo Table

Sequence Endpoint Output Explanation
1 https://localhost:5154/cache/
{ "message": "ping data: pong from get" }
Endpoint นี้ Set Cache Value เป็น "pong from get" เมื่อ Value ไม่ Set จึง Print "pong from get"
2 https://localhost:5154/cache/set
{ "message": "ping is set" }
Endpoint นี้จะ Set Value ใน Cache
3 https://localhost:5154/cache/
{ "message": "ping data: ping" }
Endpoint เดียวกับ Step 1 แตคราวนี้ Value มีใน Cache แล้ว

Delete Cache Key: เพื่อ Remove Cache Keys เราใช้ Implementation ด้านล่าง

[HttpGet("del",Name="DeletePingKey")]
public async Task<IActionResult> RemoveKey() {
    await _cache.RemoveAsync(cacheKey); //Removes data
    return Ok(new {message=$"{cacheKey} removed"});
}

Distributed Caching ใช้ Redis Server

เพื่อ Implement Distributed Caching เราต้องทำ Additional Dependency Service Configurations สำหรับตัวอย่างนี้ เราจะใช้ Redis Cache เป็น External Caching Source

ตอนนี้ เราจะ Configure Redis Package ด้วย Microsoft.Extensions.Caching.StackExchangeRedis

ติดตั้งแล้ว เราต้อง Add Code ด้านล่างเพื่อ Add Redis Connection String จะอยู่ใน Format {HOST_NAME}:{PORT_NUMBER},password={PASSWORD}

builder.Services.AddStackExchangeRedisCache(options => {
    options.Configuration = builder.Configuration.GetConnectionString("RedisConnectionString");
});

ตอนนี้ เราจะ Invoke https://localhost:5154/cache/set และ Data จะ Set ใน Redis Cache Seamlessly ด้านล่างคือลักษณะใน Redis

สร้าง Hybrid Cache ใน .NET 9 ด้วย Redis สำหรับ Unified In-Memory และ Distributed Caching

สังเกตว่า Code ของเราทำงาน Seamlessly เพียงแค่เพิ่ม Dependency สำหรับ Distributed Caching Server คือ Redis ใน Scenario ของเรา

Serialization

เพื่อส่ง Data ไป External Caching Server Package นี้ใช้ byte[], string, และ System.Text.Json นี้ Customizable เพิ่มเติมด้วย .AddSerializer() หรือ AddSerializerFactory() Methods เพิ่มเติมกับ AddHybrid Cache Dependency Service เมื่อเราใช้ Optimized Serializer มากขึ้น พวกมันปรับปรุง Performance ของ Application

More Customizations

เราสามารถ Customize Behavior ของ Cache Services เพิ่มเติมได้โดย Setting Flags เมื่อ Configure Dependencies ตัวอย่าง Flag หนึ่ง Disable Compression สำหรับ Caching

หมายเหตุ. นี่ไม่ใช่ Best Practice แต่อาจแตกต่างตาม Scenario เมื่อ Compression ไม่จำเป็นหรือไม่ใช้

builder.Services.AddHybridCache(options => {
    options.ReportTagMetrics = true;
    options.DefaultEntryOptions = new Microsoft.Extensions.Caching.Hybrid.HybridCacheEntryOptions {
        Flags = Microsoft.Extensions.Caching.Hybrid.HybridCacheEntryFlags.DisableCompression
    };
});

Experimental Feature: Tags - ทำงานกับ Inter-related Keys

บางครั้งเราอาจ Deal กับ Many Interrelated Keys ตัวอย่าง: ใน E-commerce Application เรา Cache หลาย Fields ที่ Correspond 到 Cart Item 到 User สำหรับนี้ เราสามารถสร้าง Tags ได้

Code ด้านล่าง สำหรับทำงานกับ Inter-related Keys เราสร้าง Tag ชื่อ "testdata" เราสามารถ Add มากกว่า 1 Tag ขึ้นอยู่กับ Type of Grouping ที่ Apply ได้สำหรับ Scenarios ต่างๆ Code นี้ Self-explanatory แต่ถ้ามี Confusion ให้ mention ใน Comments

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Hybrid;
namespace HybridCacheDemoApplication.Controllers {
    [ApiController]
    [Route("[controller]")]
    public class CacheController : ControllerBase {
        private readonly HybridCache _cache;
        const string cacheKey = "ping";
        public CacheController(HybridCache cache) {
            _cache = cache;
        }
        [HttpGet(Name = "GetPingKey")]
        public async Task<IActionResult> Get() {
            var keydata = await _cache.GetOrCreateAsync<string>(
                cacheKey, // Unique key to the cache entry
                async cancel => await Task.FromResult("pong from get"),
                tags: ["testdata"]
            );
            return Ok(new {message = $"{cacheKey} data: {keydata}"});
        }
        [HttpGet("set",Name = "SetPingKey")]
        public async Task<IActionResult> Set() {
            await _cache.SetAsync<string>(cacheKey, "ping", tags: ["testdata"] );
            return Ok(new { message = $"{cacheKey} is set" });
        }
        [HttpGet("del",Name="DeletePingKey")]
        public async Task<IActionResult> RemoveKey() {
            await _cache.RemoveAsync(cacheKey);
            return Ok(new {message=$"{cacheKey} removed"});
        }
        [HttpGet("delbytag", Name = "DeleteByTestTags")]
        public async Task<IActionResult> RemoveKeyByTags() {
            await _cache.RemoveByTagAsync(["testdata"]);
            return Ok(new { message = $"{cacheKey} removed" });
        }
    }
}

ขอบคุณที่อ่านจนจบ Hybrid Caching นี้เสนอ Amazing Benefits และหวังว่าคุณจะพบว่ามัน Helpful