20190802_Nginx基础

文章主要内容:

关于中间件比较通俗易懂的解释

个人理解:将具体业务和底层逻辑解耦的组件。

大致的效果是:需要利用服务的人(前端写业务的),不需要知道底层逻辑(提供服务的)的具体实现,只要拿着中间件结果来用就好了。

介于操作系统和应用程序之间的产品,中间件简单解释,你可以理解为面向信息系统交互,集成过程中的通用部分的集合,屏蔽了底层的通讯,交互,连接等复杂又通用化的功能,以产品的形式提供出来,系统在交互时,直接采用中间件进行连接和交互即可,避免了大量的代码开发和人工成本。其实,理论上来讲,中间件所提供的功能通过代码编写都可以实现,只不过开发的周期和需要考虑的问题太多,逐渐的,这些部分,以中间件产品的形式进行了替代。

比如常见的消息中间件,即系统之间的通讯与交互的专用通道,类似于邮局,系统只需要把传输的消息交给中间件,由中间件负责传递,并保证传输过程中的各类问题,如网络问题,协议问题,两端的开发接口问题等均由消息中间件屏蔽了,出现了网络故障时,消息中间件会负责缓存消息,以避免信息丢失。相当于你想给美国发一个邮包,只需要把邮包交给邮局,填写地址和收件人,至于运送过程中的一系列问题你都不需要关心了。

通俗易懂的例子:我开了一家炸鸡店(业务端),然而周边有太多屠鸡场(底层),为了成本我肯定想一个个比价,再综合质量挑选一家屠鸡场合作(适配不同底层逻辑)。由于市场变化,合作一段时间后,或许性价比最高的屠鸡场就不是我最开始选的了,我又要重新和另一家屠鸡场合作,进货方式、交易方式等等全都要重来一套(重新适配)。然而我只想好好做炸鸡,有性价比高的肉送来就行。于是我找到了一个专门整合屠鸡场资源的第三方代理(中间件),跟他谈好价格和质量后(统一接口),从今天开始,我就只需要给代理钱,然后拿肉就行。代理负责保证肉的质量,至于如何根据实际性价比,选择不同的屠鸡场,那就是代理做的事了。

--------------------------------------------------------------------------------------------------------------------------------------------

在网站后台,往往存在很多应用服务,对应的是操作系统驱动硬件提供对应的服务。在很多应用的情况下,应用与应用之间直接调用,或应用直接与操作系统交互,会导致层次化的应用不够隔离,代码耦合程度高。此时需要中间件代理处理一些请求,让应用只负责业务的逻辑处理。

大型网站中,中间件一个个串联,使得网站层次性更高,维护更简单。

中间件还有web请求负载均衡,http请求缓存服务,安全应用防控等作用。

Nginx简述

Nginx是一个开源且高性能、可靠的HTTP中间件(企业应用场景最多)、代理服务。

常见HTTP服务:HTTPD,IIS,GWS(不对外开放)

Nginx特性优点:

  1. IO多路复用epoll:IO多路复用解决方案有select、poll和epoll,epoll是select和poll的增强版本,更灵活,没有描述符限制,效率更高
  2. 轻量级:功能模块少,代码模块化
  3. CPU亲和(affinity,一种把CPU核心和nginx工作进程绑定的方式):通过把每个worker进程固定在一个cpu上执行,减少切换cpu的cache miss,获得更好性能
  4. 采用sendfile使得nginx处理静态文件的效率非常有优势:sendfile机制,静态文件直接通过内核空间传递给socket,而不需要进一步经过用户空间的逻辑性的处理。

Nginx环境配置以及安装

安装Nginx前的环境配置:

  • 系统能上外网,确认yum可用,需要通过yum来安装装一些编译环境和实用软件;
  • 禁用iptables,firewall防火墙;
  • 禁用SELinux
cd /opt
mkdir app dpwnload logs work backup
yum install -y gcc gcc-c++ autoconf pcre pcre-devel make automake
yum install -y wget httpd-tools vim

