从入门到精通-Nginx,图文并茂、负载均衡、动静分离、虚拟主机 附案例源码

导读

  篇幅较长,干货满满,需花费较长时间,转载请注明出处!

Nginx概述

简介

  Nginx (engine x) 是一个高性能HTTP反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。

  Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少并发能力,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。

代理服务器

  代理服务器根据代理对象不同,可以分为正向代理服务器与反向代理服务器。这里的“正”与“反”均是站在客户端角度来说的。

正向代理

  正向代理是对客户端代理客户端C服务端S获取资源,但于某些原因不能直接访问服务端,而是通过另外一台主机P向服务端发送请求。当服务端处理完毕请求将响应发主机P,主机P接收到来自服务端的响应后将响应又转给客户端C。此时的主机P,就称为客户端C正向代理服务器

  客户端在使用正向代理服务器时是知道其要访问的目标服务器的地址等信息的。

  正向代理服务器是服务器的用户(客户端)架设的主机,与服务器无关,正向代理服务器的出现,使服务端根本就不知道真正客户端的存在。

反向代理

  反向代理,其实客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们需要将请求送到反向代理服务器由反向代理服务器选择目标服务器获取数据后,在将响应返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器IP地址

两者区别

  在知乎上找了2张图,可以帮助我们更好的理解。

 

Nginx的特点

  • 高并发
  • 低消耗
  • 热部署
  • 高扩展
  • 高可用

Nginx的web请求处理机制

  Nginx结合多进程机制和异步机制对外提供服务异步机制使用的是异步非阻塞方式。Nginx的master进程会生成多个worker进程,master进程负责管理这些worker进程生命周期接受外部命令解析perl脚本等。而worker进程则用于接受和处理客户端请求

  每个worker进程能够使用异步非阻塞方式处理多个客户端请求。当某个worker进程接收到客户端的请求后,会调用IO进程处理,如果不能立即得到结果,worker进程就去处理其他的请求。当IO返回结果后,就会通知worker进程,而worker进程得到通知后,就会挂起当前正在处理的事务,拿IO返回结果去响应客户端请求,worker进程采用的是epoll事件驱动模型IO进行通信的。epoll模型底层采用的是“回调callback”代替里轮询,使效率高于select模型。

Nginx的下载与安装

Nginx的下载

nginx的官网:http://nginx.org/

  注:主线版,是最新的版本;稳定版,推荐生产环境下使用;旧版,以前的版本。

百度云盘地址

链接:https://pan.baidu.com/s/1kjQST_x1Sf_thg3XDmqx6w  密码:18sc

将nginx上传至linux

环境搭建

前期准备

  因为nginx是C语言写的,而且是源码安装,安装前需安装C语言环境!!!

yum install -y gcc-c++ gcc

安装依赖库

  基本的Nginx功能依赖于一些基本的库,在安装Nginx之前需要提前安装这些库。

  pcre-devel:pcre,Perl Compatible Regular Expressions,Perl脚本语言兼容正则表达式,为Nginx提供正则表达式库。

  openssl-devel:为Nginx提供SSL(安全套接字层)密码库,包含主要的密码算法,常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其他目的使用。

  在安装之前需要注意,很多库具有devel库与非devel库两种。devel库表示development开发库,比非devel库会多出一些头文件、静态库、源码包等。而这些包在运行时不可能用到,但在开发时有可能用到。所以对于程序员来说,一般都是需要安装devel库的。不过在yum安装devel库时,由于其依赖于非devel库,所以其会先自动安装非devel库,而后再安装devel库。所以真正安装时,只需显示的安装devel库即可。

 yum -y install pcre-devel openssl-devel

解压Nginx包

tar -zxvf nginx-1.16.1.tar.gz -C /opt/apps

进入刚才解压后的目录

查看帮助(可忽略)

./configure --help

安装模块

此时Nginx解压路径下会多出一个:Makefile

