Created
March 14, 2013 02:15
-
-
Save digitalBush/5158269 to your computer and use it in GitHub Desktop.
SignalR scale out via RabbitMQ and ServiceStack.Text
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using Microsoft.AspNet.SignalR; | |
| using Microsoft.AspNet.SignalR.Messaging; | |
| using RabbitMQ.Client; | |
| namespace SignalR.RabbitMQ { | |
| public static class DependencyResolverExtensions { | |
| public static IDependencyResolver UseRabbitMq(this IDependencyResolver resolver, ConnectionFactory connectionFactory) { | |
| if (connectionFactory == null) { | |
| throw new ArgumentNullException("connectionFactory"); | |
| } | |
| resolver.Register(typeof(IMessageBus), () => new RabbitMQBackplane(connectionFactory,resolver)); | |
| return resolver; | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.IO; | |
| using RabbitMQ.Client; | |
| using ServiceStack.Text; | |
| namespace SignalR.RabbitMQ { | |
| public class MessageConsumer<T>:DefaultBasicConsumer { | |
| private readonly Action<T> _action; | |
| public MessageConsumer(Action<T> action) { | |
| _action = action; | |
| } | |
| public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey,IBasicProperties properties, byte[] body) { | |
| using (var stream = new MemoryStream(body)) { | |
| var obj = JsonSerializer.DeserializeFromStream<T>(stream); | |
| _action(obj); | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Threading.Tasks; | |
| using Microsoft.AspNet.SignalR; | |
| using Microsoft.AspNet.SignalR.Messaging; | |
| using RabbitMQ.Client; | |
| using ServiceStack.Text; | |
| using IConnection = RabbitMQ.Client.IConnection; | |
| namespace SignalR.RabbitMQ { | |
| public class RabbitMQBackplane:ScaleoutMessageBus { | |
| private const string Exchange = "signalR"; | |
| private readonly IModel _sendChannel; | |
| private readonly IConnection _connection; | |
| private ulong _id = 0; //TODO: Not sure what to do here... | |
| public RabbitMQBackplane(ConnectionFactory connectionFactory, IDependencyResolver resolver) : base(resolver) { | |
| _connection = connectionFactory.CreateConnection(); | |
| _sendChannel = _connection.CreateModel(); | |
| _sendChannel.ExchangeDeclare(Exchange, ExchangeType.Fanout, true, false, null); | |
| var receiveChannel = _connection.CreateModel(); | |
| var queue = receiveChannel.QueueDeclare("signalR-"+Guid.NewGuid(), false, true, true, null); | |
| receiveChannel.QueueBind(queue.QueueName, Exchange, ""); | |
| var consumer = new MessageConsumer<IList<Message>>(messages => OnReceived("0", _id++, messages)); | |
| receiveChannel.BasicConsume(queue.QueueName, true, consumer); | |
| } | |
| protected override Task Send(IList<Message> messages) { | |
| return Task.Factory.StartNew(msgs => { | |
| using (var stream = new MemoryStream()){ | |
| JsonSerializer.SerializeToStream(msgs, stream); | |
| _sendChannel.BasicPublish(Exchange, "", null, stream.ToArray()); | |
| } | |
| }, messages); | |
| } | |
| protected override void Dispose(bool disposing) { | |
| if (disposing){ | |
| _connection.Abort(); | |
| _connection.Dispose(); | |
| } | |
| base.Dispose(disposing); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I really wrestled with the .Net RabbitMQ.Client API on monitoring shutdowns. They've got some poor design/buggy behavior with respect to their ConnectionShutdown event. I ended up reestablishing connections on one of their internal SessionShutdown events and I restablish the bus's previous subscriptions and push queued publications upon reconnect.
On the issue of grabbing a new connection with each publish, that's not the recommended approach and is really going to be a bottleneck for nodes issuing many messages. That's what channels are for.