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

Redis দিয়ে আপনার ওয়েব API বूस্ট করুন: একটি প্রমাণিত পারফরম্যান্স অপ্টিমাইজেশন গাইড

লেখক: Tarique Ejaz

পারফরম্যান্স একটি অপরিহার্য প্যারামিটার যেটি আপনি যেকোনো সফটওয়্যার ডিজাইন করার সময় বিবেচনা করেন। এটি বিশেষ করে গুরুত্বপূর্ণ যখন ব্যাকএন্ডে কী হচ্ছে সেটি বোঝার কথা।

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

ক্যাশিং ডিফাইন করা হয়েছে একটি মেকানিজম হিসেবে ডেটা বা ফাইল একটি টেম্পরারি স্টোরেজ লোকেশনে সংরক্ষণ করতে যেখান থেকে এটি তাৎক্ষণিকভাবে অ্যাক্সেস করা যায় যখনই প্রয়োজন।

ক্যাশিং আজকাল ওয়েব অ্যাপ্লিকেশনে একটি must-have। আমরা Node.js এবং MongoDB দিয়ে বিল্ট আপনার ওয়েব API-কে সুপারচার্জ করতে Redis ব্যবহার করতে পারি।

"ক্যাশিং স্পষ্টত ১০০ থেকে ২০০ বছর পরেও সুপার গুরুত্বপূর্ণ ভূমিকা পালন করবে।"

Redis: একটি লেম্যান্স ওভারভিউ

অফিসিয়াল ডকুমেন্টেশন অনুযায়ী, Redis ডিফাইন করা হয়েছে একটি ইন-মেমরি ডেটা স্ট্রাকচার স্টোর হিসেবে যা ব্যবহার হয় একটি ডাটাবেস, মেসেজ ব্রোকার, বা ক্যাশ স্টোরেজ হিসেবে। এটি সাপোর্ট করে ডেটা স্ট্রাকচার যেন স্ট্রিংস, হ্যাশস, লিস্টস, সেটস, সোর্টেড সেটস রেঞ্জ কোয়েরি সহ, বিটম্যাপস, হাইপারলগলগস, জিওস্পেশিয়াল ইনডেক্সস রেডিয়াস কোয়েরি সহ এবং স্ট্রিমস।

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

একটা জিনিস স্পষ্ট। Redis পাওয়ারফুল, এবং সঠিকভাবে ব্যবহার করলে এটি আমাদের অ্যাপ্লিকেশনকে শুধু দ্রুত করতে পারে না, বরংеверি এফিশিয়েন্ট বানিয়ে দিতে পারে। পর্যাপ্ত কথা। হাত গরাম করি।

কোডের কথা বলি

শুরু করার আগে, আপনাকে আপনার লোকাল সিস্টেমে redis সেট আপ করতে হবে। আপনি এই কুইক সেটআপ প্রসেস ফলো করতে পারেন redis আপ এবং রানিং করার জন্য।

হয়েছে? কুল। শুরু করি। আমাদের Express-এ তৈরি একটি সিম্পল অ্যাপ্লিকেশন আছে যা MongoDB Atlas-এ একটি ইন্সট্যান্স ব্যবহার করে ডেটা রিড এবং রাইট করতে।

আমাদের /blogs রুট ফাইলে দুটি মেজর API তৈরি করা আছে।

... 
// GET - নির্দিষ্ট ইউজারের সব ব্লগ পোস্ট ফেচ করে
blogsRouter.route('/:user')
.get(async (req, res, next) => {
    const blogs = await Blog.find({ user: req.params.user });
    res.status(200).json({ blogs, });
});

// POST - একটি নতুন ব্লগ পোস্ট তৈরি করে
blogsRouter.route('/')
.post(async (req, res, next) => {
    const existingBlog = await Blog.findOne({ title: req.body.title });
    if (!existingBlog) {
        let newBlog = new Blog(req.body);
        const result = await newBlog.save();
        return res.status(200).json({
            message: `Blog ${result.id} is successfully created`,
            result,
        });
    }
    res.status(200).json({
        message: 'Blog with same title exists',
    });
});
... 

Redis ভালোবাসা ছড়িয়ে দেওয়া

