Swoole\Table共享内存实现多进程数据共享

作者: 温新

分类: 【高性能PHP】

阅读: 2914

时间: 2021-04-22 16:40:45

由于 PHP 语言不支持多线程,因此 Swoole 使用多进程模式,在多进程模式下存在进程内存隔离,在工作进程内修改 global 全局变量和超全局变量时,在其他进程是无效的。

Swoole中实现多进程间数据共享有三种方法实现:

  • 数据库,如:MySQLMongoDB
  • 缓存服务器,如:RedisMemcache
  • 磁盘文件,多进程并发读写时需要加锁

虽然有这三种方法实现,但会带来新的问题。如多进程同时操作一个文件,以数据库为列,多个进程同时操作一条数据,这样会导致最终结果与所预期的不一致,此时可以通过锁来解决。

Swoole\Table是一个基于共享内存和锁实现的高性能并发数据结构,可用于解决多进程/多线程数据共享和同步加锁问题,有如下优势:

  • 性能强悍,单线程每秒可读写 200 万次;
  • 应用代码无需加锁,Table 内置行锁自旋锁,所有操作均是多线程 / 多进程安全。用户层完全不需要考虑数据同步问题;
  • 支持多进程,Table 可以用于多进程之间共享数据;
  • 使用行锁,而不是全局锁,仅当 2 个进程在同一 CPU 时间,并发读取同一条数据才会进行发生抢锁。

通过Swoole\Table实现小案例

<?php


// 初始化一个内存容量为1024的内存表
$table = new \Swoole\Table(1024);

// 内存表中新增 id 列
$table->column('id',\Swoole\Table::TYPE_INT);
// 内存表中新增 name 列
$table->column('name', \Swoole\Table::TYPE_STRING, 10);
// 内存表中新增中 score 列
$table->column('score', \Swoole\Table::TYPE_FLOAT);

// 创建内存表
$table->create();


// 设置数据
$table->set('stu-1',['id'=>1, 'name'=>'lisi', 'score'=>60]);
$table->set('stu-2',['id'=>2, 'name'=>'wangwu','score'=>90 ]);

// 若key存在则打印其值
if ($table->exist('stu-1')) {
    echo "stu-" . $table->get('stu-1', 'id') . ':' . $table->get('stu-1', 'name').":".
        $table->get('stu-1', 'score') . "\n";
}

// 自增
$table->incr('stu-2', 'score', 5);
// 自减
$table->decr('stu-2', 'score', 5);


// 获取表中总数量
$count = $table->count();

// 删除指定表
$table->del('stu-1');

Laravel8中使用Swoole\Table

如何在Laravel8中实现Swoole\Table,这里还是基于先前安装的LaravelS作为案例。

本篇文章基于 WebSocket实现,沿用之前的代码。若还没有配置WebSocket请参考 Laravel8使用laravel-s实现WebSocket服务器

第一步:配置laravels.php

文件位置:config/laravels.php配置swoole_tables(该配置默认为空数组)

'swoole_tables' => [
    // ws为表名前缀,完整表为wsTable
    'ws'    =>  [
        // 表容量
        'size'  =>  102400,
        // 表字段,字段名为value
        'column'    =>  [
            ['name'=>'value', 'type'=>\Swoole\Table::TYPE_INT, 'size'=>8]
        ]
    ]
    // 可以配置多个表
],

第二步:修改WebSocketService.php

文件位置:app/Services/WebSocketService.php

只修改 onOpen与onMessage方法,没有变动的使用省略号替代

<?php
   
// *** 省略命名空间
    
class WebSocketService implements WebSocketHandlerInterface
{
  // *** 
   
   public function onOpen(Server $server, Request $request)
   {
       Log::info('WebSocket 建立连接' . $request->fd);
       // 设置表中的数据
       app('swoole')->wsTable->set('fd:' . $request->fd, ['value' => $request->fd]);
       // 发送给客户端的数据
       $server->push($request->fd, 'Welcome to LaravelS,I am WebSocket');
   }
    
   public function onMessage(Server $server, Frame $frame)
   {
       // 遍历表中的数据
        foreach (app('swoole')->wsTable as $key => $row) {
            if (strpos($key, 'fd:') === 0 && $server->exist($row['value'])) {
                Log::info('Receive message from client: ' . $row['value']);
                // 调用 push 方法向客户端推送数据
                $server->push($frame->fd, 'This is a message sent from WebSocket Server at ' . date('Y-m-d H:i:s'));
            }
        }
   }
    
  // ***
}

第三步:运行html文件

我这里将html文件放在了windows上

第四步:查看laravel日志文件

tail -f laravel.log 
    
[2021-04-22 16:36:29] local.INFO: WebSocket 建立连接1  
[2021-04-22 16:36:33] local.INFO: Receive message from client: 1  
[2021-04-22 16:36:41] local.INFO: Receive message from client: 1 

如果看到日志信息,那么此案例就完成了。

2021-04-23

请登录后再评论