Redis uses tcp port 6379 by default. The protocol is in text line format rather than binary format. Each line ends with rn, which is easy to understand. For more information, see protocolspecification.html. The commands published to redis have the following return formats (for non-existent values,-1 is returned. At this time, clientlibrary should return the appropriate n
Redis uses tcp port 6379 by default. The Protocol is in the text line format rather than the binary format. Each line ends with \ r \ n, which is easy to understand. Refer to protocolspecification.html. The command published to redis has the following return formats (for non-existent values,-1 is returned, and the client library should return the appropriate n
Redis uses tcp port 6379 by default. The protocol is in text line format rather than binary format. Each line ends with "\ r \ n", which is easy to understand.
Refer to protocolspecification.html. The command published to redis has the following return formats (for non-existent values,-1 is returned, in this case, the client library should return a suitable nil object (such as NULL in C), rather than a NULL String ):
1) The first byte is the character "-" followed by an error message (error reply)
For example, in the lpop command, when the object to be operated is not a linked list, the following error message is returned:
"-ERR Operation against a key holding the wrong kind of value \ r \ n"
2) the first byte is the character "+", followed by a line indicating the execution result (line reply)
For example, after the set command is successfully executed, "+ OK \ r \ n" is returned"
3) The first byte is the character "$", followed by a row, with only one number. This number indicates the number of characters in the next line (if not, the number is-1) (bulk reply)
For example, if the get command is successful, the returned value is similar to "$7 \ r \ nmyvalue". If no value exists, the returned information is "$-1 \ r \ n"
4) The first byte is the character "*", followed by a row, with only one number. This number indicates the number of bulk reply (if not, the number is-1) (multi-bulk reply)
For example, if the lrange command is required to return a value between 0 and 2, the returned value is similar to "* 3 \ r \ n $6 \ r \ nvalue1 \ r \ n $7 \ r \ nmyvalue \ r \ n $5 \ r \ nhello \ r \ n ", the returned information is similar to "*-1 \ r \ n" If no data exists ".
5) the first byte is the character ":" followed by an integer reply)
For example, if the incr command succeeds, the value after the object + 1 is returned.
Client commands are in the following formats. The first string must be a command word. Different parameters are separated by a space:
1) Inline Command: only one line
For example, with the EXISTS command, the byte stream sent by the client is similar to "EXISTS mykey \ r \ n ".
2) Bulk Command: similar to the Return protocol's bulk reply, there are generally two lines, the first line is "Command word parameter a number" in turn, this number indicates the number of characters in the next line
For example, for the SET command, the byte stream sent by the client is similar to "SET mykey 5 \ r \ nhello \ r \ n ".
3) multi-bulk Command: similar to the multi-bulk reply of the Return protocol.
For example, the SET command above, in the multi-bulk protocol, the value is "* 3 \ r \ n $3 \ r \ nSET \ r \ n $5 \ r \ nmykey \ r \ n $5 \ r \ nhello \ r \ n ".
Although some commands send more byte streams than bulk command, it supports any command, supports commands with multiple binary security parameters (only one bulk command is supported ), you can also enable the client library to support new redis commands without modifying the code (as long as the unsupported commands are published in the multi-bulk format ). Redis official documentation also mentions that in the future, only the client may be supported to publish commands in the multi-bulk Command format.
In addition, the client library can issue multiple commands consecutively, instead of releasing new commands until redis returns the execution result of the previous command. This mechanism is called pipelining, most client libraries that support redis support this mechanism. You can refer to this mechanism on your own.
Finally, let's take a look at the related functions used to return information during redis implementation.
Redis uses addReplySds, addReplyDouble, addReplyLongLong, addReplyUlong, addReplyBulkLen, addReplyBulk, and addReplyBulkCString to package different returned information, and finally calls addReply to send the information.
AddReply adds the sending information to the end of the reply linked list of the corresponding redisClient, and uses sendReplyToClient to send the message. SendReplyToClient traverses the reply linked list and sends it in sequence. If you can package reply (server. glueoutputbuf is true), you can use glueReplyBuffersIfNeeded to merge the values in the reply linked list into a buffer zone and send.
static void addReply(redisClient *c, robj *obj) { if (listLength(c->reply) == 0 && (c->replstate == REDIS_REPL_NONE || c->replstate == REDIS_REPL_ONLINE) && aeCreateFileEvent(server.el, c->fd, AE_WRITABLE, sendReplyToClient, c) == AE_ERR) return; if (server.vm_enabled && obj->storage != REDIS_VM_MEMORY) { obj = dupStringObject(obj); obj->refcount = 0; /* getDecodedObject() will increment the refcount */ } listAddNodeTail(c->reply,getDecodedObject(obj));}static void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) { redisClient *c = privdata; int nwritten = 0, totwritten = 0, objlen; robj *o; REDIS_NOTUSED(el); REDIS_NOTUSED(mask); /* Use writev() if we have enough buffers to send */ if (!server.glueoutputbuf && listLength(c->reply) > REDIS_WRITEV_THRESHOLD && !(c->flags & REDIS_MASTER)) { sendReplyToClientWritev(el, fd, privdata, mask); return; } while(listLength(c->reply)) { if (server.glueoutputbuf && listLength(c->reply) > 1) glueReplyBuffersIfNeeded(c); o = listNodeValue(listFirst(c->reply)); objlen = sdslen(o->ptr); if (objlen == 0) { listDelNode(c->reply,listFirst(c->reply)); continue; } if (c->flags & REDIS_MASTER) { /* Don't reply to a master */ nwritten = objlen - c->sentlen; } else { nwritten = write(fd, ((char*)o->ptr)+c->sentlen, objlen - c->sentlen); if (nwritten <= 0) break; } c->sentlen += nwritten; totwritten += nwritten; /* If we fully sent the object on head go to the next one */ if (c->sentlen == objlen) { listDelNode(c->reply,listFirst(c->reply)); c->sentlen = 0; } /* Note that we avoid to send more thank REDIS_MAX_WRITE_PER_EVENT * bytes, in a single threaded server it's a good idea to serve * other clients as well, even if a very large request comes from * super fast link that is always able to accept data (in real world * scenario think about 'KEYS *' against the loopback interfae) */ if (totwritten > REDIS_MAX_WRITE_PER_EVENT) break; } if (nwritten == -1) { if (errno == EAGAIN) { nwritten = 0; } else { redisLog(REDIS_VERBOSE, "Error writing to client: %s", strerror(errno)); freeClient(c); return; } } if (totwritten > 0) c->lastinteraction = time(NULL); if (listLength(c->reply) == 0) { c->sentlen = 0; aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE); }}
The implementation of the client library can be implemented by yourself according to the format described above, or you can read the existing client library for better understanding.
Original article address: redis source code analysis 22-protocol, thanks to the original author for sharing.