Get rid of pub sub abstraction and add redis impl
This commit is contained in:
parent
4221db3890
commit
915ebbda6b
|
|
@ -1,22 +1,30 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Channels;
|
using Channels;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
using SocketsSample.Hubs;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using StackExchange.Redis;
|
||||||
|
|
||||||
namespace SocketsSample.EndPoints.Hubs
|
namespace SocketsSample.EndPoints.Hubs
|
||||||
{
|
{
|
||||||
public class PubSubHubLifetimeManager<THub> : HubLifetimeManager<THub>
|
public class RedisHubLifetimeManager<THub> : HubLifetimeManager<THub>, IDisposable
|
||||||
{
|
{
|
||||||
private readonly IPubSub _bus;
|
|
||||||
private readonly InvocationAdapterRegistry _registry;
|
private readonly InvocationAdapterRegistry _registry;
|
||||||
|
private readonly ConnectionMultiplexer _redis;
|
||||||
|
private readonly ISubscriber _bus;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
|
||||||
public PubSubHubLifetimeManager(IPubSub bus, InvocationAdapterRegistry registry)
|
public RedisHubLifetimeManager(InvocationAdapterRegistry registry, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_bus = bus;
|
var writer = new LoggerTextWriter(loggerFactory.CreateLogger<RedisHubLifetimeManager<THub>>());
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
_redis = ConnectionMultiplexer.Connect("localhost", writer);
|
||||||
|
_bus = _redis.GetSubscriber();
|
||||||
_registry = registry;
|
_registry = registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,7 +36,7 @@ namespace SocketsSample.EndPoints.Hubs
|
||||||
Arguments = args
|
Arguments = args
|
||||||
};
|
};
|
||||||
|
|
||||||
return _bus.Publish(typeof(THub).Name, message);
|
return PublishAsync(typeof(THub).Name, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task InvokeConnection(string connectionId, string methodName, params object[] args)
|
public override Task InvokeConnection(string connectionId, string methodName, params object[] args)
|
||||||
|
|
@ -39,7 +47,7 @@ namespace SocketsSample.EndPoints.Hubs
|
||||||
Arguments = args
|
Arguments = args
|
||||||
};
|
};
|
||||||
|
|
||||||
return _bus.Publish(typeof(THub) + "." + connectionId, message);
|
return PublishAsync(typeof(THub) + "." + connectionId, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task InvokeGroup(string groupName, string methodName, params object[] args)
|
public override Task InvokeGroup(string groupName, string methodName, params object[] args)
|
||||||
|
|
@ -50,7 +58,7 @@ namespace SocketsSample.EndPoints.Hubs
|
||||||
Arguments = args
|
Arguments = args
|
||||||
};
|
};
|
||||||
|
|
||||||
return _bus.Publish(typeof(THub) + "." + groupName, message);
|
return PublishAsync(typeof(THub) + "." + groupName, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task InvokeUser(string userId, string methodName, params object[] args)
|
public override Task InvokeUser(string userId, string methodName, params object[] args)
|
||||||
|
|
@ -61,7 +69,21 @@ namespace SocketsSample.EndPoints.Hubs
|
||||||
Arguments = args
|
Arguments = args
|
||||||
};
|
};
|
||||||
|
|
||||||
return _bus.Publish(typeof(THub) + "." + userId, message);
|
return PublishAsync(typeof(THub) + "." + userId, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task PublishAsync(string channel, InvocationDescriptor message)
|
||||||
|
{
|
||||||
|
// TODO: What format??
|
||||||
|
var invocationAdapter = _registry.GetInvocationAdapter("json");
|
||||||
|
|
||||||
|
// BAD
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
invocationAdapter.WriteInvocationDescriptor(message, ms);
|
||||||
|
|
||||||
|
return _bus.PublishAsync(channel, ms.ToArray());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task OnConnectedAsync(Connection connection)
|
public override Task OnConnectedAsync(Connection connection)
|
||||||
|
|
@ -87,6 +109,8 @@ namespace SocketsSample.EndPoints.Hubs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connection.Metadata.Get<ConnectionMultiplexer>("redis")?.Dispose();
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,15 +133,67 @@ namespace SocketsSample.EndPoints.Hubs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDisposable Subscribe(string signal, Connection connection)
|
private IDisposable Subscribe(string channel, Connection connection)
|
||||||
{
|
{
|
||||||
return _bus.Subscribe(signal, message =>
|
var muxer = connection.Metadata.GetOrAdd("redis", k =>
|
||||||
{
|
{
|
||||||
var invocationAdapter = _registry.GetInvocationAdapter((string)connection.Metadata["formatType"]);
|
var logger = _loggerFactory.CreateLogger("REDIS_" + connection.ConnectionId);
|
||||||
|
return ConnectionMultiplexer.Connect("localhost", new LoggerTextWriter(logger));
|
||||||
|
});
|
||||||
|
|
||||||
return invocationAdapter.WriteInvocationDescriptor((InvocationDescriptor)message, connection.Channel.GetStream());
|
var subscriber = muxer.GetSubscriber();
|
||||||
|
|
||||||
|
subscriber.SubscribeAsync(channel, (c, data) =>
|
||||||
|
{
|
||||||
|
connection.Channel.Output.WriteAsync((byte[])data);
|
||||||
|
});
|
||||||
|
|
||||||
|
return new DisposableAction(() =>
|
||||||
|
{
|
||||||
|
subscriber.Unsubscribe(channel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_redis.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DisposableAction : IDisposable
|
||||||
|
{
|
||||||
|
private Action _action;
|
||||||
|
|
||||||
|
public DisposableAction(Action action)
|
||||||
|
{
|
||||||
|
_action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref _action, () => { }).Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LoggerTextWriter : TextWriter
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public LoggerTextWriter(ILogger logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Encoding Encoding => Encoding.UTF8;
|
||||||
|
|
||||||
|
public override void Write(char value)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteLine(string value)
|
||||||
|
{
|
||||||
|
_logger.LogDebug(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SocketsSample.Hubs
|
|
||||||
{
|
|
||||||
public interface IPubSub
|
|
||||||
{
|
|
||||||
IDisposable Subscribe(string topic, Func<object, Task> callback);
|
|
||||||
Task Publish(string topic, object data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Bus : IPubSub
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, List<Func<object, Task>>> _subscriptions = new ConcurrentDictionary<string, List<Func<object, Task>>>();
|
|
||||||
|
|
||||||
public IDisposable Subscribe(string key, Func<object, Task> observer)
|
|
||||||
{
|
|
||||||
var subscriptions = _subscriptions.GetOrAdd(key, _ => new List<Func<object, Task>>());
|
|
||||||
subscriptions.Add(observer);
|
|
||||||
|
|
||||||
return new DisposableAction(() =>
|
|
||||||
{
|
|
||||||
subscriptions.Remove(observer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Publish(string key, object data)
|
|
||||||
{
|
|
||||||
List<Func<object, Task>> subscriptions;
|
|
||||||
if (_subscriptions.TryGetValue(key, out subscriptions))
|
|
||||||
{
|
|
||||||
foreach (var c in subscriptions)
|
|
||||||
{
|
|
||||||
await c(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DisposableAction : IDisposable
|
|
||||||
{
|
|
||||||
private Action _action;
|
|
||||||
|
|
||||||
public DisposableAction(Action action)
|
|
||||||
{
|
|
||||||
_action = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Interlocked.Exchange(ref _action, () => { }).Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -15,8 +15,8 @@ namespace SocketsSample
|
||||||
{
|
{
|
||||||
services.AddRouting();
|
services.AddRouting();
|
||||||
|
|
||||||
services.AddSingleton<IPubSub, Bus>();
|
services.AddSingleton(typeof(HubLifetimeManager<>), typeof(DefaultHubLifetimeManager<>));
|
||||||
services.AddSingleton(typeof(HubLifetimeManager<>), typeof(PubSubHubLifetimeManager<>));
|
// services.AddSingleton(typeof(HubLifetimeManager<>), typeof(RedisHubLifetimeManager<>));
|
||||||
services.AddSingleton(typeof(HubEndPoint<>), typeof(HubEndPoint<>));
|
services.AddSingleton(typeof(HubEndPoint<>), typeof(HubEndPoint<>));
|
||||||
services.AddSingleton(typeof(RpcEndpoint<>), typeof(RpcEndpoint<>));
|
services.AddSingleton(typeof(RpcEndpoint<>), typeof(RpcEndpoint<>));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
"type": "platform"
|
"type": "platform"
|
||||||
},
|
},
|
||||||
"Newtonsoft.Json": "9.0.1",
|
"Newtonsoft.Json": "9.0.1",
|
||||||
|
"StackExchange.Redis": "1.1.*",
|
||||||
"Microsoft.AspNetCore.Diagnostics": "1.1.0-*",
|
"Microsoft.AspNetCore.Diagnostics": "1.1.0-*",
|
||||||
"Microsoft.AspNetCore.StaticFiles": "1.1.0-*",
|
"Microsoft.AspNetCore.StaticFiles": "1.1.0-*",
|
||||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0-*",
|
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0-*",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue