---
title: "NGINX वर्कर्स के बीच संचार का जादू: सबसे महत्वपूर्ण डेटा संरचनाओं में से एक `shared dict` - API7.ai"
description: "shared dict, NGINX और OpenResty में एक महत्वपूर्ण डेटा स्ट्रक्चर है।"
url: "https://api7.ai/hi/learning-center/openresty/communication-magic-between-nginx-workers-shared-dict"
---

# NGINX वर्कर्स के बीच संचार का जादू: सबसे महत्वपूर्ण डेटा संरचनाओं में से एक \`shared dict\`

API7.ai

October 27, 2022

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

जैसा कि हमने पिछले लेख में कहा था, Lua में `table` एकमात्र डेटा संरचना है। यह `shared dict` से मेल खाता है, जो OpenResty प्रोग्रामिंग में उपयोग की जाने वाली सबसे महत्वपूर्ण डेटा संरचना है। यह डेटा स्टोरेज, पठन, परमाणु गिनती, और कतार संचालन का समर्थन करता है।

`shared dict` के आधार पर, आप कैशिंग और कई `Worker` के बीच संचार, दर सीमित करने, ट्रैफ़िक सांख्यिकी, और अन्य कार्यों को लागू कर सकते हैं। आप `shared dict` को एक सरल Redis के रूप में उपयोग कर सकते हैं, सिवाय इसके कि `shared dict` में डेटा स्थायी नहीं होता है, इसलिए आपको संग्रहीत डेटा के नुकसान पर विचार करना चाहिए।

## डेटा साझा करने के कई तरीके

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

इसलिए, `shared dict` API को औपचारिक रूप से पेश करने से पहले, आइए पहले OpenResty में सामान्य डेटा साझा करने के तरीकों को समझें और वर्तमान स्थिति के अनुसार अधिक उपयुक्त डेटा साझा करने का तरीका चुनना सीखें।

**पहला है NGINX में वेरिएबल।** यह NGINX C मॉड्यूल के बीच डेटा साझा कर सकता है। स्वाभाविक रूप से, यह C मॉड्यूल और OpenResty द्वारा प्रदान किए गए `lua-nginx-module` के बीच भी डेटा साझा कर सकता है, जैसा कि निम्नलिखित कोड में है।

```
location /foo {
     set $my_var ''; # यह लाइन $my_var को कॉन्फ़िग समय पर बनाने के लिए आवश्यक है
     content_by_lua_block {
         ngx.var.my_var = 123;
         ...
     }
 }
```

हालांकि, NGINX वेरिएबल का उपयोग करके डेटा साझा करना धीमा है क्योंकि इसमें हैश लुकअप और मेमोरी आवंटन शामिल है। इसके अलावा, इस तरीके की सीमा यह है कि इसे केवल स्ट्रिंग्स को स्टोर करने के लिए उपयोग किया जा सकता है और यह जटिल Lua प्रकारों का समर्थन नहीं कर सकता है।

**दूसरा है `ngx.ctx`, जो एक ही अनुरोध के विभिन्न चरणों के बीच डेटा साझा कर सकता है।** यह एक सामान्य Lua `table` है, इसलिए यह तेज़ है और विभिन्न Lua ऑब्जेक्ट्स को स्टोर कर सकता है। इसका जीवनकाल अनुरोध-स्तर का है; जब अनुरोध समाप्त होता है, तो `ngx.ctx` नष्ट हो जाता है।

निम्नलिखित एक विशिष्ट उपयोग परिदृश्य है जहां हम `ngx.ctx` का उपयोग करते हैं जैसे कि NGINX वेरिएबल और इसे विभिन्न चरणों में उपयोग करते हैं।

```
location /test {
     rewrite_by_lua_block {
         ngx.ctx.host = ngx.var.host
     }
     access_by_lua_block {
        if (ngx.ctx.host == 'api7.ai') then
            ngx.ctx.host = 'test.com'
        end
     }
     content_by_lua_block {
         ngx.say(ngx.ctx.host)
     }
 }
```

इस मामले में, यदि आप `curl` का उपयोग करके इसे एक्सेस करते हैं।

```
curl -i 127.0.0.1:8080/test -H 'host:api7.ai'
```

