227 usomaji

Hakuna chombo kinaweza kushughulikia mfumo wetu wa mtihani wa mzigo kwa ajili ya michezo—Kwa hiyo tulijenga Swarm

kwa Andrew Rakhubov19m2025/05/20
Read on Terminal Reader

Ndefu sana; Kusoma

Tulihitaji chombo maalum cha mtihani wa mzigo ambacho kinaweza kushughulikia protocols za kibinafsi za WebSocket+Protobuf, usindikaji wa inter-bot, na simulations kubwa. Zana zilizopo zilianguka, hivyo walijenga Swarm: mfumo wa ndani wa C# ambapo "bots" (watendaji) huendesha mipango chini ya mipangilio iliyoundwa, inasaidia mawasiliano ya bot-to-bot kupitia tiketi na wapiga kura, na chombo cha moja kwa moja cha mtihani wote kwa takwimu na traces. Muundo wa Swarm unachangia urahisi wa kujibu upyaji wa hali, udhibiti wa ushindani kupitia ConcurrentExclusiveSchedulerPair, kuzalisha msimbo kupitia wazalishaji wa chanzo, na ufuat
featured image - Hakuna chombo kinaweza kushughulikia mfumo wetu wa mtihani wa mzigo kwa ajili ya michezo—Kwa hiyo tulijenga Swarm
Andrew Rakhubov HackerNoon profile picture
0-item
1-item

Jinsi ya kuunda Swarm, mfumo wetu wa kupima uzito wa ndani, kukabiliana na changamoto za mtihani wa mwisho wa mwisho, mikataba ya kibinafsi, na simulations kubwa ya bot katika gamedev.

Jinsi ya kuunda Swarm, mfumo wetu wa kupima uzito wa ndani, kukabiliana na changamoto za mtihani wa mwisho wa mwisho, mikataba ya kibinafsi, na simulations kubwa ya bot katika gamedev.


Siku hizi, ni vigumu kufikiria bidhaa ya programu bila angalauBaadhi yakiwango cha majaribio. majaribio ya kitengo ni njia ya kawaida ya kukamata makosa katika vipande vidogo vya nambari, wakati majaribio ya mwisho hadi mwisho yanahifadhi mchakato wa kazi wa maombi yote. GameDev sio kipimo, lakini inakuja na changamoto zake za kipekee ambazo sio daima zinakubalika na jinsi tunavyojaribu maombi ya wavuti. Makala hii ni kuhusu njia ambayo tumechukua ili kukidhi mahitaji hayo ya kipekee na chombo chetu cha majaribio ya ndani.


Hi, mimi ni Andrey Rakhubov, Mhandisi Mkuu wa Programu katika MY.GAMES! Katika chapisho hili, nitashiriki maelezo kuhusu backend iliyotumika katika studio yetu, na mbinu yetu ya kupakia mtihani wa meta-mchezo katika War Robots: Frontiers.

Maelezo juu ya usanifu wetu

Bila kuingia katika maelezo yoyote yasiyofaa, backend yetu inajumuisha seti ya huduma za jadi na kundi la nodes zinazohusiana ambapo watendaji wanaishi.

backend architecture overview

Licha ya upungufu wengine ambao wanaweza kuwepo, mbinu hii imethibitishwa kuwa nzuri sana katika kutatua moja ya matatizo magumu zaidi katika maendeleo ya mchezo: kasi ya iteration. Unaweza kukumbuka kwamba wakati wa siku za mwanzo za maendeleo ya Rust-lang kulikuwa na shaka kwamba nyaraka itabadilika kama wewe kusomayaNaam, tunaambia sawa kuhusu GDD (Dokumento ya Mpango wa Mchezo).


Wachezaji husaidia kwa njia ambayo ni nafuu sana kutekeleza na kurekebisha katika hatua za mwanzo za kubuni ya kipengele na wao ni rahisi sana refactor kama huduma tofauti ikiwa haja hutokea baadaye.


