WebSocket API v1 - odottamattomat katkaisut markkinadatasyötteissä

Viimeksi päivitetty: 1.4.2025

WebSocket API v1 -rajapintamme on tarkoitettu tarjoamaan jatkuva markkinadatasyöte (tickerit, tilauskirjat, toteutuneet kaupat jne.), mutta joskus Cloudflare katkaisee WebSocket-yhteydet odottamatta.

Cloudflaren aloittamat katkaisut johtavat yleensä verkkovirheisiin, kuten seuraavaan 1006-virheeseen:

  • Error reading from websocket: close 1006 (abnormal closure): unexpected EOF

tai seuraavaan, jossa Python aiheuttaa poikkeuksen yrittäessään lukea uutta markkinadataviestiä jo suljetusta yhteydestä:

  • websocket._exceptions.WebSocketConnectionClosedException: socket is already closed

Vaikka tällaisten odottamattomien katkaisujen estämiseen ei ole suoraa ratkaisua, on olemassa joitakin erittäin tehokkaita kiertotapoja. Jos havaitset toistuvia WebSocket-katkaisuja, suosittelemme ottamaan käyttöön yhden tai useamman seuraavista:

Vaihtoehtoinen IP-osoite

Cloudflaren turvatoimia sovelletaan usein tiettyihin IP-osoitteisiin, samankaltaisten IP-osoitteiden ryhmiin (kuten C-luokan lohkoon) tai tiettyihin maantieteellisiin sijainteihin, joten IP-osoitteen vaihtaminen on joskus yksinkertainen mutta tehokas ratkaisu.

Esimerkiksi API-ohjelmiston isännöinti eri AWS-alueella tai VPN:n käyttö eri maantieteellisessä sijainnissa muuttaisi molemmissa tapauksissa paikallista IP-osoitetta merkittävästi ja voisi mahdollisesti ratkaista odottamattoman katkaisuongelman.

Uudelleenyhdistämislogiikka

Yhden WebSocket-yhteyden käyttö, jota valvotaan tahattomien katkaisujen varalta ja joka yhdistetään ja tilataan uudelleen tarvittaessa, on toinen yksinkertainen mutta tehokas ratkaisu. Seuraavassa on perusesimerkki Python-koodista, joka näyttää, miten WebSocket-yhteys muodostetaan ja tilataan uudelleen aina, kun WebSocketConnectionClosed-poikkeus tapahtuu:

#!/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)

Kuten seuraavassa esimerkkitulosteessa näkyy, WebSocket-yhteys ja tilaus uusitaan aina, kun yhteys katkeaa odottamatta (silmukan 30. ja 60. iteraatiossa):

{"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"] ...

Redundanttiset yhteydet

Useiden (kahden tai useamman) redundanttisten WebSocket-yhteyksien käyttö on yleensä tehokkain ratkaisu odottamattomiin WebSocket-katkaisuihin, koska se mahdollistaa markkinadatasyötteiden jatkumisen keskeytyksettä riippumatta siitä, kuinka usein taustalla olevat yhteydet katkeavat.

Seuraava Python-koodiesimerkki näyttää, miten useita WebSocket-yhteyksiä käytetään automaattisella vikasietoisuudella:

#!/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)

Yllä oleva koodi luo kaksi WebSocket-yhteyttä ja tilaa molemmat samaan markkinadatasyötteeseen. Ensisijaista yhteyttä käytetään markkinadatan tulostamiseen, kunnes tapahtuu näppäimistökeskeytys (esim. Ctrl+C), joka sulkee ensisijaisen yhteyden. Seuraavat lukuyritykset ensisijaisesta yhteydestä aiheuttavat poikkeuksen, joka otetaan kiinni ja jota käytetään markkinadatalähteen vaihtamiseen varayhteyteen, jota käytetään sen jälkeen markkinadatan tulostamiseen.

Seuraava esimerkkituloste näyttää, miten markkinadatalähde vaihtuu ensisijaisesta yhteydestä varayhteyteen keskeyttämättä markkinadatasyötettä:

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"]

Huomaa, että redundanttiset WebSocket-yhteydet on näistä kolmesta ratkaisusta monimutkaisin, mutta se on optimaalinen ratkaisu luotettavuuden kannalta.

Tarvitsetko lisää apua?