227 уншилтууд

Ямар ч хэрэгсэл тоглоомын манай ачааллын туршилтын бүтэц хийх боломжтой - Тиймээс бид Swarm-ийг бий болгосон

by Andrew Rakhubov19m2025/05/20
Read on Terminal Reader

Хэтэрхий урт; Унших

Бид өөрчлөн WebSocket+Protobuf протоколуудыг, интер-бот синхронизацийг, томъёо симуляцийг ажиллуулах боломжийг олгодог мэргэшсэн ачаалал шалгалт хэрэгсэл хэрэгтэй. Үнэнтэй хэрэгслүүд хязгаарлагдмал байсан бөгөөд тэд Swarm-ийг үүсгэсэн: "ботоо" (захиалга) координатаар төлөвлөлтийн дагуу үйл явцыг ажиллуулах, билет, сонгогч дамжуулан ботоо нь ботоос харилцааг дэмждэг, автомашины хэрэгсэл нь метрик, шуудангийн хувьд бүх кодыг ашигладаг. Swarm-ийн загвар нь статусын шинэчлэхэд хариулах хялбар байдал, ConcurrentExclusiveSchedulerPair дамжуулах, код генератор дамжуулан үүсгэх, бүрэн
featured image - Ямар ч хэрэгсэл тоглоомын манай ачааллын туршилтын бүтэц хийх боломжтой - Тиймээс бид Swarm-ийг бий болгосон
Andrew Rakhubov HackerNoon profile picture
0-item
1-item

Бид Swarm, бидний дотоод ачаалал шалгалт бүтэц, gamedev-д эцэст-аас эцэст шалгалт, өөрчлөн протоколуудыг, томъёо бот симуляцийг хангахын тулд хэрхэн боловсруулсан.

Бид Swarm, бидний дотоод ачаалал шалгалт бүтэц, gamedev-д эцэст-аас эцэст шалгалт, өөрчлөн протоколуудыг, томъёо бот симуляцийг хангахын тулд хэрхэн боловсруулсан.


Өнөөдөр, энэ нь хамгийн бага бол програм хангамжийн бүтээгдэхүүнийг санаа зовох нь маш хялбар юмНөхцөлтүвшин туршилтын түвшин. Unit туршилт нь кодын жижиг хэсэгт буурчлах стандарт арга юм. Энэ нь эцэст-аас эцэст туршилт бүх хэрэглээний ажлын үйл явцыг хамардаг. GameDev нь ихэвчлэн өөр өөрсдийн туршилтын арга хэрэгсэл ашиглан тэдний тусгай хэрэгцээг хангахын тулд ашигласан арга зам юм.


Сайн байна уу, би Andrey Rakhubov байна, MY.GAMES-ийн тэргүүлэх програм хангамжийн инженерийн! Энэ бичлэгт, би манай студиод ашиглагддаг backend, болон War Robots: Frontiers-ийн мета-ийг татаж авахын тулд бидний арга хэрэгсэл талаар дэлгэрэнгүй мэдээллийг хуваалцах болно.

Бидний архитектурын талаар дэлгэрэнгүй

Ямар ч хязгааргүй дэлгэрэнгүй, бидний backend нь класик үйлчилгээний багц, үйлчлүүлэгчдэд жинхэнэ холбогдсон түлхүүрүүдтэй багц юм.

backend architecture overview

Нэгдэж байгаа зарим бусад хязгаарлалыг хамаарна, энэ арга нь тоглоомын хөгжүүлэх хамгийн хязгаарлагдмал асуудлуудыг шийдэхийн тулд маш сайн хандлагатай. Та Rust-lang-ийн хөгжүүлэх эхний хоногийн үед та унших үед документын өөрчилж байгаа эсэхийг мэдэгдэж болно.ЭнэГэгээ, бид GDD (Game Design Document) талаар нэг хэлбэрийг байна.


Бүтээгдэхүүний дизайны эхний этапед имплементийн, өөрчлөх нь маш хямд байдаг бөгөөд дараа нь шаардлагатай бол өөр нэг үйлчилгээ гэж refactor нь маш хялбар байдаг.


Үүнээс гадна, энэ нь багасгалт, интеграцийн туршилтанд хязгаарлагддаг, учир нь танд бага, сайн тодорхойлолттай API-тэй үйлчилгээг ягаан тугалгагүй байдаг. Үүнээс гадна, хязгаарлагдмал түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн түдгэлзүүлсэн.

