Эта страница была переведена сообществом и не обязательно является актуальной. Обратитесь к справочной версии.

Эта страница была переведена сообществом и не обязательно является актуальной. Обратитесь к справочной версии.

Эта страница была переведена сообществом и не обязательно является актуальной. Обратитесь к справочной версии.

Спонсор
Расширение workers

Расширение workers

Расширение Workers позволяет вашему расширению FrankenPHP управлять выделенным пулом PHP-потоков для выполнения фоновых задач, обработки асинхронных событий или реализации пользовательских протоколов. Полезно для систем очередей, слушателей событий, планировщиков и т. д.

# Регистрация worker-а

# Статическая регистрация

Если вам не нужно делать Worker-а настраиваемым пользователем (фиксированный путь к скрипту, фиксированное количество потоков), вы можете просто зарегистрировать Worker в функции init().

package myextension

import (
	"github.com/dunglas/frankenphp"
	"github.com/dunglas/frankenphp/caddy"
)

// Глобальный дескриптор для связи с пулом Worker-ов
var worker frankenphp.Workers

func init() {
	// Зарегистрировать Worker при загрузке модуля.
	worker = caddy.RegisterWorkers(
		"my-internal-worker", // Уникальное имя
		"worker.php",         // Путь к скрипту (относительный или абсолютный)
		2,                    // Фиксированное количество потоков
		// Дополнительные хуки жизненного цикла
		frankenphp.WithWorkerOnServerStartup(func() {
			// Глобальная логика настройки...
		}),
	)
}

# В модуле Caddy (настраивается пользователем)

Если вы планируете делиться своим расширением (например, универсальной очередью или слушателем событий), вам следует обернуть его в модуль Caddy. Это позволит пользователям настраивать путь к скрипту и количество потоков через свой Caddyfile. Это требует реализации интерфейса caddy.Provisioner и парсинга Caddyfile (см. пример).

# В чистом Go-приложении (встраивание)

Если вы встраиваете FrankenPHP в стандартное Go-приложение без Caddy, вы можете зарегистрировать Worker-ы расширения, используя frankenphp.WithExtensionWorkers при инициализации опций.

# Взаимодействие с worker-ами

Как только пул Worker-ов активен, вы можете отправлять ему задачи. Это можно сделать внутри нативных функций, экспортированных в PHP, или из любой Go-логики, такой как планировщик cron, слушатель событий (MQTT, Kafka) или любая другая горутина.

# Безголовый режим: SendMessage

Используйте SendMessage для прямой передачи необработанных данных вашему скрипту Worker-а. Это идеально подходит для очередей или простых команд.

Пример: расширение асинхронной очереди

// #include <Zend/zend_types.h>
import "C"
import (
	"context"
	"unsafe"
	"github.com/dunglas/frankenphp"
)

//export_php:function my_queue_push(mixed $data): bool
func my_queue_push(data *C.zval) bool {
	// 1. Убедитесь, что Worker готов
	if worker == nil {
		return false
	}

	// 2. Отправить фоновому Worker-у
	_, err := worker.SendMessage(
		context.Background(), // Стандартный Go-контекст
		unsafe.Pointer(data), // Данные для передачи Worker-у
		nil, // Опциональный http.ResponseWriter
	)

	return err == nil
}

# Эмуляция HTTP: SendRequest

Используйте SendRequest, если ваше расширение должно вызвать PHP-скрипт, который ожидает стандартную веб-среду (заполнение $_SERVER, $_GET и т. д.).

// #include <Zend/zend_types.h>
import "C"
import (
	"net/http"
	"net/http/httptest"
	"unsafe"
	"github.com/dunglas/frankenphp"
)

//export_php:function my_worker_http_request(string $path): string
func my_worker_http_request(path *C.zend_string) unsafe.Pointer {
	// 1. Подготовьте запрос и рекордер
	url := frankenphp.GoString(unsafe.Pointer(path))
	req, _ := http.NewRequest("GET", url, http.NoBody)
	rr := httptest.NewRecorder()

	// 2. Отправить Worker-у
	if err := worker.SendRequest(rr, req); err != nil {
		return nil
	}

	// 3. Вернуть захваченный ответ
	return frankenphp.PHPString(rr.Body.String(), false)
}

# Скрипт worker-а

PHP-скрипт Worker-а выполняется в цикле и может обрабатывать как необработанные сообщения, так и HTTP-запросы.

<?php
// Обрабатывать как необработанные сообщения, так и HTTP-запросы в одном цикле
$handler = function ($payload = null) {
    // Случай 1: Режим сообщений
    if ($payload !== null) {
        return "Received payload: " . $payload;
    }

    // Случай 2: Режим HTTP (заполняются стандартные суперглобальные переменные PHP)
    echo "Hello from page: " . $_SERVER['REQUEST_URI'];
};

while (frankenphp_handle_request($handler)) {
    gc_collect_cycles();
}

# Хуки жизненного цикла

FrankenPHP предоставляет хуки для выполнения Go-кода в определенные моменты жизненного цикла.

Тип хукаИмя опцииПодписьКонтекст и вариант использования
СерверWithWorkerOnServerStartupfunc()Глобальная настройка. Выполняется один раз. Пример: Подключение к NATS/Redis.
СерверWithWorkerOnServerShutdownfunc()Глобальная очистка. Выполняется один раз. Пример: Закрытие общих соединений.
ПотокWithWorkerOnReadyfunc(threadID int)Настройка для каждого потока. Вызывается при запуске потока. Получает ID потока.
ПотокWithWorkerOnShutdownfunc(threadID int)Очистка для каждого потока. Получает ID потока.

# Пример

package myextension

import (
    "fmt"
    "github.com/dunglas/frankenphp"
    frankenphpCaddy "github.com/dunglas/frankenphp/caddy"
)

func init() {
    workerHandle = frankenphpCaddy.RegisterWorkers(
        "my-worker", "worker.php", 2,

        // Запуск сервера (Глобальный)
        frankenphp.WithWorkerOnServerStartup(func() {
            fmt.Println("Extension: Server starting up...")
        }),

        // Поток готов (Для каждого потока)
        // Примечание: Функция принимает целое число, представляющее ID потока
        frankenphp.WithWorkerOnReady(func(id int) {
            fmt.Printf("Extension: Worker thread #%d is ready.\n", id)
        }),
    )
}
Редактировать эту страницу