Bila shaka, hii inachangia mtihani wa mzigo na ushirikiano, kwa sababu huna tena tofauti ya wazi ya huduma na API ndogo na zilizoelezwa vizuri. Badala yake, kuna kundi la nodes tofauti, ambapo kuna uhusiano mdogo au unaweza kudhibiti kati ya wachezaji.

Utafiti wa

Kama unaweza kufikiria, mtihani wa kipekee kwa watendaji na huduma (na mtihani wa mzigo / ushirikiano kwa huduma) sio tofauti na kile unachokiona katika aina nyingine yoyote ya programu.

  • Mashabiki wa mtihani wa mzigo na ushirikiano
  • Utafiti wa End-to-End na uendeshaji wa backend


Hatutazama kujaribu wachezaji kwa undani hapa kwa sababu sio maalum kwa GameDev, lakini inahusiana na mfano wa mchezaji mwenyewe. mbinu ya kawaida ni kuwa na cluster maalum ya node moja ambayo ni sahihi kuunganishwa katika kumbukumbu ndani ya mtihani mmoja na ambayo pia inaweza kuzuia maombi yote ya nje na kuita API ya wachezaji.


Alisema, mambo huanza kuwa ya kuvutia zaidi wakati tunapokuja na kupakia au mtihani wa mwisho hadi mwisho - na hii ni ambapo hadithi yetu inaanza.


Hivyo, katika kesi yetu, programu ya mteja ni mchezo mwenyewe. Mchezo hutumia Unreal Engine, hivyo msimbo unaandikwa katika C++, na kwenye seva tunatumia C#. Wachezaji huingiliana na vipengele vya UI katika mchezo, kuzalisha maombi kwa backend (au maombi hutolewa kwa moja kwa moja).


Katika hatua hii, idadi kubwa ya miundombinu tu kuacha kufanya kazi kwetu, na aina yoyote ya kit kama selenium ambayo inachukulia maombi ya mteja kama kivinjari ni nje ya kiwango.


Swali jingine ni kwamba tunatumia mkataba wa mawasiliano wa kibinafsi kati ya mteja na backend. sehemu hii kwa kweli inastahili makala tofauti kabisa, lakini nitazungumzia dhana kuu:

  • Mawasiliano yanafanyika kupitia uhusiano wa WebSocket
  • Ni mpango wa kwanza; tunatumia Protobuf kufafanua muundo wa ujumbe na huduma
  • Ujumbe wa WebSocket ni ujumbe wa Protobuf unaounganishwa katika chombo na metadata ambayo inachanganya baadhi ya habari muhimu zinazohusiana na gRPC, kama URL na kichwa

Kwa hivyo, chombo chochote ambacho haiwezekani kufafanua protocols za kibinafsi sio sahihi kwa kazi hiyo.

Chombo cha kupakia ili kujaribu wote

Wakati huo huo, tulitaka kuwa na chombo kimoja ambacho kinaweza kuandika majaribio ya mzigo wa REST / gRPC na majaribio ya mwisho hadi mwisho na mkataba wetu wa kibinafsi. Baada ya kuzingatia mahitaji yote yaliyojadiliwa katika Uchunguzi na majadiliano ya awali, tumeachwa na wagombea hawa:


K6 yaya LocustNembo ya

Kila mmoja wao alikuwa na faida na hasara zao, lakini kulikuwa na mambo kadhaa ambayo hakuna wao inaweza kutatua bila mabadiliko makubwa (wakati mwingine ya ndani):


  • Kwanza, kulikuwa na haja inayohusiana na mawasiliano kati ya bot kwa madhumuni ya synchronization, kama ilivyojadiliwa hapo awali.
  • Pili, wateja ni reactive kabisa na hali yao inaweza kubadilika bila hatua wazi kutoka kwa hali ya mtihani; hii inasababisha synchronization ya ziada na haja ya kupiga njia nyingi katika code yako.
  • Hatimaye, zana hizi zilikuwa na lengo ndogo sana kwenye majaribio ya utendaji, zinatoa vipengele vingi vya kuongezeka kwa kasi ya mzigo au kufafanua muda wa muda, lakini hawakupata uwezo wa kuunda matukio magumu yanayohusiana kwa njia rahisi.