Тест

Хэрэв та мэдэж байгаа бол, үйлчлүүлэгч болон үйлчилгээний нэгжийн туршилтын (ийг үйлчилгээнд ачаалал / интеграцийн туршилтын) ямар ч бусад төрлийн програм хангамжтай харьцуулахад янз бүрийн биш юм.

  • Load болон интеграцийн туршилтын үйлчлүүлэгчид
  • End-to-end болон load backend шалгалт


Бид энэ нь GameDev-ийн тусгай биш, харин тоглоомын загваруудтай холбоотой бөгөөд энэ талаар дэлгэрэнгүй мэдэгдэж чадахгүй байна. Үнэгүй арга хэрэгсэл нь нэг туршилтын дотор хадгаламжийн тохиромжтой тусгай нэг нодоод clusterтай байх бөгөөд энэ нь бүх outgoing хүсэлтийг хамарж, акторууд API-ийг хайж чадна. Бид нэг арга хэрэгсэл ашигладаг.


Гэсэн хэдий ч, зүйлүүд тавтай морилно уу, эсвэл эцэст-аас эцэст шалгалт хийх үед илүү сонирхолтой байх болно - энэ нь бидний түүх эхлэх юм.


Тиймээс бидний тохиолдолд хэрэглэгчийн хэрэглээ нь тоглоом юм. Тоглоом нь Unreal Engine-ийг ашигладаг, Тиймээс код C++ хэлбэрээр бичсэн бөгөөд сервер дээр бид C#-ийг ашигладаг. Тоглоомчид тоглоомын интерфэйс элементүүдтэй харилцаа холбоо барина, backend-д хүсэлтийг үүсгэхийн тулд (или хүсэлтийг шууд хэлбэрээр хийж байна).


Энэ үед фреймворгийн ихэнх нь бидний хувьд ажилладаг, клиент програм хангамж нь браузер гэж үздэг бүх төрлийн селен хэлбэртэй багц нь хязгаарлагддаг.


Дараа нь бид хэрэглэгчийн болон backend хооронд өөрчилсөн харилцаа холбооны протокол ашигладаг. Энэ хэсэг нь үнэхээр тусгай өгүүллийг хүсч байна, гэхдээ ялангуяа гол концептуудыг үзнэ үү:

  • Холбоо барих WebSocket холболт дамжуулан
  • Энэ нь схем-first юм; Бид Protobuf-ийг санал, үйлчилгээний бүтэц тохируулахын тулд ашигладаг
  • WebSocket бичлэгүүд нь Protobuf бичлэгүүд нь metadata нь контейнеруудтай бөгөөд энэ нь шаардлагатай gRPC-тэй холбоотой мэдээлэл, URL болон headers гэх мэт загвартай.

Тиймээс өөрчилсөн протоколын тодорхойлолт боломжийг олгодог ямар ч хэрэгсэл үйл ажиллагаа нь тохиромжтой биш юм.

Татаж авах хэрэгсэл Тэдний бүх тест

Мөн бид өөрчилсөн протоколтай REST / gRPC ачаалал шалгалт, эцэст-ээс эцэст шалгалтанд нэг арга хэрэгсэл хийх хүссэн. Тестинг-д харьцуулагдсан бүх шаардлага, зарим эх сурвалжуулалттай дараа бид дараах санал болгож байна:


НөхцөлӨнгөрсөнНэмэлт

Ихэнх нь тэдний давуу талтай байсан, гэхдээ ихэнх зүйлс нь ихэвчлэн (адамдаа дотор) өөрчлөлтийггүйгээр шийдэж чадахгүй байна:


  • First, there was a need related to inter-bot communication for synchronization purposes, as discussed earlier.
  • Хоёр дахь, хэрэглэгчид нь маш реактив бөгөөд тэдний станц нь туршилтын үзэсгэлэнт явдалтай үйл явцгүйгээр өөрчилж болно; Энэ нь нэмэлт синхронжуулалт, таны кодд олон хоорондын дамжуулах хэрэгтэй.
  • Өнгөрсөн боловч найдвартай, эдгээр хэрэгсэл нь гүйцэтгэлийн туршилтанд ихэвчлэн төвлөрсөн бөгөөд багасгах, цаг хугацааны интервалг тодорхойлохын тулд олон онцлог санал болгож байна, харин хялбар арга замыг үүсгэх чадвартай байсан.


