mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-19 04:13:02 +00:00
Websocket connection performance improvements, Added multiple options, SymbolOrderBook improvements
This commit is contained in:
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
@@ -215,14 +216,19 @@ namespace CryptoExchange.Net.Sockets
|
||||
return false;
|
||||
}
|
||||
|
||||
_sendTask = Task.Run(SendLoopAsync);
|
||||
_receiveTask = Task.Run(ReceiveLoopAsync);
|
||||
log.Write(LogLevel.Trace, $"Socket {Id} connection succeeded, starting communication");
|
||||
_sendTask = Task.Factory.StartNew(SendLoopAsync, TaskCreationOptions.LongRunning);
|
||||
_receiveTask = Task.Factory.StartNew(ReceiveLoopAsync, TaskCreationOptions.LongRunning);
|
||||
if (Timeout != default)
|
||||
_timeoutTask = Task.Run(CheckTimeoutAsync);
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
while (!_startedSent || !_startedReceive)
|
||||
// Wait for the tasks to have actually started
|
||||
await Task.Delay(10).ConfigureAwait(false);
|
||||
|
||||
log.Write(LogLevel.Warning, $"Socket {Id} waited for {sw.ElapsedMilliseconds}ms for tasks to start");
|
||||
|
||||
log.Write(LogLevel.Debug, $"Socket {Id} connected");
|
||||
return true;
|
||||
}
|
||||
@@ -237,6 +243,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
throw new InvalidOperationException("Can't send data when socket is not connected");
|
||||
|
||||
var bytes = _encoding.GetBytes(data);
|
||||
log.Write(LogLevel.Trace, $"Socket {Id} Adding {bytes.Length} to sent buffer");
|
||||
_sendBuffer.Enqueue(bytes);
|
||||
_sendEvent.Set();
|
||||
}
|
||||
@@ -278,6 +285,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
if (_timeoutTask != null)
|
||||
tasksToAwait.Add(_timeoutTask);
|
||||
|
||||
log.Write(LogLevel.Trace, $"Socket {Id} waiting for communication loops to finish");
|
||||
await Task.WhenAll(tasksToAwait).ConfigureAwait(false);
|
||||
log.Write(LogLevel.Debug, $"Socket {Id} closed");
|
||||
Handle(closeHandlers);
|
||||
@@ -296,6 +304,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
openHandlers.Clear();
|
||||
closeHandlers.Clear();
|
||||
messageHandlers.Clear();
|
||||
log.Write(LogLevel.Trace, $"Socket {Id} disposed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,6 +9,7 @@ using CryptoExchange.Net.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using CryptoExchange.Net.Objects;
|
||||
|
||||
namespace CryptoExchange.Net.Sockets
|
||||
{
|
||||
@@ -68,7 +69,14 @@ namespace CryptoExchange.Net.Sockets
|
||||
/// If the socket should be reconnected upon closing
|
||||
/// </summary>
|
||||
public bool ShouldReconnect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current reconnect try
|
||||
/// </summary>
|
||||
public int ReconnectTry { get; set; }
|
||||
/// <summary>
|
||||
/// Current resubscribe try
|
||||
/// </summary>
|
||||
public int ResubscribeTry { get; set; }
|
||||
/// <summary>
|
||||
/// Time of disconnecting
|
||||
/// </summary>
|
||||
@@ -133,6 +141,7 @@ namespace CryptoExchange.Net.Sockets
|
||||
Socket.OnClose += SocketOnClose;
|
||||
Socket.OnOpen += () =>
|
||||
{
|
||||
ReconnectTry = 0;
|
||||
PausedActivity = false;
|
||||
Connected = true;
|
||||
};
|
||||
@@ -325,7 +334,21 @@ namespace CryptoExchange.Net.Sockets
|
||||
Socket.Reset();
|
||||
if (!await Socket.ConnectAsync().ConfigureAwait(false))
|
||||
{
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to reconnect");
|
||||
ReconnectTry++;
|
||||
if(socketClient.MaxReconnectTries != null
|
||||
&& ReconnectTry >= socketClient.MaxReconnectTries)
|
||||
{
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to reconnect after {ReconnectTry} tries, closing");
|
||||
ShouldReconnect = false;
|
||||
|
||||
if (socketClient.sockets.ContainsKey(Socket.Id))
|
||||
socketClient.sockets.TryRemove(Socket.Id, out _);
|
||||
|
||||
Closed?.Invoke();
|
||||
break;
|
||||
}
|
||||
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to reconnect{(socketClient.MaxReconnectTries != null ? $", try {ReconnectTry}/{socketClient.MaxReconnectTries}": "")}");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -337,9 +360,28 @@ namespace CryptoExchange.Net.Sockets
|
||||
|
||||
var reconnectResult = await ProcessReconnectAsync().ConfigureAwait(false);
|
||||
if (!reconnectResult)
|
||||
{
|
||||
ResubscribeTry++;
|
||||
|
||||
if (socketClient.MaxResubscribeTries != null &&
|
||||
ResubscribeTry >= socketClient.MaxResubscribeTries)
|
||||
{
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} failed to resubscribe after {ResubscribeTry} tries, closing");
|
||||
ShouldReconnect = false;
|
||||
|
||||
if (socketClient.sockets.ContainsKey(Socket.Id))
|
||||
socketClient.sockets.TryRemove(Socket.Id, out _);
|
||||
|
||||
Closed?.Invoke();
|
||||
}
|
||||
else
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} resubscribing all subscriptions failed on reconnected socket{(socketClient.MaxResubscribeTries != null ? $", try {ResubscribeTry}/{socketClient.MaxResubscribeTries}" : "")}. Disconnecting and reconnecting.");
|
||||
|
||||
await Socket.CloseAsync().ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResubscribeTry = 0;
|
||||
if (lostTriggered)
|
||||
{
|
||||
lostTriggered = false;
|
||||
@@ -389,29 +431,39 @@ namespace CryptoExchange.Net.Sockets
|
||||
lock (subscriptionLock)
|
||||
subscriptionList = subscriptions.Where(h => h.Request != null).ToList();
|
||||
|
||||
var success = true;
|
||||
var taskList = new List<Task>();
|
||||
// Foreach subscription which is subscribed by a subscription request we will need to resend that request to resubscribe
|
||||
foreach (var subscription in subscriptionList)
|
||||
for (var i = 0; i < subscriptionList.Count; i += socketClient.MaxConcurrentResubscriptionsPerSocket)
|
||||
{
|
||||
var task = socketClient.SubscribeAndWaitAsync(this, subscription.Request!, subscription).ContinueWith(t =>
|
||||
var success = true;
|
||||
var taskList = new List<Task>();
|
||||
foreach (var subscription in subscriptionList.Skip(i).Take(socketClient.MaxConcurrentResubscriptionsPerSocket))
|
||||
{
|
||||
if (!t.Result)
|
||||
success = false;
|
||||
});
|
||||
taskList.Add(task);
|
||||
}
|
||||
var task = socketClient.SubscribeAndWaitAsync(this, subscription.Request!, subscription).ContinueWith(t =>
|
||||
{
|
||||
if (!t.Result)
|
||||
success = false;
|
||||
});
|
||||
taskList.Add(task);
|
||||
}
|
||||
|
||||
await Task.WhenAll(taskList).ConfigureAwait(false);
|
||||
if (!success)
|
||||
{
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} resubscribing all subscriptions failed on reconnected socket. Disconnecting and reconnecting.");
|
||||
return false;
|
||||
}
|
||||
await Task.WhenAll(taskList).ConfigureAwait(false);
|
||||
if (!success)
|
||||
return false;
|
||||
}
|
||||
|
||||
log.Write(LogLevel.Debug, $"Socket {Socket.Id} all subscription successfully resubscribed on reconnected socket.");
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task UnsubscribeAsync(SocketSubscription socketSubscription)
|
||||
{
|
||||
await socketClient.UnsubscribeAsync(this, socketSubscription).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task<CallResult<bool>> ResubscribeAsync(SocketSubscription socketSubscription)
|
||||
{
|
||||
return await socketClient.SubscribeAndWaitAsync(this, socketSubscription.Request!, socketSubscription).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the connection
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using CryptoExchange.Net.Objects;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CryptoExchange.Net.Sockets
|
||||
@@ -91,5 +92,23 @@ namespace CryptoExchange.Net.Sockets
|
||||
{
|
||||
return connection.Socket.CloseAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe a subscription
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal async Task UnsubscribeAsync()
|
||||
{
|
||||
await connection.UnsubscribeAsync(subscription).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resubscribe this subscription
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal async Task<CallResult<bool>> ResubscribeAsync()
|
||||
{
|
||||
return await connection.ResubscribeAsync(subscription).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user