php redis返回值,redis 通信协议,php实现redis协议

redis通信协议

redis通信协议由tcp协议进行数据交互,默认端口为6379

请求

Redis 服务器接受命令以及命令的参数。

服务器会在接到命令之后,对命令进行处理,并将命令的回复传送回客户端。

命令格式为:* CRLF

$ CRLF

 CRLF

...

$ CRLF

 CRLF

命令本身也作为协议的其中一个参数来发送。

例如:set a tioncico

tcp原始数据打印为:string(34) "*3

$3

set

$1

a

$8

tioncico

"

实际数据为:

string(34) "*3CRLF$3CRLFsetCRLF$1CRLFa$8CRLFtioncicoCRLF"

CRLF也就是"\r\n"

redis 响应

当redis服务器接收到请求时,会做出响应,redis会根据不同的命令以及数据,返回不同类型的数据

redis响应类型

通过检查redis服务器返回数据的第一个字节,可确定这个回复是什么类型:1:"+" 状态回复(status reply)

2:"-" 错误回复(error reply)

3:":" 整数回复(integer reply)

4:"$" 批量回复(bulk reply)

5:"*" 多条批量回复(multi bulk reply)

状态回复

一个状态回复(或者单行回复,single line reply)是一段以 “+” 开始、 “\r\n” 结尾的单行字符串。

例如:

当你set a tioncic之后,redis服务器会给你回复:+OK\r\n

错误回复

错误回复第一个字节以"-"开头:

示例:

当你 st a tioncico 发送不存在的"st"命令时:-ERR unknown command 'st'\r\n

在 “-” 之后,直到遇到第一个空格或新行为止,这中间的内容表示所返回错误的类型。剩余内容为错误内容

除了ERR错误这种通用型错误之外,还有更加特定的错误消息,例如:-WRONGTYPE Operation against a key holding the wrong kind of value

整数回复

由":"开头,\r\n结尾的消息为整数回复,例如::1000\r\n

":"到\r\n中间的内容即是整数回复

返回值的唯一限制是该数据必须用64位整数存储

批量回复

服务器使用批量回复来返回二进制安全的字符串,字符串的最大长度为 512 MB 。

例如:

get a (在上面的例子中,已经set a的值为tioncico)

将返回$8\r\ntioncico\r\n

服务器发送的内容格式为:1:第一个字符为"$"

2:随后跟着随机回复内容的长度值8

3:然后跟着CRLF(\r\n)

4:然后随后的8个字节,都代表是回复的内容

5:然后再跟着CRLF(\r\n)

6:该回复完成

特殊情况

1:当你的数据本身带\r\n时,无需顾虑,直接跟着回复内容的长度值进行获取,里面包含了这个的\r\n,但如果你的tcp客户端是通过\r\n进行分批次获取数据,需要额外的进行判断,组装数据

2:如果当你get一个不存在的键时,redis将会给你返回"$-1\r\n" 来表示该数据不存在,注意,它不是表达空字符串,而是表达它为null

多条批量回复

多条批量回复由"*" 开头,后面的数字代表共有几条回复,例如:smembers listA

listA是本人已经add过的集合键名,数据如下:

77d6aa845cac104af03630a417716fb5.png

将回复:*5

$1

a

$1

1

$12

1

2

a

f

$0

$1

由于\r\n太多,这里不处理为字符串显示

可看出:*符号后面,是5,代表是5条回复

\r\n

后面跟着$1,代表是批量回复,1是跟着的字节

\r\n

读取1字节的a

\r\n

继续读取$1,代表是批量回复,1是跟着的字节

...

读取$0,代表是批量回复,0代表该键值为空

\r\n

注意事项

1:多条批量回复,也可能后面跟着一个*多条批量回复,比如在geohash里面:

147ad85907ad0d904a99da22eab6a19f.png  *1后面跟着*2,或者可能*2后面跟着*2,代表这个回复,有2条回复,并且回复里面也有2条回复

2:$0代表着空字符串

3:$-1代表着null

php实现

本人使用swoole client协程客户端,已经实现了redis的通信协议,组件地址:https://github.com/easy-swoole/redis

核心处理代码如下:

代码使用swoole tcp客户端,配置为每次根据\r\n读取,每次读取到\r\n时返回

请求:public function sendCommand(array $commandList)

