I am working on a project that consumes messages using MassTransit and RabbitMQ in C#
I'm building a prototype and right now the consumer project is a console application. Within the Main program.cs class, I connect to the bus and subscribe to accept messages from the publisher like this:
var bus = BusInitializer.CreateBus("Subscriber", x =>
{
x.Subscribe(subs =>
{
subs.Consumer<UpdatedInformation>().Permanent();
});
});
For reference, here's the CreateBus() method:
public static IServiceBus CreateBus(string queueName, Action<ServiceBusConfigurator> moreInitialization)
{
var bus = ServiceBusFactory.New(x =>
{
x.UseRabbitMq();
x.ReceiveFrom("rabbitmq://localhost/test" + queueName);
moreInitialization(x);
});
return bus;
}
And here is an example of one of the consumer classes:
class UpdatedInformationConsumer : Consumes<UpdatedInformation>.Context
{
public void Consume(IConsumeContext<UpdatedInformation> message)
{
Console.Write("\nMessage: " + message.Message);
Console.Write("\nPROCESSED: " + DateTime.Now.ToString());
}
}
In the main class of the subscriber, I also initialize services and other configurations. What I need to do is be able to access those services/objects in my consumer classes as I don't want to initialize new ones every time a message is received in consumed.
This should be relatively easy, but I'm stumped since I'm not actually creating instances of my consumer classes.
Rather than put a bunch of singletons in your service, which leads to a bunch of static methods and so forth, you can specify a consumer factory with your subscription that passes those dependencies to your consumer. You can also use any of the supported containers (or even your own) to resolve those dependencies.
x.Subscribe<UpdatedInformationConsumer>(() =>
new UpdatedInformationConsumer(dependency1, dependency2, etc));
A new instance of the consumer will be created for each message receiving, so object lifecycles can also be managed in this way.
Related
I created several microservices using the ASP.net core API
One of these microservices returns the exact address of the other microservices
How to update the address of any of these microservices without restarting if the address is changed
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("MainMicroservice", x =>
{
x.BaseAddress = new Uri("http://mainmicroservice.com");
});
services.AddHttpClient("Microservice1", x =>
{
x.BaseAddress = new Uri("http://microservice1.com");
});
services.AddHttpClient("Microservice2", x =>
{
x.BaseAddress = new Uri("http://microservice2.com");
});
services.AddHttpClient("Microservice3", x =>
{
x.BaseAddress = new Uri("http://microservice3.com");
});
}
}
public class Test
{
private readonly IHttpClientFactory _client;
public Test(IHttpClientFactory client)
{
_client = client;
}
public async Task<string> Test()
{
var repeat = false;
do
{
try
{
return await _client
.CreateClient("Microservice1")
.GetStringAsync("Test")
.ConfigureAwait(false);
}
catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.NotFound)
{
var newAddress = await _client
.CreateClient("MainMicroservice")
.GetStringAsync("Microservice1")
.ConfigureAwait(false);
//todo change address of microservice1
repeat = true;
}
} while (repeat);
}
}
If you are building a microservice-based solution sooner or later (rather sooner) you will encounter a situation when one service needs to talk to another. In order to do this caller must know the exact location of a target microservice in a network, they operate in.
You must somehow provide an IP address and port where the target microservice listens for requests. You can do it using configuration files or environment variables, but this approach has some drawbacks and limitations.
First is that you have to maintain and properly deploy
configuration files for all your environments: local development,
test, pre-production, and production. Forgetting to update any of
these configurations when adding a new service or moving an existing
one to a different node will result in errors discovered at runtime.
Second, the more important issue is that it works only in a static
environment, meaning you cannot dynamically add/remove nodes,
therefore you won’t be able to dynamically scale your system. The
ability to scale and deploy given microservice autonomously is one
of the key advantages of microservice-based architecture, and we do
not want to lose this ability.
Therefore we need to introduce service discovery. Service discovery is a mechanism that allows services to find each other's network location. There are many possible implementations of this pattern.
We have two types of service discovery: client-side and server-side which you can find a good NuGet package to handle in ASP.NET projects.
We use the Akka.net Framework for highly scaling applications in the energy sector.
We use Akka.net for various tasks, mostly in the following form:
var system=ActorSystem.Create("actorSystem");
var props=Props.Create<UpdateActor>();
.WithRouter(new SmallesMailboxPool(100));
var actorRef=system.ActorOf(props,"UpdateActor");
foreach(var timerow in timeRowList)
actorRef.Tell(timerow)
Unfortunately the Akka.net framework scales very badly in many cases.
The CPU load is only 12%.
Obviously only one thread or a few threads are used.
How can you configure Akka.Net to use multiple threads for processing the actors?
This is an educated guess, but if you're using SmallestMailboxPool, keep in mind that it works pretty badly with non-blocking I/O and terribly with stashing.
First thing is usually to check, if there are no blocking operations (like synchronous I/O, calling AsyncMethod().Result or Thread.Sleep), which will block current thread, effectively preventing it from being used by other actors.
Another issue is very specific to smallest mailbox router, and it's related to stashing and persistent actors.
Stashing
Stashing is one of the popular ways to work with multi-step operations. This pattern can be represented as bellow.
public class MyActor : ActorBase, IWithUnboundedStash
{
public IStash Stash { get; set; }
public Receive Active(State workUnit) => message =>
{
switch(message)
{
case DoWork:
// stash all messages not related to current work
Stash.Stash(message);
return true;
case WorkDone done:
// when current unit of work is done, unstash pending messages
Stash.UnstashAll();
Become(Idle);
return true;
}
};
public bool Idle(object message)
{
switch(message)
{
case DoWork work:
StartWork(work.State);
Become(Active(work.State)); //continue work in new behavior
return true;
default:
return false;
}
}
public bool Receive(object message) => Idle(message);
}
This case is pretty common i.e. persistent actors use it during their recovery process. Problem is that, it's cleaning up the mailbox, which gives SmallestMailbox routers a false sense that this actor's mailbox is empty, while in practice it's just stashing all incoming message.
This is also a reason why peristent actors should not be routed using SmallestMailbox routers! Tbh. I cannot think of any scenario where putting persistent actors behind router of any kind is a valid option.
I think what you need to do is create an an actor coordinator class, and inside create a list/dictionary of actors. then (for what i understand) they should be working in parallel after you tell the coordinator about new updates.
public class UpdateCoordinator : ReceiveActor
{
//all your update referenced
private readonly Dictionary<int, IActorRef> _updates;
public UpdateCoordinator()
{
_updates = new Dictionary<int, IActorRef>();
//create the update reference
Receive<UpdateMessage>(updateMessage =>
{
//add to the list of actors
CreateUpdateReferenceIfNotExists(updateMessage.Identifier);
IActorRef childUpdateRef = _updates[updateMessage.Identifier];
//start your update actor
childUpdateRef.Tell(updateMessage);
});
}
private void CreateUpdateReferenceIfNotExists(int identifier)
{
if (!_updates.ContainsKey(identifier))
{
IActorRef newChildUpdateRef = Context.ActorOf(Props.Create(()=> new UpdateActor(identifier)), $"Update_{identifier}");
_updates.Add(identifier, newChildUpdateRef);
}
}
}
Using JustSaying 4.0.306 and configuring the publisher like so:
_publisher = CreateMeABus
.InRegion("eu-west-2").WithNamingStrategy(()=>new Naming<T>())
.WithSnsMessagePublisher<MessageHolder<T>>();
I'm overriding the naming strategy as when using generics is creates a very verbose name.
public class Naming<T> : INamingStrategy
{
public string GetTopicName(string topicName, string messageType)
{
return "topic_" + typeof(T).Name.ToLower();
}
public string GetQueueName(SqsReadConfiguration sqsConfig, string messageType)
{
return "queue_" + typeof(T).Name.ToLower();
}
}
I'm configuring the subscription end like so:
var queuename = "queue" + typeof(T).Name.ToLower();
var bus = CreateMeABus.InRegion("eu-west-2")
.WithNamingStrategy(() => new NamingStrategy<T>())
.WithSqsTopicSubscriber().IntoQueue(queuename)
.WithMessageHandler<MessageHolder<T>>(new HandleMessage<T>(_handler));
bus.StartListening();
In AWS the SNS Topic gets created along with the SQS queue's and all the subscriptions etc. When publishing I can see messages flowing into the queue and they are being read from the queue but the HandleMessage<T>.Handle(MessageHolder<T> message) never gets invoked.
I can see that the messages are taken from the queue and sometimes they are in flight so something in the JustSaying world is taking them out of the queue but it never gets to the handler.
Anyone failure with this framework have any idea why is there something I have missed or could it be serialization is failing before the handler is getting invoked?
g/
I have to Rebus buses that use Azure ServiceBus, but when I try to send to from Bus2 in one of Bus1's messagehandlers this does not work. The message is not sent.
Any thoughts ?
EDIT
Bus 1
string padesQueueAddress = "padesworker";
int numberOfWorkes = Settings.NumberOfWorkers>0 Settings.NumberOfWorkers:10;
string errorQueueAddress = string.Format("{0}-error", queueAddress);
var adapter = new AutofacContainerAdapter(Container);
Bus = Configure.With(adapter)
.Logging(l => l.Use(UseRaygunRebusLoggingFactory(rayclient,Settings.Debug ? RaygunLoggerLevel.DEBUG : RaygunLoggerLevel.WARN)))
.Transport(t => t.UseAzureServiceBus( Settings.AzureQueueConnectionString, queueAddress,AzureServiceBusMode.Standard))
.Sagas(s => s.StoreInSqlServer(string.IsNullOrWhiteSpace(Settings.RebusSagaSqlConnectionString) ? Settings.AzureSqlConnectionString : Settings.RebusSagaSqlConnectionString, "Saga", "SagaIndex") )
.Routing(r => r.TypeBased().MapAssemblyOf<SendSmsCommand>(queueAddress).MapAssemblyOf<Unipluss.Sign.Pades.Commands.CreatePadesCommand>(padesQueueAddress))
.Options(o =>
{
o.SimpleRetryStrategy(secondLevelRetriesEnabled: true, maxDeliveryAttempts:5,errorQueueAddress: errorQueueAddress);
o.SetNumberOfWorkers(numberOfWorkes);
o.SetMaxParallelism(numberOfWorkes);
})
.Start();
await Bus.SendLocal(new HeartBeatCommand());
Bus 2
private IBus CreateExternalEventBus()
{
var eventBus = Configure.With(new BuiltinHandlerActivator())
.Transport(t => t.UseAzureServiceBus(Settings.EventServiceBusConnectionString, queueAddress+"_event", AzureServiceBusMode.Basic))
.Logging(x=>x.ColoredConsole(LogLevel.Debug))
.Options(o =>
{
o.LogPipeline(true);
o.EnableCompression();
o.EnableEncryption(Settings.RebusEncryptionExternalEvents);
})
.Start();
eventBus.Advanced.Routing.Send("1dd0f6f9422146048516a30f00aef4e5",new Unipluss.Sign.Events.Entities.DocumentCancledEvent() {CancledMessage = "test",DocumentId = Guid.NewGuid()});
return eventBus;
}
The eventBus send with the hardcoded send is working, but when this i sent from within one of Bus1's message handler the message is not sent (With logging on the logger says that the message is sent but it does appear in the queue).
Bus 2 is wrapped in a wrapper class an then injected in to Autofac to avoid having to IBus interfaces in Autofac.
builder.Register(c => new ExternalEventsBus(CreateExternalEventBus()))
.As<IExternalEventsBus>().SingleInstance();
public class ExternalEventsBus:IExternalEventsBus
{
private IBus Bus;
public ExternalEventsBus(IBus bus)
{
Bus = bus;
}
public async Task Send(object message, Guid documentProviderId)
{
await Bus.Advanced.Routing.Send(documentProviderId.ToString("n"), message);
}
public Task Send(object message, DocumentProvider documentProvider)
{
if (!string.IsNullOrWhiteSpace(documentProvider.RebusQueueConnectionString))
return Send(message, documentProvider.Id);
return Task.FromResult(true);
}
public void Dispose()
{
if(Bus!=null)
Bus.Dispose();
}
}
IExternalEventsBus is then used in multiple messagehandlers in Bus1.
Ok... let me see if I understand this (please correct me if I am wrong):
You have two bus instances in your process:
One that uses an input queue with the value specified by queueAddress, which you did not include here.
Another one (the "external event bus") that has an input queue with the value specified by queueAddress+"_event", which you also did not include here.
It seems like the purpose of your bus instances is that the first one is used to coordinate stuff within the application, whereas the second one is used to route events to listeners on the outside - this is what you would call a "content-based router", since it will route messages depending on some value of the content of the message (in this case documentProviderId).
Now you are experiencing an error: When bus (1) uses bus (2) from within one of its own message handlers, the routed message does not seem to be sent.
It is not clear from the code you posted from ExternalEventsBus which Send method you are calling - but I can tell you that the method with the signature public Task Send(object message, DocumentProvider documentProvider) will only send a message if documentProvider.RebusQueueConnectionString is not null.
Did you verify that the connection string does in fact carry a value?
Why is there a _connectionString field in ExternalEventsBus? Should you have used that when creating the bus?
Do you remember to await bus.Send(...) (i.e. await the result of the asynchronous operation) each time you call the bus?
I currently have a simple use case.
1) A client app that connects to a WCF Service using Castle's AsWcfClient option.
2) WCF Service "A" that is hosted using Castle and is injecting a single dependency. This dependency is a client proxy to another WCF Service (Call it Service "B").
3) Service "B" does some work.
To visualize: Client -> Service "A" with Castle injected proxy to -> Service "B"
Simple right? Works without issue IF, and that's a big if, the Service "B" host is up and running.
The behavior I have seen and can reproduce on demand is that if Service "B" is down, the call chain completes without any hint that there is any issue. To say it another way, there is no resolution exception thrown by Castle nor any WCF exception. I have isolated this to how IsOneWay=true operations are handled.
This is a major issue because you think that everything has executed correctly but in reality none of your code has been executed!
Is this expected behavior? Is there away I can turn on some option in Castle so that it will throw and exception when a WCF Client proxy is the resolved dependency? Some other option?
One more note, the only clue that you have that is issue is occurring is when/if you do a Container.Release() on the client proxy as it throws an exception. This can't be depended upon thou for various reasons not worth getting into here.
Thank!
Additionally below is the code that recreates this issue. To run it
1) Create a new Unit Test project in Visual Studio
2) Add the Castle Windsor WCF Integration Facility via NuGet
3) Paste the code from below into a .cs file, everything is in one to make it easy.
4) Run the two unit tests, SomeOperation_With3Containers_NoException() works as the dependency service (Service "B" from above) is running. SomeOperation_With2Containers_NoException() fails are the .Release
5) Set break points and you can see that no code is hit in the implementations.
****UPDATE****: The primary way this needs to be handled is with an IErrorHandler implantation (As mentioned by Roman in the comments below). Details and an example can be found here: http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.110).aspx
Use this implementation to log any exception on the One Way operation and use that data to take the appropriate action.
using Castle.Facilities.WcfIntegration;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace UnitTestProject1
{
[ServiceContract]
public interface IServiceContractA
{
[OperationContract(IsOneWay = true)]
void SomeOperation();
}
[ServiceContract]
public interface IServiceDependancy
{
[OperationContract]
void SomeOperation();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ServiceContractAImplementation : IServiceContractA
{
private IServiceDependancy ServiceProxy;
public ServiceContractAImplementation() { }
public ServiceContractAImplementation(IServiceDependancy dep)
{
ServiceProxy = dep;
}
public void SomeOperation()
{
ServiceProxy.SomeOperation();
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ServiceDependancyImplementation : IServiceDependancy
{
public void SomeOperation()
{
//do nothing, just want to see if we can create an instance and hit the operation.
//if we need to do something, do something you can see like: System.IO.File.Create(#"d:\temp\" + Guid.NewGuid().ToString());
}
}
public class ServiceCastleInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero);
var returnFaults = new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true, HttpHelpPageEnabled = true };
container.Register(Component.For<IServiceBehavior>().Instance(returnFaults));
//local in-proc service hosting
var namedPipeBinding = new NetNamedPipeBinding();
//it works using Named Pipes
var serviceModelPipes = new DefaultServiceModel().AddEndpoints(
WcfEndpoint.BoundTo(namedPipeBinding).At("net.pipe://localhost/IServiceContractA")
).Discoverable();
container.Register(Component.For<IServiceContractA>()
.ImplementedBy<ServiceContractAImplementation>()
.LifeStyle.PerWcfOperation()
.AsWcfService(serviceModelPipes)
);
//our service (IServiceContractA) has a dependancy on another service so needs a client to access it.
container.Register(Castle.MicroKernel.Registration.Component.For<IServiceDependancy>()
.AsWcfClient(WcfEndpoint.BoundTo(namedPipeBinding)
.At(#"net.pipe://localhost/IServiceDependancy")).LifeStyle.Transient);
}
}
public class ServiceDependancyCastleInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero);
var returnFaults = new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true, HttpHelpPageEnabled = true };
container.Register(Component.For<IServiceBehavior>().Instance(returnFaults));
//local in-proc service hosting
var namedPipeBinding = new NetNamedPipeBinding();
var serviceModel = new DefaultServiceModel().AddEndpoints(
WcfEndpoint.BoundTo(namedPipeBinding).At("net.pipe://localhost/IServiceDependancy")
).Discoverable();
container.Register(Component.For<IServiceDependancy>()
.ImplementedBy<ServiceDependancyImplementation>()
.LifeStyle.PerWcfOperation()
.AsWcfService(serviceModel)
);
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void SomeOperation_With3Containers_NoException()
{
//setup the container that is going to host the service dependancy
using (var dependancyContainer = new WindsorContainer().Install(new ServiceDependancyCastleInstaller()))
{
//container that host the service that the client will call.
using (var serviceContainer = new WindsorContainer().Install(new ServiceCastleInstaller()))
{
//client container, nice and simple so doing it in the test here.
using (var clientContainer = new WindsorContainer())
{
clientContainer.AddFacility<WcfFacility>();
var endpoint = WcfEndpoint.BoundTo(new NetNamedPipeBinding())
.At("net.pipe://localhost/IServiceContractA");
clientContainer.Register(Castle.MicroKernel.Registration.Component.For<IServiceContractA>()
.AsWcfClient(endpoint).LifeStyle.Transient);
var proxy = clientContainer.Resolve<IServiceContractA>();
proxy.SomeOperation();
clientContainer.Release(proxy);
}
}
}
}
[TestMethod]
public void SomeOperation_With2Containers_NoException()
{
//this one fails.
// this test omits the dependancy that the IServiceContractA has
//Note that all seems to work, the only hint you have that it doesnt
//is the .Release call throws and exception.
//container that host the service that the client will call.
using (var serviceContainer = new WindsorContainer().Install(new ServiceCastleInstaller()))
{
//client container, nice and simple so doing it in the test here.
using (var clientContainer = new WindsorContainer())
{
clientContainer.AddFacility<WcfFacility>();
var endpoint = WcfEndpoint.BoundTo(new NetNamedPipeBinding())
.At("net.pipe://localhost/IServiceContractA");
clientContainer.Register(Castle.MicroKernel.Registration.Component.For<IServiceContractA>()
.AsWcfClient(endpoint).LifeStyle.Transient);
var proxy = clientContainer.Resolve<IServiceContractA>();
//this call seems like it works but any break points
//in code don't get hit.
proxy.SomeOperation();
//this throws and exception
clientContainer.Release(proxy);
}
}
}
}
}
One way operations exists for the purpose of "Fire and forget" scenarios. You don't care about result, whether it was successful or not. You don't have to wait for the server to respond (only the initial TCP handshake if it's HTTP binding). By using one way operations, the client only gets the confidence that the server received the message successfully on the wire, and the server makes no guarantees that it will succeed in processing the message. This is true in HTTP protocol. In other protocols, like Microsoft MSMQ, or IBM MQ, the server doesn't even need to be online at the same time as the client.
In your scenario, The client does not receive an exception, because service A is up and running. If service A was down, you would have seen an error (Again, assuming HTTP, or in your case .net pipe). The condition of service B does not matter, because service B is an implementation detail of service A, and your client doesn't care about service A return values. If your were to debug service A (by attaching to it) while service B is down, you would have seen first chance, and maybe even unhandled exceptions (depending on the implementation of service A).
Castle should not have thrown an exception anyway, because it has successfully resolved a proxy for service B in service A. The fact that service B is down is no concern of Castle, or any other DI container for that matter.