خادم اللعبة على MS Orleans - الجزء 1: ما هي الجهات الفاعلة



مرحباً هبر! وهكذا ، في اليوم الرابع عشر من الرمح ، قررت البدء في إنشاء خادم ألعاب بسيط للتصوير عبر الإنترنت. لموضوع واحد من الحوسبة الموزعة للمس. في هذه المقالة التمهيدية للدورة ، أود أن أقول ما هي الجهات الفاعلة (في أورليانز تسمى الحبوب) ومبدأ عملهم. لهذا ، ما زلت مثالًا على تطبيق بسيط مع ممثلين محليين بدون أورليانز. كما يقولون قبل بناء سفينة ، دعونا نرى كيف تطفو ولماذا يطفو قارب ورقي عادي. لمزيد من التفاصيل ، مرحبا بك في القط.

المحتوى


  • خادم اللعبة على MS Orleans - الجزء 1: ما هي الجهات الفاعلة


ممثلين


الفاعل هو كيان حاسوبي يمكنه ، في استجابة للرسالة المستلمة ، في وقت واحد:
إرسال عدد محدود من الرسائل إلى جهات فاعلة أخرى ؛
إنشاء عدد محدود من الممثلين الجدد ؛
حدد السلوك الذي سيتم استخدامه عند معالجة الرسالة المستلمة التالية.
لا يفترض وجود تسلسل معين من الإجراءات المذكورة أعلاه ، ويمكن تنفيذها جميعًا بالتوازي.
نرسل إلى الممثل بعض الرسائل (الفرق أو الأحداث) ويمكنه هو نفسه إرسال رسائل أخرى للآخرين. تعمل Coroutines مع القنوات على مبدأ مماثل. يتلقون بعض البيانات من القناة ويرسلون بعض البيانات إلى القناة أثناء العمل بشكل غير متزامن.

مثال


في الواقع رمز اثنين من الممثلين بسيطة للغاية:
    class Program
    {
        interface IMessage { }

        class IncrementCommand : IMessage { }

        class TellCountCommand : IMessage { }

        class SaidCountEvent : IMessage
        {
            public int Count { get; }
            public int ActorId { get; }

            public SaidCountEvent(int count, int actorId)
            {
                Count = count;
                ActorId = actorId;
            }
        }

        class WriteMessageCommand : IMessage
        {
            public string Message { get; }

            public WriteMessageCommand(string message)
            {
                Message = message;
            }
        }

        static Task CreateCounterActor(
            BlockingCollection<IMessage> output,
            BlockingCollection<IMessage> input,
            int id
            )
        {
            return Task.Run(() =>
             {
                 var count = 0;

                 while (true)
                 {
                     var m = input.Take();
                     if (m is IncrementCommand)
                         count++;
                     if (m is TellCountCommand)
                         output.Add(new SaidCountEvent(count, id));
                 }
             });
        }

        static Task CreateWriterActor(BlockingCollection<IMessage> input)
        {
            return Task.Run(() =>
             {
                 while (true)
                 {
                     var m = input.Take();
                     if (m is WriteMessageCommand write)
                         Console.WriteLine(write.Message);
                     if (m is SaidCountEvent sc)
                         Console.WriteLine(
                             $"Counter   {sc.Count}   {sc.ActorId}"
                             );
                 }
             });
        }

        static void Main(string[] args)
        {
            var writerInput = new BlockingCollection<IMessage>();
            var firstInput = new BlockingCollection<IMessage>();
            var secondInput = new BlockingCollection<IMessage>();
            var writer = CreateWriterActor(writerInput);
            var firstCounter = CreateCounterActor(writerInput, firstInput, 1);
            var secondCounter = CreateCounterActor(writerInput, secondInput, 2);
            for (int i = 0; i < 5; i++)
            {
                firstInput.Add(new IncrementCommand());
            }
            for (int i = 0; i < 9; i++)
            {
                secondInput.Add(new IncrementCommand());
            }
            firstInput.Add(new TellCountCommand());
            secondInput.Add(new TellCountCommand());
            writerInput.Add(new WriteMessageCommand("  Main"));
            Console.ReadLine();
        }
    }


  1. الواجهة التي نميز بواسطتها الرسائل التي يستقبلها الفاعل أو يرسلها:
    interface IMessage { }
    

  2. الأمر الذي نطلب منه من الممثل زيادة عداده (الحالة الداخلية):
    class IncrementCommand : IMessage { }
    

  3. الأمر الذي نساعد به الممثل أن يخبر حالته الحالية (العداد) للآخرين:
    class TellCountCommand : IMessage { }
    

  4. حدث يخبر الممثلين الآخرين أن الممثل قد أخبر الجميع حالته الحالية (العداد). يتم إنشاؤه عند معالجة أمر TellCountCommand:
    class SaidCountEvent : IMessage
    {
        public int Count { get; }
        public int ActorId { get; }
    
        public SaidCountEvent(int count, int actorId)
        {
            Count = count;
            ActorId = actorId;
        }
    }
    

    العدد هو المبلغ الذي يتم طلبه الآن على عداد ممثل بمعرف يساوي ActorId
  5. يخبر هذا الأمر الفاعل بطباعة هذه الرسالة إلى وحدة التحكم:
    class WriteMessageCommand : IMessage
    {
        public string Message { get; }
    
        public WriteMessageCommand(string message)
        {
            Message = message;
        }
    }
    

  6. يبدأ مثيل الممثل الذي يتحكم في الحالة الحالية للعداد:
            static Task CreateCounterActor(
             //   
                BlockingCollection<IMessage> output,
              // 
                BlockingCollection<IMessage> input,
             //              
                int id
                )
            {
                return Task.Run(() =>
                 { 
                     //  .    
                     var count = 0;
    
                     while (true)
                     {
                       //     
                      //             
                         var m = input.Take();
                         if (m is IncrementCommand)
                             count++;
                         if (m is TellCountCommand)
                             output.Add(new SaidCountEvent(count, id));
                     }
                 });
            }
    

  7. ينشئ ممثلاً يكتب الرسائل ببساطة إلى وحدة التحكم:
            static Task CreateWriterActor(BlockingCollection<IMessage> input)
            {
                return Task.Run(() =>
                 {
                     while (true)
                     {
                         var m = input.Take();
                         if (m is WriteMessageCommand write)
                             Console.WriteLine(write.Message);
                         if (m is SaidCountEvent sc)
                             Console.WriteLine(
                                 $"Counter   {sc.Count}   {sc.ActorId}"
                                 );
                     }
                 });
            }
    

  8.         static void Main(string[] args)
            {
                var writerInput = new BlockingCollection<IMessage>();
                var firstInput = new BlockingCollection<IMessage>();
                var secondInput = new BlockingCollection<IMessage>();
                var writer = CreateWriterActor(writerInput);
              //       
                var firstCounter = CreateCounterActor(writerInput, firstInput, 1);
                var secondCounter = CreateCounterActor(writerInput, secondInput, 2);
                //       
                for (int i = 0; i < 5; i++)
                {
                    firstInput.Add(new IncrementCommand());
                }
                //       .
                for (int i = 0; i < 9; i++)
                {
                    secondInput.Add(new IncrementCommand());
                }
                //         .
                //      writer      
                firstInput.Add(new TellCountCommand());
                secondInput.Add(new TellCountCommand());
               // writer     .
                writerInput.Add(new WriteMessageCommand("  Main"));
                Console.ReadLine();
            }
    