{

$argNum = count($commandList);

$str = "*{$argNum}\r\n";

foreach ($commandList as $value) {

$len = strlen($value);

$str = $str . '$' . "{$len}\r\n{$value}\r\n";

}

return $this->send($str);//tcp流发送,本文不做说明,可查看源码

}

$client->sendCommand(['sadd', "listA", 'a']);

响应:function recv(): ?Response

{

/*

*

用单行回复,回复的第一个字节将是“+”

错误消息,回复的第一个字节将是“-”

整型数字,回复的第一个字节将是“:”

批量回复,回复的第一个字节将是“$”

多个批量回复,回复的第一个字节将是“*”

*/

$result = new Response();

$str = $this->client->recv($this->timeout);

if (empty($str)) {

$result->setStatus($result::STATUS_TIMEOUT);

return $result;

}

/**

* 去除每行的\r\n

*/

$str = substr($str, 0, -2);

$op = substr($str, 0, 1);

$result = $this->opHandel($op, $str);

return $result;

}

/**

* 字符串处理方法

* opHandel

* @param $op

* @param $value

* @return Response

* @author Tioncico

* Time: 11:52

*/

protected function opHandel($op, $value)

{

$result = new Response();

switch ($op) {

case '+':

{

$result = $this->successHandel($value);

break;

}

case '-':

{

$result = $this->errorHandel($value);

break;

}

case ':':

{

$result = $this->intHandel($value);

break;

}

case '$':

{

$result = $this->batchHandel($value);

break;

}

case "*":

{

$result = $this->multipleBatchHandel($value);

break;

}

}

return $result;

}

/**

* 状态类型处理

* successHandel

* @param $value

* @return Response

* @author Tioncico

* Time: 11:52

*/

protected function successHandel($value): Response

{

$result = new Response();

$result->setStatus($result::STATUS_OK);

$result->setData(substr($value, 1));

return $result;

}

/**

* 错误类型处理

* errorHandel

* @param $value

* @return Response

* @author Tioncico

* Time: 11:53

*/

protected function errorHandel($value): Response

{

$result = new Response();

//查看空格位置

$spaceIndex = strpos($value, ' ');

//查看换行位置

$lineIndex = strpos($value, PHP_EOL);

if ($lineIndex === false || $lineIndex > $spaceIndex) {

$result->setErrorType(substr($value, 1, $spaceIndex - 1));

} else {

$result->setErrorType(substr($value, 1, $lineIndex - 1));

}

$result->setStatus($result::STATUS_ERR);

$result->setMsg(substr($value, 1));

return $result;

}

/**

* int类型处理

* intHandel

* @param $value

* @return Response

* @author Tioncico

* Time: 11:53

*/

protected function intHandel($value): Response

{

$result = new Response();

$result->setStatus($result::STATUS_OK);

$result->setData((int)substr($value, 1));

return $result;

}

/**

* 批量回复处理

* batchHandel

* @param $str

* @param $timeout

* @return bool|string

* @author Tioncico

* Time: 17:13

*/

protected function batchHandel($str)

{

$response = new Response();

$strLen = substr($str, 1);

//批量回复,继续读取字节

$len = 0;

$buff = '';

if ($strLen == 0) {

$this->client->recv($this->timeout);

$response->setData('');

} else {

while ($len 

$strTmp = $this->client->recv($this->timeout);

$len += strlen($strTmp);

$buff .= $strTmp;

}

$response->setData(substr($buff, 0, -2));

}

$response->setStatus($response::STATUS_OK);

return $response;

}

/**

* 多条批量回复

* multipleBatchHandel

* @param $value

* @return Response

* @author Tioncico

* Time: 14:33

*/

protected function multipleBatchHandel($value)

{

$result = new Response();

$len = substr($value, 1);

if ($len == 0) {

$result->setStatus($result::STATUS_OK);

$result->setData([]);

} elseif ($len == -1) {

$result->setStatus($result::STATUS_OK);

$result->setData(null);

} else {

$arr = [];

while ($len--) {

$str = $this->client->recv($this->timeout);

$str = substr($str, 0, -2);

$op = substr($str, 0, 1);

$response = $this->opHandel($op, $str);

$arr[] = $response->getData();

}

$result->setStatus($result::STATUS_OK);

$result->setData($arr);

}

return $result;

}

$recv = $client->recv();

本文为仙士可原创文章,转载无需和我联系,但请注明来自仙士可博客www.php20.cn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值