系统配置信息

  • path prefix:Nginx安装目录
  • binary file:Nginx命令文件
  • modules path:Nginx模块存放路径
  • configuration prefix:Nginx配置文件存放路径
  • configuration file:Nginx配置文件名
  • pid file:Nginx的进程id文件
  • error log file:错误日志文件
  • http access log file:http访问日志文件
  • http xxx:其他http请求相关的文件

  配置成功后,再次查看Nginx解压目录,发现其中多出一个文件Makefile。后面的编译就是依靠该文件进行的。

编译安装

  这是两个命令,make:为编译命令;make install:为安装命令,可以分别执行。这里使用&&将两个命令连接执行,会在前面命令执行成功的前提下才会执行第二个命令。

编译安装完成后,会在/usr/local下出现nginx目录

进入安装目录

设置任务目录可以使用sbin(可忽略)

ln -n /usr/local/nginx/sbin/nginx /usr/local/sbin

格式:
ln -n 源路径 目标路径

注:软链接建立完成后就可以在任意路径下使用nginx命令

Nginx命令

  • 查看命令选项:nginx -h
  • 启动命令:nginx -c file
  • 停止命令:nginx -s stop/quit
  • 平滑重启命令:nginx -s reload
  • 测试配置文件命令:nginx -tq

nginx -t:测试配置文件是否正确,默认只测试默认的配置文件conf/nginx.conf

nginx -T:测试配置文件是否正确,并显示配置文件内容

nginx -tq:在配置文件测试过程中,禁止显示非错误信息,即只显示错误信息

启动nginx

  启动后,并查看,发现有2个线程,一个master,一个worker

查看nginx端口号

nginx页面访问测试

注:linux需关闭防火墙!!!!

Nginx性能调优

查看nginx配置文件

nginx进程

  worker_processes,工作进程,用于指定Nginx的工作进程数量。该值应该设置为多少合适呢?其数值一般设置为cpu内核数量,或内核数量的整数倍。注意,现代的cpu一般都是多核,即一块cpu中包含多个内核。

  若当前系统具有2块cpu,而每块cpu中包含2个内核,那么worker_processes的值一般可以设置为4或8个。当然也可以为2个。

  不过需要注意,该值不仅仅取决于cpu内核数量,还与硬盘数量及负载均衡模式相关,在不确定时还可以指定其值为auto

worker_cpu_affinity的设置

  为了进一步提高系统性能,我们会将worker进程与具体的内核进行绑定。该绑定操作是通过worker_cpu_affinity属性进行设置的。affinity,密切关系。

  不过,若指定worker_processes的值为auto,则无法设置worker_cpu_affinity。该设置是通过二进制进行的。每个内核使用一个二进制表示,0代表内核关闭;1代表内核开启。也就是说,有几个内核,就需要使用几个二进制位。

  下面通过几个例子来增进对worker_processes与worker_cpu_affinity的理解  

Nginx核心功能

请求定位

探究欢迎页面显示原因

访问资源

查看nginx.conf配置

使用perl语言自定义一个网站

检测语法格式是否正确

重启nginx

访问成功

  注:字打错了,应该是“欢迎来到陈彦斌的Nginx!!”

静态代理

  Nginx静态代理,将所有的静态资源,如:css、js、html、jpg等资源放到Nginx服务器,而不存在应用服务器Tomcat中,当客户端发出的请求是对这些静态资源的请求时,Nginx直接将这些静态资源响应给客户端,而无需提交给应用服务器处理。这样就降低了应用服务器的压力

  同时,Nginx对于静态资源的处理较Tomcat,性能更高效率更高。所以,在实际生产环境下,会使用Nginx作为静态代理服务器,专门处理静态资源的响应。

  Nginx对于静态资源请求的拦截方式,可以通过静态资源名称的扩展名拦截,也可以通过静态资源所在的目录名称拦截

创建目录存放文件

往images目录下存放一些图片

编辑nginx.conf配置文件

扩展名拦截

扩展名拦截测试

目录名拦截