配置好环境后,到Nginx官网:http://nginx.org/en/linux_packages.html#RHEL-CentOS,复制对应系统的yum源参数,其中nginx-stable是稳定版,nginx-mainline为开发版,实际应用中当然选稳定版。

vim /etc/yum.repos.d/nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=
enabled=
gpgkey=https://nginx.org/keys/nginx_signing.key

安装nginx:

yum install -y nginx
#查看nginx版本
nginx -v
#查看nginx编译参数
nginx -V
#启动nginx服务
nginx

Nginx的目录和配置语法

查看nginx安装目录:rpm -ql nginx

路径

类型

作用

/etc/logrotate.d/nginx

配置文件

Nginx日志轮转,用于logrotate服务的日志切割

/etc/nginx

/etc/nginx/nginx.conf

/etc/nginx/conf.d

/etc/nginx/conf.d/default.conf

目录、配置文件

Nginx主配置文件。默认配置下nginx.conf中加载了default.conf

/etc/nginx/fastcgi_params

/etc/nginx/uwsgi_params

/etc/nginx/scgi_params

配置文件

cgi配置相关,fastcgi配置

/etc/nginx/koi-utf

/etc/nginx/koi-win

/etc/nginx/win-utf

配置文件

编码转换映射转化文件

/etc/nginx/mime.types

配置文件

设置http协议的Content-Type与拓展名对应关系

/usr/lib/systemd/system/nginx-debug.service

/usr/lib/systemd/system/nginx.service

/etc/sysconfig/nginx

/etc/sysconfig/nginx-debug

配置文件

用于配置系统守护进程管理器管理方式

/usr/lib64/nginx/modules

/etc/nginx/modules

目录

Nginx模块目录

/usr/sbin/nginx

/usr/sbin/nginx-debug

命令

Nginx服务的启动管理的终端命令

/usr/share/doc/nginx-1.16.0

/usr/share/doc/nginx-1.16.0/COPYRIGHT

/usr/share/man/man8/nginx.8.gz

文件、目录

Nginx的手册和帮助文件

/var/cache/nginx

目录

Nginx的缓存目录

/var/log/nginx

目录

Nginx的日志目录

查看Nginx安装编译参数:nginx -V

编译选项

作用

--prefix=/etc/nginx

--sbin-path=/usr/sbin/nginx

--modules-path=/usr/lib64/nginx/modules

--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

安装目的目录或路径

--http-client-body-temp-path=/var/cache/nginx/client_temp

--http-proxy-temp-path=/var/cache/nginx/proxy_temp

--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp

--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp

--http-scgi-temp-path=/var/cache/nginx/scgi_temp

执行对应模块时,Nginx所保留的临时性文件

--user=nginx

--group=nginx

设定Nginx进程启动的用户和用户组

--with-cc-opt=parameters

设置额外的参数将被添加到CFLAGS变量

--with-ld-opt=parameters

设置附加的参数,链接系统库

完整的nginx配置文件结构

一个http可以有多个server,一个server可以有多个location。

