As the core of Nginx Web server configuration

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/qq_42298432/article/details/101715672

Nginx

Nginx web server is asynchronous frame can also be used as a reverse proxy servers, mail servers, load balancers and HTTP caching. Change the software created by Igor Saisuoyefu and in 2004 the first public release;. Was established in 2011 to provide support to the company of the same name, March 11, 2019, Nginx F5 networks company was acquired for $ 670 million.
 

Nginx features:

1.Nginx use asynchronous event-driven processing request, Nginx event-driven modular architecture can provide more predictable performance under load out.
 
2.Nginx is a performance-oriented design HTTP server, compared to Apache, lighttpd has occupy less memory , different advantages of higher stability, with older versions (<= 2.2) of Apache, Nginx does not use a thread per client design model, but full use of asynchronous logic, thereby reducing the context of scheduling overhead, so more concurrent server capacity, overall modular design, by a third party module library rich library of modules, flexible configuration, under LInux operating system, Nginx use epoll event model, thanks to this, Nginx in the Linux operating system efficiency is very high.
 
3. can massively parallel processing
Nginx in the official results of the test, it is possible to support fifty thousand concurrent connections, but in actual operation, can support 20,000 to 40,000 concurrent connections.
 

Compared with Apache:

Nginx has written a clear goal is to exceed the performance of the Apache Web server, Nginx serve static files out of the box, use much less memory than Apache, Apache's request can be processed in about four times per second. Low with inferior performance in Apache fairly, sometimes lower than, but in a highly concurrent Nginx can maintain high performance low resource consumption, as well as highly modular design, modules written in simple, concise profiles. And this performance at the cost of reduced flexibility.
 
Code the Status:
1xx:
2xx: Success response class code, 200 is
3xx: Redirection response code class, 301, 302, 304
4xx: Client Error, 403, 404
5xx: server-side error, 502

Common status code:
200: successful, requesting all the data sent in response entity-body portion of the packet; the OK
301: the URL points to the resource request has been deleted; but the response packet by the header Location indicates the resources are now the new location is located; Moved Permanently
302: 301 with similar, but in the response packet in which resources are now specified by location new temporary location; Found
304: the client sends requests conditions, but resources on the server has not changed , the response to this response status code to notify the client; not Modified
401: need to enter the account number and password authentication in order to access resources; Unauthorized
403: requests are prohibited; forbidden
404: the server can not find the resources requested by the client; not found
500: internal server error; internal server error
502: proxy server receives a response from the backend server dummy; Bad Gateway

nginx installation configuration:

 
The official pre-package:
http://nginx.org/packages/centos/7/x86_64/RPMS/
compiler installation:

	~] yum groupinstall "Development Tools" "Server Platform Development"							
	~] yum install pcre-devel openssl-devel zlib-devel
	~] useradd -r nginx
	~] ./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_dav_module --with-http_stub_status_module --with-threads --with-file-aio
	~] make && make install	
配置:
			主配置文件的配置指令:
				directive value [value2 ...];
				
				注意:
					(1) 指令必须以分号结尾;
					(2) 支持使用配置变量;
						内建变量:由Nginx模块引入,可直接引用;
						自定义变量:由用户使用set命令定义;
							set variable_name value;
							引用变量:$variable_name
									
							
			主配置文件结构:
				main block:主配置段,也即全局配置段;
					event {
						...
					}:事件驱动相关的配置;
				http {
					...
				}:http/https 协议相关的配置段;
				mail {
					...
				}
				stream {
					...
				}
			
			http协议相关的配置结构
				http {
					...
					...:各server的公共配置
					server {
						...
					}:每个server用于定义一个虚拟主机;
					server {
						...
						listen 
						server_name
						root
						alias
						location [OPERATOR] URL {
							...
							if CONDITION {
								...
							}
						}
					}
				}

Nginx configuration directives
Category:
configuration running essential
optimization of performance-related configuration
for debugging and positioning problems associated configuration
event-driven relevant configuration

