public RedisHubLifetimeManager(InvocationAdapterRegistry registry, ILoggerFactory loggerFactory, IOptions <RedisOptions> options) { _loggerFactory = loggerFactory; _registry = registry; _options = options.Value; var writer = new LoggerTextWriter(loggerFactory.CreateLogger <RedisHubLifetimeManager <THub> >()); _redisServerConnection = _options.Connect(writer); _bus = _redisServerConnection.GetSubscriber(); var previousBroadcastTask = Task.CompletedTask; _bus.Subscribe(typeof(THub).FullName, async(c, data) => { await previousBroadcastTask; var tasks = new List <Task>(_connections.Count); foreach (var connection in _connections) { tasks.Add(connection.Channel.Output.WriteAsync((byte[])data)); } previousBroadcastTask = Task.WhenAll(tasks); }); }
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); var writer = new LoggerTextWriter(logger); RedisLog.ConnectingToEndpoints(_logger, options.Value.Options.EndPoints, _serverName); _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; } RedisLog.ConnectionRestored(_logger); }; _redisServerConnection.ConnectionFailed += (_, e) => { // We use the subscription connection type // Ignore messages from the interactive connection (avoids duplicates) if (e.ConnectionType == ConnectionType.Interactive) { return; } RedisLog.ConnectionFailed(_logger, e.Exception); }; if (_redisServerConnection.IsConnected) { RedisLog.Connected(_logger); } else { RedisLog.NotConnected(_logger); } _bus = _redisServerConnection.GetSubscriber(); SubscribeToAll(); SubscribeToGroupManagementChannel(); SubscribeToAckChannel(); }
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); } }); }