예제 #1
0
        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;
        }
예제 #2
0
        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;
        }
예제 #3
0
        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);
        }
예제 #4
0
        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();
        }
예제 #5
0
        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..");
        }