Ni wakati wa kukubali ukweli kwamba kwa kweli tunahitaji chombo maalum.Maji yaAlikuwa amezaliwa

Maji ya

Katika ngazi ya juu, jukumu la Swarm ni kuanzisha watendaji wengi ambao watatoa mzigo kwenye seva. Watendaji hawa wanatajwa bots, na wanaweza kuiga tabia ya mteja au tabia ya seva ya kujitolea.


Zaidi rasmi, hapa ni orodha ya mahitaji ya chombo:


  1. Jibu kwa updates ya hali inapaswa kuwa rahisi
  2. Ushindani haupaswi kuwa suala la
  3. Kodi inapaswa kuwa inapatikana moja kwa moja
  4. Mawasiliano ya bot-to-bot inapaswa kuwa rahisi
  5. Maonyesho mengi yanapaswa kuunganishwa ili kuunda mzigo wa kutosha
  6. Zana inapaswa kuwa nyepesi na inaweza kuunda shinikizo la kutosha kwenye backend yenyewe


Kama bonus, mimi pia aliongeza baadhi ya pointi ziada:

  1. Vituo viwili vya uendeshaji na vipimo vya mtihani wa mwisho lazima vinawezekana
  2. Chombo kinapaswa kuwa agnostic ya usafiri; tunapaswa kuwa na uwezo wa kuunganisha hii kwenye usafiri wowote mwingine unaowezekana ikiwa inahitajika
  3. Zana hii ina mtindo wa msimbo wa imperative, kwa sababu mimi binafsi nina maoni imara kwamba mtindo wa declarative sio sahihi kwa matukio magumu na maamuzi ya hali.
  4. Bots inapaswa kuwa na uwezo wa kuwepo tofauti na chombo cha mtihani, ambayo inamaanisha kwamba hakuna utegemezi mkali


Malengo yetu

Hebu fikiria msimbo tunataka kuandika; tutafikiria bot kama doll, na haiwezi kufanya mambo peke yake, inaweza tu kudumisha invariants, wakati Scenario ni dolleteer ambaye huchukua nguzo.


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));
    }
}


Steam ya

Backend inasababisha update nyingi kwa mteja ambayo inaweza kutokea mara kwa mara; katika mfano hapo juu, tukio moja kama hilo niGroupInviteAddedEventBot inapaswa kuwa na uwezo wa kujibu matukio haya kwa ndani na kutoa fursa ya kutazama kutoka kwa msimbo wa nje.


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


Wakati code ni rahisi sana (na kama unaweza kufikiriOnGroupStateUpdateHandler ni tu kesi ya muda mrefu ya kubadili), mambo mengi hutokea hapa.


StreamSubscriptionNi mwenyewe aIObservable<ObservedEvent<TStreamMessage>>

na hutoa upanuzi muhimu, ina mzunguko wa maisha unaohusiana, na inashughulikiwa na takwimu.

Faida nyingine: hauhitaji usindikaji wazi wa kusoma au kurekebisha hali, bila kujali ambapo mpangilio unatumika.


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


Ushindani haupaswi kuwa suala la

Jambo la msingi ni rahisi: kamwe kuendesha msimbo ambao unategemea script au bot iliyotengenezwa katika hali hiyo, na zaidi ya hayo, msimbo wa ndani wa bot / module haipaswi kuendeshwa kwa wakati huo huo na sehemu zingine za bot.


Hii ni sawa na kufungia kusoma / kuandika, ambapo msimbo wa hali ni kusoma (kufikia pamoja) na msimbo wa bot ni kuandika (kufikia pekee).

Mipango ya kazi na mazingira ya synchronization

Mbinu mbili zilizotajwa katika kichwa hiki sasa ni sehemu yenye nguvu sana za msimbo wa async katika C#. Ikiwa umewahi kuendeleza programu ya WPF, unaweza kujua kwamba niDispatcherSynchronizationContextambayo ni wajibu wa kurejesha kwa unyenyekevu wito wako wa async nyuma kwenye thread ya UI.


