Websocket परिवहन विश्वसनीयता (Socket.io डेटा हानि के दौरान पुन: संयोजन)


81

उपयोग किया गया

NodeJS, सॉकेट.आईओ

मुसीबत

कल्पना करें कि Socket.io के माध्यम से ऐप से जुड़े 2 उपयोगकर्ता U1 & U2 हैं । एल्गोरिथ्म निम्नलिखित है:

  1. U1 पूरी तरह से इंटरनेट कनेक्शन खो देता है (उदा। इंटरनेट बंद कर देता है)
  2. U2 U1 को एक संदेश भेजता है ।
  3. U1 संदेश अभी तक प्राप्त नहीं करता है, क्योंकि इंटरनेट नीचे है
  4. सर्वर U1 वियोग का पता दिल की धड़कन के समय से लगाता है
  5. U1 सॉकेट के लिए फिर से जुड़ता है
  6. U1 कभी भी U2 से संदेश प्राप्त नहीं करता है - यह मुझे लगता है कि चरण 4 पर खो गया है।

संभव व्याख्या

मुझे लगता है कि मैं समझता हूं कि ऐसा क्यों होता है:

  • चरण 4 पर सर्वर सॉकेट आवृत्ति और संदेशों की कतार U1 को भी मारता है
  • इसके अलावा चरण 5 यू 1 और सर्वर नया कनेक्शन बनाते हैं (इसका पुन: उपयोग नहीं किया जाता है), इसलिए भले ही संदेश अभी भी पंक्तिबद्ध हो, पिछला कनेक्शन वैसे भी खो जाता है।

मदद की ज़रूरत है

मैं इस तरह के डेटा हानि को कैसे रोक सकता हूं? मुझे सुनने के लिए उपयोग करना है, क्योंकि मैं लोगों को हमेशा के लिए ऐप में नहीं लटकाता हूं। इसके अलावा, मुझे अभी भी फिर से कनेक्ट करने की संभावना देनी चाहिए, क्योंकि जब मैं ऐप का नया संस्करण तैनात करता हूं तो मुझे शून्य डाउनटाइम चाहिए।

PS जिस चीज को मैं "संदेश" कहता हूं वह केवल एक पाठ संदेश नहीं है जिसे मैं डेटाबेस में संग्रहीत कर सकता हूं, बल्कि मूल्यवान सिस्टम संदेश, जिसे डिलीवरी की गारंटी दी जानी चाहिए, या यूआई शिकंजा।

धन्यवाद!


जोड़ 1

मेरे पास पहले से ही उपयोगकर्ता खाता प्रणाली है। इसके अलावा, मेरा आवेदन पहले से ही जटिल है। ऑफ़लाइन / ऑनलाइन स्टेटस जोड़ने से मदद नहीं मिलेगी, क्योंकि मेरे पास पहले से ही इस तरह का सामान है। समस्या अलग है।

चरण 2 देखें। इस चरण पर हम तकनीकी रूप से यह नहीं कह सकते हैं कि क्या यू 1 ऑफ़लाइन हो जाता है , वह केवल 2 सेकंड के लिए कनेक्शन खो देता है, शायद खराब इंटरनेट के कारण। तो U2 उसे एक संदेश भेजता है, लेकिन U1 इसे प्राप्त नहीं करता है क्योंकि इंटरनेट अभी भी उसके लिए नीचे है (चरण 3)। ऑफ़लाइन उपयोगकर्ताओं का पता लगाने के लिए चरण 4 की आवश्यकता है, जो कहते हैं कि, टाइमआउट 60 सेकंड है। आखिरकार U1 के लिए एक और 10 सेकंड के इंटरनेट कनेक्शन में है और वह सॉकेट के लिए फिर से जुड़ता है। लेकिन U2 का संदेश अंतरिक्ष में खो गया है क्योंकि सर्वर U1 पर टाइमआउट द्वारा डिस्कनेक्ट कर दिया गया था।

यही समस्या है, मैं 100% डिलीवरी नहीं करता।