আমরা npm প্যাকেজ redis ডাউনলোড করে শুরু করি লোকাল redis সার্ভারের সাথে কানেক্ট করতে।

const mongoose = require('mongoose');
const redis = require('redis');
const util = require('util');
const redisUrl = 'redis://127.0.0.1:6379';
const client = redis.createClient(redisUrl);
client.hget = util.promisify(client.hget);
... 

আমরা utils.promisify ফাংশন ব্যবহার করি client.hget ফাংশনকে ট্রান্সফর্ম করতে যাতে এটি একটি প্রমিস রিটার্ন করে কলব্যাকের বদলে। আপনি promisification সম্পর্কে আরও পড়তে পারেন এখানে।

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

আমাদের ক্যাশিং স্ট্র্যাটেজি নিচের পয়েন্টগুলো অ্যাড্রেস করতে সক্ষম হওয়া উচিত।

  • একটি নির্দিষ্ট ইউজারের জন্য সব ব্লগ পোস্টের রিকোয়েস্ট ক্যাশ করুন
  • প্রতিবার একটি নতুন ব্লগ পোস্ট তৈরি হলে ক্যাশ ক্লিয়ার করুন

    সंभाव্য চ্যালেঞ্জগুলো যেগুলোর বিষয়ে আমাদের সতর্ক থাকা উচিত আমরা আমাদের স্ট্র্যাটেজি নিয়ে যাওয়ার সাথে সাথে:

  • ক্যাশ ডেটা সংরক্ষণের জন্য কি ক্রিয়েশন হ্যান্ডল করার সঠিক পথ

  • ক্যাশ এক্সপায়ারেশন লজিক এবং ফোর্সড এক্সপায়ারেশন ক্যাশ ফ্রেশনেস মেইনটেইন করার জন্য
  • ক্যাশিং লজিকের রিয়ুজেবল ইমপ্লিমেন্টেশন

ঠিক আছে। আমাদের পয়েন্টগুলো নোট করা হয়েছে এবং redis কানেক্টেড। পরের ধাপে যাওয়া যাক।

ডিফল্ট Mongoose Exec ফাংশন ওভাররাইড করা

আমরা আমাদের ক্যাশিং লজিককে রিয়ুজেবল চাই। এবং শুধু রিয়ুজেবল নয়, আমরা চাই এটি ডাটাবেসে যেকোনো কোয়েরি করার আগে প্রথম চেকপয়েন্ট হিসেবে কাজ করে। এটি সহজে করা যায় mongoose exec ফাংশনে পিগি-ব্যাকিং করে একটি সিম্পল হ্যাক দিয়ে।

...
const exec = mongoose.Query.prototype.exec;
...
mongoose.Query.prototype.exec = async function() {
    ...
    const result = await exec.apply(this, arguments);
    console.log('Data Source: Database');
    return result;
}
... 

আমরা mongoose-এর প্রোটোটাইপ অবজেক্ট ব্যবহার করি আমাদের ক্যাশিং লজিক কোড যোগ করার জন্য কোয়েরিতে প্রথম এক্সিকিউশন হিসেবে।

ক্যাশ কোয়েরি হিসেবে যোগ করা

কোন কোয়েরিগুলো ক্যাশ করার জন্য আপ থাকবে সেটি ডিনোট করার জন্য, আমরা একটি mongoose কোয়েরি তৈরি করি। আমরা options অবজেক্টের মাধ্যমে user পাস করার ক্ষমতা প্রদান করি যেটি একটি হ্যাশ-কি হিসেবে ব্যবহার হবে।

নোট: হ্যাশকি একটি হ্যাশ ডেটা স্ট্রাকচারের জন্য একটি আইডেন্টিফায়ার হিসেবে কাজ করে যা, লেম্যান টার্মে, বলা যায় একটি প্যারেন্ট কি একটি কি-ভ্যালু পেয়ার সেটের। இத্যardoor, বড় সংখ্যক কোয়েরি-ভ্যালু সেট ক্যাশিং করা সম্ভব হয়। Redis-এ হ্যাশস সম্পর্কে আরও পড়ুন এখানে।

...
mongoose.Query.prototype.cache = function(options = {}) {
    this.enableCache = true;
    this.hashKey = JSON.stringify(options.key || 'default');
    return this;
};
... 