负载均衡

   负载均衡,Load Balancing,就是将对请求的处理分摊多个操作单元上进行。这个均衡是指在大批量访问前提下的一种基本均衡,并非是绝对的平均。

  对于Web工程中的负载均衡,就是将相同的Web应用部署多个不同的Web服务器上,形成多个Web应用服务器。当请求到来时,由负载均衡服务器负责请求事先设定好的比例向Web应用服务器进行分发,从而增加系统的整体吞吐量

总体规划

  该机群包含一台Nginx服务器,两台Tomat服务器。

  首先开发一个web工程,将其打包。然后,在克隆出两台Tomcat主机,前面的web工程分别部署到这两台Tomcat主机上。然后,在Nginx服务器上设置对这两台Tomcat主机的均在均衡。 

配置说明

  1. tomcat服务器1:ip地址192.168.31.213
  2. tomcat服务器2:ip地址192.168.31.214
  3. nginx服务器:ip地址192.168.31.201

创建一个Web工程

项目结构图

配置一台Tomcat主机

具体配置,请参考另一篇博客:点我直达

复制并配置另一台Tomcat主机

   克隆上面配置好的Tomcat主机

配置Nginx主机

配置nginx.conf

  注:weight权重

平滑重启nginx

项目的启动与访问

项目下载源码

jsp项目
百度云盘
链接:https://pan.baidu.com/s/18z5c93jM6S-qvoT2cUZi7g  密码:u08i

动静分离

简介

  动静分离,就是JSP、Servlet等态资源交由Tomcat其他Web服务器处理CSS、js、image等静态资源交由Nginx或其他Http服务器处理,充分发挥各自的优势减轻其他服务器压力,搭建更为高效的系统架构。

Nginx动静分析的实现

  下面要搭建Nginx,环境中有三台Nginx主机一台用于完成负载均衡两台Nginx用于存放前面项目中的静态资源。另外,还包含前面的两台Tomcat主机

复制并配置一台Nginx服务器

  将原来安装有Nginx的主机作为母机,克隆一台Nginx主机,用于存放静态资源:css、js、image。

修改nginx.conf

存放静态资源

复制并配置一台Nginx服务器

  重复上面,复制的那台Nginx操作!!!

修改原始那台nginx配置文件(负载均衡,重要!!!)

  设置静态资源负载均衡代理,两台nginx的ip分别为:192.168.31.212、192.168.31.213

 

web工程

 

测试

由于图片太大,截成2段了

linux搭建相关配置

