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

.NET Core API-তে রেডিস ক্যাশিং ইন্টিগ্রেশন পারফরম্যান্স ও স্কেলেবিলিটির জন্য

ভূমিকা

আমরা .NET Core-এ ক্যাশিং এবং এটি কীভাবে কাজ করে সেটি আলোচনা করব। এর জন্য আমরা নিম্নলিখিত বিষয়গুলো একে একে দেখব।

  • ক্যাশিং পরিচিতি
  • ক্যাশ কী?
  • ক্যাশের প্রকারভেদ
  • ক্যাশ বাস্তবায়ন

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

তো, চলুন একে একে শুরু করি।

ক্যাশিং কী?

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

.NET Core API-তে রেডিস ক্যাশিং ইন্টিগ্রেশন পারফরম্যান্স ও স্কেলেবিলিটির জন্য

.NET Core API-তে রেডিস ক্যাশিং ইন্টিগ্রেশন পারফরম্যান্স ও স্কেলেবিলিটির জন্য

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

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

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

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

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

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

.NET Core API-তে রেডিস ক্যাশিং ইন্টিগ্রেশন পারফরম্যান্স ও স্কেলেবিলিটির জন্য

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

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

রেডিস ক্যাশ

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

রেডিস ক্যাশ ইনস্টলেশন

ধাপ ১। নিচের URL থেকে রেডিস সার্ভার ডাউনলোড করুন।

https://github.com/microsoftarchive/redis/releases/tag/win-3.0.504

ধাপ ২। জিপ ফাইল এক্সট্র্যাক্ট করুন এবং পরে রেডিস সার্ভার এবং রেডিস CLI খুলুন।

.NET Core API-তে রেডিস ক্যাশিং ইন্টিগ্রেশন পারফরম্যান্স ও স্কেলেবিলিটির জন্য

.NET Core API ব্যবহার করে রেডিস ক্যাশ বাস্তবায়ন

ধাপ ১। .NET Core API ওয়েব অ্যাপ্লিকেশন তৈরি করুন।

ধাপ ২। আমাদের অ্যাপ্লিকেশনে ধাপে ধাপে নিচের NuGet প্যাকেজগুলো ইনস্টল করুন।

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Design
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Swashbuckle.AspNetCore
  • StackExchange.Redis

ধাপ ৩। Model ফোল্ডার তৈরি করুন এবং সেটিতে Product ক্লাস তৈরি করুন।

namespace RedisCacheDemo.Model {
    public class Product {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
        public string ProductDescription { get; set; }
        public int Stock { get; set; }
    }
}

ধাপ ৪। ডেটাবেস অপারেশনের জন্য DbContextClass তৈরি করুন।

using Microsoft.EntityFrameworkCore;
using RedisCacheDemo.Model;
namespace RedisCacheDemo.Data {
    public class DbContextClass: DbContext {
        public DbContextClass(DbContextOptions < DbContextClass > options): base(options) {}
        public DbSet < Product > Products { get; set; }
    }
}

ধাপ ৫। এখন রেডিস ক্যাশ সম্পর্কিত ব্যবহারের জন্য ICacheService ইন্টারফেস এবং CacheService ক্লাস তৈরি করুন।

using System;
namespace RedisCacheDemo.Cache {
    public interface ICacheService {
        T GetData<T>(string key);
        bool SetData<T>(string key, T value, DateTimeOffset expirationTime);
        object RemoveData(string key);
    }
}
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
namespace RedisCacheDemo.Cache {
    public class CacheService: ICacheService {
        private IDatabase _db;
        public CacheService() { ConfigureRedis(); }
        private void ConfigureRedis() { _db = ConnectionHelper.Connection.GetDatabase(); }
        public T GetData<T>(string key) {
            var value = _db.StringGet(key);
            if (!string.IsNullOrEmpty(value)) {
                return JsonConvert.DeserializeObject<T>(value);
            }
            return default;
        }
        public bool SetData<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;
        }
        public object RemoveData(string key) {
            bool _isKeyExist = _db.KeyExists(key);
            if (_isKeyExist == true) { return _db.KeyDelete(key); }
            return false;
        }
    }
}

ধাপ ৬। ProductController ক্লাস তৈরি করুন এবং নিচের মেথডগুলো যোগ করুন।

