Actor模式是一种并发模型,与另一种模型共享内存完全相反,使用者不需要考虑资源抢占问题,每个Actor都是一个单线程,但是使用非线程安全的接口或数据还需要通过单一Actor进行处理

使用Actor模型的好处:
  1. 事件模型驱动--Actor之间的通信是异步的,即使Actor在发送消息后也无需阻塞或者等待就能够处理其他事情
  2. 强隔离性--Actor中的方法不能由外部直接调用,所有的一切都通过消息传递进行的,从而避免了Actor之间的数据共享,想要
    观察到另一个Actor的状态变化只能通过消息传递进行询问
  3. 位置透明--无论Actor地址是在本地还是在远程机上对于代码来说都是一样的
  4. 轻量性--Actor是非常轻量的计算单机,单个Actor仅占400多字节,只需少量内存就能达到高并发
测试程序截图

actor模型使用,.net的AKKA使用_.Net

测试程序主要代码

Server

//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Akka.NET Project">
//     Copyright (C) 2009-2020 Lightbend Inc. <http://www.lightbend.com>
//     Copyright (C) 2013-2020 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using Akka.Actor;
using Akka.Configuration;
using ChatMessages;

namespace ChatServer
{
    class Program
    {
        static void Main(string[] args)
        {
            var config = ConfigurationFactory.ParseString(@"
akka {  
    actor {
        provider = remote
    }
    remote {
        dot-netty.tcp {
            port = 8081
            hostname = 0.0.0.0
            public-hostname = localhost
        }
    }
}
");

            using (var system = ActorSystem.Create("MyServer", config))
            {
                system.ActorOf(Props.Create(() => new ChatServerActor()), "ChatServer");

                Console.ReadLine();
            }
        }
    }

    class ChatServerActor : ReceiveActor, ILogReceive
    {
        private readonly HashSet<IActorRef> _clients = new HashSet<IActorRef>();

        public ChatServerActor()
        {
            Receive<SayRequest>(message =>
            {
                var response = new SayResponse
                {
                    Username = message.Username,
                    Text = message.Text,
                };
                foreach (var client in _clients) client.Tell(response, Self);
            });

            Receive<int>(message =>
            {
                Console.WriteLine($"Heart={message.ToString()}");
                Sender.Tell("发给发送者的消息!");
                Self.Tell("发给自己的消息!");
            });

            Receive<string>(message =>
            {
                Console.WriteLine($"Printf Heart={message.ToString()}");
            });

            Receive<ConnectRequest>(message =>
            {
                _clients.Add(Sender);
                Sender.Tell(new ConnectResponse
                {
                    Message = "Hello and welcome to Akka.NET chat example",
                }, Self);
                Self.Tell(_clients.Count);
            });

            Receive<NickRequest>(message =>
            {
                var response = new NickResponse
                {
                    OldUsername = message.OldUsername,
                    NewUsername = message.NewUsername,
                };

                foreach (var client in _clients) client.Tell(response, Self);
            });
        }
    }
}

Message

//-----------------------------------------------------------------------
// <copyright file="Messages.cs" company="Akka.NET Project">
//     Copyright (C) 2009-2020 Lightbend Inc. <http://www.lightbend.com>
//     Copyright (C) 2013-2020 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using Akka.Actor;

namespace ChatMessages
{
    public class ConnectRequest
    {
        public string Username { get; set; }
    }

    public class ConnectResponse
    {
        public string Message { get; set; }
    }

    public class NickRequest
    {
        public string OldUsername { get; set; }
        public string NewUsername { get; set; }
    }

    public class NickResponse
    {
        public string OldUsername { get; set; }
        public string NewUsername { get; set; }
    }

    public class SayRequest
    {
        public string Username { get; set; }
        public string Text { get; set; }
    }

    public class SayResponse
    {
        public string Username { get; set; }
        public string Text { get; set; }
    }
}

client

//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Akka.NET Project">
//     Copyright (C) 2009-2020 Lightbend Inc. <http://www.lightbend.com>
//     Copyright (C) 2013-2020 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Linq;
using Akka.Actor;
using Akka.Configuration;
using ChatMessages;

namespace ChatClient
{
    class Program
    {
        static void Main(string[] args)
        {
            var config = ConfigurationFactory.ParseString(@"
akka {  
    actor {
        provider = remote
    }
    remote {
        dot-netty.tcp {
		    port = 0
		    hostname = localhost
        }
    }
}
");

            using (var system = ActorSystem.Create("MyClient", config))
            {
                var chatClient = system.ActorOf(Props.Create<ChatClientActor>());
                chatClient.Tell(new ConnectRequest()
                {
                    Username = "Roggan",
                });

                while (true)
                {
                    var input = Console.ReadLine();
                    if (input.StartsWith("/"))
                    {
                        var parts = input.Split(' ');
                        var cmd = parts[0].ToLowerInvariant();
                        var rest = string.Join(" ", parts.Skip(1));

                        if (cmd == "/nick")
                        {
                            chatClient.Tell(new NickRequest
                            {
                                NewUsername = rest
                            });
                        }
                        if (cmd == "/exit")
                        {
                            Console.WriteLine("exiting");
                            break;
                        }
                    }
                    else
                    {
                        chatClient.Tell(new SayRequest()
                        {
                            Text = input,
                        });
                    }
                }

                system.Terminate().Wait();
            }
        }
    }

    class ChatClientActor : ReceiveActor, ILogReceive
    {
        private string _nick = "Roggan";
        private readonly ActorSelection _server = Context.ActorSelection("akka.tcp://MyServer@localhost:8081/user/ChatServer");

        public ChatClientActor()
        {
            Receive<ConnectRequest>(cr =>
            {
                Console.WriteLine("Connecting....");
                _server.Tell(cr);
            });

            Receive<ConnectResponse>(rsp =>
            {
                Console.WriteLine("Connected!");
                Console.WriteLine(rsp.Message);
            });

            Receive<NickRequest>(nr =>
            {
                nr.OldUsername = _nick;
                Console.WriteLine("Changing nick to {0}", nr.NewUsername);
                _nick = nr.NewUsername;
                _server.Tell(nr);
            });

            Receive<NickResponse>(nrsp =>
            {
                Console.WriteLine("{0} is now known as {1}", nrsp.OldUsername, nrsp.NewUsername);
            });

            Receive<SayRequest>(sr =>
            {
                sr.Username = _nick;
                _server.Tell(sr);
            });

            Receive<SayResponse>(srsp =>
            {
                Console.WriteLine("{0}: {1}", srsp.Username, srsp.Text);
            });
        }
    }
}