WebSocket API v1 - απροσδόκητες αποσυνδέσεις από ροές δεδομένων αγοράς

Τελευταία ενημέρωση: 1 Απριλίου 2025

Η WebSocket API v1 μας προορίζεται να παρέχει μια συνεχή πηγή δεδομένων αγοράς (tickers, βιβλία εντολών, χρόνος και πωλήσεις κ.λπ.), αλλά μερικές φορές οι συνδέσεις WebSocket τερματίζονται απροσδόκητα από την Cloudflare.

Οι αποσυνδέσεις που ξεκινούν από την Cloudflare συνήθως οδηγούν σε σφάλματα δικτύου, όπως το ακόλουθο σφάλμα 1006:

  • Σφάλμα ανάγνωσης από websocket: κλείσιμο 1006 (μη φυσιολογικό κλείσιμο): απροσδόκητο EOF

ή το ακόλουθο όπου η Python εμφανίζει μια εξαίρεση όταν προσπαθεί να διαβάσει ένα νέο μήνυμα δεδομένων αγοράς από μια ήδη κλειστή σύνδεση:

  • websocket._exceptions.WebSocketConnectionClosedException: η υποδοχή είναι ήδη κλειστή

Ενώ δεν υπάρχει λύση που να αποτρέπει αυτού του είδους τις απροσδόκητες αποσυνδέσεις, υπάρχουν ορισμένες πολύ αποτελεσματικές λύσεις. Σε περίπτωση που αντιμετωπίζετε συχνές αποσυνδέσεις WebSocket, συνιστούμε να εφαρμόσετε μία ή περισσότερες από τις ακόλουθες:

Εναλλακτική διεύθυνση IP

Οι προφυλάξεις ασφαλείας της Cloudflare εφαρμόζονται συχνά σε συγκεκριμένες διευθύνσεις IP, ομάδες παρόμοιων διευθύνσεων IP (όπως ένα μπλοκ κλάσης C) ή συγκεκριμένες γεωγραφικές τοποθεσίες, επομένως η αλλαγή της διεύθυνσης IP σας είναι μερικές φορές μια απλή αλλά αποτελεσματική λύση.

Για παράδειγμα, η φιλοξενία του λογισμικού API σας σε διαφορετική περιοχή AWS ή η χρήση VPN σε διαφορετική γεωγραφική τοποθεσία, θα άλλαζε σημαντικά την τοπική διεύθυνση IP και ενδεχομένως θα έλυνε το πρόβλημα της απροσδόκητης αποσύνδεσης.

Λογική επανασύνδεσης

Η χρήση μιας ενιαίας σύνδεσης WebSocket που παρακολουθείται για τυχόν ακούσιες αποσυνδέσεις και επανασυνδέεται και επανεγγράφεται όπως είναι απαραίτητο, είναι μια άλλη απλή αλλά αποτελεσματική λύση. Ακολουθεί βασικός κώδικας Python που δείχνει πώς να επανασυνδέσετε και να επανεγγράψετε μια σύνδεση WebSocket κάθε φορά που εμφανίζεται μια εξαίρεση WebSocketConnectionClosed:

#!/usr/bin/env python3 import sys from websocket import create_connection ws = create_connection('wss://ws.kraken.com/') print(ws.recv()) ws.send('{"event":"subscribe", "subscription":{"name":"ticker"}, "pair":["XBT/USD"]}') print(ws.recv()) for count in range(100): if count == 30 or count == 60: ws.close() try: print(ws.recv()) except: ws = create_connection('wss://ws.kraken.com/') print(ws.recv()) ws.send('{"event":"subscribe", "subscription":{"name":"ticker"}, "pair":["XBT/USD"]}') print(ws.recv()) ws.close() sys.exit(0)

Όπως φαίνεται στην ακόλουθη δείγμα εξόδου, η σύνδεση WebSocket και η εγγραφή ανανεώνονται κάθε φορά που η σύνδεση τερματίζεται απροσδόκητα (στις 30 και 60 επαναλήψεις του βρόχου):

