LNMP架构(三)

Nginx负载均衡

负载均衡 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。负载均衡,英文名称为Load Balance,其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。

如何配置负载均衡呢?借助一个模块叫做upstream,可以在该模块下可以定义多个IP和端口。

现在借助qq.com来做个演示,如何知道qq.com的IP呢?可以用命令dig,没找到命令安装bind-utils包。

  [root@zyshanlinux-001 ~]# yum install -y bind-utils

获取qq.com的IP,其实就是域名解析。

  [root@zyshanlinux-001 ~]# dig qq.com
  
  ; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> qq.com
  ;; global options: +cmd
  ;; Got answer:
  ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31325
  ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
  
  ;; OPT PSEUDOSECTION:
  ; EDNS: version: 0, flags:; udp: 4096
  ;; QUESTION SECTION:
  ;qq.com.                IN  A
  
  ;; ANSWER SECTION:
  qq.com.         394 IN  A   111.161.64.48
  qq.com.         394 IN  A   111.161.64.40
  
  ;; Query time: 53 msec
  ;; SERVER: 119.29.29.29#53(119.29.29.29)
  ;; WHEN: 日 7月 08 20:26:44 CST 2018
  ;; MSG SIZE  rcvd: 67
  

拿这2个IP来做负载均衡,配置文件

  [root@zyshanlinux-001 ~]# vim /usr/local/nginx/conf/vhost/load.conf

配置内容

  upstream qq  ##upstream模块,qq可以是任何名字
  {
      ip_hash;  ##在多台服务器提供同一个服务的时候,支持用户始终登录到A服务器上,避免登录到B机器上导致没有在B机器上输入过密码登录不成功。
      server 111.161.64.48:80;  ##如果是80,那么:80可以省略掉。
      server 111.161.64.40:80;
  }
  server
  {
      listen 80;
      server_name www.qq.com;
      location /
      {
          proxy_pass      http://qq;  ##http://后面跟上面起的名字qq。
          proxy_set_header Host   $host;
          proxy_set_header X-Real-IP      $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      }
  }

保存退出,在未加载配置前

  [root@zyshanlinux-001 vhost]# curl -x127.0.0.1:80 www.qq.com

会访问到默认虚拟主机默认页

加载配置后,就会访问到QQ的真正主页去。

  [root@zyshanlinux-001 vhost]# /usr/local/nginx/sbin/nginx -t
  [root@zyshanlinux-001 vhost]# /usr/local/nginx/sbin/nginx -s reload
  [root@zyshanlinux-001 vhost]# curl -x127.0.0.1:80 www.qq.com

注意:Nginx不支持代理https,即是443端口;如果用户要登录https,只能通过代理服务器(https)访问web服务器的http(即80端口),间接达到访问443(https)。

ssl原理

浏览器发送一个https的请求给服务器;

服务器要有一套数字证书,可以自己制作(后面的操作就是阿铭自己制作的证书),也可以向组织申请,区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出>提示页面,这套证书其实就是一对公钥和私钥;

服务器会把公钥传输给客户端;

客户端(浏览器)收到公钥后,会验证其是否合法有效,无效会有警告提醒,有效则会生成一串随机数,并用收到的公钥加密;

客户端把加密后的随机字符串传输给服务器;

服务器收到加密随机字符串后,先用私钥解密(公钥加密,私钥解密),获取到这一串随机数后,再用这串随机字符串加密传输的数据(该加密为对称加密,所谓对称加密,就是将数据和私钥也就是这个随机字符串>通过某种算法混合在一起,这样除非知道私钥,否则无法获取数据内容);

服务器把加密后的数据传输给客户端;

客户端收到数据后,再用自己的私钥也就是那个随机字符串解密;

生成ssl密钥对

生成密钥需要工具,生成的密钥放到/usr/local/nginx/conf路径下

  [root@zyshanlinux-001 vhost]# cd /usr/local/nginx/conf

生成私钥key文件,还要输入私钥密码

  [root@zyshanlinux-001 conf]# openssl genrsa -des3 -out tmp.key 2048
  Generating RSA private key, 2048 bit long modulus
  .+++
  ..........................+++
  e is 65537 (0x10001)
  Enter pass phrase for tmp.key:
  Verifying - Enter pass phrase for tmp.key:

