2016-03-30 9 views
0

Ich möchte eine Anfrage von meinem Node-Server an Twitter, analysieren Sie die Antwortdaten, und senden Sie es über eine websocket (Ws, nicht WSS) Verbindung. Alles funktioniert - außer, dass, nach 30 Sekunden (I timed es, es ist immer 30 +/- 1 Sekunden), die Socket-Verbindung auflegt, und ich erhalte die folgende Fehlerstack:Socket auflegen: Websocket GET HTTPS Anfrage an Twitter API

Error: socket hang up 
    at createHangUpError (_http_client.js:200:15) 
    at TLSSocket.socketOnEnd (_http_client.js:292:23) 
    at emitNone (events.js:72:20) 
    at TLSSocket.emit (events.js:166:7) 
    at endReadableNT (_stream_readable.js:905:12) 
    at nextTickCallbackWith2Args (node.js:442:9) 
    at process._tickCallback (node.js:356:17) 

Ich habe benutze das gleiche Serverdesign mit dem öffentlichen Stream von Twitter und es hat gut funktioniert. Nur wenn die Anforderung GET implementiert wird, treten die Hangups auf.

Bisher hat ich folgende Lösungen versucht:

  • Drosselung Anfrage eingehende Rate waaaay nach unten mit einem niedrigen Zählwert in der Twitter-Abfrage uri-codierte Zeichenfolge; Timeout tritt sogar mit 1 tweet mich zurückgeschickt, nachdem noch ~ 30 Sekunden
  • gesetzt ein keepAliveAgent meine GET-Anfrage Optionen
  • versucht, ein manuell gebaut (keine npm Modul) Mittel für die Anforderung
  • KeepAliveInterval gesetzt, keepaliveGracePeriod , und dropConnectionOnKeepaliveTimeout auf dem Socket-Server
  • und eine Reihe von Knoten newbish Dinge

alle ohne Erfolg. Die App funktioniert 30 Sekunden lang wunderbar. Dann hängt es sich auf.

Meine nächste Führung: Twitter erfordert Application-only authentifizierte Anfragen über HTTPS gesendet werden. Ich habe in meinem Code für die verschiedenen Sicherheitsstufen keine Bestimmungen getroffen. Ich werde das jetzt weiter verfolgen - und schauen, ob die SO-Community irgendwelche Gedanken hat. Jede Hilfe, die Sie anbieten können, wird sehr geschätzt!

Hier ist der Code, auf das Wesentliche abgespeckte:

// Initialize basic server for the web socket requests 
 
var handShakeServer = http.createServer(function(request, response) { 
 
    console.log((new Date()) + ' Received request for ' + request.url); 
 
    response.writeHead(404); 
 
    response.end(); 
 
}); 
 

 
handShakeServer.listen(8080, function() { 
 
    console.log((new Date()) + ' Socket server is listening on port 8080'); 
 
}); 
 

 
// Initialize the socket server itself. 
 
var socketServer = new WebSocketServer({ 
 
    httpServer: handShakeServer, 
 
    autoAcceptConnections: false, 
 
    keepAliveInterval: (3600 * 1000), 
 
    dropConnectionOnKeepaliveTimeout: false 
 
}); 
 

 
// On request, listen for messages. 
 
socketServer.on('request', function(request) { 
 
    
 
    // Initialize connection from verified origin 
 
    var connection = request.accept('echo-protocol', request.origin); 
 
    
 
    // On message (search params from client), query Twitter. 
 
    connection.on('message', function(message) { 
 
    
 
    // BEARER_ACCESS_TOKEN is for Twitter's application-only authentication 
 
    var options = { 
 
     'path': '/1.1/search/tweets.json?q=stuff', 
 
     'hostname': 'api.twitter.com', 
 
     'method': 'GET', 
 
     'headers': { 
 
     'Authorization': ('Bearer ' + process.env.BEARER_ACCESS_TOKEN), 
 
     'Accept': '*/*' 
 
     }, 
 
     'agent': agent, 
 
     'port': 443, 
 
    }; 
 
    
 
    // Query twitter via HTTPS GET, listen for response 
 
    var req = new https.request(options, function(res) { 
 
     var responseString = ''; 
 

 
     // On data, concatenate the chunks into a whole JSON object 
 
     res.on('data', function(tweet) { 
 
     responseString += tweet; 
 
     }); 
 
     
 
     // On completion of request, send data to be analyzed. 
 
     res.on('end', function() { 
 

 
     // Once returned, send data to client through socket connection. 
 
     var result = doSomeAnalysis(JSON.parse(responseString)); 
 
     connection.sendUTF(JSON.stringify(result)); 
 

 
     }); 
 
    }); 
 

 
    // The https request is done; terminate it. 
 
    req.end(); 
 
    }); 
 
});

