赞
踩
webman是一款基于workerman开发的高性能HTTP服务框架。webman用于替代传统的php-fpm架构,提供超高性能可扩展的HTTP服务。你可以用webman开发网站,也可以开发HTTP接口或者微服务。
除此之外,webman还支持自定义进程,可以做workerman能做的任何事情,例如websocket服务、物联网、游戏、TCP服务、UDP服务、unix socket服务等等。
首先,小编不会推荐看到的朋友使用,不是因为它不好,而是因为有更多的选择.
一段很普通的代码逻辑:更新订单退款状态字段+新增退款申请记录.然后在一个事务里就行原子操作.
- return Db::transaction(function () use ($order, $explain) {
- $order->refund_status = OrderEnum::ORDER_REFUND_STATUS_ING;
- $order->save();
- $orderRefundModel = new OrderRefund();
- $orderRefundModel->refund_id = 'R' . date('YmdHis') . random_int(10000, 99999);
- $orderRefundModel->order_id = $order->order_id;
- $orderRefundModel->status = OrderEnum::ORDER_REFUND_APPLY_STATUS_ING;
- $orderRefundModel->refund_price = $order->paid_amount;
- $orderRefundModel->refund_num = 1;
- $orderRefundModel->refund_phone = $order->user_mobile;
- $orderRefundModel->refund_explain = $explain;
- $orderRefundModel->save();
-
- return $orderRefundModel->id;
- });
这样的代码在我们的laravel项目上是一点问题没有,但是在webman框架使用却出现了事务回滚失败的问题.
其实这个问题还是因为小编没有认真阅读官方文档导致的,而上面的代码导致了一个什么错误让回滚失效的呢?
当事务中操作模型时,特别需要注意模型是否设置了连接。如果模型设置了连接开启事务的时候要指定连接,否则事务无效(think-orm类似)。例如
- <?php
-
- namespace app\model;
- use support\Model;
-
- class User extends Model
- {
-
- // 这里给模型指定了连接
- protected $connection = 'mysql';
-
- protected $table = 'users';
-
- protected $primaryKey = 'id';
-
- }
当模型指定了连接时,开启事务、提交事务、回滚事务必须指定连接
- Db::connection('mysql')->beginTransaction();
- try {
- // 业务处理
- $user = new User;
- $user->name = 'webman';
- $user->save();
- Db::connection('mysql')->commit();
- } catch (\Throwable $exception) {
- Db::connection('mysql')->rollBack();
- }
通过代码是无法发现问题的,小编通过打开mysql 查询日志general_log来观察执行的sql
打开查询日志配置
- [mysqld]
- general_log = ON
- # 这个日志文档路径必须要有权限写入,否则general_log打开失败
- general_log_file = /path/to/your/logfile.log
-
-
- SHOW VARIABLES LIKE 'general_log%';
将 general_log 设置为 ON表示启用查询日志,general_log_file 指定了日志文件的路径,重启 MySQL 服务器:在修改了配置文件后,需要重启 MySQL 服务器以使更改生效。查看日志文件:启用查询日志后,MySQL 会将所有执行过的 SQL 语句记录到指定的日志文件中。您可以查看该文件以获取所有 SQL 语句以及它们对应的连接信息。
查询日志
从上面的日志我们可以看到,事务和业务sql对应的线程ID是不一样的,那么这就是事务回滚两个业务sql不启用的原因了
为啥是不同的线程ID呢
线程ID不一样,肯定是两边分别创建了各自的连接,那我们就开始排查.通过查找了,我发现了webman admin 插件所有的model 都继承了Base类,而Base类指定了连接名,这下就破案了,也对应了官方的那句话:当模型指定了连接时,开启事务、提交事务、回滚事务必须指定连接
因此我们正确的代码:
Db::connection('plugin.admin.mysql')->transaction(...)
再看看连接的源码,默认的连接指向我们配置文件`database.php`的default属性值
DB manager 源码:
-
- /**
- * Get a database connection instance.
- *
- * @param string|null $name
- * @return \Illuminate\Database\Connection
- */
- public function connection($name = null)
- {
- [$database, $type] = $this->parseConnectionName($name);
-
- $name = $name ?: $database;
-
- // If we haven't created this connection, we'll create it based on the config
- // provided in the application. Once we've created the connections we will
- // set the "fetch mode" for PDO which determines the query return types.
- if (! isset($this->connections[$name])) {
- $this->connections[$name] = $this->configure(
- $this->makeConnection($database), $type
- );
- }
-
- return $this->connections[$name];
- }
-
- /**
- * Parse the connection into an array of the name and read / write type.
- *
- * @param string $name
- * @return array
- */
- protected function parseConnectionName($name)
- {
- $name = $name ?: $this->getDefaultConnection();
-
- return Str::endsWith($name, ['::read', '::write'])
- ? explode('::', $name, 2) : [$name, null];
- }
-
- /**
- * Make the database connection instance.
- *
- * @param string $name
- * @return \Illuminate\Database\Connection
- */
- protected function makeConnection($name)
- {
- $config = $this->configuration($name);
-
- // First we will check by the connection name to see if an extension has been
- // registered specifically for that connection. If it has we will call the
- // Closure and pass it the config allowing it to resolve the connection.
- if (isset($this->extensions[$name])) {
- return call_user_func($this->extensions[$name], $config, $name);
- }
-
- // Next we will check to see if an extension has been registered for a driver
- // and will call the Closure if so, which allows us to have a more generic
- // resolver for the drivers themselves which applies to all connections.
- if (isset($this->extensions[$driver = $config['driver']])) {
- return call_user_func($this->extensions[$driver], $config, $name);
- }
-
- return $this->factory->make($config, $name);
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。