mirror of https://github.com/Squidex/squidex.git
8 changed files with 175 additions and 3 deletions
@ -0,0 +1,80 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Infrastructure.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Actors |
|||
{ |
|||
public sealed class DefaultRemoteActorChannel : IRemoteActorChannel |
|||
{ |
|||
private static readonly string ChannelName = typeof(DefaultRemoteActorChannel).Name; |
|||
private readonly IPubSub pubSub; |
|||
private readonly JsonSerializer serializer; |
|||
private readonly TypeNameRegistry typeNameRegistry; |
|||
|
|||
private sealed class Envelope |
|||
{ |
|||
public string Recipient { get; set; } |
|||
|
|||
public string PayloadType { get; set; } |
|||
|
|||
public JToken Payload { get; set; } |
|||
} |
|||
|
|||
public DefaultRemoteActorChannel(IPubSub pubSub, TypeNameRegistry typeNameRegistry, JsonSerializerSettings serializerSettings = null) |
|||
{ |
|||
Guard.NotNull(pubSub, nameof(pubSub)); |
|||
Guard.NotNull(typeNameRegistry, nameof(typeNameRegistry)); |
|||
|
|||
this.pubSub = pubSub; |
|||
|
|||
this.typeNameRegistry = typeNameRegistry; |
|||
|
|||
serializer = JsonSerializer.Create(serializerSettings ?? new JsonSerializerSettings()); |
|||
} |
|||
|
|||
public Task SendAsync(string recipient, IMessage message) |
|||
{ |
|||
Guard.NotNullOrEmpty(recipient, nameof(recipient)); |
|||
Guard.NotNull(message, nameof(message)); |
|||
|
|||
var messageType = typeNameRegistry.GetName(message.GetType()); |
|||
var messagePayload = WriteJson(message); |
|||
|
|||
var envelope = new Envelope { Recipient = recipient, Payload = messagePayload, PayloadType = messageType }; |
|||
|
|||
pubSub.Publish(ChannelName, JsonConvert.SerializeObject(envelope), true); |
|||
|
|||
return TaskHelper.Done; |
|||
} |
|||
|
|||
public void Subscribe(string recipient, Action<IMessage> handler) |
|||
{ |
|||
Guard.NotNullOrEmpty(recipient, nameof(recipient)); |
|||
|
|||
pubSub.Subscribe(ChannelName, json => |
|||
{ |
|||
var envelope = JsonConvert.DeserializeObject<Envelope>(json); |
|||
|
|||
if (string.Equals(envelope.Recipient, recipient, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
var messageType = typeNameRegistry.GetType(envelope.PayloadType); |
|||
var messagePayload = ReadJson<IMessage>(envelope.Payload, messageType); |
|||
|
|||
handler?.Invoke(messagePayload); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private T ReadJson<T>(JToken token, Type type = null) |
|||
{ |
|||
return (T)token.ToObject(type ?? typeof(T), serializer); |
|||
} |
|||
|
|||
private JToken WriteJson(object value) |
|||
{ |
|||
return JToken.FromObject(value, serializer); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Squidex.Infrastructure.Actors |
|||
{ |
|||
public interface IActors |
|||
{ |
|||
IActor Get(string id); |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Actors |
|||
{ |
|||
public interface IRemoteActorChannel |
|||
{ |
|||
Task SendAsync(string recipient, IMessage message); |
|||
|
|||
void Subscribe(string recipient, Action<IMessage> handler); |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Infrastructure.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Actors |
|||
{ |
|||
public sealed class RemoteActors : IActors |
|||
{ |
|||
private readonly ConcurrentDictionary<string, IActor> senders = new ConcurrentDictionary<string, IActor>(); |
|||
private readonly ConcurrentDictionary<string, bool> receivers = new ConcurrentDictionary<string, bool>(); |
|||
private readonly IRemoteActorChannel channel; |
|||
|
|||
private sealed class Sender : IActor |
|||
{ |
|||
private readonly IRemoteActorChannel channel; |
|||
private readonly string recipient; |
|||
|
|||
public Sender(IRemoteActorChannel channel, string recipient) |
|||
{ |
|||
this.channel = channel; |
|||
|
|||
this.recipient = recipient; |
|||
} |
|||
|
|||
public Task SendAsync(IMessage message) |
|||
{ |
|||
return channel.SendAsync(recipient, message); |
|||
} |
|||
|
|||
public Task SendAsync(Exception exception) |
|||
{ |
|||
throw new NotSupportedException(); |
|||
} |
|||
|
|||
public Task StopAsync() |
|||
{ |
|||
throw new NotSupportedException(); |
|||
} |
|||
} |
|||
|
|||
public RemoteActors(IRemoteActorChannel channel) |
|||
{ |
|||
Guard.NotNull(channel, nameof(channel)); |
|||
|
|||
this.channel = channel; |
|||
} |
|||
|
|||
public IActor Get(string id) |
|||
{ |
|||
Guard.NotNullOrEmpty(id, nameof(id)); |
|||
|
|||
return senders.GetOrAdd(id, k => new Sender(channel, id)); |
|||
} |
|||
|
|||
public void Connect(string id, IActor actor) |
|||
{ |
|||
Guard.NotNullOrEmpty(id, nameof(id)); |
|||
Guard.NotNull(actor, nameof(actor)); |
|||
|
|||
channel.Subscribe(id, message => |
|||
{ |
|||
actor.SendAsync(message).Forget(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue