কম্পিউটার টিউটোরিয়াল

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

এই নিবন্ধে এজার রেডিস ক্যাশের বেসিক এবং .NET Core Web API ব্যবহার করে এর বাস্তবায়ন নিয়ে আলোচনা করা হয়েছে।

এজেন্ডা

  • ভূমিকা
  • ক্যাশ কী?
  • ক্যাশের প্রকারভেদ
  • রেডিস ক্যাশ
  • এজার রেডিস ক্যাশ সেটআপ
  • ধাপে ধাপে বাস্তবায়ন

পূর্বশর্ত

  • Visual Studio 2022
  • এজার অ্যাকাউন্ট
  • .NET Core 6

ভূমিকা

সফ্টওয়্যার ইন্ডাস্ট্রিতে ক্যাশিং বর্তমানে খুব জনপ্রিয় কারণ এটি অ্যাপ্লিকেশনের পারফরম্যান্স এবং স্কেলেবিলিটি উন্নত করে। আমরা Gmail এবং Facebook-এর মতো অনেক ওয়েব অ্যাপ্লিকেশন ব্যবহার করি এবং দেখি কত রেসপন্সিভ সেগুলো, এবং আমাদের চমৎকার ইউজার এক্সপেরিয়েন্স থাকে। ইন্টারনেটে প্রচুর ব্যবহারকারী রয়েছে এবং যদি একটি অ্যাপ্লিকেশনে বিশাল নেটওয়ার্ক ট্রাফিক এবং ডিমান্ড থাকে, তবে আমাদের অনেক জিনিস যত্ন নিতে হয় যা অ্যাপ্লিকেশনের পারফরম্যান্স এবং রেসপন্সিভনেস উন্নত করতে সাহায্য করে। তাই, তাই ক্যাশিং সমাধান আসলো এবং তাই ক্যাশিং চিত্রে এলো।

ক্যাশ কী?

ক্যাশ হলো একটি মেমরি স্টোরেজ যা ঘন ঘন অ্যাক্সেস করা ডেটাকে অস্থায়ী স্টোরেজে সংরক্ষণ করতে ব্যবহৃত হয়, এটি পারফরম্যান্স ড্রাস্টিক্যালি উন্নত করে, অনাবশ্যক ডেটাবেস হিট এড়ায় এবং ঘন ঘন ব্যবহৃত ডেটাকে ক্যাশে সংরক্ষণ করে।

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

উপরের ছবিতে দুটি পরিস্থিতি দেখানো হয়েছে: একটি ক্যাশ ছাড়া, আরেকটি ক্যাশ সহ। যখন ক্যাশ ব্যবহার করা হয় না, তখন ব্যবহারকারীরা ডেটা চাইলে প্রতিবার ডেটাবেস হিট করে, যা টাইম কমপ্লেক্সিটি বাড়ায় এবং পারফরম্যান্স কমিয়ে দেয়, বিশেষ করে যখন সব ব্যবহারকারীর জন্য একই স্ট্যাটিক ডেটা থাকে। ক্যাশ না ব্যবহার করলে প্রতিটি ব্যবহারকারী অনাবশ্যক ডেটাবেস হিট করে ডেটা ফেচ করে। অন্যদিকে, ক্যাশ ব্যবহার করলে, যদি সব ব্যবহারকারীর জন্য একই স্ট্যাটিক ডেটা থাকে, তবে শুধু প্রথম ব্যবহারকারী ডেটাবেস হিট করে, ডেটা ফেচ করে, ক্যাশ মেমরিতে সংরক্ষণ করে, এবং বাকি ব্যবহারকারীরা অনাবশ্যক ডেটাবেস হিট ছাড়াই ক্যাশ থেকে ডেটা ব্যবহার করে।

ক্যাশের প্রকারভেদ

মূলত .NET Core দুটি ধরনের ক্যাশিং সাপোর্ট করে

  1. ইন-মেমরি ক্যাশিং (In-Memory Caching)
  2. ডিস্ট্রিবিউটেড ক্যাশিং (Distributed Caching)