Энэ нь бидний мэргэшсэн тоног төхөөрөмж хэрэгтэй гэж мэдэгдэх үед байна. Энэ нь бидний хэрэгсэл -ӨргөтгөлӨнгөрсөн

Өргөтгөл

Өндөр түвшинд, Swarm-ийн үйл ажиллагаа нь сервер дээр ачаалал үүсгэхийн тулд олон үйлчлүүлэгчид эхлэх юм. Эдгээр үйлчлүүлэгчид нь бот гэж нэрлэдэг бөгөөд тэд хэрэглэгчийн үйл явцыг чийгшээлж болно. Swarm-ийн нэг эсвэл хэд хэдэн үйлчлүүлэгчид юм.


Дэлгэрэнгүй, энд хэрэгсэл нь шаардлагыг жагсаалт байна:


  1. Статийн шинэчлэлд хариулах нь хялбар байх ёстой
  2. Конкурент нь асуудал байх ёстой
  3. Код нь автоматаар Instrumentable байх ёстой
  4. Bot-to-bot холбоо нь хялбар байх ёстой
  5. Multiple instances should be joinable in order to create sufficient load
  6. Ажлын хэрэгсэл нь хялбар байх ёстой бөгөөд backend-д ач холбогдолтой стресс үүсгэх чадвартай байх ёстой


Бонус гэж, би бас хэд хэдэн нэмэлт зүйл нэмсэн:

  1. Бүх гүйцэтгэлийн болон эцсийн туршилтын үзэсгэлэнтэй байх ёстой
  2. Тоног төхөөрөмж нь тээвэрлэлт агностик байх ёстой; бид энэ нь шаардлагатай бол бусад тээвэрлэлт холбох боломжтой байх ёстой
  3. Тоног төхөөрөмж нь императив код хэлбэртэй байдаг, учир нь би хувийн хэлбэрээр хэлбэртэй хэлбэр нь усны шийдэлтэй комплексны үзэсгэлэнд тохиромжтой биш юм.
  4. Бот нь туршилтын хэрэгсэлээс тусгаарлагдмал байх ёстой, энэ нь хэзээ ч хатуу түншүүд байх ёстой.


Бидний зорилго

Сэтгэгдэл үлдээх: Бид шилжүүлэх хүсэж байгаа кодыг үзнэ үү; Бид бот гэж үзнэ үү, энэ нь өөрсдийн зүйлийг хийж чадахгүй, энэ нь зөвхөн өөрсдийн өөрчлөлтийг хадгалах боломжтой бөгөөд Scenario нь шилжүүлэгч юм.


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


Зөвлөгөө

Backend хэрэглэгчдэд маш их шинэчлэлтүүдээр идэвхжүүлдэг бөгөөд энэ нь хязгаарлагдмал байж болох юм; дээрх жишээ нь, энэ ньGroupInviteAddedEventБот нь эдгээр үйл явдлыг интернетийн талаар анхаарлаа хандуулж, тэднийг гадааны код дээр үзэх боломжийг олгодог.


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


Код нь маш хялбар юм (а тэрээр та нарттай байж болох юм).OnGroupStateUpdatehandler нь зүгээр л урт шилжих тохиолдолд юм), тутмын олон зүйл байдаг.


StreamSubscriptionЭнэ нь аIObservable<ObservedEvent<TStreamMessage>>

Энэ нь хэрэглэгддэг өргөтгөл үзүүлдэг, өөрсдийн амьдралын цикл байдаг, метрикеар хамарсан байна.

Өнгөрсөн ашиг: Энэ нь үйлчлүүлэгчийн тохиргоог тохиргоог тохиргоог тохиргоог унших эсвэл өөрчлөхын тулд тодорхой синхронизацийг шаарддаггүй.


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


Конкурент нь асуудал байх ёстой

Үүнээс гадна, код нь шууд, нэг ширхэг код гэж бичсэн юм. Үзүүлэлт нь хялбар юм: ямар ч хэзээ ч сценарий, эсвэл энэ сценарийг үүсгэсэн ботонд оролцох код дуусгах, түүнээс гадна, бот / модульын дотоод код нь ботоны бусад хэсгүүдтэй хооронд дуусгах ёстой.


