最適なワーカー配布モードのためにワーカーをランク付けする方法のカスタマイズ方法

best-worker 配布モードでは、最初にジョブを処理できる最適なワーカーが選択されます。 ワーカーをランク付けするロジックはカスタマイズ可能で、式や Azure 関数で 2 人のワーカーを比較できます。 次の例は、独自の Azure 関数を使用してこのロジックをカスタマイズする方法を示しています。

シナリオ:最適なワーカー分散モードでのカスタム スコアリング ルール

キューに関連付けられているワーカー間でオファーを配布する必要があります。 ワーカーには、ラベルとスキル セットに基づいてスコアが与えられます。 スコアが最も高いワーカーが、最初のオファーを取得します (BestWorker 配布モード)

Diagram showing Best Worker Distribution Mode problem statement

状況

  • ジョブが作成され、分類されました。
    • ジョブには、次のラベルが関連付けられています。
      • ["CommunicationType"] = "Chat"
      • ["IssueType"] = "XboxSupport"
      • ["Language"] = "en"
      • ["HighPriority"] = true
      • ["SubIssueType"] = "ConsoleMalfunction"
      • ["ConsoleType"] = "XBOX_SERIES_X"
      • ["Model"] = "XBOX_SERIES_X_1TB"
    • ジョブには、次の WorkerSelectors が関連付けられています。
      • ["English"] >= 7
      • ["ChatSupport"] = true
      • ["XboxSupport"] = true
  • 現在、ジョブは "Queued" の状態です。ワーカーとの一致を待機している Xbox ハードウェア サポート キューに挿入されました。
  • 複数のワーカーが同時に使用可能になります。
    • Worker 1 は、次のラベルで作成されています。
      • ["HighPrioritySupport"] = true
      • ["HardwareSupport"] = true
      • ["Support_XBOX_SERIES_X"] = true
      • ["English"] = 10
      • ["ChatSupport"] = true
      • ["XboxSupport"] = true
    • Worker 2 は、次のラベルで作成されています。
      • ["HighPrioritySupport"] = true
      • ["HardwareSupport"] = true
      • ["Support_XBOX_SERIES_X"] = true
      • ["Support_XBOX_SERIES_S"] = true
      • ["English"] = 8
      • ["ChatSupport"] = true
      • ["XboxSupport"] = true
    • Worker 3 は、次のラベルで作成されています。
      • ["HighPrioritySupport"] = false
      • ["HardwareSupport"] = true
      • ["Support_XBOX"] = true
      • ["English"] = 7
      • ["ChatSupport"] = true
      • ["XboxSupport"] = true

期待

最初のオファーを取得するワーカーを選択するためにワーカーをスコアリングするときには、次の処理が必要です。

Decision flow diagram for scoring worker

(上に示す) 決定フローは次のようになります。

  • ジョブが HighPriority ではない場合:

    • ラベルが ["Support_XBOX"] = true のワーカーはスコア 100 を取得します。
    • それ以外の場合は、スコア 1 を取得します。
  • ジョブが HighPriority の場合:

    • ラベルが ["HighPrioritySupport"] = false のワーカーがスコア 1 を取得します。
    • それ以外の場合で、["HighPrioritySupport"] = true の場合:
      • ワーカーはコンソール タイプで専門ですか-> ワーカーのラベルは ["Support_<jobLabels.ConsoleType>"] = true ですか? true の場合、ワーカーはスコア 200 を取得します
      • それ以外の場合は、スコア 1 を取得します。

Azure 関数の作成

プロセスをさらに進む前に、まずワーカーにスコアを付ける Azure 関数を定義しましょう。

Note

次の Azure 関数は JavaScript を使用しています。 詳細については、「クイックスタート: Visual Studio Code を使用して Azure に JavaScript 関数を作成する」を参照してください。

ワーカー 1 の入力のサンプル

{
  "job": {
    "CommunicationType": "Chat",
    "IssueType": "XboxSupport",
    "Language": "en",
    "HighPriority": true,
    "SubIssueType": "ConsoleMalfunction",
    "ConsoleType": "XBOX_SERIES_X",
    "Model": "XBOX_SERIES_X_1TB"
  },
  "selectors": [
    {
      "key": "English",
      "operator": "GreaterThanEqual",
      "value": 7,
      "expiresAfterSeconds": null
    },
    {
      "key": "ChatSupport",
      "operator": "Equal",
      "value": true,
      "expiresAfterSeconds": null
    },
    {
      "key": "XboxSupport",
      "operator": "Equal",
      "value": true,
      "expiresAfterSeconds": null
    }
  ],
  "worker": {
    "Id": "e3a3f2f9-3582-4bfe-9c5a-aa57831a0f88",
    "HighPrioritySupport": true,
    "HardwareSupport": true,
    "Support_XBOX_SERIES_X": true,
    "English": 10,
    "ChatSupport": true,
    "XboxSupport": true
  }
}

実装例:

module.exports = async function (context, req) {
    context.log('Best Worker Distribution Mode using Azure Function');

    let score = 0;
    const jobLabels = req.body.job;
    const workerLabels = req.body.worker;

    const isHighPriority = !!jobLabels["HighPriority"];
    context.log('Job is high priority? Status: ' + isHighPriority);

    if(!isHighPriority) {
        const isGenericXboxSupportWorker = !!workerLabels["Support_XBOX"];
        context.log('Worker provides general xbox support? Status: ' + isGenericXboxSupportWorker);

        score = isGenericXboxSupportWorker ? 100 : 1;

    } else {
        const workerSupportsHighPriorityJob = !!workerLabels["HighPrioritySupport"];
        context.log('Worker provides high priority support? Status: ' + workerSupportsHighPriorityJob);

        if(!workerSupportsHighPriorityJob) {
            score = 1;
        } else {
            const key = `Support_${jobLabels["ConsoleType"]}`;
            
            const workerSpecializeInConsoleType = !!workerLabels[key];
            context.log(`Worker specializes in consoleType: ${jobLabels["ConsoleType"]} ? Status: ${workerSpecializeInConsoleType}`);

            score = workerSpecializeInConsoleType ? 200 : 100;
        }
    }
    context.log('Final score of worker: ' + score);

    context.res = {
        // status: 200, /* Defaults to 200 */
        body: score
    };
}

