private async Task UnregisterChannels(WebSocket webSocket) { logger_.LogInformation("Unregistering websockets channels"); bool removedSocket = false; int tries = 0; logger_.LogInformation("Removing websocket"); while (!removedSocket && tries < MAX_CHANNEL_REMOVAL_TRIES) { removedSocket = subscriptions_.TryRemove(webSocket, out _); if (!removedSocket) { ++tries; await Task.Delay(TimeSpan.FromSeconds(DICT_REMOVAL_HOLDOFF)); } } WebSocketStats.DecrementSubscriberCount(); logger_.LogDebug("WebSocket status " + webSocket.State); logger_.LogInformation("Closing websocket"); if (WebSocketCanSend(webSocket)) { await webSocket.CloseOutputAsync(WebSocketCloseStatus.EndpointUnavailable, "All subscriptions cancelled", CancellationToken.None); logger_.LogInformation("Websocket closed"); } logger_.LogInformation("Channel unregistered"); //TODO // If the last subscriber is removed , clear the pending queue }
private void RegisterChannel(WebSocket webSocket, string channelName) { if (subscriptions_.TryAdd(webSocket, new ConcurrentDictionary <string, byte>())) { WebSocketStats.IncrementSubscriberCount(); } subscriptions_[webSocket].TryAdd(channelName, 1); }
private async Task PublisherThread() { logger_.LogInformation("Initializing websocket publisher thread"); try { var keepRunning = true; while (keepRunning) { //This call blocks on an empty queue var message = await messageQueue_.DequeueAsync(); WebSocketStats.IncrementOutputMessages(); WebSocketStats.DecrementPendingQueueSize(); keepRunning = message.MessageType != BitprimWebSocketMessageType.SHUTDOWN; if (keepRunning) { var buffer = Encoding.UTF8.GetBytes(message.Content); foreach (var ws in subscriptions_) { if (ws.Value.TryGetValue(message.ChannelName, out var dummy)) { try { await retryPolicy_.ExecuteAsync(async() => { await ws.Key.SendAsync ( new ArraySegment <byte>(buffer, 0, message.Content.Length), WebSocketMessageType.Text, true, CancellationToken.None ); WebSocketStats.IncrementSentMessages(); logger_.LogDebug($"Sent Frame {WebSocketMessageType.Text}: Len={message.Content.Length}, Fin={true}: {message.Content}"); }); } catch (WebSocketException ex) { logger_.LogWarning(ex, "Maxed out retries sending to client, closing channels"); await UnregisterChannels(ws.Key); } } } } } } catch (Exception ex) { logger_.LogError(ex, "PublisherThread error"); } logger_.LogInformation("Closing websocket publisher thread"); }
private async Task Publish(string channelName, string item) { logger_.LogDebug($"Adding websocket message to queue ({channelName})"); if (subscriptions_.Count <= 0) { logger_.LogDebug("Zero subscriptions. websocket message dropped"); } else { await messageQueue_.EnqueueAsync ( new BitprimWebSocketMessage { ChannelName = channelName, Content = item, MessageType = BitprimWebSocketMessageType.PUBLICATION } ); WebSocketStats.IncrementInputMessages(); WebSocketStats.IncrementPendingQueueSize(); } }