Энэ нь read/write lock-тай харьцуулагдсан код (shared access) болон bot code-тай харьцуулагдсан (exclusive access) юм. Тэгээд бидний зорилго нь энэ төрлийн locks ашиглан хангах боломжтой боловч илүү сайн арга юм.

Task Scheduler болон синхронизацийн контекст

Эдгээр дөрвөн механизм нь C#-д асинхрон кодны маш хүчтэй хэсэг юм. Хэрэв та WPF програмуудыг боловсруулсан бол энэ нь асинхрон юм.DispatcherSynchronizationContextЭнэ нь таны async дуудлага нь UI нунтаг руу гарчигтай дамжуулдаг.


Бидний үзэсгэлэнд, бид ширээний аффиниц тухай үгүйсэг, гэхдээ өөрсдийн үзэсгэлэнд үйл явдлын гүйцэтгэлийн дүрэмд илүү үгүйсэг.


Нийт түвшин кодыг бичнэ үүсээс өмнө бид ихэвчлэн мэдэгдэжгүй нэг класс гэж нэрлэдэг.ConcurrentExclusiveSchedulerPair. ньdocs:

Дотоо


Тавтай морилно уу Тавтай морилно уу Тавтай морилно уу Тавтай морилно уу

Тавтай морилно уу Тавтай морилно уу Тавтай морилно уу Тавтай морилно уу


Энэ нь зөв бид хүссэн гэж үздэг! Одоо бид сценарийн дотор бүх кодыг гүйцэтгэлийг хангах хэрэгтэй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();
}

... async state машин бидний үйл явдлын төлөвлөгөөг хадгалж бидний ажил хийж байна.

ОдооSendGroupInviteЭнэ нь concurrent scheduler дээр ажиллуулдаг, Тиймээс бид энэ нь адил хийлгэх болно:


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 абстракцийг өмнөтай харьцуулахад цаг хугацааны програм хангамж хамардагTask.Factory.StartNewЗөвхөн тохиромжтой график.

Код Generation

OK, одоо бид гарын авлага нь бүх зүйлийг хавтан хийх хэрэгтэйDoЗохиогчийн эрх © Зохиогчийн эрх © Зохиогчийн эрх хуулиар хамгаалагдсан.

Хамгийн сүүлд энэ хэсэг нь кодыг үзнэ үү:


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


Энд, бидний бот байна aGroupЗохиогчийн эрх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Энэ нь client-side RPC invocation нь gRPC-ийн алдаа юм.


Дараа нь гүйцэтгэлийг хэмнэхын тулд кодыг зарим хэсэгт өргөн хүрээрэй. Тиймээс бүр модуль нь injectsIInstrumentationFactoryдараах интерфэйс нь:


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


Инструментацийн хүрээ нь гүйцэтгэлийн цаг хугацаа, тусгаарлалт жагсаалт, дистрибьюцийг үүсгэдэг, debug logs бичж чадна, бусад олон configurable зүйл хийх болно.


Bot-to-bot холбоо

"Our goal" хэсэгт жишээ нь үнэхээр ямар ч харилцааг харуулсан биш юм. Бид зүгээр л дараах бот мэддэгPlayerIdучир нь энэ нь бусад орон сууцны өөрчлөн юм. Энэ стиль бичлэгийн сценарий нь ихэвчлэн хялбар, шууд байдаг, Энэ арга нь зүгээр л ажиллахгүй нь илүү комплексны тохиолдолд байдаг.


Би эхлээд хуваалцахын тулд хуваалцсан ключ-д үнэ цэнэтэй хадгалах талаарх харьцуулахад харьцуулахад харьцуулахад харьцуулахад харьцуулахад харьцуулахад ямар нэг төрөл бүрийн харьцуулахад төлөвлөж байна (жишээ нь Redis), гэхдээ концепт сценарийн туршилтаас зарим нь баталгаатай дараа энэ нь ажлын хэмжээ нь хоёр энгийн концептуудыг багасгах боломжтой гэж үзэж байна: цуврал, selector.

Билет - Tickets

