大家都知道redis是用C来实现的,现在我用php来实现一个简单的仅支持SET和GET命令的redis服务端,主要是为了更好的了解redis的服务端和php的网络编程.
代码如下:
<?php /** * 多进程阻塞式 */ class Xtgxiso_server { private $socket = false; private $process_num = 100; public $redis_kv_data = array(); public $onMessage = null; function __construct($host="0.0.0.0",$port=1215) { $this->socket = stream_socket_server("tcp://".$host.":".$port,$errno, $errstr); if (!$this->socket) die($errstr."--".$errno); echo "listen $host $port \r\n"; ini_set("memory_limit", "128M"); } private function parseRESP(&$conn){ $line = fgets($conn); if($line === '' || $line === false) { return null; } $type = $line[0]; $line = mb_substr($line,1,-2); switch ( $type ){ case "*": $count = (int) $line; $data = array(); for ($i = 1; $i <= $count; $i++) { $data[] = $this->parseRESP($conn); } return $data; case "$": if ($line == '-1') { return null; } $length = $line + 2; $data = ''; while ($length > 0) { $block = fread($conn, $length); if ($length !== strlen($block)) { throw new Exception('RECEIVING'); } $data .= $block; $length -= mb_strlen($block); } return mb_substr($data, 0, -2); } return $line; } private function start_worker_process(){ $pid = pcntl_fork(); switch ($pid) { case -1: echo "fork error : {$i} \r\n"; exit; case 0: while ( 1 ) { echo "waiting...\n"; $conn = stream_socket_accept($this->socket, -1); if ( !$conn ){ continue; } //"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n" while(1){ $arr = $this->parseRESP($conn); if ( is_array($arr) ) { if ($this->onMessage) { call_user_func($this->onMessage, $conn, $arr); } }else if ( $arr ){ if ($this->onMessage) { call_user_func($this->onMessage, $conn, $arr); } }else{ fclose($conn); break; } } } default: $this->pids[$pid] = $pid; break; } } public function run(){ for($i = 1; $i <= $this->process_num; $i++){ $this->start_worker_process(); } while(1){ foreach ($this->pids as $i => $pid) { if($pid) { $res = pcntl_waitpid($pid, $status,WNOHANG); if ( $res == -1 || $res > 0 ){ $this->start_worker_process(); unset($this->pids[$pid]); } } } sleep(1); } } } $server = new Xtgxiso_server(); $server->onMessage = function($conn,$info) use($server){ if ( is_array($info) ){ if ( $info["0"] == "SET" ) { $key = $info[1]; $val = $info[2]; $server->redis_kv_data[$key] = $val; fwrite($conn, "+OK\r\n"); }else if ( $info["0"] == "GET" ){ $key = $info[1]; fwrite($conn, "$".strlen($server->redis_kv_data[$key])."\r\n".$server->redis_kv_data[$key]."\r\n"); }else{ fwrite($conn,"+OK\r\n"); } }else{ fwrite($conn,"+OK\r\n"); } }; $server->run();
通过如下命令来测试PHP实现的性能:
redis-benchmark -h 10.170.233.221 -p 1215 -t set -n 80000 -q
看来还是不错的,大家有兴趣可以再实现其他命令!