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

.NET 8 ওয়েব এপিআই-তে রেডিসকে প্রাইমারি ডেটা স্টোর হিসেবে ব্যবহার

ভূমিকা

এই নিবন্ধে, আমি রেডিস এবং এটিকে প্রাইমারি ডেটাবেস হিসেবে ডেটা সংরক্ষণের জন্য ব্যবহার করার ইউজ কেস নিয়ে আলোচনা করব।

  • রেডিস পরিচিতি
  • রেডিস ডেটা টাইপস
  • রেডিসকে ডিবি হিসেবে ব্যবহার করার সুবিধা ও অসুবিধা
  • কন্টেইনারে রেডিস সেটআপ
  • .NET 8 ওয়েব এপিআই-তে রেডিসকে প্রাইমারি ডেটাবেস হিসেবে ব্যবহার

পূর্বশর্ত

  • ভিজুয়াল কোড
  • .NET 8 SDK
  • রেডিস ডেস্কটপ ম্যানেজার। ডাউনলোড: https://redis.io/resources/tools/
  • ডকার ডেস্কটপ
  • প্যাকেজগুলো:
    • Microsoft.Extensions.Caching.StackExchangeRedis
    • StackExchange.Redis

রেডিস পরিচিতি

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

ডেটা পারসিস্টেন্স সমস্যা সমাধানের জন্য রেডিস বিভিন্ন 메কানিজম প্রদান করে যেমন স্ন্যাপশট (snapshots) এবং অ্যাপেন্ড-ওনلی ফাইলস (AOF)। এই মেকানিজমগুলো আপনাকে ডেটা ডিস্কে স্থায়ীভাবে সংরক্ষণ করতে দেয়, যা সিস্টেম রিবুট হওয়া পরেও রিকভার করা যায়। রেডিস পারসিস্টেনস সম্পর্কে আরও জানতে URL দেখুন।

প্রায়শই রেডিস ক্যাশ লেয়ার হিসেবে ব্যবহৃত হয় কিন্তু আমরা এটিকে ডেটাবেস হিসেবেও ব্যবহার করতে পারি যাতে জটিল ডেটাবেস সিস্টেমে অন্য কল কমাতে পারি। রেডিস ডকুমেন্ট ডেটাবেস মডেল ব্যবহার করে যেখানে ডেটা докумен্ট হিসেবে সংরক্ষণ হয়, অধিকাংশে JSON ফরম্যাটে। এ সম্পর্কে আরও জানতে URL দেখুন।

রেডিস ডেটা টাইপস

রেডিস কিছু ডেটা টাইপ প্রদান করে যা ক্যাশিং, কিউইং, এবং ইভেন্ট প্রসেসিংয়ের সমস্যা সমাধানে সাহায্য করে।

  • স্ট্রিংস (Strings): বাইট সিকোয়েন্স রিপ্রেজেন্ট করে
  • লিস্টস (Lists): স্ট্রিংসের তালিকা
  • সেটস (Sets): ইউনিক স্ট্রিংসের অঅর্ডার্ড কালেকশন
  • হ্যাশেস (Hashes): ফিল্ড-ভ্যালু জোড়ার কালেকশন হিসেবে রেকর্ড টাইপ মডেল করে

ডেটা টাইপস সম্পর্কে আরও জানতে URL দেখুন।

রেডিসকে ডিবি হিসেবে ব্যবহার করার সুবিধা (Pros)

  • উচ্চ পারফরম্যান্স
  • বহুমুখী ডেটা স্ট্রাকচার
  • কম লেটেন্সি
  • স্কেলেবিলিটি

রেডিসকে ডিবি হিসেবে ব্যবহার করার অসুবিধা (Cons)

  • ডেটা ডিউরাবিলিটি (স্থায়িত্ব) চ্যালেঞ্জ
  • সীমিত কোয়েরি ক্যাপাবিলিটি
  • মেমরি সীমাবদ্ধতা

কন্টেইনারে রেডিস সেটআপ

নিশ্চিত করুন যে ডকার ডেস্কটপ চলছে এবং ভিজুয়াল স্টুডিও ২০২২ খুলুন।

"Create a New Project" বেছে নিন --> "ASP.NET Core Web API" ক্লিক করুন এবং "Next" চাপুন।

কনফিগার পেজে প্রজেক্টের নাম দিন এবং "Next" চাপুন।

.NET 8 ওয়েব এপিআই-তে রেডিসকে প্রাইমারি ডেটা স্টোর হিসেবে ব্যবহার

অ্যাডিশনাল ইনফরমেশন পেজে স্ক্রিনশট অনুযায়ী তথ্য বেছে নিয়ে "Create" চাপুন।

.NET 8 ওয়েব এপিআই-তে রেডিসকে প্রাইমারি ডেটা স্টোর হিসেবে ব্যবহার

এখন, আমাদের রেডিসের কনফিগারেশন রাখতে docker-compose.yaml ফাইল তৈরি করুন এবং নিচের কোড পেস্ট করুন।

version: '3.8'
services:
  redis:
    image: redis:alpine
    container_name: redisStudentAPI
    ports:
      - 6379:6379

