2020/7/17 java 異常
背景
今天在學習mysql時,看到一個案例,大體來說,就是客戶端報Too many connections
。但是,客戶端的連線池,限制為了200,兩個客戶端java程式,那也才400,然後mysql配置了800的連線。
mysql是在my.cnf
中配置了:
[[email protected] CAD_OneKeyDeploy]# vim /etc/my.cnf
[mysqld]
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
max_connections=800
symbolic-links = 0
這個不應該吧,我最多建立400個連線,資料庫設定了最大連線為800,結果就報:Too many connections。
然後在mysql上執行:
SHOW VARIABLES LIKE 'max_connections'
發現結果是200左右,說明設定了沒生效啊。
結果在mysql的啟動日誌:
Could not increase number of max_open_files to more than mysqld (request: 65535)
Changed limits: max_connections: 214 (requested 2000)
然後案例中的博主就去修改了:
ulimit -HSn 65535
vim /etc/security/limits.conf
我跟著操作了一把,結果,發現並不是那麼回事。
也就是說,我照著做了,沒生效,那,到底怎麼回事?
這裡,我總結了一些資料,大家先看看,然後最後我會說明,怎麼去正確設定一個程式的最大檔案數量。
程式的最大檔案數量,受到多方面影響:
登入shell後,手動啟動的程式
影響因素包括:
- 作業系統整體的、所有程式可以開啟的檔案數量總和(由
/proc/sys/fs/file-max
控制); - 該shell使用者,可以開啟的最大檔案數量(由
/etc/security/limits.conf
控制); - 程式本身的最大檔案數量限制(可以通過os提高的api控制,如setrlimit)
開機自動啟動的程式
影響因素包括:
- 作業系統整體的、所有程式可以開啟的檔案數量總和(由
/proc/sys/fs/file-max
- 如果由systemd方式啟動,則systemd的service檔案中可以進行限制。
受知識所限,目前知道的就上面這些,shell據我所知,還分log和no-log shell,不是很懂,先跳過。
檢視某個已執行程式的資源限制
在經過一番修改後,想知道修改是否生效時,可以通過如下方式:
先把程式執行起來,檢視其最終生效的資源限制的辦法,就是如下:
[[email protected] ~]# cat /proc/6660/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 3795 3795 processes
-----------------------------如下
Max open files 5000 5000 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 3795 3795 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us
os級別,如何檢視與設定
檢視可通過如下命令:
[[email protected] ~]# cat /proc/sys/fs/file-max
95086
這裡其實就是查看了/proc下的某個檔案,那麼這個檔案的意思是啥呢?檢視幫助:
man proc
...
/proc/sys/fs/file-max
This file defines a system-wide limit on the number of open files for all processes. (See also setrlimit(2), which can be used by a process to set the per-process limit, RLIMIT_NOFILE, on the number of files it may open.) If you get lots of error messages in the kernel log about running out of file handles (look for "VFS: file-max limit <number> reached"), try increasing this value:
echo 100000 > /proc/sys/fs/file-max
The kernel constant NR_OPEN imposes an upper limit on the value that may be placed in file-max.
If you increase /proc/sys/fs/file-max, be sure to increase /proc/sys/fs/inode-max to 3-4 times the new value of /proc/sys/fs/file-max, or you will run out of inodes.
Privileged processes (CAP_SYS_ADMIN) can override the file-max limit.
簡單翻譯下,該檔案定義了一個作業系統級別的,最大可以開啟的檔案數量限制(針對所有程式加起來)。
針對一個程式,去設定針對每個程式的最大可開啟檔案數量的限制,可以檢視setrlimit
中的RLIMIT_NOFILE
。
如果要修改,則:
echo 100000 > /proc/sys/fs/file-max
修改後,重啟os生效。
coding時,呼叫api進行資源限制
可通過man檢視:
man setrlimit
GETRLIMIT(2) Linux Programmer's Manual GETRLIMIT(2)
NAME
getrlimit, setrlimit, prlimit - get/set resource limits
SYNOPSIS
#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
The getrlimit() and setrlimit() system calls get and set resource limits respectively. Each resource has an associated soft and hard limit, as defined by the rlimit structure:
struct rlimit {
rlim_t rlim_cur; /* Soft limit */
rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */
};
注意,這個都是針對當前程式的,即,如果你用c語言程式設計,基本就會直接和這個打交道。
RLIMIT_NPROC
The maximum number of processes (or, more precisely on Linux, threads) that can be created for the real user ID of the calling process. Upon encountering this limit, fork(2) fails with the error EAGAIN.
我在redis的原始碼中,見過相關的api呼叫。
針對單個使用者/使用者組的資源限制(修改後,重新登入shell後,啟動的程式生效)
檢視:
vim /etc/security/limits.conf
如果要修改,則在上述檔案中,增加如下兩行,前面的萬用字元,表示匹配任意使用者和group
* soft nofile 65535
* hard nofile 65535
這裡把任意使用者的最大檔案數量,改為了65535.
另外,我這裡看了下elastic search的官網,因為我記得它就是比較繁瑣,需要改這些,
https://www.elastic.co/guide/en/elasticsearch/reference/master/setting-system-settings.html#ulimit
其中有如下一段話:
On Linux systems, persistent limits can be set for a particular user by editing the /etc/security/limits.conf file. To set the maximum number of open files for the elasticsearch user to 65,535, add the following line to the limits.conf file:
elasticsearch - nofile 65535
This change will only take effect the next time the elasticsearch user opens a new session.
意思是,在linux上,針對某個使用者的持久化資源,可以通過設定/etc/security/limits.conf。
比如,要設定elasticsearch使用者的最大檔案數量為65535,需要增加如下行:
elasticsearch - nofile 65535
注意,上面還說了,該change只在下次 elasticsearch 開啟一個新session時生效。
上面那個elasticsearch的檔案,真心不錯,大家可以看看。
所以,這個是shell級別的,修改後,要重新登入shell,該修改檔案才生效,同時,要在登入shell後,啟動的程式才有用。
簡單測試
我們修改該檔案為:
* soft nofile 6666
* hard nofile 6666
然後啟動一個程式,監聽1235埠:
[[email protected] ~]# nc -l 1235
然後在另外一個shell中,檢視該程式的資源資訊:
[[email protected] ~]# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:1235 0.0.0.0:* LISTEN 7015/nc
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 6632/sshd
tcp6 0 0 :::3306 :::* LISTEN 6670/mysqld
tcp6 0 0 :::1235 :::* LISTEN 7015/nc
tcp6 0 0 :::22 :::* LISTEN 6632/sshd
[[email protected] ~]# cat /proc/7015/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 3795 3795 processes
Max open files 65535 65535 files
這裡的最後一行可以發現,檔案數量為65535.
然後我們關閉shell,重新登入,此時,就會去執行新的/etc/security/limits.conf
,設定為6666,然後我們啟動個程式:
[[email protected] ~]# nc -l 1236
另一個shell中檢視該程式:
[[email protected] ~]# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:1236 0.0.0.0:* LISTEN 7089/nc
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 6632/sshd
tcp6 0 0 :::3306 :::* LISTEN 6670/mysqld
tcp6 0 0 :::1236 :::* LISTEN 7089/nc
tcp6 0 0 :::22 :::* LISTEN 6632/sshd
[[email protected] ~]# cat /proc/7089/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 3795 3795 processes
Max open files 6666 6666 files
可以看到,已經變成6666了。
總結一下,該方式,修改檔案後,需要重啟shell後生效,且需要是在該shell中啟動的程式才生效。
ulimit 方式(不推薦)
注意,該種方式,僅當前shell生效,且,僅在修改後,啟動的程式才有效。
比如,我們這裡在shell1下,修改:
[[email protected] ~]# ulimit -HSn 9999
[[email protected] ~]# nc -l 1234
然後啟動了一個程式,監聽1234埠。
然後我們看看該程式的資源資訊:
[[email protected] ~]# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:1234 0.0.0.0:* LISTEN 6982/nc
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 6632/sshd
tcp6 0 0 :::3306 :::* LISTEN 6670/mysqld
tcp6 0 0 :::1234 :::* LISTEN 6982/nc
tcp6 0 0 :::22 :::* LISTEN 6632/sshd
[[email protected] ~]# cat /proc/6982/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 3795 3795 processes
Max open files 9999 9999 files
然後,在我關閉該shell,重新登入shell進來後,執行
[[email protected] ~]# nc -l 1234
此時再去檢視:
[[email protected] ~]# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:1234 0.0.0.0:* LISTEN 7007/nc
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 6632/sshd
tcp6 0 0 :::3306 :::* LISTEN 6670/mysqld
tcp6 0 0 :::1234 :::* LISTEN 7007/nc
tcp6 0 0 :::22 :::* LISTEN 6632/sshd
[[email protected] ~]# cat /proc/7007/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 3795 3795 processes
--------------------------- 1
Max open files 65535 65535 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
注意,這裡1處顯示,已經變成了65535.
注意這個的限制,是設定了該值後,在此之後啟動的程式才有用;而且對於開機自啟的程式,應該是沒什麼用的。
我不建議這種方式。
ulimit [-HSTabcdefilmnpqrstuvx [limit]]
Provides control over the resources available to the shell and to processes started by it, on systems that allow such control.
如果要長久生效,可以這樣:
echo ulimit -SHn 65535 >> /etc/profile
但是,這個是隻針對由該shell啟動的程式。比如開機啟動的那些,比如mysql,應該是沒法用這個控制的。
當mysql使用systemd方式開機自啟時,怎麼正確修改
我們這邊的mysql,是使用rpm安裝的,安裝的時候,就用瞭如下命令:
sed -i '/\# End of file/i * soft nofile 65535' /etc/security/limits.conf
sed -i '/\# End of file/i * hard nofile 65535' /etc/security/limits.conf
echo ulimit -SHn 65535 >> /etc/profile
source /etc/profile
同時,也修改了其配置檔案:
/etc/my.cnf
[[email protected] CAD_OneKeyDeploy]# vim /etc/my.cnf
[mysqld]
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
max_connections=10000
symbolic-links = 0
比如這裡的max_connections=10000。
但是,開機重啟後,使用:
[[email protected] CAD_OneKeyDeploy]# cat /proc/6677/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 3795 3795 processes
// 1------------------
Max open files 5000 5000 files
Max locked memory 65536 65536 bytes
上面1處,最大開啟檔案是5000,這是為啥呢?說明沒效果啊。
為啥呢,找了半天,發現我們的mysql是通過systemd方式啟動的。
[[email protected] CAD_OneKeyDeploy]# systemctl status mysqld
● mysqld.service - MySQL Server
// 1
Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
Active: active (running) since Sat 2020-07-18 10:58:07 CST; 44min ago
Docs: man:mysqld(8)
http://dev.mysql.com/doc/refman/en/using-systemd.html
Process: 6674 ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid $MYSQLD_OPTS (code=exited, status=0/SUCCESS)
Process: 6636 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS)
Main PID: 6677 (mysqld)
CGroup: /system.slice/mysqld.service
└─6677 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
Jul 18 10:58:05 localhost.localdomain systemd[1]: Starting MySQL Server...
Jul 18 10:58:07 localhost.localdomain systemd[1]: Started MySQL Server.
上面1處,指定了該service的位置:
/usr/lib/systemd/system/mysqld.service
.
我們開啟該檔案看一下:
...
[Service]
User=mysql
Group=mysql
Type=forking
PIDFile=/var/run/mysqld/mysqld.pid
# Disable service start and stop timeout logic of systemd for mysqld service.
TimeoutSec=0
# Execute pre and post scripts as root
PermissionsStartOnly=true
# Needed to create system tables
ExecStartPre=/usr/bin/mysqld_pre_systemd
# Start main service
ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid $MYSQLD_OPTS
# Use this to switch malloc implementation
EnvironmentFile=-/etc/sysconfig/mysql
# 1 Sets open_files_limit
LimitNOFILE = 5000
注意這裡的最後一行,
# 1 Sets open_files_limit
LimitNOFILE = 5000
應該就是這個的問題了。
我這裡改成10000,然後執行:
systemctl daemon-reload
然後重啟mysql:
systemctl restart mysqld
重新檢視最大資源限制:
[[email protected] CAD_OneKeyDeploy]# cat /proc/19617/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 3795 3795 processes
Max open files 10000 10000 files
看最後一行,已經改成10000了。
重啟後測試,依然是10000,說明修改成功了。
檢視/var/log/mysql.log可以發現如下字樣:
2020-07-18T03:48:11.235058Z 0 [Warning] Changed limits: max_open_files: 10000 (requested 50000)
這裡,因為我們在systemd的service中限制成了10000,所以這裡就顯示成10000了。
總結
os級別的,必須改後重啟;
ulimit方式,極度不推薦,只能是臨時修改;
/etc/security/limits.conf 方式,使用者級別,修改後,使用者需重登陸shell,檔案才生效;此後啟動的程式才生效。
systemd方式的開機自啟動程式,修改對應的service檔案才有效。