用PHP实现memcache客户端

1、缘起

偶然翻看openresty安装文件的目录,看到lualib/resty目录下有一些用lua写的模块,其中有个memcached.lua,原来是memcache客户端的源码,突然想起memcached协议是基于命令行的。他这个模块用到了ngx.socket.tcp模块,粗略看了下,发现也不是特别复杂,就是用socket收发数据。于是心血来潮,百度了下memcached协议,来造个轮子。

2、代码

memcached协议可参考:memcached协议中文版,讲得挺详细的。

废话少说,上代码(只实现了get,set,add,replace,flush_all共5个命令)。

/** 封装的异常类 */
class MemcacheException extends Exception {
    public function __construct($message, $code = 0) {
        parent::__construct($message, $code);
    }
}

class MyMemcacheClient {
    private $host;
    private $port;
    private $socket;

    public function __construct() {
        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    }

    /**
     * 获取最后一次的socket错误
     * @return string 最后一次socket错误字符串
     */
    public function getSocketError() {
        return socket_strerror(socket_last_error($this->socket));
    }

    /**
     * 抛出异常封装函数
     * @return MemcacheException MemcacheException实例
     */
    private function throwException() {
        throw new MemcacheException($this->getSocketError());
    }

    /**
     * 链接memcached服务器
     * @param  string  $host memcached监听的ip
     * @param  integer $port memcached监听的端口
     * @return boolean     true表示连接成功,false表示连接失败
     */
    public function connect($host = '127.0.0.1', $port = 11211) {
        $this->host = $host;
        $this->port = $port;

        $result = socket_connect($this->socket, $host, $port);
        if ($result === false) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 执行set|add|replace命令
     * @param string  $cmd   命令(set|add|replace)
     * @param string  $key   键
     * @param string  $value 值
     * @param nteger   $ttl  生存时间
     * @return boolean true for success, false for fail
     */
    private function _set_add_replace($cmd, $key, $value, $ttl = 10) {
        $line1 = sprintf("$cmd %s 0 %d %d\r\n", $key, $ttl, strlen($value));
        $line2 = $value . "\r\n";

        $data = $line1 . $line2;

        $result = socket_write($this->socket, $data, strlen($data));
        if ($result === false) {
            $this->throwException();
        }

        $response = socket_read($this->socket, 1024, PHP_NORMAL_READ);
        /** 读取最后一个 \n 字符 */
        socket_read($this->socket, 1, PHP_BINARY_READ);

        if ($response === false) {
            $this->throwException();
        }

        /** 操作成功会返回STORED\r\n */
        if (!strncmp($response, 'STORED', 6)) {
            return true;
        }

        return false;
    }

    public function set($key, $value, $ttl = 10) {
        return $this->_set_add_replace('set', $key, $value, $ttl);
    }

    public function add($key, $value, $ttl = 10) {
        return $this->_set_add_replace('add', $key, $value, $ttl);
    }

    public function replace($key, $value, $ttl = 10) {
        return $this->_set_add_replace('replace', $key, $value, $ttl);
    }

    /**
     * 获取一个键的值
     * @param  string $key 键
     * @return string|boolean    值, false表示没有这个键或者已过期
     */
    public function get($key) {
        $data = sprintf("get %s\r\n", $key);

        $result = socket_write($this->socket, $data, strlen($data));
        if ($result === false) {
            $this->throwException();
        }

        $line1 = socket_read($this->socket, 1024, PHP_NORMAL_READ);
        /** 读取最后一个 \n 字符 */
        socket_read($this->socket, 1, PHP_BINARY_READ);

        if (!$line1) {
            $this->throwException();
        }

        /** 获取成功,第一行返回 VALUE <key> <flags> <bytes>\r\n */
        if (!strncmp($line1, "VALUE", 5)) {
            $line1 = rtrim($line1, "\r\n");
            $arr = explode(' ', $line1);
            /** 获取数据长度 */
            $dataLen = intval(end($arr));

            /** 获取数据 */
            $response = socket_read($this->socket, $dataLen, PHP_BINARY_READ);
            /** 读取最后7个字符 \r\nEND\r\n  */
            socket_read($this->socket, 7, PHP_BINARY_READ);

            if ($response === false) {
                $this->throwException();
            }

            return $response;
        } else {
            return false;
        }
    }

    /**
     * 设置所有的键过期
     * @return boolean success
     */
    public function flushAll() {
        $data = "flush_all\r\n";

        $result = socket_write($this->socket, $data, strlen($data));
        /** 读取返回结果,固定为 OK\r\n  */
        socket_read($this->socket, 4, PHP_BINARY_READ);

        return true;
    }
}

3、测试

  • 测试代码
try {
    $memcache = new MyMemcacheClient();
    $memcache->connect();

    $memcache->flushAll();

    echo "a=", $memcache->get("a"), PHP_EOL;

    $memcache->set("a", "This is a");
    $data = $memcache->get("a");

    echo "a: ", $data, PHP_EOL;

    $memcache->set("a", "This is modified a");
    echo "a: ", $memcache->get("a"), PHP_EOL;

    $memcache->add("b", "This is b");
    echo "b: ", $memcache->get("b"), PHP_EOL;

    $memcache->replace("b", "This is replaced b");
    echo "b: ", $memcache->get("b"), PHP_EOL;
} catch (MemcacheException $e) {
    echo $e->getMessage();
}
  • 测试结果
a=
a: This is a
a: This is modified a
b: This is b
b: This is replaced b
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值