{"connectionID":8986307948074364287,"event":"systemStatus","status":"online","version":"1.0.1"} {"channelID":274,"channelName":"ticker","event":"subscriptionStatus","pair":"XBT/USD","status":"subscribed","subscription":{"name":"ticker"}} [274,{"a":["9766.00000",16,"16.15314657"],"b":["9765.90000",16,"16.06793017"],"c":["9766.00000","0.15407543"],"v":["934.89117067","6431.35804307"],"p":["9793.82728","9736.03263"],"t":[3442,18738],"l":["9731.30000","9465.00000"],"h":["9845.00000","9884.70000"],"o":["9792.10000","9660.80000"]},"ticker","XBT/USD"] {"event":"heartbeat"} ... [274,{"a":["9766.00000",15,"15.80052495"],"b":["9765.90000",15,"15.09399036"],"c":["9766.00000","0.18958527"],"v":["935.24379229","6430.73154248"],"p":["9793.81679","9736.04572"],"t":[3444,18738],"l":["9731.30000","9465.00000"],"h":["9845.00000","9884.70000"],"o":["9792.10000","9660.70000"]},"ticker","XBT/USD"] [274,{"a":["9766.00000",15,"15.59644495"],"b":["9765.90000",15,"15.09399036"],"c":["9766.00000","0.20408000"],"v":["935.44787229","6430.93562248"],"p":["9793.81072","9736.04667"],"t":[3445,18739],"l":["9731.30000","9465.00000"],"h":["9845.00000","9884.70000"],"o":["9792.10000","9660.70000"]},"ticker","XBT/USD"] {"event":"heartbeat"} [274,{"a":["9766.00000",11,"11.59644495"],"b":["9765.90000",20,"20.24943617"],"c":["9766.00000","4.00000000"],"v":["939.44787229","6434.93562248"],"p":["9793.69231","9736.06529"],"t":[3446,18740],"l":["9731.30000","9465.00000"],"h":["9845.00000","9884.70000"],"o":["9792.10000","9660.70000"]},"ticker","XBT/USD"] {"connectionID":14767302623274336124,"event":"systemStatus","status":"online","version":"1.0.1"} {"channelID":274,"channelName":"ticker","event":"subscriptionStatus","pair":"XBT/USD","status":"subscribed","subscription":{"name":"ticker"}} [274,{"a":["9766.00000",11,"11.59644495"],"b":["9765.90000",20,"20.24943617"],"c":["9766.00000","4.00000000"],"v":["939.44787229","6434.93562248"],"p":["9793.69231","9736.06529"],"t":[3446,18740],"l":["9731.30000","9465.00000"],"h":["9845.00000","9884.70000"],"o":["9792.10000","9660.70000"]},"ticker","XBT/USD"] [274,{"a":["9766.00000",8,"8.49644495"],"b":["9765.90000",21,"21.50083276"],"c":["9766.00000","1.50355505"],"v":["942.54787229","6438.03562248"],"p":["9793.60123","9736.07971"],"t":[3448,18742],"l":["9731.30000","9465.00000"],"h":["9845.00000","9884.70000"],"o":["9792.10000","9660.70000"]},"ticker","XBT/USD"] {"event":"heartbeat"} {"event":"heartbeat"} [274,{"a":["9766.00000",4,"4.19644495"],"b":["9765.90000",22,"22.67116661"],"c":["9766.00000","4.30000000"],"v":["946.84787229","6442.33562248"],"p":["9793.47588","9736.09968"],"t":[3449,18743],"l":["9731.30000","9465.00000"],"h":["9845.00000","9884.70000"],"o":["9792.10000","9660.70000"]},"ticker","XBT/USD"] ... {"event":"heartbeat"} {"connectionID":11355575731659611126,"event":"systemStatus","status":"online","version":"1.0.1"} {"channelID":274,"channelName":"ticker","event":"subscriptionStatus","pair":"XBT/USD","status":"subscribed","subscription":{"name":"ticker"}} [274,{"a":["9766.00000",4,"4.00000071"],"b":["9765.90000",21,"21.97845481"],"c":["9766.00000","0.19644424"],"v":["947.04431653","6442.53206672"],"p":["9793.47018","9736.10059"],"t":[3450,18744],"l":["9731.30000","9465.00000"],"h":["9845.00000","9884.70000"],"o":["9792.10000","9660.70000"]},"ticker","XBT/USD"] [274,{"a":["9766.00000",3,"3.85443405"],"b":["9765.90000",19,"19.69673762"],"c":["9766.00000","0.14556666"],"v":["947.18988319","6442.67763338"],"p":["9793.46596","9736.10127"],"t":[3451,18745],"l":["9731.30000","9465.00000"],"h":["9845.00000","9884.70000"],"o":["9792.10000","9660.70000"]},"ticker","XBT/USD"] {"event":"heartbeat"} {"event":"heartbeat"} [274,{"a":["9766.00000",3,"3.51109438"],"b":["9765.90000",19,"19.69673762"],"c":["9766.00000","0.16647855"],"v":["947.53322286","6443.02097305"],"p":["9793.45601","9736.10286"],"t":[3453,18747],"l":["9731.30000","9465.00000"],"h":["9845.00000","9884.70000"],"o":["9792.10000","9660.70000"]},"ticker","XBT/USD"] ...