এটি করলে, আমরা সহজে cache(<options argument>) কোয়েরি ব্যবহার করতে পারি আমাদের যেসব কোয়েরি ক্যাশ করতে চাই সেগুলোর সাথে নিচের মতঃ

...
const blogs = await Blog
    .find({ user: req.params.user })
    .cache({ key: req.params.user });
... 

ক্যাশ লজিক গড়ে তোলা

আমরা একটি কমন রিয়ুজেবল কোয়েরি সেট আপ করেছি যেসব কোয়েরি ক্যাশ করতে হবে সেটি ডিনোট করতে। চলুন সেন্ট্রাল ক্যাশিং লজিক লিখি।

...
mongoose.Query.prototype.exec = async function() {
    if (!this.enableCache) {
        console.log('Data Source: Database');
        return exec.apply(this, arguments);
    }
    const key = JSON.stringify(Object.assign({}, this.getQuery(), {
        collection: this.mongooseCollection.name,
    }));
    const cachedValue = await client.hget(this.hashKey, key);
    if (cachedValue) {
        const parsedCache = JSON.parse(cachedValue);
        console.log('Data Source: Cache');
        return Array.isArray(parsedCache) ? parsedCache.map(doc => new this.model(doc)) : new this.model(parsedCache);
    }
    const result = await exec.apply(this, arguments);
    client.hmset(this.hashKey, key, JSON.stringify(result), 'EX', 300);
    console.log('Data Source: Database');
    return result;
};
... 

যখনই আমরা আমাদের মেইন কোয়েরির সাথে cache() কোয়েরি ব্যবহার করি, আমরা enableCache কিকে true সেট করি।

যদি কিটি false হয়, আমরা ডিফল্ট হিসেবে মেইন exec কোয়েরি রিটার্ন করি। না হলে, আমরা প্রথমে ক্যাশ ডেটা ফেচিং এবং স্টোরিং/রিফ্রেশ করার জন্য কি ফর্ম করি।

আমরা collection নাম ডিফল্ট কোয়েরির সাথে ব্যবহার করি কি নাম হিসেবে ইউনিকনেসের জন্য। হ্যাশ-কি যা ব্যবহার করা হয়েছে সেটি user-র নাম যা আমরা ইতিমধ্যে cache() ফাংশন ডেফিনিশনে সেট করেছিলাম।

ক্যাশড ডেটা client.hget() ফাংশন ব্যবহার করে ফেচ করা হয় যেটি হ্যাশ-কি এবং অনुगামী কি প্যারামিটার হিসেবে নেয়।

নোট: আমরা সবসময় JSON.parse() ব্যবহার করি Redis থেকে যেকোনো ডেটা ফেচ করার সময়। এবং একইভাবে, আমরা JSON.stringify() ব্যবহার করি কি এবং ডেটায় Redis-এ যেকোনো কিছু স্টোর করার আগে। এটি করা হয় কারণ Redis JSON ডেটা স্ট্রাকচার সাপোর্ট করে না।

একবার ক্যাশড ডেটা পেলে, আমাদের প্রতিটি ক্যাশড অবজেক্টকে একটি mongoose মডেলে ট্রান্সফর্ম করতে হবে। এটি করা যায় সহজে new this.model(<object>) ব্যবহার করে।

যদি ক্যাশে প্রয়োজনীয় ডেটা না থাকে, আমরা ডাটাবেসে একটি কোয়েরি করি। তারপর, ডেটা API-তে রিটার্ন করার পর, আমরা ক্যাশ রিফ্রেশ করি client.hmset() ব্যবহার করে। আমরা একটি ডিফল্ট ক্যাশ এক্সপায়ারেশন টাইম ৩০০ সেকেন্ডও সেট করি। এটি আপনার ক্যাশিং স্ট্র্যাটেজির উপর ভিত্তি করে কাস্টোমাইজেবল।

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

ফোর্সড ক্যাশ এক্সপায়ারেশন

কিছু কেসে, যেন যখন একটি ইউজার একটি নতুন ব্লগ পোস্ট তৈরি করে, ইউজার এক্সপেক্ট করে যে নতুন পোস্টটি উপলব্ধ থাকা উচিত যখন সে সব পোস্ট ফেচ করে।