正常运行必备的配置:
					1、user
						Syntax:	user user [group];
						Default:	user nobody nobody;
						Context:	main
						
						定义工作进程使用的用户和组凭据。如果省略组,则使用名称等于user的组。
						
					2、pid /PATH/TO/PID_FILE;
						指定存储nginx主进程进程号码的文件路径;
						
					3include file | mask;
						指明包含进来的其它配置文件片断;
						
					4、load_module file;
						指明要装载的动态模块;
性能优化相关的配置:
					1、worker_processes number | auto;
						worker进程的数量;通常应该等于小于当前主机的cpu的物理核心数;
						auto:当前主机物理CPU核心数;
						
					2、worker_cpu_affinity cpumask ...;				
						指定处理数据响应进程的顺序    
						
						worker_cpu_affinity auto [cpumask];
						
						CPU MASK00000000000000010CPU
							000000101CPU
							000001002CPU
							... ...
					3、worker_priority number;
						指定worker进程的nice值,设定worker进程优先级;[-20,20]
						
					4、worker_rlimit_nofile number;
						worker进程所能够打开的文件数量上限;
						
					查看配置结果:		watch -n.5 'ps axo pid,psr,comm | grep nginx' 动态查看进程
										ps axo pid,psr,comm | grep nginx 查看进程
						    			axo pid,psr,ni,comm | grep nginx 查看进程优先级
调试、定位问题:
					1、daemon on|off;	
						是否以守护进程方式运行Nignx;
						
					2、master_process on|off;
						是否以master/worker模型运行nginx;默认为on;
						
					3、error_log file [level];
事件驱动相关的配置:
					events {
						...
					}
					
					1、worker_connections number[3073]; 1024
						每个worker进程所能够打开的最大并发连接数数量;
						
						worker_processes * worker_connections
							4 x 1024 = 3073
					2use method;
						指明并发连接请求的处理方法;
							
							use epoll;
							Epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目
1.效率提升,epoll对于句柄事件的选择不是遍历的,是事件响应的,就是句柄上事件来就马上选择出来,不需要遍历整个句柄链表,因此效率非常高,内核将句柄用红黑树保存的,IO效率不随FD数目增加而线性下降。
2.内存拷贝, select让内核把 FD 消息通知给用户空间的时候使用了内存拷贝的方式,开销较大,但是Epoll 在这点上使用了共享内存的方式,这个内存拷贝也省略了。
相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多
					
					3、accept_mutex on | off;
						处理新的连接请求的方法;on意味着由各worker轮流处理新请求,Off意味着每个新请求的到达都会通知所有的worker进程;

http protocol configuration:

