Websocketを介したプロキシデータサーバーとしてのNode.js

ほぼすべてのデータサーバーとブラウザ間で安定した非同期データストリームを作成するために喫煙をやめる簡単な方法に関する別のバイク。



前文 :私が同行するプロジェクトの1つは、包括的なGPS車両監視システムです。 これには、カートラッカーからのデータを処理および保存するためのサーバーと、ギガバイト程度の総量のタイルに打たれた粗いラスターマップ上にリアルタイムで車の動きを描くデスクトップクライアントが含まれています。 プロジェクト管理から、デスクトップからだけでなく、どこからでも、どのデバイスからでも視覚データにすばやくアクセスできるように、Google Yandexやその他のミニターゲットベクトルマップに基づいてWebクライアントを作成するよう指示されました。



タスクは可能限り迅速かつ最小限に完了ました。GPSデータ処理サーバーに接続し、リクエストを送信し、応答を待機し、Webクライアントに応答を返す単純なphpスクリプトが作成されました。 したがって、タイマーに従って体系的に、古き良き$ .ajax()を介してPHPスクリプトにPOSTリクエストを送信し、上記の素晴らしいベクターオンラインマップに回答を美しく描いた単純なクライアントが作成されました。



すべての利点について、この方法には明らかな欠点がありました-同期リクエストは、応答を待つために多くの時間を必要とすることがありました。接続されたトラッカーの状態を変更するために同じサーバーの非同期応答がデータサーバーソケットから上昇したため、それらはフィルタリングされ、元のリクエストに対する応答を待つ必要がありました。 そしてもちろん、クライアント側のブラウザーはキャッシュを犯罪的に使用し始め、最新のデータを無視することがありました。 繰り返しますが、接続されたクライアントの数の増加に伴い、ApacheはVPSのメモリを積極的に消費し始めました。 20秒のサーバーのポーリング間隔は、実際にサーバーに負担をかけず、同時にWebクライアントの対話性を維持することが実験的に計算されました。



このようなスキームは管理に非常に適していました。製品は正常に起動し、ユーザーはWebクライアントの使用に切り替えました。 しかし、実装されたソリューションは、美しく調和のとれたすべての愛好家として私には向いていませんでした。



Java Webクライアントにデータを転送するためのプロキシサーバーを作成するさらなる試みは、知識が不足しており、Tomcatまたは同様のものをサーバーにデプロイする必要があるため、最終的に埋もれましたが、パフォーマンスは大幅に向上しませんでした。



そしてここでNode.jsとSockJSライブラリーが助けになりました。非同期Webソケット接続のエミュレーションの成功を実装し、その時点ですでに説明したsocket.ioよりもいくらか優れています。



これについて書いている理由をすぐに説明します。以下に説明するソリューションの実装により、 サーバーの負荷が約30倍減少し、すべての最新ブラウザーで動作します(もちろんIE6-7を意味するわけではありませんが、既にIE8で動作します)データ転送。 ソリューションは非常に普遍的であり、同じ方法でほぼすべての非同期データストリーム(Webサイト解析、チャットサーバー、オンライングッズ、ローバー制御システムなど)の処理を整理でき、プログラミングに関する深い知識は必要ありません。エンコーダ。



そのため、データサーバーからの情報を処理し、Webソケット接続を介してWebクライアントに非同期的に転送するサーバーは次のようになります。



