I have 3 micro-services as following communicating via masstransit/rabbitmq
I'd like that the Api makes request to the TransactionService but got the response from the PspService.
namespace API
{
//I wish to have something like this
PaymentForm form = await requestClient.GetResponse<PaymentForm>(new CreatePayment())
}
namespace TransactionService
{
public class CreatePaymentConsumer : IConsumer<CreatePayment>
{
public async Task Consume(ConsumeContext<CreatePayment> context)
{
context.Send<BuildPaymentForm>()
}
}
}
namespace PspService
{
public class BuildPaymentFormConsumer : IConsumer<BuildPaymentForm>
{
public async Task Consume(ConsumeContext<BuildPaymentForm> context)
{
context.Response<PaymentForm>() //how to make sure that the response will be sent to the API, but not to the caller (TransactionService)?
}
}
}
Please point me to the right way to make this communication pattern or a similar example, or the right part in the documentation.
You can copy the RequestId and ResponseAddress from the CreatePayment message to the message produced by the first consumer using the CreateCopyContextPipe method. There is also a built-in way to copy all headers to the outgoing message (which I've used below).
namespace TransactionService
{
public class CreatePaymentConsumer : IConsumer<CreatePayment>
{
public async Task Consume(ConsumeContext<CreatePayment> context)
{
var pipe = context.CreateCopyContextPipe();
await _otherHost.Publish(new BuildPaymentForm(...), pipe);
}
}
}
namespace PspService
{
public class BuildPaymentFormConsumer : IConsumer<BuildPaymentForm>
{
public async Task Consume(ConsumeContext<BuildPaymentForm> context)
{
await context.RespondAsync(new PaymentForm(...));
}
}
}
The first consumer will publish the command to the second consumer, copying all headers to the outbound message. The second consumer will then respond to the request initiator.
Related
I'd like to send a signal from my C# API code, probably using the SendSignal activity to a workflow that will receive that signal using the SignalReceived activity.
To send a signal to a workflow from your own code, such as a controller or any other class, you can use the ISignaler service.
Here's an example:
[ApiController]
[Route("myapi")]
public class MyApi: Controller
{
private readonly ISignaler _signaler;
public MyApiController(ISignaler signaler)
{
_signaler = signaler;
}
[HttpPost("send-signal")]
public async Task<IActionResult> SendSignal(CancellationToken cancellationToken)
{
var startedWorkflows = await _signaler.TriggerSignalAsync("MySignal", cancellationToken: cancellationToken);
return Ok(startedWorkflows);
}
}
A complete sample project can be found here.
I'm having some difficulty with registering some open generic types with simple injector.
In startup I am registering in the following way..
var assemblies = LoadAssemblies("MyProjectName");
_container.Register(typeof(IMessageProcessor<>), assemblies, Lifestyle.Singleton);
When I try to get an instance one of them is missing. If I try to explicitly register the instance I get an error that it is already registered so i'm a bit lost as to what is happening.
I have checked the classes and they all look the same and exist in the same assembly.
The interfaces look like...
public interface IBaseMessageProcessor
{
Task Process<T>(T message);
}
public interface IMessageProcessor<T> : IBaseMessageProcessor where T : BaseMessage
{
Task Process(T message);
}
There are multiple examples that implement these interfaces that look like..
public interface IAssignToMultipleMessageProcessor : IMessageProcessor<AssignToMultipleMessage> { }
public interface IReturnBenefitMessageProcessor : IMessageProcessor<ReturnBenefitMessage> { }
public interface IAssignWelcomeBenefitsMessageProcessor : IMessageProcessor<AssignWelcomeBenefitsMessage> { }
In total there are six like IMessageProcessor<> where the Messages all inherit from BaseMessage
The concerete example of the above three look like this..
public class AssignToMultipleMessageProcessor : IAssignToMultipleMessageProcessor
{
public async Task Process(AssignToMultipleMessage message)
{
await Task.CompletedTask;
}
public async Task Process<T>(T message)
{
if (message is AssignToMultipleMessage assignToMultipleMessage)
{
await Process(assignToMultipleMessage);
}
}
}
public class ReturnBenefitMessageProcessor : IReturnBenefitMessageProcessor
{
public async Task Process(ReturnBenefitMessage message)
{
await Task.CompletedTask;
}
public async Task Process<T>(T message)
{
if (message is ReturnBenefitMessage assignToMultipleMessage)
{
await Process(assignToMultipleMessage);
}
}
}
public class AssignWelcomeBenefitMessageProcessor : IAssignWelcomeBenefitsMessageProcessor
{
public async Task Process(AssignWelcomeBenefitsMessage message)
{
await Task.CompletedTask;
}
public async Task Process<T>(T message)
{
if (message is AssignWelcomeBenefitsMessage assignWelcomeBenefitsMessage)
{
await Process(assignWelcomeBenefitsMessage);
}
}
}
When I debug the startup the container shows all six instances are in the LifeStyleRegistrationCache of the container as Singletons.
Later when trying to get the instance...
var instance = scope.GetInstance<IAssignWelcomeBenefitsMessageProcessor>();
All six are still present in the LifeStyleResistrationCache as Singletons but the error is thrown
No registration for type IAssignWelcomeBenefitsMessageProcessor could be found.
In the LifeStyleRegistrationCache it shows {[{Name = AssignWelcomeBenefitMessageProcessor FullName = MyAppName.MessageProcessing.AssignWelcomeBenefitMessageProcessor}, {System.WeakReference}]}
The SerializationStackTraceString says...
at SimpleInjector.Container.ThrowMissingInstanceProducerException(Type type)\r\n at SimpleInjector.Container.GetInstanceFromProducer(InstanceProducer instanceProducer, Type serviceType)\r\n at SimpleInjector.Container.GetInstanceForRootType(Type serviceType)\r\n at SimpleInjector.Container.GetInstance(Type serviceType)\r\n at SimpleInjector.Scope.GetInstance(Type serviceType)\r\n at SimpleInjector.Scope.GetInstance[TService]()\r\n
All the other five instances can be successfully resolved in the same way but this one is failing. Would really appreciate some help on this if anyone can point me in the right direction.
Cheers :)
im new to Akka.net, im running on linux and using .NET Core 3.1, i have written a very simple code but it does not work and i dont know why.
this my program.cs where i created the ActorSystem and simply called another actor
using Akka.Actor;
namespace PBFT
{
class Program
{
static void Main(string[] args)
{
var MyActorSystem = ActorSystem.Create("ActorSystem");
var Primary = MyActorSystem.ActorOf<PrimaryActor>();
Primary.Tell("Test");
}
}
}
and this is the first actor that is supposed to receive the message and simply outputs it to the console
using Akka.Actor;
using Akka;
using Akka.Event;
namespace PBFT
{
class PrimaryActor : ReceiveActor
{
private readonly ILoggingAdapter log = Context.GetLogger();
public PrimaryActor()
{
Receive<string>(message => System.Console.WriteLine(message));
}
}
}
the problem is there are no errors and the message does not get processsed by the Actor, am i missing something ?
Messages to and between actors in Akka.NET are passed asynchronously. What happens in your example, is that you're Telling a message to an actor and exiting the program immediately afterwards, before actor got a chance to process the message.
You can either suspend main thread (using eg. Console.ReadLine()) in your example, or - if you need to be sure that actor had processed the message before going forward - use combination of actor.Ask(message, cancellationToken) on the caller side (which will return Task, that completes once actor sends a response back) and Sender.Tell(response) inside your actor's receive method:
class PrimaryActor : ReceiveActor
{
private readonly ILoggingAdapter log = Context.GetLogger();
public PrimaryActor()
{
Receive<string>(message => {
System.Console.WriteLine(message);
Sender.Tell(new object()); // or any other response you want
});
}
}
class Program
{
static async Task Main(string[] args)
{
var MyActorSystem = ActorSystem.Create("ActorSystem");
var Primary = MyActorSystem.ActorOf<PrimaryActor>();
await Primary.Ask<object>("Test", default(CancellationToken));
}
}
Is it possible to share a queue between 2 or more stateful services, or do I need to directly call it via tcp/http to put a message on its own internal queue?
For example; say I have my first service that puts an order on a queue based on a condition:
public sealed class Service1 : StatefulService
{
public Service1(StatefulServiceContext context, IReliableStateManagerReplica reliableStateManagerReplica)
: base(context, reliableStateManagerReplica)
{ }
protected override async Task RunAsync(CancellationToken cancellationToken)
{
var customerQueue = await this.StateManager.GetOrAddAsync<IReliableQueue<Order>>("orders");
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
using (var tx = this.StateManager.CreateTransaction())
{
if (true /* some logic here */)
{
await customerQueue.EnqueueAsync(tx, new Order());
}
await tx.CommitAsync();
}
}
}
}
Then my second service reads from that queue and then continues the processing.
public sealed class Service2 : StatefulService
{
public Service2(StatefulServiceContext context, IReliableStateManagerReplica reliableStateManagerReplica)
: base(context, reliableStateManagerReplica)
{ }
protected override async Task RunAsync(CancellationToken cancellationToken)
{
var customerQueue = await this.StateManager.GetOrAddAsync<IReliableQueue<Order>>("orders");
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
using (var tx = this.StateManager.CreateTransaction())
{
var value = await customerQueue.TryDequeueAsync(tx);
if (value.HasValue)
{
// Continue processing the order.
}
await tx.CommitAsync();
}
}
}
}
I can't see much within the documentation on this, I can see that GetOrAddAsync method can take in a uri but I've seen no examples on how this works or if you can even do cross services?
The idea behind this is to split up the processing on to separate queues so that we don't get in a inconsistent state when we try to re-try a message.
There's no way to share state across services. The statemanager acts on a service partition level.
You could use an external queue for this purpose, like Service Bus.
You could also invert control, by using an Event Driven approach. Service 1 would raise an event, that Service 2 would use as a trigger to continue processing. The data to process could be inside the event, or data stored in another location, referenced to from the event.
I'm building a hub which should send message to specific user and this Question and this Question would work perfectly, only I can't find AddToGroup method, I know a lot things have changed in version 0.5, is this one of them?
Yes, there's a Groups property now on the Hub class that you could use to add users to. The documentation also illustrates this:
public class MyHub : Hub, IDisconnect
{
public Task Join()
{
return Groups.Add(Context.ConnectionId, "foo");
}
public Task Send(string message)
{
return Clients["foo"].addMessage(message);
}
public Task Disconnect()
{
return Clients["foo"].leave(Context.ConnectionId);
}
}