public RedisHubLifetimeManager(ILogger <RedisHubLifetimeManager <THub> > logger, IOptions <RedisOptions> options, IHubProtocolResolver hubProtocolResolver) { _logger = logger; _options = options.Value; _ackHandler = new AckHandler(); _channels = new RedisChannels(typeof(THub).FullName); _protocol = new RedisProtocol(hubProtocolResolver.AllProtocols); RedisLog.ConnectingToEndpoints(_logger, options.Value.Configuration.EndPoints, _serverName); _ = EnsureRedisServerConnection(); }
public RedisHubLifetimeManager(ILogger <RedisHubLifetimeManager <THub> > logger, IOptions <RedisOptions> options) { _logger = logger; _options = options.Value; _ackHandler = new AckHandler(); var writer = new LoggerTextWriter(logger); _logger.ConnectingToEndpoints(options.Value.Options.EndPoints); _redisServerConnection = _options.Connect(writer); _redisServerConnection.ConnectionRestored += (_, e) => { // We use the subscription connection type // Ignore messages from the interactive connection (avoids duplicates) if (e.ConnectionType == ConnectionType.Interactive) { return; } _logger.ConnectionRestored(); }; _redisServerConnection.ConnectionFailed += (_, e) => { // We use the subscription connection type // Ignore messages from the interactive connection (avoids duplicates) if (e.ConnectionType == ConnectionType.Interactive) { return; } _logger.ConnectionFailed(e.Exception); }; if (_redisServerConnection.IsConnected) { _logger.Connected(); } else { _logger.NotConnected(); } _bus = _redisServerConnection.GetSubscriber(); SubscribeToHub(); SubscribeToAllExcept(); SubscribeToInternalGroup(); SubscribeToInternalServerName(); }
public RedisHubLifetimeManager(ILogger <RedisHubLifetimeManager <THub> > logger, IOptions <RedisOptions> options) { _logger = logger; _options = options.Value; _ackHandler = new AckHandler(); var writer = new LoggerTextWriter(logger); _logger.LogInformation("Connecting to redis endpoints: {endpoints}", string.Join(", ", options.Value.Options.EndPoints.Select(e => EndPointCollection.ToString(e)))); _redisServerConnection = _options.Connect(writer); if (_redisServerConnection.IsConnected) { _logger.LogInformation("Connected to redis"); } else { // TODO: We could support reconnecting, like old SignalR does. throw new InvalidOperationException("Connection to redis failed."); } _bus = _redisServerConnection.GetSubscriber(); var previousBroadcastTask = Task.CompletedTask; var channelName = _channelNamePrefix; _logger.LogInformation("Subscribing to channel: {channel}", channelName); _bus.Subscribe(channelName, async(c, data) => { await previousBroadcastTask; _logger.LogTrace("Received message from redis channel {channel}", channelName); var message = DeserializeMessage <HubMessage>(data); // TODO: This isn't going to work when we allow JsonSerializer customization or add Protobuf var tasks = new List <Task>(_connections.Count); foreach (var connection in _connections) { tasks.Add(WriteAsync(connection, message)); } previousBroadcastTask = Task.WhenAll(tasks); }); var allExceptTask = Task.CompletedTask; channelName = _channelNamePrefix + ".AllExcept"; _logger.LogInformation("Subscribing to channel: {channel}", channelName); _bus.Subscribe(channelName, async(c, data) => { await allExceptTask; _logger.LogTrace("Received message from redis channel {channel}", channelName); var message = DeserializeMessage <RedisExcludeClientsMessage>(data); var excludedIds = message.ExcludedIds; // TODO: This isn't going to work when we allow JsonSerializer customization or add Protobuf var tasks = new List <Task>(_connections.Count); foreach (var connection in _connections) { if (!excludedIds.Contains(connection.ConnectionId)) { tasks.Add(WriteAsync(connection, message)); } } allExceptTask = Task.WhenAll(tasks); }); channelName = _channelNamePrefix + ".internal.group"; _bus.Subscribe(channelName, async(c, data) => { var groupMessage = DeserializeMessage <GroupMessage>(data); if (groupMessage.Action == GroupAction.Remove) { if (!await RemoveGroupAsyncCore(groupMessage.ConnectionId, groupMessage.Group)) { // user not on this server return; } } if (groupMessage.Action == GroupAction.Add) { if (!await AddGroupAsyncCore(groupMessage.ConnectionId, groupMessage.Group)) { // user not on this server return; } } // Sending ack to server that sent the original add/remove await PublishAsync($"{_channelNamePrefix}.internal.{groupMessage.Server}", new GroupMessage { Action = GroupAction.Ack, ConnectionId = groupMessage.ConnectionId, Group = groupMessage.Group, Id = groupMessage.Id }); }); // Create server specific channel in order to send an ack to a single server var serverChannel = $"{_channelNamePrefix}.internal.{_serverName}"; _bus.Subscribe(serverChannel, (c, data) => { var groupMessage = DeserializeMessage <GroupMessage>(data); if (groupMessage.Action == GroupAction.Ack) { _ackHandler.TriggerAck(groupMessage.Id); } }); }