ইন-মেমরি ক্যাশ ব্যবহার করলে ডেটা অ্যাপ্লিকেশন সার্ভারের মেমরিতে সংরক্ষণ হয়; প্রয়োজন पड़লে সেখান থেকে ফেচ করে ব্যবহার করা হয়। ডিস্ট্রিবিউটেড ক্যাশিং-এ রেডিস সহ অনেক থার্ড-পার্টি মেকানিজম রয়েছে। কিন্তু এই সেকশনে আমরা রেডিস ক্যাশে বিস্তারিতভাবে দেখব এবং এটি .NET Core-এ কীভাবে কাজ করে।

ডিস্ট্রিবিউটেড ক্যাশিং

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

  • ব্যাসিক্যালি, ডিস্ট্রিবিউটেড ক্যাশিং-এ ডেটা একাধিক সার্ভারের মধ্যে সংরক্ষণ ও শেয়ার করা হয়
  • এছাড়াও, মাল্টি-টেন্যান্ট অ্যাপ্লিকেশন ব্যবহার করার সময় একাধিক সার্ভারের মধ্যে লোড ম্যানেজ করার পর অ্যাপ্লিকেশনের স্কেলেবিলিটি এবং পারফরম্যান্স উন্নত করা সহজ
  • মান लीজिए, ভবিষ্যতে যদি একটি সার্ভার ক্র্যাশ করে এবং রিস্টার্ট হয়, তবে অ্যাপ্লিকেশনে কোনো প্রভাব পড়বে না কারণ আমাদের প্রয়োজন অনুযায়ী একাধিক সার্ভার থাকতে পারে

রেডিস বর্তমানে সবচেয়ে জনপ্রিয় ক্যাশ, যা অনেক কোম্পানি অ্যাপ্লিকেশনের পারফরম্যান্স এবং স্কেলেবিলিটি উন্নত করার জন্য ব্যবহার করে। তাই, আমরা একে একে রেডিস এবং এর ব্যবহার নিয়ে আলোচনা করব।

রেডিস ক্যাশ

  • রেডিস একটি ওপেন সোর্স (BSD লাইসেন্সড) ইন-মেমরি ডেটা স্ট্রাকচার স্টোর যা ডেটাবেস হিসেবে ব্যবহৃত হয়।
  • ব্যাসিক্যালি, এটি ঘন ঘন ব্যবহৃত এবং কিছু স্ট্যাটিক ডেটা ক্যাশে সংরক্ষণ করে এবং ব্যবহারকারীর প্রয়োজন অনুযায়ী সেটি সংরক্ষণ ও ব্যবহার করে।
  • রেডিসে অনেক ডেটা স্ট্রাকচার রয়েছে যা আমরা ব্যবহার করতে পারি যেমন List, Set, Hashing, Stream, এবং অন্যান্য।

এজার রেডিস ক্যাশ সেটআপ

ধাপ ১

এজার পোর্টালে লগ ইন করুন।

ধাপ ২

মার্কেটপ্লেসে "Azure Cache for Redis" সার্চ করুন এবং খুলুন।

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

ধাপ ৩

Create-এ ক্লিক করুন এবং অন্যান্য তথ্য প্রদান করুন।

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

ধাপ ৪

এরপর আগে তৈরি করা ক্যাশের Access keys সেকশনে যান এবং প্রাইমারি কানেকশন স্ট্রিং কপি করুন যা .NET Core Web API-তে প্রয়োজন।

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

ধাপে ধাপে বাস্তবায়ন

ধাপ ১

Visual Studio খুলুন এবং একটি নতুন .NET Core Web API প্রজেক্ট তৈরি করুন।

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

ধাপ ২

নতুন প্রজেক্ট কনফিগার করুন।

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

ধাপ ৩

অ্যাডিশনাল ডিটেইলস প্রদান করুন।

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

ধাপ ৪

প্রজেক্ট স্ট্রাকচার।

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

ধাপ ৫

ProductDetails ক্লাস তৈরি করুন।

namespace AzureRedisCacheDemo.Models {
    public class ProductDetails {
        public int Id { get; set; }
        public string ProductName { get; set; }
        public string ProductDescription { get; set; }
        public int ProductPrice { get; set; }
        public int ProductStock { get; set; }
    }
}