उसके बाद, Tools -> Command line -> Developer Powershell থেকে ডেভেলপার পাওয়ারশেল প্রম্পট খুলুন।

প্রজেক্ট ফোল্ডারের ভিতরে নেভিগেট করুন এবং YAML ফাইল চালানোর কমান্ড দিন: docker compose up -d

ডকার ডেস্কটপ খুলুন, কন্টেইনারসে যান, আপনি YAML ফাইল থেকে তৈরি কন্টেইনারটি দেখতে পাবেন। আপনি docker ps চালিয়ে রানিং কন্টেইনারগুলোও দেখতে পারেন।

.NET 8 ওয়েব এপিআই-তে রেডিসকে প্রাইমারি ডেটা স্টোর হিসেবে ব্যবহার

রেডিস সার্ভারের সাথে ইন্টারঅ্যাক্ট করতে, কন্টেইনার আইডি দিয়ে নিচের কমান্ড চালান।

docker exec -it <container_id> /bin/sh

ডকারের বেসিক সেটআপ হয়ে গেছে এবং আমরা কমান্ড লাইন মাধ্যমে রেডিসের সাথে ইন্টারঅ্যাক্ট করতে পারি।

এখন, স্ট্রিং ডেটা টাইপ সম্পর্কে আরও জানব কারণ আমরা কী-ভ্যালু জোড়া স্ট্রিং হিসেবে সংরক্ষণ করব।

স্ট্রিংস (Strings)

এটি কী-এর সাথে জড়িত থাকা সবচেয়ে সহজ টাইপের মান এবং কী ও ভ্যালুর মধ্যে এক-से-এক ম্যাপিং।

আমরা SET <key> <value> ব্যবহার করে মান সেট করতে পারি এবং GET <key> ব্যবহার করে পেতে পারি।

আপনি DEL <key> ব্যবহার করে কী ডিলিট করতে পারেন।

এখন, নুগেট প্যাকেজ ম্যানেজার থেকে প্রয়োজনীয় প্যাকেজগুলো ইনস্টল করুন।

.NET 8 ওয়েব এপিআই-তে রেডিসকে প্রাইমারি ডেটা স্টোর হিসেবে ব্যবহার

Program.cs ফাইলে কনফিগারেশন যোগ করুন

Program.cs

builder.Services.AddSingleton<IConnectionMultiplexer>(options => ConnectionMultiplexer.Connect(("127.0.0.1:6379")));
builder.Services.AddScoped<IStudentRepository, StudentRepository>();

Models ফোল্ডার তৈরি করুন এবং Student.cs ফাইলে মডেলের কোড পেস্ট করুন।

Student.cs

namespace StudentAPIWithRedisDB.Models {
    public class Student {
        public string Id { get; set; } = $"student:{Guid.NewGuid().ToString()}";
        public required string StudentName { get; set; } = string.Empty;
    }
}

নিচের কোডগুলো সংশ্লিষ্ট ফাইলগুলোতে কপি-পেস্ট করুন।

IStudentRepository.cs

using StudentAPIWithRedisDB.Models;
namespace StudentAPIWithRedisDB.Data {
    public interface IStudentRepository {
        IEnumerable<Student> GetAllStudents();
        Student? GetStudentById(string id);
        void AddStudent(Student student);
        Student? UpdateStudent(Student student);
        Student? DeleteStudent(string id);
    }
}

StudentRepository.cs - IStudentRepository ইন্টারফেসের বাস্তবায়ন।

using StackExchange.Redis;
using StudentAPIWithRedisDB.Models;
using System.Text.Json;
namespace StudentAPIWithRedisDB.Data {
    public class StudentRepository : IStudentRepository {
        private readonly IConnectionMultiplexer _redis;
        public StudentRepository(IConnectionMultiplexer redis) {
            _redis = redis;
        }
        public void AddStudent(Student student) {
            if(student == null) { throw new ArgumentOutOfRangeException(nameof(student)); }
            var db = _redis.GetDatabase();
            var serializedStudent = JsonSerializer.Serialize(student);
            db.StringSet(student.Id, serializedStudent);
        }
        public Student? DeleteStudent(string id) {
            var db = _redis.GetDatabase();
            var student = db.StringGet(id);
            if (student.IsNullOrEmpty) { return null; }
            db.KeyDelete(id);
            return JsonSerializer.Deserialize<Student>(student);
        }
        public IEnumerable<Student> GetAllStudents() {
            var db = _redis.GetDatabase();
            var studentKeys = db.Multiplexer.GetServer(_redis.GetEndPoints().First()).Keys(pattern: "student:*");
            var students = new List<Student>();
            foreach (var key in studentKeys) {
                var studentJson = db.StringGet(key);
                if (!studentJson.IsNullOrEmpty) {
                    var student = JsonSerializer.Deserialize<Student>(studentJson);
                    students.Add(student);
                }
            }
            return students;
        }
        public Student? GetStudentById(string id) {
            var db = _redis.GetDatabase();
            var student = db.StringGet(id);
            if(student.IsNullOrEmpty) { return null; }
            return JsonSerializer.Deserialize<Student>(student);
        }
        public Student UpdateStudent(Student student) {
            var db = _redis.GetDatabase();
            var id = student.Id;
            if (db.KeyExists(id)) {
                var updatedStudentJson = JsonSerializer.Serialize(student);
                db.StringSet(id, updatedStudentJson);
                return student;
            } else { return null; }
        }
    }
}

