之前系统采用的单一tomcat服务的模式,已经不能满足现阶段的需求了,经常宕机,因此决定换成nginx+tomcat+memcached的结构。
把所有的静态文件都交给nginx来处理,tomcat则只处理jsp、servlet,tomcat的session则托管给memcached实现集群;最后,用awstats来分析nginx的日志文件,用qqhostinfo这个插件在awstats中解析IP归属地。
说下文件夹的规划。把所有的东东都放到/app下。
/app/autodeploy :基于ant和svn的自动构建
/app/nginx :nginx的配置文件、日志切割脚本、日志文件
/app/tomcat :tomcat集群
/app/webapps :web工程文件夹
安装JDK:这个就略过不说了,只要记得要export JAVA_HOME就行了
安装svn、ant :这两个都可以直接yum install来安装。
安装nginx :网上有很多都是编译安装,我这情况比较简单,直接从官网上下载.rpm文件,安装得到nginx的资源库,然后再yum install安装nginx就行了。
安装tomcat :这个直接下载后解压缩就行了。
安装memcached:同样是yum install 就可以了。
好,我们第一步是先把nginx+tomcat+memcached的集群跑起来。
先配置两个tomcat,把memcached相关的jar包都放到tomcat/lib下面去,然后修改tomcat/conf/context.xml
<?xml version='1.0' encoding='utf-8'?>
<Context>
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<Manager
className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:127.0.0.1:11211"
sticky="false"
sessionBackupAsync="true"
lockingMode="uriPattern:/path1|/path2"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
</Context>
然后修改server.xml,把tomcat-user.xml这个文件提取到其他文件夹,让多个tomcat共用这个文件。
多个tomcat做负载均衡,让每个tomcat的端口不一样、jvmRoute名字不一样。
<?xml version='1.0' encoding='utf-8'?>
<Server port="8105" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JasperListener" />
<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="/app/tomcat/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Executor name="tomcatThreadPool" namePrefix="catalina1-exec-" maxThreads="1000" minSpareThreads="200"/>
<Connector
port="8081"
redirectPort="8543"
protocol="HTTP/1.1"
connectionTimeout="60000"
enableLookups="false"
executor="tomcatThreadPool"
acceptCount="1200"
URIEncoding="UTF-8"
compression="on"
compressionMinSize="2048"
noCompressionUserAgents="gozilla,traviata"
compressableMimeType="text/html,text/json,application/javascript,text/javascript,text/css,text/plain,text/xml" />
<Connector port="8109" protocol="AJP/1.3" redirectPort="8543" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="/app/webapps" unpackWARs="true" autoDeploy="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>
接下来,就是把tomcat里面的webapps文件夹复制成 /app/webapps,这样,ROOT就用来放自己的工程文件,manager文件夹就用来监控tomcat的状态。
另外,再配置一下tomcat的内存,修改tomcat/bin/catalina.sh,在文件开头加入下面这句,-server是为了给多个CPU提升性能,另外两个是分配内存,根据实际情况来就可以了。
export JAVA_OPTS="-server -Xms3072m -Xmx25600m"
OK,到这里,tomcat的配置就算是OK了。
下面,开始nginx的配置。
nginx的默认配置文件是/etc/nginx/nginx.conf,但是这个文件,一要root权限,二是分散不好管理,我把nginx.conf移到/app/nginx/nginx.conf,再链接成/etc/nginx/nginx.conf就完事了。
user nginx;
worker_processes 16;
error_log /app/nginx/logs/error.log warn;
pid /var/run/nginx.pid;
events {
use epoll;
worker_connections 1024;
}
http {
include /etc/nginx/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 /app/nginx/logs/access.log main;
server_names_hash_bucket_size 128;
client_header_buffer_size 32k;
large_client_header_buffers 8 32k;
client_max_body_size 30m;
client_body_buffer_size 1024k;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
gzip on;
gzip_vary on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_types text/plain application/x-javascript text/css application/xml;
upstream tomcat{
server 127.0.0.1:8081 weight=1;
server 127.0.0.1:8082 weight=1;
}
server{
listen 80;
server_name localhost;
index index.html;
root /app/webapps/ROOT;
error_page 404 = /404.html;
error_page 500 502 503 504 = /50x.html;
location ~ .*.(do|jsp)$ {
index index.jsp;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://tomcat;
proxy_connect_timeout 10;
proxy_read_timeout 10m;
proxy_buffer_size 4k;
proxy_buffers 64 32k;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 10d;
}
location ~ .*\.(js|css)?$ {
expires 1d;
}
location ~ ^/awstats/ {
index awstats.qht.html;
access_log off;
error_log off;
charset gb2312;
auth_basic "";
auth_basic_user_file /app/nginx/admin.pass; #用htdpasswd生成
}
}
}
文件里awstats的股份,是用来做日志分析的,后面再讲。 upstream 就是用来搞负载均衡的了,把.jsp和.do的请求都给了tomcat,其他的都nginx拦截下来。图片缓存10天,js缓存1天。
配置好了,启动memcached先,用service来启动。
现在就可以启动nginx和tomcat了,至于具体先启动哪一个,没啥特别的讲究。nginx可以用service来启动,tomcat就暂时用普通用户的身份通过startup.sh启动就行了。
# service nginx start
现在,tomcat+nginx的集群就应该可以正常工作了。
由于nginx的轻量,日志文件不会自动切割,几天下来生成的access.log文件就上G了,肯定不行的了,得切割。 用脚本实现。
#!/bin/bash
# Script Name: nginx-log-split.sh
# Author: xiaosilent@gmail.com
# Create Date: 2012-12-14
LOG_PATH=/app/nginx/logs
DATE=$(date -d yesterday +%Y-%m-%d)
cd $LOG_PATH
mv access.log access_$DATE.log
mv error.log error_$DATE.log
kill -USR1 $(cat /var/run/nginx.pid)
很简单的脚本,就实现了自动切割, 然后,我们把任务加入到crontab,这样就不用手工搞了。每天晚上12点弄一下就可以了,这就保证每天一个日志文件了。
# crontab -e
出来vi的界面,把脚本加入进去就行了。
00 00 * * * /app/nginx/nginx-log-split.sh
00 01 * * * /usr/local/awstats/wwwroot/cgi-bin/awstats.pl -update -config=qht
00 10 * * * /usr/local/awstats/tools/awstats_buildstaticpages.pl -update -config=qht -lang=cn -dir=/app/webapps/ROOT/awstats -awstatsprog=/usr/local/awstats/wwwroot/cgi-bin/awstats.pl
先说下,后面的两个脚本,是用来自动生成awstats的报告的。
前面已经两处出现awstats相关的东西了,现在就说下。 从官网上下载rpm然后安装,安装好后的文件在/usr/local/awstats。
把qqhostinfo相关文件传到/usr/local/awstats/wwwroot/cgi-bin/plugins下面,把pm和pl文件里的关于qqwry.dat文件的路径改成绝对路径
执行下 /usr/local/awstats/tools/awstats_configure.pl文件,第一个问题要求输入web server路径的时候填写none,因为我们用的是nginx。后面的都默认。
然后修改/etc/awstats/awstats.domain.conf文件,指定其中的LogFile,然后再跟着加一句: LogdPlugin "qqhostinfo" 就搞定了。
把上面crontab里面的后两个命令搞一遍,就可以访问到统计信息了。
最后贴下自动部署的脚本:
#!/bin/bash
# Script Name: autodeploy.sh
# Description: auto deploy QHT on CentOS
# Author: xiaosilent@gmail.com
# Create Date: 2012-10-16
# Update Date: 2012-12-14
echo "step1. 载入全局变量..."
source /etc/profile
echo "step2. 输出新的环境变量..."
export TOMCAT_HOME=/app/tomcat
export DEPLOY_HOME=/app/autodeploy
export WEBAPPS_HOME=/app/webapps
echo "step3. 生成内部临时变量..."
SOURCE_DIR=$DEPLOY_HOME/source
VERSION_FILE=$DEPLOY_HOME/svn.txt
echo "step4. 删除旧版本号..."
rm -rf $VERSION_FILE
echo "step5. 清理并更新源代码..."
svn cleanup $SOURCE_DIR
svn update $SOURCE_DIR
echo "step6. 输出新版本号..."
svnversion $SOURCE_DIR>$VERSION_FILE
echo "step7. 通过ANT构建系统,耗时较长..."
ant -f $DEPLOY_HOME/build.xml
function compile_js(){
for file in `ls $1`
do
if [ -d $1/$file ]; then
compile_js $1/$file
else
local path=$1/$file
local name=$file
fi
if [ ${#path} -gt "0" ]; then
java -jar $DEPLOY_HOME/compiler.jar --charset utf-8 --js $path --js_output_file $path.min
echo "压缩JS:"$path
mv $path.min $path
fi
done
}
echo "step8. 最后压缩JS文件,耗时最长..."
compile_js $WEBAPPS_HOME/ROOT/resources/js
echo ""
echo ""
echo "final. 成功完成自动部署,如无意外,可以关闭PuTTY了。"
echo ""
<project name="build" default="build" basedir=".">
<property environment="env" />
<property name="tomcat.home" value="${env.TOMCAT_HOME}" />
<property name="webapps.home" value="${env.WEBAPPS_HOME}" />
<property name="project.dir" value="${basedir}/source" />
<property name="path.dir" value="ROOT" />
<property name="build.dir" value="${basedir}/build" />
<property name="src.dir" value="${build.dir}/src" />
<property name="web.dir" value="${build.dir}/WebContent" />
<property name="class.dir" value="${web.dir}/WEB-INF/classes" />
<property name="lib.dir" value="${web.dir}/WEB-INF/lib" />
<tstamp>
<format property="build.time" pattern="yyyy-MM-dd" />
</tstamp>
<path id="build.classpath">
<fileset dir="${tomcat.home}/tomcat1/lib">
<include name="*.jar" />
</fileset>
<fileset dir="${lib.dir}">
<include name="*.jar" />
</fileset>
<pathelement location="${class.dir}" />
</path>
<target name="build">
<echo>step7.1 删除可能已经存在的build文件夹</echo>
<delete dir="${build.dir}" />
<echo>step7.2 新建build文件夹及其附属文件夹</echo>
<mkdir dir="${build.dir}" />
<mkdir dir="${src.dir}" />
<mkdir dir="${class.dir}" />
<mkdir dir="${lib.dir}" />
<echo>step7.3 复制Java源代码到build文件夹的src下</echo>
<copy todir="${src.dir}" overwrite="true">
<fileset dir="${project.dir}/src" />
</copy>
<echo>step7.4 复制WebContent的源码到build文件夹的WebContent下</echo>
<copy todir="${web.dir}" overwrite="true">
<fileset dir="${project.dir}/WebContent" />
</copy>
<echo>step7.5 在build文件夹下编译源码</echo>
<javac fork="true" memoryinitialsize="512m" memorymaximumsize="1536m" srcdir="${src.dir}" destdir="${class.dir}" includes="**" encoding="utf-8" debug="true">
<classpath refid="build.classpath" />
</javac>
<echo>step7.6 将src下的非java文件复制到class文件夹下</echo>
<copy todir="${class.dir}" overwrite="true">
<fileset dir="${src.dir}" excludes="**/*.java" />
<fileset file="${basedir}/svn.txt" />
</copy>
<echo>step7.7 关闭正在运行的Tomcat</echo>
<!--
以下注释掉的两种,是在windows平台上关闭Tomcat用的,分别是用bat和服务方式关闭。
<exec executable="${tomcat.home}/bin/shutdown.bat" failοnerrοr="false" />
<exec executable="net"><arg line=" stop Tomcat7" /></exec>
下面注释掉的,是在Linux平台上,关闭服务方式启动的Tomcat。
<exec executable="service"><arg line=" tomcat stop" /></exec>-->
<exec executable="sh"><arg line=" ${tomcat.home}/tomcat1/bin/shutdown.sh" /></exec>
<exec executable="sh"><arg line=" ${tomcat.home}/tomcat2/bin/shutdown.sh" /></exec>
<echo>step7.8 暂停,休息5s</echo>
<sleep seconds="5" />
<echo>step7.9 删除原工程文件和Tomcat的work目录</echo>
<delete dir="${webapps.home}/${path.dir}/resources" />
<delete dir="${webapps.home}/${path.dir}/WEB-INF" />
<delete dir="${tomcat.home}/tomcat1/work" />
<delete dir="${tomcat.home}/tomcat2/work" />
<echo>step7.10 将编译好的新版本程序复制到工程文件夹来</echo>
<copy todir="${webapps.home}/${path.dir}">
<fileset dir="${web.dir}" />
</copy>
<echo>step7.11 重新启动Tomcat</echo>
<!--以下注释掉的两种,是在windows平台上启动Tomcat用的,分别是用bat和服务方式启动。
<exec executable="${tomcat.home}/bin/startup.bat" failοnerrοr="false" />
<exec executable="net"><arg line=" start Tomcat7" /></exec>
<exec executable="service"><arg line=" tomcat start" /></exec>-->
<exec executable="sh"><arg line=" ${tomcat.home}/tomcat1/bin/startup.sh" /></exec>
<exec executable="sh"><arg line=" ${tomcat.home}/tomcat2/bin/startup.sh" /></exec>
<echo>step7.final OK 服务器基本部署已经完成,下面开始JS文件压缩操作!</echo>
</target>
</project>