Throttling of Long sms using camel-smpp - apache-camel

I want to know how to do throttling on outgoing SMS's on camel-smpp endpoint with long sms .Long SMS breaks down smaller with max size of 160 chars. if i use camel throttling , it is applied on incoming messages instead of outgoing sms. But the behavior i need is throttling on outgoing sms.
for example of my route is like :
<route id="firstsmsc" >
<from uri="seda:firstsmsc"/>
<throttle timePeriodMillis="1000">
<constant>8</constant>
<to uri=" {{smsc1.protocol}}://{{smsc1.username.producer}}#{{smsc1.host.mt}}: {{smsc1.port}}?password={{smpp1.enc.producerpassword}}&enquireLinkTimer=30000&transactionTimer=5000&systemType={{smsc1.systemType}}&sourceAddrTon={{CamelSmppSourceAddrTon1}}&destAddrTon={{CamelSmppDestAddrTon1}}&sourceAddrNpi={{CamelSmppSourceAddrNpi1}}&destAddrNpi={{CamelSmppDestAddrNpi1}}&typeOfNumber={{CamelSmppSourceAddrTon1}}&numberingPlanIndicator={{CamelSmppSourceAddrNpi1}}&lazySessionCreation=true"/>
</throttle>
</route>

If either the smpp component or the SMSC breaks down your message payload into chunks of 160 characters, then throttling like you propose will not help you. From a Camel perspective there is just one exchange, not many. Maybe you can try and split your exchange payload by 160 characters and then throttle the resulting exchanges. Something along the lines of
<route id="firstsmsc" >
<from uri="seda:firstsmsc"/>
<split streaming=false, parallelProcessing="false" >
<camel:bean ref="SMSSplitter" />
<throttle timePeriodMillis="1000">
<to uri="{{smsc1.protocol}}://{{smsc1.username.producer}}..."/>
</throttle>
</split>
</route>

Related

How can I make only one instance of an application receive each AMQP topic message (consumer group behaviour)