ワーカー 1 の出力

200

前述の実装では、指定されたジョブに対して、ワーカーは次のスコアを取得します。

ワーカー スコア
ワーカー 1 200
ワーカー 2 200
ワーカー 3 1

最適なワーカー モードに基づいてオファーを配布する

Azure 関数アプリの準備ができたので、Router SDK を使用して BestWorkerDistribution モードのインスタンスを作成しましょう。

var administrationClient = new JobRouterAdministrationClient("<YOUR_ACS_CONNECTION_STRING>");

// Setup Distribution Policy
var distributionPolicy = await administrationClient.CreateDistributionPolicyAsync(
    new CreateDistributionPolicyOptions(
        distributionPolicyId: "BestWorkerDistributionMode",
        offerExpiresAfter: TimeSpan.FromMinutes(5),
        mode: new BestWorkerMode(scoringRule: new FunctionRouterRule(new Uri("<insert function url>")))
    ) { Name = "XBox hardware support distribution" });

// Setup Queue
var queue = await administrationClient.CreateQueueAsync(
    new CreateQueueOptions(
        queueId: "XBox_Hardware_Support_Q",
        distributionPolicyId: distributionPolicy.Value.Id
    ) { Name = "XBox Hardware Support Queue" });

// Create workers
var worker1 = await client.CreateWorkerAsync(new CreateWorkerOptions(workerId: "Worker_1", capacity: 100)
    {
        Queues = { queue.Value.Id },
        Channels = { new RouterChannel(channelId: "Xbox_Chat_Channel", capacityCostPerJob: 10) },
        Labels =
        {
            ["English"] = new RouterValue(10),
            ["HighPrioritySupport"] = new RouterValue(true),
            ["HardwareSupport"] = new RouterValue(true),
            ["Support_XBOX_SERIES_X"] = new RouterValue(true),
            ["ChatSupport"] = new RouterValue(true),
            ["XboxSupport"] = new RouterValue(true)
        }
    });

var worker2 = await client.CreateWorkerAsync(new CreateWorkerOptions(workerId: "Worker_2", capacity: 100)
    {
        Queues = { queue.Value.Id },
        Channels = { new RouterChannel(channelId: "Xbox_Chat_Channel", capacityCostPerJob: 10) },
        Labels =
        {
            ["English"] = new RouterValue(8),
            ["HighPrioritySupport"] = new RouterValue(true),
            ["HardwareSupport"] = new RouterValue(true),
            ["Support_XBOX_SERIES_X"] = new RouterValue(true),
            ["ChatSupport"] = new RouterValue(true),
            ["XboxSupport"] = new RouterValue(true)
        }
    });

var worker3 = await client.CreateWorkerAsync(new CreateWorkerOptions(workerId: "Worker_3", capacity: 100)
    {
        Queues = { queue.Value.Id },
        Channels = { new RouterChannel(channelId: "Xbox_Chat_Channel", capacityCostPerJob: 10) },
        Labels =
        {
            ["English"] = new RouterValue(7),
            ["HighPrioritySupport"] = new RouterValue(true),
            ["HardwareSupport"] = new RouterValue(true),
            ["Support_XBOX_SERIES_X"] = new RouterValue(true),
            ["ChatSupport"] = new RouterValue(true),
            ["XboxSupport"] = new RouterValue(true)
        }
    });

// Create Job
var job = await client.CreateJobAsync(
    new CreateJobOptions(jobId: "job1", channelId: "Xbox_Chat_Channel", queueId: queue.Value.Id)
    {
        Priority = 100,
        ChannelReference = "ChatChannel",
        RequestedWorkerSelectors =
        {
            new RouterWorkerSelector(key: "English", labelOperator: LabelOperator.GreaterThanEqual, value: new RouterValue(7)),
            new RouterWorkerSelector(key: "ChatSupport", labelOperator: LabelOperator.Equal, value: new RouterValue(true)),
            new RouterWorkerSelector(key: "XboxSupport", labelOperator: LabelOperator.Equal, value: new RouterValue(true))
        },
        Labels =
        {
            ["CommunicationType"] = new RouterValue("Chat"),
            ["IssueType"] = new RouterValue("XboxSupport"),
            ["Language"] = new RouterValue("en"),
            ["HighPriority"] = new RouterValue(true),
            ["SubIssueType"] = new RouterValue("ConsoleMalfunction"),
            ["ConsoleType"] = new RouterValue("XBOX_SERIES_X"),
            ["Model"] = new RouterValue("XBOX_SERIES_X_1TB")
        }
    });

// Wait a few seconds and see which worker was matched
await Task.Delay(TimeSpan.FromSeconds(5));
var getJob = await client.GetJobAsync(job.Value.Id);
Console.WriteLine(getJob.Value.Assignments.Select(assignment => assignment.Value.WorkerId).First());

出力

Worker_1 // or Worker_2

Since both workers, Worker_1 and Worker_2, get the same score of 200,
the worker who has been idle the longest will get the first offer.