এটি করার জন্য, আমাদের সেই ইউজার সম্পর্কিত ক্যাশ ক্লিয়ার করতে হবে এবং নতুন ডেটা দিয়ে আপডেট করতে হবে। তাই আমাদের ফোর্স এক্সপায়ারেশন করতে হবে। আমরা এটি করতে পারি Redis-র প্রদত্ত del() ফাংশন আহ্বান করে।

...
module.exports = {
    clearCache(hashKey) {
        console.log('Cache cleaned');
        client.del(JSON.stringify(hashKey));
    }
}
... 

আমাদের মনে রাখতে হবে যে আমরা একাধিক রুটে ফোর্স এক্সপায়ারেশন করব। একটি এক্সটেনসিবল পথ হলো এই clearCache() কে একটি মিডলওয়্যার হিসেবে ব্যবহার করা এবং একবার যেকোনো রুট সম্পর্কিত কোয়েরি এক্সিকিউশন ফিনিশ হলে সেটি কল করা।

const { clearCache } = require('../services/cache');
module.exports = async (req, res, next) => {
    // রুট হ্যান্ডলার ফিনিশ হওয়ার জন্য অপেক্ষা করুন
    await next();
    clearCache(req.body.user);
} 

এই মিডলওয়্যারটি সহজে কোনো নির্দিষ্ট রুটে কল করা যায় নিচের মতঃ

...
blogsRouter.route('/')
.post(cleanCache, async (req, res, next) => {
    ...
}
... 

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

Redis একশনে

আমরা Postman ব্যবহার করি API ক্লায়েন্ট হিসেবে আমাদের ক্যাশিং স্ট্র্যাটেজি একশনে দেখতে। চলুন। API অপারেশনগুলো এক এক করে চলে যাই।

  1. আমরা /blogs রুট ব্যবহার করে একটি নতুন ব্লগ পোস্ট তৈরি করি

নতুন ব্লগ পোস্ট তৈরি

  1. আমরা তারপর ইউজার tejaz-এর সাথে সম্পর্কিত সব ব্লগ পোস্ট ফেচ করি

ইউজার tejaz-এর জন্য সব ব্লগ পোস্ট ফেচ করা

  1. আমরা একবার nữa ইউজার tejaz-এর জন্য সব ব্লগ পোস্ট ফেচ করি।

ইউজার tejaz-এর জন্য একবার nữa সব ব্লগ পোস্ট ফেচ করা

আপনি স্পষ্টভাবে দেখতে পারেন যে যখন আমরা ক্যাশ থেকে ফেচ করি, টাইম নেওয়া হয়েছে 409ms- থেকে 24ms-এ। এটি আপনার APIকে সুপারচার্জ করে টাইম নেওয়া কমিয়ে প্রায় ৯৫%।

প্লাস, আমরা স্পষ্টভাবে দেখতে পাচ্ছি যে ক্যাশ এক্সপায়ারেশন এবং আপডেট অপারেশনಗಳು প্রত্যাশা অনুযায়ী কাজ করছে।

আপনি পূর্ণ সোর্স কোড পাবেন redis-express ফোল্ডারে এখানে।

উপসংহার

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

আপনি Redis কমান্ডের পূর্ণ সেট পাবেন এখানে। আপনি এটি redis-cli দিয়ে ব্যবহার করতে পারেন আপনার ক্যাশ ডেটা এবং অ্যাপ্লিকেশন প্রসেস মনিটর করতে।

যেকোনো নির্দিষ্ট টেকনোলজি দ্বারা অফার করা সম্ভাবনাগুলো সত্যিই অसीম। আপনার যদি কোনো কোয়েরি থাকে, আপনি আমার সাথে LinkedIn-এ যোগাযোগ করতে পারেন।

মানে وقت, কোডিং চালু রাখুন।

  1. অ্যান্ড্রয়েডে EditText ফিল্ডের মান পাওয়ার পদ্ধতি

  2. এন্ড্রয়েডে নেটওয়ার্ক সংযোগের স্ট্যাটাস মনিটর করার পদ্ধতি

  3. SQL Server-এ IF...ELSE স্টেটমেন্ট

  4. C++-এ শহর এবং স্টেশনের মধ্যে সর্বোচ্চ দূরত্ব নির্ণয়