swooletw / laravel-swoole

High performance HTTP server based on Swoole. Speed up your Laravel or Lumen applications.
MIT License
4.04k stars 389 forks source link

Any way to share same singletone instance between requests? #395

Open ddzobov opened 4 years ago

ddzobov commented 4 years ago

Hello!

Anyone knows how to share same singletone instance between requests in same thread with laravel AppServiceProvider?

Now i’m saving singletone instance into static class property but for me it is very dirty way.

Arkanius commented 4 years ago

Well, I imagine that you're manually cleaning your singleton instance at every request isn't it? As each request runs as Coroutine I think you could use Channels.

Table could be a way as well imo.

ddzobov commented 3 years ago

I have instance of AMQP connector, i want to use single connection from all requests in current thread.

public function register()
    {
        $this->app->singleton(AMQP::class, function ($app) {
            return new AMQP($host, $port, $username, $password, $exchange, $queue, $queueType, $prefetchCount);
        });

        $this->app->alias(AMQP::class, 'amqp');
    }

I made simple test - in controller i'm returning spl_object_hash of some instances:

return response()->json([
            'response' => 'ok',
            'instances' => collect(['log', 'amqp', 'view'])->mapWithKeys(function($name){
                return [
                    $name => spl_object_hash(app($name))
                ];
            })
        ], 201);

And made 2 requests:

{
    "response": "ok",
    "instances": {
        "log": "000000001e6bf27d0000000054e3eb80",
        "amqp": "000000001e6bf23f0000000054e3eb80",
        "view": "000000001e6bf2010000000054e3eb80"
    }
}
{
    "response": "ok",
    "instances": {
        "log": "000000001e6bf27d0000000054e3eb80",
        "amqp": "000000001e6bf1ff0000000054e3eb80",
        "view": "000000001e6bf1f30000000054e3eb80"
    }
}

As you can see, log instance unchanged between requests, but view and amqp have different signatures.

Now i have only one solution - save instance into static AMQP property and return instance from this property in provider, but this is very dirty way.

Arkanius commented 3 years ago

Well, this looks weird to me. This is exactly the behavior that swoole would provide to you. I did some tests here as well:

Request 1:

"Log: 0000000023b4852d000000001920f5f7"
"View: 0000000023b4839a000000001920f5f7"
"Auth: 0000000023b483d6000000001920f5f7"
"Custom: 0000000023b483d4000000001920f5f7"

Request 2:

"Log: 0000000023b4852d000000001920f5f7"
"View: 0000000023b4839a000000001920f5f7"
"Auth: 0000000023b483d6000000001920f5f7"
"Custom: 0000000023b483d4000000001920f5f7"

Have you setted the SWOOLE_HTTP_MAX_REQUESTS env?

ddzobov commented 3 years ago

I dont see SWOOLE_HTTP_MAX_REQUESTS env

i'm using SWOOLE_MAX_REQUEST=100

When i'm setting instance to static class property, it have same signature at every request. Probably, something wrong with sandbox...

I'm using Lumen in this project, maybe problems only with it? "laravel/lumen-framework": "^7.0",

Arkanius commented 3 years ago

Hum, maybe...

I'll run some tests with lumen to check it

ddzobov commented 3 years ago

any updates here?

Arkanius commented 3 years ago

Not yet, sorry

ddzobov commented 3 years ago

ping

Arkanius commented 3 years ago

Sorry for the late reply. I'm able to reproduce this behavior with Lumen but not with Laravel. I think it's any particularity with Lumen. I'm looking toward it at this moment

Arkanius commented 3 years ago

Hey @albertcht sorry to bother you. Could you try to give us some light here? I reproduced this and it's happening only with lumen. I tried to debug Sandbox but couldn't find anything. I think that it's happening because lumen misses some of laravel kernel's components.

I'm not sure about which place I can find this

albertcht commented 3 years ago

Hi @ddzobov and @Arkanius ,

Well, basically you can share any singleton instances in the same worker. Swoole HTTP server generated several workers for incoming requests according to your settings. Requests dispatched to the same worker will share any global variables including static properties.

In your case, set your worker number to 1 first to make sure all the requests are handled by the same worker process. And make sure your ampq instance is set in pre_resolved in swoole_http.php config. Otherwise all the resolved singleton instances will be cleared every time the request initialized.

You can refer to: https://github.com/swooletw/laravel-swoole/wiki/2.-Swoole-Structure#sandbox-container

ddzobov commented 3 years ago

Hi @ddzobov and @Arkanius ,

Well, basically you can share any singleton instances in the same worker. Swoole HTTP server generated several workers for incoming requests according to your settings. Requests dispatched to the same worker will share any global variables including static properties.

In your case, set your worker number to 1 first to make sure all the requests are handled by the same worker process. And make sure your ampq instance is set in pre_resolved in swoole_http.php config. Otherwise all the resolved singleton instances will be cleared every time the request initialized.

You can refer to: https://github.com/swooletw/laravel-swoole/wiki/2.-Swoole-Structure#sandbox-container

Just repeated this test with SWOOLE_HTTP_WORKER_NUM=1 and SWOOLE_HTTP_REACTOR_NUM=1

'pre_resolved' => [
        'view', 'files', 'session', 'session.store', 'routes',
        'db', 'db.factory', 'cache', 'cache.store', 'config', 'cookie',
        'encrypter', 'hash', 'router', 'translator', 'url', 'log', 'amqp',
    ],
$this->app->singleton(AMQP::class, function ($app) {
            return new AMQP(
                config('amqp.host'),
                config('amqp.port'),
                config('amqp.username'),
                config('amqp.password'),
                config('amqp.exchange'),
                config('amqp.queue'),
                config('amqp.queueType'),
                config('amqp.prefetchCount'),
                config('amqp.publishRetries'),
                config('amqp.connectionTimeout'),
                config('amqp.readWriteTimeout'),
                config('amqp.heartbeat')
            );
        });
return response()->json([
            'response' => 'ok',
            'instances' => collect(['log', 'amqp', 'view'])->mapWithKeys(function($name){
                return [
                    $name => spl_object_hash(app($name))
                ];
            })
        ], 201);
{
    "response": "ok",
    "instances": {
        "log": "00000000622e85b6000000004399d6c2",
        "amqp": "00000000622e80dd000000004399d6c2",
        "view": "00000000622e80c2000000004399d6c2"
    }
}
{
    "response": "ok",
    "instances": {
        "log": "00000000622e85b6000000004399d6c2",
        "amqp": "00000000622e8104000000004399d6c2",
        "view": "00000000622e8109000000004399d6c2"
    }
}
{
    "response": "ok",
    "instances": {
        "log": "00000000622e85b6000000004399d6c2",
        "amqp": "00000000622e814b000000004399d6c2",
        "view": "00000000622e8170000000004399d6c2"
    }
}

As you can see, only log instance have same signature. amqp and view changes every request.

Arkanius commented 3 years ago

Yes, I reproduced your test and it's happening with Lumen. I tested with Laravel and is everything ok. I haven't found any problem with Sadbox, my guess is some problem with Container app binding

ddzobov commented 3 years ago

Any updates here?

Arkanius commented 3 years ago

Sorry, could reproduce but didn't find the bug at lumen yet =/