top-think / think-swoole

Swoole extend for thinkphp
Apache License 2.0
462 stars 105 forks source link

> @he426100 多个协程同时用model去查询 或者插入数据就会报错 提醒不同协程不能用同一个连接了。你是怎么解决的呢?用扩展里面的Db类吗。 因为我很多东西在model里面要完成。。。所以不能用Db类的连接池 #355

Closed stonegithubs closed 1 year ago

stonegithubs commented 1 year ago
          > @he426100 多个协程同时用model去查询 或者插入数据就会报错 提醒不同协程不能用同一个连接了。你是怎么解决的呢?用扩展里面的Db类吗。 因为我很多东西在model里面要完成。。。所以不能用Db类的连接池
<?php

declare(strict_types=1);

namespace app\command;

use app\model\Test as TestModel;
use think\facade\Db;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\swoole\command\Swoole;
use Swoole\Coroutine\WaitGroup;
use Swoole\Coroutine\Channel;
use function Swoole\Coroutine\go;

class Test extends Swoole
{
    protected function configure()
    {
        // 指令配置
        $this->setName('swoole:test')
            ->addOption(
                'env',
                'E',
                Option::VALUE_REQUIRED,
                'Environment name',
                ''
            )
            ->setDescription('the swoole test command');
    }

    protected function runInSwoole(Input $input, Output $output)
    {
        TestModel::find(1)->save(['val' => 0]);
        $wg = new WaitGroup();
        // 用channel控制并发数量,config/swoole里面配置'max_active' => 64,
        $chan = new Channel(64);
        for ($c = 10000; $c--;) {
            $wg->add();
            $chan->push(true);
            go(function () use ($wg, $chan) {
                Db::transaction(function () use ($wg, $chan) {
                    try {
                        $t = TestModel::lock(true)->find(1);
                        $t->save(['val' => $t['val'] + 1]);
                        echo TestModel::find(1)['val'], PHP_EOL;
                    } catch (\Throwable $e) {
                        echo 'error: ' . $e->getMessage(), PHP_EOL;
                    } finally {
                        $chan->pop();
                        $wg->done();
                    }
                });
            });
        }
        $wg->wait();
        echo TestModel::find(1)['val'], PHP_EOL;
        echo 'ok', PHP_EOL;
    }
}

Originally posted by @he426100 in https://github.com/top-think/think-swoole/issues/331#issuecomment-1382635656 在think-swoole中新增CommandManager.php,SwooleCommand.php,在command目录添加以上代码测试会遇到以下问题

环境:php8.2.8,Ubuntu20.04,thinkphp8.0.2,think-swoole4.0.11

/www/tp8/vendor/think-swoole/src/ipc/driver/UnixSocket.php on line 41
PHP Fatal error:  Uncaught Error: Call to a member function getProcess() on null in /www/tp8/vendor/topthink/think-swoole/src/ipc/driver/UnixSocket.php:41
Stack trace:
#0 /www/tp8/vendor/topthink/think-swoole/src/ipc/driver/UnixSocket.php(22): think\swoole\ipc\driver\UnixSocket->getSocket()
#1 /www/tp8/vendor/topthink/think-swoole/src/ipc/Driver.php(27): think\swoole\ipc\driver\UnixSocket->subscribe()
#2 /www/tp8/vendor/topthink/framework/src/think/Manager.php(171): think\swoole\ipc\Driver->listenMessage()
#3 /www/tp8/vendor/topthink/think-swoole/src/concerns/InteractsWithServer.php(92): think\Manager->__call()
#4 [internal function]: think\swoole\CommandManager->think\swoole\concerns\{closure}()
#5 {main}
  thrown in /www/tp8/vendor/topthink/think-swoole/src/ipc/driver/UnixSocket.php on line 41
he426100 commented 1 year ago

给个完整的代码,有空我试试

stonegithubs commented 1 year ago

给个完整的代码,有空我试试 在协程代码中只有Db更新操作

<?php

declare(strict_types=1);

namespace app\command;

use think\facade\Db;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\swoole\command\Swoole;
use Swoole\Coroutine\WaitGroup;
use Swoole\Coroutine\Channel;
use function Swoole\Coroutine\go;

class Test extends Swoole
{
    protected function configure()
    {
        // 指令配置
        $this->setName('swoole:test')
            ->addOption(
                'env',
                'E',
                Option::VALUE_REQUIRED,
                'Environment name',
                ''
            )
            ->setDescription('the swoole test command');
    }

    protected function runInSwoole(Input $input, Output $output)
    {
        $wg = new WaitGroup();
        // 用channel控制并发数量,config/swoole里面配置'max_active' => 64,
        $chan = new Channel(64);
        for ($c = 10000; $c--;) {
            $wg->add();
            $chan->push(true);
            go(function () use ($wg, $chan) {
                Db::transaction(function () use ($wg, $chan) {
                    try {
                        Db::name('product')->where('id',1)->update(['name' => 'abc']);
                    } catch (\Throwable $e) {
                        echo 'error: ' . $e->getMessage(), PHP_EOL;
                    } finally {
                        $chan->pop();
                        $wg->done();
                    }
                });
            });
        }
        $wg->wait();
        echo 'ok', PHP_EOL;
    }
}
stonegithubs commented 1 year ago

给个完整的代码,有空我试试

非常感谢你提供的办法,之前批量操作数据,都是一条一条更新,用上你给的代码可以多个协程,速度快多了