Node.js
var http = require('http'), net = require('net'), sockjs = require('sockjs'), ADDR_GPS = "127.0.0.1", //   ,  - PORT_GPS = 3201, //   server = sockjs.createServer(); server.on('connection', function(conn) { //         - SockJS   //   ,      , //     ,     var com = new Commander(ADDR_GPS, PORT_GPS, conn, function(e){ console.log("! We had an Error in socket: ", e, "at ", new Date()); conn.close(); }); conn.on('data', function(data) { //         JSON //        var dat = JSON.parse(data); if(dat.command == "@auth") { //    , com.auth(dat.param.log, dat.param.pwd); } else if(dat.command == "@bye") { // -      com.bye(); conn.close(); } }); conn.on('close', function() { console.log("Websocket connection closed"); com = null; }); }); //  -HTTP-        // SockJS,    http://mydomen.com:8081/data var srv = http.createServer(); server.installHandlers(srv, {prefix:'/data'}); srv.listen(8081, '0.0.0.0'); var Commander = function (adr, port, clientConn, onError) { //        var self = this; this.status = 0; this.chunk = ""; //     this.answers = []; //      this.connection = clientConn; //    , //        this.client = new net.Socket(); //      this.client.connect(port,adr,function(){ //     console.log("New connect to created..."); }); this.client.on('data', function(data) { //         - self.onData(data); }); this.client.on('error', function(e) { //      onError(e); self.client.destroy(); }); }; Commander.prototype.auth = function(login, pass) { //            console.log("written auth for "+ login); this.client.write('(auth "'+login+'" "'+pass+'")\n'); }; Commander.prototype.bye = function() { //     this.client.write('(exit)\n'); }; Commander.prototype.onData = function(data) { //       ,      , //    -,      //    ,       //   -        var pos; this.chunk+=data.toString(); pos=this.chunk.indexOf('\n'); if(pos > -1) { this.connection.write(this.chunk.substring(0,pos)); this.chunk = this.chunk.substring(pos); } };
      
      









クライアント、SockJSハンドラーモジュール接続が必要です。 シートは申し訳ありませんが、理解のために、たとえばスタイルやスクリプトをファイルに投稿してください。

index.html
 <!DOCTYPE html> <html lang="ru"> <head> <meta charset="utf-8"> <title>   </title> <style> body { padding:0; margin: 0; font: 10pt sans-serif, Arial, Tahoma; } h1 { font-size: 2em; margin: 0.8em 0; } h3 { font-size:1.5em; margin: 0.1em; } #content { position: relative; margin: 0 auto; width:960px; min-width:800px; } #left { position:absolute; top:0px; left:0px; padding:2px; width:220px; height:560px; } #right { position:absolute; top:0px; left:250px; padding:2px; width:710px; height:560px; } #scroller { position:relative; width: 400px; height:90%; overflow-y:auto; border:1px dotted black; padding:5px; margin-top:10px; } .off { color:red; } .on { color: green; } .inBottom { position: absolute; bottom: 20px; } </style> <script src="sockjs-0.3.4.min.js" type="text/javascript"></script> <script> var sock;stat = document.getElementById("status"); function connect() { //      sock = new SockJS('http://mysite.com:8081/data'); var l = document.getElementById("login").value, p = document.getElementById("passw").value stat = document.getElementById("status"); setTimeout(function(){ //    ,       sock.send(toJSON("@auth", { log: l, pwd: p })); },2000); sock.onopen = function() { //   ,     stat.innerHTML = "ON"; stat.className = "on"; }; sock.onmessage = function(e) { //   ,     ,   //       "data" document.getElementById("scroller").innerHTML += "<p>"+e.data+"</p>"; }; sock.onclose = function() { //    ,     stat.innerHTML = "OFF"; stat.className = "off"; }; } function disconnect() { //   if(sock !== undefined) { sock.send(toJSON("@bye", {})); } } function toJSON (com, param){ return JSON.stringify({ command: com, param: param }); } </script> </head> <body> <div id="content"> <div id="left"> <p style="width:100%;"> <input type="text" id="login" style="float:right;"></p> <p style="width:100%;"> <input type="password" id="passw" style="float:right;"></p> <button onclick="connect();"></button><button onclick="disconnect();"></button><br> <p class="inBottom">: <span id="status"></span></p> </div> <div id="right"> <div id="scroller"></div> </div> </div> </body> </html>
      
      









すべての重要なポイントに関するコメントをコードに提供しようとしました。 ライブのオンラインデモを提供することはできません。車両にあまり注意を払って満足していない実際の組織のデータがサーバー上で回転しています。 収穫機がどのように機能するかをオンラインで見るのは面白いですが、半日で畑の一部が地図上に陰影を付けられ、収穫機のキャプテンが昼食を取りたいときに付録が最も近い村の方向にはっきりと見えます。



コメント、質問、提案をお待ちしています。



All Articles