حسنًا ، حتى تكون جميلة تمامًا وتبدو مثل الممثل (الممثل) من أورليانز ، دعنا نصنع غلافًا على مكتبنا:
    public interface ICounterActor
    {
        void Increment();
        void TellCount();
    }

    public class CounterActor : ICounterActor
    {
        private readonly Task _taks;
        private readonly BlockingCollection<IMessage> _input;
        private readonly BlockingCollection<IMessage> _output;

        public CounterActor(BlockingCollection<IMessage> output)
        {
            _input = new BlockingCollection<IMessage>(); ;
            _output = output;
            _taks = Task.Run(() =>
            {
                //State
                var count = 0;

                while (true)
                {
                    var m = _input.Take();
                    if (m is IncrementCommand)
                        count++;
                    if (m is TellCountCommand)
                        _output.Add(new SaidCountEvent(count, _taks.Id));
                }
            });
        }

        public void Increment()
        {
            _input.Add(new IncrementCommand());
        }

        public void TellCount()
        {
            _input.Add(new TellCountCommand());
        }
    }

هنا تم استخدام نموذج رد الفعل. لا يقوم ممثلونا بإرجاع القيم ، ولكنهم يولدون فقط رسائل جديدة بالقيم المطلوبة. هنا لدينا ممثل واحد فقط في خيطه الخاص ، خيط آمن من أي شخص آخر ، يزيد من عداده. ممثل آخر يكتب للتو إلى وحدة التحكم. ومع ذلك ، دعنا نتخيل أنه بالنسبة لنا كل ممثل ، على سبيل المثال ، يحسب الإحداثيات الحالية وصحة ومانع أحد اللاعبين العشرة الذين يلعبون حاليًا على خادمنا. لكل منها حالتها الخاصة في التدفق الخاص بها. نرسل رسائل إلى الآخرين أو نتلقى رسائل من الآخرين حول الضرر المتلقى أو المهارة المستخدمة أو تغيير إحداثياتنا.

All Articles