ধাপ ৬

Data ফোল্ডারের ভিতরে DbContext ক্লাস যোগ করুন।

using AzureRedisCacheDemo.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
namespace AzureRedisCacheDemo.Data {
    public class DbContextClass: DbContext {
        public DbContextClass(DbContextOptions < DbContextClass > options): base(options) {}
        public DbSet < ProductDetails > Products { get; set; }
    }
}

ধাপ ৭

उसके बाद, SeedData ক্লাস যোগ করুন যা আমরা প্রাথমিক ডেটা ইনসার্টের জন্য ব্যবহার করব।

using AzureRedisCacheDemo.Models;
using Microsoft.EntityFrameworkCore;
namespace AzureRedisCacheDemo.Data {
    public class SeedData {
        public static void Initialize(IServiceProvider serviceProvider) {
            using (var context = new DbContextClass(
                serviceProvider.GetRequiredService<DbContextOptions<DbContextClass>>())) {
                if (context.Products.Any()) { return; }
                context.Products.AddRange(
                    new ProductDetails {
                        Id = 1,
                        ProductName = "IPhone",
                        ProductDescription = "IPhone 14",
                        ProductPrice = 120000,
                        ProductStock = 100
                    },
                    new ProductDetails {
                        Id = 2,
                        ProductName = "Samsung TV",
                        ProductDescription = "Smart TV",
                        ProductPrice = 400000,
                        ProductStock = 120
                    });
                context.SaveChanges();
            }
        }
    }
}

ধাপ ৮

appsettings.json ফাইলে এজার রেডিস ক্যাশ কানেকশন স্ট্রিং কনফিগার করুন।

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "RedisURL": "<valuefromportal>"
}

ধাপ ৯

Helper ফোল্ডারের ভিতরে ConfigurationManager এবং ConnectionHelper ক্লাস তৈরি করুন যা কানেকশনের জন্য ব্যবহার করব।

Configuration Manager

namespace AzureRedisCacheDemo.Helper {
    static class ConfigurationManager {
        public static IConfiguration AppSetting { get; }
        static ConfigurationManager() {
            AppSetting = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build();
        }
    }
}

Connection Helper

using StackExchange.Redis;
namespace AzureRedisCacheDemo.Helper {
    public class ConnectionHelper {
        static ConnectionHelper() {
            ConnectionHelper.lazyConnection = new Lazy<ConnectionMultiplexer>(() => {
                return ConnectionMultiplexer.Connect(ConfigurationManager.AppSetting["RedisURL"]);
            });
        }
        private static Lazy<ConnectionMultiplexer> lazyConnection;
        public static ConnectionMultiplexer Connection {
            get { return lazyConnection.Value; }
        }
    }
}

ধাপ ১০

Repositories-এ IProductService যোগ করুন।

using AzureRedisCacheDemo.Models;
namespace AzureRedisCacheDemo.Repositories {
    public interface IProductService {
        public Task<List<ProductDetails>> ProductListAsync();
        public Task<ProductDetails> GetProductDetailByIdAsync(int productId);
        public Task<bool> AddProductAsync(ProductDetails productDetails);
        public Task<bool> UpdateProductAsync(ProductDetails productDetails);
        public Task<bool> DeleteProductAsync(int productId);
    }
}

ধাপ ১১

ProductService ক্লাস তৈরি করুন এবং IProductService ইন্টারফেস বাস্তবায়ন করুন।