तो यह `test.com` प्रिंट करेगा, यह दिखाते हुए कि `ngx.ctx` विभिन्न चरणों में डेटा साझा कर रहा है। बेशक, आप उपरोक्त उदाहरण को संशोधित करके सरल स्ट्रिंग्स के बजाय `table` जैसे अधिक जटिल ऑब्जेक्ट्स को सहेज सकते हैं और देख सकते हैं कि यह आपकी अपेक्षाओं को पूरा करता है या नहीं।

हालांकि, यहां एक विशेष नोट यह है कि क्योंकि `ngx.ctx` का जीवनकाल अनुरोध-स्तर का है, यह मॉड्यूल स्तर पर कैश नहीं करता है। उदाहरण के लिए, मैंने अपनी `foo.lua` फ़ाइल में इसका उपयोग करने की गलती की।

```
local ngx_ctx = ngx.ctx

local function bar()
    ngx_ctx.host =  'test.com'
end
```

हमें फ़ंक्शन-स्तर पर कॉल और कैश करना चाहिए।

```
local ngx = ngx

local function bar()
    ngx_ctx.host =  'test.com'
end
```

`ngx.ctx` के बारे में और भी कई विवरण हैं, जिन्हें हम बाद में प्रदर्शन अनुकूलन अनुभाग में जारी रखेंगे।

**तीसरा तरीका मॉड्यूल-स्तरीय वेरिएबल का उपयोग करके एक ही `Worker` के भीतर सभी अनुरोधों के बीच डेटा साझा करना है।** पिछले NGINX वेरिएबल और `ngx.ctx` के विपरीत, यह तरीका थोड़ा कम समझ में आता है। लेकिन चिंता न करें, अवधारणा अमूर्त है, और कोड पहले आता है, इसलिए आइए एक उदाहरण देखकर मॉड्यूल-स्तरीय वेरिएबल को समझें।

```
-- mydata.lua
local _M = {}

local data = {
    dog = 3,
    cat = 4,
    pig = 5,
}

function _M.get_age(name)
    return data[name]
end

return _M
```

`nginx.conf` में कॉन्फ़िगरेशन निम्नलिखित है।

```
location /lua {
     content_by_lua_block {
         local mydata = require "mydata"
         ngx.say(mydata.get_age("dog"))
     }
 }
```

इस उदाहरण में, `mydata` एक मॉड्यूल है जो `Worker` प्रक्रिया द्वारा केवल एक बार लोड किया जाता है, और उसके बाद `Worker` द्वारा संसाधित सभी अनुरोध `mydata` मॉड्यूल के कोड और डेटा को साझा करते हैं।

स्वाभाविक रूप से, `mydata` मॉड्यूल में `data` वेरिएबल एक मॉड्यूल-स्तरीय वेरिएबल है जो मॉड्यूल के शीर्ष स्तर पर स्थित है, यानी मॉड्यूल की शुरुआत में, और सभी फ़ंक्शन्स के लिए सुलभ है।

इसलिए, आप अनुरोधों के बीच साझा किए जाने वाले डेटा को मॉड्यूल के शीर्ष-स्तरीय वेरिएबल में रख सकते हैं। हालांकि, यह ध्यान रखना आवश्यक है कि हम आमतौर पर इस तरीके का उपयोग केवल पठनीय डेटा को स्टोर करने के लिए करते हैं। यदि लेखन संचालन शामिल हैं, तो आपको बहुत सावधान रहना चाहिए क्योंकि `race condition` हो सकता है, जो एक पेचीदा बग है जिसे ढूंढना मुश्किल होता है।

हम निम्नलिखित सरल उदाहरण के साथ इसे अनुभव कर सकते हैं।

```
-- mydata.lua
local _M = {}

local data = {
    dog = 3,
    cat = 4,
    pig = 5,
}

function _M.incr_age(name)
    data[name]  = data[name] + 1
    return data[name]
end

return _M
```

मॉड्यूल में, हम `incr_age` फ़ंक्शन जोड़ते हैं, जो `data` टेबल में डेटा को संशोधित करता है।

फिर, कॉलिंग कोड में, हम सबसे महत्वपूर्ण लाइन `ngx.sleep(5)` जोड़ते हैं, जहां `sleep` एक `yield` ऑपरेशन है।

```
location /lua {
     content_by_lua_block {
         local mydata = require "mydata"
         ngx.say(mydata. incr_age("dog"))
         ngx.sleep(5) -- yield API
         ngx.say(mydata. incr_age("dog"))
     }
 }
```

