---
title: "`lua-resty-*` एनकैप्सुलेशन डेवलपर्स को मल्टी-लेवल कैशिंग से मुक्त करता है - API7.ai"
description: "यह लेख दो कैश एनकैप्सुलेशन लाइब्रेरीज़ `lua-resty-memcached-shdict` और `lua-resty-mlcache` के कार्यान्वयन सिद्धांतों और उपयोग का परिचय देता है।"
url: "https://api7.ai/hi/learning-center/openresty/lua-resty-encapsulation-releases-developers-from-caching"
---

# \`lua-resty-\*\` एनकैप्सुलेशन डेवलपर्स को मल्टी-लेवल कैशिंग से मुक्त करता है

API7.ai

December 30, 2022

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

पिछले दो लेखों में, हमने OpenResty में कैशिंग और कैश स्टैम्पीड समस्या के बारे में सीखा है, जो सभी बुनियादी पक्ष पर हैं। वास्तविक परियोजना विकास में, डेवलपर्स एक ऐसी लाइब्रेरी पसंद करते हैं जो सभी विवरणों को संभालती और छिपाती हो और जिसका उपयोग सीधे व्यावसायिक कोड विकसित करने के लिए किया जा सके।

यह श्रम विभाजन का एक लाभ है, बुनियादी घटक डेवलपर्स लचीली आर्किटेक्चर, अच्छे प्रदर्शन और कोड स्थिरता पर ध्यान केंद्रित करते हैं, बिना ऊपरी व्यावसायिक तर्क की परवाह किए; जबकि एप्लिकेशन इंजीनियर व्यावसायिक कार्यान्वयन और तेजी से पुनरावृत्ति पर अधिक ध्यान देते हैं, और यह आशा करते हैं कि वे निचले स्तर के विभिन्न तकनीकी विवरणों से विचलित न हों। इनके बीच की खाई को रैपर लाइब्रेरीज़ द्वारा भरा जा सकता है।

OpenResty में कैशिंग भी इसी समस्या का सामना करती है। `shared dict` और `lru caches` पर्याप्त स्थिर और कुशल हैं, लेकिन इनके साथ कई विवरणों को संभालना पड़ता है। एप्लिकेशन विकास इंजीनियर्स के लिए "अंतिम मील" कुछ उपयोगी एनकैप्सुलेशन के बिना कठिन हो सकती है। यहीं पर समुदाय का महत्व सामने आता है। एक सक्रिय समुदाय खुद से खाई को ढूंढेगा और उसे जल्दी से भरेगा।

## `lua-resty-memcached-shdict`

चलिए कैश एनकैप्सुलेशन पर वापस आते हैं। `lua-resty-memcached-shdict` एक आधिकारिक OpenResty प्रोजेक्ट है जो `shared dict` का उपयोग करके `memcached` के लिए एक परत एनकैप्सुलेशन बनाता है, जो कैश स्टैम्पीड और समाप्त डेटा जैसे विवरणों को संभालता है। यदि आपका कैश्ड डेटा बैकएंड में `memcached` में संग्रहीत है, तो आप इस लाइब्रेरी का उपयोग कर सकते हैं।