उपाय

  1. यादृच्छिक एमिट द्वारा पहचाने गए {} उपयोगकर्ता में एक एमिट (इमिट नाम और डेटा) लीजिए। एमिट भेजें
  2. ग्राहक पक्ष पर उत्सर्जन की पुष्टि करें (emitID के साथ सर्वर पर वापस भेजें)
  3. यदि पुष्टि की गई है - ऑब्जेक्ट को {} से हटाकर emitID द्वारा पहचाना गया है
  4. यदि उपयोगकर्ता पुन: कनेक्ट हो गया है - इस उपयोगकर्ता के लिए {} की जाँच करें और इसके माध्यम से लूप करें {{प्रत्येक वस्तु में चरण 1 को निष्पादित करने के लिए।
  5. जब डिस्कनेक्ट या / और कनेक्ट फ्लश {} यदि आवश्यक हो तो उपयोगकर्ता के लिए
// Server
const pendingEmits = {};

socket.on('reconnection', () => resendAllPendingLimits);
socket.on('confirm', (emitID) => { delete(pendingEmits[emitID]); });

// Client
socket.on('something', () => {
    socket.emit('confirm', emitID);
});

समाधान 2 (थोड़े)

1 फरवरी 2020 को जोड़ा गया।

हालांकि यह वेबसोकेट के लिए वास्तव में एक समाधान नहीं है, फिर भी कोई इसे काम में ले सकता है। हम Websockets से SSE + Ajax में चले गए। SSE आपको एक ग्राहक से लगातार टीसीपी कनेक्शन रखने और रियलटाइम में सर्वर से संदेश प्राप्त करने के लिए कनेक्ट करने की अनुमति देता है। क्लाइंट से सर्वर पर संदेश भेजने के लिए - बस अजाक्स का उपयोग करें। विलंबता और ओवरहेड जैसे नुकसान हैं, लेकिन एसएसई विश्वसनीयता की गारंटी देता है क्योंकि यह टीसीपी कनेक्शन है।

चूंकि हम एक्सप्रेस का उपयोग करते हैं, इसलिए हम इस लाइब्रेरी का उपयोग SSE https://github.com/dpskvn/express-sse के लिए करते हैं , लेकिन आप उसे फिट कर सकते हैं।

SSE IE और अधिकांश एज संस्करणों में समर्थित नहीं है, इसलिए आपको एक पॉलीफ़िल की आवश्यकता होगी: https://github.com/Yaffle/EventSource


सच है। लेकिन सॉकेट.आईओ वास्तव में सिर्फ एक परिवहन प्रोटोकॉल है। यह अकेले सुसंगत और विश्वसनीय संदेश वितरण की गारंटी नहीं दे सकता है। आपको पब (सब-पब्लिश-सब्सक्राइब) आर्किसेक्टर्स और संदेश कतारों को देखना (और पढ़ना) चाहिए। व्यवहार में आप संदेशों को संग्रहीत करने के लिए रेडिस जैसे लगातार डेटाबेस का उपयोग करेंगे।
user568109

तो पबब्स इस मुद्दे को हल करेंगे? यदि आप एक व्यापक उत्तर और समाधान कार्य लिखते हैं, तो आपको इनाम (50 अंक) से पुरस्कृत किया जाएगा।
igorpavlov

8
इस तरह के एक सुंदर संगठित प्रश्न
केटी

1
धन्यवाद। मुझे कहना होगा कि स्वीकृत उत्तर मेरे लिए काम करता है। मैं वर्तमान में सुझाई गई योजना का उपयोग करता हूं और समस्या नहीं है।
igorpavlov

हाय इगोर! मैं Node.js और Socket.io में नया हूं। यदि यह संभव हो तो आप अपना कोड दिखा सकते हैं :)
Eazy

जवाबों:


103

अन्य लोगों ने अन्य उत्तरों और टिप्पणियों में इस पर संकेत दिया है, लेकिन मूल समस्या यह है कि सॉकेट.आईओ केवल एक वितरण तंत्र है, और आप विश्वसनीय वितरण के लिए अकेले इस पर निर्भर नहीं हो सकते हैं। केवल एक व्यक्ति जो यह सुनिश्चित करने के लिए जानता है कि ग्राहक को एक संदेश सफलतापूर्वक वितरित किया गया है वह ग्राहक ही है । इस तरह की प्रणाली के लिए, मैं निम्नलिखित दावे करने की सलाह दूंगा:

  1. संदेश सीधे ग्राहकों को नहीं भेजे जाते हैं; इसके बजाय, वे सर्वर को भेजे जाते हैं और किसी प्रकार के डेटा स्टोर में संग्रहीत होते हैं।
  2. जब वे पुन: कनेक्ट होते हैं, तो ग्राहक "मैंने क्या याद किया" पूछने के लिए जिम्मेदार हैं, और अपने राज्य को अपडेट करने के लिए डेटा स्टोर में संग्रहीत संदेशों को क्वेरी करेगा।
  3. यदि प्राप्तकर्ता क्लाइंट से कनेक्ट होने के दौरान एक संदेश सर्वर पर भेजा जाता है, तो वह संदेश ग्राहक को वास्तविक समय में भेजा जाएगा।

बेशक, आपके आवेदन की जरूरतों के आधार पर, आप इस के टुकड़ों को ट्यून कर सकते हैं - उदाहरण के लिए, आप संदेशों के लिए एक रेडिस सूची या सॉर्ट किए गए सेट का उपयोग कर सकते हैं, कह सकते हैं और उन्हें साफ़ कर सकते हैं यदि आप जानते हैं कि एक ग्राहक है तारीख तक।


यहां कुछ उदाहरण दिए गए हैं:

खुश रास्ता :

  • U1 और U2 दोनों सिस्टम से जुड़े हैं।
  • U2 सर्वर को एक संदेश भेजता है जिसे U1 को प्राप्त करना चाहिए।
  • सर्वर किसी प्रकार के लगातार स्टोर में संदेश संग्रहीत करता है, इसे U1 के लिए किसी प्रकार के टाइमस्टैम्प या अनुक्रमिक आईडी के साथ चिह्नित करता है।
  • सर्वर U1 को Socket.IO के माध्यम से संदेश भेजता है।
  • U1 का क्लाइंट पुष्टि करता है (शायद सॉकेट.आईओ कॉलबैक के माध्यम से) यह संदेश प्राप्त करता है।
  • सर्वर डेटा स्टोर से जारी संदेश को हटा देता है।

ऑफ़लाइन पथ :

  • U1 इंटरनेट कनेक्टिविटी खो देता है।
  • U2 सर्वर को एक संदेश भेजता है जिसे U1 को प्राप्त करना चाहिए।
  • सर्वर किसी प्रकार के लगातार स्टोर में संदेश संग्रहीत करता है, इसे U1 के लिए किसी प्रकार के टाइमस्टैम्प या अनुक्रमिक आईडी के साथ चिह्नित करता है।
  • सर्वर U1 को Socket.IO के माध्यम से संदेश भेजता है।
  • U1 के ग्राहक रसीद की पुष्टि नहीं करते हैं, क्योंकि वे ऑफ़लाइन हैं।
  • शायद U2 U1 को कुछ और संदेश भेजता है; वे सभी एक ही अंदाज में डेटा स्टोर में जमा हो जाते हैं।
  • जब U1 पुन: जुड़ता है, तो यह सर्वर से पूछता है "मैंने जो अंतिम संदेश देखा वह X था / मेरे पास राज्य X है, मुझे क्या याद आया।"
  • सर्वर U1 के अनुरोध के आधार पर डेटा स्टोर से छूटे सभी संदेशों को U1 भेजता है
  • U1 का क्लाइंट रसीद की पुष्टि करता है और सर्वर उन संदेशों को डेटा स्टोर से हटा देता है।

यदि आप पूरी तरह से गारंटीकृत डिलीवरी चाहते हैं, तो अपने सिस्टम को इस तरह से डिजाइन करना महत्वपूर्ण है कि जुड़ा होना वास्तव में कोई मायने नहीं रखता है, और यह कि रियलटाइम डिलीवरी केवल एक बोनस है ; इसमें लगभग हमेशा किसी प्रकार का डेटा स्टोर शामिल होता है। जैसा कि user568109 ने एक टिप्पणी में उल्लेख किया है, संदेश प्रणालियां हैं जो उक्त संदेशों के भंडारण और वितरण को दूर करती हैं, और यह इस तरह के पूर्वनिर्मित समाधान को देखने के लायक हो सकता है। (आपको संभवतः सॉकेट.आईओ एकीकरण खुद लिखना होगा।)

यदि आप डेटाबेस में संदेशों को संग्रहीत करने में रुचि नहीं रखते हैं, तो आप उन्हें स्थानीय सरणी में संग्रहीत करने में सक्षम हो सकते हैं; सर्वर U1 संदेश भेजने की कोशिश करता है, और इसे "लंबित संदेशों" की सूची में तब तक संग्रहीत करता है जब तक कि U1 का ग्राहक यह पुष्टि न कर ले कि उसे यह प्राप्त हो गया है। यदि क्लाइंट ऑफ़लाइन है, तो जब यह वापस आता है तो यह सर्वर को बता सकता है "अरे मैं डिस्कनेक्ट हो गया था, कृपया मुझे कुछ भी भेजें जो मैंने याद किया" और सर्वर उन संदेशों के माध्यम से पुनरावृति कर सकता है।

सौभाग्य से, सॉकेट.आईओ एक तंत्र प्रदान करता है जो एक ग्राहक को एक संदेश को "जेएस" करने की अनुमति देता है जो मूल जेएस कॉलबैक की तरह दिखता है। यहाँ कुछ छद्मकोड है:

// server
pendingMessagesForSocket = [];

function sendMessage(message) {
  pendingMessagesForSocket.push(message);
  socket.emit('message', message, function() {
    pendingMessagesForSocket.remove(message);
  }
};

socket.on('reconnection', function(lastKnownMessage) {
  // you may want to make sure you resend them in order, or one at a time, etc.
  for (message in pendingMessagesForSocket since lastKnownMessage) {
    socket.emit('message', message, function() {
      pendingMessagesForSocket.remove(message);
    }
  }
});

// client
socket.on('connection', function() {
  if (previouslyConnected) {
    socket.emit('reconnection', lastKnownMessage);
  } else {
    // first connection; any further connections means we disconnected
    previouslyConnected = true;
  }
});

socket.on('message', function(data, callback) {
  // Do something with `data`
  lastKnownMessage = data;
  callback(); // confirm we received the message
});

यह पिछले सुझाव के समान है, बस एक निरंतर डेटा स्टोर के बिना।


इवेंट सोर्सिंग की अवधारणा में भी आपकी रुचि हो सकती है ।


2
मैंने एक बयान के साथ अंतिम व्यापक उत्तर की प्रतीक्षा की है, कि क्लाइंट को डिलीवरी की पुष्टि करनी होगी। लगता है कि वास्तव में कोई और रास्ता नहीं है।
igorpavlov

मदद करने के लिए खुश! यदि आपके कोई प्रश्न हैं, तो मुझे पिंग दें
मिशेल टाइली

यह एक-से-एक चैट परिदृश्य में काम करेगा। कमरों में क्या होता है, जहां संदेश कई उपयोगकर्ताओं को भेजा जाता है। प्रसारण / socket.in कॉलबैक का समर्थन नहीं करता है। तो हम उस स्थिति को कैसे संभालेंगे? इस पर मेरा सवाल है। ( stackoverflow.com/questions/43186636/… )
jit

2

मिशेल का जवाब बहुत ज्यादा है, लेकिन विचार करने के लिए कुछ अन्य महत्वपूर्ण बातें हैं। अपने आप से पूछने के लिए मुख्य सवाल यह है: "क्या मेरे ऐप में उपयोगकर्ता और सॉकेट के बीच अंतर है?" यह पूछने का एक और तरीका है कि "क्या प्रत्येक लॉग इन उपयोगकर्ता के पास एक समय में 1 से अधिक सॉकेट कनेक्शन हो सकता है?"

वेब दुनिया में यह हमेशा एक संभावना है कि एक एकल उपयोगकर्ता के पास कई सॉकेट कनेक्शन हैं, जब तक कि आपने विशेष रूप से कुछ ऐसा नहीं रखा है जो इसे रोकता है। इसका सबसे सरल उदाहरण है यदि उपयोगकर्ता के पास एक ही पृष्ठ के दो टैब खुले हैं। इन मामलों में आप केवल एक बार मानव उपयोगकर्ता को एक संदेश / घटना भेजने के बारे में परवाह नहीं करते हैं ... आपको इसे उस उपयोगकर्ता के लिए प्रत्येक सॉकेट इंस्टेंस पर भेजने की आवश्यकता है ताकि प्रत्येक टैब ui राज्य को अपडेट करने के लिए कॉलबैक चला सके। शायद यह कुछ अनुप्रयोगों के लिए चिंता का विषय नहीं है, लेकिन मेरा पेट कहता है कि यह अधिकांश के लिए होगा। अगर यह आपके लिए चिंता का विषय है, तो पढ़ें ...।

इसे हल करने के लिए (यह मानते हुए कि आप एक डेटाबेस को अपने लगातार भंडारण के रूप में उपयोग कर रहे हैं) आपको 3 तालिकाओं की आवश्यकता होगी।

  1. उपयोगकर्ता - जो वास्तविक लोगों के साथ 1 से 1 है
  2. क्लाइंट - जो एक "टैब" का प्रतिनिधित्व करता है जो कर सकता है एक सॉकेट सर्वर से एकल कनेक्शन है। (कोई भी 'उपयोगकर्ता' कई हो सकता है)
  3. संदेश - एक संदेश जो क्लाइंट को भेजा जाना चाहिए (संदेश नहीं जिसे उपयोगकर्ता या सॉकेट के लिए भेजा जाना चाहिए)

यदि आपके ऐप को इसकी आवश्यकता नहीं है, तो उपयोगकर्ता तालिका वैकल्पिक है, लेकिन ओपी ने कहा कि उनके पास एक है।

दूसरी चीज़ जिसे ठीक से परिभाषित करना है, वह है "सॉकेट कनेक्शन क्या है?", "सॉकेट कनेक्शन कब बनाया जाता है?", "सॉकेट कनेक्शन का पुन: उपयोग कब किया जाता है?"। मिशेल के स्यूडोकोड से ऐसा लगता है कि सॉकेट कनेक्शन का पुन: उपयोग किया जा सकता है। सॉकेट.आईओ के साथ, उनका पुन: उपयोग नहीं किया जा सकता है। मैंने देखा है कि बहुत भ्रम का स्रोत है। वास्तविक जीवन परिदृश्य हैं जहां मिशेल का उदाहरण समझ में आता है। लेकिन मुझे लगता है कि उन परिदृश्यों दुर्लभ हैं। वास्तव में क्या होता है जब एक सॉकेट कनेक्शन खो जाता है, तो उस कनेक्शन, आईडी आदि का पुन: उपयोग नहीं किया जाएगा। तो उस सॉकेट के लिए चिह्नित किए गए किसी भी संदेश को विशेष रूप से किसी को भी वितरित नहीं किया जाएगा, क्योंकि जब ग्राहक जो मूल रूप से जुड़ा था, फिर से जुड़ता है, तो उन्हें पूरी तरह से नया कनेक्शन और नई आईडी मिलती है। इसका मतलब यह है '

तो एक वेब आधारित उदाहरण के लिए यहां उन चरणों का सेट होगा जो मैं सुझाऊंगा:

  • जब कोई उपयोगकर्ता किसी क्लाइंट (आमतौर पर एक एकल वेबपेज) को लोड करता है जिसमें सॉकेट कनेक्शन बनाने की क्षमता होती है, तो क्लाइंट डेटाबेस में एक पंक्ति जोड़ें जो उनके उपयोगकर्ता आईडी से जुड़ी होती है।
  • जब उपयोगकर्ता वास्तव में सॉकेट सर्वर से कनेक्ट होता है, तो क्लाइंट आईडी को कनेक्शन अनुरोध के साथ सर्वर से पास करें।
  • सर्वर को मान्य करना चाहिए कि उपयोगकर्ता को कनेक्ट करने की अनुमति दी गई है और क्लाइंट तालिका में क्लाइंट पंक्ति कनेक्शन के लिए उपलब्ध है और तदनुसार अनुमति दें / अस्वीकार करें।
  • सॉकेट आईडी द्वारा उत्पन्न सॉकेट आईडी के साथ क्लाइंट पंक्ति को अपडेट करें।
  • क्लाइंट आईडी से जुड़ी संदेश तालिका में कोई भी आइटम भेजें। प्रारंभिक कनेक्शन पर कोई भी नहीं होगा, लेकिन अगर यह ग्राहक पुन: कनेक्ट करने की कोशिश कर रहा था, तो कुछ हो सकता है।
  • किसी भी समय संदेश को उस सॉकेट पर भेजने की आवश्यकता होती है, संदेश तालिका में एक पंक्ति जोड़ें जो आपके द्वारा बनाई गई क्लाइंट आईडी से जुड़ी हुई है (सॉकेट आईडी से नहीं)।
  • संदेश को स्वीकार करने और ग्राहक को पावती के साथ सुनने का प्रयास करें।
  • जब आपको पावती मिल जाती है, तो उस आइटम को संदेश तालिका से हटा दें।
  • आप क्लाइंट साइड पर कुछ लॉजिक बनाने की इच्छा कर सकते हैं जो सर्वर से भेजे गए डुप्लीकेट मैसेजेस को डिस्क्राइब करता है क्योंकि यह तकनीकी रूप से एक संभावना है क्योंकि कुछ ने इशारा किया है।
  • फिर जब कोई ग्राहक सॉकेट सर्वर से (उद्देश्य या त्रुटि के माध्यम से) डिस्कनेक्ट होता है, तो क्लाइंट पंक्ति को न हटाएं, बस सॉकेट आईडी को अधिक से अधिक साफ़ करें। ऐसा इसलिए है क्योंकि वही क्लाइंट पुन: कनेक्ट करने का प्रयास कर सकता है।
  • जब कोई ग्राहक पुन: कनेक्ट करने का प्रयास करता है, तो उसी क्लाइंट आईडी को भेजें, जिसे उसने मूल कनेक्शन के प्रयास के साथ भेजा था। सर्वर इसे शुरुआती कनेक्शन की तरह ही देखेगा।
  • जब क्लाइंट नष्ट हो जाता है (उपयोगकर्ता टैब को बंद कर देता है या दूर चला जाता है), यह तब होता है जब आप क्लाइंट पंक्ति और इस क्लाइंट के लिए सभी संदेश हटाते हैं। यह कदम थोड़ा मुश्किल हो सकता है।

क्योंकि अंतिम चरण मुश्किल है (कम से कम इसका उपयोग किया जाता था, मैंने लंबे समय में ऐसा कुछ नहीं किया है), और क्योंकि बिजली के नुकसान जैसे मामले हैं जहां ग्राहक क्लाइंट पंक्ति को साफ किए बिना डिस्कनेक्ट कर देगा और कभी कोशिश नहीं करेगा उसी ग्राहक पंक्ति के साथ पुन: कनेक्ट करने के लिए - आप शायद कुछ ऐसा करना चाहते हैं जो किसी भी बासी ग्राहक और संदेश पंक्तियों को साफ करने के लिए समय-समय पर चलता है। या, आप स्थायी रूप से सभी ग्राहकों और संदेशों को हमेशा के लिए स्टोर कर सकते हैं और बस उनके राज्य को उचित रूप से चिह्नित कर सकते हैं।

तो बस स्पष्ट होने के लिए, उन मामलों में जहां एक उपयोगकर्ता के दो टैब खुले हैं, आप प्रत्येक ग्राहक के लिए प्राप्त किए गए संदेश तालिका में दो समान संदेश जोड़ेंगे क्योंकि आपके सर्वर को यह जानना होगा कि क्या प्रत्येक ग्राहक ने उन्हें प्राप्त किया है, न कि प्रत्येक उपयोगकर्ता को।


1

ऐसा लगता है कि आपके पास पहले से ही उपयोगकर्ता खाता प्रणाली है। आप जानते हैं कि कौन सा खाता ऑनलाइन / ऑफ़लाइन है, आप कनेक्ट / डिस्कनेक्ट घटना को संभाल सकते हैं:

इसलिए समाधान यह है कि, प्रत्येक उपयोगकर्ता के लिए डेटाबेस पर ऑनलाइन / ऑफलाइन और ऑफलाइन संदेश जोड़ें:

chatApp.onLogin(function (user) {
   user.readOfflineMessage(function (msgs) {
       user.sendOfflineMessage(msgs, function (err) {
           if (!err) user.clearOfflineMessage();
       });
   })
});

chatApp.onMessage(function (fromUser, toUser, msg) {
   if (user.isOnline()) {
      toUser.sendMessage(msg, function (err) {
          // alert CAN NOT SEND, RETRY?
      });
   } else {
      toUser.addToOfflineQueue(msg);
   }
})

कृपया मेरे प्रश्न में "अतिरिक्त 1" अनुभाग पढ़ें। मुझे नहीं लगता कि आपका जवाब एक समाधान है।
igorpavlov 14

यह दिलचस्प है, मैं अपनी खुद की चैट परियोजना शुरू करता हूं, शायद वेब आरटीसी के साथ: ->
damphat

WebRTC पर भी। लेकिन इस संदर्भ में यह कोई मायने नहीं रखता। आह ... यदि सभी लोगों के पास स्थिर इंटरनेट है ... उपयोगकर्ताओं द्वारा स्पीडटेस्ट पर 100Mbps होने पर मैं बहुत निराश हूं, लेकिन वास्तव में अगर वे पिंग करने की कोशिश करते हैं तो उन्हें 20% पैकेट नुकसान होता है। ऐसे इंटरनेट की जरूरत किसे है? =)
igorpavlov

0

यहां देखें: ब्राउज़र ब्राउज़र सॉकेट पुनः लोड करें

मुझे लगता है कि आप समाधान का उपयोग कर सकते हैं जो मैं लेकर आया हूं। यदि आप इसे ठीक से संशोधित करते हैं, तो यह काम करना चाहिए जैसा आप चाहते हैं।


यह दिलचस्प है, मैं इस सवाल का पता नहीं लगा सका, लेकिन कई घंटों तक गुगला रहा। देख लेंगे!
igorpavlov

ऐसा लगता है कि मैं पहले से ही इस तरह की वास्तुकला का उपयोग करता हूं। यह मेरे द्वारा बताए गए सटीक मुद्दे को हल नहीं करता है।
igorpavlov

0

मुझे लगता है कि आप चाहते हैं कि प्रत्येक उपयोगकर्ता के लिए एक पुन: प्रयोज्य सॉकेट होना चाहिए, जैसे कुछ:

ग्राहक:

socket.on("msg", function(){
    socket.send("msg-conf");
});

सर्वर:

// Add this socket property to all users, with your existing user system
user.socket = {
    messages:[],
    io:null
}
user.send = function(msg){ // Call this method to send a message
    if(this.socket.io){ // this.io will be set to null when dissconnected
        // Wait For Confirmation that message was sent.
        var hasconf = false;
        this.socket.io.on("msg-conf", function(data){
            // Expect the client to emit "msg-conf"
            hasconf = true;
        });
        // send the message
        this.socket.io.send("msg", msg); // if connected, call socket.io's send method
        setTimeout(function(){
            if(!hasconf){
                this.socket = null; // If the client did not respond, mark them as offline.
                this.socket.messages.push(msg); // Add it to the queue
            }
        }, 60 * 1000); // Make sure this is the same as your timeout.

    } else {
        this.socket.messages.push(msg); // Otherwise, it's offline. Add it to the message queue
    }
}
user.flush = function(){ // Call this when user comes back online
    for(var msg in this.socket.messages){ // For every message in the queue, send it.
        this.send(msg);
    }
}
// Make Sure this runs whenever the user gets logged in/comes online
user.onconnect = function(socket){
    this.socket.io = socket; // Set the socket.io socket
    this.flush(); // Send all messages that are waiting
}
// Make sure this is called when the user disconnects/logs out
user.disconnect = function(){
    self.socket.io = null; // Set the socket to null, so any messages are queued not send.
}

फिर सॉकेट कतार को डिस्कनेक्ट के बीच संरक्षित किया जाता है।

सुनिश्चित करें कि यह socketडेटाबेस के लिए प्रत्येक उपयोगकर्ता की संपत्ति बचाता है और तरीकों को अपने उपयोगकर्ता प्रोटोटाइप का हिस्सा बनाता है। डेटाबेस कोई फर्क नहीं पड़ता, बस इसे सहेजें लेकिन आप अपने उपयोगकर्ताओं को बचा रहे हैं।

यह भेजे गए संदेश को चिह्नित करने से पहले क्लाइंट से पुष्टि की आवश्यकता से एडिटोन 1 में वर्णित समस्या से बच जाएगा। यदि आप वास्तव में चाहते हैं, तो आप प्रत्येक संदेश को एक आईडी दे सकते हैं और ग्राहक को संदेश आईडी भेज सकते हैंmsg-conf सकते हैं, फिर उसे जांचें।

इस उदाहरण में, user टेम्पलेट उपयोगकर्ता है जिसे सभी उपयोगकर्ताओं से कॉपी किया जाता है, या उपयोगकर्ता प्रोटोटाइप की तरह।

नोट: इसका परीक्षण नहीं किया गया है।


क्या आप मुझे बता सकते हैं कि वास्तव में "उपयोगकर्ता" चर क्या है?
igorpavlov

असल में, मुझे लगता है कि आपने मेरे सवाल का जवाब दिया। लेकिन क्या आप कृपया प्रत्येक कोड के लिए कुछ टिप्पणियां भी प्रदान कर सकते हैं? मुझे समझ नहीं आ रहा है कि मैं इसे अपने कोड में कैसे एकीकृत कर सकता हूं। इसके अलावा, मुझे इसे कहाँ डेटाबेस में सहेजना चाहिए और किस तरह के डेटाबेस से आपका मतलब है? रेडिस या मोंगो हो सकता है या कोई फर्क नहीं पड़ता?
igorpavlov 12

यह अभी भी समस्या को हल नहीं करता है। जब संदेश भेजा जाता है तो दोनों उपयोगकर्ता (प्रेषक और रिसीवर) सर्वर के लिए ऑनलाइन होते हैं। कृपया मेरे प्रश्न में जोड़ 1 को बहुत ध्यान से पढ़ें। इस मामले में "this.socket.io" हमेशा "सत्य" होगा, इसलिए संदेश भेजा जा रहा है, लेकिन प्राप्त नहीं हुआ है। आप समस्या को हल करने का प्रयास करते हैं, जब SENDER ऑफ़लाइन हो जाता है, लेकिन RECEIVER नहीं। या मैं सही नहीं हूँ?
igorpavlov

@igorpavlov, क्षमा करें, लेकिन आप मुझे गलत समझ रहे हैं। यह कल्पना करें: U1 U2 को एक संदेश भेजना चाहता है, "हाय" users.getUserByName("U2").send("Hi")। फिर यदि U2 ऑनलाइन है, तो U2 का सॉकेट.io शून्य नहीं होगा, इसलिए संदेश भेजा जाएगा। यदि U2 का सॉकेट शून्य है, तो यह U2 ऑनलाइन होने तक कतार में रहेगा।
अरी पोरद

1
मेरा मानना ​​है कि @igorpavlov सही है। उस समय की अवधि होगी जब ग्राहक वास्तव में डिस्कनेक्ट हो जाता है, लेकिन सर्वर को यह नहीं पता है क्योंकि दिल की धड़कन अभी तक नहीं हुई है। समय की इस अवधि में, this.socket.ioहोगा नहीं हो null, और सर्वर संदेश देने की कोशिश करेंगे।
मिशेल टिली

0

जैसा कि पहले से ही एक अन्य उत्तर में लिखा गया है, मेरा यह भी मानना ​​है कि आपको रियलटाइम को बोनस के रूप में देखना चाहिए: सिस्टम को अपने रियलटाइम के साथ भी काम करने में सक्षम होना चाहिए।

मैं एक बड़ी कंपनी (ios, android, web frontend और .net core + postGres backend) के लिए एक एंटरप्राइज़ चैट विकसित कर रहा हूं और वेबसैट के लिए कनेक्शन को फिर से स्थापित करने के लिए (सॉकेट uuid के माध्यम से) और अविभाजित संदेश प्राप्त करने का एक तरीका विकसित करने के बाद (एक कतार में संग्रहीत) मुझे समझ में आया कि एक बेहतर समाधान था: बाकी एपीआई के माध्यम से फिर से सिंक करें।

मूल रूप से मैं खोए हुए संदेशों की निगरानी के लिए प्रत्येक realtime संदेश (उपयोगकर्ता ऑनलाइन, टाइपर्स, चैट संदेश और इतने पर) पर एक पूर्णांक टैग के साथ, केवल realtime के लिए websocket का उपयोग करके समाप्त हुआ।

जब क्लाइंट को एक आईडी मिलती है जो मोनोलिथिक (+1) नहीं होती है तो वह समझता है कि यह सिंक से बाहर है इसलिए यह सभी सॉकेट संदेशों को छोड़ देता है और REST एपी के माध्यम से अपने सभी पर्यवेक्षकों का रेसक्यू पूछता है।

इस तरह हम ऑफ़लाइन की अवधि के दौरान एप्लिकेशन की स्थिति में कई बदलावों को संभाल सकते हैं, ताकि वे पुन: संयोजन पर एक पंक्ति में टन के वेबसैट संदेशों को पार्स कर सकें और हमें सिंक होना निश्चित है (क्योंकि अंतिम सिंक की तारीख आरएआई एप द्वारा ही सेट की गई है) , सॉकेट से नहीं)।

एकमात्र मुश्किल हिस्सा वास्तविक समय के संदेशों की निगरानी कर रहा है जिस क्षण से आप सर्वर रिप्लाई करने के लिए REST एपि को कॉल करते हैं क्योंकि db से जो पढ़ा जाता है उसे क्लाइंट को वापस पाने में समय लगता है और इस बीच भिन्नताएं हो सकती हैं इसलिए उन्हें समाप्त करने की आवश्यकता होती है और खाते में ले लिया।

हम कुछ महीनों में उत्पादन में जा रहे हैं, मुझे तब तक नींद आने की उम्मीद है :)