Auch auf der Web-Socket-Client-Seite, die ich habe:

client.connect('ws://localhost:8080/', 'echo-protocol', 'twitterQuery'); 

Und hier sind die Module von Relevanz in meinem server.js:

var util    = require('util'); 
var https   = require('https'); 
var http    = require('http'); 
var WebSocketServer = require('websocket').server; 
var HttpsAgent  = require('agentkeepalive').HttpsAgent; 

Antwort

0

Ok! Stellt sich heraus, dass die HTTPS-Kompatibilität nicht die Aufhängungen verursacht. Als ich die Websocket-Verbindungslogik von der Twitter-Anforderungslogik vollständig entkoppelt habe und die console.log-Dummy-Funktionen überlappen ließ, funktionierte die Twitter-Anfrage gut, aber die Websocket-Verbindung warf mir den Hangup-Fehler genau nach wie vor - immer nach 30 Sekunden und immer unabhängig davon, wie viele Daten über die Verbindung gesendet wurden.

Das Update: Manuelle Einstellung in Ping/Pong-Maßnahmen, in einem der beiden (oder beide, für zusätzliche Redundanz) Möglichkeiten:

//SERVER SIDE: 

    // Option 1: Use the npm module's event 'ping' 
    connection.on('ping', function(data){ 
     console.log('Server: ping received at: ' + (new Date()))   
    }) 

    // Option 2: Use the more generalized event 'message', look for your 
    // custom 'ping' message 
    connection.on('message', function(message){ 
     console.log("Socket server received message from client.") 
     if (message.utf8Data == 'ping'){ 
      console.log('pong!') 
     } 
    }) 

//CLIENT SIDE: 
    setInterval(function(){    
     if (connection.connected){ 
      // To match Option 1 above, use this: 
      connection.ping(); 
      // To match Option 2 above, use this: 
      connection.sendUTF('ping'); 
     } 
    }, 19900) 

    connection.on('pong', function(){ 
     console.log('Client: pong received at: ' + (new Date()) + '\n') 
    }) 

Also, am Ende eine sehr einfache Lösung.Ich verstehe nicht, warum die Verbindung diese Keep-Alive-Pings benötigt, da die Twitter-Antwort unmittelbar nach dem Verlassen meiner Analysefunktion in ~ 1-Sekunden-Intervallen über die Verbindung geleitet wird, wobei die Verbindung extrem aktiv bleibt (aber nicht überlastet wird)). Siehe meinen Original-Beitrag für wo dies geschieht:

// Once returned, send data to client through socket connection. 
var result = doSomeAnalysis(JSON.parse(responseString)); 
connection.sendUTF(JSON.stringify(result)); 

Wahrscheinlich ist es, dass es keine ‚Pong‘ ist wieder auf den Server, so dass, obwohl die WebSocket-Verbindung hält seine Daten herausschleuder gesendet, es hat keine Ahnung wenn das empfangende Ende davon noch aktiv ist. Aber - warum brauche ich nicht die gleiche Maßnahme für meine Streaming-Aktivität? Es funktioniert über den gleichen Kanal, empfängt keine Daten zurück vom Client (kein 'Pong'), sobald das Streaming beginnt, und wird für immer fortgesetzt (bis Google Geocoding mich zum Schweigen bringt, das heißt :)).

Ich denke, dass, durch Rückschluss, muss es einige Keep-Alive-Ponging standardmäßig in einem Stream vorhanden sein, und dass ich es manuell zur GET-Anfrage hinzufügen muss, da ich im Wesentlichen die Batch-Antwort zu streamen out , kleiner Brocken nach kleinem Brocken, da die Renditen meine Analysefunktion verlassen.

Kühl. Wie auch immer, ich dachte, ich würde diesen (zu langen) Post für den Fall lassen, dass irgendjemand auflegt (Wortspiel!), Wenn er eine GET-Anfrage mit Big-Data-Chunks verwendet und diese dann über die Web-Socket-Verbindung imitiert. Die Kombination funktioniert sehr gut, dieses Problem wurde behoben.

+0

Nur ein Kopf hoch: Wenn Sie 'npm websocket' verwenden und auf das gleiche Problem stoßen, lesen Sie diese Dokumentation [hier] (https://github.com/theturtle32/WebSocket-Node/blob/master /docs/WebSocketServer.md#server-config-options) - obwohl die Standardeinstellungen nicht als gewährt gelten. Setzen Sie 'keepalive: true' manuell, mit einem kurzen Intervall, um dasselbe zu erreichen, was ich oben getan habe. –