Эта страница была переведена сообществом и не обязательно является актуальной. Обратитесь к справочной версии.
Эта страница была переведена сообществом и не обязательно является актуальной. Обратитесь к справочной версии.
Эта страница была переведена сообществом и не обязательно является актуальной. Обратитесь к справочной версии.
Расширение Workers позволяет вашему расширению FrankenPHP управлять выделенным пулом PHP-потоков для выполнения фоновых задач, обработки асинхронных событий или реализации пользовательских протоколов. Полезно для систем очередей, слушателей событий, планировщиков и т. д.
Если вам не нужно делать 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. Это позволит пользователям настраивать путь к скрипту и количество потоков через свой Caddyfile. Это требует реализации интерфейса caddy.Provisioner и парсинга Caddyfile (см. пример).
Если вы встраиваете FrankenPHP в стандартное Go-приложение без Caddy, вы можете зарегистрировать Worker-ы расширения, используя frankenphp.WithExtensionWorkers при инициализации опций.
Как только пул 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
}
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)
}
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-кода в определенные моменты жизненного цикла.
| Тип хука | Имя опции | Подпись | Контекст и вариант использования |
|---|---|---|---|
| Сервер | WithWorkerOnServerStartup | func() | Глобальная настройка. Выполняется один раз. Пример: Подключение к NATS/Redis. |
| Сервер | WithWorkerOnServerShutdown | func() | Глобальная очистка. Выполняется один раз. Пример: Закрытие общих соединений. |
| Поток | WithWorkerOnReady | func(threadID int) | Настройка для каждого потока. Вызывается при запуске потока. Получает ID потока. |
| Поток | WithWorkerOnShutdown | func(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)
}),
)
}