Katika hali yetu, hatuna wasiwasi juu ya upendo wa thread, na badala yake tunajali zaidi juu ya utaratibu wa utekelezaji wa kazi katika hali.


Kabla ya kuchukua haraka kuandika msimbo wa kiwango cha chini, hebu tuangalie darasa moja ambayo haijulikani sana.ConcurrentExclusiveSchedulerPairKutoka kwenyeya Docs:

ya Docs


Inatoa mipangilio ya kazi ambayo inashirikisha kutekeleza kazi wakati kuhakikisha kwamba kazi za pamoja zinaweza kutumika kwa wakati mmoja na kazi za kipekee kamwe.

Inatoa mipangilio ya kazi ambayo inashirikisha kutekeleza kazi wakati kuhakikisha kwamba kazi za pamoja zinaweza kutumika kwa wakati mmoja na kazi za kipekee kamwe.


Hii inaonekana kama hasa tunataka! Sasa tunahitaji kuhakikisha kwamba msimbo wote ndani ya script huendeshwa kwenyeConcurrentSchedulerWakati code ya bot inafanya kaziExclusiveScheduler.


Jinsi ya kuweka programu kwa kazi? chaguo moja ni kuhamisha wazi kama kiwango, ambayo inafanywa wakati script huanza:


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();
}


yaRunScenarioInstanceNjia nyingine, kwa upande wake, inatoaSetUpna yaRunMchakato huu unatumika kwa kutumia mbinu maalum za usambazaji (ScenarioSchedulerNi sehemu ya ushindani waConcurrentExclusiveSchedulerPair(Kwa ajili ya

Sasa, wakati sisi ni ndani ya code script na sisi kufanya hii ...


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();
}

... mashine ya hali async inafanya kazi yake kwa sisi kwa kuweka kalenda kwa kazi zetu.

Sasa yaSendGroupInviteinafanya kazi kwenye programu ya pamoja pia, hivyo tunachukua hatua hiyo pia:


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;
   });
}


Runtime abstraction inashughulikia mipango kwa njia sawa kama ilivyokuwa kabla, kwa kuitaTask.Factory.StartNewkwa mipango sahihi.

Genera ya Kodi

OK, sasa tunahitaji kuingiza kila kitu ndani yaDowito; na wakati hii inatatua tatizo, ni makosa ya uwezekano, rahisi kusahau, na kwa ujumla kusema, vizuri, inaonekana ya ajabu.

Hebu tuangalie sehemu hii ya msimbo tena:


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


Hapa, bodi yetu ina aGroupya mali.Groupni moduli ambayo inapatikana tu ili kugeuza msimbo katika darasa tofauti na kuepuka kufunikaBotya darasa.


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


Hebu kusema kwamba moduli inapaswa kuwa na interface:


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;
   }
|


Na sasa inafanya kazi tu! Hakuna coding zaidi na mbaya, tu mahitaji rahisi (ambayo ni ya kawaida kwa watengenezaji) ya kuunda interface kwa kila moduli.


Lakini wapi programu? uchawi hutokea ndani ya kuzalisha chanzo ambayo huunda darasa la proxy kwa kila interface ya IBotModule na hufunika kila kazi katikaRuntime.DoPiga simu kwa:


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


Zana ya

Hatua inayofuata ni kuingiza msimbo ili kukusanya takwimu na njia. Kwanza, kila wito wa API unapaswa kufuatiliwa. Sehemu hii ni rahisi sana, kwa sababu usafirishaji wetu kwa ufanisi hufanya kama njia ya gRPC, hivyo tunatumia tu interceptor iliyoandikwa vizuri...


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


ambapoCallInvokerni abstraction ya gRPC ya mwito wa RPC upande wa mteja.