转换key,取消密码,否则每次访问网页都要输入私钥密码;转换Key后,tmp.key是以后密码的,zyshan.key是没密码的,可以把tmp.key删除。

  [root@zyshanlinux-001 conf]# openssl rsa -in tmp.key -out zyshan.key
  Enter pass phrase for tmp.key:
  writing RSA key
  [root@zyshanlinux-001 conf]# rm -f tmp.key

生成证书请求文件;需要填入一些信息,因为测试用可以随便填。

  [root@zyshanlinux-001 conf]# openssl req -new -key zyshan.key -out zyshan.csr
  You are about to be asked to enter information that will be incorporated
  into your certificate request.
  What you are about to enter is what is called a Distinguished Name or a DN.
  There are quite a few fields but you can leave some blank
  For some fields there will be a default value,
  If you enter '.', the field will be left blank.
  -----
  Country Name (2 letter code) [XX]:11
  State or Province Name (full name) []:bei^H^H^H^H^H^H
  Locality Name (eg, city) [Default City]:guangzhou
  Organization Name (eg, company) [Default Company Ltd]:guangzhou
  Organizational Unit Name (eg, section) []:zysyhan^H^H
  Common Name (eg, your name or your server's hostname) []:zyshan
  Email Address []:[email protected]
  
  Please enter the following 'extra' attributes
  to be sent with your certificate request
  A challenge password []:zyshan
  An optional company name []:zyshan

需要拿这个文件和之前生成的私钥一起生产公钥文件。

  [root@zyshanlinux-001 conf]# openssl x509 -req -days 365 -in zyshan.csr -signkey zyshan.key -out zyshan.crt
  Signature ok
  subject=/C=11/ST=bei\x08\x08\x08\x08\x08\x08/L=guangzhou/O=guangzhou/OU=zysyhan\x08\x08/CN=zyshan/[email protected]
  Getting Private key

这样就有三个文件了。crt公钥、csr请求文件、key私钥。

  [root@zyshanlinux-001 conf]# ls zyshan.
  zyshan.crt  zyshan.csr  zyshan.key

Nginx配置ssl

配置文件

  vim /usr/local/nginx/conf/vhost/ssl.conf

配置文件内容

  server
  {
      listen 443;
      server_name zyshan.com;
      index index.html index.php;
      root /data/wwwroot/zyshan.com;
      ssl on;
      ssl_certificate zyshan.crt;
      ssl_certificate_key zyshan.key;
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  }

创建测试目录

  [root@zyshanlinux-001 vhost]# mkdir /data/wwwroot/zyshan.com

-t检查语法,unknown directive “ssl” 报错是因为之前安装nginx时的时候配置参数没有把ssl配置上去,需要到nginx目录去重新编译。

  [root@zyshanlinux-001 nginx-1.12.1]# cd /usr/local/src/nginx-1.12.1/
  [root@zyshanlinux-001 nginx-1.12.1]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module
  [root@zyshanlinux-001 nginx-1.12.1]# make&&make install

检查,多了一个参数:--with-http_ssl_module

  [root@zyshanlinux-001 nginx-1.12.1]# /usr/local/nginx/sbin/nginx -V
  nginx version: nginx/1.12.1
  built by gcc 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC) 
  built with OpenSSL 1.0.2k-fips  26 Jan 2017
  TLS SNI support enabled
  configure arguments: --prefix=/usr/local/nginx --with-http_ssl_module

检查语法,重启Nginx,查看端口多了一个443端口。

  [root@zyshanlinux-001 nginx-1.12.1]# /usr/local/nginx/sbin/nginx -t
  nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
  nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
  [root@zyshanlinux-001 nginx-1.12.1]# /etc/init.d/nginx restart
  Restarting nginx (via systemctl):                          [  确定  ]
  [root@zyshanlinux-001 nginx-1.12.1]# netstat -lntp
  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:80              0.0.0.0:*               LISTEN      4672/nginx: master  
  tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1088/sshd           
  tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1447/master         
  tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      4672/nginx: master  
  tcp6       0      0 :::22                   :::*                    LISTEN      1088/sshd           
  tcp6       0      0 ::1:25                  :::*                    LISTEN      1447/master         
  tcp6       0      0 :::3306                 :::*                    LISTEN      1405/mysqld

创建测试/data/wwwroot/zyshan.com/index.html文件

  [root@zyshanlinux-001 nginx-1.12.1]# cd /data/wwwroot/zyshan.com/
  [root@zyshanlinux-001 zyshan.com]# ls
  index.html  index.php

