我们使用的Tomcat是一个Java的JSP/Servlet动态服务器,但并不是一个优秀静态资源服务器,使用Tomcat作为Java Web服务器没有问题,但用它来提供图片、CSS、和HTML静态资源的话访问效率并不高(rps不高)。在真实的Web应用中,JSP/Servlet的请求量相比静态资源(如图片、CSS、JS等等)的请求量要少得多,如果用Tomcat同时兼做动态、静态服务器,Tomcat的短板就会凸显。为此,我们常常需要把静态资源分离出来交给更高效的HTTP服务器去管理,这种作法称为“动静分离”。
1 Nginx服务器
1.1 Nginx简介
(1)什么是Nginx
Nginx (engine x)是一个高性能的HTTP静态服务器和反向代理服务器,其特点是占用内存少,并发能力强。Nginx服务器的官网下载地址为:nginx: download。
(2)Nginx的三种作用
静态资源服务器、反向代理和负载均衡服务器。
“反向代理”是代理服务器的一种。服务器根据客户端的请求,从其关系的一组或多组后端服务器(如Web服务器)上获取资源,然后再将这些资源返回给客户端,客户端只会得知反向代理的IP地址,而不知道在代理服务器后面的服务器簇的存在。
与反向代理相反的是“前向代理”,前向代理作为客户端的代理,将从互联网上获取的资源返回给一个或多个的客户端,服务端(如Web服务器)只知道代理的IP地址而不知道客户端的IP地址。
“负载均衡(Load Balance)”意思就是把任务分摊到多个操作单元上进行执行,从而共同完成工作任务。在实际环境中运行的Web服务器(如Tomcat),面对着大量的并发请求,如果可以把请求统一接收并均衡平摊到多台服务器上,就可以让应用程序承载更多的负荷。
1.2 Nginx安装和运行
Nginx的安装包是一个zip文件,只需解压即可使用。
通过Nginx目录下的nginx.exe命令就可以运行Nginx服务器,格式如下:
nginx -选项 参数
常用指令如下:
命令 | 描述 |
---|---|
start nginx | 启动nginx服务器 |
nginx -t | 测试配置文件是否有语法错误 |
nginx -s stop | 立即停止nginx服务器 |
nginx -s quit | 优雅的退出nginx服务器 |
nginx -s reload | 重启nginx服务器 |
Nginx在生产中的主要功能是 “静态资源服务器” 和 “反向代理服务器”。Nginx服务器功能是通过配置文件“~/conf/nginx.conf”来定制的。
在控制台中运行nginx.exe,就可以在浏览器输入localhost访问nginx。
2 使用Nginx实现静态资源服务器
假设我们把网站的静态资源放置在磁盘文件夹下(例如“D:SpringMVC_Nginxweb-staticstatic”),通过修改nginx的配置文件就可以用Nginx把它们以HTTP的方式公布出来。
(1)配置静态资源服务器
修改Nginx安装目录下的“confnginx.conf”文件。修改其中的“server”配置信息如下。
代码语言:javascript复制server {
#添加静态资源映射路径 /static/
location /static/ {
root D:SpringMVC_Nginxweb-static; #设置根目录对应的文件夹
autoindex on; #会自动显示资源目录
index noindex.htm;
}
}
(2)重启Nginx
通过安装目录下的nginx.exe程序,可以控制服务器的启动关闭。使用控制台命令行进入nginx安装目录,执行以下指令重启nginx:
代码语言:javascript复制nginx.exe -s reload
(3)访问静态资源
启动后就能通过HTTP请求访问静态资源了。
3 使用Nginx实现对Tomcat的反向代理
通过上述配置,浏览器可以通过Nginx服务器提供的“http://localhost/static/......”访问静态资源。
同样原理,我们希望Tomcat中的项目也可以由Nginx代理出去,Tomcat服务器由Nginx包装后,对外只需要暴露Nginx服务即可,这种代理方式称之为“反向代理”。
(1)启动Tomcat中的项目(例如:http://localhost:8080/demo/)
(2)在nginx配置文件nginx.conf中的Server配置节中添加如下的配置:
代码语言:javascript复制server {
# ……省略原有配置……
#反向代理Tomcat服务器
location /demo/ {
proxy_pass http://localhost:8080/demo/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# ……省略原有配置……
}
(3)重启nginx
nginx -s reload
这时,我们可以通过nginx服务器 http://localhost/demo/index 访问原来Tomcat中http://localhost:8080/demo/index的页面。这样就实现了反向代理。
4 把文件上传到静态资源服务器
这时,在Tomcat项目中,我们只要通过http://localhost/static/路径就可以访问静态资源,如果要上传文件的话,我们就应该存放在nginx对应的静态资源目录(如:上述的D:SpringMVC_Nginxweb-static)。因为这个虚拟路径(例如:http://localhost/static/)和物理路径都不和tomcat直接相关,因此对编程带来一些麻烦,为此我们可以采取配置的方式来统一处理路径问题。
(1)配置静态资源路径
为了方便编程中使用,我们可以把静态资源URL(static url)和实际存放资源的物理路径配置在项目中。例如,放置在web.xml中。
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
…其他配置…
<context-param>
<param-name>staticUrl</param-name>
<param-value>http://localhost/static</param-value>
</context-param>
<context-param>
<param-name>staticPath</param-name>
<param-value>D:SpringMVC_Nginxweb-staticstatic</param-value>
</context-param>
</web-app>
(2)在页面中使用
通过EL表达式“${staticUrl}”,我们可以在jsp页面中使用静态资源路径。例如:
代码语言:javascript复制<img src="${initParam.staticUrl}/images/1.gif" alt="" />
(3)在代码中使用
在项目代码中,我们也可以通过ServletContext对象读取路径参数。例如:
request.getServletContext().getInitParameter("staticPath")
最后,注意上传文件的保存地址,不再是Tomcat服务器的部署地址,而是Nginx服务器的静态资源放置地址。以下是SpringMVC保存上传文件的示例代码。
代码语言:javascript复制@RequestMapping("/save")
public String save(Photo photo, MultipartFile image, HttpServletRequest req) throws IllegalStateException, IOException {
if(!image.isEmpty()) {
String ext = image.getOriginalFilename()
.substring(image.getOriginalFilename().lastIndexOf("."));
String filename = System.currentTimeMillis() ext;
photo.setFilename(filename);
photoMapper.add(photo);
String path = req.getServletContext().getInitParameter("staticPath")
"/images/" filename;
image.transferTo(new File(path));
return "redirect:index";
}else {
return "error";
}
}
还需要注意的是,在真实的环境中,动态资源服务器(Tomcat)和静态资源服务器(Nginx)往往不会放在同一台机器上,这时保存文件的地址就不是本机路径了,有可能需要在Nginx所在服务器中创建FTP服务,然后在Java Web程序中通过FTP的方式把用户上传的文件再次用FTP方式上传到Nginx服务器上。实际应用中也很可能直接购买各种云服务器上的静态资源提供者服务。
5 负载均衡简介
前面提到,Nginx还可以反向代理多台后端的Web服务器,实现反向代理功能。下面用Nginx反向代理两台Tomcat服务器来简单演示负载均衡的实现。
为了能快速启动多个Tomcat实例,我们使用Spring Boot工具快速构建包含Tomcat的Jar项目。启动两个功能相同的Tomcat实例,请求地址分别为:
http://localhost:8080/index
http://localhost:8090/index
配置Nginx的nginx.conf文件,增加upstream配置节,并设置反向代理。请参见下方配置中红字部分。
upstream 中配置的weight是负载均衡中各后端服务器的权重。下面配置的权重都是10,意味着两台服务器负载量均等。
代码语言:javascript复制……
upstream tomcat_server{
server 127.0.0.1:8080 weight=10;
server 127.0.0.1:8090 weight=10;
}
server {
listen 80;
server_name localhost;
#反向代理Tomcat服务器
location /demo/ {
proxy_pass http://tomcat_server/demo/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
……
重新加载nginx,我们监控两个后端服务器接收到的请求情况,发现两台服务器被轮流访问。