Kisha, itakuwa nzuri kupanua sehemu fulani za msimbo ili kupima utendaji. Kwa sababu hiyo, kila moduli inachukuaIInstrumentationFactoryKwa njia ya interface zifuatazo:


public interface IInstrumentationFactory

{

   public IInstrumentationScope CreateScope(

       string? name = null,

       IInstrumentationContext? actor = null,

       [CallerMemberName] string memberName = "",

       [CallerFilePath] string sourceFilePath = "",

       [CallerLineNumber] int sourceLineNumber = 0);

}


Sasa unaweza kuunganisha sehemu ambazo wewe ni nia ya:


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


Ingawa bado unaweza kutumia kipengele hiki kuunda viwango vya chini, kila njia ya proxy ya moduli inafafanua kiwango cha moja kwa moja:


   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$));
       }
   }
";


Utaratibu wa kiwango cha kurekodi muda wa utekelezaji, pengine, huunda nyayo zilizosambazwa, inaweza kuandika kumbukumbu za udanganyifu, na hufanya vitu vingi vingine vinavyoweza kubadilishwa.


Mawasiliano ya Bot-to-Bot

Mfano katika sehemu ya "Lengo letu" haina kweli kuonyesha mawasiliano yoyote. Tunajua tu bot ya wafuasiPlayerIdIngawa mtindo huu wa kuandika mifano mara nyingi ni rahisi na rahisi, kuna matukio magumu ambapo mbinu hii haifanyi kazi.


Mwanzoni nilijaribu kutekeleza aina fulani ya mfano wa blackboard na uhifadhi wa thamani ya msingi wa kushirikiana kwa mawasiliano (kama Redis), lakini baada ya kuendesha ushahidi fulani wa majaribio ya mbinu, ilionekana kuwa kiwango cha kazi kinaweza kupunguzwa sana hadi dhana mbili rahisi: mstari, na mpiga kura.

Maelezo ya - tiketi

Katika ulimwengu halisi, wachezaji kuwasiliana na kila mmoja kwa namna fulani - wanaandika ujumbe wa moja kwa moja au kutumia mazungumzo ya sauti. Na bots, hatuna haja ya simulate kwamba granularity, na tunahitaji tu kuwasiliana nia. Kwa hiyo, badala ya "Hey Killer2000, wito mimi katika kikundi chako tafadhali" tunahitaji rahisi "Hey,Mtu wa“Nendeni mkaweke alama za mipaka mkishamaliza sasa muwaeleze wananchi ni umbali wanapaswa waache kutoka kwenye hizo alama zenu na muwaelimishe kwamba hiyo ndiyo buffer zone.


public interface ISwarmTickets
{
   Task PlaceTicket(SwarmTicketBase ticket);


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


Bot inaweza kuweka tiketi, na kisha bot mwingine anaweza kupata tiketi hiyo.ISwarmTicketsni kitu chochote zaidi ya mfanyabiashara wa ujumbe na mstari kwaticketType(Kwa kweli nikidogo ya more than that, as there are extra options to prevent bots from retrieving their own tickets, as well as other minor tweaks).


Kwa interface hii, tunaweza hatimaye kugawana mifano ya mfano katika mifano miwili ya kujitegemea. (hapa, msimbo wowote wa ziada unachukuliwa ili kuonyesha wazo la msingi):


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);
}


Mchezo wa

Tuna tabia mbili tofauti, moja kwa kiongozi, moja kwa wafuasi. Bila shaka, zinaweza kugawanywa katika matukio mawili tofauti na kuchapishwa pamoja. Wakati mwingine, hii ni njia bora ya kufanya hivyo, wakati mwingine unaweza kuhitaji muundo wa kiwango cha kikundi (wafuasi kadhaa kwa kiongozi mmoja) au hali nyingine yoyote ya kusambaza / kuchagua data tofauti / majukumu.


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);
}


Kwa hiyo hapa,RoundRobinRoleni tu wrapper fancy karibu na meza ya kushirikiana na operesheni ya modulo kuchagua kipengele sahihi kutoka kwenye orodha.


Mfano wa Swarms

