This is a creation in Article, where the information may have evolved or changed.
Hu
Write in front
According to the first theory, this paper implements the most streamlined single-machine forwarding version based on Redis Client Protocol. Does not include connection pooling, network timeouts, command detection, clustering, performance statistics, and service registration functions.
Archer
The version of the Proxy named Archer, meaning archer, familiar with the War3 old players must know, three bow hand is very powerful. Follow-up development is also based on this version, the code is interesting to download by themselves.
Https://github.com/dongzerun/archer
Data
For simple Strings The first byte of the reply is "+"
For Errors the first byte of the reply is "-"
For integers the first byte of the reply is ":"
For Bulk Strings The first byte of the reply is "$"
For Arrays the first byte of the reply is "*"
Redis protocol is relatively simple, with 5 types, to achieve a common interface
Type Resp Interface {
Encode () []byte//Generate binary data that satisfies the Client protocol
String () string//returns string data for Debug
Type () string//Tag type: Simpleresp, Errorresp, Intresp, Bulkresp, Arrayresp
}
Five types of Simpleresp, Errorresp, Intresp, Bulkresp, and Arrayresp are combined by BASERESP.
Type Baseresp struct {
Rtype string//RESP type
Args [][]byte//For commands, General Args[0] is the command name, Args[1] is key
}
Network protocol message sending and receiving
In terms of service, stable and efficient network protocol messaging is particularly important. But Redis is simple enough to read data from the Socket, according to the first letter to determine the type, follow-up and send and receive fixed-length data. But there are two points to note:
1. Redis supports direct sending of ping\r\n or quit\r\n, which means that the first byte, in addition to the +-$:*, may be p or Q
2. Binary security, because may carry \ r \ n, collect data cannot use Readbytes (\ n), but to read in length, and then read fixed-length fixed-length data
The function (Parser.go:ReadProtocol) is relatively simple, with the comment blank line less than 100.
Pipeline Design
What is Pipeline? is to send and receive asynchronously. The client sends commands in bulk, and then batches the packets, or two threads, one for sending commands, one for reading commands, and for programs with high latency requirements, consider using Pipeline.
Single-Machine Redis has no controversy, but in the cluster mode, the back-end node processing commands are different, the order of Pipeline send and receive commands can not be chaotic, the design requirements are high. The simplest way to think of it is to increase the Sequence Id and rearrange the Proxy before returning the data.
Type Session struct {//Session.go omit unnecessary struct member
RESPs Chan Resp//Response Buffer Channel
Cmds Chan *arrayresp//Command Buffer Channel
}
Client each connection is assigned a Session, open three Goroutine:writeloop, Dispatch, Readloop, respectively corresponding write response, distribution command, read command. This is implemented in the Proxy layer Pipeline, buffer size default 4096, can be set at startup, due to the session level, should not be set too large.
Pressure measurement performance
Commands are the same, 100 concurrent single 1000000 requests
Redis-benchmark-h localhost-n 1000000-c 100-r 20-q-P 6379
Single-Machine pressure test Redis
ping_inline:80153.90 Requests per second
ping_bulk:81327.27 Requests per second
set:42105.26 Requests per second
get:42147.86 Requests per second
incr:73163.59 Requests per second
lpush:81752.77 Requests per second
lpop:82196.28 Requests per second
sadd:80925.79 Requests per second
spop:81866.55 Requests per second
Lpush (needed to benchmark Lrange): 44499.82 Requests per second
LRANGE_100 (first elements): 27935.30 requests per second
lrange_300 (first elements): 17784.42 requests per second
lrange_500 (first elements): 11870.28 requests per second
lrange_600 (first elements): 10386.80 requests per second
MSET (keys): 33320.01 Requests per second
Proxy Tcp_nodelay=true
ping_inline:83542.19 Requests per second
ping_bulk:82973.78 Requests per second
set:37010.99 Requests per second
get:41614.65 Requests per second
incr:64412.24 Requests per second
lpush:55081.24 Requests per second
lpop:67272.12 Requests per second
sadd:56821.41 Requests per second
spop:40950.04 Requests per second
Lpush (needed to benchmark Lrange): 40146.13 Requests per second
LRANGE_100 (first elements): 23648.49 requests per second
lrange_300 (first elements): 9001.06 requests per second
lrange_500 (first elements): 6696.13 requests per second
lrange_600 (first elements): 5235.33 requests per second
MSET (keys): 31629.55 Requests per second
Proxy Tcp_nodelay=false
ping_inline:83187.76 Requests per second
ping_bulk:80749.35 Requests per second
set:40062.50 Requests per second
get:49862.88 Requests per second
incr:73099.41 Requests per second
lpush:71942.45 Requests per second
lpop:69309.67 Requests per second
sadd:60150.38 Requests per second
spop:39624.36 Requests per second
Lpush (needed to benchmark Lrange): 42016.81 Requests per second
LRANGE_100 (first elements): 26281.21 requests per second
lrange_300 (first elements): 9603.38 requests per second
lrange_500 (first elements): 6552.95 requests per second
lrange_600 (first elements): 4945.60 requests per second
MSET (keys): 29850.75 Requests per second
This performance is quite satisfactory, the Go network connection default Tcp_nodelay = True, after closing found that in the packet throughput has improved, for the large package is not obvious, or even low.
Pprof
Turn on Pprof View and the final overhead will fall to net. Conn read-write system call. The students in the group give the method: the merge request, the principle is to reduce the number of system calls, but the current scene may not be suitable. This can be done offline or insensitive to latency requirements.
About Tcp_nodelay
Nagle's algorithm is to solve the problem of the network packet overhead, if the sending side to send a packet containing a small number of characters (in general, after the uniform length is smaller than the MSS packet for the packet, in contrast, the length is equal to the MSS packet for the large package, for some contrast, There is a package, that is, longer than the packet length, but less than one MSS of the package, the sender will first send the packet out, and the subsequent arrival of a small number of character data are cached and not immediately sent, until the receiving end of the previous packet message segment ACK confirmation, or the current character belongs to emergency data, or accumulate a certain amount of data (such as the cached character data has reached the maximum length of the packet segment), and so on, and so on a large number of data packets sent out.
By default, MSS 536 bytes, for Redis service, the response packet has a minimum of 5 bytes ($-1\r\n), and the MySQL Ok_header package is less than 11 bytes. The database belongs to the OLTP scene, the request time delay is very small, from this aspect to see does not have the righteousness close tcp_nodelay, certainly will encounter the unexpected BUG.
In addition, for the delay requirements are not high, offline and long connection push service, personal feeling can be closed.
The following articles are worth reference
1. Mysterious 40 millisecond delay with Tcp_nodelay
2. Nginx about Tcp_nodelay settings
3. Nagle Network Congestion Control algorithm
4. Kingshard Performance Optimization Network Chapter
5. MSS
Conclusion
This is the most streamlined version, the skeleton is there, the next thing to do on the Dispatch, to handle the routing and ask MOVE requests, and the dynamic perception after Failover.
Previous time by the Langya list Brush screen, recommended Hu an old song "Carefree Sigh." At that time he was not in a car accident, then he was Li Carefree, love his Zhaoling son.