I have a simple WCF service serving up notifications to and from an app.
the service holds a static list of subscribers, and before it tries to send a message to any of them, it checks ...
((ICommunicationObject)callback).State == CommunicationState.Opened
unfortunately if the app consuming the WCF service falls over - thus not unsubscribing itself from the WCF service, the service will hang around and eventually timeout. far from ideal.
in an attempt to sort this I added
OperationContext.Current.Channel.Faulted += new EventHandler(Channel_Faulted);
in the subscription method, but it is never hit.
is there a reliable way to determine whether or not the callback channel is in fact open?
or maybe to asynchronously send the messages so that if a subscriber appears in the list but is actually dead, the service wont wait around like a geek being stood up on a date? :)
any help most happily received
thanks
nat
full code for service below...
private static readonly List<INotificationCallback> subscribers = new List<INotificationCallback>();
public void AddMessage(string SendingUser, string message, List<string> users, MessageType messageType, Guid? CaseID)
{
subscribers.ForEach(delegate(INotificationCallback callback)
{
if (((ICommunicationObject)callback).State == CommunicationState.Opened)
{
callback.OnMessageAdded(SendingUser, message, users, messageType, DateTime.Now, CaseID);
}
else
{
RemoveSubscriber(callback);
}
});
}
public bool Subscribe()
{
try
{
INotificationCallback callback = OperationContext.Current.GetCallbackChannel<INotificationCallback>();
OperationContext.Current.Channel.Faulted += new EventHandler(Channel_Faulted);
OperationContext.Current.Channel.Closed += new EventHandler(Channel_Closed);
if (!subscribers.Contains(callback))
subscribers.Add(callback);
return true;
}
catch
{
return false;
}
}
private void RemoveSubscriber(INotificationCallback callback)
{
if (subscribers.Contains(callback))
subscribers.Remove(callback);
}
void Channel_Closed(object sender, EventArgs e)
{
INotificationCallback machine = sender as INotificationCallback;
RemoveSubscriber(machine);
}
void Channel_Faulted(object sender, EventArgs e)
{
INotificationCallback machine = sender as INotificationCallback;
RemoveSubscriber(machine);
}
public bool Unsubscribe()
{
try
{
INotificationCallback callback = OperationContext.Current.GetCallbackChannel<INotificationCallback>();
RemoveSubscriber(callback);
return true;
}
catch
{
return false;
}
}
ended up ditching the HttpBinding and switched to NetTcpBinding which raises the the fault event immediately.
Related
We're building a WCF server (.NET 4.5). It will only use net.pipe transport.
When a client closes the PIPE connection, the server gets unhandled CommunicationException, and terminates.
Q1. How do I handle the CommunicationException so the server does not terminate and continues serving other clients?
Q2. In the handler, how do I get SessionId of the session that was aborted? I need this to do clean up some session-specific data.
Thanks in advance!
contract
[ServiceContract(CallbackContract = typeof(IContractCallback))]
public interface IContractServer
{
[OperationContract(IsOneWay = true)]
void Connect(bool status);
[OperationContract(IsOneWay = false)]
void Disconnect(IContractServer _channelCallback);
[OperationContract(IsOneWay = true)]
void Play(bool status);
}
service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service : IContractServer
{
public List<IContractCallback> _channeList = new List<IContractCallback>();
public void Connect(bool status)
{
IContractCallback a = OperationContext.Current.GetCallbackChannel<IContractCallback>();
int call = 0;
foreach (var callBack in _channeList)
{
if (callBack == a)
{
call++;
}
}
if (call == 0)
{
_channeList.Add(a);
a.ConnectCallback(true);
}
else
{
a.ConnectCallback(false);
}
}
public void Disconnect(IContractServer _channelCallback)
{
foreach (var contractCallback in _channeList)
{
if (contractCallback == _channelCallback)
{
_channeList.Remove(contractCallback);
}
}
}
public void Play(bool status)
{
foreach (var contractCallback in _channeList)
{
contractCallback.PlayCallback(status);
}
}
}
client
using System.ComponentModel;
using System.ServiceModel;
using System.Windows;
using Host;
namespace VideoPlayer
{
public partial class MainWindow : Window, IContractCallback
{
private IContractServer Proxy = null;
public MainWindow()
{
InitializeComponent();
InstanceContext context = new InstanceContext(this);
DuplexChannelFactory<IContractServer> factory = new DuplexChannelFactory<IContractServer>(context, new NetNamedPipeBinding(), "net.pipe://localhost");
Proxy = factory.CreateChannel();
Proxy.Connect(true);
}
public void ConnectCallback(bool status)
{
MessageBox.Show(status ? "connected" : "no connected");
}
public void PlayCallback(bool status)
{
if (status)
{
MessageBox.Show("status true");
}
else
{
MessageBox.Show("status false");
}
}
private void ButtonPlay(object sender, RoutedEventArgs e)
{
Proxy.Play(true);
}
private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
//хочу отправить сообщение о закрытии
Proxy.Disconnect(Proxy);
}
I faced with this problem before in my duplex services when an event raised from server side the exception occurred if there was no alive channel between client and server so server dropped to Fault state and all next requests won't be responded,
For this reason I came to this conclusion to check the channel and if it was alive then let call back methods to be raised.
In service side the trick would be like ↓
bool IsChannelAlive()
{
Logging logging = new Logging(LogFile);
try
{
if (((ICommunicationObject)_callbackChannel).State == CommunicationState.Opened)
{
logging.Log(LoggingMode.Prompt, "Channeld is still alive, can raise events...");
return true;
}
}
catch (Exception exp)
{
logging.Log(LoggingMode.Error, "IsChannelAlive()=> failed, EXP: {0}", exp);
}
logging.Log(LoggingMode.Warning, "Channeld is not alive so events won't raised...");
return false;
}
and in one of my events I use it like :
void stran_OperationTimedOut(object sender, EventArgs e)
{
if (IsChannelAlive())
_callbackChannel.OnOperationTimedOut();
}
But for a while I use this trick to know closed channel to do something:
public ImportService()
{
//Handle ContextClose to Audit all actions made in session
OperationContext.Current.InstanceContext.Closed += delegate
{
//Here
};
}
which is not reliable.
I am still using that IsAliveChannel() in my services.
Hope this answer resolve your problem or give you the clue.
I have an application that sends AMQP messages via RabbitMQ. message sending is triggered on an http request. Recently I have noticed that some messages appear to be getting lost (as in never delivered). I also noticed that the list of channels being managed by the server is steadily increasing. The first thing I have corrected is to close channels after they are no longer required. However, I am still not sure my code is correctly structured to ensure delivery. Two sections of code are below; the first is a section of a singleton that manages the connection (does not recreate on every call), the second is the sending code. Any advice / guidance would be appreciated.
#Service
public class PersistentConnection {
private static Connection myConnection = null;
private Boolean blocked = false;
#Autowired ApplicationConfiguration applicationConfiguration;
#Autowired ConfigurationService configurationService;
#PostConstruct
private void init() {
}
#PreDestroy
private void destroy() {
try {
myConnection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public Connection getConnection( ) {
if (myConnection == null) {
start();
}
else if (!myConnection.isOpen()) {
log.warn("AMQP Connection closed. Attempting to start.");
start();
}
return myConnection;
}
private void start() {
log.debug("Building AMQP Connection");
ConnectionFactory factory = new ConnectionFactory();
String ipAddress = applicationConfiguration.getAMQPHost();
String password = applicationConfiguration.getAMQPUser();
String user = applicationConfiguration.getAMQPPassword();
String virtualHost = applicationConfiguration.getAMQPVirtualHost();
String port = applicationConfiguration.getAMQPPort();
try {
factory.setUsername(user);
factory.setPassword(password);
factory.setVirtualHost(virtualHost);
factory.setPort(Integer.parseInt(port));
factory.setHost(ipAddress);
myConnection = factory.newConnection();
}
catch (Exception e) {
e.printStackTrace();
}
myConnection.addBlockedListener(new BlockedListener() {
public void handleBlocked(String reason) throws IOException {
// Connection is now blocked
blocked = true;
}
public void handleUnblocked() throws IOException {
// Connection is now unblocked
blocked = false;
}
});
}
public Boolean isBlocked() {
return blocked;
}
}
/*
* Sends ADT message to AMQP server.
*/
private void send(String routingKey, String message) throws Exception {
String exchange = applicationConfiguration.getAMQPExchange();
String exchangeType = applicationConfiguration.getAMQPExchangeType();
Connection connection = myConnection.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exchange, exchangeType);
channel.basicPublish(exchange, routingKey, null, message.getBytes());
// Close the channel if it is no longer needed in this thread
channel.close();
}
Try this code:
#Service
public class PersistentConnection {
private Connection myConnection = null;
private Boolean blocked = false;
#Autowired ApplicationConfiguration applicationConfiguration;
#Autowired ConfigurationService configurationService;
#PostConstruct
private void init() {
start(); /// In this way you can initthe connection and you are sure it is called only one time.
}
#PreDestroy
private void destroy() {
try {
myConnection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public Connection getConnection( ) {
return myConnection;
}
private void start() {
log.debug("Building AMQP Connection");
ConnectionFactory factory = new ConnectionFactory();
String ipAddress = applicationConfiguration.getAMQPHost();
String password = applicationConfiguration.getAMQPUser();
String user = applicationConfiguration.getAMQPPassword();
String virtualHost = applicationConfiguration.getAMQPVirtualHost();
String port = applicationConfiguration.getAMQPPort();
try {
factory.setUsername(user);
factory.setPassword(password);
factory.setVirtualHost(virtualHost);
factory.setPort(Integer.parseInt(port));
factory.setHost(ipAddress);
myConnection = factory.newConnection();
}
catch (Exception e) {
e.printStackTrace();
}
myConnection.addBlockedListener(new BlockedListener() {
public void handleBlocked(String reason) throws IOException {
// Connection is now blocked
blocked = true;
}
public void handleUnblocked() throws IOException {
// Connection is now unblocked
blocked = false;
}
});
}
public Boolean isBlocked() {
return blocked;
}
}
/*
* Sends ADT message to AMQP server.
*/
private void send(String routingKey, String message) throws Exception {
String exchange = applicationConfiguration.getAMQPExchange();
String exchangeType = applicationConfiguration.getAMQPExchangeType();
Connection connection = myConnection.getConnection();
if (connection!=null){
Channel channel = connection.createChannel();
try{
channel.exchangeDeclare(exchange, exchangeType);
channel.basicPublish(exchange, routingKey, null, message.getBytes());
} finally{
// Close the channel if it is no longer needed in this thread
channel.close();
}
}
}
This could be enough, you have an connection with rabbitmq when the system starts.
If you an lazy singleton, the code is just a bit different.
I suggest to not use isOpen() method, please read here:
isOpen
boolean isOpen() Determine whether the component is currently open.
Will return false if we are currently closing. Checking this method
should be only for information, because of the race conditions - state
can change after the call. Instead just execute and try to catch
ShutdownSignalException and IOException Returns: true when component
is open, false otherwise
EDIT**
Question 1:
What are you looking for is the HA client.
RabbitMQ java client by default doesn't support this features, since the version 3.3.0 supports only the reconnect,read this:
...allows Java-based clients to reconnect automatically after network
failure. If you want be sure about your messages you have to create an
robust client able to resists to all fails.
Generally you should consider the fails, for example:
what happen if there is an error during the message publish?
In your case you simply lose the message,You should re-queue the message manually.
Question 2:
I don’t know your code, but connection == null shouldn’t happen, because this procedure is called for first:
#PostConstruct
private void init() {
start(); /// In this way you can initthe connection and you are sure it is called only one time.
}
Anyway you can raise an exception, the question is:
What do I have to do with the message that I was trying to send?
See the question 1
I’d like to suggest to read more about the HA, for example this:
http://www.rabbitmq.com/ha.html
https://www.rabbitmq.com/reliability.html
And this for the client:
https://github.com/jhalterman/lyra (I never used it)
Create a reliable system with rabbitmq is not complex, but you should know some basic concept.
Anyway .. Let me know!
I have a sample service to test WCF net.tcp communication. It is very simple service and all it does is subscribing a client to the service and then calls callbackchannel to notify all connected clients about broadcasted message. The service is hosted inside IIS 7.5.
Here is service code and test client to test it.
[ServiceContract(CallbackContract = typeof(ISampleServiceCallBack), SessionMode = SessionMode.Required)]
public interface ISampleCuratioService
{
[OperationContract(IsOneWay = true)]
void SubcribeToService(string sub);
[OperationContract]
string GetData(int value);
[OperationContract(IsOneWay = true)]
void Broadcast(string message);
}
public interface ISampleServiceCallBack
{
[OperationContract(IsOneWay = true)]
void NotifyClient(string message);
}
Here is the service implementation:
[ServiceBehavior(Name = "CuratioCSMService", InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : ISampleCuratioService
{
private static List<ISampleServiceCallBack> JoinedClien = new List<ISampleServiceCallBack>();
public void SubcribeToService(string sub)
{
var subscriber = OperationContext.Current.GetCallbackChannel<ISampleServiceCallBack>();
if (!JoinedClien.Contains(subscriber))
{
JoinedClien.Add(subscriber);
}
}
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public void Broadcast(string message)
{
JoinedClien.ForEach(c => c.NotifyClient("message was received " + message));
}
}
I can not understand the behavior I get when running it. After the first client runs everything works fine but as I close and open test client app, it throws exception notifying that channel can not be used for communication as it is in fault state.
This is sample test client:
static void Main(string[] args)
{
var callneckclient = new ServiceClientProxy();
var client = new SampleCuratioServiceClient(new InstanceContext(callneckclient));
client.SubcribeToService("me");
Console.ReadLine();
for (int i = 0; i < 15; i++)
{
Console.WriteLine(client.GetData(5));
client.Broadcast("this is from client me");
}
client.Close();
Console.Read();
}
public class ServiceClientProxy : ISampleCuratioServiceCallback, IDisposable
{
public void NotifyClient(string message)
{
Console.WriteLine(message);
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
The situation gets even buggy when I run 5 clients. Non of those send or receive messages.
When a client calls SubcribeToService you add its operation context to a List called JoinedClien.
When you call Broadcast in your server, you call the method NotifyClient on all collected operation contexts for every client that has ever connected.
The problem is, that a disconnected client won't get removed from your JoinedClien list.
When you try to call an operation method on a disconnected operation context, you get the channel is in faulted state error.
To work around, you should subscribe to the Channel_Closed and Channel_Faulted events and also catch the CommunicationException when calling back into your clients and remove the operation context of the faulted clients:
public void Broadcast(string message)
{
// copy list of clients
List<OperationContext> clientsCopy = new List<OperationContext>();
lock(JoinedClien) {
clientsCopy.AddRange(JoinedClien);
}
// send message and collect faulted clients in separate list
List<OperationContext> clientsToRemove = new List<OperationContext>();
foreach (var c in JoinedClien)
{
try {
c.NotifyClient("message was received " + message));
}
catch (CommunicationException ex) {
clientsToRemove.Add(c);
}
}
foreach (var c in clientsToRemove)
{
lock(JoinedClien) {
if(JoinedClien.Contains(c))
JoinedClien.Remove(c);
}
}
}
When adding new clients you have to lock that operation, too:
var subscriber = OperationContext.Current.GetCallbackChannel<ISampleServiceCallBack>();
lock(JoinedClien)
{
if (!JoinedClien.Contains(subscriber))
{
JoinedClien.Add(subscriber);
}
}
I want to wait until an asynchronous WCF Service operation is complete. How to do that ?
Service.WebService.GetUserCompleted += new EventHandler<BGWebService.GetUserCompletedEventArgs>(WebService_GetUserCompleted);
Service.WebService.GetUserAsync(UserId);
The generated method GetUserAsync should return an IAsyncResult (see MSDN documentation). You can use that to block until the operation completes:
IAsyncResult asyncResult = Service.WebService.GetUserAsync(UserId);
asyncResult.AsyncWaitHandle.WaitOne();
Well I tried doing this in a different way. Rather than waiting for the service response I had it send a call to the interface I was working on.
public class Person : INotifyPropertyChanged
{
//Calling the service to get the object needed
private string personname
public string PersonName
{
get {
return personname;
}
set {
personname = value;
NotifyPropertyChanged("PersonName");
}
}
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void LoadMembersName()
{
ServiceSoapClient serviceClient = new ServiceSoapClient() {};
serviceClient.GetMembersNameCompleted += (sender, e)=>{
PersonName = e.Result
} ;
}
}
Now on the interface we just need to load the person's name and perform the operations after the property change event triggers.
void Page_Load (object sender, EventArgs e)
{
Person per = new Person();
per.PropertyChanged += (sender,e) =>
{
//Your code here
Response.Write(per.PersonName);
};
per.LoadMembersName();
}
I am really sorry. I too had copied the code from some source, but I have misplaced the link.
How do I handle an exception thrown in a callback method on the client in a WCF duplex setup?
Currently, the client does not appear to raise the faulted event (unless I'm monitoring it incorrectly?) but any subsequent to call Ping() using the the client fails with CommunicationException: "The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it has been Aborted.".
How do I deal with this and recreate the client etc? My first question is how to find out when it happens. Secondly, how best to deal with it?
My service and callback contracts:
[ServiceContract(CallbackContract = typeof(ICallback), SessionMode = SessionMode.Required)]
public interface IService
{
[OperationContract]
bool Ping();
}
public interface ICallback
{
[OperationContract(IsOneWay = true)]
void Pong();
}
My server implementation:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
public class Service : IService
{
public bool Ping()
{
var remoteMachine = OperationContext.Current.GetCallbackChannel<ICallback>();
remoteMachine.Pong();
}
}
My client implementation:
[CallbackBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Single)]
public class Client : ICallback
{
public Client ()
{
var context = new InstanceContext(this);
var proxy = new WcfDuplexProxy<IApplicationService>(context);
(proxy as ICommunicationObject).Faulted += new EventHandler(proxy_Faulted);
//First Ping will call the Pong callback. The exception is thrown
proxy.ServiceChannel.Ping();
//Second Ping call fails as the client is in Aborted state
try
{
proxy.ServiceChannel.Ping();
}
catch (Exception)
{
//CommunicationException here
throw;
}
}
void Pong()
{
throw new Exception();
}
//These event handlers never get called
void proxy_Faulted(object sender, EventArgs e)
{
Console.WriteLine("client faulted proxy_Faulted");
}
}
As it turns out, you cannot expect the Faulted event to be raised. So, the best way to re-establish the connection is to do it when the subsequent call to Ping() fails:
I'll keep the code simple here:
public class Client : ICallback
{
public Client ()
{
var context = new InstanceContext(this);
var proxy = new WcfDuplexProxy<IApplicationService>(context);
(proxy.ServiceChannel as ICommunicationObject).Faulted +=new EventHandler(ServiceChannel_Faulted);
//First Ping will call the Pong callback. The exception is thrown
proxy.ServiceChannel.Ping();
//Second Ping call fails as the client is in Aborted state
try
{
proxy.ServiceChannel.Ping();
}
catch (Exception)
{
//Re-establish the connection and try again
proxy.Abort();
proxy = new WcfDuplexProxy<IApplicationService>(context);
proxy.ServiceChannel.Ping();
}
}
/*
[...The rest of the code is the same...]
//*/
}
Obviously, in my example code, the Exception will be thrown again but I hope this is useful to give people an idea of how to re-establish the connection.