• PHP共享内存的应用shmop系列


    简单的说明

    可能很少情况会使用PHP来操控共享内存,一方面在内存的控制上,MC已经提供了一套很好的方式,另一方面,自己来操控内存的难度较大,内存的读写与转存,包括后面可能会用到的存储策略,要是没有一定计算机组成原理的基础,想做这些不是一件容易的事情。那为什么还要使用它呢?如果我想进行管道通信,为其它的应用服务准备数据;我想建立自己的数据缓存体系,使用MC有点大炮打苍蝇的感觉。那么shmop会是一个选择,当然,在操作内存前,一定要谨慎。

    系统要求

    shmop系列函数只是在unix/Linux下可用,可以通过命令:

    Plain代码 
    1. ipcs -m  


    来查看当前的共享内存使用情况。其中,各个部分解释如下:

    key :共享内存的唯一的key值,共享内存通过该key来判断你读取的是哪一块内存。

    shmid:当使用key来获取内存时,你获得的是这个id的值。它作为你操作内存块的标识。

    owner:创建该共享内存块的用户

    perms:该共享内存的读写权限,8禁止,可以是777,与文件的读写权限一致。

    bytes:该内存块的大小

    nattch:连接该内存块的进程数

    status:当前状态,如:dest,即将删除等。

    使用示例

    具体的使用说明,在PHP手册中有详细介绍,这里不进行赘述。这里将写一些简单的操作例子。

    写入

    Php代码 
    1. <?php  
    2. /** 
    3.   * SHMOP共享内存操作示例 
    4.   * @author monkee 
    5.  **/  
    6. $key = 0x4337b700;  
    7. $size = 4096;  
    8. $shmid = @shmop_open($key, 'c', 0644, $size);  
    9. if($shmid === FALSE){  
    10.     exit('shmop_open error!');  
    11. }  
    12.   
    13. $data = '世界,你好!我将写入很多的数据,你能罩得住么?';  
    14.   
    15. $length = shmop_write($shmid, pack('a*',$data), 0);  
    16. if($length === FALSE){  
    17.     exit('shmop_write error!');  
    18. }  
    19.   
    20. @shmop_close($shmid);  
    21.   
    22. exit('succ');  
    23. ?>  

    读取

    Php代码 
    1. <?php  
    2. /** 
    3.   * SHMOP共享内存操作示例 
    4.   * @author monkee 
    5.  **/  
    6. $key = 0x4337b700;  
    7. $size = 256;  
    8. $shmid = @shmop_open($key, 'c', 0644, $size);  
    9. if($shmid === FALSE){  
    10.     exit('shmop_open error!');  
    11. }  
    12.   
    13. $data = unpack('a*', shmop_read($shmid, 0, 256));  
    14. if($data === FALSE){  
    15.     exit('shmop_read error!');  
    16. }  
    17. @shmop_close($shmid);  
    18.   
    19. exit($data[1]);  
    20. ?>  


    这里使用到了函数:pack() 这个函数用来将内存里的内容转化为二进制内容,具体请查看手册内容。

    多服务器内存同步

    已经在本地做好了这个服务,现在需要在多台服务器上进行内存数据同步。虽然这个时候可以放弃共享内存的方式来处理数据了,然而你被要求需要这么做。于是,同步我想不是问题,做好“主-从”的架构,我同步好的内存及时推送过去就可以了。然而,我是不是需要在从机上做一个监听程序呢?这样的代价有点大,好的一点是,从机上有apache。也就是说可以使用HTTP协议来进行通信了。

    同步策略

    如何同步?看似无聊的问题,却又产生了疑惑。同步数据呗,但是同步什么数据!一种方式是主机的内存改变后,程序读取所有内存数据然后发送到从机进行同步;如果我只是更改一些简单的操作位的话,那么小的更新却要引起整个内存块的同步,似乎有些浪费。还有一种,是更新变化。将变化进行更新。这种比较复杂,因为你需要定义每一种操作的处理。幸运的是,你需要操作的数据并不多,还有,你要定义的操作也不多:write,delete(read可以不要,因为你很少会从从机上读取数据)。那么好了,我们选择其中一种来做吧。

    主机发送

    Php代码 
    1. <?php  
    2.   
    3. /**  
    4.   * 共享内存操作,支持远程内存同步。 
    5.   * @author hufeng@ 
    6.   * @since 2011-08-10 
    7.   * 
    8.   */  
    9. define('PSHMOP_HOST', '192.168.0.1');   
    10. define('PSHMOP_SEPE', " ----------CKSJFIOWKJDFOCKJVNBBSDF---------- ");  
    11. class Pshmop   
    12. {  
    13.     static private $data = array();  
    14.     static public function write($key, $offset, $data, $size = 0){   
    15.         $h = array('key' => $key, 'offset' => $offset, 'size' => $size, 'ac' => 'write');   
    16.         return self::add($h, $data);   
    17.     }   
    18.        
    19.     static public function del($key){   
    20.         $h = array('key' => $key, 'ac' => 'delete');   
    21.         return self::add($h);   
    22.     }  
    23.   
    24.     static private function add($dataheader, $databody=''){  
    25.         self::$data[] = serialize($dataheader)." ".$databody;   
    26.     }  
    27.    
    28.     static private function send(){   
    29.         $d = & self::$data;  
    30.         if(count($d) == 0){  
    31.             return ;   
    32.         }  
    33.         $http_entity_body = join(PSHMOP_SEPE, $d);  
    34.         $http_entity_length = strlen($http_entity_body);  
    35.         $fp = fsockopen(PSHMOP_HOST, 80, $errno, $error);   
    36.         if(!$fp){   
    37.             return -1;   
    38.         }   
    39.   
    40.         fputs($fp, "PUT /pshmop.php HTTP/1.1 ");   
    41.         fputs($fp, 'Host: '.PSHMOP_HOST." ");   
    42.         fputs($fp, "Content-Type: application/x-www-form-urlencoded ");   
    43.         fputs($fp, "Content-Length: {$http_entity_length} ");   
    44.         fputs($fp, "Connection: close ");   
    45.         fputs($fp, $http_entity_body . " ");   
    46.         $d = '';   
    47.         while(!feof($fp)){   
    48.             $d .= fgets($fp, 4096);   
    49.         }   
    50.         fclose($fp);   
    51.         return $d;   
    52.     }   
    53. }   


    使用的时候,进行:

    Php代码 
    1. Pshmop::write();  
    2. Pshmop::write();  
    3. Pshmop::write();  
    4. Pshmop::send();  


    PS:为了支持多个更新一次传递的原则,以上便是举例。

    从机监听

    Php代码 
    1. <?php  
    2.   
    3. /** 
    4.   * 共享内存远程处理类 
    5.   * 对从远端传输到的数据进行处理、内存同步更新 
    6.   * @author hufeng@ 
    7.   * @since 2011-08-11 
    8.   **/  
    9. define('RSHMOP_SEPE', " ----------CKSJFIOWKJDFOCKJVNBBSDF---------- ");  
    10. class Rshmop  
    11. {  
    12.     static public function run($data){  
    13.         $items = @explode(RSHMOP_SEPE, $data);  
    14.         if($items === NULL){  
    15.             return -1;  
    16.         }  
    17.         $result = array('succ' => 0, 'error' => 0);  
    18.         foreach($items as $k => $i){  
    19.             self::op($i) === 0 ? $result['succ']++ : $result['error']++;  
    20.             unset($items[$k]);  
    21.         }  
    22.         return $result;  
    23.     }  
    24.     static public function op($str){  
    25.         $p = strpos($str, " ");  
    26.         $header = @unserialize(substr($str, 0, $p));  
    27.         if($header === FALSE){  
    28.             return 'Data Format Error!';  
    29.         }  
    30.         $body = substr($str, $p+1);  
    31.         $shmid = null;  
    32.         if($header['size'] > 0){  
    33.             $shmid = @shmop_open($header['key'], 'c', 0644, $header['size']);  
    34.         }  
    35.         if(!$shmid){  
    36.             $cmd = "ipcs -m | grep '{$header['key']}'";  
    37.             $em = exec($cmd);  
    38.             $em = preg_replace('/ +/', ' ', $em);  
    39.             $ems = explode(' ', $em);  
    40.             $header['size'] = intval($ems[4]);  
    41.             if($header['size'] == 0){  
    42.                 if($headerreturn ['ac'] == 'delete'){  
    43.                     return 0;  
    44.                 }else{  
    45.                     return 'Param `size` required!';  
    46.                 }  
    47.             }  
    48.             $shmid = @shmop_open($header['key'], 'c', 0644, $header['size']);  
    49.         }  
    50.         if($header['ac'] == 'write'){  
    51.             shmop_write($shmid, $body, $header['offset']);  
    52.         }  
    53.         if($header['ac'] == 'delete'){  
    54.             shmop_delete($shmid);  
    55.         }  
    56.         shmop_close($shmid);  
    57.         return 0;  
    58.     }  
    59. }  


    如果遇到主机有而从机未有的数据块(可能由网络问题造成,也可以有其它解决办法),可以选择delete然后再进行其它操作。

    缓存使用的策略

    觉得使用MC代价有点高,可以自己来控制内存和使用。当然,小部分的数据可以容易使用,但是当数据多的时候,决定哪部分数据进入内存,哪部分数据进入硬盘都是值得商榷的。这也就是为什么要提策略。

    常见的策略无非是 FIFO,LUR,LAR等,但并不是说这些策略就是好的。实际情况中,根据具体的业务需求,来组织相应的策略。最常见的,我们是将从数据库查询时间长的、获取数据耗时(从其它机器上获取)、计算耗时的、需要及时使用的放入内存中。

    这部分的代码就不写了,希望有所帮助。

    转载自:http://tubaluer.iteye.com/blog/1349797 作者 tubaluer

  • 相关阅读:
    突然想到一个问题:有关 cqrs 分离
    Oracle 12C 数据库安装与配置
    Android 绑定服务的作用 (参考,不确定他说的是不是对的)
    SSM框架整合(一)
    常见数据库优化方案(九)
    大量文件存储
    MyBatis 参数传递小知识(划重点)|划掉 MyBatis 常见小debug
    MyBatis 自动代码生成器
    常见数据库优化方案(八)
    常用数据库优化方案(五)
  • 原文地址:https://www.cnblogs.com/chengzhi59/p/6489820.html
Copyright © 2020-2023  润新知