यह एक आधिकारिक OpenResty-विकसित लाइब्रेरी है, लेकिन यह OpenResty पैकेज में डिफ़ॉल्ट रूप से शामिल नहीं है। यदि आप इसे स्थानीय रूप से परीक्षण करना चाहते हैं, तो आपको इसका [स्रोत कोड](https://github.com/openresty/lua-resty-memcached-shdict) पहले स्थानीय OpenResty खोज पथ में डाउनलोड करना होगा।

यह एनकैप्सुलेशन लाइब्रेरी वही समाधान है जिसका हमने पिछले लेख में उल्लेख किया था। यह `lua-resty-lock` का उपयोग करके पारस्परिक रूप से अनन्य बनाता है। कैश विफल होने की स्थिति में, केवल एक अनुरोध `memcached` से डेटा प्राप्त करता है और कैश स्टॉर्म से बचता है। यदि नवीनतम डेटा प्राप्त नहीं होता है, तो पुराना डेटा एंडपॉइंट को वापस कर दिया जाता है।

हालांकि, यह `lua-resty` लाइब्रेरी, हालांकि एक आधिकारिक OpenResty प्रोजेक्ट है, परिपूर्ण नहीं है:

1. पहले, इसमें कोई टेस्ट केस कवरेज नहीं है, जिसका अर्थ है कि कोड की गुणवत्ता को लगातार सुनिश्चित नहीं किया जा सकता है।
2. दूसरा, यह बहुत अधिक इंटरफ़ेस पैरामीटर्स को उजागर करता है, जिसमें 11 आवश्यक और 7 वैकल्पिक पैरामीटर्स हैं।

```
local memc_fetch, memc_store =
    shdict_memc.gen_memc_methods{
        tag = "my memcached server tag",
        debug_logger = dlog,
        warn_logger = warn,
        error_logger = error_log,

        locks_shdict_name = "some_lua_shared_dict_name",

        shdict_set = meta_shdict_set,  
        shdict_get = meta_shdict_get,  

        disable_shdict = false,  -- optional, default false

        memc_host = "127.0.0.1",
        memc_port = 11211,
        memc_timeout = 200,  -- in ms
        memc_conn_pool_size = 5,
        memc_fetch_retries = 2,  -- optional, default 1
        memc_fetch_retry_delay = 100, -- in ms, optional, default to 100 (ms)

        memc_conn_max_idle_time = 10 * 1000,  -- in ms, for in-pool connections,optional, default to nil

        memc_store_retries = 2,  -- optional, default to 1
        memc_store_retry_delay = 100,  -- in ms, optional, default to 100 (ms)

        store_ttl = 1,  -- in seconds, optional, default to 0 (i.e., never expires)
    }
```

ज्यादातर पैरामीटर्स को "एक नया `memcached` हैंडलर बनाकर" सरल किया जा सकता है। वर्तमान में सभी पैरामीटर्स को उपयोगकर्ता पर फेंकने का तरीका उपयोगकर्ता-अनुकूल नहीं है, इसलिए मैं इच्छुक डेवलपर्स को इसका अनुकूलन करने के लिए PR योगदान करने के लिए आमंत्रित करता हूं।

इसके अलावा, इस एनकैप्सुलेशन लाइब्रेरी के दस्तावेज़ में निम्नलिखित दिशाओं में और अनुकूलन का उल्लेख किया गया है।

1. `lua-resty-lrucache` का उपयोग करके `Worker`-स्तरीय कैश बढ़ाएं, न कि केवल `server`-स्तरीय `shared dict` कैश।
2. `ngx.timer` का उपयोग करके एसिंक्रोनस कैश अपडेट ऑपरेशन करें।

पहली दिशा एक बहुत अच्छा सुझाव है, क्योंकि वर्कर के भीतर कैश प्रदर्शन बेहतर होता है; दूसरा सुझाव आपको अपने वास्तविक परिदृश्य के आधार पर विचार करने की आवश्यकता है। हालांकि, मैं आमतौर पर दूसरे की सिफारिश नहीं करता हूं, न केवल क्योंकि टाइमर्स की संख्या सीमित है, बल्कि यह भी कि यदि यहां अपडेट लॉजिक गलत हो जाता है, तो कैश फिर से अपडेट नहीं होगा, जिसका बड़ा प्रभाव होता है।

## `lua-resty-mlcache`

अगले, हम OpenResty में आमतौर पर उपयोग की जाने वाली एक कैशिंग एनकैप्सुलेशन का परिचय देते हैं: `lua-resty-mlcache`, जो `shared dict` और `lua-resty-lrucache` का उपयोग करके एक मल्टी-लेयर कैशिंग मैकेनिज्म को लागू करता है। आइए निम्नलिखित दो कोड उदाहरणों में देखें कि यह लाइब्रेरी कैसे उपयोग की जाती है।

```
local mlcache = require "resty.mlcache"

local cache, err = mlcache.new("cache_name", "cache_dict", {
    lru_size = 500,    -- size of the L1 (Lua VM) cache
    ttl = 3600,   -- 1h ttl for hits
    neg_ttl  = 30,     -- 30s ttl for misses
})
if not cache then
    error("failed to create mlcache: " .. err)
end
```

आइए पहले कोड को देखें। इस कोड की शुरुआत में `mlcache` लाइब्रेरी को शामिल किया गया है और इनिशियलाइज़ेशन के लिए पैरामीटर्स सेट किए गए हैं। हम आमतौर पर इस कोड को `init` चरण में रखते हैं और इसे केवल एक बार करने की आवश्यकता होती है।

दो आवश्यक पैरामीटर्स, कैश नाम और डिक्शनरी नाम के अलावा, तीसरा पैरामीटर एक डिक्शनरी है जिसमें 12 विकल्प हैं जो वैकल्पिक हैं और यदि भरे नहीं जाते हैं तो डिफ़ॉल्ट मानों का उपयोग करते हैं। यह `lua-resty-memcached-shdict` की तुलना में बहुत अधिक सुंदर है। यदि हमें इंटरफ़ेस डिज़ाइन करना होता, तो यह बेहतर होता कि हम `mlcache` के दृष्टिकोण को अपनाएं - इंटरफ़ेस को जितना संभव हो सरल रखें, जबकि पर्याप्त लचीलापन बनाए रखें।

यहां दूसरा कोड है, जो अनुरोध प्रसंस्करण के दौरान का तार्किक कोड है।

```
local function fetch_user(id)
    return db:query_user(id)
end

local id = 123
local user , err = cache:get(id , nil , fetch_user , id)
if err then
    ngx.log(ngx.ERR , "failed to fetch user: ", err)
    return
end

if user then
    print(user.id) -- 123
end
```

जैसा कि आप देख सकते हैं, मल्टी-लेयर कैश छिपा हुआ है, इसलिए आपको कैश प्राप्त करने के लिए `mlcache` ऑब्जेक्ट का उपयोग करना होगा और कैश समाप्त होने पर कॉलबैक फ़ंक्शन सेट करना होगा। इसके पीछे की जटिल तर्क को पूरी तरह से छिपाया जा सकता है।

आपको यह जानने की उत्सुकता हो सकती है कि यह लाइब्रेरी आंतरिक रूप से कैसे लागू की गई है। आइए अब इस लाइब्रेरी की आर्किटेक्चर और कार्यान्वयन पर एक और नज़र डालें। निम्नलिखित छवि OpenResty Con 2018 में `mlcache` के लेखक Thibault Charbonnier द्वारा दिए गए एक टॉक का स्लाइड है।

![mlcache architecture](https://static.api7.ai/2022/12/08/6391afa6d43fb.webp?imageMogr2/format/webp)

जैसा कि आप डायग्राम से देख सकते हैं, `mlcache` डेटा को तीन परतों में विभाजित करता है, जिन्हें `L1`, `L2` और `L3` कहा जाता है।

`L1` कैश `lua-resty-lrucache` है, जहां प्रत्येक `Worker` का अपना कॉपी होता है, और `N` `Worker`s के साथ, डेटा के `N` कॉपी होते हैं, इसलिए डेटा रिडंडेंसी होती है। चूंकि एकल `Worker` के भीतर `lrucache` को संचालित करने से लॉक ट्रिगर नहीं होते हैं, इसलिए इसका प्रदर्शन अधिक होता है और यह प्रथम-स्तरीय कैश के रूप में उपयुक्त है।

`L2` कैश एक `shared dict` है। सभी `Worker`s कैश्ड डेटा की एक ही कॉपी साझा करते हैं और यदि `L1` कैश हिट नहीं होता है, तो `L2` कैश को क्वेरी किया जाएगा। `ngx.shared`.DICT एक API प्रदान करता है जो स्पिनलॉक का उपयोग करके ऑपरेशन की परमाणुता सुनिश्चित करता है, इसलिए हमें यहां रेस कंडीशन की चिंता करने की आवश्यकता नहीं है।

`L3` वह स्थिति है जब `L2` कैश भी हिट नहीं होता है, और कॉलबैक फ़ंक्शन को निष्पादित करने की आवश्यकता होती है ताकि डेटा स्रोत, जैसे कि एक बाहरी डेटाबेस, से डेटा क्वेरी किया जा सके और फिर इसे `L2` में कैश किया जा सके। यहां, कैश स्टॉर्म से बचने के लिए, यह `lua-resty-lock` का उपयोग करता है ताकि केवल एक `Worker` डेटा स्रोत से डेटा प्राप्त करे।

एक अनुरोध के दृष्टिकोण से:

- पहले, यह `Worker` के भीतर `L1` कैश को क्वेरी करेगा और यदि `L1` हिट होता है, तो सीधे वापस कर देगा।
- यदि `L1` हिट नहीं होता है या कैश विफल हो जाता है, तो यह `Worker`s के बीच `L2` कैश को क्वेरी करेगा। यदि `L2` हिट होता है, तो यह वापस कर देगा और परिणाम को `L1` में कैश करेगा।
- यदि `L2` भी मिस हो जाता है या कैश अमान्य हो जाता है, तो एक कॉलबैक फ़ंक्शन को कॉल किया जाएगा ताकि डेटा स्रोत से डेटा खोजा जा सके और इसे `L2` कैश में लिखा जा सके, जो `L3` डेटा लेयर का कार्य है।

आप इस प्रक्रिया से भी देख सकते हैं कि कैश अपडेट एंडपॉइंट अनुरोधों द्वारा सक्रिय रूप से ट्रिगर किए जाते हैं। यहां तक कि यदि एक अनुरोध कैश प्राप्त करने में विफल हो जाता है, तो बाद के अनुरोध अभी भी अपडेट लॉजिक को ट्रिगर कर सकते हैं ताकि कैश सुरक्षा को अधिकतम किया जा सके।

हालांकि, `mlcache` को परिपूर्ण रूप से लागू किया गया है, फिर भी एक दर्द बिंदु है - डेटा का सीरियलाइज़ेशन और डीसीरियलाइज़ेशन। यह `mlcache` की समस्या नहीं है, बल्कि `lrucache` और `shared dict` के बीच का अंतर है, जिसका हमने बार-बार उल्लेख किया है। `lrucache` में, हम विभिन्न Lua डेटा प्रकारों को संग्रहीत कर सकते हैं, जिसमें `table` शामिल है; लेकिन `shared dict` में, हम केवल स्ट्रिंग्स को संग्रहीत कर सकते हैं।

L1, `lrucache` कैश, उस डेटा की परत है जिसे उपयोगकर्ता छूता है, और हम इसमें विभिन्न प्रकार के डेटा को कैश करना चाहते हैं, जिसमें `string`, `table`, `cdata`, आदि शामिल हैं। समस्या यह है कि `L2` केवल स्ट्रिंग्स को संग्रहीत कर सकता है, और जब डेटा `L2` से `L1` तक बढ़ाया जाता है, तो हमें स्ट्रिंग्स से उन डेटा प्रकारों में एक परत रूपांतरण करने की आवश्यकता होती है जिन्हें हम सीधे उपयोगकर्ता को दे सकते हैं।

सौभाग्य से, `mlcache` ने इस स्थिति को ध्यान में रखा है और `new` और `get` इंटरफ़ेस में वैकल्पिक फ़ंक्शन `l1_serializer` प्रदान किया है, जो विशेष रूप से `L2` से `L1` तक डेटा प्रसंस्करण को संभालने के लिए डिज़ाइन किया गया है। हम निम्नलिखित नमूना कोड देख सकते हैं, जिसे मैंने अपने टेस्ट केस सेट से निकाला है।

```
local mlcache = require "resty.mlcache"

local cache, err = mlcache.new("my_mlcache", "cache_shm", {
l1_serializer = function(i)
    return i + 2
end,
})

local function callback()
    return 123456
end

local data = assert(cache:get("number", nil, callback))
assert(data == 123458)
```

मुझे इसे जल्दी से समझाएं। इस मामले में, कॉलबैक फ़ंक्शन संख्या `123456` वापस करता है; `new` में, हमने जो `l1_serializer` फ़ंक्शन सेट किया है, वह आने वाली संख्या में `2` जोड़ देगा, जो `L1` कैश सेट करने से पहले `123458` बन जाता है। ऐसे सीरियलाइज़ेशन फ़ंक्शन के साथ, डेटा `L1` और `L2` के बीच रूपांतरण करते समय अधिक लचीला हो सकता है।

## सारांश

कई कैशिंग परतों के साथ, सर्वर-साइड प्रदर्शन को अधिकतम किया जा सकता है, और बीच में कई विवरण छिपे होते हैं। इस बिंदु पर, एक स्थिर और कुशल रैपर लाइब्रेरी हमें बहुत सारा प्रयास बचाती है। मुझे आशा है कि आज की ये दो रैपर लाइब्रेरीज़ आपको कैशिंग को बेहतर ढंग से समझने में मदद करेंगी।

अंत में, इस प्रश्न पर विचार करें: क्या कैश की साझा डिक्शनरी परत आवश्यक है? क्या केवल `lrucache` का उपयोग करना संभव है? अपनी राय मुझे बताने के लिए टिप्पणी करने के लिए स्वतंत्र महसूस करें, और आप इस लेख को अधिक लोगों के साथ साझा करके संवाद और प्रगति करने के लिए भी स्वागत करते हैं।

Share article link

Share

Related Posts

[![OpenResty FAQ | OpenResty का व्यवहारिक उपयोग कैसे किया जाता है](https://static.api7.ai/uploads/2023/02/12/jbv9M0zi_Hello%20World%20in%20OpenResty%20%283%29.png)](https://api7.ai/learning-center/openresty/faq-5)

## [OpenResty FAQ | OpenResty का व्यवहारिक उपयोग कैसे किया जाता है](https://api7.ai/learning-center/openresty/faq-5)

[![भाग 3: OpenResty का उपयोग करके एक Microservices API Gateway कैसे बनाएं](https://static.api7.ai/uploads/2023/02/07/3XJXY9Jy_45.png)](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

## [भाग 3: OpenResty का उपयोग करके एक Microservices API Gateway कैसे बनाएं](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

Table of Contents

- [`lua-resty-memcached-shdict`](#lua-resty-memcached-shdict)
- [`lua-resty-mlcache`](#lua-resty-mlcache)
- [सारांश](#%E0%A4%B8%E0%A4%BE%E0%A4%B0%E0%A4%BE%E0%A4%82%E0%A4%B6)