StudentsController.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using StudentAPIWithRedisDB.Data;
using StudentAPIWithRedisDB.Models;
namespace StudentAPIWithRedisDB.Controllers {
    [Route("api/[controller]")]
    [ApiController]
    public class StudentsController : ControllerBase {
        private readonly IStudentRepository _studentRepository;
        public StudentsController(IStudentRepository studentRepository) {
            _studentRepository = studentRepository;
        }
        [HttpGet("{Id}", Name = "GetStudentById")]
        public ActionResult<Student> GetStudentById(string Id) {
            var student = _studentRepository.GetStudentById(Id);
            if(student == null) { return NotFound(); }
            return Ok(student);
        }
        [HttpPost]
        public ActionResult<Student> AddStudent(Student student) {
            _studentRepository.AddStudent(student);
            return CreatedAtRoute(nameof(GetStudentById), new { Id = student.Id }, student);
        }
        [HttpGet(Name = "GetAllStudents")]
        public ActionResult<Student> GetAllStudents() {
            var students = _studentRepository.GetAllStudents();
            return Ok(students);
        }
        [HttpDelete("{id}")]
        public ActionResult<Student> DeleteStudent(string id) {
            var student = _studentRepository.DeleteStudent(id);
            if(student == null) { return NotFound(); }
            return Ok(student);
        }
        [HttpPatch]
        public ActionResult<Student> UpdateStudent(Student student) {
            var studentToUpdate = _studentRepository.GetStudentById(student.Id);
            if(studentToUpdate == null) { return NotFound(); }
            _studentRepository.UpdateStudent(student);
            return NoContent();
        }
    }
}

এখন অ্যাপ্লিকেশন চালান, স্বagger-এ এন্ডপয়েন্টগুলো দেখতে পাবেন।

.NET 8 ওয়েব এপিআই-তে রেডিসকে প্রাইমারি ডেটা স্টোর হিসেবে ব্যবহার

রেডিস ডেস্কটপ ম্যানেজার খুলুন ডেটা দেখতে। প্রাথমিকভাবে ১৬টি ডেটাবেস থাকবে কোনো ডেটা ছাড়া।

একবার Create এন্ডপয়েন্ট হিট করলে DB0-এ আটোমেটিক্যালি রেকর্ড তৈরি হবে।

CREATE

সহজ টেস্টের জন্য পোস্টম্যান ব্যবহার করেছি।

শুধু নাম পাস করলে, এটি আটোমেটিক্যালি নতুন GUID সাফিক্স সহ স্টুডেন্টের আইডি তৈরি করবে।

ডকুমেন্ট DB0-এ তৈরি হয়েছে।

GetStudentById

এজন্য ডকুমেন্ট থেকে আইডি কপি করে রিকোয়েস্ট URL-এ পাস করুন।

এটি আইডি অনুযায়ী মান রিটার্ন করে।

GetAllStudents

ডেটাবেসে উপলব্ধ সব ডকুমেন্ট রিটার্ন করে।

Update

আইডি এবং আপডেটেড মান পাস করুন যাতে আইডি অনুযায়ী আপডেট হয়।

রেডিস ডেস্কটপ ম্যানেজার রিফ্রেশ করলে আপডেটেড মান দেখতে পাবেন।

Delete

ইউজার ডিলিট করতে আইডি পাস করুন, এটি স্টুডেন্ট ডিবি থেকে ডকুমেন্ট সরিয়ে দেবে।

ডকুমেন্ট ফাইল থেকে ডিলিট হয়ে গেছে।

আগে স্টুডেন্ট টেবিলে দুটি ডকুমেন্ট ছিল কিন্তু এখন প্রথমটি ডিলিট হয়ে গেছে।

এইভাবে রেডিসকে ডেটাবেস হিসেবে অ্যাপ্লিকেশনে ব্যবহার করার একটি সহজ উদাহরণ দেখানো হয়েছে। এখানে আমরা ক্যাশ হ্রাস করে সরাসরি রেডিসে ডেটা সংরক্ষণ করেছি এবং সেটি রিট্রিভ, আপডেট, ডিলিট করেছি। একইভাবে আপনি আপনার অ্যাপ্লিকেশনে প্রয়োগ করতে পারেন।

  1. Tkinter-এ ডেট পিকার ক্যালেন্ডার (Date Picker Calendar) তৈরি করা

  2. অ্যান্ড্রয়েডে ConcurrentLinkedQueue-এ offer() মেথড ব্যবহার

  3. জাভাস্ক্রিপ্ট ব্যবহার করে ডাবল লিংকড লিস্ট থেকে এলিমেন্ট সরানো

  4. CSS দিয়ে ব্রাউজার উইন্ডোর পুরো উচ্চতায় এলিমেন্ট ফैलানো