在所有 PHP 进程之间共享变量/内存

问题描述 投票:0回答:5

是否可以在所有 PHP 进程之间共享变量和数组而不重复它们

使用memcached,我认为PHP会重复使用的内存:

$array = $memcache->get('array');

$array 将包含来自 memcached 的副本。

所以我的想法是,可能有一个已经定义的static变量,并在所有进程之间共享。

php shared-memory
5个回答
28
投票

使用

Shmop

Shmop 是一组易于使用的函数,允许 PHP 读取、 写入、创建和删除 Unix 共享内存段。

来自:http://www.php.net/manual/en/intro.shmop.php

构建此扩展不需要外部库。

共享内存功能

  • shmop_close — 关闭共享内存块
  • shmop_delete — 删除共享内存块
  • shmop_open — 创建或打开共享内存块
  • shmop_read — 从共享内存块读取数据
  • shmop_size — 获取共享内存块的大小
  • shmop_write — 将数据写入共享内存块

基本用法

// Create 100 byte shared memory block with system id of 0xff3
$shm_id = shmop_open(0xff3, "c", 0644, 100);
if (!$shm_id) {
    echo "Couldn't create shared memory segment\n";
}

// Get shared memory block's size
$shm_size = shmop_size($shm_id);
echo "SHM Block Size: " . $shm_size . " has been created.\n";

// Lets write a test string into shared memory
$shm_bytes_written = shmop_write($shm_id, "my shared memory block", 0);
if ($shm_bytes_written != strlen("my shared memory block")) {
    echo "Couldn't write the entire length of data\n";
}

// Now lets read the string back
$my_string = shmop_read($shm_id, 0, $shm_size);
if (!$my_string) {
    echo "Couldn't read from shared memory block\n";
}
echo "The data inside shared memory was: " . $my_string . "\n";

//Now lets delete the block and close the shared memory segment
if (!shmop_delete($shm_id)) {
    echo "Couldn't mark shared memory block for deletion.";
}
shmop_close($shm_id);

8
投票

在 PHP 进程之间共享内存的一种方法是安装 PHP 字节码缓存,如 APC。 APC 主要用于将字节码存储到操作系统管理的共享内存段中,但它也有一个 API,用于在进程之间共享您想要的任何内容(例如本地版本的 memcache)。

<?php
   $foobar = array('foo', 'bar');
   apc_store('foobar', $foobar);
?>

然后其他地方:

<?php
    $foobar = apc_fetch('foobar');
    var_dump($foobar);
?>

共享内存的一个大问题是两个进程很容易互相踩对方的脚。 因此,共享内存最适合那些不会改变太多的东西,比如大型全局数组。


3
投票

默认情况下这是不可能的。每个解决方案总是会将内容复制到当前作用域中,因为如果没有,就无法访问它。

我不知道,到底想做什么,但也许你可以在“外部”做到这一点,例如作为 gearman 工作,然后只捕获过程的结果,而不是整个数组。

您还可以考虑将“大”数组拆分为切片,然后始终从 apc 或 memcached 检索当前需要的部分。


3
投票

PHP 有神奇的方法:

  • __get($property)
    让我们实现对象上$property的访问
  • __set($property, $value)
    让我们实现对象上 $property 的分配

PHP 可以序列化变量:

  • serialize($variable)
    返回变量的字符串表示形式
  • unserialize($string)
    从字符串返回一个变量

PHP可以处理文件,具有并发访问管理:

  • fopen($file, 'c+')
    打开一个启用了咨询锁定选项的文件(允许您使用集群)
  • flock($descriptor, LOCK_SH)
    获取共享锁(用于读取)
  • flock($descriptor, LOCK_EX)
    需要独占锁(用于写入)

因此,在应用程序之间共享对象的最简单方法是创建一个类,该类实现并使用所有这些内容将其所有数据立即保存和恢复到文件中。

该类的一个简单实现可以是:

class Synchro
{

   private $_file;

