Один работник общей очереди в приложении Laravel multi-tenant
Я создаю многопользовательское приложение Laravel (на Laravel 5.3), которое позволяет каждому арендатору иметь свой собственный набор конфигураций для любых поддерживаемых настроек Laravel. В настоящее время это достигается путем переопределения значения по умолчанию Laravel Application
с моей собственной реализацией, которая предоставляет пользовательский загрузчик конфигурации (переопределяет значение по умолчанию IlluminateFoundationBootstrapLoadConfiguration
). Приложение обнаруживает текущего арендатора из среды (либо PHP $_ENV
или .env
file) при загрузке, а затем загружает соответствующие файлы конфигурации для обнаруженного клиента.
вышеуказанный подход отлично работает как для HTTP, так и для консольных ядер, где каждый запрос/команда имеет ограниченный жизненный цикл, но я не уверен, как подойти к работнику очереди. Я хотел бы иметь одного работника очереди для всех арендаторов, и я уже реализовал пользовательский соединитель очереди для добавления дополнительных метаданных при планировании задания очереди, чтобы можно было идентифицировать арендатора, когда работник получает он.
часть, на которой я ищу вашу помощь, - это как запустить каждое задание очереди в изолированной среде, которую я могу загрузить с помощью своей пользовательской конфигурации.
несколько возможных решений, которые я вижу такой:
для запуска пользовательского работника очереди, который работает как демон и получает задание из очереди, но выполняет задание в отдельном процессе PHP (созданном через
exec()
); после выполнения задания работник собирает результаты (статус, исключения и т. д.) и завершает задание в Родительском процессе (например, удаляет задание и т. д.)аналогично приведенному выше, но запустите задание в отдельном потоке PHP вместо отдельного процесса, используя
RunKit Sandbox
реализовать решение, которое "перезагружает" приложение после получения задания очереди (например, перезагружает конфигурации для текущего арендатора, сбрасывает все разрешенные зависимости и т. д.)
что важно я бы хотел, чтобы это выполнение задания с несколькими арендаторами было прозрачным для самого задания, чтобы задания, которые не предназначены для работы в среде с несколькими арендаторами (например, задания из сторонних пакетов, таких как Laravel Scout), можно было обрабатывать без каких-либо изменений.
какие-либо предложения о том, как подойти к этому?
1 ответов
у нас почти такая же ситуация. Вот наш подход:
Провайдер
у нас есть ServiceProvider называется BootTenantServiceProvider
это загружает арендатора в обычный запрос HTTP / Console. Он ожидает, что переменная среды будет существовать с именем TENANT_ID
. При этом он загрузит все соответствующие конфигурации и настроит определенный клиент.
черта с _ _ sleep () и __wakeup ()
у нас есть BootsTenant
признак, который мы будем использовать в нашем очередь заданий, выглядит так:
trait BootsTenant
{
protected $tenantId;
/**
* Prepare the instance for serialization.
*
* @return array
*/
public function __sleep()
{
$this->tenantId = env('TENANT_ID');
return array_keys(get_object_vars($this));
}
/**
* Restore the ENV, and run the service provider
*/
public function __wakeup()
{
// We need to set the TENANT_ID env, and also force the BootTenantServiceProvider again
\Dotenv::makeMutable();
\Dotenv::setEnvironmentVariable('TENANT_ID', this->tenantId);
app()->register(BootTenantServiceProvider::class, [], true);
}
}
теперь мы можем написать задание очереди, которое использует эту черту. Когда задание сериализуется в очереди,__sleep()
метод будет хранить tenantId локально. Когда необходимо выполнить десериализацию в __wakeup()
метод восстановит переменную среды и запустит поставщика услуг.
очереди рабочих мест
наши задания очереди просто должны использовать эту черту:
class MyJob implements SelfHandling, ShouldQueue {
use BootsTenant;
protected $userId;
public function __construct($userId)
{
$this->userId = $userId;
}
public function handle()
{
// At this point the job has been unserialized from the queue,
// the trait __wakeup() method has restored the TENANT_ID
// and the service provider has set us all up!
$user = User::find($this->userId);
// Do something with $user
}
}
конфликт с SerializesModels
в SerializesModels
черта, которую включает Laravel, обеспечивает свою собственную __sleep
и __wakeup
методы. Я не совсем понял, как сделать, как черт, работать вместе, или даже если это возможно.
пока я уверен, что никогда не предоставляю полную красноречивую модель в конструкторе. Вы можете видеть в моем примере задания выше я храню только идентификаторы как атрибуты класса, никогда не полные модели. У меня есть handle()
метод выборки моделей во время выполнения очереди. Тогда мне не нужно SerializesModels
черта в все.
использовать очередь: слушайте вместо --daemon
вам нужно запустить своих работников очереди, используя queue:listen
вместо queue:work --daemon
. Первый загружает фреймворк для каждого задания очереди, последний сохраняет загруженную фреймворк в памяти.
по крайней мере, вам нужно сделать это, предполагая, что ваш процесс загрузки клиента нуждается в новой загрузке фреймворка. Если вы можете загружать несколько арендаторов последовательно, чисто перезаписывая конфигурации для каждого, то вы можете уходи с queue:work --daemon
просто отлично.