redis的通信协议本文不做分析和解读,建议参考redis官方文档或其它博文,我们直接看实现:
redis相关命令及常量定义:
package protocol
const DOLLARBYTE = "$"
const ASTERISKBYTE = "*"
const PLUSBYTE = "+"
const MINUS_BYTE = "-"
const COLON_BYTE = ":"
const CRLF = "\r\n"
const BLANK = ""
const OK = "+OK\r\n"
const NONEXIST = "$-1"
const PING = "PING"
const SET = "SET"
const GET = "GET"
const QUIT = "QUIT"
const EXISTS = "EXISTS"
const DEL = "DEL"
const TYPE = "TYPE"
const FLUSHDB = "FLUSHDB"
const KEYS = "KEYS"
const RANDOMKEY = "RANDOMKEY"
const RENAME = "RENAME"
const RENAMENX = "RENAMENX"
const RENAMEX = "RENAMEX"
const DBSIZE = "DBSIZE"
const EXPIRE = "EXPIRE"
const EXPIREAT = "EXPIREAT"
const TTL = "TTL"
const SELECT = "SELECT"
const MOVE = "MOVE"
const FLUSHALL = "FLUSHALL"
const GETSET = "GETSET"
const MGET = "MGET"
const SETNX = "SETNX"
const SETEX = "SETEX"
const MSET = "MSET"
const MSETNX = "MSETNX"
const DECRBY = "DECRBY"
const DECR = "DECR"
const INCRBY = "INCRBY"
const INCR = "INCR"
const APPEND = "APPEND"
const SUBSTR = "SUBSTR"
const HSET = "HSET"
const HGET = "HGET"
const HSETNX = "HSETNX"
const HMSET = "HMSET"
const HMGET = "HMGET"
const HINCRBY = "HINCRBY"
const HEXISTS = "HEXISTS"
const HDEL = "HDEL"
const HLEN = "HLEN"
const HKEYS = "HKEYS"
const HVALS = "HVALS"
const HGETALL = "HGETALL"
const RPUSH = "RPUSH"
const LPUSH = "LPUSH"
const LLEN = "LLEN"
const LRANGE = "LRANGE"
const LTRIM = "LTRIM"
const LINDEX = "LINDEX"
const LSET = "LSET"
const LREM = "LREM"
const LPOP = "LPOP"
const RPOP = "RPOP"
const RPOPLPUSH = "RPOPLPUSH"
const SADD = "SADD"
const SMEMBERS = "SMEMBERS"
const SREM = "SREM"
const SPOP = "SPOP"
const SMOVE = "SMOVE"
const SCARD = "SCARD"
const SISMEMBER = "SISMEMBER"
const SINTER = "SINTER"
const SINTERSTORE = "SINTERSTORE"
const SUNION = "SUNION"
const SUNIONSTORE = "SUNIONSTORE"
const SDIFF = "SDIFF"
const SDIFFSTORE = "SDIFFSTORE"
const SRANDMEMBER = "SRANDMEMBER"
const ZADD = "ZADD"
const ZRANGE = "ZRANGE"
const ZREM = "ZREM"
const ZINCRBY = "ZINCRBY"
const ZRANK = "ZRANK"
const ZREVRANK = "ZREVRANK"
const ZREVRANGE = "ZREVRANGE"
const ZCARD = "ZCARD"
const ZSCORE = "ZSCORE"
const MULTI = "MULTI"
const DISCARD = "DISCARD"
const EXEC = "EXEC"
const WATCH = "WATCH"
const UNWATCH = "UNWATCH"
const SORT = "SORT"
const BLPOP = "BLPOP"
const BRPOP = "BRPOP"
const AUTH = "AUTH"
const SUBSCRIBE = "SUBSCRIBE"
const PUBLISH = "PUBLISH"
const UNSUBSCRIBE = "UNSUBSCRIBE"
const PSUBSCRIBE = "PSUBSCRIBE"
const PUNSUBSCRIBE = "PUNSUBSCRIBE"
const PUBSUB = "PUBSUB"
const ZCOUNT = "ZCOUNT"
const ZRANGEBYSCORE = "ZRANGEBYSCORE"
const ZREVRANGEBYSCORE = "ZREVRANGEBYSCORE"
const ZREMRANGEBYRANK = "ZREMRANGEBYRANK"
const ZREMRANGEBYSCORE = "ZREMRANGEBYSCORE"
const ZUNIONSTORE = "ZUNIONSTORE"
const ZINTERSTORE = "ZINTERSTORE"
const ZLEXCOUNT = "ZLEXCOUNT"
const ZRANGEBYLEX = "ZRANGEBYLEX"
const ZREVRANGEBYLEX = "ZREVRANGEBYLEX"
const ZREMRANGEBYLEX = "ZREMRANGEBYLEX"
const SAVE = "SAVE"
const BGSAVE = "BGSAVE"
const BGREWRITEAOF = "BGREWRITEAOF"
const LASTSAVE = "LASTSAVE"
const SHUTDOWN = "SHUTDOWN"
const INFO = "INFO"
const MONITOR = "MONITOR"
const SLAVEOF = "SLAVEOF"
const CONFIG = "CONFIG"
const STRLEN = "STRLEN"
const SYNC = "SYNC"
const LPUSHX = "LPUSHX"
const PERSIST = "PERSIST"
const RPUSHX = "RPUSHX"
const ECHO = "ECHO"
const LINSERT = "LINSERT"
const DEBUG = "DEBUG"
const BRPOPLPUSH = "BRPOPLPUSH"
const SETBIT = "SETBIT"
const GETBIT = "GETBIT"
const BITPOS = "BITPOS"
const SETRANGE = "SETRANGE"
const GETRANGE = "GETRANGE"
const EVAL = "EVAL"
const EVALSHA = "EVALSHA"
const SCRIPT = "SCRIPT"
const SLOWLOG = "SLOWLOG"
const OBJECT = "OBJECT"
const BITCOUNT = "BITCOUNT"
const BITOP = "BITOP"
const SENTINEL = "SENTINEL"
const DUMP = "DUMP"
const RESTORE = "RESTORE"
const PEXPIRE = "PEXPIRE"
const PEXPIREAT = "PEXPIREAT"
const PTTL = "PTTL"
const INCRBYFLOAT = "INCRBYFLOAT"
const PSETEX = "PSETEX"
const CLIENT = "CLIENT"
const TIME = "TIME"
const MIGRATE = "MIGRATE"
const HINCRBYFLOAT = "HINCRBYFLOAT"
const SCAN = "SCAN"
const HSCAN = "HSCAN"
const SSCAN = "SSCAN"
const ZSCAN = "ZSCAN"
const WAIT = "WAIT"
const CLUSTER = "CLUSTER"
const ASKING = "ASKING"
const PFADD = "PFADD"
const PFCOUNT = "PFCOUNT"
const PFMERGE = "PFMERGE"
const READONLY = "READONLY"
const GEOADD = "GEOADD"
const GEODIST = "GEODIST"
const GEOHASH = "GEOHASH"
const GEOPOS = "GEOPOS"
const GEORADIUS = "GEORADIUS"
const GEORADIUSBYMEMBER = "GEORADIUSBYMEMBER"
const BITFIELD = "BITFIELD"
请求参数进行编码:
package handler
import "protocol"
func HandleMultiBulkRequest(key string, elements []string) [][]byte{
bytes := make([][]byte, len(elements)+1)
bytes[0] = protocol.SafeEncode(key)
for i := 0; i < len(elements); i++ {
bytes[i+1] = protocol.SafeEncode(elements[i])
}
return bytes
}
func HandleBulkRequest(elements []string) [][]byte{
bytes := make([][]byte, len(elements))
for i := 0; i < len(elements); i++ {
bytes[i] = protocol.SafeEncode(elements[i])
}
return bytes
}
package protocol
import "strconv"
func SafeEncode(arg string) []byte {
return []byte(arg)
}
func SafeEncodeInt(arg int64) []byte {
return SafeEncode(strconv.FormatInt(arg, 10))
}
消息发送模块:
package client
import (
"net"
"bytes"
"protocol"
"strconv"
"fmt"
"os"
)
func SendCommand(conn *net.TCPConn, cmd string, a ...[]byte) string {
var buffer bytes.Buffer
buffer.Write(protocol.SafeEncode(protocol.ASTERISKBYTE))
buffer.Write(protocol.SafeEncode(strconv.Itoa(len(a) + 1)))
buffer.Write(protocol.SafeEncode(protocol.CRLF))
buffer.Write(protocol.SafeEncode(protocol.DOLLARBYTE))
buffer.Write(protocol.SafeEncode(strconv.Itoa(len(cmd))))
buffer.Write(protocol.SafeEncode(protocol.CRLF))
buffer.Write(protocol.SafeEncode(cmd))
buffer.Write(protocol.SafeEncode(protocol.CRLF))
for _, arg := range a {
buffer.Write(protocol.SafeEncode(protocol.DOLLARBYTE))
buffer.Write(protocol.SafeEncode(strconv.Itoa(len(arg))))
buffer.Write(protocol.SafeEncode(protocol.CRLF))
buffer.Write(arg)
buffer.Write(protocol.SafeEncode(protocol.CRLF))
}
return send(conn, buffer)
}
func send(conn *net.TCPConn, content bytes.Buffer) string {
//send to server
_, err := conn.Write(content.Bytes())
if err != nil {
fmt.Println(conn.RemoteAddr().String(), "server response")
os.Exit(1)
}
buffer := make([]byte, 1024)
//receive server info
msg, err := conn.Read(buffer)
if err != nil {
fmt.Println(conn.RemoteAddr().String(), "server response:"+err.Error())
os.Exit(1)
}
return string(buffer[:msg])
}
消息接收处理模块:
package handler
import (
"strings"
"protocol"
"fmt"
"github.com/emirpasic/gods/lists/arraylist"
)
/**
* 处理redis响应的结果
* 1、复杂的*...
* 2、:数字
* 3、简单的$...
* 4、+OK
* 5、-ERR(WRONGTYPE...)
*/
func HandleReply(result string) (interface{}, error) {
if strings.HasPrefix(result, protocol.MINUS_BYTE) {
return handleMinusReply(result)
}
if strings.HasPrefix(result, protocol.PLUSBYTE) {
return handlePlusReply(result)
}
if strings.HasPrefix(result, protocol.COLON_BYTE) {
return handleColonReply(result)
}
if strings.HasPrefix(result, protocol.DOLLARBYTE) {
return handleDollarReply(result)
}
if strings.HasPrefix(result, protocol.ASTERISKBYTE) {
return HandleAsteriskReply(result)
}
return nil, fmt.Errorf("reply handle err")
}
func HandleAsteriskReply(result string) (interface{}, error) {
array := strings.Split(result, protocol.CRLF)
results := arraylist.New()
for i := 1; i < len(array)-1; i++ {
if array[i] == protocol.NONEXIST {
results.Add(nil)
continue
}
results.Add(array[i+1])
i++
if i > len(array)-2 {
break
}
}
return results.Values(), nil
}
func handlePlusReply(result string) (interface{}, error) {
if result != protocol.OK {
return nil, fmt.Errorf(result)
}
return strings.ReplaceAll(strings.ReplaceAll(result, protocol.CRLF, protocol.BLANK), protocol.PLUSBYTE, protocol.BLANK), nil
}
func handleDollarReply(result string) (interface{}, error) {
if strings.HasPrefix(result, protocol.NONEXIST) {
return nil, nil
}
if !strings.HasPrefix(result, protocol.DOLLARBYTE) {
return nil, fmt.Errorf(result)
}
array := strings.Split(result, protocol.CRLF)
return array[1], nil
}
func handleColonReply(result string) (interface{}, error) {
return strings.ReplaceAll(strings.ReplaceAll(result, protocol.CRLF, protocol.BLANK), protocol.COLON_BYTE, protocol.BLANK), nil
}
func handleMinusReply(result string) (interface{}, error) {
return strings.ReplaceAll(strings.ReplaceAll(result, protocol.CRLF, protocol.BLANK), protocol.MINUS_BYTE, protocol.BLANK), nil
}
项目地址: