原生PHP进程之间是不能相互通信的,在swoole_process中则是可以的,Swoole中进程之间的通信是通过管道pipe来传递的。多进程、多线程之间的数据共享则是通过内存memory来实现的。
swoole_table共享内存表是一个基于共享内存和锁实现的超高性能并发数据结构,用于解决多进程或多线程数据共享和同步加锁问题,它不消耗服务器 IO,纯内存操作,性能十分强悍。
swoole table优势
共享内存表可以直接操作系统的内存,性能强悍,单线程每秒可读写200万每次,主要用于进程间的数据通信。
共享内存表应用代码无需加锁,内置行锁自旋锁,所有操作都是多进程或多线程安全,用户层完全不需要考虑数据同步问题。
共享内存表支持多进程,内存表可以用于多进程之间共享数据。
共享内存表使用行锁而非全局锁,仅当两个进程在同一CPU时间并发读取同一条数据时才会发生抢锁。
共享内存表不受PHP的memory_limit最大内存的控制
适用场景:用于解决多进程或多线程数据共享和同步加锁问题,进程结束后共享内存表会自动释放。
示例代码
<?php方法解析
use Swoole\Table;
//创建共享内存表对象
$size = 1024;//2的n次方
$table = new swoole_table($size);
//共享内存表添加字段
$table->column("id", Table::TYPE_INT, 11);
$table->column("name", Table::TYPE_STRING, 20);
$table->column("money", Table::TYPE_FLOAT, 10);
$table->create();
//共享内存表添加行
$table->set("alice", ["id"=>1, "name"=>"alice", "money"=>100.5]);
//使用数组方式添加行
$table["ben"] =["id"=>2, "name"=>"ben", "money"=>200.1];
//获取行返回数组
$val = $table->get("alice");
var_dump($val);
//获取行返回对象
$val = $table["ben"];
var_dump($val);
//共享内存表内字段值增加
$table->incr("alice", "money", 100);
var_dump($table["alice"]);
//共享内存表中字段值减少
$table->decr("ben", "money", 50);
var_dump($table["ben"]);
echo json_encode($table).PHP_EOL;
创建对象construct
创建共享内存表function Table->__construct(int $size, float $conflict_propertion = 0.2);
共享内存表实际上是一个开链法实现的哈希表,内存是由哈希键Key与具体数据组成的数组,如果哈希冲突即不同的键值对一个同一个哈希,那么就会从内存池pool中分配出一个元素作为数组元素的链表尾。内存表占用的内存总数 = ( 结构体长度 + 哈希键长度64byte + 行尺寸$size ) * 预留作为哈希冲突的百分比 * 列尺寸
参数1:int $size
$size表示创建共享内存表时设置的最大行数,必须是2的次方,如果不是底层会自动调整为接近的一个数字,若小于1024则默认为1024,即1024为最小值。若机器内存不足内存表会创建失败。
共享内存表底层是建立在共享内存之上的哈希表HashTable(数据结构),最大行数$size决定了哈希表的总行数。由于内存表是在共享内存之上,所以无法动态扩容,因此$size必须在创建前提前设置好。
参数2:float $conflict_propertion 哈希冲突率
如果哈希冲突超过最大比例,共享内存表将不再允许添加新的行元素。共享内存表能存储的总数据行数取决于数据的哈希键冲突率,默认如果冲突率超过20%,预留的哈希hash冲突内存块容量不足,会报Unable to allocate memory无法分配内存的错误,并返回false表示存储失败。
<?php
use Swoole\Table;
//创建共享内存表对象
$size = 1024;//2的n次方
$table = new swoole_table($size);
$table->column("data", Table::TYPE_STRING, 1);
$table->create();
echo json_encode($table).PHP_EOL;// {"size":1024,"memorySize":117548}
添加列数据column
共享内存表新增一列bool Table->column(string $name, int $type, int $size = 0)
swoole_table->column(string $name, int $type, int $size = 0)
参数列表string $name 表示字段的名称
int $type 表示字段类型,可支持3种类型分别是Table::TYPE_INT、Table::TYPE_FLOAT、Table::TYPE_STRING。
int $size 表示字符串字段的最大长度单位字节,字符串类型Table::TYPE_STRING的字段必须指定$size。
字段类型Table::TYPE_INT 整型默认4字节,可设置1、2、4、8共四种长度。
Table::TYPE_STRING 字符型必须指定,设置的字符串不得超过设置值。
Table::TYPE_FLOAT 浮点型,占用8字节的内存。
整型溢出由于Swoole底层使用有符号整型,如果传入的数值超过溢出边界就可能会发生溢出。
整数类型安全值的范围
int8 1byte -127 ~127
int16 2byte -32767~327767
int32 4byte -2147483647 ~ 2147483647
int64 8byte 不会溢出
内存对齐从Swoole4.3版本开始,底层对内存长度做了对齐处理。字符串长度必须是8的整数倍,如果长度为18字节会自动对其到24字节。注意非x86环境则内存对齐。
创建共享内存表create
当定义好表的结构后执行create会向操作系统申请内存并创建共享内存表。需要注意的是,调用create创建之前不能使用set、get等数据读写操作,调用create创建之后不能使用column添加新字段。如果 系统内存不足则会申请失败此时create将返回false,若申请成功create将会返回true。Table使用共享内存来保存数据,在创建子进程之前,务必要执行create执行创建。服务器中使用共享内存表执行create创建操作必须在服务器执行start启动方法之前。
当使用create方法创建共享内存表后,可以读取$table->memorySize属性来获取实际占用的内存尺寸,单位为字节。
function Table->create():bool
设置行数据set
共享内存表使用键值对的方式存取数据Table->set(string $key, array $value):bool
参数列表参数1:string $key
设置行数据对应的键名,相同的键名对应同一行数据,如果set设置了同一个键名则会覆盖上一次的数据。
参数2:array $value
设置行数据对应的键值,必须是一个数组,必须与字段定义的名称$name完全相同。
返回值
设置成功返回true,设置失败返回false,失败的原因可能是由于哈希冲突过多导致动态空间无法分配内存,可以调大构造方法的$conflict_propertion参数。
如果传入字符串的长度超过列定义的最大尺寸,底层会自动截断。
获取行数据get
获取单行数据array Table->get(string $key, string $field = null)
参数列表中string $key表示查询数据行的键名,必须为字符串类型。如果带查询的键名不存在,则get方法将会返回false。若成功则返回的结果为数组类型。
检查键名是否存在exist
exist方法用于检查共享内存表中是否存在某个键名key,若存在则返回true,否则返回false。bool swoole_table->exists(string $key)
条目数量count
count方法用于获取共享内存表table中存在的条目数量int function Table->count()
删除键值对del
bool Table->del(string $keyy)
del方法用于删除共享内存表$table中指定键名$key的行,如果键名$key对应的是数据不存在则返回false,否则成功返回true。此处需要注意的是键名$key是非二进制安全的,所以必须为字符串类型,因此不得传入二进制数据。原子递增incr
function Table->incr(string $key, string $column. mixed $incrby = 1): int
incr表示原子递增操作,参数string $key表示指定数据的键名,如果键名对应的行不存在则默认值为0。int column 表示指定的列表即字段名,仅支持浮点型和整型字段。
int $incrby表示则增量默认为1,若列为整型则增量必须为整型,如果列为浮点型则增量必须为浮点型。
返回最终的结果数值原子自减decr
function Table->decr(string $key, string $column, mixed $decrby = 1) int | bool
参数列表string $key表示指定数据的键名,如果键名对应的行不存在,底层会首先将该行数据初始化为0。
string $column表示指定的列名,仅支持浮点型和整型字段。
mixed $decrby表示减量默认为1,如果列为整型则减量必须也为整型,同样如果列为浮点型则减量也必须为浮点型。
返回值将返回最终的结果数值,数值为0时递减会变成负数。实例:创建swoole_table用于进程间数据共享
<?php
class Server
{
private $server;
public function __construct($host="0.0.0.0", $port = 9501, $size = 1024)
{
//创建swoole_table用于进程间数据共享
$table = new swoole_table($size);
$table->column("fd", swoole_table::TYPE_INT);
$table->column("uid", swoole_table::TYPE_INT);
$table->column("type", swoole_table::TYPE_STRING, 256);
$table->column("data", swoole_table::TYPE_STRING, 256);
$table->create();
//创建WebServer服务器对象
$this->server = new swoole_websocket_server($host, $port);
$this->server->table = $table;
//注册事件回调函数
$this->server->on("handShake", [$this, "onHandShake"]);
$this->server->on("workerStart", [$this, "onWorkerStart"]);
$this->server->on("open", [$this, "onOpen"]);
$this->server->on("message", [$this, "onMessage"]);
$this->server->on("close", [$this, "onClose"]);
//开启服务器
$this->server->start();
}
public function onHandShake($server)
{
echo "[handshake]".PHP_EOL;
}
public function onWorkerStart($server)
{
echo "[workerstart]".PHP_EOL;
}
public function onOpen($server)
{
echo "[open]".PHP_EOL;
}
public function onMessage($server)
{
echo "[message]".PHP_EOL;
}
public function onClose($message)
{
echo "[close]".PHP_EOL;
}
}
$server = new Server();
链接:https://www.jianshu.com/p/1dae870407bc
网友回复