Swoole\Table共享内存实现多进程数据共享
由于 PHP
语言不支持多线程,因此 Swoole
使用多进程模式,在多进程模式下存在进程内存隔离,在工作进程内修改 global
全局变量和超全局变量时,在其他进程是无效的。
Swoole中实现多进程间数据共享有三种方法实现:
- 数据库,如:
MySQL
、MongoDB
- 缓存服务器,如:
Redis
、Memcache
- 磁盘文件,多进程并发读写时需要加锁
虽然有这三种方法实现,但会带来新的问题。如多进程同时操作一个文件,以数据库为列,多个进程同时操作一条数据,这样会导致最终结果与所预期的不一致,此时可以通过锁来解决。
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
请登录后再评论