using AzureRedisCacheDemo.Data;
using AzureRedisCacheDemo.Models;
using Microsoft.EntityFrameworkCore;
using System.Linq;
namespace AzureRedisCacheDemo.Repositories {
    public class ProductService: IProductService {
        private readonly DbContextClass dbContextClass;
        public ProductService(DbContextClass dbContextClass) {
            this.dbContextClass = dbContextClass;
        }
        public async Task<List<ProductDetails>> ProductListAsync() {
            return await dbContextClass.Products.ToListAsync();
        }
        public async Task<ProductDetails> GetProductDetailByIdAsync(int productId) {
            return await dbContextClass.Products.Where(ele => ele.Id == productId).FirstOrDefaultAsync();
        }
        public async Task<bool> AddProductAsync(ProductDetails productDetails) {
            await dbContextClass.Products.AddAsync(productDetails);
            var result = await dbContextClass.SaveChangesAsync();
            if (result > 0) { return true; } else { return false; }
        }
        public async Task<bool> UpdateProductAsync(ProductDetails productDetails) {
            var isProduct = ProductDetailsExists(productDetails.Id);
            if (isProduct) {
                dbContextClass.Products.Update(productDetails);
                var result = await dbContextClass.SaveChangesAsync();
                if (result > 0) { return true; } else { return false; }
            }
            return false;
        }
        public async Task<bool> DeleteProductAsync(int productId) {
            var findProductData = dbContextClass.Products.Where(_ => _.Id == productId).FirstOrDefault();
            if (findProductData != null) {
                dbContextClass.Products.Remove(findProductData);
                var result = await dbContextClass.SaveChangesAsync();
                if (result > 0) { return true; } else { return false; }
            }
            return false;
        }
        private bool ProductDetailsExists(int productId) {
            return dbContextClass.Products.Any(e => e.Id == productId);
        }
    }
}

ধাপ ১২

IRedisCache ইন্টারফেস তৈরি করুন।

namespace AzureRedisCacheDemo.Repositories.AzureRedisCache {
    public interface IRedisCache {
        T GetCacheData<T>(string key);
        bool SetCacheData<T>(string key, T value, DateTimeOffset expirationTime);
        object RemoveData(string key);
    }
}

ধাপ ১৩

RedisCache ক্লাস তৈরি করুন এবং ইন্টারফেস মেথডগুলো বাস্তবায়ন করুন।

using AzureRedisCacheDemo.Helper;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace AzureRedisCacheDemo.Repositories.AzureRedisCache {
    public class RedisCache : IRedisCache {
        private IDatabase _db;
        public RedisCache() { ConfigureRedis(); }
        private void ConfigureRedis() { _db = ConnectionHelper.Connection.GetDatabase(); }
        public T GetCacheData<T>(string key) {
            var value = _db.StringGet(key);
            if (!string.IsNullOrEmpty(value)) {
                return JsonConvert.DeserializeObject<T>(value);
            }
            return default;
        }
        public object RemoveData(string key) {
            bool _isKeyExist = _db.KeyExists(key);
            if (_isKeyExist == true) { return _db.KeyDelete(key); }
            return false;
        }
        public bool SetCacheData<T>(string key, T value, DateTimeOffset expirationTime) {
            TimeSpan expiryTime = expirationTime.DateTime.Subtract(DateTime.Now);
            var isSet = _db.StringSet(key, JsonConvert.SerializeObject(value), expiryTime);
            return isSet;
        }
    }
}

ধাপ ১৪

নতুন Products Controller তৈরি করুন।

using AzureRedisCacheDemo.Models;
using AzureRedisCacheDemo.Repositories;
using AzureRedisCacheDemo.Repositories.AzureRedisCache;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace AzureRedisCacheDemo.Controllers {
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase {
        private readonly IProductService _productService;
        private readonly IRedisCache _redisCache;
        public ProductsController(IProductService productService, IRedisCache redisCache) {
            _productService = productService;
            _redisCache = redisCache;
        }
        [HttpGet]
        public async Task<ActionResult<List<ProductDetails>>> ProductListAsync() {
            var cacheData = _redisCache.GetCacheData<List<ProductDetails>>("product");
            if (cacheData != null) { return new List<ProductDetails>(cacheData); }
            var productList = await _productService.ProductListAsync();
            if(productList != null) {
                var expirationTime = DateTimeOffset.Now.AddMinutes(5.0);
                _redisCache.SetCacheData<List<ProductDetails>>("product", productList, expirationTime);
                return Ok(productList);
            } else { return NoContent(); }
        }
        [HttpGet("{productId}")]
        public async Task<ActionResult<ProductDetails>> GetProductDetailsByIdAsync(int productId) {
            var cacheData = _redisCache.GetCacheData<List<ProductDetails>>("product");
            if (cacheData != null) {
                ProductDetails filteredData = cacheData.Where(x => x.Id == productId).FirstOrDefault();
                return new ActionResult<ProductDetails>(filteredData);
            }
            var productDetails = await _productService.GetProductDetailByIdAsync(productId);
            if(productDetails != null) { return Ok(productDetails); }
            else { return NotFound(); }
        }
        [HttpPost]
        public async Task<IActionResult> AddProductAsync(ProductDetails productDetails) {
            var isProductInserted = await _productService.AddProductAsync(productDetails);
            _redisCache.RemoveData("product");
            if (isProductInserted) { return Ok(isProductInserted); }
            else { return BadRequest(); }
        }
        [HttpPut]
        public async Task<IActionResult> UpdateProductAsync(ProductDetails productDetails) {
            var isProductUpdated = await _productService.UpdateProductAsync(productDetails);
            _redisCache.RemoveData("product");
            if (isProductUpdated) { return Ok(isProductUpdated); }
            else { return BadRequest(); }
        }
        [HttpDelete]
        public async Task<IActionResult> DeleteProductAsync(int productId) {
            var isProductDeleted = await _productService.DeleteProductAsync(productId);
            _redisCache.RemoveData("product");
            if (isProductDeleted) { return Ok(isProductDeleted); }
            else { return BadRequest(); }
        }
    }
}