把zyshan.com配置到hosts去

  [root@zyshanlinux-001 zyshan.com]# vi /etc/hosts
  127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
  ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
  192.168.106.150 www.qq123.com www.19.com www.zyshan.com
  127.0.0.1 www.19.com zyshan.com

测试成功,只是证书是自己颁发的,浏览器认为不安全,报错而已。

  [root@zyshanlinux-001 zyshan.com]# curl https://zyshan.com
  curl: (60) Peer's certificate issuer has been marked as not trusted by the user.
  More details here: http://curl.haxx.se/docs/sslcerts.html
  
  curl performs SSL certificate verification by default, using a "bundle"
   of Certificate Authority (CA) public keys (CA certs). If the default
   bundle file isn't adequate, you can specify an alternate file
   using the --cacert option.
  If this HTTPS server uses a certificate signed by a CA represented in
   the bundle, the certificate verification probably failed due to a
   problem with the certificate (it might be expired, or the name might
   not match the domain name in the URL).
  If you'd like to turn off curl's verification of the certificate, use
   the -k (or --insecure) option.

php-fpm的pool

   vim /usr/local/php/etc/php-fpm.conf//在[global]部分增加
   include = etc/php-fpm.d/*.conf
   mkdir /usr/local/php/etc/php-fpm.d/
   cd /usr/local/php/etc/php-fpm.d/
   vim www.conf //内容如下
  [www]
  listen = /tmp/www.sock
  listen.mode=666
  user = php-fpm
  group = php-fpm
  pm = dynamic
  pm.max_children = 50
  pm.start_servers = 20
  pm.min_spare_servers = 5
  pm.max_spare_servers = 35
  pm.max_requests = 500
  rlimit_files = 1024
  继续编辑配置文件
   vim aming.conf //内容如下
  [aming]
  listen = /tmp/aming.sock
  listen.mode=666
  user = php-fpm
  group = php-fpm
  pm = dynamic
  pm.max_children = 50
  pm.start_servers = 20
  pm.min_spare_servers = 5
  pm.max_spare_servers = 35
  pm.max_requests = 500
  rlimit_files = 1024
   /usr/local/php/sbin/php-fpm –t
   /etc/init.d/php-fpm restart
  

php-fpm慢执行日志

配置文件

  vim /usr/local/php-fpm/etc/php-fpm.d/www.conf//加入如下内容
  request_slowlog_timeout = 1  ##超过1秒的录入日志
  slowlog = /usr/local/php-fpm/var/log/www-slow.log  ##录入的日志路径

测试,写入一个休眠2秒的php,慢日志就会把这个慢了2秒的日志记录下来,标记出是这句休眠语句导致的慢。

   配置nginx的虚拟主机test.com.conf,把unix:/tmp/php-fcgi.sock改为unix:/tmp/www.sock
   重新加载nginx服务
   vim /data/wwwroot/test.com/sleep.php//写入如下内容
   <?php echo “test slow log”;sleep(2);echo “done”;?>
   curl -x127.0.0.1:80 test.com/sleep.php 
   cat /usr/local/php-fpm/var/log/www-slow.log

php-fpm定义open_basedir

  vim /usr/local/php-fpm/etc/php-fpm.d/aming.conf//加入如下内容
  php_admin_value[open_basedir]=/data/wwwroot/aming.com:/tmp/
   创建测试php脚本,进行测试
   再次更改aming.conf,修改路径,再次测试
   配置错误日志
   再次测试
   查看错误日志

php-fpm进程管理

配置文件中,pm = dynamic动态进程管理;动态的时候后面的所有参数都生效。这里配置注释不用“#”而是用“;”

  [root@zyshanlinux-001 etc]# cat php-fpm.conf
  [global]
  pid = /usr/local/php-fpm/var/run/php-fpm.pid
  error_log = /usr/local/php-fpm/var/log/php-fpm.log
  [www]
  listen = /tmp/php-fcgi.sock
  ;listen = 127.0.0.1:9000
  listen.mode = 666
  user = php-fpm
  group = php-fpm
  pm = dynamic
  ;pm = static
  pm.max_children = 50
  pm.start_servers = 20
  pm.min_spare_servers = 5
  pm.max_spare_servers = 35
  pm.max_requests = 500
  rlimit_files = 1024

测试,一开始默认启动20个子进程

  [root@zyshanlinux-001 etc]# ps aux |grep www
  php-fpm   1486  0.0  0.2 227244  4712 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1487  0.0  0.2 227244  4712 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1488  0.0  0.2 227244  4712 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1489  0.0  0.2 227244  4712 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1490  0.0  0.2 227244  4716 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1491  0.0  0.2 227244  4716 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1492  0.0  0.2 227244  4716 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1493  0.0  0.2 227244  4716 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1494  0.0  0.2 227244  4716 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1495  0.0  0.2 227244  4716 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1496  0.0  0.2 227244  4720 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1497  0.0  0.2 227244  4720 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1498  0.0  0.2 227244  4720 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1499  0.0  0.2 227244  4720 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1500  0.0  0.2 227244  4720 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1501  0.0  0.2 227244  4720 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1502  0.0  0.2 227244  4720 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1504  0.0  0.2 227244  4720 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1505  0.0  0.2 227244  4720 ?        S    20:13   0:00 php-fpm: pool www
  php-fpm   1506  0.0  0.2 227244  4720 ?        S    20:13   0:00 php-fpm: pool www
  root      5029  0.0  0.0 112720   972 pts/0    R+   22:50   0:00 grep --color=auto www

配置pm = static静态,只有后面跟着的那一行生效,一开始就运行50个子进程,后面的参数都不生效了。

  pm = static
  pm.max_children = 50  

检查语法,重启服务,测试,确实是默认启动50个子进程

  [root@zyshanlinux-001 etc]# /usr/local/php-fpm/sbin/php-fpm -t
  [08-Jul-2018 22:57:13] NOTICE: configuration file /usr/local/php-fpm/etc/php-fpm.conf test is successful
  
  [root@zyshanlinux-001 etc]# /etc/init.d/php-fpm restart
  Gracefully shutting down php-fpm . done
  Starting php-fpm  done
  [root@zyshanlinux-001 etc]# ps aux |grep www
  php-fpm   5088  0.0  0.2 227244  4716 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5089  0.0  0.2 227244  4716 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5090  0.0  0.2 227244  4720 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5091  0.0  0.2 227244  4720 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5092  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5093  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5094  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5095  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5096  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5097  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5098  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5099  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5100  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5101  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5102  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5103  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5104  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5105  0.0  0.2 227244  4724 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5106  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5107  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5108  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5109  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5110  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5111  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5112  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5113  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5114  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5115  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5116  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5117  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5118  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5119  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5120  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5121  0.0  0.2 227244  4728 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5122  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5123  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5124  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5125  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5126  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5127  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5128  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5129  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5130  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5131  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5132  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5133  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5134  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5135  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5136  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www
  php-fpm   5137  0.0  0.2 227244  4732 ?        S    22:57   0:00 php-fpm: pool www

pm.max_children = 50 //最大子进程数,ps aux可以查看pm.start_servers = 20 //启动服务时会启动的进程数pm.min_spare_servers = 5 //定义在空闲时段,子进程数的最少数量,如果达到这个数值时,php-fpm服务会自动派生新的子进程。pm.max_spare_servers = 35 //定义在空闲时段,子进程数的最大值,如果高于这个数值就开始清理空闲的子进程。pm.max_requests = 500 //定义一个子进程最多处理的请求数,也就是说在一个php-fpm的子进程最多可以处理这么多请求,当达到这个数值时,它会自动退出

拓展:

  针对请求的uri来代理 http://ask.apelearn.com/question/1049
  根据访问的目录来区分后端的web http://ask.apelearn.com/question/920
  nginx长连接 http://www.apelearn.com/bbs/thread-6545-1-1.html
  nginx算法分析 http://blog.sina.com.cn/s/blog_72995dcc01016msi.html
  nginx中的root和alias区别 http://blog.csdn.net/21aspnet/article/details/6583335
  nginx的alias和root配置 http://www.ttlsa.com/nginx/nginx-root_alias-file-path-configuration/
  http://www.iigrowing.cn/shi-yan-que-ren-nginx-root-alias-location-zhi-ling-shi-yong-fang-fa.html 这个更详细

直播拓展:

  证书 https://coding.net/u/aminglinux/p/nginx/git/blob/master/ssl/ca.md
  Nginx几种负载均衡算法及配置实例 https://blog.whsir.com/post-1482.html
  Nginx正向代理配置 https://coding.net/u/aminglinux/p/nginx/git/blob/master/proxy/z_proxy.md

猜你喜欢

转载自blog.csdn.net/zhengyshan/article/details/80963953