Sasa, na mawasiliano yote yamefichwa nyuma ya mikono na hesabu zinazoshirikiwa, inakuwa ndogo kuanzisha kitufe cha orchestrator, au kutumia baadhi ya hifadhi zilizopo za MQ na KV.


Ukweli funny: hatujawahi kumaliza utekelezaji wa kipengele hiki. Wakati QA walipata mikono yao juu ya utekelezaji wa node moja SwarmAgent, walianza mara moja tu kutumia mifano kadhaa ya wafanyabiashara huru ili kuongeza mzigo.

Mafanikio ya moja kwa moja

Nini kuhusu utendaji? Ni kiasi gani kinachopoteza ndani ya vifungo vyote hivi na synchronizations ya implicit? Sitawafurahia na aina zote za majaribio yaliyofanywa, tu mbili muhimu zaidi ambazo zimeonyesha kuwa mfumo una uwezo wa kujenga mzigo mzuri wa kutosha.

Utekelezaji wa Benchmark:

BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4651/22H2/2022Update)
CPU ya Intel Core i7-10875H 2.30GHz, 1 CPU, 16 kimwili na 8 kimwili
Kuanzishwa kwa .NET SDK 9.0.203
[Host] : .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);
        }
    }
}


Kwanza, hebu tuangalie juu ya karibu tu juu ya mipangilio dhidi ya rahisi Task kuzalisha.

Katika kesi hii, mbinu zote mbili za mzigo ni kama ifuatavyo:


private ValueTask CounterLoadMethod(int i)

{

   Interlocked.Increment(ref StaticControl.Counter);

   return ValueTask.CompletedTask;

}


Matokeo ya Iterations = 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

rahisi ya

10

ya 565

kwa ajili ya 0.0450

0 0 0 0 0 0 0

0.3433

-

-

Mchakato wa 2.83KB

rahisi ya

100

281 ya

0 0 0 0 20

kwa ajili ya 2544

3.1128

0.061

-

5.7 Kb kwa ajili ya

rahisi ya

1000

250 693 ya

1626 kwa ajili ya

2.403 kwa ajili ya

30.2734

5.8594

-

250.67KB kwa ajili ya

Mipango ya

10

40629 ya

Mshahara wa 7842

kwa ajili ya 8054

8.1787

0.1221

-

66.15Kb ya

Mipango ya

100

5386 kwa ajili ya

2 414 ya

2 Mwaka 1901

81.0547

14.6484

-

ya 62.09kb

Mipango ya

1000

4685.812 kwa ajili ya

247917 kwa ajili ya

21772 ya

812.5

375

-

wa 6617.59KB



Inaonekana mbaya? Si kwa usahihi. Kabla ya kufanya mtihani, nilitarajia utendaji mbaya zaidi, lakini matokeo yalikuwa ya kushangaza.Nadhani tu kuhusu kiasi gani kilichotokea chini ya kapu na faida tulizopata tu kwa ~4us kwa kesi ya parallel.


Nini inaweza kuwa hali mbaya zaidi ya majaribio ya kesi mbaya? Nini kuhusu kazi ambayo haina kitu zaidi ya wito wa API? Kwa upande mmoja, karibu kila mbinu ya bot hufanya hivyo, lakini kwa upande mwingine, ikiwa hakuna kitu zaidi ya wito wa API tu, basi juhudi zote za synchronization zinatumika, sivyo?


Njia ya kupakua inaweza tu kuitaPingAsyncKwa urahisi, wateja wa RPC hupatikana nje ya bots.


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


Hapa ni matokeo, tena iterations 10 (server grpc ni katika mtandao wa ndani):


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

rahisi ya

100

Urefu wa 94.45 ms

Mchakato wa 804 ms

Maoni ya 148

600

200

6.4 Mb ya

rahisi ya

1000

596.69 kwa ajili ya

5952 kwa ajili ya

Maoni ya 45730 ms

9000

7000

77.7 MB ya juu

Mipango ya

100

Urefu wa 95.48 ms