Πλεονάζουσες συνδέσεις

Η χρήση πολλαπλών (δύο ή περισσότερων) πλεοναζουσών συνδέσεων WebSocket είναι συνήθως η πιο αποτελεσματική λύση για απροσδόκητες αποσυνδέσεις WebSocket, επειδή επιτρέπει στις ροές δεδομένων αγοράς να συνεχίζονται αδιάκοπα, ανεξάρτητα από το πόσο συχνά τερματίζονται οι υποκείμενες συνδέσεις.

Ο ακόλουθος βασικός κώδικας Python δείχνει πώς να εφαρμόσετε πολλαπλές συνδέσεις WebSocket με αυτόματη εναλλαγή:

#!/usr/bin/env python3 import sys from websocket import create_connection ws = [ None, None ] data = [ "", "" ] ws[0] = create_connection("wss://ws.kraken.com/") print("WebSocket (Primary): %s" % ws[0].recv()) ws[1] = create_connection("wss://ws.kraken.com/") print("WebSocket (Backup): %s" % ws[1].recv()) ws[0].send('{"event":"subscribe", "subscription":{"name":"spread"}, "pair":["XBT/USD"]}') print("WebSocket (Primary): %s" % ws[0].recv()) ws[1].send('{"event":"subscribe", "subscription":{"name":"spread"}, "pair":["XBT/USD"]}') print("WebSocket (Backup): %s" % ws[1].recv()) source = 0 for count in range(100): try: try: data[0] = ws[0].recv() except Exception: source = 1 try: data[1] = ws[1].recv() except Exception: source = 0 except KeyboardInterrupt: ws[0].close() print("WebSocket (%(source)s): %(data)s" % {"source":"Primary" if source == 0 else "Backup", "data":data[source]}) ws[source].close() sys.exit(1)

Ο παραπάνω κώδικας δημιουργεί δύο συνδέσεις WebSocket και εγγράφει και τις δύο συνδέσεις στην ίδια ροή δεδομένων αγοράς. Η κύρια σύνδεση χρησιμοποιείται για την έξοδο των δεδομένων αγοράς, μέχρι να δημιουργηθεί μια διακοπή πληκτρολογίου (μέσω Ctrl+C, για παράδειγμα), η οποία κλείνει την κύρια σύνδεση. Οι επόμενες αναγνώσεις από την κύρια σύνδεση εμφανίζουν μια εξαίρεση, η οποία συλλαμβάνεται και χρησιμοποιείται για την αλλαγή της πηγής δεδομένων αγοράς στην εφεδρική σύνδεση, η οποία στη συνέχεια χρησιμοποιείται για την έξοδο των δεδομένων αγοράς.

