php实现redis的服务端的示例

四月 26, 2019 | views
Comments 0

大家都知道redis是用C来实现的,现在我用php来实现一个简单的仅支持SET和GET命令的redis服务端,主要是为了更好的了解redis的服务端和php的网络编程.代码如下:

  1. /** 
  2.  * 多进程阻塞式 
  3.  */ 
  4. class Xtgxiso_server 
  5.     private $socket = false; 
  6.     private $process_num = 100; 
  7.     public $redis_kv_data = array(); 
  8.     public $onMessage = null; 
  9.  
  10.     function __construct($host="0.0.0.0",$port=1215) 
  11.     { 
  12.         $this->socket = stream_socket_server("tcp://".$host.":".$port,$errno$errstr); 
  13.         if (!$this->socket) die($errstr."--".$errno); 
  14.         echo "listen $host $port \r\n"
  15.         ini_set("memory_limit""128M"); 
  16.     } 
  17.  
  18.     private function parseRESP(&$conn){ 
  19.         $line = fgets($conn); 
  20.         if($line === '' || $line === false) 
  21.         { 
  22.             return null; 
  23.         } 
  24.         $type = $line[0]; 
  25.         $line = mb_substr($line,1,-2); 
  26.         switch ( $type ){ 
  27.             case "*"
  28.                 $count = (int) $line
  29.                 $data = array(); 
  30.                 for ($i = 1; $i <= $count$i++) { 
  31.                     $data[] = $this->parseRESP($conn); 
  32.                 } 
  33.                 return $data
  34.             case "$"
  35.                 if ($line == '-1') { 
  36.                     return null; 
  37.                 } 
  38.                 $length = $line + 2; 
  39.                 $data = ''
  40.                 while ($length > 0) { 
  41.                     $block = fread($conn$length); 
  42.                     if ($length !== strlen($block)) { 
  43.                         throw new Exception('RECEIVING'); 
  44.                     } 
  45.                     $data .= $block
  46.                     $length -= mb_strlen($block); 
  47.                 } 
  48.                 return mb_substr($data, 0, -2); 
  49.         } 
  50.         return $line
  51.     } 
  52.  
  53.     private function start_worker_process(){ 
  54.         $pid = pcntl_fork(); 
  55.         switch ($pid) { 
  56.             case -1: 
  57.                 echo "fork error : {$i} \r\n"
  58.                 exit
  59.             case 0: 
  60.                 while ( 1 ) { 
  61.                     echo  "waiting...\n"
  62.                     $conn = stream_socket_accept($this->socket, -1); 
  63.                     if ( !$conn ){ 
  64.                         continue
  65.                     } 
  66.                     //"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n" 
  67.                     while(1){ 
  68.                         $arr = $this->parseRESP($conn); 
  69.                         if ( is_array($arr) ) { 
  70.                             if ($this->onMessage) { 
  71.                                 call_user_func($this->onMessage, $conn$arr); 
  72.                             } 
  73.                         }else if ( $arr ){ 
  74.                             if ($this->onMessage) { 
  75.                                 call_user_func($this->onMessage, $conn$arr); 
  76.                             } 
  77.                         }else
  78.                             fclose($conn); 
  79.                             break
  80.                         } 
  81.                     } 
  82.                 } 
  83.             default
  84.                 $this->pids[$pid] = $pid
  85.                 break
  86.         } 
  87.     } 
  88.  
  89.     public function run(){ 
  90.         for($i = 1; $i <= $this->process_num; $i++){ 
  91.             $this->start_worker_process(); 
  92.         } 
  93.  
  94.         while(1){ 
  95.             foreach ($this->pids as $i => $pid) { 
  96.                 if($pid) { 
  97.                     $res = pcntl_waitpid($pid$status,WNOHANG); 
  98.  
  99.                     if ( $res == -1 || $res > 0 ){ 
  100.                         $this->start_worker_process(); 
  101.                         unset($this->pids[$pid]); 
  102.                     } 
  103.                 } 
  104.             } 
  105.             sleep(1); 
  106.         } 
  107.     } 
  108.  
  109. $server =  new Xtgxiso_server(); 
  110.  
  111. $server->onMessage = function($conn,$infouse($server){ 
  112.     if ( is_array($info) ){ 
  113.         if ( $info["0"] == "SET" ) { 
  114.             $key = $info[1]; 
  115.             $val = $info[2]; 
  116.             $server->redis_kv_data[$key] = $val
  117.             fwrite($conn"+OK\r\n"); 
  118.         }else if ( $info["0"] == "GET" ){ 
  119.             $key = $info[1]; 
  120.             fwrite($conn"$".strlen($server->redis_kv_data[$key])."\r\n".$server->redis_kv_data[$key]."\r\n"); 
  121.         }else
  122.             fwrite($conn,"+OK\r\n"); 
  123.         } 
  124.     }else
  125.         fwrite($conn,"+OK\r\n"); 
  126.     } //phpfensi.com 
  127. }; 
  128.  
  129. $server->run(); 

通过如下命令来测试PHP实现的性能:

redis-benchmark -h 10.170.233.221 -p 1215 -t set -n 80000 -q

看来还是不错的,大家有兴趣可以再实现其他命令!



zend