本文基于
redis_version:6.2.5
用法
設(shè)置一個(gè)redis實(shí)例支持的最大連接數(shù)。
maxclients 10000
?
注意事項(xiàng):
- 默認(rèn)情況下砸彬,一個(gè)實(shí)例支持
1萬
條連接疯攒。 - 如果你的服務(wù)是redis-cluster集群托享,
最大連接數(shù)會與內(nèi)部連接數(shù)共享
俊卤。例:3主3從的cluster集群(6個(gè)實(shí)例)。每個(gè)實(shí)例會創(chuàng)建 (6-1)*2=10個(gè)內(nèi)部連接血筑。假設(shè)每個(gè)實(shí)例的maxclients配置為100绘沉,那么100條連接中會被占用10條。如果你的cluster集群很龐大豺总,尤其要注意這一點(diǎn)车伞。 - 如果你要設(shè)置的
maxclients再加上32
超過資源限制(例:我要設(shè)置maxclient=1000,會用1032去判斷是否超過資源限制)喻喳,redis會根據(jù)內(nèi)部邏輯去設(shè)置實(shí)際可以支持的maxclients另玖。具體看源碼部分。
?
實(shí)操
配置一個(gè)三主的cluster集群表伦,分別為6370
日矫,6380
,6390
绑榴。
$ redis-cli --cluster check 127.0.0.1:6370
127.0.0.1:6370 (3cd5de09...) -> 0 keys | 5461 slots | 0 slaves.
127.0.0.1:6390 (d4ea04d6...) -> 0 keys | 5461 slots | 0 slaves.
127.0.0.1:6380 (fad5592c...) -> 0 keys | 5462 slots | 0 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:6370)
M: 3cd5de0940dbbed9dcf0f3d9541464664dd05c3d 127.0.0.1:6370
slots:[0-5460] (5461 slots) master
M: d4ea04d67e5e7dc8e1bae6e586c3999edbd99c16 127.0.0.1:6390
slots:[10923-16383] (5461 slots) master
M: fad5592c85a84e2d97ea3158468594c46720c28d 127.0.0.1:6380
slots:[5461-10922] (5462 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
內(nèi)部連接的樣子見下圖。
cluster集群內(nèi)部連接圖示.png
以監(jiān)聽在6380的實(shí)例為例子盈魁,它的內(nèi)部連接數(shù)為(3-1)X 2 = 4
條(后4行)
$ lsof -p 2688
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
redis-ser 2688 root cwd DIR 0,173 4096 396203 /root/redis-cluster
...忽略部分...
redis-ser 2688 root 6u IPv4 571968 0t0 TCP localhost:6380 (LISTEN)
redis-ser 2688 root 7wW REG 0,173 370 397395 /root/redis-cluster/nodes-6380.conf
redis-ser 2688 root 8u IPv4 571969 0t0 TCP localhost:16380 (LISTEN)
redis-ser 2688 root 10u IPv4 571977 0t0 TCP localhost:46051->localhost:16370 (ESTABLISHED)
redis-ser 2688 root 11u IPv4 572704 0t0 TCP localhost:16380->localhost:44247 (ESTABLISHED)
redis-ser 2688 root 12u IPv4 569320 0t0 TCP localhost:45051->localhost:16390 (ESTABLISHED)
redis-ser 2688 root 13u IPv4 569321 0t0 TCP localhost:16380->localhost:40545 (ESTABLISHED)
設(shè)置下maxclients=5(設(shè)置完畢后不要關(guān)閉該窗口和該連接翔怎,此時(shí)連接數(shù)是4個(gè)內(nèi)部連接+當(dāng)前連接=5條)
$ redis-cli -p 6380
127.0.0.1:6380> config get maxclients
1) "maxclients"
2) "10000"
127.0.0.1:6380> config set maxclients 5
OK
127.0.0.1:6380> config get maxclients
1) "maxclients"
2) "5"
127.0.0.1:6380>
另啟一個(gè)窗口,建立連接發(fā)送請求報(bào)連接超限杨耙。因?yàn)?+1=6超過了maxclients=5赤套。
$ redis-cli -p 6380
127.0.0.1:6380> get c
(error) ERR max number of clients + cluster connections reached
?
redis源碼
cluster port = 基礎(chǔ)端口+1W(例子:實(shí)例監(jiān)聽在6379端口,則cluster port為16379)珊膜,其他節(jié)點(diǎn)與cluster port建立內(nèi)部連接容握。
// https://github.com/redis/redis/blob/6.2.6/src/cluster.h
12 #define CLUSTER_PORT_INCR 10000 /* Cluster port = baseport + PORT_INCR */
關(guān)于連接數(shù)的判斷(1101~1109行):
- 如果是cluster集群,則判斷外部鏈接數(shù)(server.clients)+內(nèi)部連接數(shù)(clusterConn)的大小是否超過maxclients并輸出相應(yīng)錯(cuò)誤车柠。
- 非cluster集群只判斷外部鏈接數(shù)的大小是否超過maxclients.并輸出相應(yīng)錯(cuò)誤剔氏。
// https://github.com/redis/redis/blob/6.2.6/src/networking.c
1082 static void acceptCommonHandler(connection *conn, int flags, char *ip) {
...忽略部分...
1101 if (listLength(server.clients) + getClusterConnectionsCount()
1102 >= server.maxclients)
1103 {
1104 char *err;
1105 if (server.cluster_enabled)
1106 err = "-ERR max number of clients + cluster "
1107 "connections reached\r\n";
1108 else
1109 err = "-ERR max number of clients reached\r\n";
1110
1111 /* That's a best effort error message, don't check write errors.
1112 * Note that for TLS connections, no handshake was done yet so nothing
1113 * is written and the connection will just drop. */
1114 if (connWrite(conn,err,strlen(err)) == -1) {
1115 /* Nothing to do, Just to avoid the warning... */
1116 }
1117 server.stat_rejected_conn++;
1118 connClose(conn);
1119 return;
1120 }
內(nèi)部連接數(shù)的獲取:
- 如果是cluster集群模式竹祷,返回(節(jié)點(diǎn)數(shù)-1)*2谈跛。節(jié)點(diǎn)數(shù)包含主節(jié)點(diǎn)與從節(jié)點(diǎn)。
- 普通模式塑陵,返回0感憾。
// https://github.com/redis/redis/blob/6.2.6/src/networking.c
779 unsigned long getClusterConnectionsCount(void) {
780 /* We decrement the number of nodes by one, since there is the
781 * "myself" node too in the list. Each node uses two file descriptors,
782 * one incoming and one outgoing, thus the multiplication by 2. */
783 return server.cluster_enabled ?
784 ((dictSize(server.cluster->nodes)-1)*2) : 0;
785 }
maxclients被設(shè)置為實(shí)際支持的數(shù)值
// https://github.com/redis/redis/blob/6.2.6/src/config.c
// CONFIG_MIN_RESERVED_FDS = 32
// 這個(gè)數(shù)字是為redis持久化、偵聽套接字令花、日志文件等額外操作保留的文件描述符數(shù)
void adjustOpenFilesLimit(void) {
// maxfiles設(shè)置為maxclients+32
rlim_t maxfiles = server.maxclients+CONFIG_MIN_RESERVED_FDS;
struct rlimit limit;
// getrlimit是一個(gè)系統(tǒng)調(diào)用阻桅,返回rlim_cur凉倚,rlim_max
// rlim_cur是進(jìn)程資源的實(shí)際限制
// rlim_max是rlim_cur限制的上限,rlim_cur不可超過rlim_max
if (getrlimit(RLIMIT_NOFILE,&limit) == -1) { // 獲取失敗
serverLog(LL_WARNING,"Unable to obtain the current NOFILE limit (%s), assuming 1024 and setting the max clients configuration accordingly.",
strerror(errno));
// maxclients被設(shè)置為 1024-32
server.maxclients = 1024-CONFIG_MIN_RESERVED_FDS;
} else {
// 保存下來當(dāng)前獲取到的限制數(shù)
rlim_t oldlimit = limit.rlim_cur;
// redis所需要的fd數(shù)量超過了獲取到的限制數(shù)
if (oldlimit < maxfiles) {
rlim_t bestlimit;
int setrlimit_error = 0;
// 先設(shè)置bestlimit為redis所需要的fd數(shù)量
bestlimit = maxfiles;
// 如果redis所需要的fd數(shù)量一直大于獲取到的限制數(shù)嫂沉,就一直循環(huán)
while(bestlimit > oldlimit) {
// 每次循環(huán)將redis所需要的fd數(shù)量減16
rlim_t decr_step = 16;
limit.rlim_cur = bestlimit;
limit.rlim_max = bestlimit;
// 通過系統(tǒng)調(diào)用稽寒,去重新設(shè)置限制數(shù),設(shè)置成功跳出while循環(huán)
if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;
setrlimit_error = errno;
// 如果redis所需要的fd數(shù)量比16還小了
if (bestlimit < decr_step) {
// 將redis所需要的fd數(shù)量設(shè)置為最初我們拿到的限制數(shù)并跳出while循環(huán)
bestlimit = oldlimit;
break;
}
// 遞減16
bestlimit -= decr_step;
}
// 當(dāng)前的redis所需要的fd數(shù)量比最開始拿到的限制數(shù)還小输瓜,還是用之前的限制數(shù)吧
if (bestlimit < oldlimit) bestlimit = oldlimit;
// 已經(jīng)處理過的redis所需要的fd數(shù)量比最開始的redis所需要的fd數(shù)量小
if (bestlimit < maxfiles) {
unsigned int old_maxclients = server.maxclients;
// 設(shè)置maxclients
server.maxclients = bestlimit-CONFIG_MIN_RESERVED_FDS;
// 如果經(jīng)過一番操作已經(jīng)處理過的redis所需要的fd數(shù)量比17還小瓦胎,直接掛掉!
if (bestlimit <= CONFIG_MIN_RESERVED_FDS) {
serverLog(LL_WARNING,"Your current 'ulimit -n' "
"of %llu is not enough for the server to start. "
"Please increase your open file limit to at least "
"%llu. Exiting.",
(unsigned long long) oldlimit,
(unsigned long long) maxfiles);
exit(1);
}
// 忽略部分尤揣,一些日志打印
} else {
// 忽略部分搔啊,一些日志打印
}
}
}
}
?
原生注釋
# Set the max number of connected clients at the same time. By default
# this limit is set to 10000 clients, however if the Redis server is not
# able to configure the process file limit to allow for the specified limit
# the max number of allowed clients is set to the current file limit
# minus 32 (as Redis reserves a few file descriptors for internal uses).
#
# Once the limit is reached Redis will close all the new connections sending
# an error 'max number of clients reached'.
#
# IMPORTANT: When Redis Cluster is used, the max number of connections is also
# shared with the cluster bus: every node in the cluster will use two
# connections, one incoming and another outgoing. It is important to size the
# limit accordingly in case of very large clusters.
#
# maxclients 10000
本文屬于原創(chuàng),首發(fā)于微信公眾號【小易哥學(xué)呀學(xué)】北戏,如需轉(zhuǎn)載請后臺留言负芋。
加微信入交流群。vx:17610015120