229 чытанні

Няма інструментаў, якія могуць справіцца з нашымі грузавымі тэстамі для гульняў — так мы пабудавалі Swarm

па Andrew Rakhubov19m2025/05/20
Read on Terminal Reader

Занадта доўга; Чытаць

Нам патрэбны спецыяльны інструмент для тэставання загрузкі, які мог бы адпрацаваць патэнцыйныя пратаколы WebSocket+Protobuf, синхронізацыю паміж ботамі і шырокамасштабныя сімуляцыі. Існуючыя інструменты падпарадкаваліся, так што яны стварылі Swarm: ўнутраная рамка C#, у якой «боты» (актёры) запускаюць сцэнары пад каардынаванымі графікамі, падтрымліваюць сувязь з ботамі праз білеты і селектары, а таксама аўтаматызаваныя коды для метрыкі і сцэнак.
featured image - Няма інструментаў, якія могуць справіцца з нашымі грузавымі тэстамі для гульняў — так мы пабудавалі Swarm
Andrew Rakhubov HackerNoon profile picture
0-item
1-item

Як мы распрацавалі Swarm, нашу ўнутраную рамочную структуру для тэставання нагрузкі, каб справіцца з задавальненнем тэставання ад канца да канца, патэнцыйных пратаколаў і шырокіх сімуляцый ботаў у gamedev.

Як мы распрацавалі Swarm, нашу ўнутраную рамочную структуру для тэставання нагрузкі, каб справіцца з задавальненнем тэставання ад канца да канца, патэнцыйных пратаколаў і шырокіх сімуляцый ботаў у gamedev.


У цяперашні час, гэта вельмі цяжка падумаць пра праграмнае забеспячэнне безнекаторыяУсё, што вы чулі пра карысць пара раней - усяго толькі чуткі і павер'і.


Гэтак будуць апошнія першымі, і першыя - апошнімі, бо шмат пакліканых, але мала выбраных".

Узнагароджанне на нашу архітэктуру

Без ідучы ў любыя небяспечныя дэталі, наш бак-энд складаецца з набору класічных паслуг і кластера сумяшчальных вузлаў, дзе жывуць акцёры.

backend architecture overview

Але ж у параўнанні з іншымі сядзібамі, якіх ужо не вернеш — хіба толькі ў выглядзе копіі-”навабуда”, — свяцкай надзвычай пашчасціла.ГэтаНу, мы маем тое ж самае сказаць пра GDD (Document Game Design).


Фактычна, прысвечаныя такому спорту як бокс, онлайн гульні заўсёды прызнаваліся нашмат больш цікавымі чым звычайныя аднакарыстальніцкія цацкі.


У нас функцыяну­юць таварыствы “Разумнікі і разумніцы”, “Даследчык”, а таксама адзіная ў Магілёўскай вобласці астранамічная пляцоўка.

Выпрабаванне

Як вы маглі падумаць, адзінкавы тэст для акцёраў і паслуг (і нагрузка / інтэграцыя тэст для паслуг) не адрозніваецца ад таго, што вы знойдзеце ў любым іншым тыпу праграмнага забеспячэння.

  • Загрузка і інтэграцыя теста акцёраў
  • Вынікі пошуку - download and load backend


Мы не будзем глядзець на тэставанне акцёраў падрабязна тут, таму што гэта не спецыяльна для GameDev, але адносіцца да самай мадэлі акцёра. звычайны падыход з'яўляецца мець спецыяльны адзін-вунд кластер, які падыходзіць, каб быць з'яўлены ў памяці ўнутры адным тэставанні і што таксама можа вытрымаць усе выхадныя патрабаванні і называць акцёраў API. Мы выкарыстоўваем той жа падыход.


Гэта сказана, што рэчы пачынаюць стаць больш цікавымі, калі мы прыходзім да загрузкі або канца да канца тэставання - і гэта тое, дзе наша гісторыя пачынаецца.


