Im playing with using akka.Net to develop a plugin architecture whereby each dll that contains one or more plugins is loaded into its own AppDomain and a new actor system is initialized ready to recieve messages from the "Host".
I become unstuck trying to get this to work with multiple plugins.
So the Host config looks like:
akka {
actor {
provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
}
remote {
helios.tcp {
transport-class = ""Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote""
applied-adapters = []
transport-protocol = tcp
port = 50003
hostname = localhost
}
}
}
And the plugin config looks like:
akka {
actor {
provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
}
remote {
helios.tcp {
transport-class = ""Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote""
applied-adapters = []
transport-protocol = tcp
port = 50004
hostname = localhost
}
}
(there are many of these)
My question is how do i get messages from the Host to all of the plugins?
The best recommendation is to use Akka.Cluster. Here's a well-documented example: https://github.com/petabridge/akkadotnet-code-samples/tree/master/Cluster.WebCrawler
Edit - removed suggestion to use dynamic port. Much better off using static ones so node reboots can be handled correctly.
Have each plugin config use a plugin-specific port (akka.remote.helios.tcp.port = 1231) and then define a clustered router that talks to actor systems fulfilling specific roles.
/api/broadcaster {
router = broadcast-group
routees.paths = ["user/api"]
cluster {
enabled = on
max-nr-of-instances-per-node = 1
allow-local-routees = on
use-role = crawler
}
}
This router, deployed at the path user/api/broadcaster on some node can communicate (via the Broadcast routing strategy) with any actor deployed at path user/api on any node in the cluster with role crawler without needing to look up IP addresses, ports, or any of that crap.
You configure a node's clustering information via the following section in Akka.NET's config:
cluster {
#manually populate other seed nodes here, i.e. "akka.tcp://lighthouse#127.0.0.1:4053"
seed-nodes = ["akka.tcp://webcrawler#127.0.0.1:4053"]
roles = [crawler]
}
Seed nodes - has to be a well-known, statically-defined port and IP address. Read the article for an explanation on why this is important.
Roles - comma-delimited strings that define what this particular nodes' capabilities are. They're more like tags. You can use them inside clustered routers (like the one I showed earlier) to articulate which types of nodes you want to communicate with.
Related
The Environment:
Nodes: 5
Replication: 5
Consistency: 1
The code:
using System;
using Cassandra;
namespace CassandraSelectTest
{
class Program
{
static void Main(string[] args)
{
var cluster = Cluster.Builder()
.AddContactPoints("192.168.0.18","192.168.0.21","192.168.0.22","192.168.0.23","192.168.0.24")
.WithPort(9042)
.Build();
var session = cluster.Connect("test_keyspace");
var results = session.Execute("SELECT * FROM test_table");
foreach(var result in results)
{
Console.WriteLine(result.GetValue<string>("col1"));
}
Console.WriteLine($"Finished");
Console.ReadKey();
}
}
}
The problem:
Some of the nodes ideally need to be highly portable, which results in the IP address of the node changing when it is in a different location, and then changing back to its normal IP address when back to its original location. This happens a few times a week.
The question:
Is it possible to configure a single node to have multiple IP addresses, or dynamic addresses which change automatically?
I think that in that scenario the driver will receive a protocol event which will make the driver refresh the cluster's topology. As long as the node's IP is up to date in system.peers table, i.e., you update broadcast_rpc_address / rpc_address / listen_address on the node's cassandra.yml the driver should be able to notice that the old IP is no longer in that table (which will remove the old host) and that there's a new IP (which will add a new host).
If the control connection is not able to reconnect to any of the IPs on the local metadata cache (e.g. if all the nodes go down and change their IP addresses at the same time) then the driver will not be able to reconnect. There's this open ticket which will resolve this issue as long as the user provides hostnames as the contact points (and DNS resolution returns the updated IPs).
I have two apps on two computers. These apps are communicating by .NET Remoting.
First app acts as server for access to database, second app acts as client and writes that data to another database.
When my client calls some method on my remote object on server, I get following error:
A connection attempt failed because the connected party did not
properly respond after a period of time, or established connection
failed because connected host has failed to respond
192.168.200.60:31621
Well, that's nice, but I don't know anything about IP address 192.168.200.60, I was connecting to 172.XXX.XXX.216. It seems that there are two network connections and it's somehow not good for remoting.
ipcongif on my server look like that:
Exactly the same solution works on another 3 computers with Windows 2000, Windows XP and Windows 7. Server is developed in .NET Framework 2.0.
Client and server have common DLL library with two interfaces ICAOFactory and ICAO. First I create factory CAOFactory, which has method CreateCAO, which returns CAO object. When I call some method oh that ICAO object, it fails.
This is how my server app registers remoting object:
TcpChannel channel = new TcpChannel(31621);
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(My_Server.CAOFactory), "My_Server", WellKnownObjectMode.Singleton);
This is how my client creates remote object:
My_Share.ICAOFactory srvFactory;
My_Share.ICAO srvCAO;
srvFactory = (My_Share.ICAOFactory)Activator.GetObject(typeof(Foerster_Share.ICAOFactory), "tcp://" + ip + ":" + port + "/My_Server");
srvCAO = srvFactory.CreateCAO(); // no problem
srvCAO.Init(dateTime); // problem
This is my CAOFactory object:
public class CAOFactory : MarshalByRefObject, ICAOFactory
{
public ICAO CreateCAO()
{
ICAO CAOObj = new CAO();
return CAOObj;
}
public void GetClientCount(out long clientCountSum, out long clientCountMax, out long clientCountActive)
{
clientCountSum = 0;
clientCountMax = 0;
clientCountActive = 0;
return;
}
public override object InitializeLifetimeService()
{
return null;
}
}
This is my CAO object:
public class CAO : MarshalByRefObject, ICAO
{
public void Ping()
{
return;
}
DateTime dtInit;
public void Init(DateTime dt)
{
dtInit = dt;
}
// + some other methods
}
Any help greatly appreciated!
What version of .NET are you targeting?
I think you need to use the bindTo property of the TcpChannel class https://msdn.microsoft.com/en-us/library/bb187434(v=vs.85).aspx to tell your server to bind to the correct NIC. This is probably most easily done in the configuration. Does your server project have an app.config? If not add one then add a section like this to it (this is copy/pasted from this question .Net Remoting: Indicate which local interface to use to connect to one server)
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="tcp" port="0" bindTo="172.xxx.xxx.xxx" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
This will tell the server to bind to the specific IP address.
Reordering network connection priority helped in my case.
http://ecross.mvps.org/howto/change-network-connection-priority-in-windows-10.htm
Press the Windows Key + X and select Network Connections from the menu.
Press the ALT key, click Advanced and then Advanced Settings.
Select the network connection and click the arrows to give priority to the network connection.
Click Ok when you are done organizing the priority of the network connection. The computer will now follow an order when connections are available.
I have a distributed system of actors, some on Windows, and some on Linux machine. Sometimes one actor may need to connect other actor and make some communications. Of course, there are cases when one of them is on Windows, and other is on Linux system.
Actors connect each other via ActorSelection. There problem is, that when Windows actor is trying to communicate with Linux one, all works fine. But when Linux actor initiating communication, the ActorSelection.ResolveOne failes.
I've made a little sample here:
static void Main(string[] args)
{
ActorSystem system = ActorSystem.Create("TestSystem");
system.ActorOf(Props.Create(() => new ConnectActor()), "test");
while (true)
{
var address = Console.ReadLine();
if (string.IsNullOrEmpty(address))
{
system.Terminate();
return;
}
var remoteAddress = $"akka.tcp://{system.Name}#{address}/user/test";
try
{
var actor = system.ActorSelection(remoteAddress).ResolveOne(TimeSpan.FromMilliseconds(5000)).Result;
Console.WriteLine("Resolved: " + actor.Path);
}
catch (Exception ex)
{
Console.WriteLine("Failed: " + ex.Message);
}
}
}
Configuration in app.config is the following:
akka {
loggers = ["Akka.Logger.NLog.NLogLogger, Akka.Logger.NLog"]
suppress-json-serializer-warning = on
loglevel = "DEBUG"
log-config-on-start = on
actor {
provider = "Akka.Remote.RemoteActorRefProvider, Akka.Remote"
debug {
receive = on
autoreceive = on
lifecycle = on
event-stream = on
unhandled = on
}
}
remote {
log-remote-lifecycle-events = DEBUG
log-received-messages = on
helios.tcp {
transport-class = "Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote"
transport-protocol = tcp
applied-adapters = []
port = 9000
hostname = "0.0.0.0"
public-hostname = "192.168.0.251" // This is different for different hosts, of course
}
}
}
The public-hostname is publicly available ip address.
So, here are the cases:
When running Windows/Windows, both instances see each other (I give them remote address - they output "Resolved ")
When running Windows/Linux, and give linux actor's address to windows actor, it outputs "Resolved". So windows connects linux with no problem. After that giving windows actor's address to linux actor also gives "Resolved" - I suppose, the connection is already established and there is no real handshakes passing
BUT when running Windiws/Linux and give windows actor's address to linux actor, it gives "Failed". No messages about any errors or dropping packages. At the end of the log there is the following:
Akka.Remote.Transport.AkkaProtocolManager|now supervising akka://TestSystem/system/transports/akkaprotocolmanager.tcp.0/akkaProtocol-tcp%3A%2F%2FTestSystem%40%5B%3A%3Affff%3A192.168.0.252%5D%3A36983-1||||
13:20:08.3766|DEBUGAkka.Remote.Transport.ProtocolStateActor|Started (Akka.Remote.Transport.ProtocolStateActor)||||
13:20:08.3922|DEBUG|Akka.Remote.Transport.ProtocolStateActor|Stopped||||
The issue with similar logs is described here: Akka.net starting and stopping with no activity
The reason there is that system protocols are not compatible. Is this the same issue? As I got from Akka.NET docs and release notes, it has full linux support...
So, am I missing something in configuration? Can anybody make this sample work with Linux -> Windows connection?
The issue here appears to be that Mono is using an IPV6 address mapped to IPV4 in its bound address for some reason.
akka://TestSystem/system/transports/akkaprotocolmanager.tcp.0/akkaProtocol-tcp%3A%2F%2FTestSystem%40%5B%3A%3Affff%3A192.168.0.252%5D%3A36983-1
If you decode this URL that gets translated to
akkaProtocol-tcp://TestSystem#[::ffff:192.168.0.252]:36983-
So I think what is happening here is that outbound address Helios is supposed to parse from that is getting screwed up on the Linux side, so it attempts to connect to a mal-formed address that isn't the same as the one Windows listens on. Something platform-specific in the actor selection URI parsing code is incorrect I suspect.
I've filed a bug here: https://github.com/akkadotnet/akka.net/issues/2254
I have written a c# application for gathering all XenCenter VMs and populating a database with VMs information for my project.
It looks like this
// Establish XenCenter session
session = new Session(HostName, 80);
// Authenticate with username and password. The third parameter tells the server which API version we support.
session.login_with_password(XenUserName, XenPassword, API_Version.API_1_3);
List<XenRef<Host>> pools = Host.get_all(session);
foreach (XenRef<Host> pool in pools)
{
// Get all records associated with this specific VM server
Host host = Host.get_record(session, pool);
// Get all records associated with this specific VM server resources.
List<XenRef<VM>> residentVms = host.resident_VMs;
foreach (var vm in residentVms)
{
VM vmRecord = VM.get_record(session, vm);
}
From vmRecord I can get some data like RAM size, Operation system, description and etc. But I can't get SR (storage repository) size.
Thanks in advance.
We like to enable some hidden features of our software only if it is run inside of the company network. The key requirements are:
no need for a third party library outside of DotNet 4.5.1
easy to implement (should not be more than some dozens of lines .. I don't want to reimplement a crypto library)
It should be reasonable safe:
at least: hard to reverse engineer
at best: "impossible" to break even with read-access to the source code
low maintenance overhead
Win2012-Server is available for installation of additional software (open source or own implementation prefered - server can be assumed to be safe)
What I have thought about:
Check if a specific PC is available with a known MAC or IP (current implementation, not really secure and some other flaws)
Test, if a service is available on a specific response (i.e. I send 'Hello' to MyServer:12345 - server responses with 'World')
Similar to 2nd but a more complex challenge (i.e. send a seed for a RNG to the server, verify the response)
Set up an apache with HTTPS and verify the certificate
If you use ActiveDirectory, you could add a reference to the System.DirectoryServices namespace and check
ActiveDirectorySite currentSite = ActiveDirectorySite.GetComputerSite();
then you can get a bit of information from the currentSite object and check against that. That's how I enable/disable features of an application I'm developing currently.
I also grab:
var client = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in client.AddressList)
{
if(ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
ipAddress = ip;
}
}
Which you can check to make sure the client is connected with the proper protocol.
I've choosen the last option: Set up a webserver in the intranet and verify the certificate.
It was easier than expected. There are enough tutorials for setting up an apache with https for every supported OS. The self-signed certificate have a lifetime of 9999 days - should be okay until 2042. The C#-part is also reasonable small:
private static bool m_isHomeLocation = false;
public static bool IsHomeLocation
{
get
{
if (m_isHomeLocation)
return true;
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://yourLicenseServer:yourConfiguredPort");
request.ServerCertificateValidationCallback += ((s, certificate, chain, sslPolicyErrors) => true);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
response.Close();
var thumbprint = new X509Certificate2(request.ServicePoint.Certificate).Thumbprint;
m_isHomeLocation = (thumbprint == "WhateverThumbprintYourCertificateHave");
}
catch
{
// pass - maybe next time
}
return m_isHomeLocation;
}
}