इस `sleep` कोड (या अन्य नॉन-ब्लॉकिंग IO ऑपरेशन, जैसे Redis एक्सेस, आदि) के बिना, कोई yield ऑपरेशन नहीं होगा, कोई प्रतिस्पर्धा नहीं होगी, और अंतिम आउटपुट अनुक्रमिक होगा।

लेकिन जब हम इस कोड को जोड़ते हैं, तो भले ही यह केवल 5 सेकंड की नींद के भीतर हो, एक अन्य अनुरोध संभवतः `mydata.incr_age` फ़ंक्शन को कॉल करेगा और वेरिएबल के मान को संशोधित करेगा, इस प्रकार अंतिम आउटपुट संख्याएं असंतत हो जाएंगी। वास्तविक कोड में तर्क इतना सरल नहीं है, और बग को ढूंढना बहुत कठिन होता है।

इसलिए, जब तक आप सुनिश्चित नहीं हैं कि बीच में कोई `yield` ऑपरेशन नहीं होगा जो NGINX इवेंट लूप को नियंत्रण देगा, मैं अनुशंसा करता हूं कि आप अपने मॉड्यूल-स्तरीय वेरिएबल को केवल पठनीय रखें।

चौथा और अंतिम तरीका `shared dict` का उपयोग करके डेटा साझा करना है जो कई workers के बीच साझा किया जा सकता है।

यह तरीका एक रेड-ब्लैक ट्री कार्यान्वयन पर आधारित है, जो अच्छा प्रदर्शन करता है। फिर भी, इसकी सीमाएं हैं: आपको NGINX कॉन्फ़िगरेशन फ़ाइल में शेयर्ड मेमोरी का आकार पहले से घोषित करना होगा, और इसे रनटाइम में बदला नहीं जा सकता है:

```
lua_shared_dict dogs 10m;
```

`shared dict` केवल `string` डेटा को कैश करता है और जटिल Lua डेटा प्रकारों का समर्थन नहीं करता है। इसका मतलब है कि जब मुझे `table` जैसे जटिल डेटा प्रकारों को स्टोर करने की आवश्यकता होती है, तो मुझे JSON या अन्य तरीकों का उपयोग करके उन्हें सीरियलाइज़ और डीसीरियलाइज़ करना होगा, जो स्वाभाविक रूप से बहुत अधिक प्रदर्शन हानि का कारण बनेगा।

वैसे भी, यहां कोई सिल्वर बुलेट नहीं है, और डेटा साझा करने का कोई सही तरीका नहीं है। आपको अपनी आवश्यकताओं और परिदृश्यों के अनुसार कई तरीकों को संयोजित करना होगा।

## Shared dict

हमने ऊपर डेटा साझा करने के बारे में बहुत समय बिताया है, और आप में से कुछ लोग सोच रहे होंगे: ऐसा लगता है कि वे सीधे shared dict से संबंधित नहीं हैं। क्या यह विषय से हटकर नहीं है?

वास्तव में, नहीं। कृपया इस बारे में सोचें: OpenResty में `shared dict` क्यों है? याद रखें कि डेटा साझा करने के पहले तीन तरीके सभी अनुरोध स्तर या व्यक्तिगत `Worker` स्तर पर हैं। इसलिए, OpenResty के वर्तमान कार्यान्वयन में, केवल `shared dict` ही `Worker` के बीच डेटा साझा कर सकता है, जो `Worker` के बीच संचार को सक्षम करता है, और यही इसके अस्तित्व का मूल्य है।

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

Shared dict पर वापस आते हुए, जो 20 से अधिक Lua API को सार्वजनिक करता है, सभी परमाणु हैं, इसलिए आपको कई `Workers` और उच्च समवर्तीता के मामले में प्रतिस्पर्धा की चिंता करने की आवश्यकता नहीं है।