Так, у нашым выпадку, кліент-прыкладанне - самая гульня.Гісторыя выкарыстоўвае Unreal Engine, так што код напісаны на C++, а на серверы мы выкарыстоўваем C#.Плаеры ўзаемадзейнічаюць з элементамі UI ў гульні, вырабляючы патрабаванні ў бакэнд (або патрабаванні зроблены непасрэдна).


На гэты момант, вялікая частка фрэймаў проста перастаюць працаваць для нас, і любыя віды селену, якія лічаць кліент прыкладанняў браўзэра, выйшлі з галіны.


І тут мы павінны слухаць саміх людзей — інакш кажучы, тое, што яны кажуць, для мяне самае важнае.

  • Камунікацыя адбываецца праз WebSocket злучэнне
  • Гэта схема-першае; мы выкарыстоўваем Protobuf, каб вызначыць структуру паведамлення і паслуг
  • WebSocket паведамлення з'яўляюцца Protobuf паведамлення ўпакоены ў кантэйнер з метаданнымі, якія імітуюць некаторыя неабходныя gRPC-адукацыйныя інфармацыі, такія як URL і заголоўкі

Так што любы інструмент, які не дазваляе вызначаць наладныя пратаколы, не падыходзіць для задачы.

Загрузіць інструмент, каб выпрабаваць іх усе

Усё ж казаць краіне «Добрай раніцы» для мяне больш проста і зразумела, чым улюбляцца і дэманстраваць сімпатыю на камеру.


К6ЛокальнасцьНБМБ

У кожнага з іх былі свае плюсы і мінусы, але было некалькі рэчаў, якія ніхто з іх не мог вырашыць без вялікіх (іначай унутраных) зменаў:


  • У першай частцы сустрэчы школьнікі пазнаёміліся інфармацыйнымі блокамі.
  • Іншая справа, як раскрыць душу героя... Калі мы кажам пра кіно, каб захаваць гэтую натуральнасць паўсядзённасці і выявіць драму, неабходны моцны сцэнарый.
  • Найбольш традыцыйным з'яўляецца выкарыстанне назвы «Insecta» (Уільям Элфард Ліч, 1815) — у значэнні шасціногія; але і ў сучаснай навуковай літаратуры назву «Insecta» ужываюць не менш чым у трох ці чатырох розных значэннях[9].


— Наша фірма існуе ўжо сёмы год, мы спецыялізуемся на будаўнічых работах.СваркаБыў народжаны

Сварка

На высокім узроўні, задачай Swarm з'яўляецца запуск шмат акцёраў, якія будуць вырабляць нагрузку на сервер. Гэтыя акцёры называюцца ботамі, і яны могуць імітаваць паводзіны кліента або прысвечаны паводзіны сервера.


Больш формальна, тут ёсць спіс патрабаванняў для інструмента:


  1. Здаровая касметыка вы можаце зрабіць самі
  2. Конкуренцыя не можа быць праблемай
  3. Код можа быць аўтаматычным
  4. Комунікацыя з ботам да бота павінна быць лёгкай
  5. Многія інстанцыі павінны быць злучаемымі для стварэння дастатковай нагрузкі
  6. Інструмент павінен быць лёгкім і здольным ствараць достойны напружанне на самай бакэйн


Як бонус, я таксама дадаў некалькі дадатковых пунктаў:

  1. У будучыні гэта можа стаць выдатным і паспяховым бізнесам.
  2. Гэта могуць быць лясныя сцежкі, участкі грэйдарных і пяшчаных дарог.
  3. Гэтая знаходка атрымала назву «Хлопчык з Турканы»[4].
  4. Боты павінны быць ў стане існаваць адрознівальна ад тэставага інструмента, а гэта значыць, што не павінна быць жорсткіх залежнасцяў


Наша мета

Давайце падумаем, які код мы хацелі б напісаць; мы будзем разглядаць бот як лялька, і ён не можа рабіць рэчы самому, ён можа толькі падтрымліваць інварыяльныя, у той час як Сцэнарый - гэта лялька, якая выцягвае струны.


