进程间通信
进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法。每个进程都有自己的一部分独立的系统资源,彼此是隔离的。为了能使不同的进程互相访问资源并进 行协调工作,才有了进程间通信。
进程通信有如下的目的:
数据传输: 一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M之间;
共享数据: 多个进程想要操作共享数据,一个进程对数据的修改,其他进程应该立刻看到; 系统进行进程间通信(IPC)的时候,可用的方式包括管道、命名管道、消息队列、信号、信号量、共享内存、套接字(socket)等形式。
消息队列
关于消息队列实际上是一种思想或者说是策略,而并非是某一个程序所独有,我们可以通过消息队列让php与java等其他语言程序对接交互;并且值得一提的是,最简单的实现方式就是通过与一个文件记录信息,然后 通过另一个进程循环读取这个文件即可;
比如可以利用 file_put_contents 与 file_get_contents 就可以简单实现,不过我们这里讲的是如何利用系统的消息队列
示例演示
注意php操作系统内核的消息队列主要是使用sysvmsg扩展,以及相关函数如下:
函数 | 连接 |
---|---|
ftok | https://php.golaravel.com/function.ftok.html |
msg_get_queue | https://php.golaravel.com/function.msg-get-queue.html |
msg_receive | https://php.golaravel.com/function.msg-receive.html |
msg_remove_queue | https://php.golaravel.com/function.msg-remove-queue.html |
msg_send | https://php.golaravel.com/function.msg-send.html |
<?php
//父进程跟子进程实现消息发送
$msg_key = ftok(__DIR__, 'u'); //注意在php创建消息队列,第二个参数会直接转成字符串,可能会导致通讯失败
$msg_queue = msg_get_queue($msg_key);
$pid = pcntl_fork();
if ($pid == 0) {
// 子进程发送消息
msg_send($msg_queue, 10, "我是子进程发送的消息");
exit();
} else if ($pid) {
msg_receive($msg_queue, 10, $message_type, 1024, $message);
var_dump($message);
// 父进程接收消息
pcntl_wait($status);
msg_remove_queue($msg_queue);
}
也可以用不同的程序访问操作:
<?php
$msg_key = ftok(__DIR__, 'u'); //注意在php创建消息队列,第二个参数会直接转成字符串,可能会导致通讯失败
$msg_queue = msg_get_queue($msg_key);
msg_receive($msg_queue, 10, $message_type, 1024, $message);
var_dump($message);
// 父进程接收消息
msg_remove_queue($msg_queue);
<?php
$msg_key = ftok(__DIR__,'u'); //注意在php创建消息队列,第二个参数会直接转成字符串,可能会导致通讯失败
$msg_queue = msg_get_queue($msg_key);
msg_send($msg_queue, 10, "我是while");
注意:msg_receive在没有接收到信息的时候会是一个阻塞的状态
函数介绍
写信息:msg_send ( resource $queue , int $msgtype , mixed $message [, bool $serialize = true [, bool KaTeX parse error: Expected 'EOF', got '&' at position 24: … = true [, int &̲errorcode ]]] )
第1个参数 : resource $queue 表示要写入的消息队列资源。
第2个参数 : int $msgtype 表示写入消息队列的 消息类型,这个参数是 配合 msg_receive读取消息队列函数 使用的,下面会说。
第3个参数 : mixed $message 你要发送的信息,最大为 65536 个字节。
读信息:msg_receive ( resource $queue , int KaTeX parse error: Expected 'EOF', got '&' at position 22: …dmsgtype , int &̲msgtype , int KaTeX parse error: Expected 'EOF', got '&' at position 17: …axsize , mixed &̲message [, bool $unserialize = true [, int KaTeX parse error: Expected 'EOF', got '&' at position 18: …ags = 0 [, int &̲errorcode ]]] )
第1个参数 : resource $queue 表示要读取的消息队列资源。
第2个参数 : int $ desiredmsgtype 读取的消息类型。这个参数为 0 的时候,你可以读取 msg_send 以任意 消息类型 发送的消息。 如果此参数和你发送的某个消息类型相同,比如你有 2个消息,一个是通 过 1类型发送的,一个是通过2 类型发送的。你用 0 可以接收这两种消息 ,而你用 1 只能接收到 以1类型发送的消息。
第3个参数 : int&$msgtype 你读取到的信息,它发送时的消息类型会存储在该参数中。
第4个参数 : int $ maxsize 你以多大的字节去读取消息,如果这个值小于你要读取的内容的长度,你会读取失败。
第5个参数 : mixed&$message 读取的内容。
swoole官网实例
https://wiki.swoole.com/wiki/page/212.html
<?php
class SwooleTask {
protected $queueId;
protected $workerId;
protected $taskId = 1;
const SW_TASK_TMPFILE = 1; //tmp file
const SW_TASK_SERIALIZE = 2; //php serialize
const SW_TASK_NONBLOCK = 4; //task
const SW_EVENT_TASK = 7;
function __construct($key, $workerId = 0) {
$this->queueId = msg_get_queue($key);
if ($this->queueId === false) {
throw new \Exception("msg_get_queue() failed.");
}$this->workerId = $workerId;
}
protected static function pack($taskId, $data) {
$flags = self::SW_TASK_NONBLOCK;
$type = self::SW_EVENT_TASK;
if (!is_string($data)) {
$data = serialize($data);
$flags |= self::SW_TASK_SERIALIZE;
}if (strlen($data) >= 8180) {
$tmpFile = tempnam('/tmp/', 'swoole.task');
file_put_contents($tmpFile, $data);
$data = pack('l', strlen($data)) . $tmpFile . "\0";
$flags |= self::SW_TASK_TMPFILE;
$len = 128 + 24;
} else {
$len = strlen($data);
}return pack('lSsCCS', $taskId, $len, 0, $type, 0, $flags) . $data;
}
function dispatch($data) {
$taskId = $this->taskId++;
if (!msg_send($this->queueId, 2, self::pack($taskId, $data), false)) {
return false;
} else {
return $taskId;
}
}
}
echo "Sending text to msg queue.\n";
$task = new SwooleTask(822094551, 1);
//普通字符串
$task->dispatch("Hello from PHP!");
<?php
$server = new Swoole\Server("0.0.0.0", 9000); //创建server对象
$server->set(['worker_num' => 1, //设置进程
'task_worker_num' => 1, //task进程数
]);
//消息发送过来
$server->on('receive', function (swoole_server $server, int $fd, int $reactor_id, string $data) {
$server->task(7);
$server->send($fd, 1);
});
//ontask事件回调
$server->on('task', function (swoole_server $server, $task_id, $form_id, $data) {
echo "接受到信息\n";
var_dump($server->worker_id);
$server->sendMessage("Task数据", 2); // 0 ~ (worker_num + task_worker_num - 1)
$server->finish("执行完毕");
});
$server->on('finish', function ($server, $task_id, $data) {
});
$server->on('PipeMessage', function (swoole_server $server, $src_worker_id, $message) {
echo "\n接收到数据\n";
var_dump($message);
});
//服务器开启
$server->start();
在worker中实现task
// io\src\Reactor\Swoole\MulitEndhancedTask\Traits\TaskTraits.php
task的作用
注意关于swoole的task虽然有体现这种异步的效果,但是并不意味这它是用来替代消息中间件的; swoole中的task最大的特点在于帮助我们处理一些耗时任务的业务逻辑,完成之后就会通知给相应的进程; 而消息中间件-》在实际的工作中我们可能会考虑消息的优先级,延迟任务,存储,应答等等;
swoole中的编程需知
https://wiki.swoole.com/wiki/page/500.html
建议大家在使用的时候一定要关注这些内容