I am using Apache Camel's AMQP component to listen to messages from an ActiveMQ Artemis topic.
This application is run on Kubernetes with two replicas.
I've configured a durable subscription with a unique clientId per pod and a common subscription name:
<route autoStartup=true" id="myRoute">
<from id="_amqp_topic" uri="amqp:topic:xxx?connectionFactory=#amqpCF&disableReplyTo=true&transacted=false&subscriptionDurable=true&clientId={{container-id}}&durableSubscriptionName=eventSubscription"/>
<log loggingLevel="INFO" message="Received event: ${body}"/>
...
</route>
The problem is both pods receive the message, when only one of them should. I'm trying to achieve something similar to Kafka's consumer groups, where only one member of the group receives each message.
If you want only one subscriber to receive the message then the subscribers must share the same subscription. Therefore, you need to:
set subscriptionShared=true in your _amqp_topic uri
use the same client ID (i.e. don't use {{container-id}})
For example:
<route autoStartup=true" id="myRoute">
<from id="_amqp_topic" uri="amqp:topic:xxx?connectionFactory=#amqpCF&disableReplyTo=true&transacted=false&subscriptionDurable=true&clientId=myClientID&durableSubscriptionName=eventSubscription&subscriptionShared=true"/>
<log loggingLevel="INFO" message="Received event: ${body}"/>
...
</route>
Another option would simply to use a queue instead of a topic, e.g.:
<route autoStartup=true" id="myRoute">
<from id="_amqp_queue" uri="amqp:queue:xxx?connectionFactory=#amqpCF&disableReplyTo=true&transacted=false"/>
<log loggingLevel="INFO" message="Received event: ${body}"/>
...
</route>

Apache Camel AMQP with selector is consuming any message

I am trying to use selectors on an amqp with Azure Service Bus consumer. However, for some reason the route is also consuming messages that do not match the selector.
Here's an example:
This route generates messages and append a header:
<route id="MessageGenerator">
<from uri="timer:generator?delay=5000&period=5000"/>
<setHeader headerName="INSTANCE_ID">
<simple>{{env:INSTANCE_ID}}</simple>
</setHeader>
<to uri="amqp:queue:external_queue" />
</route>
While this route should consume only those that contain INSTANCE_ID matching 2 possible values: env:INSTANCE_ID or Any.
<route id="ExternalConsumer">
<from uri="amqp:queue:external_queue?selector=INSTANCE_ID IN ('{{env:INSTANCE_ID}}', 'Any')"/>
<log message="{{env:INSTANCE_ID}} consumed message with Instance ID: ${header.INSTANCE_ID}" logName="AMQP_TEST" loggingLevel="INFO"/>
</route>
But the the log shows that it is consuming any message, regardless of the selector specifying which ones.
Am I missing something?
Thanks!
Issue here was that Azure Service Bus does NOT support selectors on queues. I switched to topics, which already have filters per subscription.

Camel + sql + error handling

I have a task to handle all incoming messages within the route and save them to database.
My route starts with webservice (camel-cxf), and then process to ejb and return soap response.
I've decided to use camel-sql component. As far as I don't have any problems with logging incoming message:
<camelContext id="InstitutionContext" xmlns="http://camel.apache.org/schema/spring">
<onException>
<exception>org.example.MyException</exception>
<continued><constant>true</constant></continued>
<to uri="bean:myExceptionHandler?method=handle" />
</onException>
<route id="InstitutionRoute" >
<from uri="direct:start" />
<to uri="sql:insert into translog(id,type,data) values(2,'IN',#)" />
<split>
<tokenize token="\n" />
<unmarshal>
<csv delimiter=";" />
</unmarshal>
<process ref="InstitutionProcessorTest" />
</split>
<to uri="bean:myExceptionHandler?method=checkErrors" />
</route>
</camelContext>
but I don't have idea how to handle exceptions and save them in the form of SOAP message.
<onCompletion onFailureOnly="true">
<to uri="sql:insert into translog(id,type,data) values(2,'ERROR',#)" />
</onCompletion>
but it saves original message in data column. Is there any body who can help?
I might be misunderstanding the question but it looks you are trying to save the SOAP message in XML format if there is a exception.
By default the dataFormat for CXF is POJO meaning it sends a POJO around the camel route i.e. the SOAP XML is converted to a POJO.
Two options come to mind:
Set the dataformat to message or payload. This will send the XML message across the camel route instead of a POJO
Marshall the POJO into a XML message and save it.

Throttling based on content

I would like to know if it's possible with Camel to do throttling based on the content of the exchange.
The situation is the following: I have to call a webservice via soap. Among, the parameters sent to that webservice there is a customerId. The problem is that the webservice send back an error if there are more than 1 request per minute for a given customerId.
I'm wondering if it would be possible to implement throttling per customerId with Camel. So the throttling should not be implemented for all messages but only for messages with the same customerId.
Let me know how I could implement this or if I need to clarify my question.
ActiveMQ Message Groups is designed to handle this case. So, if you can introduce a JMS queue hop in your route, then just set the JMSXGroupId header to the customerId. Then in another route, you can consume from this queue and send to your web service to get the behavior you described.
also see http://camel.apache.org/parallel-processing-and-ordering.html for more information...
While ActiveMQ Message Groups would definitely address the parallel processing of unique customer ID's, in my assessment Claus is correct that introducing a throttle for each unique group represents an unimplemented feature for Camel/ActiveMQ.
Message Groups alone will not meet the SLA described. While each group of messages (correlated by the customer ID) will be processed in order with one thread per group, as long as requests take less than a minute to receive a response, the requirement of one request per minute per customer would not be enforced.
That said, I would be very interested to know if it would be possible to combine Message Groups and a throttle strategy in a way that would simulate the feature request in JIRA. My attempts so far have failed. I was thinking something along these lines:
<route>
<from uri="activemq:pending?maxConcurrentConsumers=10"/>
<throttle timePeriodMillis="60000">
<constant>1</constant>
<to uri="mock:endpoint"/>
</throttle>
</route>
However, the throttle seems to be applied to the entire set of requests moving to the endpoint, and not to each individual consumer. I have to admit, I was a bit surprised to find that behavior. My expectation was that the throttle would apply to each consumer individually, which would satisfy the SLA in the original question, provided that the messages include the customer ID in the JMSXGroupId header.
I came across a similar problem and finally came up with the solution described here.
My assumptions are:
Order of messages is not important (though it can be solved by re-sequencer)
Total volume of messages per customer ID is not great so the runtime is not saturated.
The solution approach:
Run aggregator for 1 minute while using customerID to assemble messages with the same customer ID into a list
Use Splitter to split the list into individual messages
Send the first message from the splitter to the actual service
Re-route the rest of the list back into the aggregator.
Java DSL version is a bit easier to understand:
final AggregationStrategy aggregationStrategy = AggregationStrategies.flexible(Object.class)
.accumulateInCollection(ArrayList.class);
from("direct:start")
.log("Receiving ${body}")
.aggregate(header("customerID"), aggregationStrategy).completionTimeout(60000)
.log("Aggregate: releasing ${body}")
.split(body())
.choice()
.when(header(Exchange.SPLIT_INDEX).isEqualTo(0))
.log("*** Processing: ${body}")
.to("mock:result")
.otherwise()
.to("seda:delay")
.endChoice();
from("seda:delay")
.delay(0)
.to("direct:start");
Spring XML version looks like the following:
<!-- this is our aggregation strategy defined as a spring bean -->
<!-- see http://stackoverflow.com/questions/27404726/how-does-one-set-the-pick-expression-for-apache-camels-flexibleaggregationstr -->
<bean id="_flexible0" class="org.apache.camel.util.toolbox.FlexibleAggregationStrategy"/>
<bean id="_flexible2" factory-bean="_flexible0" factory-method="accumulateInCollection">
<constructor-arg value="java.util.ArrayList" />
</bean>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start"/>
<log message="Receiving ${body}"/>
<aggregate strategyRef="_flexible2" completionTimeout="60000" >
<correlationExpression>
<xpath>/order/#customerID</xpath>
</correlationExpression>
<log message="Aggregate: releasing ${body}"/>
<split>
<simple>${body}</simple>
<choice>
<when>
<simple>${header.CamelSplitIndex} == 0</simple>
<log message="*** Processing: ${body}"/>
<to uri="mock:result"/>
</when>
<otherwise>
<log message="--- Delaying: ${body}"/>
<to uri="seda:delay" />
</otherwise>
</choice>
</split>
</aggregate>
</route>
<route>
<from uri="seda:delay"/>
<to uri="direct:start"/>
</route>
</camelContext>

How to set endpoint specific header value in Camel Multicast

I want to set the endpoint specific header value in Multicast component.
XML DSL as below:
<route>
<from uri="direct:testRoute"/>
<multicast strategyRef="MyAggregator" parallelProcessing="true">
<to uri="direct:call1"/> <!-- set the header MY_HEADER = "call_1" -->
<to uri="direct:call2/> <!-- set the header MY_HEADER = "call_2" -->
</multicast>
</route>
Basically in the response aggregation I want to know, to which service request this response belongs to.
I tried by doing this, but its not the correct way (parse exception):
<to uri="direct:call1">
<setHeader headerName="MY_HEADER"><simple>call1</simple></setHeader>
</to>
What I see from reading the documentation is that, multicast will copy the source Exchange and multicast each copy. So its a shallow copy of the Exchange and kind of reference shared between all the multicast recipient.
But here I am looking for specific header value for individual recipient.
How to do this? Any pointers?
You can't do that in the multicast route. But it should be simple in the direct route afterwards.
<route>
<from uri="direct:call1"/>
<setHeader headerName="MY_HEADER"><simple>call1</simple></setHeader>
.. do whatever
</from>
</route>
otherwise, if call1 is used for other things and you cannot know when to put the header once in that route, make a simple prep-route:
<route>
<from uri="direct:prepCall1"/>
<setHeader headerName="MY_HEADER"><simple>call1</simple></setHeader>
<to uri="direct:call1"/>
</from>
</route>
As a third option, even though you cannot place DSL (xml or java) in the multicast list, you can supply an "onPrepareRef" processor bean that adds the headers to your exchange. But one processor will handle all multicast endpoints.
There is a header with the key Exchange.TO_ENDPOINT that you can see which of the 2 endpoints the response is from.

Resources