public class ExampleScenario : ScenarioBase
{
    /* ... */
    public override async Task Run(ISwarmAgent swarm)
    {
        // spawn bots and connect to backend
        var leader = SpawnClient();
        var follower = SpawnClient();
        await leader.Login();
        await follower.Login();


        // expect incoming InviteAddedEvent
        var followerWaitingForInvite = follower.Group.Subscription
            .ListenOnceUntil(GroupInviteAdded)
            .ThrowIfTimeout();


        // leader sends invite and followers waits for it
        await leader.Group.SendGroupInvite(follower.PlayerId);
        await followerWaitingForInvite;
        Assert.That(follower.Group.State.IncomingInvites.Count, Is.EqualTo(1));
        var invite = follower.Group.State.IncomingInvites[0];


        // now vice versa, the leader waits for an event...
        var leaderWaitingForAccept = leader.Group.Subscription
            .ListenOnceUntil(InviteAcceptedBy(follower.PlayerId))
            .ThrowIfTimeout();


        // ... and follower accept invite, thus producing the event
        await follower.Group.AcceptGroupInvite(invite.Id);
        await leaderWaitingForAccept;
        Assert.That(follower.Group.State.GroupId, Is.EqualTo(leader.Group.State.GroupId));


        PlayerId[] expectedPlayers = [leader.PlayerId, follower.PlayerId];
        Assert.That(leader.Group.State.Players, Is.EquivalentTo(expectedPlayers));
        Assert.That(follower.Group.State.Players, Is.EquivalentTo(expectedPlayers));
    }
}


Стэінг

У далейшым Андрэй працягнуў пісаць песні для Лявонцьева, а таксама многіх іншых артыстаў.GroupInviteAddedEventБот можа як адказваць на гэтыя падзеі ўнутры, так і даць магчымасць назіраць на іх з замежнага кода.


public Task SubscribeToGroupState()
{
   Subscription.Listen(OnGroupStateUpdate);
   Subscription.Start(api.GroupServiceClient.SubscribeToGroupStateUpdates, new SubscribeToGroupStateUpdatesRequest());
   return Task.CompletedTask;
}