服务器配置信息

  1 #user  nobody;
  2 worker_processes  1;
  3 
  4 #error_log  logs/error.log;
  5 #error_log  logs/error.log  notice;
  6 #error_log  logs/error.log  info;
  7 
  8 #pid        logs/nginx.pid;
  9 
 10 
 11 events {
 12     worker_connections  1024;
 13 }
 14 
 15 
 16 http {
 17     include       mime.types;
 18     default_type  application/octet-stream;
 19 
 20     #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
 21     #                  '$status $body_bytes_sent "$http_referer" '
 22     #                  '"$http_user_agent" "$http_x_forwarded_for"';
 23 
 24     #access_log  logs/access.log  main;
 25 
 26     sendfile        on;
 27     #tcp_nopush     on;
 28 
 29     #keepalive_timeout  0;
 30     keepalive_timeout  65;
 31 
 32     #gzip  on;
 33     # 配置上传流,用于负载均衡
 34     upstream tomcat.cyb.com{
 35     server 192.168.31.214:8080 weight=1;
 36         server 192.168.31.215:8080 weight=1;
 37     }
 38     upstream static.cyb.com{
 39     server 192.168.31.212:80 weight=1;
 40         server 192.168.31.213:80 weight=1;
 41     }
 42     server {
 43         listen       80;
 44         server_name  localhost;
 45 
 46         #charset koi8-r;
 47 
 48         #access_log  logs/host.access.log  main;
 49 
 50         location / {
 51            # root   html;
 52            # index  index.html index.htm;
 53             proxy_pass http://tomcat.cyb.com;
 54         }
 55         location ~.*\.(jpg|jpeg|js|css|html)$ {
 56             proxy_pass http://static.cyb.com; 
 57        }
 58         #error_page  404              /404.html;
 59 
 60         # redirect server error pages to the static page /50x.html
 61         #
 62         error_page   500 502 503 504  /50x.html;
 63         location = /50x.html {
 64             root   html;
 65         }
 66 
 67         # proxy the PHP scripts to Apache listening on 127.0.0.1:80
 68         #
 69         #location ~ \.php$ {
 70         #    proxy_pass   http://127.0.0.1;
 71         #}
 72 
 73         # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
 74         #
 75         #location ~ \.php$ {
 76         #    root           html;
 77         #    fastcgi_pass   127.0.0.1:9000;
 78         #    fastcgi_index  index.php;
 79         #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
 80         #    include        fastcgi_params;
 81         #}
 82 
 83         # deny access to .htaccess files, if Apache's document root
 84         # concurs with nginx's one
 85         #
 86         #location ~ /\.ht {
 87         #    deny  all;
 88         #}
 89     }
 90 
 91 
 92     # another virtual host using mix of IP-, name-, and port-based configuration
 93     #
 94     #server {
 95     #    listen       8000;
 96     #    listen       somename:8080;
 97     #    server_name  somename  alias  another.alias;
 98 
 99     #    location / {
100     #        root   html;
101     #        index  index.html index.htm;
102     #    }
103     #}
104 
105 
106     # HTTPS server
107     #
108     #server {
109     #    listen       443 ssl;
110     #    server_name  localhost;
111 
112     #    ssl_certificate      cert.pem;
113     #    ssl_certificate_key  cert.key;
114 
115     #    ssl_session_cache    shared:SSL:1m;
116     #    ssl_session_timeout  5m;
117 
118     #    ssl_ciphers  HIGH:!aNULL:!MD5;
119     #    ssl_prefer_server_ciphers  on;
120 
121     #    location / {
122     #        root   html;
123     #        index  index.html index.htm;
124     #    }
125     #}
126 
127 }
nginx-1
  1 #user  nobody;
  2 worker_processes  1;
  3 
  4 #error_log  logs/error.log;
  5 #error_log  logs/error.log  notice;
  6 #error_log  logs/error.log  info;
  7 
  8 #pid        logs/nginx.pid;
  9 
 10 
 11 events {
 12     worker_connections  1024;
 13 }
 14 
 15 
 16 http {
 17     include       mime.types;
 18     default_type  application/octet-stream;
 19 
 20     #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
 21     #                  '$status $body_bytes_sent "$http_referer" '
 22     #                  '"$http_user_agent" "$http_x_forwarded_for"';
 23 
 24     #access_log  logs/access.log  main;
 25 
 26     sendfile        on;
 27     #tcp_nopush     on;
 28 
 29     #keepalive_timeout  0;
 30     keepalive_timeout  65;
 31 
 32     #gzip  on;
 33 
 34     server {
 35         listen       80;
 36         server_name  localhost;
 37 
 38         #charset koi8-r;
 39 
 40         #access_log  logs/host.access.log  main;
 41 
 42        # location / {
 43        #     root   html;
 44        #     index  index.html index.htm;
 45        # }
 46         # 通过扩展名方式拦截
 47     location ~.*\.(jpg|jpeg|js|css|html)$ {
 48         root /opt;
 49     }
 50 
 51         #error_page  404              /404.html;
 52 
 53         # redirect server error pages to the static page /50x.html
 54         #
 55         error_page   500 502 503 504  /50x.html;
 56         location = /50x.html {
 57             root   html;
 58         }
 59 
 60         # proxy the PHP scripts to Apache listening on 127.0.0.1:80
 61         #
 62         #location ~ \.php$ {
 63         #    proxy_pass   http://127.0.0.1;
 64         #}
 65 
 66         # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
 67         #
 68         #location ~ \.php$ {
 69         #    root           html;
 70         #    fastcgi_pass   127.0.0.1:9000;
 71         #    fastcgi_index  index.php;
 72         #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
 73         #    include        fastcgi_params;
 74         #}
 75 
 76         # deny access to .htaccess files, if Apache's document root
 77         # concurs with nginx's one
 78         #
 79         #location ~ /\.ht {
 80         #    deny  all;
 81         #}
 82     }
 83 
 84 
 85     # another virtual host using mix of IP-, name-, and port-based configuration
 86     #
 87     #server {
 88     #    listen       8000;
 89     #    listen       somename:8080;
 90     #    server_name  somename  alias  another.alias;
 91 
 92     #    location / {
 93     #        root   html;
 94     #        index  index.html index.htm;
 95     #    }
 96     #}
 97 
 98 
 99     # HTTPS server
100     #
101     #server {
102     #    listen       443 ssl;
103     #    server_name  localhost;
104 
105     #    ssl_certificate      cert.pem;
106     #    ssl_certificate_key  cert.key;
107 
108     #    ssl_session_cache    shared:SSL:1m;
109     #    ssl_session_timeout  5m;
110 
111     #    ssl_ciphers  HIGH:!aNULL:!MD5;
112     #    ssl_prefer_server_ciphers  on;
113 
114     #    location / {
115     #        root   html;
116     #        index  index.html index.htm;
117     #    }
118     #}
119 
120 }
nginx-2
  1 #user  nobody;
  2 worker_processes  1;
  3 
  4 #error_log  logs/error.log;
  5 #error_log  logs/error.log  notice;
  6 #error_log  logs/error.log  info;
  7 
  8 #pid        logs/nginx.pid;
  9 
 10 
 11 events {
 12     worker_connections  1024;
 13 }
 14 
 15 
 16 http {
 17     include       mime.types;
 18     default_type  application/octet-stream;
 19 
 20     #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
 21     #                  '$status $body_bytes_sent "$http_referer" '
 22     #                  '"$http_user_agent" "$http_x_forwarded_for"';
 23 
 24     #access_log  logs/access.log  main;
 25 
 26     sendfile        on;
 27     #tcp_nopush     on;
 28 
 29     #keepalive_timeout  0;
 30     keepalive_timeout  65;
 31 
 32     #gzip  on;
 33 
 34     server {
 35         listen       80;
 36         server_name  localhost;
 37 
 38         #charset koi8-r;
 39 
 40         #access_log  logs/host.access.log  main;
 41 
 42        # location / {
 43        #     root   html;
 44        #     index  index.html index.htm;
 45        # }
 46         # 通过扩展名方式拦截
 47     location ~.*\.(jpg|jpeg|js|css|html)$ {
 48         root /opt;
 49     }
 50 
 51         #error_page  404              /404.html;
 52 
 53         # redirect server error pages to the static page /50x.html
 54         #
 55         error_page   500 502 503 504  /50x.html;
 56         location = /50x.html {
 57             root   html;
 58         }
 59 
 60         # proxy the PHP scripts to Apache listening on 127.0.0.1:80
 61         #
 62         #location ~ \.php$ {
 63         #    proxy_pass   http://127.0.0.1;
 64         #}
 65 
 66         # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
 67         #
 68         #location ~ \.php$ {
 69         #    root           html;
 70         #    fastcgi_pass   127.0.0.1:9000;
 71         #    fastcgi_index  index.php;
 72         #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
 73         #    include        fastcgi_params;
 74         #}
 75 
 76         # deny access to .htaccess files, if Apache's document root
 77         # concurs with nginx's one
 78         #
 79         #location ~ /\.ht {
 80         #    deny  all;
 81         #}
 82     }
 83 
 84 
 85     # another virtual host using mix of IP-, name-, and port-based configuration
 86     #
 87     #server {
 88     #    listen       8000;
 89     #    listen       somename:8080;
 90     #    server_name  somename  alias  another.alias;
 91 
 92     #    location / {
 93     #        root   html;
 94     #        index  index.html index.htm;
 95     #    }
 96     #}
 97 
 98 
 99     # HTTPS server
100     #
101     #server {
102     #    listen       443 ssl;
103     #    server_name  localhost;
104 
105     #    ssl_certificate      cert.pem;
106     #    ssl_certificate_key  cert.key;
107 
108     #    ssl_session_cache    shared:SSL:1m;
109     #    ssl_session_timeout  5m;
110 
111     #    ssl_ciphers  HIGH:!aNULL:!MD5;
112     #    ssl_prefer_server_ciphers  on;
113 
114     #    location / {
115     #        root   html;
116     #        index  index.html index.htm;
117     #    }
118     #}
119 
120 }
nginx-3

