redis通讯协议

  • redis通信协议

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

 

请求

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

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

命令格式为:

1

2

3

4

5

6

*<参数数量> CRLF

$<参数 1 的字节数量> CRLF

<参数 1 的数据> CRLF

...

$<参数 N 的字节数量> CRLF

<参数 N 的数据> CRLF

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

例如:

1

set a tioncico

tcp原始数据打印为:

1

2

3

4

5

6

7

8

string(34) "*3

$3

set

$1

a

$8

tioncico

"

实际数据为:

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

CRLF也就是"\r\n"

 

redis 响应

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

 

redis响应类型

通过检查redis服务器返回数据的第一个字节,可确定这个回复是什么类型:

1

2

3

4

5

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服务器会给你回复:

1

+OK\r\n

错误回复

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

 

示例:

 

当你 st a tioncico 发送不存在的"st"命令时:

1

-ERR unknown command 'st'\r\n

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

除了ERR错误这种通用型错误之外,还有更加特定的错误消息,例如:

1

-WRONGTYPE Operation against a key holding the wrong kind of value

整数回复

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

1

:1000\r\n

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

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

 

批量回复

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

例如:

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

将返回

1

$8\r\ntioncico\r\n

服务器发送的内容格式为:

1

2

3

4

5

6

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

 

多条批量回复

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

1

smembers listA

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

仙士可博客

 

将回复:

1

2

3

4

5

6

7

8

9

10

11

12

13

*5

$1

a

$1

1

$12

1

 2

 a

f

$0

 

$1

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

可看出:

1

2

3

4

5

6

7

8

9

10

*符号后面,是5,代表是5条回复

\r\n

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

\r\n

读取1字节的a

\r\n

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

...

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

\r\n

注意事项

1:多条批量回复,也可能后面跟着一个*多条批量回复,比如在geohash里面:仙士可博客  *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时返回

请求:

1

2

3

4

5

6

7

8

9

10

11

12

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']);

 

响应:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

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 $strLen) {

            $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();

 

注,本文协议内容参考了https://blog.csdn.net/tanswer_/article/details/80846757文章

 

 

本文为仙士可原创文章,本人转载复制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值