How to split Akka.NET remoting solution to two solutions? - c#

I have made 2 Akka.NET solutions in the hope of testing out Remoting on a simple hello world example, however, I keep getting a Disassociated exception when the communication attempt is made. I have reason to believe that this is because of the shared class Greet which should be a message that both systems should understand. Unfortunately, they don't. How can I fix this?
This is the code of the "Server" application:
namespace Shared
{
public class Greet
{
public string Who { get; set; }
public Greet(string who)
{
Who = who;
}
}
}
namespace AkkaTest
{
using Shared;
class GreeterActor : ReceiveActor
{
public GreeterActor()
{
Receive<Greet>(x => Console.WriteLine("Hello {0}", x.Who));
}
}
class Program
{
static void Main(string[] args)
{
var config = ConfigurationFactory.ParseString(#"
akka {
actor.provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
remote {
helios.tcp {
port = 9099
hostname = 127.0.0.1
}
}
}
");
using (ActorSystem system = ActorSystem.Create("MyServer", config))
{
system.ActorOf<GreeterActor>("greeter");
Console.ReadLine();
system.Shutdown();
}
}
}
}
Here is the code for the client:
namespace Shared
{
public class Greet
{
public string Who { get; set; }
public Greet(string who)
{
Who = who;
}
}
}
namespace AkkaTest
{
using Shared;
class Program
{
static void Main(string[] args)
{
var config = ConfigurationFactory.ParseString(#"
akka {
actor.provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
remote {
helios.tcp {
port = 9090
hostname = 127.0.0.1
}
}
}
");
using (var system = ActorSystem.Create("MyClient", config))
{
//get a reference to the remote actor
var greeter = system
.ActorSelection("akka.tcp://MyServer#127.0.0.1:9099/user/greeter");
//send a message to the remote actor
greeter.Tell(new Greet("Roger"));
Console.ReadLine();
}
}
}
}
EDIT: Putting both client and server in the same solution but different projects, and the GreetingActor and Greet in a shared projects fixes the issues. However, I would like to have completely separate solutions.

If you are using Greet messages on both sides, you need to provide some way do share this message schema between them. Usually this is done as a separate project shared between other projects or solutions.
While default Akka.NET serializer uses fully qualified type name with assembly to serialize/deserialize messages, it's also version tolerant - you can modify message schema and gradually update it's assembly node by node.
Other option is to use custom serializer. This way you'll be able to determine by yourself, how message will be serialized/deserialized on both ends. You can read more about this topic here.

Related

EasyNetQ Publish method not found

I have been going through the set up for EasyNetQ https://github.com/EasyNetQ/EasyNetQ/wiki/Quick-Start
The following code
using System;
using EasyNetQ;
static void Main(string[] args)
{
using (var bus = RabbitHutch.CreateBus("host=localhost"))
{
var input = "";
Console.WriteLine("Enter a message. 'Quit' to quit.");
while ((input = Console.ReadLine()) != "Quit")
{
bus.Publish(new TextMessage
{
Text = input
});
}
Throws this error, on the bus.Publish line
Error CS1929 'IBus' does not contain a definition for 'Publish' and the best extension method overload 'PubSubExtensions.Publish(IPubSub, TextMessage, CancellationToken)' requires a receiver of type 'IPubSub' PetStore.StockDelivery.Publish
I have EasyNetQ installed
<ItemGroup>
<PackageReference Include="EasyNetQ" Version="5.3.0-alpha0074" />
</ItemGroup>
I added this through nuget and got RabbitMQ and other dependancys
When I look at what is available on IBus
IPubSub PubSub { get; }
IRpc Rpc { get; }
ISendReceive SendReceive { get; }
IScheduler Scheduler { get; }
IAdvancedBus Advanced { get; }
Is there something I am missing?
I aslo have RabbitMQ server running okay on my mashine on localhost and I have tested that works
They moved it under PubSub property. See code example below:
public class Publisher<T>
{
IBus bus;
public Publisher()
{
bus = RabbitHutch.CreateBus("host=localhost");
}
public async void Send(T message)
{
await bus.PubSub.PublishAsync(message).ConfigureAwait(false);
}
}

PushSharp Separation of Concerns

I'm currently working on a C# web application and I'm trying to get push notifications to work using the PushSharp package. I have all of my code for pushing notifications in the Global.asax file in my project, but I keep getting the error:
The collection has been marked as complete with regards to additions.
Here is my Global.asax file:
using BYC.Models;
using BYC.Models.Enums;
using Newtonsoft.Json.Linq;
using PushSharp.Apple;
using PushSharp.Google;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace BYC
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
protected void Application_End()
{
PushBrokerSingleton pbs = new PushBrokerSingleton();
pbs.SendQueuedNotifications();
}
}
public sealed class PushBrokerSingleton
{
private static ApnsServiceBroker Apns { get; set; }
private static GcmServiceBroker Gcm { get; set; }
private static bool ApnsStarted = false;
private static bool GcmStarted = false;
private static object AppleSyncVar = new object();
private static object GcmSyncVar = new object();
private static readonly log4net.ILog log = log4net.LogManager.GetLogger
(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public PushBrokerSingleton()
{
if (Apns == null)
{
string thumbprint = (AppSettings.Instance["APNS:Thumbprint"]);
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
ApnsConfiguration.ApnsServerEnvironment production = Convert.ToBoolean(AppSettings.Instance["APNS:Production"]) ?
ApnsConfiguration.ApnsServerEnvironment.Production : ApnsConfiguration.ApnsServerEnvironment.Sandbox;
X509Certificate2 appleCert = store.Certificates
.Cast<X509Certificate2>()
.SingleOrDefault(c => string.Equals(c.Thumbprint, thumbprint, StringComparison.OrdinalIgnoreCase));
ApnsConfiguration apnsConfig = new ApnsConfiguration(production, appleCert);
Apns = new ApnsServiceBroker(apnsConfig);
Apns.OnNotificationFailed += (notification, aggregateEx) => {
aggregateEx.Handle(ex => {
// See what kind of exception it was to further diagnose
if (ex is ApnsNotificationException)
{
var notificationException = ex as ApnsNotificationException;
// Deal with the failed notification
var apnsNotification = notificationException.Notification;
var statusCode = notificationException.ErrorStatusCode;
log.Error($"Notification Failed: ID={apnsNotification.Identifier}, Code={statusCode}");
}
else {
// Inner exception might hold more useful information like an ApnsConnectionException
log.Error($"Notification Failed for some (Unknown Reason) : {ex.InnerException}");
}
// Mark it as handled
return true;
});
};
Apns.OnNotificationSucceeded += (notification) => {
log.Info("Notification Successfully Sent to: " + notification.DeviceToken);
};
}
if(Gcm == null)
{
GcmConfiguration gcmConfig = new GcmConfiguration(AppSettings.Instance["GCM:Token"]);
Gcm = new GcmServiceBroker(gcmConfig);
}
}
public bool QueueNotification(Notification notification, Device device)
{
if (!ApnsStarted)
{
ApnsStarted = true;
lock (AppleSyncVar)
{
Apns.Start();
}
}
if(!GcmStarted)
{
GcmStarted = true;
lock (GcmSyncVar)
{
Gcm.Start();
}
}
switch (device.PlatformType)
{
case PlatformType.iOS:
return QueueApplePushNotification(notification, device.PushRegistrationToken);
case PlatformType.Android:
return QueueAndroidPushNotification(notification, device.PushRegistrationToken);
default: return false;
}
}
private bool QueueApplePushNotification(Notification notification, string pushNotificationToken)
{
string appleJsonFormat = "{\"aps\": {\"alert\":" + '"' + notification.Subject + '"' + ",\"sound\": \"default\", \"badge\": " + notification.BadgeNumber + "}}";
lock (AppleSyncVar)
{
Apns.QueueNotification(new ApnsNotification()
{
DeviceToken = pushNotificationToken,
Payload = JObject.Parse(appleJsonFormat)
});
}
return true;
}
private bool QueueAndroidPushNotification(Notification notification, string pushNotificationToken)
{
string message = "{\"alert\":\"" + notification.Subject + "\",\"badge\":" + notification.BadgeNumber + "\"}";
lock (GcmSyncVar)
{
Gcm.QueueNotification(new GcmNotification()
{
RegistrationIds = new List<string>
{
pushNotificationToken
},
Data = JObject.Parse(message),
Notification = JObject.Parse(message)
});
}
return true;
}
public void SendQueuedNotifications()
{
if(Apns != null)
{
if (ApnsStarted)
{
lock(AppleSyncVar){
Apns.Stop();
log.Info("Sent Apns Notifications");
ApnsStarted = false;
}
}
}
if(Gcm != null)
{
if (GcmStarted)
{
lock (GcmSyncVar)
{
Gcm.Stop();
log.Info("Sent Gcm Notifications");
GcmStarted = false;
}
}
}
}
}
}
That happens when you try and reuse an instance of a service broker (eg: ApnsServiceBroker) which Stop() has been called on.
I'm guessing your Application_End is getting called at some point and Application_Start gets called again, but since PushBrokerSingleton.Apns is not null (it's a static field so it must live on even though the Application has stopped/started), it never gets recreated.
PushSharp is a hard thing to make work nicely with the ASP.NET pattern, some sort of service daemon would be better.
The main issue is that your app may be recycled or ended when you don't expect it to. Unrelated requests in the same app can take down your process, or your AppDomain may be torn down. If this happens and the brokers' Stop() calls can't end successfully, some queued messages could be lost. Here's a great article on some of the caveats: http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/ In practice, this may not be a big deal, and you can certainly mitigate parts of it, but keep it in mind.
Having said all that, I think a simple fix would be to create a new instance of PushBrokerSingleton.Apns and PushBrokerSingleton.Gcm in your Application_Start. This may cause other issues for you so I'm not sure if it's the right fix, but it will work around the issue that the broker is not meant to be reused after Stop() has been called.
I'm also going to consider adding some way to 'reset' the collection. I'm not sure if doing this automatically after .Stop() ends is a good idea, but I may look at adding a .Reset() or similar kind of method to achieve this. In any case, creating a new broker instance is perfectly acceptable for now.

MassTransit - PublishFault stops working when Message Q is down

I am connecting a publish observer, using the code below (and RabbitMQ). When RabbitMQ is running, it works well - I can see the console messages on PostPublish and PrePublish.
However, when i stop RabbitMQ, and publish, the PublishFault works once, but never again whilst RabbitMQ remains stopped.
I am attempting to persist a message to another datastore (and log and error), in the event of a publish failing - I thought that the PublishFault method would be the best place to do this. This doesn't really work if only the first failure is detected.
Is this behaviour expected? Is there a better way to achieve failed message persistance.
PS...as soon as I start RabbitMQ again, I then see all my PrePublish and PostPublish debug messages , for my failed messages. Which, I assume, is to be expected.
using MassTransit;
using MassTransit.Pipeline;
using System;
using System.Threading.Tasks;
namespace Mtt.Publisher
{
class Program
{
static void Main(string[] args)
{
IBusControl busControl = Bus.Factory.CreateUsingRabbitMq(sbc =>
{
var host = sbc.Host(new Uri("rabbitmq://localhost"), h =>
{
h.Username("user");
h.Password("pass");
});
sbc.UseRetry(Retry.Immediate(5));
});
busControl.Start();
busControl.ConnectPublishObserver(new PublishObserver());
var input = "";
while (input != "exit")
{
input = Console.ReadLine();
busControl.Publish<Test>(new TestMessage());
}
busControl.Stop();
}
}
public interface Test { }
public class TestMessage : Test { }
public class PublishObserver : IPublishObserver
{
public async Task PostPublish<T>(MassTransit.PublishContext<T> context) where T : class
{
Console.WriteLine("--- POST PUBLISH ----");
}
public async Task PrePublish<T>(MassTransit.PublishContext<T> context) where T : class
{
Console.WriteLine("**** PRE PUBLISH ****");
}
public async Task PublishFault<T>(MassTransit.PublishContext<T> context, Exception exception) where T : class
{
Console.WriteLine("%%%%%% EXCEPTION %%%%%%%");
}
}
}

Detecting MSMQ errors

I have this following code (verbatim) that I expect to generate a error:
using System.Messaging;
namespace MsmqTest
{
class Program
{
static void Main()
{
string invalidQueue = #"FormatName:DIRECT=OS:sometrahsname\private$\anothertrahsname";
Enqueue("test",invalidQueue);
}
private static void Enqueue(object o, string queueName)
{
using (MessageQueue msmq = new MessageQueue(queueName))
using (MessageQueueTransaction transaction = new MessageQueueTransaction())
{
msmq.DefaultPropertiesToSend.Recoverable = true;
transaction.Begin();
msmq.Send(new Message(o), transaction);
transaction.Commit();
}
}
}
}
Here we are sending a message to a queue on a server that does not exist. I expect to receive an indication that something went wrong. I do not get any. How do I check for error in this scenario?
Note: in order to run the code above you need to have MSMQ installed on your machine.
I stumbled about this problem before in one of my applications. MSDN documented that in MessageQueue.Send, the message might be sent to the dead-letter queue without throwing an exception. And that is what you are experiencing right now. What I did is to check if the queue exists.
using System;
using System.Messaging;
namespace MsmqTest
{
class Program
{
static void Main(string[] args)
{
string invalidQueue = #"FormatName:DIRECT=OS:sometrahsname\private$\anothertrahsname";
Enqueue("test", invalidQueue);
}
private static void Enqueue(object o, string queueName)
{
try
{
MessageQueue msmq = null;
//check if queueName exists
//it also validates if you have access to MSMQ server
if (!MessageQueue.Exists(queueName))
{
msmq = MessageQueue.Create(queueName);
//you can also set the permission here
//because the other application that may be reading
//has different account with the application that created the queue
//set to Everyone for demonstration purposes
msmq.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
}
else
{
msmq = new MessageQueue(queueName);
}
using (msmq)
{
using (MessageQueueTransaction transaction = new MessageQueueTransaction())
{
msmq.DefaultPropertiesToSend.Recoverable = true;
transaction.Begin();
msmq.Send(new Message(o), transaction);
transaction.Commit();
}
}
}catch(Exception)
{
//handle error here
}
}
}
}

Send Message to other Process

I want to be able to communicate between a Server-Application and a Client-Application. Both applications are written in C#/WPF. Interfaces are located in a separate DLL where both applications have a reference to it.
In the interface-dll is the IDataInfo-Interface which looks like:
public interface IDataInfo
{
byte[] Header { get; }
byte[] Data { get; }
}
The Server-Application calls the client by the following code:
Serializer<IDataInfo> serializer = new Serializer<IDataInfo>();
IDataInfo dataInfo = new DataInfo(HEADERBYTES, CONTENTBYTES);
Process clientProcess = Process.Start("Client.exe", serializer.Serialize(dataInfo));
The Client-Applications gets the message from the server by:
Serializer<IDataInfo> serializer = new Serializer<IDataInfo>();
IDataInfo dataInfo = serializer.Deserialize(string.Join(" ", App.Args));
The Serializer-Class is just a generic class which uses the Soap-Formatter to serialize/deserialze. The code looks like:
public class Serializer<T>
{
private static readonly Encoding encoding = Encoding.Unicode;
public string Serialize(T value)
{
string result;
using (MemoryStream memoryStream = new MemoryStream())
{
SoapFormatter soapFormatter = new SoapFormatter();
soapFormatter.Serialize(memoryStream, value);
result = encoding.GetString(memoryStream.ToArray());
memoryStream.Flush();
}
return result;
}
public T Deserialize(string soap)
{
T result;
using (MemoryStream memoryStream = new MemoryStream(encoding.GetBytes(soap)))
{
SoapFormatter soapFormatter = new SoapFormatter();
result = (T)soapFormatter.Deserialize(memoryStream);
}
return result;
}
}
Until here everything works fine. The server creates the client and the client can deserialize it's argument to the IDataInfo-Object.
Now I want to be able to send a message from the server to a running client. I Introduced the IClient-Interface in the Interface-DLL with the method void ReceiveMessage(string message);
The MainWindow.xaml.cs is implementing the IClient-Interface.
My Question is now how can I get the IClient-Object in my server, when I just have the Process-Object. I thought about Activator.CreateInstance, but I have no clue how to do this. I'm pretty sure that I can get the IClient by the Handle of the Process, but I don't know how.
Any idea?
As the other posts mention a common way is to create a service,
too keep it more simple I would consider a look at ServiceStack. AFAIK ServiceStack is used on stackoverflow
There also as course about it on pluralsight
ServiceStack is really easy to host in any .net dll (without iis and so on) and doesn't have the configuration complexity of WCF.
Also endpoints are available as SOAP and REST without the need to configure anything
For Example this defines a hello world service
public class HelloService : IService<Hello>
{
public object Execute(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
Here an example of the client code:
var response = client.Send<HelloResponse>(new Hello { Name = "World!" });
Console.WriteLine(response.Result); // => Hello, World
You can find more
complex examples and walk-throughs at: ServiceStack.Hello
Communication that between multi processed have many waies to implement.
Like socket, fileMapping, share memory, windows 32 message and so on.
Maybe sample way is you can use WCF.
There are many ways to do inter process communication,
but if you are looking for a quick and easy solution you may want to look at ZeroMQ.
WCF is also an option but it might be overkill in your situation.
You can find more information about ZeroMQ here: http://www.zeromq.org/
And you can install it into your project using NuGet.
A quick example with a server and a client:
The server listens for connections, expects a string, reverses the string and returns it:
public class Server
{
public Server()
{
}
public void Listen()
{
Task.Run(() =>
{
using (var context = new Context())
{
//Open a socket to reply
using (var socket = context.Socket(SocketType.REP))
{
socket.Bind("tcp://127.0.0.1:32500");
while (true)
{
//Again you could also receive binary data if you want
var request = socket.Recv(Encoding.UTF8);
var response = ReverseString(request);
socket.Send(response, Encoding.UTF8);
}
}
}
});
}
private string ReverseString(string request)
{
var chars = request.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}
The client connects to the server (in this case the same machine):
public class Client
{
public Client()
{
}
public string ReverseString(string message)
{
using (var context = new Context())
{
//Open a socket to request data
using (var socket = context.Socket(SocketType.REQ))
{
socket.Connect("tcp://127.0.0.1:32500");
//Send a string, you can send a byte[] as well, for example protobuf encoded data
socket.Send(message, Encoding.UTF8);
//Get the response from the server
return socket.Recv(Encoding.UTF8);
}
}
}
}
To test it, the program might look like this:
public class Program
{
public static void Main()
{
new Program();
}
public Program()
{
var server = new Server();
server.Listen();
var client = new Client();
var input = String.Empty;
while (input != "/quit")
{
input = Console.ReadLine();
Console.WriteLine(client.ReverseString(input));
}
}
}
It's easy and it gets the job done.
Another alternative is to use named pipes for IPC: http://www.codeproject.com/Tips/492231/Csharp-Async-Named-Pipes

Categories