HowTo: DjSo दोस्तों को कैसे बनाया जा सकता है WebSocket (socket.io, sockjs) के साथ

संस्करण: 0.2

मुझे उपयोगकर्ताओं की एक निश्चित संख्या के लिए एक पृष्ठ के वास्तविक समय के परमाणु अद्यतन की आवश्यकता थी, जो अन्य उपयोगकर्ताओं ( हर्बलाइफ चैट नहीं) के कार्यों पर निर्भर करता है। बेशक , आप सब कुछ कूड़ेदान में फेंक सकते हैं और अच्छी तरह से किया जा सकता है , इसे बवंडर / ट्विस्टेड.वेब पर खरोंच से मिटा सकते हैं, लेकिन यह स्पष्ट रूप से सबसे उत्पादक तरीका नहीं है (और मैं एक बार में अच्छा नहीं हूं) जब जरूरत है कि पहले से ही Django पर काम कर रहा है और यह आवश्यक है। बस थोड़ा सा ... स्वाभाविक रूप से, संक्षेप में, WebSocket यहां अनुरोध किया गया है। और सभी एक Django WSGI एप्लिकेशन के अलावा कुछ भी नहीं होगा, और यह मानक ऐसी चालों को भी बंद नहीं करता है (अभी के लिए)। इंटरनेट पर चलते हुए, एक बार फिर, जाने-माने अजगर गुरु किमीिक के काम का नेतृत्व किया (यह व्यंग्य के बिना है, क्योंकि उनके काम ने मुझे व्यक्तिगत रूप से एक से अधिक बार बचाया, जिसके लिए मैं उन्हें नमन करता हूं!)।

इसलिए यदि आप अपने Django प्रोजेक्ट को sso.io या sockjs js लाइब्रेरी का उपयोग करके वेबसोकेट के साथ पार करना चाहते हैं - तो महसूस करें!

अपडेट के बारे में


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

प्रविष्टि


लंबे समय से मैं अतुल्यकालिक कुछ करने की कोशिश करना चाहता था, लेकिन फिर भी कोई अच्छा कारण नहीं था, एक आवश्यकता थी, और जहां इसे शुरू करना पूरी तरह से अस्पष्ट था। दरअसल, यहां मैं इसे सबसे अधिक प्रासंगिक बनाने की कोशिश करूंगा (मैंने खुद ही उपरोक्त दस्तावेज को एक शुरुआती बिंदु के रूप में लिया था, लेकिन यह काफी पुराना है और कुछ सुधार पहले ही सामने आ चुके हैं) शुरू करने के लिए शुरुआती बिंदु। Django का एक परिचित आइलेट है, जिसमें मैं दिखाऊंगा कि कैसे एक ताजा हवा में जाने के लिए ...

जिस तरह से किमीकाइक के काम में बदलाव के बिना कुछ कार्यों का उपयोग किया गया था, मुझे उम्मीद है कि लेखक को कोई आपत्ति नहीं है।

हमें क्या मिलता है


नतीजतन, हमें एक अतुल्यकालिक सेवा मिलती है जो मुख्य django साइट के बगल में घूमती है, यह जानती है कि उपयोगकर्ता किस django को अनुरोध भेजता / प्राप्त करता है, और [सेवा] django से कमांड प्राप्त कर सकता है, उपयोगकर्ता के ब्राउज़र में उनके आधार पर कुछ क्रियाएं करता है।

उदाहरण


उदाहरण के लिए एक काल्पनिक विनिमय करें। उसके पास मॉडरेटर्स और क्लाइंट्स हैं। आपके लिए सब कुछ ठीक रहा, और यहां एक्सचेंजों में स्थितियों में बदलाव देखने के लिए मध्यस्थों को वास्तविक समय में अवसर देना आवश्यक था। उसी समय, मध्यस्थ किसी तरह एक्सचेंज में पदों के साथ काम कर सकते हैं और आप केवल पृष्ठ को पुनः लोड नहीं कर सकते।

इससे पहले, आप सभी F5 एक साथ सॉसेज थे ... और, सामान्य तौर पर, हाईलोड , जैसे, हम विशेष रूप से रुचि नहीं रखते हैं।

उपकरण


काम के लिए, हमें चाहिए:
pip install redis tornado-redis  pip install tornadio2  pip install sockjs-tornado     . 

