разработка серверов и серверных приложений лекция №4

17

Click here to load reader

description

В данной лекции рассмотрена минимальная реализация акторной модели, включающая - отправку сообщений, - создание новых акторов и смену поведения для приема следующего сообщения. Исходный код реализации выложен на https://github.com/hwdtech/HWdTech.DS. Код на C#. При разработке использовались библиотеки: Autofac, NuUnit, Moq

Transcript of разработка серверов и серверных приложений лекция №4

Page 1: разработка серверов и серверных приложений лекция №4

Глава 5. Первая реализация 118

Page 2: разработка серверов и серверных приложений лекция №4

Концепция 119

Page 3: разработка серверов и серверных приложений лекция №4

IMessage 120

public interface IMessage

{

string Target

{

get;

}

}

Page 4: разработка серверов и серверных приложений лекция №4

MessageBus 121

public class MessageBus{

public static void Send(IMessage message){…}

public static void Join(string address, Actor actor

) { … }

}

Page 5: разработка серверов и серверных приложений лекция №4

Actor 122

public abstract class Actor{

public Actor(string address) { … }public Actor() { … }

public void Become(Action<IMessage> handler){ … }public void Receive(IMessage message) { … }

public abstract void Handle(IMessage message);}

Page 6: разработка серверов и серверных приложений лекция №4

Фасад для IoC 123

public class DIContainer{

IContainer container;

public T Resolve<T>(){

return container.Resolve<T>();}

}

Page 7: разработка серверов и серверных приложений лекция №4

Фасад для IoC 124

Файл: RouterImpl/IoCRegistration.csusing Autofac;

namespace HWdTech.DS.Internals.Implementation{

class RouterRegistrationModule : Module{

protected override void Load(ContainerBuilder builder){

IRouter router = new RouterImpl();

builder.RegisterInstance(router).As<IRouter>();}

}}

Page 8: разработка серверов и серверных приложений лекция №4

Фасад для IoC 125

public class DIContainer{

public DIContainer(){

ContainerBuilder builder = new ContainerBuilder();Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();builder.RegisterAssemblyModules(assemblies);this.container = builder.Build();AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoadEventHandler;

}void AssemblyLoadEventHandler(object sender, AssemblyLoadEventArgs args){

LoadAssembly(args.LoadedAssembly);}LoadAssembly(Assembly assembly){

ContainerBuilder builder = new ContainerBuilder();builder.RegisterAssemblyModules(assembly);builder.Update(container);

}}

Page 9: разработка серверов и серверных приложений лекция №4

Реализация MessageBus.Join 126

static IRouter router;

public static void Join(string address, Actor actor){

if (null == router){

router = Singleton<DIContainer>.Instance.Resolve<IRouter>();}

router.RegisterOrReplace(address, actor.Receive);}

Page 10: разработка серверов и серверных приложений лекция №4

Реализация MessageBus.Send 127

static IThreadPool threadPool;

public static void Send(IMessage message){

if (null == threadPool) {threadPool = Singleton<DIContainer>.Instance.Resolve<IThreadPool>();

}threadPool.StartTask(HandleMessage, message);

}static void HandleMessage(object o){

IMessage message = (IMessage) o;if (null == router) {

router = Singleton<DIContainer>.Instance.Resolve<IRouter>();}router.Send(message);

}

Page 11: разработка серверов и серверных приложений лекция №4

Ping-Pong Test 128

[Test]public void PingPongTest(){

ThreadPoolImpl threadPool = new ThreadPoolImpl();RouterImpl router = new RouterImpl();

Mock<IMessage> pingMessage = repository.Create<IMessage>();pingMessage.SetupGet(m => m.Target).Returns("ping");

Actor pongActor = new PongActor(pingMessage.Object);

Mock<IMessage> pongMessage = repository.Create<IMessage>();pongMessage.SetupGet(m => m.Target).Returns("pong");

ManualResetEvent waitSignal = new ManualResetEvent(false);PingActor pingActor = new PingActor(pongMessage.Object, waitSignal);

Assert.True(waitSignal.WaitOne(1000));pingActor.Verify();

}

Page 12: разработка серверов и серверных приложений лекция №4

Ping-Pong Test: Ping Actor 129

class PingActor : Actor{int gotMessages;IMessage pongMessage;ManualResetEvent signal;const int numberOfResponsesShoulgGet = 5;

public PingActor(IMessage pongMessage, ManualResetEvent signal) : base("ping") {this.signal = signal;this.pongMessage = pongMessage; MessageBus.Send(pongMessage);

}public override void Handle(IMessage message) {

++gotMessages;if (numberOfResponsesShoulgGet > gotMessages) {

MessageBus.Send(pongMessage);}else { signal.Set(); }

}public void Verify() {

Assert.AreEqual(numberOfResponsesShoulgGet, gotMessages);}

}

Page 13: разработка серверов и серверных приложений лекция №4

Ping-Pong Test: Pong Actor 130

class PongActor : Actor{

IMessage pingMessage;

public PongActor(IMessage pingMessage): base("pong")

{this.pingMessage = pingMessage;

}

public override void Handle(IMessage message){

MessageBus.Send(pingMessage);}

}

Page 14: разработка серверов и серверных приложений лекция №4

Фасад для ThreadPool 131

public interface IThreadPool{

void StartTask(Action<object> task, object arg);}

public class ThreadPoolImpl : IThreadPool{

public void StartTask(Action<object> action, object args = null){

System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(action), args);

}}

Page 15: разработка серверов и серверных приложений лекция №4

Пример теста для ThreadPool 132

[Test]public void ThreadPoolShouldRunTasksWithArgument(){

System.Threading.ManualResetEvent canContinue = new System.Threading.ManualResetEvent(false);

ThreadPoolImpl pool = new ThreadPoolImpl();

object o = new object();bool gotArgument = false;

pool.StartTask((obj) => {gotArgument = object.ReferenceEquals(obj, o);canContinue.Set();

}, o);

Assert.True(canContinue.WaitOne(1000));Assert.True(gotArgument);

}

Page 16: разработка серверов и серверных приложений лекция №4

Фасад для роутера 133

public interface IRouter{

void Send(IMessage message);IRouter RegisterOrReplace(string channel, Action<IMessage> handler);

}

Page 17: разработка серверов и серверных приложений лекция №4

Фасад для роутера 134

public class RouterImpl: IRouter {public RouterImpl() {

routerImpl = new ConcurrentDictionary<string, Action<IMessage>>();}public void Send(IMessage message){

Action<IMessage> handler;if (routerImpl.TryGetValue(message.Target, out handler)) {

handler(message);}else { //ToDo: адресат неизвестен - надо разрешить с помощью внешнего сервиса.

}}public IRouter RegisterOrReplace(string channel, Action<IMessage> handler){

routerImpl.AddOrUpdate(channel, handler, (key, oldValue) => { return handler; });return this;

}ConcurrentDictionary<string, Action<IMessage>> routerImpl;

}