Mchakato wa 1.547 ms

Mchakato wa 1.292

833.3333

333.3333

Kiwango cha 5.85 MB

Mipango ya

1000

Maoni ya 625,52

Mchakato wa 697 ms

Maoni ya 24405 ms

8000

7000

Mchezo wa 68.57 MB


Kama ilivyotarajiwa, athari ya utendaji kwenye matukio halisi ya kazi ni ya kutosha.


Uchambuzi wa

Bila shaka, kuna kumbukumbu za backend, takwimu, na nyayo ambazo hutoa mtazamo mzuri wa kile kinachotokea wakati wa kupakia. Lakini Swarm huenda hatua zaidi, kuandika data yake mwenyewe - wakati mwingine iliyounganishwa na backend - kukamilisha.


Hapa ni mfano wa takwimu kadhaa katika mtihani wa kushindwa, na idadi ya watendaji inaongezeka. mtihani ulishindwa kutokana na ukiukwaji wa SLA kadhaa (tazama muda katika wito wa API), na tuna vifaa vyote muhimu vya kufuatilia kile kilichotokea kutoka kwa mteja.


Grafana dashboard panels showing failed test


Sisi kawaida kuacha mtihani kabisa baada ya idadi fulani ya makosa, ambayo ni kwa nini API wito mstari mwisho ghafla.


Bila shaka, nyayo zinahitajika kwa ufuatiliaji mzuri. Kwa majaribio ya Swarm, nyayo za mteja zinahusishwa na nyayo za nyuma kwenye mti mmoja.connectionIdD/botIdambayo inasaidia katika kuimarisha.


Distributed tracing connecting Swarm scenario with backend


Kumbuka: DevServer ni muundo rahisi wa monolith ya huduma zote-in-one za backend zinazotolewa kwenye PC za watengenezaji, na hii ni mfano maalum uliofanywa ili kupunguza kiasi cha maelezo kwenye skrini.


Swarm bots write another kind of trace: they mimic the traces used in performance analysis. With the aid of existing tools, those traces can be viewed and analyzed using built-in capabilities like PerfettoSQL.


Adapting perf logs to show bot execution flow

Nini kilichotokea katika mwisho wa

Sasa tuna SDK ambayo bots ya GameClient na Dedicated Server wote ni kujengwa juu yake. Bots hizi sasa zinaunga mkono kimsingi ya mantiki ya mchezo - ikiwa ni pamoja na mifumo ya marafiki, vikundi vya msaada, na matchmaking. bots za kujitolea zinaweza kuiga mechi ambapo wachezaji wanapata tuzo, majaribio ya maendeleo, na mengi zaidi.


Kuwa na uwezo wa kuandika msimbo rahisi iliwezesha kuunda modules mpya na mifano rahisi ya mtihani kwa timu ya QA. Kuna wazo kuhusu mifano ya FlowGraph (programming ya Visual), lakini hii bado ni wazo tu kwa wakati huu.


Majaribio ya utendaji ni karibu ya kuendelea - ninasema karibu kwa sababu mtu bado anahitaji kuanza kwa manually.


Swarm husaidia si tu na majaribio, lakini kwa kawaida hutumiwa kuigiza na kurekebisha makosa katika mantiki ya mchezo, hasa wakati huu ni vigumu kufanya kwa mkono, kama wakati unahitaji wateja kadhaa wa mchezo kufanya mfululizo maalum wa vitendo.


Kwa ujumla, tunafurahia sana na matokeo yetu na hatuna hatia kuhusu juhudi zilizotumika katika kuendeleza mfumo wetu wa mtihani.



Natumaini umefurahia makala hii, ilikuwa changamoto kukujulisha juu ya nuances zote za maendeleo ya Swarm bila kufanya maandishi haya ya kutosha. Nina uhakika kwamba ninaweza kufuta baadhi ya maelezo muhimu katika mchakato wa usawa wa ukubwa wa maandishi na habari, lakini nafurahia kutoa mazingira zaidi ikiwa una maswali yoyote!

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks