以前写过一篇Apache+Tomcat集群的文章,最近正好有空,把Nginx+Tomcat也写一下做为比较。
首先,用Nginx和Apache+Tomcat做集群的区别,我就直接从网上找了点资料。
主要区别在于,Apache是同步多进程模型,一个连接对应一个进程。Nginx是异步多进程模型,多个连接对应一个进程,这里的多个连接可以达到上万的级别,所以普遍认为,Nginx的性能更好。
Nginx:
-
轻量级,采用 C 进行编写,同样的 web 服务,会占用更少的内存及资源
-
抗并发,nginx 以 epoll and kqueue 作为开发模型,处理请求是异步非阻塞的,负载能力比 apache 高很多,而 apache 则是阻塞型的。在高并发下 nginx 能保持低资源低消耗高性能 ,而 apache 在 PHP 处理慢或者前端压力很大的情况下,很容易出现进程数飙升,从而拒绝服务的现象。
-
nginx 处理静态文件好,静态处理性能比 apache 高三倍以上
-
nginx 的设计高度模块化,编写模块相对简单
-
nginx 配置简洁,正则配置让很多事情变得简单,而且改完配置能使用 -t 测试配置有没有问题,apache 配置复杂 ,重启的时候发现配置出错了,会很崩溃
-
nginx 作为负载均衡服务器,支持 7 层负载均衡
-
nginx 本身就是一个反向代理服务器,而且可以作为非常优秀的邮件代理服务器
-
启动特别容易, 并且几乎可以做到 7*24 不间断运行,即使运行数个月也不需要重新启动,还能够不间断服务的情况下进行软件版本的升级
-
社区活跃,各种高性能模块出品迅速
-
apache 的 rewrite 比 nginx 强大,在 rewrite 频繁的情况下,用 apache
-
apache 发展到现在,模块超多,基本想到的都可以找到
-
apache 更为成熟,少 bug ,nginx 的 bug 相对较多
-
apache 超稳定
-
apache 对 PHP 支持比较简单,nginx 需要配合其他后端用
-
apache 在处理动态请求有优势,nginx 在这方面是鸡肋,一般动态请求要 apache 去做,nginx 适合静态和反向。
-
apache 仍然是目前的主流,拥有丰富的特性,成熟的技术和开发社区
总结:
两者最核心的区别在于 apache 是同步多进程模型,一个连接对应一个进程,而 nginx 是异步的,多个连接(万级别)可以对应一个进程
一般来说,需要性能的 web 服务,用 nginx 。如果不需要性能只求稳定,更考虑 apache ,后者的各种功能模块实现得比前者,例如 ssl 的模块就比前者好,可配置项多。epoll(freebsd 上是 kqueue ) 网络 IO 模型是 nginx 处理性能高的根本理由,但并不是所有的情况下都是 epoll 大获全胜的,如果本身提供静态服务的就只有寥寥几个文件,apache 的 select 模型或许比 epoll 更高性能。当然,这只是根据网络 IO 模型的原理作的一个假设,真正的应用还是需要实测了再说的。
更为通用的方案是,前端 nginx 抗并发,后端 apache 集群,配合起来会更好。
--------------------------------------------------------------------------------------------------
下面介绍如何配置Nginx+Tomcat集群并做到Session复制:
一. Tomcat配置(Tomcat8):
1.conf/server.xml:
主要注意的是端口号(port)的设置,<Engine>中jvmRoute的设置,<Cluster>的注释放开。
<?xml version='1.0' encoding='utf-8'?>
<Server port="8075" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<!-- 多个Tomcat的端口号不能相同,否则无法启动 -->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" maxHttpHeaderSize="2000000"
redirectPort="8444" />
<Connector port="8072" protocol="AJP/1.3" redirectPort="8444" />
<!-- 多个Tomcat的jvmRoute必须要设置且必须相同 -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
<!-- Cluster原本是被注释掉的,现在要放开 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true" reloadable="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
2.webapps/ROOT/WEB-INF/web.xml:
添加<distributable />
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app 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"
version="3.0"
metadata-complete="true">
<display-name>Welcome to Tomcat</display-name>
<description>
Welcome to Tomcat
</description>
<distributable />
</web-app>
3.在webapps/ROOT/下添加testCluster.jsp:
<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="java.util.*" %>
<html><head><title>Cluster Test</title></head>
<body>
<%
//HttpSession session = request.getSession(true);
System.out.println(session.getId());
out.println("<br> SESSION ID:" + session.getId()+"<br>");
// 如果有新的请求,则添加session属性
String name = request.getParameter("name");
if (name != null && name.length() > 0) {
String value = request.getParameter("value");
session.setAttribute(name, value);
}
out.print("<b>Session List:</b>");
Enumeration<String> names = session.getAttributeNames();
while (names.hasMoreElements()) {
String sname = names.nextElement();
String value = session.getAttribute(sname).toString();
out.println( sname + " = " + value+"<br>");
System.out.println( sname + " = " + value);
}
%>
<form action="testCluster.jsp" method="post">
名称:<input type=text size=20 name="name">
<br>
值:<input type=text size=20 name="value">
<br>
<input type=submit value="提交">
</form>
<b>负载均衡测试:此为:Tomcat1上的文件,<font color=red>111111</font><b>
</body>
</html>
在复制到其他Tomcat下时,将下面这段改掉即可
<b>负载均衡测试:此为:Tomcat1上的文件,<font color=red>111111</font><b>
<b>负载均衡测试:此为:Tomcat2上的文件,<font color=red>222222</font><b>
4. 启动多个Tomcat。
二. Nginx配置(nginx-1.12.2):
1.conf/nginx.conf:
#user nobody;
worker_processes 1; #工作进程的个数,一般与计算机的cpu核数一致
#错误日志存放路径
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#指定pid存放文件
#pid logs/nginx.pid;
events {
#使用网络IO模型linux建议epoll,FreeBSD建议采用kqueue,window下不指定。
#use epoll;
worker_connections 1024; #单个进程最大连接数(最大连接数=连接数*进程数)
}
http {
include mime.types; #文件扩展名与文件类型映射表
default_type application/octet-stream; #默认文件类型
#定义日志格式
#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 logs/access.log main;
sendfile on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65; #长连接超时时间,单位是秒
#gzip on; #启用Gizp压缩
#服务器的集群
upstream netitcast.com { #服务器集群名字
#Nginx是如何实现负载均衡的,Nginx的upstream目前支持以下几种方式的分配
#1、轮询(默认)
#每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
#2、weight
#指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
#2、ip_hash
#每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
#3、fair(第三方)
#按后端服务器的响应时间来分配请求,响应时间短的优先分配。
#4、url_hash(第三方)
#按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
#ip_hash;
server 192.168.8.23:8080 weight=1;#服务器配置 weight是权重的意思,权重越大,分配的概率越大。
server 192.168.8.23:8079 weight=2;
}
#当前的Nginx的配置
server {
listen 8082; #监听80端口,可以改成其他端口
server_name localhost; #当前服务的域名
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
proxy_pass http://netitcast.com;
proxy_redirect default;
proxy_connect_timeout 3; #nginx跟后端服务器连接超时时间(代理连接超时)
proxy_send_timeout 30; #后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 30; #连接成功后,后端服务器响应时间(代理接收超时)
#root html;
#index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1: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;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
2. 点击nginx.exe,启动Nginx:
3.我在Nginx的启动过程中,遇到过一闪而过,启动失败的情况,这时可以看一下错误日志 logs/error.log
提示的错误是:2018/01/25 11:07:24 [emerg] 456476#460620: bind() to 0.0.0.0:8081 failed (10013: An attempt was made to access a socket in a way forbidden by its access permissions)
这是因为端口被占用了,把8081端口的进程关掉或者换一个其他的端口即可。
三. 测试
我Nginx用的端口是8082,所以输入http://localhost:8082/testCluster.jsp
1.这是第一次显示的画面,Tomcat1代表这是第一个Tomcat的页面
2.这是第二次显示的画面,Tomcat2表示这是第二个Tomcat的页面,说明负载均衡已成功
3.接下来测试Session复制。根据Tomcat变换前后,SESSION ID都是D07D8BB95B783F8CAA83D94175D24846.jvm1可以判断出,这是同一个Session。
可以输入名称和值,看看在服务器切换后,Session中是否可以保存。
4. 可以看到,2=222保存了,但是现在仍然在Tomcat2上,看不出效果,再试一次。
5.这次切换到Tomcat1了,而且Session List里面的值都带过来了。再试试Tomcat1能否传回Tomcat2。
6.回到Tomcat2以后,SessionList中的值仍然保持,至此Session复制完全成功了。