<div id="outDiv">
    <div id="middleDiv">
        <div id="inDiv">请在此点击鼠标。</div>
    </div>
</div>
<div id="info"></div>

<script>
    // 先顶层捕获再往里层 true , 后从里层往外冒泡 false 
    // event.preventDefault(); 阻止默认行为
    // event.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行
    window.onload = function () {
        var outDiv = document.getElementById("outDiv");
        var middleDiv = document.getElementById("middleDiv");
        var inDiv = document.getElementById("inDiv");
        var info = document.getElementById("info");
        outDiv.addEventListener("click", function () { info.innerHTML += "outDiv" + "<br>"; }, true);
        middleDiv.addEventListener("click", function () { info.innerHTML += "middleDiv" + "<br>"; }, false);
        inDiv.addEventListener("click", function () { info.innerHTML += "inDiv" + "<br>"; }, true);
    }
</script>

yum install -y epel-release
yum install -y supervisor
cp /etc/supervisord.conf /etc/supervisord.d/supervisord.conf

vi /etc/supervisord.conf

; Sample supervisor config file.

[unix_http_server]
file=/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=0.0.0.0:9001        ; (ip_address:port specifier, *:port for all iface)
username=root              ; (default is no username (open server))
password=密码               ; (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=/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:///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

vi /etc/supervisord.d/hyperf.ini

# 应用名称
[program:hyperf]
# 工作目录
directory=/data/wwwroot/hy/
# 这里为您要管理的项目的启动命令
command=php -d swoole.use_shortname=Off -d memory_limit=-1 ./bin/hyperf.php start
# 以哪个用户来运行该进程
user=root
# supervisor 启动时自动该应用
autostart=true
# 进程退出后自动重启进程
autorestart=true
# 进程持续运行多久才认为是启动成功
startsecs=1
# 重试次数
startretries=3
# stderr 日志输出位置
stderr_logfile=/data/wwwroot/hy/runtime/logs/stderr.log
stderr_logfile_backups=100
# stdout 日志输出位置
stdout_logfile=/data/wwwroot/hy/runtime/logs/stdout.log
stdout_logfile_backups=100

开机自启动 vi /usr/lib/systemd/system/supervisord.service

[Unit]
Description=Supervisor daemon

[Service]
Type=forking
PIDFile=/var/run/supervisord.pid
ExecStart=/bin/supervisord -c /etc/supervisord.conf
ExecStop=/bin/supervisorctl shutdown
ExecReload=/bin/supervisorctl reload
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

开机启动服务 systemctl enable supervisord
查看是否启动 systemctl is-enabled supervisord
重新加载配置文件 supervisorctl reload

systemctl stop supervisord
systemctl start supervisord
systemctl status supervisord
systemctl reload supervisord
systemctl restart supervisord
/bin/supervisord -c /etc/supervisord.conf
supervisorctl
supervisorctl -c /etc/supervisord.conf
supervisorctl -s http://localhost:7001

防火墙

firewall-cmd --query-port=9001/tcp          //监测端口是否开启
firewall-cmd --add-port=9001/tcp            //开启端口
firewall-cmd --remove-port=9001/tcp       //关闭端口
sudo firewall-cmd --reload                  //重新加载配置

Supervisor部署

Supervisor是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统。很方便的监听、启动、停止、重启一个或多个进程。用Supervisor管理的进程,当一个进程意外被Kill,会自动将它重新拉起,不需要开发者自己编写shell进行进程管理来维护自己的服务。

安装Supervisor

采用Ubuntu系统下的安装方式进行演示:

apt-get -y install supervisor

创建配置文件

vim /etc/supervisor/conf.d/easyswoole.conf

文件内容如下:

# 设置应用名称为easyswoole
[program:easyswoole]
# 设置运行目录
directory=/data/wwwroot/EasySwoole3.4.x
# 项目的启动命令
command=php easyswoole server start
# 设置用户来运行该进程
user=www-data
# 是否随着supervisor启动时 自动启动该应用
autostart=true
# 进程退出 是否自动重启进程
autorestart=true
# 进程启动多少秒之后被认为是启动成功 默认1s
startsecs=1
# 失败最大尝试次数 默认3
startretries=3
# stderr
stderr_logfile=/data/wwwlog/easyswoole-stderr.log
# stdout
stdout_logfile=/data/wwwlog/easyswoole-stdout.log

启动Supervisor
service supervisor start

Supervisorctl

启动EasySwoole应用

supervisorctl start easyswoole

停止EasySwoole应用

supervisorctl stop easyswoole

重启EasySwoole应用

supervisorctl restart easyswoole

启动EasySwoole应用

supervisorctl start easyswoole

查看所有监控的应用

supervisorctl status

重新加载配置文件

supervisorctl update

重启所有应用

supervisorctl reload

Centos上安装Docker如下:

sudo su
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce 
systemctl start docker
systemctl enable docker

安装完毕以后, 查看Docker版本:

sh docker version

server_name  ~^(?<subdomain>.+)\.yourdomain\.com$;

set $root_path '/var/www/yanue.net';
root $root_path;
root html/$subdomain;
server{
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;
    root /data/$subdomain/public;
    # Make site accessible from http://localhost/
    server_name ~^(?<subdomain>.+).zdb.im$;
    location / {
        index  index.php index.html index.htm;
         #如果请求既不是一个文件,也不是一个目录,则执行一下重写规则
         if (!-e $request_filename)
         {
            #地址作为将参数rewrite到index.php上。
            rewrite ^/(.*)$ /index.php/$1;
            #若是子目录则使用下面这句,将subdir改成目录名称即可。
            #rewrite ^/subdir/(.*)$ /subdir/index.php/$1;
         }
    }
    #proxy the php scripts to php-fpm
    location ~ \.php {
            include fastcgi_params;
            ##pathinfo支持start
            #定义变量 $path_info ,用于存放pathinfo信息
            set $path_info "";
            #定义变量 $real_script_name,用于存放真实地址
            set $real_script_name $fastcgi_script_name;
            #如果地址与引号内的正则表达式匹配
            if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") {
                    #将文件地址赋值给变量 $real_script_name
                    set $real_script_name $1;
                    #将文件地址后的参数赋值给变量 $path_info
                    set $path_info $2;
            }
            #配置fastcgi的一些参数
            fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
            fastcgi_param SCRIPT_NAME $real_script_name;
            fastcgi_param PATH_INFO $path_info;
            ###pathinfo支持end
        fastcgi_intercept_errors on;
        fastcgi_pass   127.0.0.1:9000;
    }

    location ^~ /data/runtime {
        return 404;
    }

    location ^~ /application {
        return 404;
    }

}