और सॉकेट.आईओ या सॉक्स लाइब्रेरी भी

सिद्धांत


हम सॉकेट के साथ काम करने के लिए tornadio2 पुस्तकालय का उपयोग करेंगे। और sockjs-tornado, जो स्वाभाविक रूप से sockjs के लिए बवंडर अतुल्यकालिक ढांचे पर आधारित हैं। इस मामले को django टीम (हैलो सुपरवाइजर ) के रूप में लॉन्च किया जाएगा। जब कोई बवंडर नहीं होता है, तो जंगल खेलने में कोई विशेष समस्या नहीं होती है, लेकिन बदले में हमारे पास एक छोटा सा गग होता है, जिसे पबिस रेडिस क्षमताओं के साथ हल करता है (संक्षेप में, ये चैनल या संदेश कतार हैं जिसमें प्रकाशक के पुश संदेश और ग्राहक उन्हें प्राप्त करते हैं)।

बहाना


जैसे-जैसे नाटक आगे बढ़ता है, चौकस पाठक असंगतियों को नोटिस कर सकते हैं, जैसे कि django फ़ंक्शन का उपयोग, जो, संक्षेप में, तुल्यकालिक हैं, लेकिन यह तेजी से विकास के लिए एक छोटा बलिदान है। इसके अलावा, शुरू में हाइलाइड की कोई बात नहीं है, और यह एक व्यापक समाधान नहीं है, लेकिन एक प्रारंभिक बिंदु है। इसलिए मैं आपको अपने कार्यान्वयन की बारीकियों और आपके कोड की बाधाओं के साथ मज़े करना छोड़ दूंगा, जिसके लिए मैं आपसे उदारतापूर्वक मुझे माफ करने के लिए कहता हूं ...

दस्तावेज़ में किमी के बहाने भी देखें, जिसका उल्लेख मैं हर समय करता हूँ।

अभ्यास


अभ्यास व्यावहारिक होगा, क्योंकि स्रोत में टिप्पणियों में कई स्पष्टीकरण हैं।

service.py

वास्तव में सेवा ही, जो ब्राउज़र से कनेक्शन का समर्थन करेगी, django से कमांड प्राप्त करेगी, उन्हें ग्राहकों को भेज देगी (और इसी तरह विपरीत दिशा में)।

कार्यान्वयन के लिए on_message विधि आवश्यक है, लेकिन उपरोक्त उदाहरण में इसकी आवश्यकता नहीं है, क्योंकि सब कुछ एक newfangled इवेंट मॉडल (सॉकेट के लिए) पर लागू किया गया है।

सॉकेट के लिए कार्यान्वयन

 # -*- coding: utf-8 -*- import tornado import tornadoredis from tornadio2 import SocketConnection from tornadio2.conn import event import django from django.utils.importlib import import_module from django.conf import settings from django.utils import simplejson # start of kmike's sources _engine = import_module(settings.SESSION_ENGINE) def get_session(session_key): return _engine.SessionStore(session_key) def get_user(session): class Dummy(object): pass django_request = Dummy() django_request.session = session return django.contrib.auth.get_user(django_request) # end of kmike's sources #     redis     django ORDERS_REDIS_HOST = getattr(settings, 'ORDERS_REDIS_HOST', 'localhost') ORDERS_REDIS_PORT = getattr(settings, 'ORDERS_REDIS_PORT', 6379) ORDERS_REDIS_PASSWORD = getattr(settings, 'ORDERS_REDIS_PASSWORD', None) ORDERS_REDIS_DB = getattr(settings, 'ORDERS_REDIS_DB', None) #   unjson = simplejson.loads json = simplejson.dumps class Connection(SocketConnection): def __init__(self, *args, **kwargs): super(Connection, self).__init__(*args, **kwargs) self.listen_redis() @tornado.gen.engine def listen_redis(self): """     . """ self.redis_client = tornadoredis.Client( host=ORDERS_REDIS_HOST, port=ORDERS_REDIS_PORT, password=ORDERS_REDIS_PASSWORD, selected_db=ORDERS_REDIS_DB ) self.redis_client.connect() yield tornado.gen.Task(self.redis_client.subscribe, [ 'order_lock', 'order_done' ]) self.redis_client.listen(self.on_redis_queue) #    #  self.on_redis_queue def on_open(self, info): """   django. """ self.django_session = get_session(info.get_cookie('sessionid').value) @event # ,    def login(self): """      """ #      ,      on_open self.user = get_user(self.django_session) self.is_client = self.user.has_perm('order.lock') self.is_moder = self.user.has_perm('order.delete') def on_message(self): """  . """ pass def on_redis_queue(self, message): """     """ if message.kind == 'message': #      , #  ,     message_body = unjson(message.body) #  ,   #      JSON #        if message.channel == 'order_lock': self.on_lock(message_body) if message.channel == 'order_done: self.on_done(message_body) def on_lock(self, message): """   """ if message['user'] != self.user.pk: # -       self.emit('lock', message) def on_done(self, message): """   """ if message['user'] != self.user.pk: if self.is_client: message['action'] = 'hide' else: message['action'] = 'highlight' self.emit('done', message) def on_close(self): """       """ self.redis_client.unsubscribe([ 'order_lock', 'order_done' ]) self.redis_client.disconnect() 