nginx服务会先读取主配置文件nginx.conf,默认的nginx.conf的最后有一句“include /etc/nginx/conf.d/*.conf”,即会读取该目录下的所有conf文件。

nginx服务主配置文件/etc/nginx/nginx.conf的参数解释:

user  nginx;    #指定Nginx的worker进程运行用户以及用户组,默认为nginx
worker_processes ; #指定Nginx要开启的进程数,一般与cpu核心数一样即可 error_log /var/log/nginx/error.log warn; #用来定义全局错设日志文件的路径和日志名称。日志输出级别有debug,info,notice,warn,error,crit可供选择,其中debug输出日志最为详细,面crit输出日志最少。
pid /var/run/nginx.pid; #用来指定进程id的存储文件位置。 events { #设定nginx的工作模式及连接数上限
worker_connections ; #设置nginx每个进程最大的连接数,默认是1024,所以nginx最大的连接数max_client=worker_processes * worker_connections。进程最大连接数受到系统最大打开文件数的限制,需要设置ulimit。
#use epoll #指定nginx的工作模式(这里是epoll,epoll是多路复用IO(I/O Multiplexing)中的一种方式),nginx支持的工作模式有select ,poll,kqueue,epoll,rtsig,/dev/poll。其中select和poll都是标准的工作模式,kqueue和epoll是高效的工作模式,对于linux系统,epoll是首选。
}
#以上是对nginx全局属性的配置
#下面是nginx对http服务相关属性设置
http {
include /etc/nginx/mime.types; #include包含某文件的设定,减少主配置文件的复杂度,相当于把部分设置放在别的地方,然后在包含进来,保持主配置文件的简洁。
default_type application/octet-stream; #默认文件类型,当文件类型未定义时候就使用这类设置。
#log_format:指定nginx日志的格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #开启sendfile,开启高效文件传输模式(zero copy 方式),避免内核缓冲区数据和用户缓冲区数据之间的拷贝。
#tcp_nopush on; keepalive_timeout ; #客户端连接超时时间 #gzip on; #是否开启gzip模块 include /etc/nginx/conf.d/*.conf; #包含指定路径下所有conf文件的配置
}

nginx服务子配置文件/etc/nginx/conf.d/default.conf配置参数解释:

server {
listen ; #虚拟主机的服务端口
server_name localhost; #指定ip或域名,多个域名用逗号分开 #charset koi8-r;
#access_log /var/log/nginx/host.access.log main; location / { #地址匹配设置,支持正则匹配,也支持条件匹配,这里是默认请求地址,用户可以location命令对nginx进行动态和静态网页过滤处理
root /usr/share/nginx/html; #虚拟主机的网页根目录
index index.html index.htm; #默认访问首页文件
} #error_page /.html; # redirect server error pages to the static page /50x.html
#
error_page /50x.html; #若出现500等错误,则跳转到下面的location配置路径下的50x.html
location = /50x.html {
root /usr/share/nginx/html; #错误页面的路径
} # proxy the PHP scripts to Apache listening on 127.0.0.1:
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:; #将以php为后缀的文件转发到 FastCGI处理. 使用FastCGI默认配置。本地9000端口处理
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#} # deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

修改静态内容无需重启nginx服务,但修改了conf配置文件则需要重启服务方能生效,也可以柔和重启

vim /etc/nginx/conf.d/default.conf
error_page /50x.html; #添加404错误,出现404错误时跳转到50x.html
nginx -t #检查nginx配置文件是否有语法问题
nginx -s reload #柔和重启,无需关闭服务即可加载最新的配置

Nginx日志_log_format

Nginx日志主要分为两种:访问日志(access.log)和错误日志(error.log)。日志开关在Nginx配置文件(/etc/nginx/nginx.conf)中设置,两种日志都可以选择性关闭,默认都是打开的。

访问日志

主要记录客户端访问Nginx的每一个请求,格式可以自定义。通过访问日志,可以得到用户地域来源、跳转来源、使用终端、某个URL访问量等相关信息。Nginx中访问日志相关指令主要有两条:

(1)log_format用来设置日志格式,也就是日志文件中每条日志的格式,具体说明:

  • 语法:log_format name(格式名称) type(格式样式)
  • 支持模块:http

nginx.conf中默认的log_format 格式样式的变量含义:

  • $remote_addr:远程客户端的IP地址。
  • -:空白,用一个“-”占位符替代,历史原因导致还存在。
  • $remote_user:远程客户端用户名称,用于记录浏览者进行身份验证时提供的名字,若没有登录就是空白(变量值为空,在日志输出结果为一个"-"符号)。
  • [$time_local]:访问的时间与时区,如[04/Aug/2019:11:13:43 +0800],时间信息最后的"+0800"表示服务器所处时区位于UTC之后的8小时。
  • $request:请求的URI(即ip地址后的/XXX/XXX.html)和HTTP协议(有版本号),这是整个PV日志记录中最有用的信息,记录服务器收到一个什么样的请求
  • $status:记录请求返回的http状态码,比如成功是200。
  • $body_bytes_sent:发送给客户端的文件主体内容的大小,比如899,可以将日志每条记录中的这个值累加起来以粗略估计服务器吞吐量。
  • $http_referer:记录从哪个页面链接访问过来的。
  • $http_user_agent:客户端浏览器信息
  • $http_x_forwarded_for:客户端的真实ip,通常web服务器放在反向代理的后面,这样就不能获取到客户的IP地址了,通过$remote_add拿到的IP地址是反向代理服务器的iP地址。反向代理服务器在转发请求的http头信息中,可以增加x_forwarded_for信息,用以记录原有客户端的IP地址和原来客户端的请求的服务器地址。

HTTP请求变量:http请求的request头和response头,可以用curl命令看http请求响应报文的详细内容。

  • arg_PARAMETER:HTTP请求中某个参数的值,如/index.php?site=www.ttlsa.com,可以用$arg_site取得www.ttlsa.com
  • http_HEADER:客户端向服务端的request请求header,如user_agent,host,accept
  • sent_http_HEADER:服务端返回给客户端的response响应header,如server,date,content_type,content_length等

(2)access_log用来指定日志文件的存放路径(包含日志文件名)、格式和缓存大小,具体说明:

  • 语法:access_log path(存放路径) [format(自定义日志格式名称) [buffer=size | off]]
  • 支持模块:http、server、location

如果想关闭日志,可以如下:access_log off;

需要注意的是:Nginx进程设置的用户和组必须对日志路径有创建文件的权限,否则会报错。

Nginx支持为每个location指定强大的日志记录。同样的连接可以在同一时间输出到不止一个的日志中。

错误日志

主要记录客户端访问Nginx出错时的日志,格式不支持自定义。通过错误日志,可以得到系统某个服务或server的性能瓶颈等。

错误日志由指令error_log来指定,具体格式如下:error_log path(存放路径) level(日志等级)

path含义同access_log,level表示日志等级,具体如下:[ debug | info | notice | warn | error | crit ]

从左至右,日志详细程度逐级递减,即debug最详细,crit最少。

需要注意的是:error_log off并不能关闭错误日志,而是会将错误日志记录到一个文件名为off的文件中。

正确的关闭错误日志记录功能的方法如下:error_log /dev/null;把存储日志路径定义为“黑洞”,即不会记录错误日志。

Nginx模块

--with-http_stub_status_module:nginx的客户端状态。在server块里面添加如下location,浏览器访问http://ip地址/mystatus,显示客户端状态信息

location /mystatus {
stub_status; #可以使用在server块和location块中
}

--with-http_random_index_module:目录中选择一个随机主页。在server块里面添加如下location,浏览器访问http://ip地址,随机显示location定义路径下的一个html文件作为主页,可刷新页面测试。

该模块只能在location为“/”的情况下生效,且隐藏文件不会作为随机主页的对象。

location / {
root /opt/app/code; #在此路径下创建若干个html文件
random_index on; #开启随机主页功能,只能用在location块中
#index index.html index.htm;
}

--with-http_sub_module:HTTP内容替换。在server块里面添加如下location,浏览器访问http://ip地址,可以对页面的指定字符串内容替换为指定内容。

该模块只能在location为“/”的情况下生效。

 location / {
root /usr/share/nginx/html; #主页文件的内容为fuck you fuck me
index index.html index.htm;
sub_filter 'fuck' 'FUCK'; #fuck替换为FUCK,可用在http,server,location中
sub_filter_once off; #替换文本中所有匹配的fuck为FUCK(默认为on,只会替换按第一个fuck)。可用在http,server,location中
}

Nginx的请求限制

HTTP请求建立在一次TCP连接的基础上。一次TCP连接至少可以产生一次HTTP请求,HTTP1.1版本以后,建立一次TCP连接可以发送多次HTTP请求。

连接频率限制:limit_conn_module

#语法
Syntax: limit_conn_zone key zone=name:size;
Default: —
Context: http Syntax: limit_conn zone number;
Default: —
Context: http, server, location

请求频率限制:limit_req_module

#语法
Syntax: limit_req_zone key zone=name:size rate=rate;
Default: —
Context: http Syntax: limit_req zone=name [burst=number] [nodelay];
Default: —
Context: http, server, location

例:

http {
# ...其它代码省略...
# 开辟一个10m的连接空间,命名为conn_name
limit_conn_zone $binary_remote_addr zone=conn_name:10m;
# 开辟一个10m的请求空间,命名为req_zone。同一个IP发送的请求,平均每秒只处理一次
limit_req_zone $binary_remote_addr zone=req_zone:10m rate=1r/s;
server {
...
location / {
...
#服务器每次只允许一个IP地址连接
#limit_conn con_zone ;
#limit_req zone=req_zone;
#当客户端请求超过指定次数,最多宽限3次请求,并延迟处理,1秒1个请求
#limit_req zone=req_zone burst=;
#当客户端请求超过指定次数,最多宽限次请求,并立即处理。
#limit_req zone=req_zone burst= nodelay;
}
}
}

具体可以用ab压力测试命令进行测试:ab -n 50 -c 20 http://ip地址。n表示请求数,c表示同时并发请求数。

Nginx的访问控制

基于IP的访问控制:http_access_module

语法:#address表示IP地址,CIDR表示网段,unix表示Socket方式,all代表所有

Syntax:  allow address | CIDR | unix: | all;
Default: —
Context: http, server, location, limit_except Syntax: deny address | CIDR | unix: | all;
Default: —
Context: http, server, location, limit_except

用法:在nginx配置文件中的 server 下配置

server {
# ...其它代码省略...
location ~ ^/index_1.html {
root /usr/share/nginx/html;
deny 151.19.57.60; # 拒绝这个IP访问
allow all; # 允许其他所有IP访问
}
location ~ ^/index_2.html {
root /usr/share/nginx/html;
allow 151.19.57.0/; # 允许IP 151.19..* 访问
deny all; # 拒绝其他所有IP访问
}
}

http_access_module局限性:当客户端通过代理访问时,nginx的remote_addr获取的是代理的IP,此时用http_access_module就不是对客户端的精准控制了,所有通过该代理IP访问nginx服务的客户端都会被配置规则所控制。

http_x_forwarded_for:可以记录客户端及所有中间代理的IP,格式:http_x_forwarded_for = Client IP, Proxy1 IP, Proxy2 IP, ...

基于用户的登录认证:http_auth_basic_module

语法:

Syntax:  auth_basic string | off;
Default: auth_basic off;
Context: http, server, location, limit_except Syntax: auth_basic_user_file file;
Default: —
Context: http, server, location, limit_except

用法:

1.用htpasswd命令生成账号密码文件(没有该命令则安装httpd-tools)

yum -y install httpd-tools
htpasswd -c /etc/nginx/auth_conf kamin #kamin是用户名(随便起)

2.在nginx配置文件中的 server 下配置

server {
# ...其它代码省略...
location ~ ^/index.html {
root /usr/share/nginx/html;
auth_basic "Auth access! Input your password!"; #提示字符串
auth_basic_user_file /etc/nginx/auth_conf; #以生成的auth_conf文件中的用户密码作为认证条件
}
}

配置好并重启nginx服务后,浏览器访问会弹出用户密码认证输入框。

http_auth_basic_module 局限性:用户信息依赖文件方式,操作管理效率低下。

20190802_Nginx基础的更多相关文章

  1. LInux 就该这么学 笔记分享

    看了Linux就该这么学的前部分书,觉得写的还可以,就在网上找了下面这个同学写的笔记,觉得很详细,所以保存地址,供以后查阅参看.这里对作者表示感谢!!! 博客地址: https://www.cnblo ...

  2. java基础集合经典训练题

    第一题:要求产生10个随机的字符串,每一个字符串互相不重复,每一个字符串中组成的字符(a-zA-Z0-9)也不相同,每个字符串长度为10; 分析:*1.看到这个题目,或许你脑海中会想到很多方法,比如判 ...

  3. node-webkit 环境搭建与基础demo

    首先去github上面下载(地址),具体更具自己的系统,我的是windows,这里只给出windows的做法 下载windows x64版本 下载之后解压,得到以下东西 为了方便,我们直接在这个目录中 ...

  4. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  5. Golang, 以17个简短代码片段,切底弄懂 channel 基础

    (原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程.管道等高并发编程知识 ...

  6. [C#] C# 基础回顾 - 匿名方法

    C# 基础回顾 - 匿名方法 目录 简介 匿名方法的参数使用范围 委托示例 简介 在 C# 2.0 之前的版本中,我们创建委托的唯一形式 -- 命名方法. 而 C# 2.0 -- 引进了匿名方法,在 ...

  7. HTTPS 互联网世界的安全基础

    近一年公司在努力推进全站的 HTTPS 化,作为负责应用系统的我们,在配合这个趋势的过程中,顺便也就想去搞清楚 HTTP 后面的这个 S 到底是个什么含义?有什么作用?带来了哪些影响?毕竟以前也就只是 ...

  8. Swift与C#的基础语法比较

    背景: 这两天不小心看了一下Swift的基础语法,感觉既然看了,还是写一下笔记,留个痕迹~ 总体而言,感觉Swift是一种前后端多种语言混合的产物~~~ 做为一名.NET阵营人士,少少多多总喜欢通过对 ...

  9. .NetCore MVC中的路由(1)路由配置基础

    .NetCore MVC中的路由(1)路由配置基础 0x00 路由在MVC中起到的作用 前段时间一直忙于别的事情,终于搞定了继续学习.NetCore.这次学习的主题是MVC中的路由.路由是所有MVC框 ...

随机推荐

  1. vb Replace 实现

    今天改一个VB程序时发现程序自带的replace 函数不知什么原因竟然不好用了 所以就自己写了一个玩玩 记录一下 'XGZ '替换字符 Private Function Replace1(ByVal ...

  2. Winform 后台生成饼状图并保存为图片

    .cs代码如下 string ldt_picPath = System.Windows.Forms.Application.StartupPath + @"Pic\" + Item ...

  3. C# 简单日志帮助类LogHelper

    调用: LogHelper.Debug(""); LogHelper.Info(""); LogHelper.Error(""); 项目添加 ...

  4. Centos 7 JDK 安装(默认之前没有安装过)

    第一步: 安装JDK,先检查JDK是否存在,输入以下命令回车: java -version 没有安装过会显示: [root@heyouhao /]# java -version [root@heyou ...

  5. Node.js返回JSON

    在使用JQuery的Ajax从服务器请求数据或者向服务器发送数据时常常会遇到跨域无法请求的错误,常用的解决办法就是在Ajax中使用JSONP.基于安全性考虑,浏览器会存在同源策略,然而<scri ...

  6. Cocos Creator 返回字符串长度(字符),汉字计数为2

    function strLength(str) { var a = 0; for (var i = 0; i < str.length; i++) { if (str.charCodeAt(i) ...

  7. day 69作业

    """ 1.按照上方 知识点总结 模块,总结今天所学知识点: 2.有以下广告数据(实际数据命名可以略做调整) ad_data = { tv: [ {img: 'img/t ...

  8. Linux服务管理之ntp

    NTP是网络时间协议(Network Time Protocol),它是用来同步网络中各个计算机的时间的协议. 在计算机的世界里,时间非常地重要,例如对于火箭发射这种科研活动,对时间的统一性和准确性要 ...

  9. oarcle wm_concat 值过长解决--使用 clob

    sql 语句替换 :select XMLAGG(XMLELEMENT(a, WSODETAILPALINCD || ',')).EXTRACT('//text()').getclobval() as ...

  10. Java多线程编程核心技术-第4章-Lock的使用-读书笔记

    第 4 章 Lock 的使用 本章主要内容 ReentrantLocal 类的使用. ReentrantReadWriteLock 类的使用. 4.1 使用 ReentrantLock 类 在 Jav ...