using Microsoft.AspNetCore.Mvc;
using RedisCacheDemo.Cache;
using RedisCacheDemo.Data;
using RedisCacheDemo.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RedisCacheDemo.Controllers {
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController: ControllerBase {
        private readonly DbContextClass _dbContext;
        private readonly ICacheService _cacheService;
        public ProductController(DbContextClass dbContext, ICacheService cacheService) {
            _dbContext = dbContext;
            _cacheService = cacheService;
        }
        [HttpGet("products")]
        public IEnumerable<Product> Get() {
            var cacheData = _cacheService.GetData<IEnumerable<Product>>("product");
            if (cacheData != null) { return cacheData; }
            var expirationTime = DateTimeOffset.Now.AddMinutes(5.0);
            cacheData = _dbContext.Products.ToList();
            _cacheService.SetData<IEnumerable<Product>>("product", cacheData, expirationTime);
            return cacheData;
        }
        [HttpGet("product")]
        public Product Get(int id) {
            Product filteredData;
            var cacheData = _cacheService.GetData<IEnumerable<Product>>("product");
            if (cacheData != null) {
                filteredData = cacheData.Where(x => x.ProductId == id).FirstOrDefault();
                return filteredData;
            }
            filteredData = _dbContext.Products.Where(x => x.ProductId == id).FirstOrDefault();
            return filteredData;
        }
        [HttpPost("addproduct")]
        public async Task<Product> Post(Product value) {
            var obj = await _dbContext.Products.AddAsync(value);
            _cacheService.RemoveData("product");
            _dbContext.SaveChanges();
            return obj.Entity;
        }
        [HttpPut("updateproduct")]
        public void Put(Product product) {
            _dbContext.Products.Update(product);
            _cacheService.RemoveData("product");
            _dbContext.SaveChanges();
        }
        [HttpDelete("deleteproduct")]
        public void Delete(int Id) {
            var filteredData = _dbContext.Products.Where(x => x.ProductId == Id).FirstOrDefault();
            _dbContext.Remove(filteredData);
            _cacheService.RemoveData("product");
            _dbContext.SaveChanges();
        }
    }
}

ধাপ ৭। appsettings.json-এ SQL Server কানেকশন স্ট্রিং এবং রেডিস URL যোগ করুন।

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "RedisURL": "127.0.0.1:6379",
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=Server;Initial Catalog=RedisCache;User Id=sa;Password=***;"
  }
}

ধাপ ৮। Startup ক্লাসের ConfigureServices মেথডে ICacheService রেজিস্টার করুন এবং Swagger কনফিগারেশন যোগ করুন।

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using RedisCacheDemo.Cache;
using RedisCacheDemo.Data;
namespace RedisCacheDemo {
    public class Startup {
        public Startup(IConfiguration configuration) { Configuration = configuration; }
        public IConfiguration Configuration { get; }
        public void ConfigureServices(IServiceCollection services) {
            services.AddControllers();
            services.AddScoped<ICacheService, CacheService>();
            services.AddDbContext<DbContextClass>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            services.AddSwaggerGen(c => {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "RedisCacheDemo", Version = "v1" });
            });
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
            if (env.IsDevelopment()) {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "RedisCacheDemo v1"));
            }
            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
        }
    }
}

ধাপ ৯। অ্যাপ সেটিং কনফিগার করার জন্য ConfigurationManager ক্লাস তৈরি করুন।

using Microsoft.Extensions.Configuration;
using System.IO;
namespace RedisCacheDemo {
    static class ConfigurationManager {
        public static IConfiguration AppSetting { get; }
        static ConfigurationManager() {
            AppSetting = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build();
        }
    }
}

ধাপ ১০। রেডিস কানেকশনের জন্য ConnectionHelper ক্লাস তৈরি করুন।

using StackExchange.Redis;
using System;
namespace RedisCacheDemo.Cache {
    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; }
        }
    }
}

ধাপ ১১। Package Manager Console-এ নিচের কমান্ডগুলো দিয়ে মাইগ্রেশন এবং ডেটাবেস আপডেট করুন।

add-migration "FirstMigration"
update-database

ধাপ ১২। অবশেষে অ্যাপ্লিকেশন চালান এবং Swagger UI ব্যবহার করে ডেটা যোগ করুন এবং দেখুন ক্যাশিং products এবং product এন্ডপয়েন্টে কীভাবে কাজ করে।

ব্যাসিক্যালি, আমি প্রোডাক্ট এবং প্রোডাক্টস এন্ডপয়েন্টে ক্যাশ যোগ করেছি; আপনি দেখতে পাচ্ছেন, যখন ব্যবহারকারী সব প্রোডাক্টের ডেটা ফেচ করতে চায়, তবে প্রথমে চেক করা হয় ডেটা রেডিস ক্যাশে আছে কিনা এবং যদি থাকে তবে সেটি ব্যবহারকারীকে রিটার্ন করা হয়, এবং যদি ক্যাশে না থাকে, তবে ডেটাবেস থেকে ডেটা ফেচ করে সেটিও ক্যাশে সেট করা হয়। তাই পরবর্তীতে ব্যবহারকারী ক্যাশ থেকে ডেটা পাবে এবং অনাবশ্যক ডেটাবেস হিট এড়াবে।

