概况
- 搭建Tomcat集群,通过横向拓展解决单服务器上限瓶颈
- Nginx处理静态资源,Tomcat处理接口请求,动静分离
- Nginx实现负载均衡,均衡Tomcat服务的并发压力
搭建Tomcat集群
1.部署架构
- 192.168.214.150:centos6-1 —— Tomcat1
- 192.168.214.151:centos6-2 —— Tomcat2
- 192.168.214.152:centos6-3 —— Tomcat3
其实两台就行了,博主习惯搭建奇数台
2.首先安装单机Tomcat
详见:大型网站分布式架构(二)—— Linux下Tomcat的安装和项目部署
博主在192.168.214.150节点上首先安装了单机Tomcat
3.分发Tomcat
首先需要配置3台服务器间的ssh免密登录:基于SSH的用户名密码验证和免密登录原理
[root@centos6-1 servers]# scp -r apache-tomcat-8.0.53 root@centos6-2:$PWD
[root@centos6-1 servers]# scp -r apache-tomcat-8.0.53 root@centos6-3:$PWD
查看是否分发成功
centos6-2
[root@centos6-2 ~]# cd /export/servers/
[root@centos6-2 servers]# ll
total 1
drwxr-xr-x. 9 root root 4096 Aug 27 06:40 apache-tomcat-8.0.53
centos6-3
[root@centos6-3 ~]# cd /export/servers/
[root@centos6-3 servers]# ll
total 1
drwxr-xr-x. 9 root root 4096 Aug 27 06:40 apache-tomcat-8.0.53
更新项目
1.修改IndexController
package com.zaomianbao.appdemo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
@Controller
public class IndexController {
@RequestMapping("/")
public String index(HttpServletRequest request){
try {
System.out.println(new Date());
InetAddress addr = InetAddress.getLocalHost();
String ip=addr.getHostAddress().toString(); //获取本机ip
String host=addr.getHostName().toString(); //获取本机计算机名称
System.out.println(ip + " " + host);
request.setAttribute("ip",ip);
request.setAttribute("host",host);
} catch (UnknownHostException e) {
e.printStackTrace();
}
return "index";
}
}
2.修改index.html
使用的是thymeleaf模板语法
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>欢迎进入枣面包的面包坊</title>
<script type="text/javascript" src="js/nothing.js"></script>
</head>
<body>
<h1>欢迎进入枣面包的面包坊</h1>
<img src="img/photo.jpg"><br/>
<span th:text="${host}" ></span><br/>
<span th:text="${ip}" ></span>
</body>
</html>
3.修改pom.xml
之前一直没有管过项目打包后的包名,现在设置一下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zaomianbao</groupId>
<artifactId>appdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<!--<packaging>jar</packaging>-->
<name>appdemo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>appdemo</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.部署项目到Tomcat集群
上传appdemo.war到Tomcat1
[root@centos6-1 apache-tomcat-8.0.53]# cd webapps/
[root@centos6-1 webapps]# rz
rz waiting to receive.
Starting zmodem transfer. Press Ctrl+C to cancel.
Transferring appdemo.war...
100% 17279 KB 17279 KB/sec 00:00:01 0 Errors
分发到centos6-2的Tomcat2和centos6-3的Tomcat3
[root@centos6-1 webapps]# scp -r appdemo.war root@centos6-2:$PWD
appdemo.war 100% 17MB 16.9MB/s 00:00
[root@centos6-1 webapps]# scp -r appdemo.war root@centos6-3:$PWD
appdemo.war 100% 17MB 16.9MB/s 00:00
[root@centos6-1 webapps]#
启动Tomcat集群
分别启动centos6-1、centos6-2、centos6-3的Tomcat
启动Tomcat1
[root@centos6-1 bin]# ./startup.sh
启动Tomcat2
[root@centos6-2 bin]# ./startup.sh
启动Tomcat3
[root@centos6-3 bin]# ./startup.sh
浏览器分别访问三节点Tomcat
访问Tomcat1
访问Tomcat2
访问Tomcat3
可以看到3台服务器上的Tomcat都可以访问,同时在访问我们的项目的时候,都在页面显示出了Tomcat所在服务器本地的主机名和ip;这个就是同一个项目部署到集群的不同服务器中为用户提供服务;但是到目前为止,这三台服务器还是通过不同的访问地址才能访问得到,而我们需要得到的效果是访问同一地址,3台Tomcat都同时为用户提供服务,这里就需要我们的Nginx来做反向代理,实现Tomcat集群的负载均衡
配置Nginx反向代理
修改nginx.conf文件如下:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
#动态服务器组
upstream zaomianbao.com {
server 192.168.214.150:8080;
server 192.168.214.151:8080;
server 192.168.214.152:8080;
}
server {
listen 80;
server_name localhost;
#配置静态资源交给nginx处理,这里先只配置js和jpg
location ~ .*\.(js|jpg) {
root /export/data/nginx; #静态文件目录
expires 30d; #缓存天数
}
#配置除静态资源以外的交给tomcat处理
location / {
proxy_pass http://zaomianbao.com;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
重新加载配置文件
[root@centos6-1 ~]# cd /usr/local/nginx/sbin/
[root@centos6-1 sbin]# ./nginx -s reload
浏览器访问Nginx
连续访问三次出现以下结果:
第一次
第二次
第三次
发现3次访问同一个地址,但返回的内容却又差别,因为分别是3台不同的Tomcat处理动态的接口请求并返回html文档内容,而文档内容中动态渲染了Tomcat所在主机的主机名和ip,所以我们看到了不同的内容;而静态资源只在Nginx所在的centos6-1服务器上部署了一份。到这里就充分看到了Nginx集成Tomcat实现动静分离的好处以及Nginx对Tomcat横向拓展的负载均衡的技术支持。
容错机制
我们将Tomcat3宕掉
[root@centos6-3 bin]# ./shutdown.sh
再访问Nginx,发现不影响应用的使用,只是请求分发时不再分配到已经宕机的Tomcat3上了,这里就是Nginx的upstream所实现的容错机制,这样就可以实现web服务的7*24小时不间断服务。
总结
以上架构足以解决大中型网站的高并发访问。Nginx反向代理,Tomcat横向拓展集群,Nginx处理静态内容,Tomcat处理动态接口请求,Nginx负责动态请求的负载均衡分配,Nginx容错机制实现高可用。随着集群的搭建,带来了显而易见的好处就是网站的并发能力大大提升,但是集群的产生也带来了一些难题,例如会话的问题,后面就将讨论集群模式下Session的处理。