-2

बाद में इस सामान को देखिए और सोचिए कि अलग रास्ता बेहतर हो सकता है।

Azure Service Bus, ques और topic को देखने की कोशिश करें, ऑफ लाइन स्टेट्स का ध्यान रखें। संदेश उपयोगकर्ता के वापस आने की प्रतीक्षा करता है और फिर उन्हें संदेश मिलता है।

एक कतार को चलाने के लिए एक लागत है, लेकिन एक बुनियादी कतार के लिए $ 0.05 प्रति मिलियन संचालन की तरह इसलिए देव की लागत एक कतार प्रणाली लिखने के लिए घंटों के काम से अधिक होगी। https://azure.microsoft.com/en-us/pricing/details/service-bus/

और azure बस में PHP, C #, Xarmin, Anjular, Java Script आदि के लिए पुस्तकालय और उदाहरण हैं।

इसलिए सर्वर संदेश भेजता है और उन्हें ट्रैक करने के बारे में चिंता करने की आवश्यकता नहीं है। क्लाइंट संदेश भेजने के लिए भी उपयोग कर सकता है क्योंकि जरूरत पड़ने पर लोड संतुलन को संभाल सकता है।


यह मेरे लिए उत्पाद प्लेसमेंट जैसा दिखता है। किसी को यह मददगार लग सकता है, लेकिन यह भी एक तकनीक नहीं है, बल्कि एक पूरी सेवा भी है।
igorpavlov