Эдүүлбэр » Бүтээгдэхүүн » Бүтээгдэхүүн » Бүтээгдэхүүн » Бүтээгдэхүүн » Бүтээгдэхүүн » Бүтээгдэхүүн » Бүтээгдэхүүн » Бүтээгдэхүүн » Бүтээгдэхүүн » БүтээгдэхүүнЯмар ч, тавтай морилно уу, группд мийг зочилж байна. "Энд тавтай морилно уу:


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Энэ нь зүгээр л хуваалцсан хуваалцсан нь хуваалцсан хуваалцсан болон жагсаалт дээр зөв элемент сонгох модуль үйл ажиллагаа юм.


Cluster нь Swarms

Одоо, бүх харилцаа холбооны давхарга, хуваалцсан хуваалцсан, оркестратор зүрх, эсвэл одоогийн зарим MQ болон KV хадгалах ашиглах нь хязгааргүй байх болно.


Хялбар зүйл: Бид энэ шинж чанарын имплементацийг хэзээ ч төгсгөлгүй. QA нь нэг зүрхний имплементацийг хандах үед SwarmAgent, тэд дараа нь зүгээр л өөрчлөн агентлын хэд хэдэн инстанцийг ашиглахыг эхэлсэн. Үнэндээ, энэ нь илүү хялбар байсан.

Нэг инстанц гүйцэтгэл

Өндөр гүйцэтгэлийн талаар юу вэ? Эдгээр бүх хавхлага, хязгаарлагдмал синхронизацийг хоорондоо багассан байна уу? Би хийж байгаа бүх янз бүрийн туршид танд хялбар байх болно, зөвхөн систем нь маш сайн ачаалал үүсгэх боломжийг олгодог хамгийн чухал хоёр.

Бенчмарк тохируулга:

BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4651/22H2/2022Update)
Intel Core i7-10875H CPU 2.30GHz, 1 CPU, 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);
        }
    }
}


Хамгийн анхны, бид зүгээр л Task spawning-ийг харьцуулахад төлөвлөгөөний хоорондоо харьцуулаарай.

Энэ тохиолдолд хоолойны хоолойны хоолой нь дараах юм:


private ValueTask CounterLoadMethod(int i)

{

   Interlocked.Increment(ref StaticControl.Counter);

   return ValueTask.CompletedTask;

}


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

Өнгөрсөн

10

5356 Үнэгүй

Тавтай морилно уу

0.0421ийг

0.3433

-

-

2.8 КБ

Өнгөрсөн

100

30.281Уурууд

0.2720Ус

0.2544Ус

3.1128

0.061

-

25.67кб

Өнгөрсөн

1000

250693аг

Бүртгүүлэх 1626

2.4037Ус

30.2734

5.8594

-

250.67КБ

Үнэлгээ

10

40629аяар

0.7842Ус

0.8054Ус

8.1787

0.1221

-

66.15КБ

Үнэлгээ

100

325.386Ус

2.3414Ус

2 1901 оны

81.0547

14.6484

-

Бүртгүүлэх

Үнэлгээ

1000

4,685.812Ус

24.7917Ус

21.9772Ус

812.5

375

-

Зохиогчийн эрх © 2018 Шивээсний зургийн санаанууд



Үнэндээ биш. Үнэндээ туршилтын хийх өмнө би маш сайн гүйцэтгэлийг хүссэн байсан боловч үр дүнг маш их гайхалтай байсан. Зөвхөн хавтгай дөрвөлжин тохиолдолд зөвхөн ~4us-ийн ашигтай үр дүнг хэрхэн олж авсан гэж бодож байна. Гэсэн хэдий ч, энэ нь зүгээр л оёдлын иллюстраци юм; Бид илүү практик харьцуулалттай байдаг.


Ямар ч ач холбогдолтой найдвартай туршилтын тохиолдолд юу байж болох юм? Эцэст нь API-ийн дуудлагатай ямар ч үйл ажиллагаа нь юу вэ? Нэг хуудсуудаас, ботооны бүх арга нь энэ нь хийж байна, гэхдээ бусад нь, зөвхөн API-ийн дуудлагатай ямар ч зүйл биш бол, дараа нь бүх синхронжуулалтын үйл явцыг харьцуулана уу?


Татаж авах арга нь зүгээр л зовохPingAsyncХэвлэхэд хялбар, RPC хэрэглэгчид ботодын гадна хадгалах болно.


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


