I need some help to get to the bottom of these errors we see constantly with our SSE implementation.
They have been going on for months now, and now need to be erradicated.
We have two regular ServiceStack web services that communicate using SSE. Service A raises certain events to Services B using SSE:
- Service A : Publishes SSE events (on several channels)
- Service B: subscribes to several of the channels.
ServiceStack version 56.
- We run both services as Cloud Services in Azure in production.
- We run both services as Cloud Services in the Azure emulator in development (in IISExpress).
- We are using the default
MemoryServerEvents
at present because theRedisServerEvents
version causes other problems when run. - We currently do not have authentication on, but will need to go there once this problem is understood.
In development, when running on the Azure Emulator environment (in IISExpress), once Service A is started we see this exception every 10-12 seconds.
[iisexpress.exe] 09:58:32 [Error] [] [RecorderLogger.Error] "Error publishing notification to: Crib.Services.Profile.Changed@cmd.onJoin": HttpException { WebEventCode: 0, ErrorCode: -2147023667, Message: "The remote host closed the connection. The error code is 0x800704CD.", Data: [], InnerException: null, TargetSite: Void RaiseCommunicationError(Int32, Boolean), StackTrace: " at System.Web.Hosting.IIS7WorkerRequest.RaiseCommunicationError(Int32 result, Boolean throwOnDisconnect)
at System.Web.Hosting.IIS7WorkerRequest.ExplicitFlush()
at System.Web.HttpResponse.Flush(Boolean finalFlush, Boolean async)
at System.Web.HttpWriter.WriteFromStream(Byte[] data, Int32 offset, Int32 size)
at ServiceStack.ServerEventsFeature.<.ctor>b__0(IResponse res, String frame)
at ServiceStack.EventSubscription.Publish(String selector, String message)", HelpLink: null, Source: "System.Web", HResult: -2147023667 }
Where Crib.Services.Profile.Changed is one of the names of one of the channels we publish to.
We see this exception raised in Service B: (again every 10-12 seconds) Presumably they are related?
[iisexpress.exe] 09:58:32 [Error] [] [RecorderLogger.Error] "[SSE-CLIENT] OnExceptionReceived: Unable to connect to the remote server on #user1": WebException { Status: ConnectFailure, Response: null, Message: "Unable to connect to the remote server", Data: [], InnerException: SocketException { ErrorCode: 10061, Message: "No connection could be made because the target machine actively refused it 127.0.0.1:4433", SocketErrorCode: ConnectionRefused, NativeErrorCode: 10061, Data: [], InnerException: null, TargetSite: Void EndConnect(System.IAsyncResult), StackTrace: " at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception)", HelpLink: null, Source: "System", HResult: -2147467259 }, TargetSite: System.Net.WebResponse EndGetResponse(System.IAsyncResult), StackTrace: " at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at ServiceStack.HttpUtils.<>c__DisplayClass16.<GetResponseAsync>b__14(IAsyncResult iar)", HelpLink: null, Source: "System", HResult: -2146233079 }
In Service A, we are raising the SSE event like this during a service PUT call of one of its services:
ServerEvents.NotifyChannel("Crib.Services.Verification.Changed", new CreateUpdateVerificationEvent());
In Service B, we are listening for this event with a ServerEventsClient
like this, using an instance of a GlobalReceiver
. That is configured once in the AppHost.Configure().
var baseUrl = "https://localhost:4433/api"; //URL of Service A
this.serverEventsClient = new ServerEventsClient(baseUrl, Channels.All);
this.serverEventsClient.RegisterReceiver<GlobalReceiver>();
container.RegisterAutoWiredTypes(this.serverEventsClient.ReceiverTypes);
this.serverEventsClient.Resolver = container;
this.serverEventsClient.Start();
The GlobaReciever looks like this:
internal class GlobalReceiver : ServerEventReceiver
{
public void UpdateVerification(UpdateVerificationEvent verification)
{
try
{
// Doing something useful with the event here
}
catch (Exception ex)
{
// Log the exception
}
}
public override void NoSuchMethod(string selector, object message)
{
base.NoSuchMethod(selector, message);
// Log the exception
}
}
The assumptions with this setup are:
- The SSE Server (
IServerEvents
) can publish during any web service request Service A handles. - The SSEClient (
ServerEventsClient
) will always be alive and listening to its subscribed channels, since its instance is created in the AppHost.Configure() method of Service B.
What could be going wrong here?