虚拟主机

简介

  虚拟主机,就是将一台物理服务器虚拟为多个服务器来使用,从而实现一台服务器上配置多个站点,即可以在一台物理主机上配置多个域名。Nginx中,一个server标签是一台虚拟主机,配置多个server标签就虚拟出了多台主机。

  Nginx虚拟主机实现方式有两种域名虚拟方式端口虚拟方式。域名虚拟方式是指不同的虚拟机使用不同的域名,通过不同的域名虚拟出不同的主机;端口虚拟方式是指不同的虚拟机使用相同的域名不同的端口号,通过不同的端口号虚拟出不同的主机。基于端口的虚拟方式不常用。。。

规划

  现在很多生活服务类网络平台都具有这样的功能:不同城市的用户可以打开不同城市专属的站点。用户首先打开的是平台总的站点,然后允许用户切换到不同的城市。其实,不同的城市都是一个不同的站点。

  这里我们要实现的功能是为平台总站点、北京、上海两个城市站点分别创建一个虚拟主机。每个虚拟主机都具有两台Tomcat的负载均衡主机。由于有三个站点,所以共需六台Tomcat主机。

  首先要创建一个web工程,其中就一个index.jsp页面,页面除了显示当前城市外,还要显示城市切换的超链接。为了能够明细的区分出当前访问的Tomcat,在页面中显示出客户端ip和服务器ip。