सोकज के लिए कार्यान्वयन

 # -*- coding: utf-8 -*- import tornado import tornadoredis from sockjs.tornado import SockJSConnection import django from django.utils.importlib import import_module from django.conf import settings from django.utils import simplejson # start of kmike's sources _engine = import_module(settings.SESSION_ENGINE) def get_session(session_key): return _engine.SessionStore(session_key) def get_user(session): class Dummy(object): pass django_request = Dummy() django_request.session = session return django.contrib.auth.get_user(django_request) # end of kmike's sources #     redis     django ORDERS_REDIS_HOST = getattr(settings, 'ORDERS_REDIS_HOST', 'localhost') ORDERS_REDIS_PORT = getattr(settings, 'ORDERS_REDIS_PORT', 6379) ORDERS_REDIS_PASSWORD = getattr(settings, 'ORDERS_REDIS_PASSWORD', None) ORDERS_REDIS_DB = getattr(settings, 'ORDERS_REDIS_DB', None) #   unjson = simplejson.loads json = simplejson.dumps class Connection(SocketConnection): def __init__(self, *args, **kwargs): super(Connection, self).__init__(*args, **kwargs) self.listen_redis() @tornado.gen.engine def listen_redis(self): """     . """ self.redis_client = tornadoredis.Client( host=ORDERS_REDIS_HOST, port=ORDERS_REDIS_PORT, password=ORDERS_REDIS_PASSWORD, selected_db=ORDERS_REDIS_DB ) self.redis_client.connect() yield tornado.gen.Task(self.redis_client.subscribe, [ 'order_lock', 'order_done' ]) self.redis_client.listen(self.on_redis_queue) #    #  self.on_redis_queue def send(self, msg_type, message): """  . """ return super(Connection, self).send({ 'type': msg_type, 'data': message, }) def on_open(self, info): """   django. """ self.django_session = get_session(info.get_cookie('sessionid').value) self.user = get_user(self.django_session) self.is_client = self.user.has_perm('order.lock') self.is_moder = self.user.has_perm('order.delete') def on_message(self): """  . """ pass def on_redis_queue(self, message): """     """ if message.kind == 'message': #      , #  ,     message_body = unjson(message.body) #  ,   #      JSON #        if message.channel == 'order_lock': self.on_lock(message_body) if message.channel == 'order_done: self.on_done(message_body) def on_lock(self, message): """   """ if message['user'] != self.user.pk: # -       self.send('lock', message) def on_done(self, message): """   """ if message['user'] != self.user.pk: if self.is_client: message['action'] = 'hide' else: message['action'] = 'highlight' self.send('done', message) def on_close(self): """       """ self.redis_client.unsubscribe([ 'order_lock', 'order_done' ]) self.redis_client.disconnect() 

models.py