两种认证方式,
第一种: 临时链接;
第二种: 固定密码链接;

第一种: 临时链接

  1. 服务端生成md5值和expires过期时间, 代码如下:
$expire = time() + 20;/*链接的有效期*/
$uri = '/audio/979.mp3'; /*访问的资源*/
$remoteAddr = '183.45.175.200';/*访问者的公网IP地址*/
$secret = 'zhuangdebiao'; /*密钥*/

/*拼接字符串, 生成哈希值*/
$hash = base64_encode(md5($expire.' '.$uri .' '.$remoteAddr . ' ' .$secret, true));
$hash = strtr($hash, '+/', '-_'); /*可选*/
$hash = str_replace('=', '', $hash); /*可选*/
/*最终URL*/
echo 'https://yey.zdb.im'.$uri.'?hash='.$hash.'&expires='.$expire;

URL示例 :
https://yey.zdb.im/audio/979.mp3?hash=qdmp79trLCCKKVzKAvia4A==&expires=1610085706

  1. 修改nginx配置
location /audio/ {
    secure_link $arg_hash,$arg_expires;
    secure_link_md5 "$secure_link_expires $uri $remote_addr zhuangdebiao";

    # hash error
    if ($secure_link = "") {
        return 403;
    }
    # expire
    if ($secure_link = "0") {
        return 410;
    }
    try_files $uri $uri/ = 404;
}
  1. 从$args即queryString中获取md5值和过期时间

secure_link $arg_hash,$arg_expires;

  1. 计算md5值的字符串模板

secure_link_md5 "$secure_link_expires $uri $remote_addr zhuangdebiao";

$secure_link_expires 即过期时间 $arg_expires;

$uri 即访问的资源路径;

$remote_addr 即用户的IP地址;

最后一段是密钥 secret;

用空格分隔, 也可以不用空格, 随意字符串都行;

  1. 如果md5值不相等, 即密钥等参数错误, 返回http 403错误;
  2. 如果链接过期, 返回410错误;
  3. 最终, 允许访问资源, 可以proxy_pass或者try_files检测文件是否存在 并返回给客户端浏览器;

第二种: 固定密码链接

uri 格式是 /prefix/hash/link

md5值的 格式是 hex(md5(link.secret))

原链接 https://yey.zdb.im/zdb/979.mp3

固定密码链接, 举例: https://yey.zdb.im/p/1656e96df11cbbcab7186e9f5dd171f3/979.mp3

location /p/ {
    secure_link_secret zhuangdebiao; # 密钥

    if ($secure_link = "") { 
        return 403;
    }

    rewrite ^ /zdb/$secure_link;
}
location /zdb/ { 
    alias /home/wwwroot/yey/Public/audio/;
    internal; # 仅允许内部访问,直接访问会返回404
}

计算md5值的算法:

$prefix = 'p'; /*入口前缀*/
$secret = 'zhuangdebiao'; /*固定的密钥*/
$link = '979.mp3'; /*访问的资源*/

$hash = md5($link .$secret, false); /*十六进制*/
echo 'https://yey.zdb.im'.'/'.$prefix.'/'.$hash.'/' .$link;