文章目录
-
-
- Nginx概述
- Nginx作用
- [Nginx的安装 -->Docker](#Nginx的安装 -->Docker)
-
- [3.1 安装Nginx](#3.1 安装Nginx)
- [3.2 Nginx的配置文件](#3.2 Nginx的配置文件)
- [3.3 修改docker-compose文件](#3.3 修改docker-compose文件)
- Nginx源码安装
- nginx常用命令
- nginx配置文件
- Nginx的反向代理【`重点`】
-
- 基于Nginx实现反向代理
- [4.3 关于Nginx的location路径映射](#4.3 关于Nginx的location路径映射)
- Nginx负载均衡【`重点`】
-
- [5.1 轮询](#5.1 轮询)
- [5.2 权重](#5.2 权重)
- [5.3 ip_hash](#5.3 ip_hash)
- fair
- upstream模块中的常用参数
- Nginx动静分离【`重点`】
-
- [6.1 动态资源代理](#6.1 动态资源代理)
- [6.2 静态资源代理](#6.2 静态资源代理)
- [root 和alias指令区](#root 和alias指令区)
- 权限项目部署
- Nginx高可用
- Nginx的进程模型
-
Nginx概述
url
http://nginx.org/en/download.html
Nginx是由俄罗斯人研发的,应对Rambler的网站,并且2004年发布的第一个版本。
Nginx是lgor Sysoev为俄罗斯访问量第二的rambler.ru站点设计开发的。从2004年发布至今,凭借开源的力量,已经接近成熟与完善。
Nginx功能丰富,可作为HTTP服务器,也可作为反向代理服务器,邮件服务器。支持FastCGI、SSL、Virtual Host、URL Rewrite、Gzip等功能。并且支持很多第三方的模块扩展。
Nginx的稳定性、功能集、示例配置文件和低系统资源的消耗让他后来居上,在全球活跃的网站中有12.18%的使用比率,大约为2220万个网站。
Nginx的特点:
- 稳定性极强。 7*24小时不间断运行。
- Nginx提供了非常丰富的配置实例。
- 占用内存小,并发能力强(官方给的数据是5w)
- 负载均衡策略
- 动静态分离
- 擅长处理静态资源,tomcat擅长处理动态资源
Nginx作用
正向代理
正向代理是给客户端做代理,客户端清楚要访问的目标服务器。
正向代理服务器作用如下
- 访问原来无法访问的资源
- 用作缓存,加速访问速度
- 对客户端访问授权,上网进行认证
- 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
反向代理
反向代理给服务端做代理,客户但不知道要访问那台服务器。
反向代理服务器作用如下:
- 保护内网安全
- 负载均衡
- 缓存,减少服务器的压力
负载均衡
将多个请求均匀的分配到多台服务器上,减轻每台服务器的压力,提高服务的吞吐量。
Nginx提供的负载均衡策略有2种:
内置策略:轮询,权重,Ip hash。
扩展策略:fair(会根据后端服务器的响应时间来分配请求,以实现更加均衡的负载分配。fair策略会优先将请求发送到响应时间较短的服务器上,从而提高整体性能和稳定性。)
Ip hash算法:对客户端请求的ip进行hash操作,然后根据hash结果将同一个客户端ip的请求分发给同一台服务器进行处理,可以解决session不共享的问题
动静分离
动静分离是指在web服务器架构中,将静态页面与动态页面或者静态内容接口和动态内容接口分开不同系统访问的架构设计方法,进而提升整个服务访问性能和可维护性。
1、代理服务器和静态服务器即为一台服务器上,这里只是为了明显区分动静分离所处服务器的不同;
2、静态服务器中,存放的资源主要是源代码文件、图片、属性、样式以及其它所有非动态的资源文件;
3、调度规则,即为代理服务器,这里是Nginx的服务器调度规则;
4、动态服务器,其种类比较繁多,可以是Apache、Tomcat、IIS以及其它Web服务器,它们一般分别隶属于一台服务器;
Nginx的安装 -->Docker
3.1 安装Nginx
使用Docker-Compose安装
yml
version: '3.1'
services:
nginx:
restart: always
image: daocloud.io/library/nginx:latest
container_name: nginx
ports:
- 80:80
3.2 Nginx的配置文件
关于Nginx的核心配置文件nginx.conf
json
worker_processes 1;
error_log /var/log/nginx/error.log warn;
# 以上统称为全局块,
# worker_processes他的数值越大,Nginx的并发能力就越强
# error_log 代表Nginx的错误日志存放的位置
events {
worker_connections 1024;
}
# events块
# worker_connections他的数值越大,Nignx并发能力越强
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# location块
# root:将接收到的请求根据/usr/share/nginx/html去查找静态资源
# index: 默认去上述的路径中找到index.html或者index.htm
}
# server块
# listen: 代表Nginx监听的端口号
# localhost:代表Nginx接收请求的ip
}
# http块
# include代表引入一个外部的文件 -> /mime.types中放着大量的媒体类型
# include /etc/nginx/conf.d/*.conf; -> 引入了conf.d目录下的以.conf为结尾的配置文件
3.3 修改docker-compose文件
为了方便修改Nginx配置,修改yml文件
yml
version: '3.1'
services:
nginx:
restart: always
image: daocloud.io/library/nginx:latest
container_name: nginx
ports:
- 80:80
volumes:
- /opt/docker_nginx/conf.d/:/etc/nginx/conf.d
Nginx源码安装
1、下载nginx源码包
shell
wget http://nginx.org/download/nginx-1.20.1.tar.gz
2、解压
shell
tar -zxf nginx-1.20.1.tar.gz
3、安装c/c++的编译器
shell
yum install gcc
yum install gcc-c++
yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel
因为下载的包是源码包,需要编译,安装后才能使用。而nginx是c写的,因此需要安装c/c++的编译器
如上,gcc已经安装完毕,下面来安装nginx
4、安装nginx
shell
1、进入nginx目录
2、 ./configure
3、make install
5、安装成功后的位置
nginx默认会按照在/usr/local/nginx中
nginx常用命令
./nginx 启动
./nginx -s stop 停止
./nginx -s reload 重启(重新加载nginx配置文件)
./nginx -v 查看nginx版本号
nginx配置文件
配置文件位置
配置文件结构详情
json
########### 每个指令必须有分号结束。#################
#user administrator administrators; #配置用户或者组,默认为nobody nobody。
worker_processes 1; #允许生成的进程数,默认为1
#pid /nginx/pid/nginx.pid; #指定nginx进程运行文件存放地址
error_log log/error.log debug; #制定日志路径,级别。这个设置可以放入全局块,http块,server块,级别以此为:debug|info|notice|warn|error|crit|alert|emerg
events {
accept_mutex on; #设置网路连接序列化,防止惊群现象发生,默认为on
multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off
#use epoll; #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
worker_connections 1024; #最大连接数,默认为512
}
http {
include mime.types; #文件扩展名与文件类型映射表
default_type application/octet-stream; #默认文件类型,默认为text/plain
#access_log off; #取消服务日志
log_format myFormat '$remote_addr--$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for'; #自定义格式
access_log log/access.log myFormat; #combined为日志格式的默认值
sendfile on; #允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。
sendfile_max_chunk 100k; #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。
keepalive_timeout 65; #连接超时时间,默认为75s,可以在http,server,location块。
upstream mysvr {
server 127.0.0.1:7878;
server 192.168.10.121:3333 backup; #热备
}
error_page 404 https://www.baidu.com; #错误页
error_page 500 https://www.jd.com; #错误页
server {
keepalive_requests 120; #单连接请求上限次数。
listen 4545; #监听端口
server_name 127.0.0.1; #监听地址
location ~*^.+$ { #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。
#root path; #根目录
#index vv.txt; #设置默认页
proxy_pass http://mysvr; #请求转向mysvr 定义的服务器列表
deny 127.0.0.1; #拒绝的ip
allow 172.18.5.54; #允许的ip
}
}
}
1、全局块
2、events块
3、http块
a)server块
1)location块
- 全局块:配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。
- events块:配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
- http块:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。
- server块:配置虚拟主机的相关参数,一个http中可以有多个server。
- location块:配置请求的路由,以及各种页面的处理情况。
Nginx的反向代理【重点
】
基于Nginx实现反向代理
准备一个目标服务器。
启动了之前的tomcat服务器。
编写default.conf配置文件,通过Nginx访问到tomcat服务器。
json
# 监听80端口
server{
listen 80;
server_name localhost;
# 基于反向代理访问到Tomcat服务器
location / {
proxy_pass http://192.168.199.109:8081/;
}
}
4.3 关于Nginx的location路径映射
优先级关系如下:
(location=) > (location /aa/bb/cc) > (location ~)
json
# 1. 精确匹配
location = / {
# 精准匹配,主机名后面不能带任何的字符串,等号决定匹配的内容,比如:http://baidu.com
}
# 案例
# 用户访问# http:192.168.193.88/abc nginx
#会自动代理到 http://192.168.193.88:8080/
location =/abc {
proxy_pass http://192.168.193.88:8080/;
}
# 2. 通用匹配
location /xxx {
# 匹配所有以/xxx开头的路径,比如:http://baidu.com/xxx
}
# 案例
# 用户访问访问 http://192.168.193.88/qfjava/webdemo/index.jsp
#会自动代理到 http://192.168.193.88:8080/webdemo/index.jsp
location /qfjava {
proxy_pass http://192.168.193.88:8080/;
}
# 3. 正则匹配
location ~ /xxx {
# 匹配所有以/xxx开头的路径
}
# 4. 匹配开头路径
location ^~ /images/ {
# 匹配所有以/images开头的路径
}
# 5. 匹配后缀
location ~* \.(gif|jpg|png)$ {
# 匹配以gif或者jpg或者png为结尾的路径
}
# 6. 全部通配
location / {
# 匹配全部路径
}
Nginx负载均衡【重点
】
Nginx为我们默认提供了三种负载均衡的策略:
- 轮询:将客户端发起的请求,平均的分配给每一台服务器
- 权重:会将客户端的请求,根据服务器的权重值不同,分配不同的数量。
- ip_hash:基于发起请求的客户端的ip地址不同,他始终会将请求发送到指定的服务器上。
5.1 轮询
想实现Nginx轮询负载均衡机制只需要在配置文件中添加以下内容
json
upstream 名字 { # 给空格
server ip:port; # 给分号 server 192.168.193.66:8080
server ip:port;
...
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://upstream的名字/; # 给分号
}
}
5.2 权重
实现权重的方式
json
# 名字中不要出现下换线
upstream 名字 {
server ip:port weight=权重比例; # server 192.168.193.66:8080 weight=8;
server ip:port weight=权重比例; # server 192.168.193.66:8080 weight=2;
...
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://upstream的名字/;
}
}
5.3 ip_hash
ip_hash实现
json
upstream 名字 {
ip_hash;
server ip:port;
server ip:port;
...
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://upstream的名字/;
}
}
fair
fair 模块(第三方)来实现负载均衡,fair 采用的不是内建的算法,而是可以根据页面大小、响应时间智能的进行负载均衡。因为是第三方所以需要单独安装,安装方式参考下面文档
json
upstream 名字 {
server ip:port;
server ip:port;
fair;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://upstream的名字/;
}
}
upstream模块中的常用参数
json
upstream linuxe_backend {
server 192.168.1.110 down; #该节点不可用
server 192.168.1.120 backup; #其他节点挂了后该节点自动上线
server 192.168.1.130 max_failes=1 fail_timeout=10s weight=5;
server backend1.linuxe.cn 8080 weight=3 max_conns=10;
}
server:负载均衡后端服务器的IP或域名,不写端口的话默认是80。高并发场景用域名,再通过DNS进行负载均衡
weight:后端服务器权重,默认为1,权重越大接收的请求越多。例:weight=5
max_fails:检查节点的健康状态并允许请求失败的次数,达到该次数就将节点下线。默认为1,0表示禁止失败尝试。例:max_fails=2
fail_timeout:max_fails失败次数达到限制后暂停该节点服务的时间,默认是10秒。例:fail_timeout=10s
backup:热备配置,当服务池中所有服务器均出现问题后会自动上线backup服务器
down:标志服务器不可用,不参与负载均衡。这个参数通常配合IP_HASH使用
max_conns:限制最大连接数,通常对后端服务器硬件不一致的情况进行配置
Nginx动静分离【重点
】
动静分离是指在web服务器架构中,将静态页面与动态页面或者静态内容接口和动态内容接口分开不同系统访问的架构设计方法,进而提升整个服务访问性能和可维护性。
动:必须依赖服务器返回,比如Controller,数据库相关操作。
静:不需要依赖容器的比如css/js/html或者图片等文件
Nginx的并发能力公式:
worker_processes * worker_connections / 4 | 2 = Nginx最终的并发能力
动态资源需要/4,静态资源需要/2.
Nginx通过动静分离,来提升Nginx的并发能力,更快的给用户响应。
6.1 动态资源代理
使用proxy_pass动态代理
json
# 配置如下
location / {
proxy_pass 服务器的路径;
}
6.2 静态资源代理
使用root或者alias静态代理
json
server{
listen 80;
server_name localhost;
location /html {
root data; // 在data/html下面找文件
index a.html; # 配置默认页面
}
location /images {
root data; // 在data/images下面找文件
autoindex on; # 地址栏访问 http://192.168.193.88/images/
}
}
root 和alias指令区
json
location /img/ {
alias /var/www/image/;
}
若按照上述配置的话,则访问/img/目录里面的文件时,ningx会自动去/var/www/image/目录找文件
Json
location /img/ {
root /var/www/image;
}
若按照这种配置的话,则访问/img/目录下的文件时,nginx会去/var/www/image/img/目录下找文件
alias是一个目录别名的定义,root则是最上层目录的定义。
还有一个重要的区别是alias后面必须要用"/"结束,否则会找不到文件的。。。而root则可有可无
权限项目部署
json
# 权限服务器集群
upstream rbac {
server 172.30.77.207:8082;
server 172.30.77.207:8083;
}
server {
listen 80;
server_name localhost;
location / { # 反向代理到后端服务器
proxy_pass http://rbac;
}
location /rbac { # 权限项目前端相关静态页面
root data;
index index.html;
}
}
前端页面访问:http://www.2105.com/rbac/login.html
后台接口访问:http://www.2105.com/user/page
后台接口取消上下文路径,多个应用可以根据端口号或者location来区分。
Nginx高可用
什么是高可用
高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。
假设系统一直能够提供服务,我们说系统的可用性是100%。
如果系统每运行100个时间单位,会有1个时间单位无法提供服务,我们说系统的可用性是99%。很多公司的高可用目标是4个9,也就是99.99%,这就意味着,系统的年停机时间为8.76个小时。
在生产环境上很多时候是以
Nginx
做反向代理对外提供服务,但是一天Nginx难免遇见故障,如:服务器宕机。当Nginx
宕机那么所有对外提供的接口都将导致无法访问。
双机热备+自动故障转移
这种方案是国内企业中最为普遍的一种高可用方案,双机热备其实就是指一台服务器在提供服务,另一台为某服务的备用状态,当一台服务器不可用另外一台就会顶替上去。
有了双机热备方案之后,还不够,每次出现故障需要人工介入恢复势必会增加系统的不可服务实践。所以,又往往是通过自动故障转移来实现系统的高可用。
Nginx如何解决高可用的
Nginx解决高可用需要准备两台nginx,一个是主(Master)服务器,一个是备(backup)服务器,使用keepalived基于主和备可以创建出一台虚拟服务器供客户端调用。虚拟服务器接收到请求会先把请求交给主服务器处理,如果主服务器宕机就把请求交给备服务器处理。
Keepalived是什么
Keepalived
软件起初是专为LVS
(Linux Virtual Server,Linux虚拟服务器)负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现高可用的VRRP (Virtual Router Redundancy Protocol ,虚拟路由器冗余协议)
功能。因此,Keepalived
除了能够管理LVS软件外,还可以作为其他服务(例如:Nginx、Haproxy、MySQL等)
的高可用解决方案软件
Keepalived实现Nginx高可用
安装前需要准备好两台虚拟器,两台都需要安装nginx和keepalived。
192.168.147.10(主)
192.168.147.11(备)
1、安装keepalived
shell
yum -y install keepalived # 配置文件在/etc/keepalived/keepalived.conf
2、修改主服务器keepalived.conf
json
# 检测脚本
vrrp_script chk_http_port {
script "/usr/local/src/check_nginx_pid.sh" #心跳执行的脚本,检测nginx是否启动
interval 2 #(检测脚本执行的间隔,单位是秒)
weight 2 #权重
}
#vrrp 实例定义部分
vrrp_instance VI_1 {
state MASTER # 指定keepalived的角色,MASTER为主,BACKUP为备
interface ens33 # 当前进行vrrp通讯的网络接口卡(当前centos的网卡) 用ifconfig查看你具体的网卡
virtual_router_id 66 # 虚拟路由编号,主从要一直
priority 100 # 优先级,数值越大,获取处理请求的优先级越高
advert_int 1 # 检查间隔,默认为1s(vrrp组播周期秒数)
track_script {
chk_http_port #(调用检测脚本)
}
virtual_ipaddress {
192.168.147.88 # 定义虚拟ip(VIP),可多设,每行一个
}
}
virtual_ipaddress 里面可以配置vip,在线上通过vip来访问服务。
interface需要根据服务器网卡进行设置通常查看方式ip addr
3、修改备服务器配置文件
json
检测脚本
vrrp_script chk_http_port {
script "/usr/local/src/check_nginx_pid.sh" #心跳执行的脚本,检测nginx是否启动
interval 2 #(检测脚本执行的间隔)
weight 2 #权重
}
#vrrp 实例定义部分
vrrp_instance VI_1 {
state BACKUP # 指定keepalived的角色,MASTER为主,BACKUP为备
interface ens33 # 当前进行vrrp通讯的网络接口卡(当前centos的网卡) 用ifconfig查看你具体的网卡
virtual_router_id 66 # 虚拟路由编号,主从要一直
priority 99 # 优先级,数值越大,获取处理请求的优先级越高
advert_int 1 # 检查间隔,默认为1s(vrrp组播周期秒数)
track_script {
chk_http_port #(调用检测脚本)
}
virtual_ipaddress {
192.168.147.88 # 定义虚拟ip(VIP),可多设,每行一个
}
}
4、检测脚本
shell
/usr/local/src/check_nginx_pid.sh
shell
#检测nginx是否启动了
A=`ps -C nginx --no-header |wc -l`
if [ $A -eq 0 ];then #如果nginx没有启动就启动nginx
systemctl start nginx #重启nginx
if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then #nginx重启失败,则停掉keepalived服务,进行VIP转移
killall keepalived
fi
fi
5、脚本授权
shell
chmod 700 check_nginx_pid.sh # 脚本必须通过授权,不然没权限访问
6、启动keepalived
shell
systemctl start keepalived
搭建高可用需要注意一下几点
1、keepalived启动先ngixn必须是启动成功的,否则keepalived启动不了
2、nginx停止后keepalived也会停止
3、主宕机后启动顺序是先启动nginx,在启动keepalived
7、测试顺序
1、主备都启动时访问速虚拟主机请求会交给主处理
2、把主中的nginx关闭后,请求会交给备服务器处理
Keepalived实现自动故障转移
Keepalived
高可用服务之间的故障切换转移,是通过VRRP
来实现的。在
Keepalived
服务正常工作时,主Master
节点会不断地向备节点发送(多播的方式)心跳消息,用以告诉备Backup
节点自己还活着,当主Master
节点发生故障时,就无法发送心跳消息,备节点也就因此无法继续检测到来自主Master
节点的心跳了,于是调用自身的接管程序,接管主Master节点的 IP资源及服务。而当主 Master节点恢复时,备Backup节点又会释放主节点故障时自身接管的IP资源及服务,恢复到原来的备用角色。
Nginx的进程模型
Nginx启动成功默认会有两个进程,master和work。
Master进程
master进程主要用来管理worker进程 ,具体包括如下4个主要功能:
1、接收来自外界的信号。
2、向各worker进程发送信号。
3、监控woker进程的运行状态。
4、当woker进程退出后(异常情况下),会自动重新启动新的woker进程
5、热加载ngixn配置文件
master进程充当整个进程组与用户的交互接口,同时对进程进行监护。它不需要处理网络事件,不负责业务的执行,只会通过管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。 我们要控制nginx,只需要通过kill向master进程发送信号就行了。比如kill -HUP pid,则是告诉nginx,从容地重启nginx,我们一般用这个信号来重启nginx,或重新加载配置,因为是从容地重启,因此服务是不中断的。
kill -HUP 是用来向指定进程发送一个HUP信号,许多程序在收到HUP信号时,会重新读取配置文件
master进程在接收到HUP信号后是怎么做的呢?
1)首先master进程在接到信号后,会先重新加载配置文件,然后再启动新的worker进程,并向所有老的worker进程发送信号,告诉他们可以光荣退休了。
2)新的worker在启动后,就开始接收新的请求,而老的worker在收到来自master的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出。
work进程
work进程主要用来处理网络请求,像网络事件,则是放在worker进程中来处理了。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。worker进程的个数是可以设置的(默认是1),一般我们会设置与机器cpu核数一致,这里面的原因与nginx的进程模型以及事件处理模型是分不开的。
worker进程之间是平等的,每个进程,处理请求的机会也是一样的
当我们提供80端口的http服务时,一个连接请求过来,每个work进程都有可能处理这个连接,怎么做到的呢?
Nginx采用异步非阻塞的方式来处理网络事件,类似于Libevent,具体过程如下:
1)接收请求:首先,每个worker进程都是从master进程fork过来,在master进程建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,每个work进程都可以去accept这个socket(listenfd)。当一个client连接到来时,所有accept的work进程都会受到通知,但只有一个进程可以accept成功,其它的则会accept失败。为保证只有一个进程处理该连接,Nginx提供了一把共享锁accept_mutex来保证同一时刻只有一个work进程在accept连接。所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。
2)处理请求:当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。
我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。worker进程之间是平等的,每个进程,处理请求的机会也是一样的。
后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