• supervisor—进程管理神器


    一、supervisor 简介

    Supervisor 是用 Python 开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon(守护进程)。

    优点:

    • 规范化

      supervisor配置文件中指定子进程的配置文件路径,将所有子进程放在一个目录,并对子进程配置文件类型(后缀)统一要求。

    • 自动重启

      可以设置 supervisor 启动后,自动启动子进程。父进程对子进程进行监控,当子进程误挂掉后,可以自动重启。

    • 功能丰富:

      配置项(自动重启次数、进程数量等)众多,可以根据自己的业务需求选择合适配置项。

    • 命令健全

      提供了重启、开启、关闭等命令选项。

    实际使用场景:

    • 场景一:

      rabbitmq生产消息,生产消息速度和消费消息速度为5:1,明显的消费跟不上生产了,此时,可开多进程去消费此队列。同一消费者脚本,希望开启多个进程,也就是单生产,多消费。并不会造成单条小心的重复消费。

    • 场景二:

      购买商品,生成订单后,十分钟后没有付款自动取消。队列延迟执行,守护进程模式运行消费队列命令。

    二、安装 supervisor

    使用 yum install supervisor 查看:

    [root@VM-8-7-centos wwwroot]# yum install supervisor
    Loaded plugins: fastestmirror, langpacks
    Repository epel is listed more than once in the configuration
    Loading mirror speeds from cached hostfile
    Resolving Dependencies
    --> Running transaction check
    ---> Package supervisor.noarch 0:3.4.0-1.el7 will be installed
    --> Processing Dependency: python-meld3 >= 0.6.5 for package: supervisor-3.4.0-1.el7.noarch
    --> Running transaction check
    ---> Package python-meld3.x86_64 0:0.6.10-1.el7 will be installed
    --> Finished Dependency Resolution
    
    Dependencies Resolved
    
    ==================================================================================================================================================================
     Package                                   Arch                                Version                                    Repository                         Size
    ==================================================================================================================================================================
    Installing:
     supervisor                                noarch                              3.4.0-1.el7                                epel                              498 k
    Installing for dependencies:
     python-meld3                              x86_64                              0.6.10-1.el7                               epel                               73 k
    
    Transaction Summary
    ==================================================================================================================================================================
    Install  1 Package (+1 Dependent package)
    
    Total download size: 571 k
    Installed size: 2.9 M
    Is this ok [y/d/N]: y
    Downloading packages:
    (1/2): python-meld3-0.6.10-1.el7.x86_64.rpm                                                                                                |  73 kB  00:00:00     
    (2/2): supervisor-3.4.0-1.el7.noarch.rpm                                                                                                   | 498 kB  00:00:00     
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------
    Total                                                                                                                             1.0 MB/s | 571 kB  00:00:00     
    Running transaction check
    Running transaction test
    Transaction test succeeded
    Running transaction
    Warning: RPMDB altered outside of yum.
      Installing : python-meld3-0.6.10-1.el7.x86_64                                                                                                               1/2 
      Installing : supervisor-3.4.0-1.el7.noarch                                                                                                                  2/2 
      Verifying  : python-meld3-0.6.10-1.el7.x86_64                                                                                                               1/2 
      Verifying  : supervisor-3.4.0-1.el7.noarch                                                                                                                  2/2 
    
    Installed:
      supervisor.noarch 0:3.4.0-1.el7                                                                                                                                 
    
    Dependency Installed:
      python-meld3.x86_64 0:0.6.10-1.el7                                                                                                                              
    
    Complete!
    [root@VM-8-7-centos wwwroot]# 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    卸载 supervisor:yum -y remove supervisor

    三、supervisor 的配置文件

    supervisor配置文件:/etc/supervisord.conf

    [root@VM-8-7-centos wwwroot]# cat /etc/supervisord.conf
    ; Sample supervisor config file.
    
    [unix_http_server]
    file=/var/run/supervisor/supervisor.sock   ; (the path to the socket file)
    ;chmod=0700                 ; sockef file mode (default 0700)
    ;chown=nobody:nogroup       ; socket file uid:gid owner
    ;username=user              ; (default is no username (open server))
    ;password=123               ; (default is no password (open server))
    
    ;[inet_http_server]         ; inet (TCP) server disabled by default
    ;port=127.0.0.1:9001        ; (ip_address:port specifier, *:port for all iface)
    ;username=user              ; (default is no username (open server))
    ;password=123               ; (default is no password (open server))
    
    [supervisord]
    logfile=/var/log/supervisor/supervisord.log  ; (main log file;default $CWD/supervisord.log)
    logfile_maxbytes=50MB       ; (max main logfile bytes b4 rotation;default 50MB)
    logfile_backups=10          ; (num of main logfile rotation backups;default 10)
    loglevel=info               ; (log level;default info; others: debug,warn,trace)
    pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
    nodaemon=false              ; (start in foreground if true;default false)
    minfds=1024                 ; (min. avail startup file descriptors;default 1024)
    minprocs=200                ; (min. avail process descriptors;default 200)
    ;umask=022                  ; (process file creation umask;default 022)
    ;user=chrism                 ; (default is current user, required if root)
    ;identifier=supervisor       ; (supervisord identifier, default is 'supervisor')
    ;directory=/tmp              ; (default is not to cd during start)
    ;nocleanup=true              ; (don't clean up tempfiles at start;default false)
    ;childlogdir=/tmp            ; ('AUTO' child log dir, default $TEMP)
    ;environment=KEY=value       ; (key value pairs to add to environment)
    ;strip_ansi=false            ; (strip ansi escape codes in logs; def. false)
    
    ; the below section must remain in the config file for RPC
    ; (supervisorctl/web interface) to work, additional interfaces may be
    ; added by defining them in separate rpcinterface: sections
    [rpcinterface:supervisor]
    supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
    
    [supervisorctl]
    serverurl=unix:///var/run/supervisor/supervisor.sock ; use a unix:// URL  for a unix socket
    ;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
    ;username=chris              ; should be same as http_username if set
    ;password=123                ; should be same as http_password if set
    ;prompt=mysupervisor         ; cmd line prompt (default "supervisor")
    ;history_file=~/.sc_history  ; use readline history if available
    
    ; The below sample program section shows all possible program subsection values,
    ; create one or more 'real' program: sections to be able to control them under
    ; supervisor.
    
    ;[program:theprogramname]
    ;command=/bin/cat              ; the program (relative uses PATH, can take args)
    ;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
    ;numprocs=1                    ; number of processes copies to start (def 1)
    ;directory=/tmp                ; directory to cwd to before exec (def no cwd)
    ;umask=022                     ; umask for process (default None)
    ;priority=999                  ; the relative start priority (default 999)
    ;autostart=true                ; start at supervisord start (default: true)
    ;autorestart=true              ; retstart at unexpected quit (default: true)
    ;startsecs=10                  ; number of secs prog must stay running (def. 1)
    ;startretries=3                ; max # of serial start failures (default 3)
    ;exitcodes=0,2                 ; 'expected' exit codes for process (default 0,2)
    ;stopsignal=QUIT               ; signal used to kill process (default TERM)
    ;stopwaitsecs=10               ; max num secs to wait b4 SIGKILL (default 10)
    ;user=chrism                   ; setuid to this UNIX account to run the program
    ;redirect_stderr=true          ; redirect proc stderr to stdout (default false)
    ;stdout_logfile=/a/path        ; stdout log path, NONE for none; default AUTO
    ;stdout_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
    ;stdout_logfile_backups=10     ; # of stdout logfile backups (default 10)
    ;stdout_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
    ;stdout_events_enabled=false   ; emit events on stdout writes (default false)
    ;stderr_logfile=/a/path        ; stderr log path, NONE for none; default AUTO
    ;stderr_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
    ;stderr_logfile_backups=10     ; # of stderr logfile backups (default 10)
    ;stderr_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
    ;stderr_events_enabled=false   ; emit events on stderr writes (default false)
    ;environment=A=1,B=2           ; process environment additions (def no adds)
    ;serverurl=AUTO                ; override serverurl computation (childutils)
    
    ; The below sample eventlistener section shows all possible
    ; eventlistener subsection values, create one or more 'real'
    ; eventlistener: sections to be able to handle event notifications
    ; sent by supervisor.
    
    ;[eventlistener:theeventlistenername]
    ;command=/bin/eventlistener    ; the program (relative uses PATH, can take args)
    ;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
    ;numprocs=1                    ; number of processes copies to start (def 1)
    ;events=EVENT                  ; event notif. types to subscribe to (req'd)
    ;buffer_size=10                ; event buffer queue size (default 10)
    ;directory=/tmp                ; directory to cwd to before exec (def no cwd)
    ;umask=022                     ; umask for process (default None)
    ;priority=-1                   ; the relative start priority (default -1)
    ;autostart=true                ; start at supervisord start (default: true)
    ;autorestart=unexpected        ; restart at unexpected quit (default: unexpected)
    ;startsecs=10                  ; number of secs prog must stay running (def. 1)
    ;startretries=3                ; max # of serial start failures (default 3)
    ;exitcodes=0,2                 ; 'expected' exit codes for process (default 0,2)
    ;stopsignal=QUIT               ; signal used to kill process (default TERM)
    ;stopwaitsecs=10               ; max num secs to wait b4 SIGKILL (default 10)
    ;user=chrism                   ; setuid to this UNIX account to run the program
    ;redirect_stderr=true          ; redirect proc stderr to stdout (default false)
    ;stdout_logfile=/a/path        ; stdout log path, NONE for none; default AUTO
    ;stdout_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
    ;stdout_logfile_backups=10     ; # of stdout logfile backups (default 10)
    ;stdout_events_enabled=false   ; emit events on stdout writes (default false)
    ;stderr_logfile=/a/path        ; stderr log path, NONE for none; default AUTO
    ;stderr_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
    ;stderr_logfile_backups        ; # of stderr logfile backups (default 10)
    ;stderr_events_enabled=false   ; emit events on stderr writes (default false)
    ;environment=A=1,B=2           ; process environment additions
    ;serverurl=AUTO                ; override serverurl computation (childutils)
    
    ; The below sample group section shows all possible group values,
    ; create one or more 'real' group: sections to create "heterogeneous"
    ; process groups.
    
    ;[group:thegroupname]
    ;programs=progname1,progname2  ; each refers to 'x' in [program:x] definitions
    ;priority=999                  ; the relative start priority (default 999)
    
    ; The [include] section can just contain the "files" setting.  This
    ; setting can list multiple files (separated by whitespace or
    ; newlines).  It can also contain wildcards.  The filenames are
    ; interpreted as relative to this file.  Included files *cannot*
    ; include files themselves.
    
    [include]
    files = supervisord.d/*.ini
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130

    四、启动 supervisor 服务

    使用命令:supervisord -c /etc/supervisord.conf

    查看是否启动成功:ps aux | grep supervisord

    [root@VM-8-7-centos supervisord.d]# ps aux | grep supervisord
    root       95494  0.0  0.6 136868 24412 ?        Ss   11:41   0:08 /usr/bin/python3.6 /usr/bin/supervisord -c /etc/supervisord.conf
    root      157859  0.0  0.0  15244  1176 pts/0    S+   16:53   0:00 grep --color=auto supervisord
    
    • 1
    • 2
    • 3

    其中,95494157859 pid

    如果,想要强制杀死 supervisor 服务,可通过:kill - 9 进程ID

    五、supervisor web管理控制台的使用

    supervisor 默认是将 web 管理控制台给关闭的,需要修改下 supervisord.conf 配置文件,将下方四行注释去掉:

    [inet_http_server]         ; inet (TCP) server disabled by default
    port=0.0.0.0:9001        ; (ip_address:port specifier, *:port for all iface)
    username=user              ; (default is no username (open server))
    password=123               ; (default is no password (open server))
    
    • 1
    • 2
    • 3
    • 4

    修改完重载下配置:

    [root@VM-8-7-centos etc]# supervisorctl
    Server requires authentication
    Username:user
    Password:
    
    supervisor> reload
    Really restart the remote supervisord process y/N? y
    Restarted supervisord
    supervisor> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    访问:http://ip:port

    在这里插入图片描述

    六、supervisorctl 常用进程操作命令

    • 更新新的配置到supervisord:supervisorctl update
    • 查看进程状态:supervisorctl status
    • 重新启动配置中的所有程序:supervisorctl reload
    • 启动某个进程(program_name=你配置中写的程序名称):supervisorctl start program_name
    • 停止某一进程 (program_name=你配置中写的程序名称):supervisorctl stop program_name
    • 重启某一进程 (program_name=你配置中写的程序名称):supervisorctl restart program_name
    • 停止全部进程:supervisorctl stop all

    七、子进程的相关配置

    7.1 如何查看子进程配置路径

    查看 supervior.conf ,找到:

    [include]
    files = supervisord.d/*.ini
    
    • 1
    • 2

    即可看到子进程配置文件路径:/etc/supervisord.d,并且子进程文件是以 .ini 结尾的。

    给需要管理的子进程(程序)编写一个配置文件,放在 /etc/supervisor.d 目录下。

    7.2 子进程配置文件相关参数

    • 示例一:
    #项目名
    [program:blog]
    #脚本目录
    directory=/opt/bin
    #脚本执行命令
    command=/usr/bin/python /opt/bin/test.py
    
    #supervisor启动的时候是否随着同时启动,默认True
    autostart=true
    #当程序exit的时候,这个program不会自动重启,默认unexpected,设置子进程挂掉后自动重启的情况,有三个选项,false,unexpected和true。如果为false的时候,无论什么情况下,都不会被重新启动,如果为unexpected,只有当进程的退出码不在下面的exitcodes里面定义的
    autorestart=false
    #这个选项是子进程启动多少秒之后,此时状态如果是running,则我们认为启动成功了。默认值为1
    startsecs=1
    
    #脚本运行的用户身份 
    user = test
    
    #日志输出 
    stderr_logfile=/tmp/blog_stderr.log 
    stdout_logfile=/tmp/blog_stdout.log 
    #把stderr重定向到stdout,默认 false
    redirect_stderr = true
    #stdout日志文件大小,默认 50MB
    stdout_logfile_maxbytes = 20MB
    #stdout日志文件备份数
    stdout_logfile_backups = 20
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 示例二:简易示例
    [root@VM-8-7-centos supervisord.d]# cat tomlibao.ini 
    [program:tomlibao]
    directory = /www/wwwroot
    command = php index.php
    autostart = true
    autorestart = true
    redirect_stderr = true
    stdout_logfile = /www/wwwroot/1.log
    stderr_logfile = /www/wwwroot/1.log
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    八、详细参数详解

    主配置文件:

    [unix_http_server]
    file=/tmp/supervisor.sock   ;UNIX socket 文件,supervisorctl 会使用
    ;chmod=0700                 ;socket文件的mode,默认是0700
    ;chown=nobody:nogroup       ;socket文件的owner,格式:uid:gid
     
    ;[inet_http_server]         ;HTTP服务器,提供web管理界面
    ;port=127.0.0.1:9001        ;Web管理后台运行的IP和端口,如果开放到公网,需要注意安全性
    ;username=user              ;登录管理后台的用户名
    ;password=123               ;登录管理后台的密码
     
    [supervisord]
    logfile=/tmp/supervisord.log ;日志文件,默认是 $CWD/supervisord.log
    logfile_maxbytes=50MB        ;日志文件大小,超出会rotate,默认 50MB,如果设成0,表示不限制大小
    logfile_backups=10           ;日志文件保留备份数量默认10,设为0表示不备份
    loglevel=info                ;日志级别,默认info,其它: debug,warn,trace
    pidfile=/tmp/supervisord.pid ;pid 文件
    nodaemon=false               ;是否在前台启动,默认是false,即以 daemon 的方式启动
    minfds=1024                  ;可以打开的文件描述符的最小值,默认 1024
    minprocs=200                 ;可以打开的进程数的最小值,默认 200
     
    [supervisorctl]
    serverurl=unix:///tmp/supervisor.sock ;通过UNIX socket连接supervisord,路径与unix_http_server部分的file一致
    ;serverurl=http://127.0.0.1:9001 ; 通过HTTP的方式连接supervisord
     
    ; [program:xx]是被管理的进程配置参数,xx是进程的名称
    [program:xx]
    command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run  ; 程序启动命令
    autostart=true       ; 在supervisord启动的时候也自动启动
    startsecs=10         ; 启动10秒后没有异常退出,就表示进程正常启动了,默认为1秒
    autorestart=true     ; 程序退出后自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启
    startretries=3       ; 启动失败自动重试次数,默认是3
    user=tomcat          ; 用哪个用户启动进程,默认是root
    priority=999         ; 进程启动优先级,默认999,值小的优先启动
    redirect_stderr=true ; 把stderr重定向到stdout,默认false
    stdout_logfile_maxbytes=20MB  ; stdout 日志文件大小,默认50MB
    stdout_logfile_backups = 20   ; stdout 日志文件备份数,默认是10
    ; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
    stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out
    stopasgroup=false     ;默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程
    killasgroup=false     ;默认为false,向进程组发送kill信号,包括子进程
     
    ;包含其它配置文件
    [include]
    files = supervisord.d/*.ini    ;可以指定一个或多个以.ini结束的配置文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    子配置文件:

    #项目名
    [program:blog]
    #脚本目录
    directory=/opt/bin
    #脚本执行命令
    command=/usr/bin/php /www/wwwroot/index.php
    
    #supervisor启动的时候是否随着同时启动,默认True
    autostart=true
    #当程序exit的时候,这个program不会自动重启,默认unexpected,设置子进程挂掉后自动重启的情况,有三个选项,false,unexpected和true。如果为false的时候,无论什么情况下,都不会被重新启动,如果为unexpected,只有当进程的退出码不在下面的exitcodes里面定义的
    autorestart=false
    #这个选项是子进程启动多少秒之后,此时状态如果是running,则我们认为启动成功了。默认值为1
    startsecs=1
    
    #脚本运行的用户身份 
    user = tomlibao
    
    #日志输出 
    stderr_logfile=/tmp/blog_stderr.log 
    stdout_logfile=/tmp/blog_stdout.log 
    #把stderr重定向到stdout,默认 false
    redirect_stderr = true
    #stdout日志文件大小,默认 50MB
    stdout_logfile_maxbytes = 20MB
    #stdout日志文件备份数
    stdout_logfile_backups = 20
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    九、实际应用案例

    1、最基础使用

    在子进程配置目录里,创建一个tomlibao.ini文件:

    [root@VM-8-7-centos supervisord.d]# cat tomlibao.ini 
    [program:tomlibao]
    directory = /www/wwwroot
    command = php index.php
    autostart = true
    autorestart = true
    redirect_stderr = true
    stdout_logfile = /www/wwwroot/1.log
    stderr_logfile = /www/wwwroot/1.log
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    开启该子进程:

    root@VM-8-7-centos supervisord.d]# supervisorctl start tomlibao
    tomlibao: started
    
    • 1
    • 2

    其中,被执行的 index.php 是要定时存入日志:

    [root@VM-8-7-centos wwwroot]# cat index.php 
    <?php
    sleep(2);
    file_put_contents("1.txt",date('Y-m-d H:i:s')."\r\n",FILE_APPEND);
    
    • 1
    • 2
    • 3
    • 4

    2、单个脚本运行多个守护进程

    背景:单个生产者,多个消费者,解决生产速度大于消费速度的问题。

    子配置文件增加以下参数:

    process_name=%(program_name)s_%(process_num)02d              ;多进程名称肯定不能相同,匹配多个
    numprocs=4       
    
    • 1
    • 2

    十、常见问题

    10.1 报错:Exited too quickly (process log may have details)

    解决:出现这个问题,类似于php代码内存溢出,在业务逻辑代码中加个sleep即可。


    参考链接:

    1、http://www.dahouduan.com/2017/11/30/supervisor-tutorial/
    2、https://www.cnblogs.com/fuqian/p/16182402.html

  • 相关阅读:
    数据库安全-Redis&Hadoop&Mysql&未授权访问&RCE
    LCR 072. x 的平方根
    【Express】登录鉴权 JWT
    DPDK系列之三十内存中的环形队列
    Head First设计模式(阅读笔记)-07.适配器模式
    C#创建AutoMapper的映射配置
    数据采集: selenium 自动翻页接口调用时的验证码处理
    阿里云SLB之:基于多域名调度的SLB七层负载均衡配置(十四)
    如何在IPhone 14、14 Pro和14 Pro Max上添加屏幕锁定
    积分商城商品供应商选择的三个要求
  • 原文地址:https://blog.csdn.net/qq_42249896/article/details/120082098