-2

यह चैट सूची उत्सर्जित करने का प्रयास करें

io.on('connect', onConnect);

function onConnect(socket){

  // sending to the client
  socket.emit('hello', 'can you hear me?', 1, 2, 'abc');

  // sending to all clients except sender
  socket.broadcast.emit('broadcast', 'hello friends!');

  // sending to all clients in 'game' room except sender
  socket.to('game').emit('nice game', "let's play a game");

  // sending to all clients in 'game1' and/or in 'game2' room, except sender
  socket.to('game1').to('game2').emit('nice game', "let's play a game (too)");

  // sending to all clients in 'game' room, including sender
  io.in('game').emit('big-announcement', 'the game will start soon');

  // sending to all clients in namespace 'myNamespace', including sender
  io.of('myNamespace').emit('bigger-announcement', 'the tournament will start soon');

  // sending to individual socketid (private message)
  socket.to(<socketid>).emit('hey', 'I just met you');

  // sending with acknowledgement
  socket.emit('question', 'do you think so?', function (answer) {});

  // sending without compression
  socket.compress(false).emit('uncompressed', "that's rough");

  // sending a message that might be dropped if the client is not ready to receive messages
  socket.volatile.emit('maybe', 'do you really need it?');

  // sending to all clients on this node (when using multiple nodes)
  io.local.emit('hi', 'my lovely babies');

};

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.