এছাড়াও, যখন ব্যবহারকারী প্রোডাক্ট আইডি ব্যবহার করে ডেটা ফেচ করতে চায়, তখন কন্ট্রোলারের দ্বিতীয় product এন্ডপয়েন্টে দেখতে পাবেন, আমরা সব প্রোডাক্টের ক্যাশ ডেটা ফেচ করি এবং প্রোডাক্ট আইডি দিয়ে ফিল্টার করি। যদি সেটি উপস্থিত থাকে, তবে ক্যাশ থেকে রিটার্ন করি; যদি না থাকে, তবে ডেটাবেস থেকে ফেচ করে ফিল্টার প্রয়োগ করে রিটার্ন করি।

আপনি আপডেট, ডিলিট, এবং পোস্ট এন্ডপয়েন্টে দেখতে পাচ্ছেন, আমরা ক্যাশ থেকে প্রোডাক্ট কী ডেটা সরানোর জন্য রিমুভ মেথড ব্যবহার করেছি। মেমরি ক্যাশের অনেক সিনেরিও এবং ব্যবহার আছে যা আপনি আপনার প্রয়োজন অনুযায়ী ব্যবহার করতে পারেন। আমি শুধু রেডিস ক্যাশের বেসিক এবং এটি .NET Core-এ কীভাবে কাজ করে সেটি কভার করেছি।

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

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

ক্লাসের টপে এই প্রাইভেট লক অবজেক্ট তৈরি করুন।

private static object _lock = new object()

পরবর্তী, Get মেথডটি নিচের মতো পরিবর্তন করুন।

public IEnumerable<Product> Get() {
    var cacheData = _cacheService.GetData<IEnumerable<Product>>("product");
    if (cacheData != null) { return cacheData; }
    lock(_lock) {
        var expirationTime = DateTimeOffset.Now.AddMinutes(5.0);
        cacheData = _dbContext.Products.ToList();
        _cacheService.SetData<IEnumerable<Product>>("product", cacheData, expirationTime);
    }
    return cacheData;
}

এখানে, আপনি দেখতে পাচ্ছেন, প্রথমে চেক করা হয় ডেটা ক্যাশে আছে কিনা। যদি থাকে, তবে রিটার্ন করা হয়। এরপর, যদি রেডিস ক্যাশে মান না থাকে, তবে লক প্রয়োগ করা হয়, এবং রিকোয়েস্ট লক হয়ে সেকশনে ঢুকে ডেটাবেস থেকে প্রোডাক্ট ডিটেইলস ফেচ করে, সেটি ক্যাশে সেট করে, এবং ডেটা রিটার্ন করে। তাই, যখন দ্বিতীয় ব্যবহারকারী প্রথম ব্যবহারকারীর রিকোয়েস্ট সম্পূর্ণ হওয়ার আগে রিকোয়েস্ট পাঠায়, তবে দ্বিতীয় রিকোয়েস্ট কিউ-তে থাকে, এবং প্রথম ব্যবহারকারীর রিকোয়েস্ট সম্পূর্ণ হলে দ্বিতীয় রিকোয়েস্ট প্রসেস হয়।

এছাড়াও, আপনি রেডিস CLI ব্যবহার করে ক্যাশে ইতিমধ্যে থাকা কী ডিটেইলস দেখতে পারেন।

.NET Core API-তে রেডিস ক্যাশিং ইন্টিগ্রেশন পারফরম্যান্স ও স্কেলেবিলিটির জন্য

এখানে আপনি দেখতে পাচ্ছেন রেডিস ক্যাশে উপস্থিত কী সম্পর্কে তথ্য দেয় এমন অনেক কমান্ড রয়েছে।

এই ছিল .NET Core-এ রেডিস ক্যাশ সম্পর্কে সব কিছু। আশা করি আপনি সম্পর্কিত বিষয়গুলো বুঝতে পেরেছেন।

হ্যাপি কোডিং!

  1. HTML DOM Input Month name প্রপার্টি

  2. HTML DOM aside অবজেক্ট

  3. পাইথনে একটি সংখ্যার গুণনীয়গুলোর ন্যূনতম যোগফল বের করার প্রোগ্রাম

  4. রিটেইল সাফল্যের জন্য রিয়েল-টাইম ইনভেন্টরির গুরুত্বপূর্ণ ভূমিকা