इन API के सभी के लिए विस्तृत [आधिकारिक दस्तावेज़](https://github.com/openresty/lua-nginx-module#ngxshareddict) हैं, इसलिए मैं उन सभी पर विस्तार से नहीं जाऊंगा। मैं फिर से जोर देना चाहता हूं कि कोई भी तकनीकी पाठ्यक्रम आधिकारिक दस्तावेज़ को ध्यान से पढ़ने की जगह नहीं ले सकता है। कोई भी इन समय लेने वाले और मूर्खतापूर्ण प्रक्रियाओं को छोड़ नहीं सकता है।

आगे, आइए `shared dict` API को देखना जारी रखें, जिन्हें तीन श्रेणियों में विभाजित किया जा सकता है: dict पठन/लेखन, कतार संचालन, और प्रबंधन।

### Dict पठन/लेखन

आइए पहले dict पठन और लेखन वर्ग को देखें। मूल संस्करण में, केवल dict पठन और लेखन वर्ग के API थे, जो साझा शब्दकोशों की सबसे सामान्य विशेषताएं हैं। यहां सबसे सरल उदाहरण है।

```
$ resty --shdict='dogs 1m' -e 'local dict = ngx.shared.dogs
                               dict:set("Tom", 56)
                               print(dict:get("Tom"))'
```

`set` के अलावा, OpenResty चार लेखन विधियां भी प्रदान करता है: `safe_set`, `add`, `safe_add`, और `replace`। यहां `safe` उपसर्ग का अर्थ है कि यदि मेमोरी भर गई है, तो LRU के अनुसार पुराने डेटा को हटाने के बजाय, लेखन विफल हो जाएगा और `no memory` त्रुटि लौटाएगा।

`get` के अलावा, OpenResty `get_stale` विधि भी प्रदान करता है, जो `get` विधि की तुलना में समाप्त हो चुके डेटा के लिए एक अतिरिक्त रिटर्न मान प्रदान करता है।

```
value, flags, stale = ngx.shared.DICT:get_stale(key)
```

आप `delete` विधि को कॉल करके निर्दिष्ट कुंजी को हटा सकते हैं, जो `set(key, nil)` के बराबर है।

### कतार संचालन

कतार संचालन की ओर मुड़ते हुए, यह OpenResty में बाद में जोड़ा गया है और Redis के समान इंटरफ़ेस प्रदान करता है। कतार में प्रत्येक तत्व को `ngx_http_lua_shdict_list_node_t` द्वारा वर्णित किया जाता है।

```
typedef struct {
    ngx_queue_t queue;
    uint32_t value_len;
    uint8_t value_type;
    u_char data[1];
} ngx_http_lua_shdict_list_node_t;
```

मैंने इन कतारिंग API के [PR](https://github.com/openresty/lua-nginx-module/pull/586/files) को लेख में पोस्ट किया है। यदि आप इसके बारे में रुचि रखते हैं, तो आप दस्तावेज़, परीक्षण मामलों, और स्रोत कोड का अनुसरण करके विशिष्ट कार्यान्वयन का विश्लेषण कर सकते हैं।

हालांकि, दस्तावेज़ में निम्नलिखित पांच कतार API के लिए संबंधित कोड उदाहरण नहीं हैं, इसलिए मैं यहां संक्षेप में उनका परिचय दूंगा।

- `lpush``/``rpush` का अर्थ है कतार के दोनों छोर पर तत्व जोड़ना।
- `lpop``/``rpop`, जो कतार के दोनों छोर से तत्व निकालता है।
- `llen`, जो कतार में तत्वों की संख्या लौटाता है।

आइए हम पिछले लेख में चर्चा किए गए एक और उपयोगी उपकरण को न भूलें: परीक्षण मामले। यदि दस्तावेज़ में नहीं है, तो हम आमतौर पर परीक्षण मामले में संबंधित कोड पा सकते हैं। कतार-संबंधित परीक्षण `145-shdict-list.t` फ़ाइल में हैं।

```
=== TEST 1: lpush & lpop
--- http_config
    lua_shared_dict dogs 1m;
--- config
    location = /test {
        content_by_lua_block {
            local dogs = ngx.shared.dogs

            local len, err = dogs:lpush("foo", "bar")
            if len then
                ngx.say("push success")
            else
                ngx.say("push err: ", err)
            end

            local val, err = dogs:llen("foo")
            ngx.say(val, " ", err)

            local val, err = dogs:lpop("foo")
            ngx.say(val, " ", err)

            local val, err = dogs:llen("foo")
            ngx.say(val, " ", err)

            local val, err = dogs:lpop("foo")
            ngx.say(val, " ", err)
        }
    }
--- request
GET /test
--- response_body
push success
1 nil
bar nil
0 nil
nil nil
--- no_error_log
[error]
```

### प्रबंधन

अंतिम प्रबंधन API भी बाद में जोड़ा गया है और समुदाय में एक लोकप्रिय आवश्यकता है। सबसे विशिष्ट उदाहरणों में से एक शेयर्ड मेमोरी का उपयोग है। उदाहरण के लिए, यदि एक उपयोगकर्ता `shared dict` के रूप में `100M` स्थान का अनुरोध करता है, तो क्या यह `100M` पर्याप्त है? इसमें कितनी कुंजियाँ संग्रहीत हैं, और वे कौन सी हैं? ये सभी प्रामाणिक प्रश्न हैं।

इस प्रकार की समस्या के लिए, OpenResty आधिकारिक रूप से उपयोगकर्ताओं को फ्लेम ग्राफ का उपयोग करके इसे हल करने की उम्मीद करता है, यानी एक गैर-आक्रामक तरीके से, कोडबेस को कुशल और साफ-सुथरा रखते हुए, बजाय एक आक्रामक API प्रदान करके सीधे परिणाम लौटाने के।

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

पहला है `get_keys(max_count?)`, जो डिफ़ॉल्ट रूप से केवल पहले `1024` कुंजियाँ लौटाता है; यदि आप `max_count` को `0` पर सेट करते हैं, तो यह सभी कुंजियाँ लौटाएगा। फिर `capacity` और `free_space` आते हैं, जो दोनों `lua-resty-core` रिपॉजिटरी का हिस्सा हैं, इसलिए आपको उनका उपयोग करने से पहले `require` करना होगा।

```
require "resty.core.shdict"

local cats = ngx.shared.cats
local capacity_bytes = cats:capacity()
local free_page_bytes = cats:free_space()
```

वे शेयर्ड मेमोरी का आकार (`lua_shared_dict` में कॉन्फ़िगर किया गया आकार) और मुक्त पृष्ठों के बाइट्स की संख्या लौटाते हैं। चूंकि `shared dict` पृष्ठ द्वारा आवंटित किया जाता है, भले ही `free_space` `0` लौटाए, आवंटित पृष्ठों में स्थान हो सकता है। इसलिए, इसका रिटर्न मान यह नहीं दर्शाता है कि शेयर्ड मेमोरी कितना उपयोग किया गया है।

## सारांश

व्यवहार में, हम अक्सर मल्टी-लेवल कैशिंग का उपयोग करते हैं, और आधिकारिक OpenResty प्रोजेक्ट में भी एक कैशिंग पैकेज है। क्या आप पता लगा सकते हैं कि वे कौन से प्रोजेक्ट हैं? या क्या आप कुछ अन्य `lua-resty` लाइब्रेरीज जानते हैं जो कैशिंग को एनकैप्सुलेट करती हैं?

आप इस लेख को अपने सहयोगियों और दोस्तों के साथ साझा करने के लिए स्वागत कर रहे हैं ताकि हम एक साथ संवाद और सुधार कर सकें।

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

- [डेटा साझा करने के कई तरीके](#%E0%A4%A1%E0%A5%87%E0%A4%9F%E0%A4%BE-%E0%A4%B8%E0%A4%BE%E0%A4%9D%E0%A4%BE-%E0%A4%95%E0%A4%B0%E0%A4%A8%E0%A5%87-%E0%A4%95%E0%A5%87-%E0%A4%95%E0%A4%88-%E0%A4%A4%E0%A4%B0%E0%A5%80%E0%A4%95%E0%A5%87)

- [Shared dict](#shared-dict)

  - [Dict पठन/लेखन](#dict-%E0%A4%AA%E0%A4%A0%E0%A4%A8%E0%A4%B2%E0%A5%87%E0%A4%96%E0%A4%A8)
  - [कतार संचालन](#%E0%A4%95%E0%A4%A4%E0%A4%BE%E0%A4%B0-%E0%A4%B8%E0%A4%82%E0%A4%9A%E0%A4%BE%E0%A4%B2%E0%A4%A8)
  - [प्रबंधन](#%E0%A4%AA%E0%A5%8D%E0%A4%B0%E0%A4%AC%E0%A4%82%E0%A4%A7%E0%A4%A8)

- [सारांश](#%E0%A4%B8%E0%A4%BE%E0%A4%B0%E0%A4%BE%E0%A4%82%E0%A4%B6)
