webman独立进程后台程序连接数据库有问题

kfcabc

问题描述

有个常驻进程 监听rabbitmq队列,如果这个队列有信息进来就把这个信息保存到一个数据库表中,自打上线后发现个规律只能运行一小段儿时间正常,过一段时间后就无法保存数据库了,日志报异常
SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
应该是数据库连接丢失了,理论上model应该有自动重连的机制,这个需要特殊配置吗?还是说有其他解决方案?

程序代码

Model::create(['id'=>'123']); // 主要就这样保存的

报错信息

SQLSTATE[HY000]: General error: 2006 MySQL server has gone away

操作系统及workerman/webman等框架组件具体版本

 "workerman/webman-framework": "~2.1",
   "workerman/workerman": "~5.1",
573 7 1
7个回答

walkor 打赏

SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
这个报错的调用栈发下

  • kfcabc 2025-02-14

    不好意思,因为重启 日志被清空了,我这边再复现 给你发,先谢过

kfcabc
岗亭同步数据业务数据处理异常 {"message":"SQLSTATE[HY000]: General error: 2006 MySQL server has gone away (Connection: mysql, SQL: select * from `vehicle_passage_tickets` where `is_entry` = 1 and `status` <> void and (`available_start_datetime` between 2025-02-13 00:00:00 and 2025-02-13 23:59:59 or `available_end_datetime` between 2025-02-13 00:00:00 and 2025-02-13 23:59:59 or (`available_start_datetime` <= 2025-02-13 00:00:00 and `available_end_datetime` >= 2025-02-13 23:59:59 and `status` = pending_use)))","trace":"#0 /var/www/backend-service/vendor/illuminate/database/Connection.php(979): Illuminate\\Database\\Connection->runQueryCallback()
#1 /var/www/backend-service/vendor/illuminate/database/Connection.php(958): Illuminate\\Database\\Connection->tryAgainIfCausedByLostConnection()
#2 /var/www/backend-service/vendor/illuminate/database/Connection.php(781): Illuminate\\Database\\Connection->handleQueryException()
#3 /var/www/backend-service/vendor/illuminate/database/Connection.php(398): Illuminate\\Database\\Connection->run()
#4 /var/www/backend-service/vendor/illuminate/database/Query/Builder.php(3133): Illuminate\\Database\\Connection->select()
#5 /var/www/backend-service/vendor/illuminate/database/Query/Builder.php(3118): Illuminate\\Database\\Query\\Builder->runSelect()
#6 /var/www/backend-service/vendor/illuminate/database/Query/Builder.php(3706): Illuminate\\Database\\Query\\Builder->Illuminate\\Database\\Query\\{closure}()
#7 /var/www/backend-service/vendor/illuminate/database/Query/Builder.php(3117): Illuminate\\Database\\Query\\Builder->onceWithColumns()
#8 /var/www/backend-service/app/service/TollGateSyncDataService.php(97): Illuminate\\Database\\Query\\Builder->get()
#9 /var/www/backend-service/app/service/TollGateSyncDataService.php(47): app\\service\\TollGateSyncDataService->getAvailableTickets()
#10 /var/www/backend-service/app/process/TollGateDataMonitor.php(202): app\\service\\TollGateSyncDataService->getAvailableTollGateData()
#11 /var/www/backend-service/app/process/TollGateDataMonitor.php(170): app\\process\\TollGateDataMonitor->downloadTollGateData()
#12 /var/www/backend-service/app/process/TollGateDataMonitor.php(77): app\\process\\TollGateDataMonitor->processMessage()
#13 [internal function]: app\\process\\TollGateDataMonitor->app\\process\\{closure}()
#14 /var/www/backend-service/vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AMQPChannel.php(1063): call_user_func()
#15 [internal function]: PhpAmqpLib\\Channel\\AMQPChannel->basic_deliver()
#16 /var/www/backend-service/vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AbstractChannel.php(221): call_user_func()
#17 /var/www/backend-service/vendor/php-amqplib/php-amqplib/PhpAmqpLib/Channel/AbstractChannel.php(367): PhpAmqpLib\\Channel\\AbstractChannel->dispatch()
#18 /var/www/backend-service/app/process/TollGateDataMonitor.php(130): PhpAmqpLib\\Channel\\AbstractChannel->wait()
#19 /var/www/backend-service/app/process/TollGateDataMonitor.php(59): app\\process\\TollGateDataMonitor->startMonitor()
#20 /var/www/backend-service/vendor/workerman/webman-framework/src/support/helpers.php(512): app\\process\\TollGateDataMonitor->onWorkerStart()
#21 /var/www/backend-service/vendor/workerman/webman-framework/src/support/helpers.php(558): worker_bind()
#22 /var/www/backend-service/vendor/workerman/workerman/src/Worker.php(2571): {closure}()
#23 [internal function]: Workerman\\Worker->Workerman\\{closure}()
#24 /var/www/backend-service/vendor/workerman/workerman/src/Worker.php(2586): Fiber->start()
#25 /var/www/backend-service/vendor/workerman/workerman/src/Worker.php(1740): Workerman\\Worker->run()
#26 /var/www/backend-service/vendor/workerman/workerman/src/Worker.php(1544): Workerman\\Worker::forkOneWorkerForLinux()
#27 /var/www/backend-service/vendor/workerman/workerman/src/Worker.php(1524): Workerman\\Worker::forkWorkersForLinux()
#28 /var/www/backend-service/vendor/workerman/workerman/src/Worker.php(593): Workerman\\Worker::forkWorkers()
#29 /var/www/backend-service/vendor/workerman/webman-framework/src/support/App.php(143): Workerman\\Worker::runAll()
#30 /var/www/backend-service/start.php(5): support\\App::run()
#31 {main}","data":"{\"cmd\":\"get_toll_gate_all_data\",\"toll_gate_code\":\"1001\",\"sync_datetime\":\"2025-02-14 10:57:20\",\"id\":\"oxcs5vnp1r\"}"} []
  • walkor 2025-02-14

    php-amqplib 是一个阻塞组件,阻塞过程中webman内核无法拿到进程控制权,无法发送心跳保持数据库连接。

  • kfcabc 2025-02-14

    哦哦 大概明白了,那如果是这种需求,需要使用 php-amqplib 这个类库连接rabbitmq,配合数据库的业务该如何做呢?

  • walkor 2025-02-14

    最简单的方法,数据库关闭连接的设置改长一点

  • kfcabc 2025-02-14

    这个脚本是常年运行的,即便是改长应该是 还有断开的时间 对吧?

  • walkor 2025-02-14

    它是连接空闲一定时间后断开的,保证这期间有处理业务就可以。
    或者你装下swoole扩展,进程开启swoole协程试下。
    也有开发者尝试用定时器去wait(), 参考https://www.workerman.net/q/8688

  • kfcabc 2025-02-14

    好的 谢谢 我看一下 非常感谢

kfcabc

回来报告一下消息:

按照walkor老大指引的开启了协程的方式,数据库开启了连接池,经过一晚的时间测试 还是没能解决问题:

SQLSTATE[HY000]: General error: 2006 MySQL server has gone away

配置如下:

return [
    'default'     => 'mysql',
    'connections' => [
        'mysql' => [
            'driver'      => 'mysql',
            'host'        => env('DB_HOST'),
            'port'        => env('DB_PORT'),
            'database'    => env('DB_NAME'),
            'username'    => env('DB_USER'),
            'password'    => env('DB_PASSWORD'),
            'charset'     => env('DB_CHARSET'),
            'collation'   => env('DB_COLLATION'),
            'unix_socket' => '',
            'prefix'      => '',
            'strict'      => true,
            'engine'      => null,
            'options'     => [
                PDO::ATTR_EMULATE_PREPARES => false,
            ],
            'pool'        => [ // 连接池配置,仅支持swoole/swow驱动
                               'max_connections'    => (int)env('DB_POOL_MAX_CONNECTIONS'), // 最大连接数
                               'min_connections'    => (int)env('DB_POOL_MIN_CONNECTIONS'), // 最小连接数
                               'wait_timeout'       => 3, // 从连接池获取连接等待的最大时间,超时后会抛出异常
                               'idle_timeout'       => 180, // 连接池中连接最大空闲时间,超时后会关闭回收,直到连接数为min_connections
                               'heartbeat_interval' => 60, // 连接池心跳检测时间,单位秒,不要小于60
            ],
        ],
    ],
];

监听的进程配置如下:

'test_pusher'                    => [
        'handler'   => TestPusher::class,
        'count'     => 1,
        'eventLoop' => \Workerman\Events\Swoole::class,
    ],
  • 暂无评论
walkor 打赏

不用swoole了。
更新下 composer require webman/database:^2.1.3 ,增加了重连。

  • kfcabc 2025-02-17

    感谢 大佬~

  • kfcabc 2025-02-17

    问一下,这个新版是否兼容连接池的方式?因为我已经将rabbitmq 协程化加入了 连接池了,如果不用swoole的话得改回来~如果我更新到这个版本,是不是 不能兼容swoole的模式了?

  • walkor 2025-02-17

    兼容

  • kfcabc 2025-02-17

    好的 感谢

kfcabc

回来再报个信息,更新后今天大部分都正常,但是收到一个错误:

PDO::prepare(): Send of 132 bytes failed with errno=110 Connection timed out (Connection: mysql, SQL: insert into push_test (push_url, request_data, push_times, id, updated_at,
  • 暂无评论
kfcabc

回来再报问题,经过这几天的测试发现上面提到的问题还是依然存在,大概现象就是,这个进程大概1天半的时间没有与数据库通信是静默状态没有数据推送过来,等到再次有数据推送过来的时候一定会报错:
"error":"PDO::prepare(): Send of 132 bytes failed with errno=110 Connection timed out
如果接下来继续有数据推送程序就正常处理,如果再次隔很久才有数据推送过来处理入库还是如上的报错。

  • walkor 2025-02-20

    Connection timed out 和 MySQL server has gone away 是不同的错误类型了。一个是发送数据超时,一个是连接断开。
    你们用的什么数据库?云数据库?

  • kfcabc 2025-02-20

    不是云数据,服务器自己架设的mysql8.0,2点高可用

  • walkor 2025-02-20

    可能是你们系统有什么防火墙之类的把不活跃连接清理了,导致超时了。

    刚看了下代码,PhpAmqpLib的wait()有超时参数,你们设置个超时,比如50秒。
    超时后执行个SQL,这样和数据库定时通讯下,维持下连接。

  • kfcabc 2025-02-20

    好的,谢谢,我研究一下,就是 PhpAmqpLib的wait() 的时候有个捕获异常? 然后在执行sql?

kfcabc

求问,经过这几天的测试,还是这个问题,这种 timeout能否有什么方法手动去连接数据库呢?

PDO::prepare(): Send of 132 bytes failed with errno=110 Connection timed out

  • walkor 2025-02-22

    如果50秒和mysql通讯一次,还有 Connection timed out ,这个应该不是程序问题了。

  • kfcabc 2025-02-22

    50秒通讯一次该如何实现呢? 因为理论上mq的进程是阻塞试的,一单这个进程启动他就一直在 wait的状态了,这个状态下如何尝试50秒与数据库通讯一次?

    另外我有个不明白的地方 理论上我启用的swoole模式下的连接池配置,理论上连接池会有心跳维持,当有数据库操作的时候不会出现连接timeout的状态因为连接池理论上是肯定有个min可用连接的,对吧?难道是连接池在这种自定义进程的模式下是不起作用的吗?

  • walkor 2025-02-22

    50秒通讯一次就是上面说的利用wait()的超时参数,超时后调用一次数据库。
    连接从连接池里获取后没归还前不会调用心跳,连接没归还说明连接正在被使用,可能正在等待数据,发出心跳查询可能导致数据错乱,swoole和swow下会报错。

  • kfcabc 2025-02-22

    好的 明白了,谢谢

×
🔝