   public function __construct($file)
   {
       $this->_file = $file;
   }

   public function __get($property)
   {
       // File does not exist
       if (!is_file($this->_file))
       {
           return null;
       }

       // Check if file is readable
       if ((is_file($this->_file)) && (!is_readable($this->_file)))
       {
           throw new Exception(sprintf("File '%s' is not readable.", $this->_file));
       }

       // Open file with advisory lock option enabled for reading and writting
       if (($fd = fopen($this->_file, 'c+')) === false)
       {
           throw new Exception(sprintf("Can't open '%s' file.", $this->_file));
       }

       // Request a lock for reading (hangs until lock is granted successfully)
       if (flock($fd, LOCK_SH) === false)
       {
           throw new Exception(sprintf("Can't lock '%s' file for reading.", $this->_file));
       }

       // A hand-made file_get_contents
       $contents = '';
       while (($read = fread($fd, 32 * 1024)) !== '')
       {
           $contents .= $read;
       }

       // Release shared lock and close file
       flock($fd, LOCK_UN);
       fclose($fd);

       // Restore shared data object and return requested property
       $object = json_decode($contents);
       if (property_exists($object, $property))
       {
           return $object->{$property};
       }

       return null;
   }

   public function __set($property, $value)
   {
       // Check if directory is writable if file does not exist
       if ((!is_file($this->_file)) && (!is_writable(dirname($this->_file))))
       {
           throw new Exception(sprintf("Directory '%s' does not exist or is not writable.", dirname($this->_file)));
       }

       // Check if file is writable if it exists
       if ((is_file($this->_file)) && (!is_writable($this->_file)))
       {
           throw new Exception(sprintf("File '%s' is not writable.", $this->_file));
       }

       // Open file with advisory lock option enabled for reading and writting
       if (($fd = fopen($this->_file, 'c+')) === false)
       {
           throw new Exception(sprintf("Can't open '%s' file.", $this->_file));
       }

       // Request a lock for writting (hangs until lock is granted successfully)
       if (flock($fd, LOCK_EX) === false)
       {
           throw new Exception(sprintf("Can't lock '%s' file for writing.", $this->_file));
       }

       // A hand-made file_get_contents
       $contents = '';
       while (($read = fread($fd, 32 * 1024)) !== '')
       {
           $contents .= $read;
       }

       // Restore shared data object and set value for desired property
       if (empty($contents))
       {
           $object = new stdClass();
       }
       else
       {
           $object = json_decode($contents);
       }
       $object->{$property} = $value;

       // Go back at the beginning of file
       rewind($fd);

       // Truncate file
       ftruncate($fd, strlen($contents));

       // Save shared data object to the file
       fwrite($fd, json_encode($object));

       // Release exclusive lock and close file
       flock($fd, LOCK_UN);
       fclose($fd);

       return $value;
   }

}

现在,您可以像

stdClass
一样使用此类,但在构造时使用文件路径。

$obj = new Synchro("/tmp/test.sync"); 
$obj->hello = 'world';

// ... and in another process...
echo $obj->hello;

这个例子当然非常简单,它关心对文件的并发访问,但不关心对变量的并发访问,在更好的实现中,您将使用类似互斥锁的锁。

我刚刚将这个课程(完成后)推送到github上,你可以在这里找到它。


0
投票

编辑:
您可能以错误的方式使用共享内存。
你的共享内存本身就是这样的数组。因此,您必须将单独的多语言字符串直接存储在共享内存中,而不是使用大数组。
然后只拉动特定页面所需的字符串。
就这样。

一般来说,要处理某些数据,程序必须通过将其存储在变量中来“复制”它。
这就是变量的用途 - 存储(或“复制”)一些外部数据。
例如,如果您的数据库中有一些用户信息,要在网页上显示用户名,您必须“复制”此数据,首先将其存储在 PHP 变量中。
等等。

您是第一个认为这种做法需要改变的人。

© www.soinside.com 2019 - 2024. All rights reserved.