I am using the solution from the ServiceStack Re-usability use case project.
To this solution I have added a new console app which contains the code below.
With the original Re-usability use-case project, when an EmailMessage is published it was handled by a subscriber which sent an email (i.e. SMessageService.Any(EmailMessage request).
When I run the console app, which means I have two applications that are subscribing to the EmailMessage, only the new console app receives the message.
I have the following:
My console app is:
class Program
{
static void Main(string[] args)
{
var subscriberHost = new SubscriberHost();
subscriberHost.Init();
Console.WriteLine("Waiting of publishing
to happen on EmailMessage as we are subscribing to it...");
Console.ReadLine();
}
}
public class SubscriberHost : AppHostHttpListenerBase
{
private RedisMqServer mqHost;
public SubscriberHost()
:base("Subscriber console",typeof(EmailMessageEventHandler).Assembly)
{
}
public override void Configure(Container container)
{
var redisFactory = new PooledRedisClientManager("localhost:6379");
mqHost = new RedisMqServer(redisFactory, retryCount:2);
mqHost.RegisterHandler<EmailMessage>((message) =>
{
var emailMessage = message.GetBody();
Console.WriteLine(emailMessage.To);
Console.WriteLine(emailMessage.Subject);
Console.WriteLine(emailMessage.Body);
return new SMessageReceipt {
Type = "not used",
To = "test",
From = "Reusability",
RefId = "1,"
};
});
// mqHost.RegisterHandler<EmailMessage>(ServiceController.ExecuteMessage);
mqHost.Start();
}
}
I was expecting both subscribers to receive the EmailMessage but only the new console app is receiving it. Why isn't the other subscriber receiving the message?
The client code that does the publishing has not been modified.
What I have shown above is using Redis MQ, and for the multiple subscribers problem I was testing I need the Redis Pub/Sub.
For MQ, a subscriber takes the message off the queue to process. Once processed, that is it.
For Pub/Sub, there could be many subscribers and each will receive a copy of the message.
I hope this helps others.
Related
I have a scenario where I want to "pull" messages of a RabbitMQ queue/topic and process them one at a time.
Specifically if there are already messages sitting on the queue when the consumer starts up.
I have tried the following with no success (meaning, each of these options reads the queue until it is either empty or until another thread closes the context).
1.Stopping route immediately it is first processed
final CamelContext context = new DefaultCamelContext();
try {
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
RouteDefinition route = from("rabbitmq:harley?queue=IN&declare=false&autoDelete=false&hostname=localhost&portNumber=5672");
route.process(new Processor() {
Thread stopThread;
#Override
public void process(final Exchange exchange) throws Exception {
String name = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
String body = exchange.getIn().getBody(String.class);
// Doo some stuff
routeComplete[0] = true;
if (stopThread == null) {
stopThread = new Thread() {
#Override
public void run() {
try {
((DefaultCamelContext)exchange.getContext()).stopRoute("RabbitRoute");
} catch (Exception e) {}
}
};
}
stopThread.start();
}
});
}
});
context.start();
while(!routeComplete[0].booleanValue())
Thread.sleep(100);
context.stop();
}
Similar to 1 but using a latch rather than a while loop and sleep.
Using a PollingConsumer
final CamelContext context = new DefaultCamelContext();
context.start();
Endpoint re = context.getEndpoint(srcRoute);
re.start();
try {
PollingConsumer consumer = re.createPollingConsumer();
consumer.start();
Exchange exchange = consumer.receive();
String bb = exchange.getIn().getBody(String.class);
consumer.stop();
} catch(Exception e){
String mm = e.getMessage();
}
Using a ConsumerTemplate() - code similar to above.
I have also tried enabling preFetch and setting the max number of exchanges to 1.
None of these appear to work, if there are 3 messages on the queue, all are read before I am able to stop the route.
If I were to use the standard RabbitMQ Java API I would use a basicGet() call which lets me read a single message, but for other reasons I would prefer to use a Camel consumer.
Has anyone successfully been able to process a single message on a queue that holds multiple messages using a Camel RabbitMQ Consumer?
Thanks.
This is not the primary intention of the component as its for continued received. But I have created a ticket to look into supporting a basicGet (single receive). There is a new spring based rabbitmq component coming in 3.8 onwards so its going to be implemeneted there (first): https://issues.apache.org/jira/browse/CAMEL-16048
I am very new to NService Bus, so I am trying to get it working with a simple test solution using LearningPersistence, obviously this will be changed soon!
So I have 3 projects:
IceDataExtractor - Client which sends a message
IceProcessManager - Processes messages
Messages - Contains a single Message class Messages
I am using the standard code generated by NServiceBus.Bootstrap.WindowsService 2.0.1
Here is page I used as to get sample
I then modified as follows
Ice Data Extractor
private async Task AsyncOnStart()
{
try
{
var endpointConfiguration = new EndpointConfiguration("IceDataExtractor");
var transport = endpointConfiguration.UseTransport<LearningTransport>();
transport.Routing().RouteToEndpoint(typeof(TestMessage), "IceProcessManager");
endpointConfiguration.UseSerialization<JsonSerializer>();
//TODO: optionally choose a different error queue. Perhaps on a remote machine
// https://docs.particular.net/nservicebus/recoverability/
endpointConfiguration.SendFailedMessagesTo("error");
//TODO: optionally choose a different audit queue. Perhaps on a remote machine
// https://docs.particular.net/nservicebus/operations/auditing
endpointConfiguration.AuditProcessedMessagesTo("audit");
endpointConfiguration.DefineCriticalErrorAction(OnCriticalError);
//TODO: For production use select a durable persistence.
// https://docs.particular.net/nservicebus/persistence/
endpointConfiguration.UsePersistence<LearningPersistence>();
//TODO: For production use script the installation.
endpointConfiguration.EnableInstallers();
endpointConfiguration.Conventions()
.DefiningCommandsAs(t => t.Namespace != null && t.Namespace.StartsWith("Messages") &&
t.Namespace.EndsWith("Commands"));
endpoint = await Endpoint.Start(endpointConfiguration)
.ConfigureAwait(false);
PerformStartupOperations();
**var testMessage = new TestMessage {Id = Guid.NewGuid()};
await endpoint.Send(testMessage).ConfigureAwait(false);**
}
catch (Exception exception)
{
logger.Fatal("Failed to start", exception);
Environment.FailFast("Failed to start", exception);
}
}
Ice Process Manager
private async Task AsyncOnStart()
{
try
{
var endpointConfiguration = new EndpointConfiguration("IceDataExtractor");
var transport = **endpointConfiguration.UseTransport<LearningTransport>();
transport.Routing().RouteToEndpoint(typeof(TestMessage), "IceProcessManager");**
endpointConfiguration.UseSerialization<JsonSerializer>();
//TODO: optionally choose a different error queue. Perhaps on a remote machine
// https://docs.particular.net/nservicebus/recoverability/
endpointConfiguration.SendFailedMessagesTo("error");
//TODO: optionally choose a different audit queue. Perhaps on a remote machine
// https://docs.particular.net/nservicebus/operations/auditing
endpointConfiguration.AuditProcessedMessagesTo("audit");
endpointConfiguration.DefineCriticalErrorAction(OnCriticalError);
//TODO: For production use select a durable persistence.
// https://docs.particular.net/nservicebus/persistence/
endpointConfiguration.UsePersistence<LearningPersistence>();
//TODO: For production use script the installation.
endpointConfiguration.EnableInstallers();
**endpointConfiguration.Conventions()
.DefiningCommandsAs(t => t.Namespace != null && t.Namespace.StartsWith("Messages") &&
t.Namespace.EndsWith("Commands"));**
endpoint = await Endpoint.Start(endpointConfiguration)
.ConfigureAwait(false);
PerformStartupOperations();
var testMessage = new TestMessage {Id = Guid.NewGuid()};
await endpoint.Send(testMessage).ConfigureAwait(false);
}
catch (Exception exception)
{
logger.Fatal("Failed to start", exception);
Environment.FailFast("Failed to start", exception);
}
}
TestMessage class
using System;
namespace Messages.Commands
{
public class TestMessage
{
public Guid Id { get; set; }
}
}
This all compiles and runs fine, other than performance warnings which I dont think matter
I have a message handler
TestMessageHandler
using System;
using System.Threading.Tasks;
using Messages.Commands;
using NServiceBus;
namespace IceProcessManager
{
public class TestMessageHandler : IHandleMessages<TestMessage>
{
public Task Handle(TestMessage message, IMessageHandlerContext context)
{
Console.WriteLine("Handled TEst MEssage ID:{0}", message.Id);
return Task.CompletedTask;
}
}
}
As you can see from the screenshot, no message is being received by the IceProcessManager. What am I doing wrong? I was thinking initially that I am sending the message too early, i.e. before the ProcessManager is up and running, but this not the problem because if I leave the ProcessManager running (i.e. run from explorer) then run the extractor, no message is receieved
Ideally I would like to have sent lots of messages to test this but I am not familiar with async stuff yet!
Can someone help please?
Paul
If I am not missing something you are using the same endpoint name for both instances?
var endpointConfiguration = new EndpointConfiguration("IceDataExtractor");
While you are routing the message to "IceDataManager" which doesn't exist.
I guess you might have pasted the wrong code?
I am new to ActiveMQ, but I tried and am able to create a durable publisher, but I am not able to set Client Id, because I am not finding any properties with client Id and am even unable to find in Google. It will be great help if I will get some sample code.
Note:
Not with the NMS protocol. I am using AMQP.Net Lite with ActiveMQ in the .NET Core Web API for creating a durable publisher/subscriber with ClientId.
In order to create a durable subscription to ActiveMQ or ActiveMQ Artemis your client needs to do a couple things.
Set a unique "client-id" for the client using the AMQP 'ContainerId' property which can be seen in the code below. The client must use that same container ID every time it connects and recovers it's durable subscription.
Create a new Session.
Create a new Receiver for the address (in this case Topic) that you want to subscribe to. The Source of a durable subscription need to have the address set to a Topic address (in ActiveMQ this is topic://name). The Source also needs the expiray policy set to NEVER, the Source must also have the terminus durability state set to UNSETTLED_STATE, and the distribution mode set to COPY.
Once the Receiver is created then you can either set an onMessage handler in start or call receive to consume messages (assuming you've granted credit for the broker to send you any).
using System;
using Amqp;
using Amqp.Framing;
using Amqp.Types;
using Amqp.Sasl;
using System.Threading;
namespace aorg.apache.activemq.examples
{
class Program
{
private static string DEFAULT_BROKER_URI = "amqp://localhost:5672";
private static string DEFAULT_CONTAINER_ID = "client-1";
private static string DEFAULT_SUBSCRIPTION_NAME = "test-subscription";
private static string DEFAULT_TOPIC_NAME = "test-topic";
static void Main(string[] args)
{
Console.WriteLine("Starting AMQP durable consumer example.");
Console.WriteLine("Creating a Durable Subscription");
CreateDurableSubscription();
Console.WriteLine("Attempting to recover a Durable Subscription");
RecoverDurableSubscription();
Console.WriteLine("Unsubscribe a durable subscription");
UnsubscribeDurableSubscription();
Console.WriteLine("Attempting to recover a non-existent durable subscription");
try
{
RecoverDurableSubscription();
throw new Exception("Subscription was not deleted.");
}
catch (AmqpException)
{
Console.WriteLine("Recover failed as expected");
}
Console.WriteLine("Example Complete.");
}
// Creating a durable subscription involves creating a Receiver with a Source that
// has the address set to the Topic name where the client wants to subscribe along
// with an expiry policy of 'never', Terminus Durability set to 'unsettled' and the
// Distribution Mode set to 'Copy'. The link name of the Receiver represents the
// desired name of the Subscription and of course the Connection must carry a container
// ID uniqure to the client that is creating the subscription.
private static void CreateDurableSubscription()
{
Connection connection = new Connection(new Address(DEFAULT_BROKER_URI),
SaslProfile.Anonymous,
new Open() { ContainerId = DEFAULT_CONTAINER_ID }, null);
try
{
Session session = new Session(connection);
Source source = CreateBasicSource();
// Create a Durable Consumer Source.
source.Address = DEFAULT_TOPIC_NAME;
source.ExpiryPolicy = new Symbol("never");
source.Durable = 2;
source.DistributionMode = new Symbol("copy");
ReceiverLink receiver = new ReceiverLink(session, DEFAULT_SUBSCRIPTION_NAME, source, null);
session.Close();
}
finally
{
connection.Close();
}
}
// Recovering an existing subscription allows the client to ask the remote
// peer if a subscription with the given name for the current 'Container ID'
// exists. The process involves the client attaching a receiver with a null
// Source on a link with the desired subscription name as the link name and
// the broker will then return a Source instance if this current container
// has a subscription registered with that subscription (link) name.
private static void RecoverDurableSubscription()
{
Connection connection = new Connection(new Address(DEFAULT_BROKER_URI),
SaslProfile.Anonymous,
new Open() { ContainerId = DEFAULT_CONTAINER_ID }, null);
try
{
Session session = new Session(connection);
Source recoveredSource = null;
ManualResetEvent attached = new ManualResetEvent(false);
OnAttached onAttached = (link, attach) =>
{
recoveredSource = (Source) attach.Source;
attached.Set();
};
ReceiverLink receiver = new ReceiverLink(session, DEFAULT_SUBSCRIPTION_NAME, (Source) null, onAttached);
attached.WaitOne(10000);
if (recoveredSource == null)
{
// The remote had no subscription matching what we asked for.
throw new AmqpException(new Error());
}
else
{
Console.WriteLine(" Receovered subscription for address: " + recoveredSource.Address);
Console.WriteLine(" Recovered Source Expiry Policy = " + recoveredSource.ExpiryPolicy);
Console.WriteLine(" Recovered Source Durability = " + recoveredSource.Durable);
Console.WriteLine(" Recovered Source Distribution Mode = " + recoveredSource.DistributionMode);
}
session.Close();
}
finally
{
connection.Close();
}
}
// Unsubscribing a durable subscription involves recovering an existing
// subscription and then closing the receiver link explicitly or in AMQP
// terms the close value of the Detach frame should be 'true'
private static void UnsubscribeDurableSubscription()
{
Connection connection = new Connection(new Address(DEFAULT_BROKER_URI),
SaslProfile.Anonymous,
new Open() { ContainerId = DEFAULT_CONTAINER_ID }, null);
try
{
Session session = new Session(connection);
Source recoveredSource = null;
ManualResetEvent attached = new ManualResetEvent(false);
OnAttached onAttached = (link, attach) =>
{
recoveredSource = (Source) attach.Source;
attached.Set();
};
ReceiverLink receiver = new ReceiverLink(session, DEFAULT_SUBSCRIPTION_NAME, (Source) null, onAttached);
attached.WaitOne(10000);
if (recoveredSource == null)
{
// The remote had no subscription matching what we asked for.
throw new AmqpException(new Error());
}
else
{
Console.WriteLine(" Receovered subscription for address: " + recoveredSource.Address);
Console.WriteLine(" Recovered Source Expiry Policy = " + recoveredSource.ExpiryPolicy);
Console.WriteLine(" Recovered Source Durability = " + recoveredSource.Durable);
Console.WriteLine(" Recovered Source Distribution Mode = " + recoveredSource.DistributionMode);
}
// Closing the Receiver vs. detaching it will unsubscribe
receiver.Close();
session.Close();
}
finally
{
connection.Close();
}
}
// Creates a basic Source type that contains common attributes needed
// to describe to the remote peer the features and expectations of the
// Source of the Receiver link.
private static Source CreateBasicSource()
{
Source source = new Source();
// These are the outcomes this link will accept.
Symbol[] outcomes = new Symbol[] {new Symbol("amqp:accepted:list"),
new Symbol("amqp:rejected:list"),
new Symbol("amqp:released:list"),
new Symbol("amqp:modified:list") };
// Default Outcome for deliveries not settled on this link
Modified defaultOutcome = new Modified();
defaultOutcome.DeliveryFailed = true;
defaultOutcome.UndeliverableHere = false;
// Configure Source.
source.DefaultOutcome = defaultOutcome;
source.Outcomes = outcomes;
return source;
}
}
}
I got this error upon receving an object from a subscription in azure service bus.
An exception of type 'System.Runtime.Serialization.SerializationException' occurred in System.Runtime.Serialization.dll but was not handled in user code
I've tried some deserialization code but nothing works.
This is how I send a message. Please tell me how to receive it.
public void SendMessage()
{
BrokeredMessage message = new BrokeredMessage(new TestMessage() {
MsgNumber = 1, MsgContent = "testing message" }, new DataContractSerializer(typeof(TestMessage)));
// Send message to the topic
TopicClient topicClient = TopicClient.CreateFromConnectionString(cn, topicNamespace);
topicClient.Send(message);
}
public string ReceiveMessage(){
//??????
}
To receive a single message, you need to get the SubscriptionClient :
public void ReceiveMessage(string connectionString, string topicPath, string subscriptionName)
{
var subscriptionClient = SubscriptionClient.CreateFromConnectionString(connectionString, topicPath, subscriptionName);
var brokeredMessage = subscriptionClient.Receive();
var message = brokeredMessage.GetBody<TestMessage>();
}
QuickBlox not delivering GCM notifications to subscribed devices. I tried sending notification message from Admin Panel too, but it isn't delivered to device, but still in Admin Panel it shows it as "sent". But no history available.
And also what could be the reason for this ? How to mitigate this ?
How to view sent GCM notifications.
The admin panel is not good enough - it lies sometime by saying 'send successfully' but it never reaches the other end.
Try sending the GCM notification by using the code snippet provided in the documentation. It works always.
public void sendMessageOnClick() {
// Send Push: create QuickBlox Push Notification Event
QBEvent qbEvent = new QBEvent();
qbEvent.setNotificationType(QBNotificationType.PUSH);
qbEvent.setEnvironment(QBEnvironment.DEVELOPMENT);
// generic push - will be delivered to all platforms (Android, iOS, WP, Blackberry..)
qbEvent.setMessage("how are you doing");
StringifyArrayList<Integer> userIds = new StringifyArrayList<Integer>();
userIds.add(6132691);
qbEvent.setUserIds(userIds);
QBMessages.createEvent(qbEvent, new QBEntityCallbackImpl<QBEvent>() {
#Override
public void onSuccess(QBEvent qbEvent, Bundle bundle) {
}
#Override
public void onError(List<String> strings) {
// errors
}
});
}
//OR the below:--
private void sendPushNotifications(){
// recipients
StringifyArrayList<Integer> userIds = new StringifyArrayList<Integer>();
userIds.add(6114793);
//userIds.add(960);
QBEvent event = new QBEvent();
event.setUserIds(userIds);
event.setEnvironment(QBEnvironment.DEVELOPMENT);
event.setNotificationType(QBNotificationType.PUSH);
event.setPushType(QBPushType.GCM);
HashMap<String, String> data = new HashMap<String, String>();
data.put("data.message", "Hello");
data.put("data.type", "welcome message");
event.setMessage(data);
QBMessages.createEvent(event, new QBEntityCallbackImpl<QBEvent>() {
#Override
public void onSuccess(QBEvent qbEvent, Bundle args) {
// sent
}
#Override
public void onError(List<String> errors) {
}
});
}