У той час як код вельмі просты (і, як вы можаце гаварыць,OnGroupStateUpdateУ гэтым выпадку пешаходы, якія сканчаюць пераход, уяўляюць істотную небяспеку (мал.


StreamSubscriptionГэта сама аIObservable<ObservedEvent<TStreamMessage>>

І гэта дае карысныя пашырэння, мае залежны жыццёвы цикл, і пакрыты метрыкі.

Інакш проста не магло быць, бо калі ўжо стварылі такі папулярны мультфільм пра гонкі, а гонкі, як вядома, з'яўляюцца самым папулярным гульнявым жанрам.


case GroupUpdateEventType.GroupInviteAddedEvent:
   State.IncomingInvites.Add(ev.GroupInviteAddedEvent.GroupId, ev.GroupInviteAddedEvent.Invite);
   break;


Конкуренцыя не можа быць праблемай

Ідэя простая: ніколі не выконваць код, які сумесна належыць сцэнарыю або боту, які нарадзіўся ў гэтым сцэнарыі, і, да таго ж, ўнутраны код бота / модуля не павінна быць выконваецца сумесна з іншымі часткамі бота.


Гэта падобна на чытанне / напісанне замка, дзе сцэнарны код прачытаецца (даступ у спісе) і бот-код напісанне (эксклюзіўны доступ).

Задачы і контекст синхронізацыі

Два механізмы, згаданыя ў гэтай назве, цяпер з'яўляюцца вельмі магутнымі часткамі асанкцыйнага кода ў C#. Калі вы ніколі распрацавалі прыкладанне WPF, вы, вядома, ведаеце, што гэта асанкцыя.DispatcherSynchronizationContextГэта выдатная платформа для выказвання маладымі людзьмі сваіх пазіцый, іх абмеркавання.


У нашым сцэнары мы не цікавімся афіннасцю шклоў, а затым больш цікавімся парадкам выканання задач у сцэнары.


Перш чым напісаць код нізкага ўзроўню, давайце паглядзім на адзін не вельмі вядомы клас, які называеццаConcurrentExclusiveSchedulerPairЗ тых часоўДокументы:

Документы


Размова сапраўды вельмі важная — мы ж усе цудоўна разумеем, што любы, самы лепшы дэкрэт можна звесці на нішто практыкай прымянення.

Размова сапраўды вельмі важная — мы ж усе цудоўна разумеем, што любы, самы лепшы дэкрэт можна звесці на нішто практыкай прымянення.


Гэта выглядае, як мы хочам! Цяпер мы павінны гарантаваць, што ўвесь код ў сцэнары выконваецца наConcurrentSchedulerУ той час як код бота выконваецца наExclusiveScheduler.


Узнікае пытанне: калі іх ведае увесь свет, ці можна назваць іх няўдачнікамі?


public Task LaunchScenarioAsyncThread(SwarmAgent swarm, Launch launch)
{
   return Task.Factory.StartNew(
       () =>
       {
           /* <some observability and context preparations> */
           return RunScenarioInstance(Scenario, swarm, LaunchOptions, ScenarioActivity, launch);
       },
       CancellationToken.None,
       TaskCreationOptions.DenyChildAttach,
       Scenario.BotScenarioScheduler.ScenarioScheduler).Unwrap();
}


ІRunScenarioInstanceНаступным крокам з'яўляецца выбар адпаведнага абсталявання дляSetUpіRunНаступная Артыкулы пра калектыўныя работы, якія праводзяцца ў калектыве (ScenarioSchedulerГэта конкурсная часткаConcurrentExclusiveSchedulerPair)

Цяпер, калі мы знаходзімся ў сцэнарным кодзе і мы робім гэта...


public Task LaunchScenarioAsyncThread(SwarmAgent swarm, Launch launch)
{
   return Task.Factory.StartNew(
       () =>
       {
           /* <some observability and context preparations> */
           return RunScenarioInstance(Scenario, swarm, LaunchOptions, ScenarioActivity, launch);
       },
       CancellationToken.None,
       TaskCreationOptions.DenyChildAttach,
       Scenario.BotScenarioScheduler.ScenarioScheduler).Unwrap();
}

...асінк-статусная машына робіць сваю работу для нас, захоўваючы графік для нашых задач.

ЦяперSendGroupInviteПрацуе на параўнальным графіку таксама, так што мы робім той жа трюк для яго, таксама:


public Task<SendGroupInviteResponse> SendGroupInvite(PlayerId inviteePlayerId)
{
   return Runtime.Do(async () =>
   {
       var result = await api.GroupServiceClient.SendGroupInviteAsync(/* ... */);
       if (!result.HasError)
       {
           State.OutgoingInvites.Add(inviteePlayerId);
       }


       return result;
   });
}


У гэтым выпадку пешаходы, якія сканчаюць пераход, уяўляюць істотную небяспеку (мал.Task.Factory.StartNewЗ правільным графікам.

Код генерацыі

Цяпер нам трэба рабіць усё, што ў нас ёсць.Doі, хоць гэта вырашае праблему, гэта схільна да няправільнасці, лёгка забыцца, і, у асноўным, гэта выглядае дзіўна.

Давайце паглядзім на гэтую частку кода:


await leader.Group.SendGroupInvite(follower.PlayerId);


І тут у нас ёсць ботGroupІснаванне І.Group— гэта модуль, які існуе толькі для падзялення коду на асобныя класы і для таго, каб не наблытацьBotІ клас.


public class BotClient : BotClientBase
{
    public GroupBotModule Group { get; }
    /* ... */
}


Давайце скажам, што модуль павінен мець інтэрфейс:


public class BotClient : BotClientBase
{
    public IGroupBotModule Group { get; }
    /* ... */
}


public interface IGroupBotModule : IBotModule, IAsyncDisposable
{
    GroupBotState State { get; }


    Task<SendGroupInviteResponse> SendGroupInvite(PlayerId toPlayerId);
    /* ... */
}




public class GroupBotModule :
   BotClientModuleBase<GroupBotModule>,
   IGroupBotModule
{
    /* ... */
    public async Task<SendGroupInviteResponse> SendGroupInvite(PlayerId inviteePlayerId)
    {
        // no wrapping with `Runtime.Do` here
        var result = await api.GroupServiceClient.SendGroupInviteAsync(new() /* ... */);
        if (!result.HasError)
        {
            State.OutgoingInvites.Add(new GroupBotState.Invite(inviteePlayerId, /* ... */));
        }


        return result;
   }
|


Ніякіх падставаў для грамадскіх хваляванняў ці рэнесансу ісламскіх радыкалаў, няма.


Чароўная печка прапануе Вам навагоднія частункі. - Ці ведаеце вы?Runtime.DoЗапішыце ў:


   public const string MethodProxy =
@"
   public async $return_type$ $method_name$($method_params$)
   {
       $return$await runtime.Do(() => implementation.$method_name$($method_args$));
   }
";


Інструменты

Наступным крокам з'яўляецца інструментаванне кода, каб сабраць метрыкі і сляды. Па-першае, кожны выклик API трэба назіраць. Гэтая частка вельмі прастая, так як наш транспарт эфектыўна робіць выгляд, што гэта канал gRPC, так што мы толькі выкарыстоўваем правільна напісаны інтэрцэптар...


callInvoker = channel
   .Intercept(new PerformanceInterceptor());


ДзеCallInvokerГэта абстрактная абстракцыя RPC-выкліку з боку кліента.


І тут мы павінны слухаць саміх людзей — інакш кажучы, тое, што яны кажуць, для мяне самае важнае.IInstrumentationFactoryЗ наступным інтэрфейсам:


public interface IInstrumentationFactory

{

   public IInstrumentationScope CreateScope(

       string? name = null,

       IInstrumentationContext? actor = null,

       [CallerMemberName] string memberName = "",

       [CallerFilePath] string sourceFilePath = "",

       [CallerLineNumber] int sourceLineNumber = 0);

}


Цяпер вы можаце пакрыць часткі, якія вас цікавяць:


public Task AcceptGroupInvite(GroupInvideId inviteId)
{
    using (instrumentationFactory.CreateScope())
    {
        var result = await api.GroupServiceClient.AcceptGroupInviteAsync(request);
    }
}


Калі вы таксама хочаце прывабіць удачу на свой лад, бярыце прыклад з герояў нашага матэрыяла!


   public const string MethodProxy =
@"
   public async $return_type$ $method_name$($method_params$)
   {
       using(instrumentationFactory.CreateScope(/* related args> */))
       {
           $return$await runtime.Do(() => implementation.$method_name$($method_args$));
       }
   }
";


Інструментальны аб'ём запісвае час выканання, выключэнні, стварае распаўсюджаныя трасы, ён можа напісаць логі дэбаг, і ён робіць шмат іншых канфігураваных рэчаў.


Сцягнуць Bot-to-Bot

Прыклад у секцыі «Наша мэта» не паказвае ніякай інфармацыі. Мы толькі ведаем бот следчыPlayerIdКалі рабочы пакідаў наймальніка без уважлівых прычын да заканчэння тэрміну найму - гэта прызнавалася самавольным адыходам.


Я першапачаткова планаваў рэалізаваць невялікі шаблон чорнай табліцы з распаўсюджанай памяшканнем ключавых значэнняў для сувязі (такім як Redis), але пасля выконвання некаторых доказу канцэпцыйных тэстаў, выявілася, што аб'ём працы можа быць значна зменшыны да двух больш простых канцэпцый: чагосьці і селектара.

Лістапад — билеты

У рэальным свеце, гульцы сустракаюцца з усім інакш – яны пішуць прамыя паведамленні або выкарыстоўваюць галасовы чат. З ботамі, нам не трэба імітаваць гэтую гранулярнасць, і нам проста трэба прапанаваць наміры.ХтосьціПаглядзі на мяне, калі ласка, у групу».І вось, дзе ўвайшлі ў гульні білеты:


public interface ISwarmTickets
{
   Task PlaceTicket(SwarmTicketBase ticket);


   Task<SwarmTicketBase?> TryGetTicket(Type ticketType, TimeSpan timeout);
}


Бот можа ставіць квіток, а затым іншы бот можа атрымаць гэты квіток.ISwarmTicketsГэта нічога іншага, як папярэдні працэсticketType(На самай справе гэтаЛегкаБольш за тое, таму што ёсць дадатковыя варыянты, каб пазбегнуць ботаў ад атрымання сваіх уласцівак, а таксама іншых невялікіх нараджанняў).


З дапамогай гэтага інтэрфейсу мы можам наколькі раздзяліць прыкладны сцэнарый на два незалежныя сцэнары. (тут усё дадатковы код знішчаецца, каб ілюстраваць асноўную ідэю):


private async Task RunLeaderRole(ISwarmAgent swarm)
{
   var ticket = await swarm.Blackboard.Tickets
       .TryGetTicket<BotWantsGroupTicket>(TimeSpan.FromSeconds(5))
       .ThrowIfTicketIsNull();


   await bot.Group.SendGroupInvite(ticket.Owner);
   await bot.Group.Subscription.ListenOnceUntil(
           GotInviteAcceptedEvent,
           TimeSpan.FromSeconds(5))
       .ThrowIfTimeout();
}


private async Task RunFollowerRole(ISwarmAgent swarm)
{
   var waitingForInvite = bot.Group.Subscription.ListenOnceUntil(
           GotInviteAddedEvent,
           TimeSpan.FromSeconds(5))
       .ThrowIfTimeout();


   await swarm.Blackboard.Tickets.PlaceTicket(new BotWantsGroupTicket(bot.PlayerId));
   await waitingForInvite;
   await bot.Group.AcceptGroupInvite(bot.Group.State.IncomingInvites[0].Id);
}


Селектар

Мы маем два розныя паводзіны, адзін для лідара, адзін для следчыка. Разумеецца, яны могуць быць падзеленыя на два розныя сцэнары і запушчаны паралельна.


public override async Task Run(ISwarmAgent swarm)
{
   var roleAction = await swarm.Blackboard
       .RoundRobinRole(
           "leader or follower",
           Enumerable.Repeat(RunFollowerRole, config.GroupSize - 1).Union([RunLeaderRole]));
   await roleAction(swarm);
}


І тут,RoundRobinRoleГэта проста фантастычны ўпрыгожванне вакол сумеснага літара і аперацыя модуля, каб выбраць правільны элемент з спісу.


Класіфікацыя Swarms

Цяпер, з усімі сувязямі, якія захоўваюцца за часам і распаўсюджанымі лічбамі, становіцца трымавіяльным увядзенне або вузла оркестратара, або выкарыстанне некаторых існуючых MQ і KV памяшканняў.


Цікавы факт: мы ніколі не скончылі рэалізацыю гэтай функцыі.Калі QA атрымаў свае рукі на адным вузле рэалізацыі SwarmAgentАле іх выступленні гучалі так, быццам бы яны доўга рэпеціравалі з аркестрам.

Еўрапейская перспектыва

— Вераніка Уладзіміраўна, што гэта за 11 андрагагічных умоў, пры якіх дарослыя без цяжкасцей могуць засвоіць новыя тэхналогіі ў фізічнай культуры?

Сцягнуць бенчмарк:

BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4651/22H2/2022Update)
Intel Core i7-10875H працэсар 2.30GHz, 1 працэсар, 16 логічных і 8 фізічных ядраў
Загрузіць .NET SDK 9.0.203
[Хост] : .NET 9.0.4 (9.0.425.16305), X64 RyuJIT AVX2
.NET 9.0 : .NET 9.0.4 (9.0.425.16305), X64 RyuJIT AVX2


private async Task SimpleWorker()
{
   var tasks = new List<Task>();
   for (var i = 0; i < Parallelism; ++i)
   {
       var index = i;
       tasks.Add(Task.Run(async () =>
       {
           for (var j = 0; j < Iterations; ++j)
           {
               await LoadMethod(index);
           }
       }));
   }


   await Task.WhenAll(tasks);
}


private async Task SchedulerWorker()
{
    // scenarios are prepared in GlobalSetup
    await Task.WhenAll(scenarios.Select(LaunchScenarioAsyncThread));
}


public class TestScenario : ScenarioBase
{
    public override async Task Run()
    {
        for (var iteration = 0; iteration < iterations; ++iteration)
        {
            await botClient.Do(BotLoadMethod);
        }
    }
}


Перш за ўсё, давайце паглядзім на амаль чыстыя перавагі планавання супраць простых заданняў.

У гэтым выпадку, оба метады загрузкі з'яўляюцца наступнымі:


private ValueTask CounterLoadMethod(int i)

{

   Interlocked.Increment(ref StaticControl.Counter);

   return ValueTask.CompletedTask;

}


Вынікі для ітэрацый = 10:

WorkerType

Parallelism

Mean

Error

StdDev

Gen0

Gen1

Gen2

Allocated

Simple

10

3.565us

0.0450us

0.0421us

0.3433

-

-

2.83KB

Simple

100

30.281us

0.2720us

0.2544us

3.1128

0.061

-

25.67KB

Simple

1000

250.693us

2.1626us

2.4037us

30.2734

5.8594

-

250.67KB

Scheduler

10

40.629us

0.7842us

0.8054us

8.1787

0.1221

-

66.15KB

Scheduler

100

325.386us

2.3414us

2.1901us

81.0547

14.6484

-

662.09KB

Scheduler

1000

4,685.812us

24.7917us

21.9772us

812.5

375

-

6617.59KB

Простыя

10

3 565 працэнтаў

0,450 млн

0,421 працэнта

0.3433

-

-

2.8 Кб

Простыя

100

30281 Вынікі

2720 г.

0,2544 г.

3.1128

0.061

-

257 Кб

Простыя

1000

250 693

1626 г.

2.4037Сірыя

30.2734

5.8594

-

250.67 Кб

Графік

10

40 629 працэнтаў

1 742

0 805

8.1787

0.1221

-

66.15 Кб

Графік

100

325.386Адміністрацыя

2.3414Адміністрацыя

2 1901 год

81.0547

14.6484

-

62.09 Кб

Графік

1000

4 685 812

247917Правіць

21 772

812.5

375

-

6617.59Кб



Здавалася б «сумнае» пытаннне разглядалася, а так лягло на душу! — так лягло на душу! — так лягло на душу!


Што можа быць рэальным найгіршым сцэнарыстычным сцэнарам? Ну, а што пра функцыю, якая не робіць нічога больш, чым выклик API? З аднаго боку, амаль кожны спосаб бота робіць гэта, але з іншага боку, калі няма нічога больш, чым толькі выклікі API, то ўсе спробы сінхранізацыі згубленыя, ці не так?


Вынікі пошуку - only callPingAsyncДля папросту, кліенты RPC захоўваюцца з-за ботаў.


private async ValueTask PingLoadMethod(int i)
{
   await clients[i].PingAsync(new PingRequest());
}


Вось вынікі, яшчэ 10 ітэрацый (сервер GRPC знаходзіцца ў локальной сеткі):


WorkerType

Parallelism

Mean

Error

StdDev

Gen0

Gen1

Allocated

Simple

100

94.45 ms

1.804 ms

2.148 ms

600

200

6.14 MB

Simple

1000

596.69 ms

15.592 ms

45.730 ms

9000

7000

76.77 MB

Scheduler

100

95.48 ms

1.547 ms

1.292 ms

833.3333

333.3333

6.85 MB

Scheduler

1000

625.52 ms

14.697 ms

42.405 ms

8000

7000

68.57 MB

Простыя

100

94.45 МЗ

1804 г.

2148 МЗ

600

200

6.14 Мб

Простыя

1000

6956 мс

15 592 МЗ

45 730 мс

9000

7000

77.7 Мб

Графік

100

95,48 мс

1 547 МЗ

1 292 МЗ

833.3333

333.3333

685 Мб

Графік

1000

625.52 мс

14 697 працэнтаў

42 405 мс

8000

7000

68,57 Мб


Як і чакалася, ўплыў на працэдуру на рэальныя працы сцэнары небяспечны.


Аналіз

Але серыя онлайн цацак Assassins creed 3 і Assassins creed 4, а таксама астатнія часткі забавы таксама заслугоўваюць увагі і вывучэння.


Спадзяёмся, што матэрыяльная падтрымка будзе і з боку Міністэрства культуры, а таксама фонду краін СНД, цяпер складаем праграму, якую павязём у Маскву на ўзгадненне, будзем там адстойваць свае пазіцыі.


Grafana dashboard panels showing failed test


Як часта бывае ў такія хвіліны, усе разгубіліся.


Праўда, сляды патрэбныя для добрай назіральнасці. Для тэставых бягун Swarm, сляды кліента звязаны з заднімі слядамі на адным дрэве. Спецыфічныя сляды можна знайсці паconnectionIdD/botIdГэта дапамагае ў дэбютаванні.


Distributed tracing connecting Swarm scenario with backend


Заўвага: DevServer з'яўляецца зручным манолітным будынкам усё-на-адзіне бакэйн-сервісаў, якія будуць запушчаны на ПК распрацоўшчыка, і гэта спецыяльна распрацаваны прыклад, каб зменшыць колькасць дэталяў на экране.


З дапамогай існуючых інструментаў, гэтыя трасы могуць быць праглядзены і аналізаваны выкарыстоўваючы ўбудаваныя магчымасці, такія як PerfettoSQL.


Adapting perf logs to show bot execution flow

Што здарылася ў нас у канцы

У нас цяпер ёсць SDK, на якім пабудаваны і GameClient і Dedicated Server боты. Гэтыя боты цяпер падтрымліваюць большую частку гульнявай логікі — яны ўключаюць падсістэмы сяброў, групы падтрымкі і matchmaking.


Арганізатар спеўных варштатаў у межах праекта «CHOICE-Беларусь: спадчына i сучаснасць» разважае, як можна пракласці мост паміж музеем і людзьмі.


Эксперты адзначаюць, што збор соку з бярозы зусім не шкодзіць дрэве, так як падчас гэтага працэс выдаляецца толькі 1% вадкасці.


Swarm дапамагае не толькі з тэстамі, але звычайна выкарыстоўваецца для рэпродукцыі і выпраўлення баг у гульнявай логіцы, асабліва калі гэта складана зрабіць ручна, напрыклад, калі вам патрэбны некалькі гульнявых кліентаў, якія выконваюць спецыяльную сцэну акцый.


У рэшце рэшт, мы надзвычай задаволеныя нашым вынікам і, безумоўна, не маем жалю пра цяжкасці, выдаткаванае на распрацоўку нашай собственнай сістэмы выпрабаванняў.



Я вельмі рада, што ён быў у маім жыцці, бо гэта ён, калі можна так сказаць, паставіў машыну майго мыслення.

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks