我们的情况几乎相同。这是我们的方法:
服务提供者
我们有一个名为 BootTenantServiceProvider 的 ServiceProvider,它在正常的 HTTP/控制台请求中引导租户。它期望存在一个名为TENANT_ID 的环境变量。这样,它将加载所有适当的配置并设置特定的租户。
具有 __sleep() 和 __wakeup() 的特征
我们有一个BootsTenant trait,我们将在队列作业中使用它,它看起来像这样:
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
}
}
与 SerializesModel 冲突
Laravel 包含的 SerializesModels trait 提供了自己的 __sleep 和 __wakeup 方法。我还没有完全弄清楚如何让这两个特征一起工作,或者即使有可能。
现在我确保我永远不会在构造函数中提供完整的 Eloquent 模型。您可以在上面的示例作业中看到,我只将 ID 存储为类属性,而不是完整的模型。我有handle() 方法在队列运行时获取模型。那么我根本不需要SerializesModels trait。
使用 queue:listen 代替 --daemon
您需要使用queue:listen 而不是queue:work --daemon 来运行队列工作程序。前者为每个队列作业启动框架,后者将启动的框架保持在内存中。
至少,假设您的租户启动过程需要全新的框架启动,您需要这样做。如果您能够连续启动多个租户,干净地覆盖每个租户的配置,那么您也许可以摆脱 queue:work --daemon 就好了。