http {
					全局配置
					... ...
					server {
						...
						listen 80
						server_name
						root
						location [OPERATOR] /uri/ {
							...
						}  可定义多个server字段
				}

Socket-related configuration:

1. the .server {...}
configure a virtual host;

server {
	listen address[:PORT]|PORT;
	server_name domain;
	root /PATH/;

2.
the listen PORT | address [: Port] | UNIX: / the PATH / the TO / SOCKET_FILE
the listen address [: Port] [the default_server] [SSL] [HTTP2 | SPDY] [backlog = Number] [rcvbuf = size] [size = sndbuf ]
 
the default_server: set as the default virtual host;
ssl: limited only to provide services via ssl connection;
backlog = Number: backup queue length;
rcvbuf = size: receive buffer size;
sndbuf = size: sending buffer size;
3.
server_name name ...;
host name specified virtual host; can after separated by whitespace with a plurality of strings;
and support any character of any length with the through-*;. server_name * .ityunncom www.ityunn *
support ~ start characters do regular expression pattern matching;. server_name ~ ^ www \ d + \ ityunn.com $

Matching mechanisms:
(1) First, the string is an exact match;
(2) left the * wildcard;
(3) * wildcard right;
(4) the regular expression;

4.
tcp_nodelay on | off
 
在keepalived模式下的连接是否启用TCP_NODELAY选项;
解释:在开启长连接的时候,delay是当客户端同时向服务器发起三个数据包请求时,服务器会将这个三个请求打包成一个报文,一个报文承载三个请求文件。这样就可以减少服务器的带宽。但是这样的话客户端发送三个请求报文的时候回应的时候是第三个响应报文先回应的。所以说delay是延迟响应。nodelay就是无延迟响应。(但是为什么要长连接的情况下才有用呢,因为在不长连接的情况下客户端发送第一个请求时,服务器会响应一个请求,但是在发送第二个请求的时候服务器是不知道这个是同一个客户端发送的请求)
 
5. tcp_nopush on|off

要结合sendfile来使用 具体是用来在一个包中发送响应头和文件的开始。以完整的数据包发送文件。
在sendfile模式下,是否启用TCP_CORK选项;
 
6. sendfile on | off
 
是否启用sendfile功能;
在一个报文中发送一个文件,而不是分开发送的。使用sendfile机制的话我们基于内核把文件发送过来以后,守护进程呢没有,内核不会生成守护进程,不是TCP守护,而是在应用层守护,我们封装一个http的响应报文的时候因该是nginx或者httpd才有权限封装的。
等待用户空间把首部送过来以后打包成一个报文向外发送的

定义路径相关的配置

6. root path;
设置web资源路径映射;用于指明用户请求的url所对应的本地文件系统上的文档所在目录路径;可用的位置:http, server, location, if in location;
 
7. location [ = | ~ | ~* | ^~ ] uri { … }
根据请求URI设置配置。
 
在一个server中location配置段可存在多个,用于实现从uri到文件系统的路径映射;ngnix会根据用户请求的URI来检查定义的所有location,并找出一个最佳匹配,而后应用其配置;
 
=:对URI做精确匹配;例如, http://www.ityunn.com/, http://www.ityunn.com/index.html
location = / {

}
~:对URI做正则表达式模式匹配,区分字符大小写;
~*:对URI做正则表达式模式匹配,不区分字符大小写;
^~:对URI的左半部分做匹配检查,不区分字符大小写;
不带符号:匹配起始于此uri的所有的url;
 
匹配优先级:=, ^~, ~/~*,不带符号;
 
root /vhosts/www/htdocs/
http://www.ityunn.com/index.html --> /vhosts/www/htdocs/index.html
 
server {
root /vhosts/www/htdocs/
 
location /admin/ {
root /webapps/app1/data/
      }
}
 
8、alias path;
定义路径别名,文档映射的另一种机制;仅能用于location上下文;
 
注意:location中使用root指令和alias指令的意义不同;
(a) root,给定的路径对应于location中的/uri/左侧的/;
(b) alias,给定的路径对应于location中的/uri/右侧的/;
 
9、index file …;
默认资源;http, server, location;
 
10、error_page code … [=[response]] uri;
指定错误的访问页面
 
案例: error_page 404 /notfound.html;
location ^~ /notfound.html {
root /web/html/page/;
}

定义客户端请求的相关配置

11. keepalived_timeout timeout [header_timeout]
设定保持连接的超时时长,0表示禁止长连接,默认为75s;

12. keepalilved_request number;
在一次长连接上所允许请求的资源的最大数量,默认为100;

13. keepalived_disable none | browser …;
对那种浏览器禁用长连接;

14. send_timeout time;
向客户端发送响应报文的超时时长,此处,是指两次写操作直接的间隔时长;

15. client_body_buffer_size size;
用于接收客户端请求报文的body部分的缓冲区大小;默认为16k;超出此大小时,其将被暂存到磁盘上的由client_body_temp_path指令所定义的位置;
 
16. client_body_temp_path path [level1 [level2 [level3]]];
设定用于存储客户端请求报文的body部分的临时存储路径及子目录结构和数量;
 
16进制的数字;
 
client_body_temp_path /var/tmp/client_body 2 1 1
1:表示用一位16进制数字表示一级子目录;0-f
2:表示用2位16进程数字表示二级子目录:00-ff
2:表示用2位16进程数字表示三级子目录:00-ff
 
对客户端进行限制的相关配置:
17. limit_rate rate;
限制响应给客户端的传输速率,单位是bytes/second,0表示无限制;
 
18. limit_except method … { … }
限制对指定的请求方法之外的其它方法的使用客户端;
 
limit_except GET {
allow 192.168.1.0/24;
deny all;
}
 
文件操作优化的配置
19. aio on | off | threads[=pool];
是否启用aio功能;
 
这个配置不关键 21、directio size | off;
在Linux主机启用O_DIRECT标记,此处意味文件大于等于给定的大小时使用,例如directio 4m;

20. open_file_cache off;
open_file_cache max=N [inactive=time];
nginx可以缓存以下三种信息:
(1) 文件的描述符、文件大小和最近一次的修改时间;
(2) 打开的目录结构;
(3) 没有找到的或者没有权限访问的文件的相关信息;
 
max=N:可缓存的缓存项上限;达到上限后会使用LRU算法实现缓存管理;
 
inactive=time:缓存项的非活动时长,在此处指定的时长内未被命中的或命中的次数少于open_file_cache_min_uses指令所指定的次数的缓存项即为非活动项;定义一个从缓存中删除元素的时间,如果在这段时间内它没有被访问;默认情况下,它是60秒;
 
21. open_file_cache_valid time;
(这个可能会误伤除一些缓存)
缓存项有效性的检查频率;默认为60s;
 
22. open_file_cache_min_uses number;
在open_file_cache指令的inactive参数指定的时长内,至少应该被命中多少次方可被归类为活动项;
 
23 open_file_cache_errors on | off;
是否缓存查找时发生错误的文件一类的信息;

	例如:open_file_cache        
								max=1000 inactive=20s;
								open_file_cache_valid    30s;
								open_file_cache_min_uses 2;
								open_file_cache_errors   on;

ngx_http_access_module模块:

 
实现基于ip的访问控制功能;
 
24. allow address IP | all;
25. deny address  IP | all;
 
http, server, location, limit_except

location ^~ /nginx/ {
							root /web/httpd/;
							allow address 192.168.10.10;
							deny address all;
							}

ngx_http_auth_basic_module模块

 
实现基于用户的访问控制,使用basic机制进行用户认证;
 
26. auth_basic string | off;
27. auth_basic_user_file file;

 location ^~ /nginx/ {
                root /web/httpd/;
                auth_basic 'This is Web Server';
                auth_basic_user_file '/etc/nginx/.yonghu';                

 
注意:htpasswd命令由httpd-tools所提供;

ngx_http_stub_status_module模块

 
用于输出nginx的基本状态信息;
 
Active connections: 291
server accepts handled requests
16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
 
Active connections: 活动状态的连接数;
accepts:已经接受的客户端请求的总数;
handled:已经处理完成的客户端请求的总数;
requests:客户端发来的总的请求数;
Reading:处于读取客户端请求报文首部的连接的连接数;
Writing:处于向客户端发送响应报文过程中的连接数;
Waiting:处于等待客户端发出请求的空闲连接数;
 
28.stub_status;

	配置示例:
						location  /nginx_status {
							stub_status;
						}

ngx_http_log_module模块

 
ngx_http_log_module模块以指定的格式写入请求日志。
 
29. log_format name string …;
重点:这个是能在httpd字段定义格式,access_log可以在httpd,server,location中调用;
string可以使用nginx核心模块及其它模块内嵌的变量;
 
 
30.access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
access_log off;

 
访问日志文件路径,格式及相关的缓冲的配置;
压缩级别可以设置在1(最快的,较少的压缩)到9(最慢,最好的压缩)之间。默认情况下,缓冲区大小等于64K字节,压缩级别设置为1。zcat“随时都可以。

示例配置:
log_format baba '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $bytes_sent '
                       '"$http_referer" "$http_user_agent" "$gzip_ratio"';

access_log /spool/logs/nginx-access.log baba buffer=32k gzip flush=5m;

 
 
31. open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
open_log_file_cache off;
缓存各日志文件相关的元数据信息;
 
max:缓存的最大文件描述符数量;
min_uses:在inactive指定的时长内访问大于等于此值方可被当作活动项;
inactive:非活动时长;
valid:验正缓存中各缓存项是否为活动项的时间间隔;
 
用法示例:
open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;
 

ngx_http_gzip_module:

 
ngx_http_gzip_module模块是一个过滤器,它使用“gzip”方法压缩响应。这通常有助于将传输数据的大小减少一半甚至更多。
 
1、gzip on | off;
启用或禁用响应的gzipping。
 
2、gzip_comp_level level;
设置响应的gzip压缩级别。可接受的值在1到9之间。
 
3、 gzip_disable regex …;
禁用“用户代理”头字段匹配任何指定正则表达式的请求的响应的gzipping。
 
4、 gzip_min_length length;
启用压缩功能的响应报文大小阈值;
 
5、gzip_buffers number size;
 
gzip_buffers 32 4k|16 8k;
设置number和size用来压缩响应的缓冲区。默认情况下,缓冲区大小等于一个内存页。这要么是4K,要么是8K,取决于平台。
 
6、 语法: gzip_http_version 1.0 | 1.1;
实例: gzip_http_version 1.1;
设置压缩响应所需的请求的最低HTTP版本。
 
7、gzip_proxied off | expired | no-cache | no-store | private | no_last_modified | no_etag | auth | any …;
nginx作为代理服务器接收到从被代理服务器发送的响应报文后,在何种条件下启用压缩功能的;
off:对代理的请求不启用
no-cache, no-store,private:表示从被代理服务器收到的响应报文首部的Cache-Control的值为此三者中任何一个,则启用压缩功能;
 

off
禁用所有代理请求的压缩,忽略其他参数;
expired
如果响应头包含具有禁用缓存的值的“Exend”字段,则启用压缩;
no-cache
如果响应头包含“缓存-控制”字段,则启用压缩。no-cache“参数;
no-store
如果响应头包含“缓存-控制”字段,则启用压缩。no-store“参数;
private
如果响应头包含“缓存-控制”字段,则启用压缩。private“参数;
no_last_modified
如果响应头不包括“最后修改”字段,则启用压缩;
no_etag
如果响应头不包括“Etag”字段,则启用压缩;
auth
如果请求头包含“授权”字段,则启用压缩;
any
为所有代理请求启用压缩。
<
 

 
8、gzip_types mime-type …;
压缩过滤器,仅对此处设定的MIME类型的内容启用压缩功能;
 

示例:
						gzip  on;
						gzip_comp_level 6;
						gzip_min_length 64;
						gzip_http_version 1.1;
						gzip_proxied any;
						gzip_types text/css  application/javascript;

ngx_http_ssl_module模块:

 
1、 ssl on | off;
为给定的虚拟服务器启用HTTPS协议。
 
2、ssl_certificate file;
当前虚拟主机使用PEM格式的证书文件;
 
3、ssl_certificate_key file;
当前虚拟主机上与其证书匹配的私钥文件;
 
4、ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2];
支持ssl协议版本,默认为后三个;
 
5、ssl_session_cache off | none | [builtin[:size]] [shared:name:size];
builtin[:size]:使用OpenSSL内建的缓存,此缓存为每worker进程私有(如果客户端访问的请求由第一个worker进程处理并缓存,但当第二次客户端访问请求时由第二个进程处理,那么之前的缓存就没有用了;就需要继续缓存从而导致浪费内存和带宽);
 
[shared:name:size]:在各worker之间使用一个共享的缓存; ssl_session_cache shared:ssl:10m;
所有工作进程之间共享的缓存。缓存大小以字节为单位;一兆字节可存储大约4000个会话。每个共享缓存都应该有一个任意名称。可以在多个服务器中使用同名缓存。
 
6、ssl_session_timeout time;
客户端一侧的连接可以复用ssl session cache中缓存 的ssl参数的有效时长;
 

server {
        listen 443 ssl;
        server_name www.ityunn.com;
        root /data/nginx/vhost1;
        ssl on;
        ssl_certificate /etc/nginx/ssl/nginx.crt;
        ssl_certificate_key /etc/nginx/ssl/nginx.key;
        ssl_protocols  SSLv3 TLSv1 TLSv1.1 TLSv1.2;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 500m;
       }

ngx_http_rewrite_module模块:

 
ngx_http_rewrite_module模块使用PCRE正则表达式、返回重定向和条件选择配置来更改请求URI。
 
bbs.magedu.com/ --> www.magedu.com/bbs/, http://www.ityunn.com/ --> https://www.ityunn.com/
http://www.ityunn.com/login.php;username=tom --> http://www.ityunncom/tom/
 
http://www.ityunn.io/bbs/ --> http://bbs.ityunn.io/
 
 
将用户请求的URI基于regex所描述的模式进行检查,而后完成替换;
 
1.rewrite regex replacement [flag]
(注意:前面时要匹配的正则表达式,后面为要重定向的内容。)
将用户请求的URI基于regex所描述的模式进行检查,匹配到时将其替换为replacement指定的新的URI;

 
注意:如果在同一级配置块中存在多个rewrite规则,那么会自下而下逐个检查;被某条件规则替换完成后,会重新一轮的替换检查,因此,隐含有循环机制;[flag]所表示的标志位用于控制此循环机制;
 
如果replacement是以http://或https://开头,则替换结果会直接以重向返回给客户端;
301:永久重定向;
 
[flag]:
last:重写完成后停止对当前URI在当前location中后续的其它重写操作,而后对新的URI启动新一轮重写检查;提前重启新一轮循环;
break:重写完成后停止对当前URI在当前location中后续的其它重写操作,而后直接跳转至重写规则配置块之后的其它配置;结束循环;
redirect:重写完成后以临时重定向方式直接返回重写后生成的新URI给客户端,由客户端重新发起请求;不能以http://或https://开头;
permanent:重写完成后以永久重定向方式直接返回重写后生成的新URI给客户端,由客户端重新发起请求;
 
案例:rewrite /(.*).png$ /$1.jpg;
 
2. return
return code [text];
return code URL;
return URL;
在0.7.51版本之前只能返回以下代码:204、400、402-406、408、410、411、413、416和500-504。
代码307直到1.1.16和1.0.13版本才被视为重定向。
直到版本1.13.0,代码308才被视为重定向。
 
停止处理并将指定的代码返回给客户机。
 
3.rewrite_log on | off;
是否开启重写日志;
 
4. if (condition) { … }
引入一个新的配置上下文 ;条件满足时,执行配置块中的配置指令;server, location;
 
 
condition:
比较操作符:
==
!=
~:模式匹配,区分字符大小写;
~*:模式匹配,不区分字符大小写;
!~:模式不匹配,区分字符大小写;
!~*:模式不匹配,不区分字符大小写;
文件及目录存在性判断:
-e, !-e
-f, !-f
-d, !-d
-x, !-x

	例子:

								if ($http_user_agent ~ MSIE) {
								    rewrite ^(.*)$ /msie/$1 break;
								}

								if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
								    set $id $1;
								}

								if ($request_method = POST) {
								    return 405;
								}

								if ($slow) {
								    limit_rate 10k;
								}

								if ($invalid_referer) {
								    return 403;
								}
						

 
5. set $variable value;
注意:定义在http字段或者server字段;
用户自定义变量 ;

ngx_http_referer_module模块:(防盗链模块)

 
ngx_http_referer_module模块用于阻止对“Referer”头字段中值无效的请求访问站点。
 
1.valid_referers none | blocked | server_names | string …;
 
定义referer首部的合法可用值;
 
none:请求报文首部没有referer首部;
blocked:请求报文的referer首部没有值;
server_names:参数,其可以有值作为主机名或主机名模式;
arbitrary_string:直接字符串,但可使用*作通配符;
regular expression:被指定的正则表达式模式匹配到的字符串;要使用~打头,例如 ~.*.magedu.com

配置实列:
 location ~* \.(jpg|gif|swf)$ {
        valid_referers none block server_names *.ityunn.com ityunn.com www.ityunn.*;
        if ($invalid_referer) {
                        return http://www.ityunn.com/lato.png;
                }
        }

Guess you like

Origin blog.csdn.net/qq_42298432/article/details/101715672