1
0
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:
Jkorf
2021-08-24 11:59:11 +02:00
parent c29b2d0cf1
commit acfd6310d9
9 changed files with 231 additions and 37 deletions
@@ -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>
+69 -17
View File
@@ -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);
}
}
}