INTEGRITY Документация

Динамический dispatch Worker

A Worker динамической диспетчеризации это специализированный маршрутизирующий Worker, который направляет входящие запросы к соответствующим пользовательским Workers в вашем пространстве имён диспетчеризации. Вместо использования Маршруты Workers, dispatch Workers позволяют программно управлять маршрутизацией запросов с помощью кода.

Рисунок 1: Workers for Platforms: основной поток

Зачем использовать dynamic dispatch Worker?

Настройте привязку dispatch namespace

Чтобы ваш динамический dispatch Worker мог динамически направлять запросы к Workers в пространстве имён, нужно настроить dispatch namespace привязка. Этот binding позволяет вашему dynamic dispatch Worker обращаться к любому user Worker в этом пространстве имён с помощью env.dispatcher.get().

{
	"dispatch_namespaces": [
		{
			"binding": "DISPATCHER",
			"namespace": "my-dispatch-namespace"
		}
	]
}
[[dispatch_namespaces]]
binding = "DISPATCHER"
namespace = "my-dispatch-namespace"

Как только привязка будет настроена, ваш dynamic dispatch Worker сможет направлять запросы к любому Worker в пространстве имён. Ниже приведены распространённые шаблоны маршрутизации, которые можно реализовать в своём диспетчере.

Примеры маршрутизации

Рисунок 2: Workers for Platforms: основной поток

Маршрутизация на основе KV

Сохраняйте сопоставления маршрутизации в Workers KV. Благодаря этому вы можете изменять логику маршрутизации, не меняя и не разворачивая заново dynamic dispatch Worker.

export default {
	async fetch(request, env) {
		try {
			const url = new URL(request.url);

			// Use hostname, path, or any combination as the routing key
			const routingKey = url.hostname;

			// Lookup user Worker name from KV store
			const userWorkerName = await env.USER_ROUTING.get(routingKey);

			if (!userWorkerName) {
				return new Response("Route not configured", { status: 404 });
			}

			// Optional: Cache the KV lookup result
			const userWorker = env.DISPATCHER.get(userWorkerName);
			return await userWorker.fetch(request);
		} catch (e) {
			if (e.message.startsWith("Worker not found")) {
				return new Response("", { status: 404 });
			}
			return new Response(e.message, { status: 500 });
		}
	},
};

Маршрутизация на основе поддоменов

Направляйте поддомены на соответствующий Worker. Например, my-customer.example.com будет направляться на Worker с именем my-customer в пространстве имён диспетчеризации.

export default {
	async fetch(request, env) {
		try {
			// Extract user Worker name from subdomain
			// Example: customer1.example.com -> customer1
			const url = new URL(request.url);
			const userWorkerName = url.hostname.split(".")[0];

			// Get user Worker from dispatch namespace
			const userWorker = env.DISPATCHER.get(userWorkerName);
			return await userWorker.fetch(request);
		} catch (e) {
			if (e.message.startsWith("Worker not found")) {
				// User Worker doesn't exist in dispatch namespace
				return new Response("", { status: 404 });
			}
			// Could be any other exception from fetch() or from the dispatched Worker
			return new Response(e.message, { status: 500 });
		}
	},
};

Маршрутизация на основе пути

Направляйте пути URL на соответствующий Worker. Например, example.com/customer-1 будет направляться на Worker с именем customer-1 в пространстве имён диспетчеризации.

export default {
	async fetch(request, env) {
		try {
			const url = new URL(request.url);
			const pathParts = url.pathname.split("/").filter(Boolean);

			if (pathParts.length === 0) {
				return new Response("Invalid path", { status: 400 });
			}

			// example.com/customer-1 -> routes to 'customer-1' worker
			const userWorkerName = pathParts[0];

			const userWorker = env.DISPATCHER.get(userWorkerName);
			return await userWorker.fetch(request);
		} catch (e) {
			if (e.message.startsWith("Worker not found")) {
				return new Response("", { status: 404 });
			}
			return new Response(e.message, { status: 500 });
		}
	},
};

Применяйте пользовательские лимиты

Используйте пользовательские лимиты чтобы ограничить, сколько времени CPU может использовать конкретный пользовательский Worker или сколько subrequests он может выполнить. Разные лимиты можно задавать в зависимости от тарифного плана клиента или других критериев.

export default {
	async fetch(request, env) {
		try {
			const url = new URL(request.url);
			const userWorkerName = url.hostname.split(".")[0];

			// Look up customer plan from your database or KV
			const customerPlan = await env.CUSTOMERS.get(userWorkerName);

			// Set limits based on plan type
			const plans = {
				enterprise: { cpuMs: 50, subRequests: 50 },
				pro: { cpuMs: 20, subRequests: 20 },
				free: { cpuMs: 10, subRequests: 5 },
			};
			const limits = plans[customerPlan] || plans.free;

			const userWorker = env.DISPATCHER.get(userWorkerName, {}, { limits });
			return await userWorker.fetch(request);
		} catch (e) {
			if (e.message.startsWith("Worker not found")) {
				return new Response("", { status: 404 });
			}
			if (e.message.includes("CPU time limit")) {
				// Track limit violations with Analytics Engine
				env.ANALYTICS.writeDataPoint({
					indexes: [userWorkerName],
					blobs: ["cpu_limit_exceeded"],
				});
				return new Response("CPU limit exceeded", { status: 429 });
			}
			return new Response(e.message, { status: 500 });
		}
	},
};

Дополнительные сведения о доступных лимитах см. в Пользовательские лимиты.

Чтобы отслеживать нарушения лимитов и другие метрики по всем user Workers, используйте Workers Analytics Engine. Для подробного логирования и отладки настройте Tail Worker для сбора событий из вашего dispatch Worker.