SockJS、Django、Tornado、ZeroMQを使用したリアルタイム同期システム開発

少し前まで、別のソフトウェア製品の開発中に、開発チームはサーバーに変更を送信(PUSHメソッド)することで、ユーザーデータをリアルタイムで同期する完全なシステムを実現するという課題に直面していました。 アプリケーション自体では、データの量は多くありませんでしたが、複数のユーザーが同時に表示できました。 そのため、Webアプリケーションのフレームワーク内でデータを同期するための軽量でかなり生産的なアプローチが必要でした。 この問題を解決するさまざまな方法を検討した後、かなり人気のあるWebSocketエミュレーターであるSockJSを選択しました。SockJSは、クライアントが使用するブラウザーに応じて、クライアントとサーバー間のデータ交換にさまざまなアルゴリズムを使用します。 この記事の枠組みでは、そのような選択が行われた理由に焦点を当てません(habrahabrを含む多くの記事がこの主題について書かれています)が、私たちはそれをまだ後悔していないと単純に言います。



最初に、そのようなタスクの実装に対する標準的なアプローチを研究するとき、1つの問題に遭遇しました。 この問題は、システムとのやり取りがWebインターフェースだけでなく、制御できないサードパーティ製品によるAPIの使用によっても実行されることでした。 そしてもちろん、当社製品のエンドユーザーは、それに関連するデータの変更に関するすべての情報を参照することを期待しています。 sockjsサーバーを使用するための標準的なアプローチでは、システム内のデータの変更通知は、これらの変更に関する情報を受信するために使用される同じJSクライアントを使用して送信されます。 それが、私たちの場合、そのようなアプローチが適用できない理由です。



この記事では、この問題をどのように解決したかについてお話したいと思います。



したがって、ソースデータ:





一般的なタスクは、2つのサブタスクに分割できます。





利用可能な情報を検討した結果、「仲介者」なしではこの問題を解決できないことが明らかになりました。 仲介者として、メッセージングシステム、つまりZeroMQが選択されました。 この特定のシステムの主な利点の中で、そのシンプルさ、軽さ、およびPython言語のサポート(これは私たちにとって非常に重要な条件です)に注目する価値があります。



一般に、結果のソリューションは、以下に示すスキームの形式で表すことができます。







エンドユーザーがWebインターフェースまたはAPIを使用して行ったシステム内のデータの変更は、最終的にDjangoサーバーに到達し、そこでZeroMQを介してSockJSサーバーに送信され、表示されたオブジェクトのデータが変更されたという事実をユーザーに通知します。



コードに行きましょう



Webクライアント


通知を受信するには、sockjs-clientライブラリーを接続し、サーバーへの接続を確立し、必要なイベントをサブスクライブする必要があります。 これはおよそ次のように実行できます。



<script src="/js/sockjs-0.3.4.min.js"></script> <script> var SyncServer = new SockJS("http://example.com/echo"); SyncServer.onmessage = function(e) { //    }; </script>
      
      





ZeroMQサーバー


この部分を実装するには、ZeroMQ自体とそれを操作するPythonライブラリpyzmqが必要です。 その後、サーバーをプロキシモードで構成および起動する必要があります(このモードはFORWARDERと呼ばれます)。 これは、ほんの数行のコードで実行できます。



 import zmq def main(): try: context = zmq.Context(1) # Socket facing clients frontend = context.socket(zmq.SUB) frontend.bind("tcp://127.0.0.1:XXXX") frontend.setsockopt(zmq.SUBSCRIBE, "") # Socket facing services backend = context.socket(zmq.PUB) backend.bind("tcp://127.0.0.1:YYYY") zmq.device(zmq.FORWARDER, frontend, backend) except Exception, e: # Handle exception pass finally: frontend.close() backend.close() context.term() if __name__ == '__main__': main()
      
      





同様のスクリプトを実行すると、ポートXXXXでメッセージを待機し、ポートYYYYに送信するプロキシサーバーを取得できます。



SockJSサーバー


SockJSサーバーとして、標準のSockJS-tornadoが選択され、ZeroMQサーバーからメッセージを受信できるように若干の変更が加えられています(これらの変更は、SockJSMyRouterクラスの__init__メソッドで見つけることができます)



 from sockjs.tornado import SockJSConnection, SockJSRouter from tornado import ioloop as tornado_ioloop, web import zmq from zmq.eventloop import ioloop from zmq.eventloop.zmqstream import ZMQStream ioloop.install() io_loop = tornado_ioloop.IOLoop.instance() class SyncConnection(SockJSConnection): _connected = set() stream = None def __init__(self, session): super(SyncConnection, self).__init__(session) self.stream.on_recv(self.on_server_message) def on_open(self, request): self._connected.add(self) def on_message(self, data): pass def on_server_message(self, data): message = "your message" self.broadcast(self._connected, {'message': message}) def on_close(self): self._connected.remove(self) class SockJSMyRouter(SockJSRouter): def __init__(self, *args, **kw): super(SockJSMyRouter, self).__init__(*args, **kw) socket = context.socket(zmq.SUB) socket.setsockopt(zmq.SUBSCRIBE, "") socket.connect("tcp://127.0.0.1:YYYY") self._connection.stream = ZMQStream(socket, self.io_loop) if __name__ == '__main__': context = zmq.Context() EchoRouter = SockJSMyRouter(SyncConnection, '/echo') app = web.Application(EchoRouter.urls) app.listen("ZZZZ") io_loop.start()
      
      





Djangoサーバー


Djangoサーバーの一部として、通知の作成とZeroMQサーバーへの送信を担当する数行のみを追加する必要があります。 これは次のように実行できます。



 import zmq context = zmq.Context() socket = context.socket(zmq.PUB) socket.connect("XXX") socket.send("your message") socket.close()
      
      





それだけです! Django、Sockjsサーバーおよびクライアントでメッセージの作成と処理を行うメソッドを追加することにより、アプリケーション用の本格的なリアルタイムの同期システムを取得できます。



更新:

timetogoのリクエストに応じて、結果のタスクリスト管理システムへのリンク投稿します: Cloud Checklist

インターネットブラウザの2つの別々のセッションで同じタスクシートを開くと、システムの動作を観察できます。 もちろん、このシナリオでは、2台のモニターを持つことは大きなプラスです。



All Articles