Why Asynchronous Messaging? Synchronous vs MQ Comparison
In synchronous HTTP integration, every request waits for the consumer to respond. When 1000 concurrent requests arrive, the thread pool is exhausted and timeouts cascade. Anypoint MQ solves this problem through producer-consumer decoupling.
| Criterion | Synchronous HTTP | Anypoint MQ (Asynchronous) |
|---|---|---|
| Behavior under load | Timeout & error | Messages queue up |
| Producer-Consumer dependency | Tightly coupled (tight coupling) | Loosely coupled (loose coupling) |
| Data loss on failure | High risk | Protected with DLQ |
| Scaling | Vertical scaling required | Increase consumer count |
| Latency | Low (ms) | Moderate (seconds range) |
When should you use MQ? If an immediate response is not critical, the consumer is slow, or the load is uneven — MQ is the right choice. For scenarios where an instant user response is required, synchronous may be preferred.
Producer Flow: Publishing Messages
On the producer side, the Anypoint MQ Publish component is used. Message body, correlation ID, and priority properties are passed via headers.
<!-- Producer Flow: HTTP → Anypoint MQ --> <flow name="order-producer-flow"> <http:listener path="/orders" allowedMethods="POST" config-ref="HTTP_Listener_config"/> <!-- Validate the message --> <validation:is-not-null value="#[payload.orderId]" message="orderId zorunludur"/> <!-- Publish to MQ --> <anypoint-mq:publish config-ref="Anypoint_MQ_Config" destination="orders-exchange" messageId=#[payload.orderId]> <anypoint-mq:properties> <anypoint-mq:property key="priority" value=#[payload.priority default 'NORMAL']/> <anypoint-mq:property key="correlationId" value=#[correlationId]/> </anypoint-mq:properties> </anypoint-mq:publish> <!-- Return 202 Accepted immediately --> <set-payload value=#[output application/json --- {status: "queued", messageId: payload.orderId}]/> <http:response statusCode="202"/> </flow>
202 Accepted immediately. No matter how slowly the consumer processes, the client does not wait and does not experience timeouts.Consumer Flow: Consuming Messages and Idempotency
On the consumer side, the anypoint-mq:subscriber component is used. In high-traffic scenarios, idempotency control with Object Store is essential to prevent the same message from being processed twice.
<flow name="order-consumer-flow"> <anypoint-mq:subscriber config-ref="Anypoint_MQ_Config" destination="orders-queue" maxConcurrency="10" ackMode="MANUAL"/> <!-- Idempotency: don't process the same message twice --> <os:retrieve config-ref="ObjectStore_Config" key=#[attributes.messageId] target="alreadyProcessed" defaultValue="false"/> <choice> <when expression=#[vars.alreadyProcessed == false]> <flow-ref name="process-order-subflow"/> <os:store config-ref="ObjectStore_Config" key=#[attributes.messageId] value="true" ttl="86400" ttlUnit="SECONDS"/> <anypoint-mq:ack ackToken=#[attributes.ackToken]/> </when> <otherwise> <!-- Duplicate: ACK only, skip processing --> <logger message="Duplicate mesaj atlandı: #[attributes.messageId]"/> <anypoint-mq:ack ackToken=#[attributes.ackToken]/> </otherwise> </choice> </flow>
- ✓maxConcurrency="10" — 10 messages are processed in parallel at once, distributing the load
- ✓ackMode="MANUAL" — ACK is sent when processing succeeds; on error, NACK returns the message to the queue
- ✓Object Store TTL — the idempotency record is deleted after 24 hours, preventing memory growth
Error Handling: Dead Letter Queue (DLQ)
Under heavy traffic, some messages cannot be processed — due to connection drops, data errors, or downstream system failures. DLQ captures these messages without losing them and puts them back for reprocessing via a retry mechanism.
<flow name="order-consumer-flow"> <anypoint-mq:subscriber destination="orders-queue" ackMode="MANUAL"/> <try> <flow-ref name="process-order-subflow"/> <anypoint-mq:ack ackToken=#[attributes.ackToken]/> <error-handler> <!-- Transient error: NACK, returns to queue --> <on-error-continue type="CONNECTIVITY, TIMEOUT"> <logger message="Geçici hata, NACK: #[error.description]" level="WARN"/> <anypoint-mq:nack ackToken=#[attributes.ackToken]/> </on-error-continue> <!-- Permanent error: send directly to DLQ --> <on-error-continue type="VALIDATION, TRANSFORMATION"> <anypoint-mq:publish destination="orders-dlq"> <anypoint-mq:properties> <anypoint-mq:property key="errorReason" value=#[error.description]/> <anypoint-mq:property key="originalMessageId" value=#[attributes.messageId]/> </anypoint-mq:properties> </anypoint-mq:publish> <anypoint-mq:ack ackToken=#[attributes.ackToken]/> </on-error-continue> </error-handler> </try> </flow>
Message Transformation with DataWeave
Messages arriving via MQ typically require format transformation between different systems. DataWeave is the cleanest way to perform this transformation within the consumer flow.
// Order from MQ → transformation to SAP format %dw 2.0 output application/xml var priorityMap = { "HIGH": "01", "NORMAL": "02", "LOW": "03" } --- SALESORDER: { HEADER: { ORDER_ID: payload.orderId, CUSTOMER: payload.customerId, PRIORITY: priorityMap[attributes.properties."priority" default "NORMAL"], CREATED_AT: now() as String { format: "yyyyMMddHHmmss" }, EXT_REF: attributes.properties."correlationId" default "" }, ITEMS: { (payload.items map (item, idx) -> { ITEM: { LINE_NO: (idx + 1) * 10, MATERIAL: item.sku, QTY: item.quantity, UNIT: item.unit default "EA" } }) } }
- ✓Header values are accessed via MQ attributes.properties — it doesn't pollute the payload
- ✓The default operator provides null-safe mapping — no NullPointerException on malformed messages
- ✓SAP date format yyyyMMddHHmmss — converted in DataWeave with
as String {format: ...}
Performance Optimization: Which Settings Make a Difference?
The right configuration in an Anypoint MQ subscriber dramatically impacts throughput. The following parameters are critical for production environments.
<anypoint-mq:subscriber config-ref="Anypoint_MQ_Config" destination="orders-queue" <!-- How many messages to process in parallel --> maxConcurrency="20" <!-- How many messages to fetch per polling (1-10) --> fetchSize="10" <!-- How long to wait if queue is empty (ms) --> pollingTime="1000" <!-- Message processing timeout --> acknowledgementTimeout="60000" <!-- Manual ACK mode --> ackMode="MANUAL"/>
| Parameter | Low Traffic | High Traffic | Description |
|---|---|---|---|
| maxConcurrency | 5 | 20–50 | Adjusted based on CloudHub worker vCPU |
| fetchSize | 1–3 | 10 | Number of messages fetched at once |
| pollingTime | 5000 ms | 500–1000 ms | Keeping it low reduces latency but increases cost |
| ackTimeout | 30 s | 60–120 s | Add processing time + buffer |