规划图

注:字打错了,北京应该是:bj.cyb.com

创建web工程

  直接复制前面的web工程,只需要一个jsp即可。

war包

总站

北京

上海 

 

项目源码下载 

百度云盘
链接:https://pan.baidu.com/s/14n1tluHohe5Il_9vi1w9SA  密码:n2sc

修改本机hosts文件

  修改hosts文件,域名绑定ip(绑定的是nginx负载均衡的ip地址!!),不能直接修改hosts文件,需要复制出来后,修改完毕后,在覆盖回去

mac方式

操作步骤:访达->shift+command+G->/private/etc

windows方式

文件位置:C:\Windows\System32\Drivers\etc

修改hosts文件

 

集群

结构图

 

nginx负载均衡nginx.conf

 

nginx.conf

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;

    keepalive_timeout  65;

    #gzip  on;
     # 总站
    upstream entry.com{
           server 192.168.1.111:8080 weight=1;
         server 192.168.1.112:8080 weight=1;
     }
     #北京
    upstream bj.com{
           server 192.168.1.113:8080 weight=1;
         server 192.168.1.114:8080 weight=1;
     }
     #上海
    upstream sh.com{
           server 192.168.1.115:8080 weight=1;
         server 192.168.1.116:8080 weight=1;
     }
    server{
        listen       80;
        server_name  www.entry.com;
           # 负载均衡
        location / {
        proxy_pass http://entry.com;
    }
     }
    server{
        listen       80;
        server_name  bj.entry.com;
           # 负载均衡
        location / {
        proxy_pass http://bj.com;
    }
     }
    server{
        listen       80;
        server_name  sh.entry.com;
           # 负载均衡
        location / {
        proxy_pass http://sh.com;
    }
     }
}

测试(虚拟主机&负载均衡)

搞定!!!!

posted @ 2020-03-30 12:01  陈彦斌  阅读(...)  评论(...编辑  收藏