public static void CreateServerAndClient <T, I>(T impl, out IActorServerProxy server, out IActorClientProxy <I> client) { server = ActorServerProxy.Create("tcp://*:0", impl); int port = server.BindEndPoint.Port; client = ActorClientProxy.CreateProxy <I>("tcp://localhost:" + port).Result; }
public static void CreateServerAndClientProxy <T, I>(out IActorServerProxy server, out IActorClientProxy <I> client) where T : new() { server = ActorServerProxy.Create <T>("tcp://*:0"); int port = server.BindEndPoint.Port; client = ActorClientProxy.CreateProxy <I>("tcp://localhost:" + port).Result; }
public async void Client_should_be_disconnected_after_over_30_seconds_after_last_ping() { var disconnected = new ManualResetEventSlim(); server = ActorServerProxy.Create <TestActor>("tcp://*:0"); int port = server.BindEndPoint.Port; client = new FramedClient(new SocketClient()); await client.Connect("tcp://localhost:" + port); client.Received.Subscribe(x => Console.WriteLine("Received " + x.Count + " bytes. " + "Header: " + BitConverter.ToInt32(x.Array, x.Offset) + " " + "Id: " + BitConverter.ToInt32(x.Array, x.Offset + 4))); client.Disconnected.Subscribe(_ => disconnected.Set()); SendHandshake(); SendPing(); Thread.Sleep(40000); Assert.False(disconnected.IsSet); disconnected.Wait(40000); Assert.True(disconnected.IsSet); }
static void Main(string[] args) { // Note: In samples below, .Result is used to force eager evaluation, however, // async / await patterns could be used as well. // Creating and starting server proxy for an actor is easy. // Returned reference can be used to stop server. var actorServer = ActorServerProxy.Create <CalculatorActor>("tcp://*:4632"); // To create client proxy and connect to the server just pass an interface which describes // what method actor supports. Returned object implements given interface, // so one can use one interface to call local and remote actors. // Returned task is signalled when actor successfully connects to the server. ICalculatorActor calculator = ActorClientProxy.CreateActor <ICalculatorActor>("tcp://localhost:4632").Result; { // Task will throw an exception if it won't be able to connect to the server. try { ICalculatorActor wrongAddress = ActorClientProxy.CreateActor <ICalculatorActor>("tcp://127.0.0.1:45662").Result; } catch (AggregateException exc) { Console.WriteLine(); Console.WriteLine("Could not connect to server: " + exc.InnerException.Message); } } { // Messages can be sent just by calling methods. // Returned tasks will be notified when network response will be received // from server. Console.WriteLine(); Console.WriteLine("5 + 4 = " + calculator.Add(5, 4).Result); } { // Primitive types are serialized automatically, if custom classes are to be used // they should be implemented with ProtoContract and ProtoMember attributes, // so that Protobuf-net will know how to serialize them. // Complex objects can be used both as parameters and results. var rectInfo = calculator.CalculateRectangle(new Rectangle { A = 5, B = 6 }).Result; Console.WriteLine(); Console.WriteLine("Recangle 5 x 6 has field equal = {0} and perimeter = {1}", rectInfo.Field, rectInfo.Perimeter); } { // Because actor called on the server side is always the same instance // (until client has been reconnected, which gives no warranty on state of server actor) // it can be used to persist state just as local actor can. // Note: You don't need to wait for task to finish, messages are invoked on the server // in the order they were called by client proxy. calculator.PushNumber(12.0); calculator.PushNumber(15.0); var x = calculator.PopNumber(); var y = calculator.PopNumber(); Console.WriteLine(); Console.WriteLine("15 - 12 = " + calculator.Subtract(x.Result, y.Result).Result); } { // Any errors will be serialized and sent back to the client proxy. // No number on stack, should throw. // Note, that Task aggregates exception, however, when using async/await pattern // they will be automatically unwrapped. var error = calculator.PopNumber(); try { error.Wait(); } catch (AggregateException exc) { Console.WriteLine(); Console.WriteLine("Error while popping from stack: " + exc.InnerException.Message); } } { // Remote actor still acts as an actor when it comes to message execution, which is serial. // This means, as with local actors, you can queue messages safetly, without worrying // about order execution and multithreading errors. Console.WriteLine(); Console.WriteLine("Pinging..."); var p1 = calculator.Ping(); var p2 = calculator.Ping(); var p3 = calculator.Ping(); Task.WhenAll(p1, p2, p3).Wait(); Console.WriteLine("Pinging complete"); } { // Arrays and even enumerables can be used as parameters var mean = calculator.Mean(new[] { 5.0, 4.0, 3.0, 1.0, 14.0 }).Result; var mean2 = calculator.MeanEnum((new List <double> { 5.0, 4.0, 3.0, 1.0, 14.0 }).AsEnumerable()).Result; Console.WriteLine(); Console.WriteLine("Mean of [1, 3, 4, 5, 14] equals " + mean); Console.WriteLine("Double check: " + mean2); } { // Because server actors behave just like local ones, you can // choose not to use actor context on called methods // and they will run synchronously, so custom synchronization // (or no synchronization) can be applied. // Note, that this might be an unexpected behaviour // for users, so this functionality should be used // with care. // Long processing methods should be run on // task pool, if actor context won't be used, otherwise // they will block network communication for all // connected clients. Console.WriteLine(); Console.WriteLine("Pinging without actor context..."); var p1 = calculator.PingAsync(); var p2 = calculator.PingAsync(); var p3 = calculator.PingAsync(); Task.WhenAll(p1, p2, p3).Wait(); Console.WriteLine("Pinging complete"); } { // Many client actors may connect to one server. // Their method calls will be scheduled as they would originate // from single client, which means, serial execution will be enforced // automatically. Console.WriteLine(); Console.WriteLine("Pinging with 2 clients"); var calculator2 = ActorClientProxy.CreateActor <ICalculatorActor>("tcp://localhost:4632").Result; var p1 = calculator.Ping(); var p2 = calculator2.Ping(); Task.WhenAll(p1, p2).Wait(); Console.WriteLine("Pinging done"); // Because 'simpler' method for creating an actor was used, // reference to an actor interface was received, instead of to wrapper object. // If wrapper object with control method is needed, returned object // can be cast to IActorClientProxy or IActorClientProxy<Actor Interface> ((IActorClientProxy)calculator2).Close(); } { // Created actor proxy is, from the user's perspective just an interface, // which can also be implemented by local actor, therefore, // where the code executes is fully transparent for other methods. ICalculatorActor localCalc = new CalculatorActor(); Console.WriteLine(); Console.WriteLine("Remote: 4 + 8 = " + Add(calculator, 4, 8)); Console.WriteLine("Local: 4 + 8 = " + Add(localCalc, 4, 8)); } // Properties with IObservable<T> types can be used to // broadcast messages to clients without receiving any // requests first. Console.WriteLine(); Console.WriteLine("Random generator: "); var rng = calculator.Rng.Subscribe(r => Console.WriteLine(r)); Thread.Sleep(4000); rng.Dispose(); Console.WriteLine(); calculator.Messages.Subscribe(m => { Console.WriteLine("A: " + m.X + "; B: " + m.Y + ". Area: " + m.Info.Field); }); Thread.Sleep(3000); // When more control over proxy is needed, CreateProxy method can be used. // This will return the same object as CreateActor method would, but returned // type will be of a wrapper type. var proxyCalc = ActorClientProxy.CreateProxy <ICalculatorActor>("tcp://localhost:4632").Result; // Actual actor implementation can be accessed through .Actor property, // or by simply casting whole proxy to actor interface. // Both method will return the same object. ICalculatorActor proxyCalcActor1 = proxyCalc.Actor; ICalculatorActor proxyCalcActor2 = (ICalculatorActor)proxyCalc; Console.WriteLine(); Console.WriteLine("Two methods of accessing proxy actor return the same object - " + object.ReferenceEquals(proxyCalcActor1, proxyCalcActor2)); actorServer.Stop(); ((IActorClientProxy)calculator).Close(); Console.WriteLine(); Console.WriteLine("Sample finished"); Console.Read(); }
private static async void Start(uint timesToRun) { const int repeatFactor = 500; const long repeat = 3000L * repeatFactor; var processorCount = Environment.ProcessorCount; if (processorCount == 0) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Failed to read processor count.."); return; } if (System.Diagnostics.Debugger.IsAttached) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Warning: Debugger is attached. This has a major performance impact on this benchmark"); Console.ResetColor(); } var serverProxyOptions = new ActorServerProxyOptions(actorSessionInjectionEnabled: false); int workerThreads; int completionPortThreads; ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); Console.WriteLine("Worker threads: {0}", workerThreads); Console.WriteLine("OSVersion: {0}", Environment.OSVersion); Console.WriteLine("ProcessorCount: {0}", processorCount); Console.WriteLine("ClockSpeed: {0} MHZ", CpuSpeed()); Console.WriteLine("Actor Count: {0}", processorCount * 2); Console.WriteLine("Messages sent/received: {0} ({0:0e0})", GetTotalMessagesReceived(repeat)); Console.WriteLine(); //Warm up Console.Write("Local first start time: "); await Benchmark(1, 1, 1, PrintStats.StartTimeOnly, -1, -1, (idx, wfs) => Tuple.Create((IActorServerProxy)null, new Destination(wfs)), (idx, d) => d, (idx, wfs, dest, r, latch) => new PingPongActor(wfs, dest, r, latch)); Console.WriteLine(" ms"); Console.Write("Remote first start time: "); await Benchmark(1, 1, 1, PrintStats.StartTimeOnly, -1, -1, (idx, wfs) => { var dest = new Destination(wfs); return(Tuple.Create(ActorServerProxy.Create("tcp://localhost:" + (54000 + idx), dest), dest)); }, (idx, d) => ActorClientProxy.CreateActor <IDestination>("tcp://localhost:" + (54000 + idx)).Result, (idx, wfs, dest, r, latch) => new PingPongActor(wfs, dest, r, latch)); Console.WriteLine(" ms"); Console.WriteLine(); Console.WriteLine(" Local actor Remote actor"); Console.WriteLine("Throughput, Msgs/sec, Start [ms], Total [ms], Msgs/sec, Start [ms], Total [ms]"); for (var i = 0; i < timesToRun; i++) { var redCountLocalActor = 0; var redCountRemoteActor = 0; var bestThroughputLocalActor = 0L; var bestThroughputRemoteActor = 0L; foreach (var throughput in GetThroughputSettings()) { var result1 = await Benchmark(throughput, processorCount, repeat, PrintStats.LineStart | PrintStats.Stats, bestThroughputLocalActor, redCountLocalActor, (idx, wfs) => Tuple.Create((IActorServerProxy)null, new Destination(wfs)), (idx, d) => d, (idx, wfs, dest, r, latch) => new PingPongActor(wfs, dest, r, latch)); bestThroughputLocalActor = result1.Item2; redCountLocalActor = result1.Item3; Console.Write(", "); var result2 = await Benchmark(throughput, processorCount, repeat, PrintStats.Stats, bestThroughputRemoteActor, redCountRemoteActor, (idx, wfs) => { var dest = new Destination(wfs); return(Tuple.Create(ActorServerProxy.Create("tcp://localhost:" + (54000 + idx), dest, serverProxyOptions), dest)); }, (idx, d) => ActorClientProxy.CreateActor <IDestination>("tcp://localhost:" + (54000 + idx)).Result, (idx, wfs, dest, r, latch) => new PingPongActor(wfs, dest, r, latch)); bestThroughputRemoteActor = result2.Item2; redCountRemoteActor = result2.Item3; Console.WriteLine(); } } Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine("Done.."); }