परिवर्तन का स्रोत। इसे एक मॉडल होने दें।

 # -*- coding: utf-8 -*- import redis from django.conf import settings from django.db import models ORDERS_FREE_LOCK_TIME = getattr(settings, 'ORDERS_FREE_LOCK_TIME', 0) ORDERS_REDIS_HOST = getattr(settings, 'ORDERS_REDIS_HOST', 'localhost') ORDERS_REDIS_PORT = getattr(settings, 'ORDERS_REDIS_PORT', 6379) ORDERS_REDIS_PASSWORD = getattr(settings, 'ORDERS_REDIS_PASSWORD', None) ORDERS_REDIS_DB = getattr(settings, 'ORDERS_REDIS_DB', 0) #   service_queue = redis.StrictRedis( host=ORDERS_REDIS_HOST, port=ORDERS_REDIS_PORT, db=ORDERS_REDIS_DB, password=ORDERS_REDIS_PASSWORD ).publish json = simplejson.dumps class Order(models.Model) … def lock(self): """   """ … service_queue('order_lock', json({ 'user': self.client.pk, 'order': self.pk, })) def done(self): """   """ … service_queue('order_done', json({ 'user': self.client.pk, 'order': self.pk, })) 

दरअसल, यहां lock और done तरीके, कुछ प्रकार के व्यापारिक तर्क निष्पादित करने के बाद, आवश्यक जानकारी के साथ संदेश भेजते हैं। यह जानकारी उपरोक्त सेवा द्वारा प्राप्त की जाएगी, जो क्लाइंट ब्राउज़र को संसाधित और भेजी जाएगी।

यानी उपयोगकर्ता द्वारा मानक योजना के अनुसार कार्रवाई की गई थी: उसने लिंक पर क्लिक किया / बटन दबाया, django ने आवश्यक कार्रवाई की, वेबसैट के माध्यम से वितरण के लिए चैनल को एक अधिसूचना भेजी और उपयोगकर्ता को क्लासिक प्रतिक्रिया लौटा दी।

client.js

Html में socket.io.js या sockjs.js लोड करना ना भूलें जो आपकी पसंद (लेख की शुरुआत में लिंक) पर निर्भर करता है!

वास्तव में इस सारी कार्रवाई के लिए - क्लाइंट की तरफ से काम करना।

सॉकेट के लिए कार्यान्वयन

  var socket = io.connect('http://' + window.location.host + ':8989'); //      //     login,       socket.on('connect', function(){ socket.emit('login'); }); //   -    socket.on('disconnect', function() { setTimeout(socket.socket.reconnect, 5000); }); //    "lock"  "ws_order_lock"       socket.on('lock', function(msg){ ws_order_lock(msg); }); socket.on('done', function(msg){ ws_order_done(msg); }); function ws_order_lock(msg){ if (msg.action == 'highlight'){ $('.id_order_row__' + msg.order).addClass('order-row_is_locked'); }else{ $('.id_info_renew_orders').addClass('hidden'); } } … 

सोकज के लिए कार्यान्वयन

 socket_connect(); function socket_connect() { socket = new SockJS('http://' + window.location.host + ':8989/orders'); //      //     login,       socket.onmessage = function(msg){ window['ws_order_' + msg.data.type](msg.data.data); // ,      } socket.onclose = function(e){ setTimeout(socket_connect, 5000); }; } function ws_order_lock(msg){ if (msg.action == 'highlight'){ $('.id_order_row__' + msg.order).addClass('order-row_is_locked'); }else{ $('.id_info_renew_orders').addClass('hidden'); } } … 

async_server.py

यह एक प्रबंधन कमांड है, फ़ाइल को फ़ोल्डर में डाला जाना चाहिए myProject/orderApp/management/commands रूप में अच्छी तरह से मत भूलना, प्रत्येक सबफ़ोल्डर में फ़ाइल __init__.py

सॉकेट के लिए कार्यान्वयन

 # -*- coding: utf-8 -*- import tornado import tornadio2 as tornadio from django.core.management.base import NoArgsCommand from myProject.order.tornado.service import Connection class Command(NoArgsCommand): def handle_noargs(self, **options): router = tornadio.TornadioRouter(Connection) app = tornado.web.Application(router.urls, socket_io_port=8989) #      tornadio.SocketServer(app) 

सोकज के लिए कार्यान्वयन

 # -*- coding: utf-8 -*- import tornado import tornadio2 as tornadio from django.core.management.base import NoArgsCommand from myProject.order.tornado.service import Connection class Command(NoArgsCommand): def handle_noargs(self, **options): router = SockJSRouter(Connection, '/orders') # sockjs      :( app = tornado.web.Application(router.urls) app.listen(8989) tornado.ioloop.IOLoop.instance().start() 


अब आप python manage.py async_server शुरू कर सकते हैं।

Source: https://habr.com/ru/post/In128562/


All Articles