Actor模式是一种并发模型,与另一种模型共享内存完全相反,使用者不需要考虑资源抢占问题,每个Actor都是一个单线程,但是使用非线程安全的接口或数据还需要通过单一Actor进行处理
使用Actor模型的好处:- 事件模型驱动--Actor之间的通信是异步的,即使Actor在发送消息后也无需阻塞或者等待就能够处理其他事情
- 强隔离性--Actor中的方法不能由外部直接调用,所有的一切都通过消息传递进行的,从而避免了Actor之间的数据共享,想要
观察到另一个Actor的状态变化只能通过消息传递进行询问 - 位置透明--无论Actor地址是在本地还是在远程机上对于代码来说都是一样的
- 轻量性--Actor是非常轻量的计算单机,单个Actor仅占400多字节,只需少量内存就能达到高并发
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);
});
}
}
}