Энд үр дүнд, шинэ 10 iterations (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 Мс

1.804 Мс

2.148 Мс

600

200

6.4 МБ

Өнгөрсөн

1000

596.69 мс

15.592 Мс

45730 Мс

9000

7000

76.77 МБ

Үнэлгээ

100

95.48 Мс

1.547 Мс

1.292 Мс

833.3333

333.3333

6.85 МБ

Үнэлгээ

1000

625,52 мс

14,697 мс

42405 Мс

8000

7000

68.57 МБ


Одоогоор, гүйцэтгэлийн үр дүнтэй үйл явдал дээр үр дүнтэй үр нөлөө багатай байдаг.


Анализ

Гэсэн хэдий ч, backend logs, метрики, шугам байдаг бөгөөд энэ нь утасны үед юу гэж үзэж байгаа талаар сайн үзэсгэлэн олгодог. Гэхдээ Swarm нь өөрийн өгөгдөл - заримдаа backend-тэй холбогдсон - хуваалцсан.


Энэхүү тест нь хэд хэдэн SLA-ийн богино (API-ийн дуудлагад цаг хугацааг үзнэ үү), хэрэглэгчийн үзэсгэлэнтээс юу хийхэд шаардлагатай бүх тоног төхөөрөмжтай байдаг.


Grafana dashboard panels showing failed test


Бид ихэвчлэн хэд хэдэн алдааны дараа туршилтын бүрэн зогсоох, энэ нь API-ийн дуудлага шуудангийн шуудангийн эцсийн шалтгаан юм.


Эцэст нь сайн харахын тулд харах хэрэгтэй. Swarm туршилтын ажиллагаанд хэрэглэгчийн харах нь нэг арьс дээр backend харах нь холбогдсон байна. Хэрэглэгчийн харах нь тодорхой харах боломжтойconnectionIdD/botId, Debugging нь туслах.


Distributed tracing connecting Swarm scenario with backend


Ангилал: DevServer нь хөгжүүлэгчид PC-д эхэлж буй нэг бүрхүүл үйлчилгээний тохиромжтой монолит бүтэц юм. Энэ нь дэлгэц дээр дэлгэрэнгүй мэдээллийг багасгахын тулд тусгайлан боловсруулсан жишээ юм.


Swarm ботоо өөр төрлийн шугам бичдэг: Тэд гүйцэтгэлийн анализэд хэрэглэгддэг шугам илгээж байна. Судалгааны хэрэгсэл ашиглан эдгээр шугам нь PerfettoSQL гэх мэт суурилсан хүчин чадал ашиглан үзэж, анализ болно.


Adapting perf logs to show bot execution flow

Ямар ч бид эцсийн үед

Бид одоо GameClient болон Dedicated Server ботоор суурилсан SDK байна. Эдгээр ботоор одоо тоглоомны логик нь ихэнх дэмждэг. Тэд нь найз нөхөд дор систем, дэмжлэг бүлэг, matchmaking зэрэг байдаг. Dedicated ботоор тоглоомын шагналыг симулирах боломжтой.


Хэвлэхэд хялбар кодыг үүсгэхийн тулд QA баг шинэ модулиуд, хялбар туршилтын үзэсгэлэн үүсгэх боломжтой. FlowGraph үзэсгэлэн (визуал програм хангамж) талаар санаа байна, Гэхдээ энэ нь одоо ч гэсэн санаа юм.


Ажлын туршилт нь ойролцоогоор байнга байнгын байдаг - Би ихээхэн хэлж байна, учир нь хэн ч бас гарын авлагатай эхлэх ёстой.


Swarm нь туршид туслах нь зөвхөн туслах, гэхдээ ихэвчлэн тоглоомны логик дээр буцаж, буцаж ашиглаж байна, ялангуяа энэ нь мануаль хийх хэцүү байдаг үед, жишээ нь хэд хэдэн тоглоомын хэрэглэгчид нь тусгай үйл явцыг гүйцэтгэх хэрэгтэй үед.


Нийтлэгдсэн нь, бидний үр дүнд маш сайн мэдэгддэг бөгөөд бид өөрийн туршилтын систем боловсруулсан үйл явдлын талаар ярихгүй байна.



Би энэ өгүүллийг тавтай морилно уу, энэ нь Swarm-ийн хөгжүүлэх бүх шинж чанарыг мэдэгдэх нь гайхалтай байсан юм. Би текст хэмжээ, мэдээллийг байлгах үйл явцад зарим чухал мэдээлэлгүй байж болох юм. Би ямар ч асуулт байгаа бол илүү контекст санал болгож байна!

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks