MassTransit Mediator: Payload added by Send/Publish not found in ConsumeContext - masstransit

I'm using MassTransit mediator to Send/Post messages to my consumers, however I'd like to get some metadata delivered to the consumers as a payload, i.e.
await mediator.Publish(message, ctx => ctx.GetOrAddPayload(() => metadata));
I've looked into MassTransit's code and it seems the payloads from MessageSendContext<T> are not copied to MediatorReceiveContext<T>
So my question is if it is by design or it was somehow overlooked passing the payloads to MediatorReceiveContext<T> .ctor as it has a parameter params object[] payloads which might perfectly do the job.

This is by design, as coupling between the producer and consumer would introduce inconsistent expectations since payloads are not transferred between producers and consumers with buses. If you need to pass data to a consumer, it should be done using headers.

Related

Event Driven Microservices: How to send protobuf messages of different types to one AWS SNS(stream)?

I am planning to develop an Event-Driven Microservices.
I create a protobuf project, which defines several types of messages.
EmployeeMessage
UserMessage
ProcessMessage
ApplicantMessage
Then, I compile the protobuf project to different languages, e.g. Ruby, Golang.
Then, the upstream application will push the following type of events to the SNS, the SNS fanout message to multiple SQS, which owned by the different downstream consumers.
Then, the downstream application consumes messages from SQS.
Here is a diagram to show the whole architecture.
When implementing it, I realize there is no way. Protobuf messages of different types are posted to SNS, the consumer doesn't know the type of each message and is not able to decode them.
Questions
How do you implement your Event-Driven microservices? Does each type of message have its own SNS (stream)?
Is there a way to allow me to push different message types to the same SNS (stream)? Do I need to append the message type in front of the payload?
In your protobuf schema, design the message to be sent to SNS like so:
message Event {
oneof request {
EmployeeMessage employee_message = 1;
UserMessage user_message = 2;
// etc
}
}
Then have your receivers decode Event and check for the correct message type.
I think this answers both of your questions.
A couple of options:
Use a composition of SNS/SQS Topic for each targeted language. In your application building the protobufs, manage a mapping in SSM/AppConfig. Pros: Reduce Costs, SNS/SQS both charge for messages handled. Cons: Increase infrastructure wrangling. The map generation is easy to do in CFN automatically.
Example: RubySNS/RubySQS --> RubyApp
Store an attribute in the protobuf or use SNS Message Attributes to signal language destination. Message attributes may be cleaner for all SQS workers. Pro: Aligns with your current design if there are constraints forcing this. Cons: Requires the downstream workers to have some knowledge of the message.

Send TraceId across Threads

We have a distributed application following microservice Architecture. In one of our microservice we are following producer-consumer pattern.
The producer receives requests, persists it to database, pushes the request into a BlockingQueue and sends the response back to the client. The consumer running on a separate thread is listening to the blocking queue. The moment it gets the request object it performs specific operations on it.
The request received by the producer is persisted to the database asynchronously using CompleteableFutures.
The problem here is how to forward TraceId to the methods processing the requestObject inside consumer thread. Since the consumer thread might process these objects much later after the response is sent to the consumer.
Also how to forward the traceId across Asynchronous calls?
Thanks
That's an interesting question. I think that what you can do is to persist the request together with its headers. Then on the consumer side you can use the SpanExtractor interface in a similar way as we do here - https://github.com/spring-cloud/spring-cloud-sleuth/blob/v1.3.0.RELEASE/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java#L351 (Span parent = spanExtractor().joinTrace(new HttpServletRequestTextMap(request));). That means that from the HttpServletRequest we're extracting values to build a span. Then, once you've retrieved the Span, you can just use Tracer#continueSpan(Span) method before processing, and then Tracer#detach(Span) in the finally block. E.g.
Span parent = spanExtractor().joinTrace(new HttpServletRequestTextMap(request));
try {
tracer.continueSpan(parent);
// do whatever you need
} catch(Exception e) {
tracer.addTag("error", doSthWithTheExceptionMsg(e));
} finally {
tracer.detach(parent);
}

Strategy for passing same payload between messages when optional outbound gateways fail

I have a workflow whose message payload (MasterObj) is being enriched several times. During the 2nd enrichment () an UnknownHostException was thrown by an outbound gateway. My error channel on the enricher is called but the message the error-channel receives is an exception, and the failed msg in that exception is no longer my MasterObj (original payload) but it is now the object gotten from request-payload-expression on the enricher.
The enricher calls an outbound-gateway and business-wise this is optional. I just want to continue my workflow with the payload that I've been enriching. The docs say that the error-channel on the enricher can be used to provide an alternate object (to what the enricher's request-channel would return) but even when I return an object from the enricher's error-channel, it still takes me to the workflow's overall error channel.
How do I trap errors from enricher's + outbound-gateways, and continue processing my workflow with the same payload I've been working on?
Is trying to maintain a single payload object for the entire workflow the right strategy? I need to be able to access it whenever I need.
I was thinking of using a bean scoped to the session where I store the payload but that seems to defeat the purpose of SI, no?
Thanks.
Well, if you worry about your MasterObj in the error-channel flow, don't use that request-payload-expression and let the original payload go to the enricher's sub-flow.
You always can use in that flow a simple <transformer expression="">.
On the other hand, you're right: it isn't good strategy to support single object through the flow. You carry messages via channel and it isn't good to be tied on each step. The Spring Integration purpose is to be able to switch from different MessageChannel types at any time with small effort for their producers and consumers. Also you can switch to the distributed mode when consumers and producers are on different machines.
If you still need to enrich the same object several times, consider to write some custom Java code. You can use a #MessagingGateway on the matter to still have a Spring Integration gain.
And right, scope is not good for integration flow, because you can simply switch there to a different channel type and lose a ThreadLocal context.

What's the best way to implement a Request/Reply pattern if no temporary queues are available?

I have many instances of my client application. These clients send requests to a server application via messaging and receive a reply. Normally the reply would be sent using a temporary queue.
Unfortunately I have to use the Stomp protocol which has no concept of temporary queues or topics. (Although the message broker has)
What's the best way to ensure only the original requestor receives the reply? Are there any best-practices for this unfortunate situation?
The customary solution when several requestors listen for replies on the same queue is to use correlation IDs to select messages. On the client side it looks like this:
Place a message on the request queue and commit.
Retrieve the JMSMessageID from the outbound message (the value is determined by the broker and updates the message object as a result of the send).
Receive a message from the reply queue specifying the JMSMessageID from the outbound message as the correlation ID in the selector.
Process and commit.
On the server side it looks like this:
Receive a message under syncpoint.
Process the request and prepare the response.
Set the JMSCorrelationID on the response to the value of JMSMessageID from the request.
Send the message.
Commit.
The consumer would set the selector something like this: activemq.selector:JMSCorrelationID=.
Since the broker creates a message ID that is supposed to be globally unique, the pattern of using it as the correlation ID prevents collisions that are possible when each requestor is allowed to specify it's own value.
The best way to implement this pattern with JMS (that I've found, anyway) is to create a pre-configured topic for the response messages, and use correlation selectors on the response message so that the client can get the correct one.
In more detail, this means setting a random ID on the request message (using setJMSCorrelationID()), and putting that message on the request Queue. The consumer of that request message processes it, creates the response message, sets the same correlation ID on the response message, and puts it on the response Topic. The client, meanwhile, is listening on the response topic with a selector expression which specifies the correlation ID that it's expecting.
The danger is that the response message is sent before the client can get around to listening for it, although that's probably unlikely. You can try using a pre-configured Queue for the responses rather than a topic, but I've found that topics tend to work more reliably (my JMS provider of choice is HornetQ - your mileage may vary).
All this is tell me that JMS is a very poor fit for the request/response model. The API just doesn't support it properly. This is hardly surprising, since that was never a use-case for JMS.
Something like a compute grid (Terracotta, Gigaspaces, Infinispan, etc) would likely yield better results, but that's not really an option for you.

Query regarding the java message queue

I have a design query regarding queues. My scenario is as follows:
I have to use a messaging system, with single producer and multiple consumers (asynchronous). The producer pushes different types of messages into the messaging system. Depending upon the message type, that particular consumer has to consume that message. (Each consumer is running on a different server). If one consumer is down and a message comes for that consumer, it will be in the messaging system only. If I use a message queue, the message in the queue will block the next messages that can be consumed by the other consumers. Are queues suitable for handling this kind of situation? Or do we need to go for a topic?
Whether you use a queue or a topic should depend on whether there an instance where multiple consumers must process the same message. If that is the case then a topic is required do generate that one-to-many pattern.
On the other hand, if any one message will only ever be consumed by one consumer, then you can use a queue or topic and the consumers specify the message type as a JMS selector. In this way, all consumers can listen on the same queue and each selects a different subset of messages. In the event one application is not there, it's messages do not "block the next messages that can be consumed by the other consumers" but rather they just stack up in the queue and other consumers still receive their messages based on selection criteria.
Please also realize that queues are lightweight constructions and you can easily have one queue per consumer. Typically, things providing a service listen on a well-known queue and each queue represents a different function of the service or a different service. Thus there may be many service input queues. Similarly, reply messages are generally uniquely addressed to the application instance that made the request and go to a unique, often dynamic, reply-to queue. Both of these implementations I have described lead to a separation of traffic across queues rather than pooling different message types into the same queue. Since JMS selectors always impart an additional processing cost, using more queues is generally more performant than selecting many types of message from the same queue.
I am responding to your question about selectors in the comment section here since I have more space and can put links in...
Section 3.8.1 of the JMS 1.1 spec states:
A JMS message selector allows a client to specify, by message header, the
messages it’s interested in. Only messages whose headers and properties
match the selector are delivered. The semantics of not delivered differ a bit
depending on the MessageConsumer being used. See Section 5.8,
“QueueReceiver,” and Section 6.11, “TopicSubscriber,” for more details.
Message selectors cannot reference message body values.
A message selector matches a message if the selector evaluates to true when
the message’s header field and property values are substituted for their
corresponding identifiers in the selector.
As noted above, selectors can be on fields that are implicit in the message such as MsgID or CorrelationID or thsey can be on fields specifically set by the message producer such as a message property. Either way, the client must specify the value of any selectors used by the message consumer.

Resources