Η ακόλουθη δείγμα εξόδου δείχνει πώς η πηγή δεδομένων αγοράς αλλάζει από την κύρια σύνδεση στην εφεδρική σύνδεση χωρίς να διακόπτεται η ροή δεδομένων αγοράς:

WebSocket (Primary): {"connectionID":6894434610526943167,"event":"systemStatus","status":"online","version":"1.0.1"} WebSocket (Backup): {"connectionID":11520162761468018366,"event":"systemStatus","status":"online","version":"1.0.1"} WebSocket (Primary): {"channelID":275,"channelName":"spread","event":"subscriptionStatus","pair":"XBT/USD","status":"subscribed","subscription":{"name":"spread"}} WebSocket (Backup): {"channelID":275,"channelName":"spread","event":"subscriptionStatus","pair":"XBT/USD","status":"subscribed","subscription":{"name":"spread"}} WebSocket (Primary): [275,["9674.90000","9675.00000","1591440118.120752","10.88476562","3.47935600"],"spread","XBT/USD"] WebSocket (Primary): {"event":"heartbeat"} WebSocket (Primary): {"event":"heartbeat"} WebSocket (Primary): [275,["9674.90000","9675.00000","1591440127.108830","10.98812533","3.47935600"],"spread","XBT/USD"] WebSocket (Primary): {"event":"heartbeat"} WebSocket (Primary): [275,["9674.90000","9675.00000","1591440129.406073","1.87412533","7.47935600"],"spread","XBT/USD"] WebSocket (Primary): [275,["9674.90000","9675.00000","1591440129.505372","0.77412533","7.47935600"],"spread","XBT/USD"] WebSocket (Primary): [275,["9674.90000","9675.00000","1591440129.572658","0.10335971","7.47935600"],"spread","XBT/USD"] WebSocket (Primary): [275,["9674.90000","9675.00000","1591440129.572658","0.10335971","7.47935600"],"spread","XBT/USD"] ^C WebSocket (Backup): [275,["9674.90000","9675.00000","1591440130.156840","0.10335971","8.27935600"],"spread","XBT/USD"] WebSocket (Backup): [275,["9674.90000","9675.00000","1591440130.195899","0.10335971","8.47435600"],"spread","XBT/USD"] WebSocket (Backup): [275,["9673.80000","9675.00000","1591440130.459388","0.10335971","8.47435600"],"spread","XBT/USD"] WebSocket (Backup): [275,["9672.60000","9675.00000","1591440130.195899","0.00516924","8.47435600"],"spread","XBT/USD"] WebSocket (Backup): [275,["9673.80000","9675.00000","1591440130.764856","0.00400000","8.47435600"],"spread","XBT/USD"] WebSocket (Backup): [275,["9673.80000","9674.90000","1591440130.780514","0.00400000","0.25000000"],"spread","XBT/USD"] WebSocket (Backup): {"event":"heartbeat"} WebSocket (Backup): [275,["9673.80000","9674.90000","1591440132.433940","4.00400000","0.25000000"],"spread","XBT/USD"] WebSocket (Backup): [275,["9673.80000","9674.90000","1591440132.519509","4.00000000","0.25000000"],"spread","XBT/USD"] WebSocket (Backup): [275,["9673.90000","9674.90000","1591440132.527887","0.10336612","0.25000000"],"spread","XBT/USD"] WebSocket (Backup): [275,["9673.90000","9675.00000","1591440132.527887","0.10336612","4.27935600"],"spread","XBT/USD"]

Σημειώστε ότι οι πλεονάζουσες συνδέσεις WebSocket είναι η πιο σύνθετη από τις τρεις λύσεις, αλλά είναι η βέλτιστη λύση όσον αφορά την αξιοπιστία.

Χρειάζεστε περισσότερη βοήθεια;