I have a Windows Service that runs some processes and it must be notify the progress of it on the browser. I am not sure if I am doing something that is good but I just did it:
Windows Service publish a json on a redis channel called 'web' -> An action on ASP.NET MVC application subscribe the 'web' channel and send the json to browser via signalR hub -> the browser take it and show the progress.
I have the following code (it is a helper) to add a channel scope after a publish. It is called from my controller/action:
public void Listen(string channel, Action<string, object> action)
{
var sub = Client.GetSubscriber();
sub.Subscribe(channel, (c, v) =>
{
action(c.ToString(), v.ToString());
});
}
The problem: It works as expetected and I get the browser notified. The problem is when the user (on browser) hits F5 or executes the action again. It creates a new channel and I get duplicated messages. If the users executes again it, I started getting 3 messages for each one and so on. I want to avoid it.
What I have tried: I tried to use the IsConnection(channel) but it always returns true. I have tried to Unsubscribe(channel) before Subscribe(channel) again and it works but I am not sure if i will lost some messages (I am afraid). I do not know how to solve it and avoid getting duplicate subscriptions. Does anyone can help me?
Thank you.
Are you using the ConnectionMultiplexer? See Using redis pub/sub.
... in the event of connection failure, the ConnectionMultiplexer will
handle all the details of re-subscribing to the requested channels
Consider switching from Pub/Sub to Redis Streams. See What are the main differences between Redis Pub/Sub and Redis Stream?
You can name groups and clients with Consumer Groups. Therefore you can control it by session, or anything else, or even use something like fingerprint.js to identify each browser anonymously.
Related
We're using ActiveMQ locally to transfer data between 5 processes that turn simultaneously.
I have some data I need to send to a process, both at runtime (which works perfectly fine), but also a default value on start. Thing is it is published when the process starts, it just doesn't read because it wasn't subscribed to the topic at the time the data was sent.
I have multiple solutions : I could delay the first publishing for a moment so that the process has time to launch (which doesn't seem very appealing) ; or is there a way to send all stored previously non-treated messages to some process that just subscribed ?
I'm coding in C#.
I don't have any experience with ActiveMQ, but other message system usually have an option which marks the subscription as persistent, which means that; after the first subscription; the message queue itself checks if a certain message is delivered to that system and retries with a timeout. In this scenario you need to start the receiver at least 1 time.
If this is not an option and you want to plug in receiver afterwards, you might want to consider a setup of your messages which allows you to retrieve the full state, i.e. if you send total-messages instead of differential- messages.
After a little google, I came upon this definition durable subscribers, I hope this helps:
See:
http://activemq.apache.org/how-do-durable-queues-and-topics-work.html
and
http://activemq.apache.org/manage-durable-subscribers.html
since you are using C# client i don't konw if this is supported
topic = new ActiveMQTopic("TEST.Topic?consumer.retroactive=true");
http://activemq.apache.org/retroactive-consumer.html
So, another solution is to configure this behavior on the broker side by adding that to the activemq.xml and restart :
The subscription recovery policy allows you to go back in time when
you subscribe to a topic.
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry topic=">" >
<subscriptionRecoveryPolicy>
<timedSubscriptionRecoveryPolicy recoverDuration="10000" />
<fixedCountSubscriptionRecoveryPolicy maximumSize="10000" />
</subscriptionRecoveryPolicy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
http://activemq.apache.org/subscription-recovery-policy.html
I went around the issue by sending a message from each process when they're launched back to the main one, and then only sending the info I needed to send.
I am building a c#/wpf project.
It's architecture is this:
A console application which will be on a virtual machine (or my home computer) that will be the server side.
A wpf application that will be the client app.
Now my problem is this - I want the server to be able to send changes to the clients. If for example I have a change for client ABC, I want the server to know how to call a service on the clients computer.
The problem is, that I don't know how the server will call the clients.
A small example in case I didn't explain it well:
The server is on computer 1, and there are two clients, on computers 2 and 3.
Client 2 has a Toyota car and client 3 has a BMW car.
The server on computer 1 wants to tell client 2 that it has a new car, an Avenger.
How do I keep track and call services on the clients?
I thought of saving their ip address (from calling ipconfig from the cmd) in the DB - but isn't that based on the WI-FI/network they are connected to?
Thanks for any help!
You could try implementing SignalR. It is a great library that uses web sockets to push data to clients.
Edit:
SignalR can help you solve your problem by allowing you to set up Hubs on your console app (server) that WPF application (clients) can connect to. When the clients start up you will register them with a specified Hub. When something changes on the server, you can push from the server Hub to the client. The client will receive the information from the server and allow you to handle it as you see fit.
Rough mockup of some code:
namepsace Server{}
public class YourHub : Hub {
public void SomeHubMethod(string userName) {
//clientMethodToCall is a method in the WPF application that
//will be called. Client needs to be registered to hub first.
Clients.User(userName).clientMethodToCall("This is a test.");
//One issue you may face is mapping client connections.
//There are a couple different ways/methodologies to do this.
//Just figure what will work best for you.
}
}
}
namespace Client{
public class HubService{
public IHubProxy CreateHubProxy(){
var hubConnection = new HubConnection("http://serverAddress:serverPort/");
IHubProxy yourHubProxy = hubConnection.CreateHubProxy("YourHub");
return yourHubProxy;
}
}
}
Then in your WPF window:
var hubService = new HubService();
var yourHubProxy = hubService.CreateHubProxy();
yourHubProxy.Start().Wait();
yourHubProxy.On("clientMethodToCall", () => DoSometingWithServerData());
You need to create some kind of subscription model for the clients to the server to handle a Publish-Subscribe channel (see http://www.enterpriseintegrationpatterns.com/patterns/messaging/PublishSubscribeChannel.html). The basic architecture is this:
Client sends a request to the messaging channel to register itself as a subscriber to a certain kind of message/event/etc.
Server sends messages to the channel to be delivered to subscribers to that message.
There are many ways to handle this. You could use some of the Azure services (like Event hub, or Topic) if you don't want to reinvent the wheel here. You could also have your server application track all of these things (updates to IP addresses, updates to subscription interest, making sure that messages don't get sent more than once; taking care of message durability [making sure messages get delivered even if the client is offline when the message gets created]).
In general, whatever solution you choose is plagued with a common problem - clients hide behind firewalls and have dynamic IP addresses. This makes it difficult (I've heard of technologies claiming to overcome this but haven't seen any in action) for a server to push to a client.
In reality, the client talks and the server listens and response. However, you can use this approach to simulate a push by;
1. polling (the client periodically asks for information)
2. long polling (the client asks for information and the server holds onto the request until information arrives or a timeout occurs)
3. sockets (the client requests server connection that is used for bi-directional communication for a period of time).
Knowing those terms, your next choice is to write your own or use a third-party service (azure, amazon, other) to deliver messages for you. I personally like long polling because it is easy to implement. In my application, I have the following setup.
A web API server on Azure with and endpoint that listens for message requests
A simple loop inside the server code that checks the database for new messages every 100ms.
A client that calls the API, handling the response.
As mentioned, there are many ways to do this. In your particular case, one way would be as follows.
Client A calls server API to listen for message
Server holds onto call, waiting for new message entry in database
Client B calls server API to post new message
Server saves message to database
Server instance from step 2 sees new message
Server returns message to Client A.
Also, the message doesn't have to be stored in a database - it just depends on your needs.
Sounds like you want to track users à la https://www.simple-talk.com/dotnet/asp.net/tracking-online-users-with-signalr/ , but in a desktop app in the sense of http://www.codeproject.com/Articles/804770/Implementing-SignalR-in-Desktop-Applications or damienbod.wordpress.com/2013/11/20/signalr-a-complete-wpf-client-using-mvvm/ .
I have a Lync 2013-based application which:
connects to a UserEndpoint (hereinafter CallCenter)
redirects calls made to CallCenter according to bla bla bla business logic.
At times, a user will see CallCenter in their standard Lync 2013 Client as Online, but if that user attempts to start an IM call with CallCenter, the user receives the message "We couldn't send this message because CallCenter is unavailable or offline."
I haven't been able to identify the process that leads up to this, but if it's happened to one user, then all of the other users experience the same problem when attempting to call CallCenter. The only way I have been able to recover CallCenter has been to restart my application. Regular interaction with CallCenter then resumes without a problem.
If CallCenter is indeed "unavailable or offline", then why does it's Presence appear as "Online"? Is there a need to renew / keep CallCenter's connection alive every so often?
For reference, I connect CallCenter like so:
UserEndpointSettings settings = new UserEndpointSettings(userURI, _ProxyHost, _ProxyPort);
settings.AutomaticPresencePublicationEnabled = true;
settings.Presence.UserPresenceState = PresenceState.UserAvailable;
_userEndpoint = new UserEndpoint(_Platform.CollabPlatform, settings);
_userEndpoint.BeginEstablish(res =>
{
try
{
_userEndpoint.EndEstablish(res);
_userEndpoint.StateChanged += new EventHandler<LocalEndpointStateChangedEventArgs>(_userEndpoint_StateChanged);
}
catch (Exception ex)
{
LogError(ex, ErrorReference.EndpointEstablishFailed);
}
}, null);
In the client, when you go offline or experience an error, your presence reflects that (most of the time, that is). This can lead you to believe that the status portion of presence [1] is somehow tied to actual availability.
When you're working with UCMA, you are given ultimate control over everything related to your endpoint. As you've seen, you can make your UCMA application do things that would otherwise be impossible in the regular client. You don't have to publish any presence status (leaving you "offline" to your users), yet the service can still send/receive IMs. And, as you've seen, your service can be "Available" and yet ... have no capability to do anything but publish its status [2].
If you fail to wire up the appropriate modality (in your case IM), or your application encounters an exception which results in a particular modality no longer working (I suspect this may be your actual problem), the status of your service will still be available.
Begin/EndTerminate on the UserEndpoint should publish Offline for you automatically and publishing a presence other than Available is the only way to guarantee the presence won't be "Available" for the lifetime of your application (and even after the application ends/dies prematurely, though this is sometimes rectified by the server -- sometimes).
Here's how I'd attack resolving this issue. Ignore the presence problem and ignore the error. They're red herrings. Many problems result in the "unavailable or offline" message that have nothing to do with the service actually being stopped.
Instead, figure out why your calls aren't connecting.
If the call takes a while before you receive the error, check for deadlocks or circumstances where the Thread Pool has no room for another thread. Troubleshooting involves reviewing your code for race conditions and the myriad of other things that multi-threaded applications throw your way. If the IMCall fails instantly, check around the parts that handle incoming calls. In the latter case, your subscription may be gone (too many causes to list here, most of which are .Net related, not UCMA related), or your service may be dead.
If the importance of presence to your application is only to show it as "available" or "offline" when it is actually able to send/receive an IM, you're going to want to ensure your application terminates the endpoint properly during tear-down (including in the case of a critical failure: catch-terminate-rethrow or whatever is appropriate in your case).
[1] Be careful when thinking about the term "presence" as it relates to Lync. Presence contains availability status, modality specific states, capabilities (IM/Voice, etc), the "note" and contact information.
[2] This seems like a bizarre thing to do, however, it gave me the ability to use an ApplicationEndpoint to report on the availability of a web service (unrelated to Lync) that I wanted to be able to view in the Mobile client without connecting via VPN. When doing something like this, it's really important to publish the capabilities of your endpoint -- this will explicitly signal to your connected clients what your service can and cannot do.
[Final Footnote] There are a few ways to publish presence. The mechanism you're using to publish is the simplest and most logical to use if you're just interested in telling your users that the "service is here"/"service is not here" which is documented rather well here: Simplified Presence Publication for Endpoints
Does anyone know of a way to create a listener for a proxy so that when the CommunicationState has changed I can invoke an action or a method?
An example, I want to update my WCF service for a code change. Since the application is in its early development code changes are very frequent. However, instead of annoying my employees with an email tell them that hey they need to restart their application. I would rather avoid them having to restart the app and having to send them an email. I would rather write a listener that looks at the communication state of a service and if it has changed to a faulted stated then attempt to reconnect.
Edit
Maybe some more context here.
InstanceContext context = new InstanceContext(this);
Subscriber = new SubscriptionService.MySubscriptionServiceClient(context);
Subscriber.Subscribe("");
So basically I want to know when the subscription service has stopped so that I can attempt to reconnect every 60 seconds or so. I tried looking for an event in the Subscriber service but I didn't see anything. Would I need to implement something on the service end?
Thanks
You can use the Faulted event available on the InnerChannel property of your generated client class. The State property of the client class is just a wrapper for InnerChannel.State, so this should work as you desire.
(For reference, you can also use the similarly named event on ChannelFactory<TChannel> if you are creating communication channels in code rather than using generated proxies.)
My problem is that I want to have a server application (on a remote computer) to publish certain events to several client computers. The server and client communicate using .Net-Remoting so currently I am using remoted .Net-Events to get the functionality. But there is one drawback: when the server (the event publisher) comes offline and is restarted, the clients lose the connection since the remote object references become invalid.
I am looking into Loosely Coupled Events and Transient COM Subscriptions to solve this issue. I put together a small demo application with one publisher and two subscribers. It works beautifully on one computer.
I am using the COMAdmin-Libraries to create a transient subscription for the event subscribers. The code looks like this:
MyEventHandler handler = new MyEventHandler();
ICOMAdminCatalog catalog;
ICatalogCollection transientCollection;
ICatalogObject subscription;
catalog = (ICOMAdminCatalog)new COMAdminCatalog();
transientCollection = (ICatalogCollection)catalog.GetCollection("TransientSubscriptions");
subscription = (ICatalogObject)transientCollection.Add();
subscription.set_Value("Name", "SubTrans");
subscription.set_Value("SubscriberInterface", handler);
string eventClassString = "{B57E128F-DB28-451b-99D3-0F81DA487EDE}";
subscription.set_Value("EventCLSID", eventClassString);
string sinkString = "{9A616A06-4F8D-4fbc-B47F-482C24A04F35}";
subscription.set_Value("InterfaceID", sinkString);
subscription.set_Value("FilterCriteria", "");
subscription.set_Value("PublisherID", "");
transientCollection.SaveChanges();
handler.Event1 += OnEvent1;
handler.Event2 += OnEvent2;
My question now is: what do I have to change in the subscription to make this work over a network? Is it even possible?
What about MSMQ? It seems perfect for what you are trying to achieve? You can use a traditional publish/subscribe model or multicast the messages.
This might be a step too far, but have you considered using WCF and the callback element of WCF?
Callback effectively turns the what was client into a server. To be honest, I don't know a great deal about callback and have only experimented. Perhaps worth a 10 minute google though.
If your server comes offline every once and a while I cannot see how you can avoid to poll it to check that it is alive.
As you are talking about COM and remote computers, I suspect you'll have to do some DCOM security configuration.