ধাপ ১৫

Program ক্লাসে কিছু সার্ভিস রেজিস্টার করুন।

using AzureRedisCacheDemo.Data;
using AzureRedisCacheDemo.Models;
using AzureRedisCacheDemo.Repositories;
using AzureRedisCacheDemo.Repositories.AzureRedisCache;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using System;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddDbContext<DbContextClass>(o => o.UseInMemoryDatabase("RedisCacheDemo"));
builder.Services.AddScoped<IRedisCache, RedisCache>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
using(var scope = app.Services.CreateScope()) {
    var services = scope.ServiceProvider;
    var context = services.GetRequiredService<DbContextClass>();
    SeedData.Initialize(services);
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) {
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

ধাপ ১৬

শেষে অ্যাপ্লিকেশন চালান এবং Swagger UI-তে আমাদের API এন্ডপয়েন্টগুলো দেখুন।

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

ধাপ ১৭

Get product এন্ডপয়েন্ট হিট করুন এবং এজার পোর্টালের ভিতরে Redis CLI খুলুন, সেখানে আপনি দেখতে পাবেন প্রথমবার এন্ডপয়েন্ট হিট করলে প্রোডাক্ট লিস্ট ক্যাশে সংরক্ষিত হবে।

.NET 6 Web API-তে এজার রেডিস ক্যাশ বাস্তবায়ন: ধাপে ধাপে গাইড

এই কেসে, প্রথমে চেক করা হয় ডেটা ক্যাশে আছে কিনা। যদি না থাকে, তবে ডেটাবেস থেকে ডেটা পেয়ে সেটিও ক্যাশে সংরক্ষণ করা হয়। আমরা ইতিমধ্যে কন্ট্রোলারে সেটি সম্পর্কিত কোড লিখেছি। তাই পরবর্তীতে ডেটা ক্যাশ থেকে আসবে। আপনি কন্ট্রোলারে ডিবাগার দিয়ে সহজে বুঝতে পারবেন কীভাবে জিনিসগুলো কাজ করছে।

GitHub URL

https://github.com/Jaydeep-007/AzureRedisCacheDemo/tree/master/AzureRedisCacheDemo

উপসংহার

এখানে আমরা ক্যাশ পরিচিতি এবং এজারে তাদের কনফিগারেশন দেখেছি। এছাড়াও .NET Core Web API ব্যবহার করে ধাপে ধাপে বাস্তবায়ন।

  1. Windows 10-এ Microsoft SQL Server 2019 ইনস্টল করার ধাপাবলি

  2. C++ দিয়ে ট্রি-এ সাবট্রির DFS কোয়ারী সমাধান

  3. JavaScript-এ For Loops: ক্লাসিক, for...in এবং for...of

  4. Pillow লাইব্রেরি ব্যবহার করে একটি ছবিতে বক্স ব্লার প্রয়োগ করা