nginx+tomcat 使用教程

Nginx + Tomcat


Nginx负载均衡,其实主要就是用upstream、server指令,再配以权重等等参数。如果为了让nginx支持session共享,还需要额外增加一个模块。 


一、Nginx负载均衡  
在http{...}中配置一个upstream{...},参考如下: 
引用

    upstream tomcat { 
        server 10.11.155.26:8080; 
        server 10.11.155.41:8080; 
    } 

接着修改location节点,配置代理: 
引用
location / { 
      ... 
            proxy_pass http://tomcat; 

      ... 
}

当访问根路径时,会轮播路由到两台服务器上,至于后端服务器是tomcat还是jetty之类的,都无所谓,照葫芦画瓢就是了。  
当然,有的机器性能好,或者负载低,可以承担高负荷访问量,可以通过权重(weight),提升访问频率。数值越高,被分配到的请求数越多。 
server指令参数如下: 
  • weight——权重,数值越大,分得的请求数就越多,默认值为1。
  • max_fails——对访问失败的后端服务器尝试访问的次数。默认值为1,当设置为0时将关闭检查。
  • fail_timeout——失效超时时间,当多次访问失败后,对该节点暂停访问。
  • down——标记服务器为永久离线状态,用于ip_hash指令。
  • backup——仅当非backup服务器全部宕机或繁忙时启用。

例如,可以这样配置: 
引用

    upstream tomcat { 
        server 10.11.155.26:8080 weight=5; 
        server 10.11.155.41:8080 weight=10; 
    } 

后者分得的请求数就会较高。 

二、Nginx+Tomcat+Session共享  
为了让Nginx支持Tomcat的Session共享,需要对其升级,增加jvmroute模块。 

1.下载nginx-upstream-jvm-route组件、对nginx源码做补丁。  
我把nginx-upstream-jvm-route下载到了/opt/software路径下。 
先切换到nginx源码目录下,执行: 
Shell代码   收藏代码
  1. patch -p0 < /opt/software/nginx_upstream_jvm_route/jvm_route.patch  

引用
patching file src/http/ngx_http_upstream.c 
Hunk #1 succeeded at 4095 (offset 358 lines). 
Hunk #3 succeeded at 4227 (offset 358 lines). 
Hunk #5 succeeded at 4326 (offset 358 lines). 
patching file src/http/ngx_http_upstream.h 
Hunk #1 succeeded at 90 (offset 5 lines). 
Hunk #3 succeeded at 118 (offset 5 lines).

说明补丁做好了!  

2.升级nginx  
先别急着折腾nginx-upstream-jvm-route,先看看nginx当时安装时的参数: 
Shell代码   收藏代码
  1. nginx -V  

引用
nginx version: nginx/1.2.0 
configure arguments: --prefix=/opt/servers/nginx --user=nginx --group=www --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-pcre=/opt/software/pcre-8.10 --with-zlib=/opt/software/zlib-1.2.5 --with-http_stub_status_module --with-http_realip_module --with-http_gzip_static_module

记得先备份nginx.conf!  
使用追加参数(--add-module),增设nginx-upstream-jvm-route模块, --add-module=/opt/software/nginx_upstream_jvm_route ,编译安装。 
Shell代码   收藏代码
  1. ./configure --prefix=/opt/servers/nginx --user=nginx --group=www --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-pcre=/opt/software/pcre-8.10 --with-zlib=/opt/software/zlib-1.2.5 --with-http_stub_status_module --with-http_realip_module --with-http_gzip_static_module --add-module=/opt/software/nginx_upstream_jvm_route && make && make insatll  

如果没有错误提示,nginx就成功升级了!  

3.修改upstream配置  
要让Nginx支持Tomcat的jvmRoute,并共享session,在upstream下作如下修改: 
引用

    upstream tomcat { 
        server 10.11.155.26:8080 srun_id=tomcat1; 
        server 10.11.155.41:8080 srun_id=tomcat2; 

        jvm_route $cookie_JSESSIONID|sessionid reverse; 
    } 

srun_id 跟tomcat配置有关。 

4.Tomcat集群配置(Tomcat6、7通用)  
该配置参考 征服 Apache + Tomcat ,以下仅作简要说明。 
a.修改server.xml 
找到 Engine 节点,并设置 jvmRoute ,这里指定 tomcat1 。 
Xml代码   收藏代码
  1. <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">  

可以直接粘贴以下代码,并对应修改 Receiver 节点中的 address 属性,指向本机: 
Xml代码   收藏代码
  1. <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"  
  2.           channelSendOptions="8">  
  3.     <Manager className="org.apache.catalina.ha.session.DeltaManager"  
  4.              expireSessionsOnShutdown="false"  
  5.              notifyListenersOnReplication="true"/>  
  6.     <Channel className="org.apache.catalina.tribes.group.GroupChannel">  
  7.        <Membership className="org.apache.catalina.tribes.membership.McastService"  
  8.                address="224.0.0.0"  
  9.                port="45564"  
  10.                frequency="500"  
  11.                dropTime="3000"/>  
  12.        <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"  
  13.                 address="10.11.155.26"  
  14.                 port="4000"  
  15.                 autoBind="100"  
  16.                 selectorTimeout="5000"  
  17.                 maxThreads="6"/>  
  18.        <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">  
  19.            <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>  
  20.        </Sender>  
  21.        <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>  
  22.        <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>  
  23.     </Channel>  
  24.     <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"  
  25.            filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>  
  26.     <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>  
  27.     <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>  
  28.     <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>  
  29.   </Cluster>  

注:如果本机上有多个tomcat并存,Receiver节点中的port属性,使其绑定在不同的端口上。  
Membership 节点 address 属性配置多播地址,可使用 route 命令将其打开,参考如下: 
Shell代码   收藏代码
  1. route add -net 224.0.0.0/8 dev eth0   

b.修改应用的 web.xml  
在web.xml末尾增加 <distributable />  
Xml代码   收藏代码
  1. <web-app>  
  2. ...  
  3.         <distributable />   
  4. </web-app>  


至此,已完成所有配置,重启tomcat、nginx,访问服务测试页面(见附件),强行关闭其中一台tomcat,令请求转向另一个台tomcat,测试session是否同步: 
nginx+tomcat 使用教程_第1张图片  

session共享成功,非粘性实现

征服 Nginx

一、准备工作  
下载如下组件: 
  • nginx-1.2.0
  • pcre-8.10
  • zlib-1.2.5

pcre,有关正则表达式匹配;zlib,用于压缩。这些就不细说了,如果要安装最简版的nginx,记得准备好这两样东西就好了。 
用root账户启动服务是比较危险的!   前段时间,测试服务器被黑掉了,终归到底是通过一个root启动的服务上传了木马,最后连ssh都屏蔽了,活生生成为一台肉鸡。。。  
所以,惨痛的经验告诉我,一定要为服务建立对应的组和用户,限制访问权限,降低风险!   
这里为nginx建立一个www组,并建立一个不登录的账户nginx: 

Shell代码   收藏代码
  1. #追加一个www组  
  2. groupadd -f www  
  3. #追加一个nginx用户  
  4. useradd -s /sbin/nologin -g www nginx  


建立一个目录用于存放nginx日志文件,并赋予相应权限: 
Shell代码   收藏代码
  1. #建立nginx日志目录  
  2. mkdir /var/log/nginx  
  3. #赋予访问权限  
  4. chown nginx.www /var/log/nginx  


二、编译安装  
我把pcre、zlib、nginx的压缩包都放在了 /opt/software 路径下,服务要装在 /opt/servers 路径下。 
先对pcre、zlib、nginx解压,然后编译安装: 
Shell代码   收藏代码
  1. ./configure --prefix=/opt/servers/nginx \  
  2. --user=nginx \  
  3. --group=www \  
  4. --pid-path=/var/run/nginx.pid \  
  5. --error-log-path=/var/log/nginx/error.log \  
  6. --http-log-path=/var/log/nginx/access.log \  
  7. --with-pcre=/opt/software/pcre-8.10 \  
  8. --with-zlib=/opt/software/zlib-1.2.5 \  
  9. --with-http_stub_status_module \  
  10. --with-http_realip_module \  
  11. --with-http_gzip_static_module \  
  12. --without-http_fastcgi_module \  
  13. --without-http_memcached_module \  
  14. --without-http_map_module \  
  15. --without-http_geo_module \  
  16. --without-http_autoindex_module \  
  17. --with-poll_module   
  18. && make && make install   


三、系统配置  
我希望nginx可以作为一个服务,通过service命令启动或停止。 
这样做的好处是,不论我用什么用户调用这个service命令,都不会因为使用错误的账户带来安全问题。 
建立一个系统文件: 
Shell代码   收藏代码
  1. vim /etc/init.d/nginx  


前人栽树,后人乘凉。已经有老鸟做好了启动配置文件: 
Shell代码   收藏代码
  1. #!/bin/bash  
  2. # v.0.0.1  
  3. # create by jackbillow at 2007.10.15  
  4. # nginx - This shell script takes care of starting and stopping nginx.  
  5. #  
  6. # chkconfig: - 60 50  
  7. # description: nginx [engine x] is light http web/proxy server  
  8. # that answers incoming ftp service requests.  
  9. # processname: nginx  
  10. # config: /etc/nginx.conf  
  11. nginx_path="/opt/servers/nginx"  
  12. nginx_pid="/var/run/nginx.pid"  
  13.   
  14. # Source function library.  
  15. . /etc/rc.d/init.d/functions  
  16.   
  17. # Source networking configuration.  
  18. . /etc/sysconfig/network  
  19.   
  20. # Check that networking is up.  
  21. [ ${NETWORKING} = "no" ] && exit 0  
  22. [ -x $nginx_path/sbin/nginx ] || exit 0  
  23. RETVAL=0  
  24. prog="nginx"  
  25. start() {  
  26. # Start daemons.  
  27. if [ -e $nginx_pid -a ! -z $nginx_pid ];then  
  28.         echo "nginx already running...."  
  29.         exit 1  
  30. fi  
  31. if [ -e $nginx_path/conf/nginx.conf ];then  
  32.         echo -n $"Starting $prog: "  
  33.         $nginx_path/sbin/nginx -c $nginx_path/conf/nginx.conf &  
  34.         RETVAL=$?  
  35.         [ $RETVAL -eq 0 ] && {  
  36.                 touch /var/lock/subsys/$prog  
  37.                 success $"$prog"  
  38.         }  
  39.         echo  
  40. else  
  41.         RETVAL=1  
  42. fi  
  43.         return $RETVAL  
  44. }  
  45. # Stop daemons.  
  46. stop() {  
  47.         echo -n $"Stopping $prog: "  
  48.         killproc -d 10 $nigx_path/sbin/nginx  
  49.         RETVAL=$?  
  50.         echo  
  51.         [ $RETVAL = 0 ] && rm -f $nginx_pid /var/lock/subsys/$prog  
  52. }  
  53. # See how we were called.  
  54. case "$1" in  
  55. start)  
  56.         start  
  57.         ;;  
  58. stop)  
  59.         stop  
  60.         ;;  
  61. restart)  
  62.         stop  
  63.         start  
  64.         ;;  
  65. status)  
  66.         status $prog  
  67.         RETVAL=$?  
  68.         ;;  
  69. *)  
  70.         echo $"Usage: $0 {start|stop|restart|status}"  
  71.         exit 1  
  72. esac  
  73. exit $RETVAL  


注意,这里的路径: 
引用
nginx_path="/opt/servers/nginx" 
nginx_pid="/var/run/nginx.pid" 


如果你的nginx安装路径在其它位置,请对应修改!  
然后赋予这个文件执行权限: 
Shell代码   收藏代码
  1. chmod +x /etc/init.d/nginx  


追加为系统服务: 
Shell代码   收藏代码
  1. chkconfig --add nginx  
  2. chkconfig nginx on  


现在就可以使用,如下命令控制nginx服务了!  
引用
#启动nginx 
service nginx start 
#停止nginx 
service nginx stop 
#重启nginx 
service nginx restart 
#查看nginx状态 
service nginx status 


三、vim语法支持  
vim对于nginx配置文件的支持不那么友好,无法对关键字高亮显示。   
不过,没关系。我们可以通过修改 ~/.vim 配置,增强vim功能。 
参考 vim for nginx   配置VIM语法高亮及自动缩进  

nginx+tomcat 使用教程_第2张图片  

可能你的用户目录下并没有 .vim 这个目录,需要自行建立。 
Shell代码   收藏代码
  1. mkdir ~/.vim  
  2. mkdir ~/.vim/syntax  


建立针对nginx的配置文件,这里的nginx配置文件的路径是 /opt/servers/nginx/conf ,注意对应修改: 
Shell代码   收藏代码
  1. echo 'au BufRead,BufNewFile /opt/servers/nginx/conf/* set ft=nginx' > ~/.vim/filetype.vim  


最后,请出主角(下载的是本文的附件  ): 
Shell代码   收藏代码
  1. wget http://dl.iteye.com/topics/download/7979de38-1263-3b65-8f7a-e5f567d40fec  
  2. unzip nginx.zip  
  3. mv nginx.vim ~/.vim/syntax/  

这样就ok了!  

四、基本配置  
完成上述工作后,nginx还不能急于投入使用,需要做一些基本配置与优化工作。 
修改nginx配置文件: 
Shell代码   收藏代码
  1. vim /opt/servers/nginx/conf/nginx.conf  

微调 
引用

#使用的用户和组,这里我们为nginx服务新建了nginx账户和www工作组 
user  nginx www; 
#制定的工作衍生进程数(2倍于CPU内核数) 
worker_processes  4; 
#错误日志存放路径,日志级别由低到高[debug | info | notice | warn | error | crit] 
error_log  /var/log/nginx/error.log crit; 
#指定文件描述符数量 与ulimit -n数值保持一致 
work_rlimit_nofile 65535; 
events { 
#使用的网络I/O模型,Linux用epoll模型,Unix用kqueue模型 
use epoll; 
#允许的连接数 
worker_connections 51200; 

http{ 
    include       mime.types; 
    default_type  application/octet-stream; 
    #追加 '"$sent_http_cache_control" "$sent_http_pl" "$request_time"'获取请求细节信息 
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" ' 
                      '$status $body_bytes_sent "$http_referer" ' 
                      '"$http_user_agent" "$http_x_forwarded_for"' 
                      '"$sent_http_cache_control""$sent_http_pl" "$request_time"'; 
    access_log          /var/log/nginx/access.log  main; 
    ... 
    server{ 
        ... 
        location / { 
            root   html; 
            index  index.html index.htm index.jsp index.do; 
            #在header中传递请求放host、ip等信息 
            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_header Content-Type; 
            proxy_pass_header Content-Disposition; 
            proxy_pass_header Content-Length; 
            ... 
        } 
    } 



五、虚拟目录  
nginx配置虚拟目录很简单,主要是运用root、alias两个指令。 
以访问图片服务为例: 
root,用于相对路径 
引用

        location /image/ { 
             root /data; 
        } 

当我们访问“/image/”路径时,实际上访问的是“/data/image/”,注意“/data”后面不要有“/” 
alias,用于绝对路径 
引用

        location /image/ { 
                alias /data/img/; 
        } 

当我们访问“/image/”路径时,实际上访问的是“/data/img/”,注意“/data/img/”以“/”结尾。 

六、重定向  
有时候链接不加考虑就放出去了,突然哪天需要调整,又不能及时撤回已放出的链接地址。只好自己修改nginx配置。 
譬如,放出去的链接:/activity.do?m=v 想让它指到/路径上: 
引用
rewrite ^/activity(.*)$ / last;


想要把请求来的参数也带上: 
引用
rewrite ^/activity(.*)$ /$1 last;


$1 指得是第一个参数,以此类推。 


六、监控  
引用
       location /status { 
            stub_status on; 
            access_log   off; 
            allow  10.10.0.0/16; 
            allow  10.1.0.0/16; 
            allow  10.11.0.0/16; 

            deny all; 
        }


引用

Active connections: 14 
server accepts handled requests 
62 62 302 
Reading: 0 Writing: 3 Waiting: 11 


七、日志分割  
Shell代码   收藏代码
  1. #!/bin/bash  
  2. # THis script run at 00:00  
  3. # author dongliang at 2012-09-07  
  4. # Nginx Log Path  
  5. logs_path="/var/log/nginx/"  
  6. # Nginx PID Path  
  7. nginx_pid="/var/run/nginx.pid"  
  8.   
  9. mkdir -p ${logs_path}$(date -d "yesterday" +"%Y")/$(date -d "yesterday" +"%m")/  
  10.   
  11. mv ${logs_path}access.log ${logs_path}$(date -d "yesterday" +"%Y")/$(date -d "yesterday" +"%m")/access_$(date -d "yesterday" +"%Y%m%  
  12. d").log  
  13.   
  14. mv ${logs_path}error.log ${logs_path}$(date -d "yesterday" +"%Y")/$(date -d "yesterday" +"%m")/error_$(date -d "yesterday" +"%Y%m%d"  
  15. ).log  
  16.   
  17. kill -USR1 `cat $nginx_pid`  


赋予执行权限 
Shell代码   收藏代码
  1. chmod +x nginx_log.sh  

凌晨执行 


Shell代码   收藏代码
  1. crontab -e  
  2. 0 0 * * * /opt/script/nginx_log.sh  


Apache + Tomcat

步骤: 
  1. 安装Apache基本模块
  2. 后台监控
  3. 负载均衡简单测试
  4. 配置Tomcat相关模块(AJP)
  5. 保持Session唯一,粘性会话
  6. Tomcat集群,Session复制


1.安装Apache相关模块  
负载均衡需要的主要是代理模块! 
经过几次Apache配置尝试,在Ubuntu下配置Apache实在是太容易了。加载什么模块、取消什么模块两个命令搞定。 

Shell代码   收藏代码
  1. #启用模块  
  2. sudo a2enmod <model>  
  3. #禁用模块  
  4. sudo a2dismod <model>  


这里,我们需要让Apache提供代理服务,其中又包含基于http、ftp、ajp等等协议的代理功能,同时还需要负载均衡模块。我们可以通过命令逐个加载: 

Shell代码   收藏代码
  1. #代理核心模块  
  2. sudo a2enmod proxy  
  3. #代理AJP模块  
  4. sudo a2enmod proxy_ajp  
  5. #代理负载均衡模块  
  6. sudo a2enmod proxy_balancer  
  7. #代理HTTP模块  
  8. sudo a2enmod proxy_http  
  9. #代理FTP模块  
  10. sudo a2enmod proxy_ftp  


完成上述操作后,系统会提示重启Apache! 
先不着急重启,现学现卖,了解下Apache的目录结构。在Ubuntu下配置Apache主要是在 /etc/apache2 目录下: 
nginx+tomcat 使用教程_第3张图片  
分述: 
  • apache2.conf核心配置文件,一般不需要修改!
  • conf.d目录,里面包含了一些字符集设置,文档等设置!
  • dav_svn.authzdav_svn.passwd是前面做SVN时,相关权限、密码文件。
  • envvars定义了运行时的用户身份——www-data。
  • httpd.conf是Apache留给我们自己折腾的配置文件,默认为空。apache2.conf会加载这个文件。
  • ports.conf端口默认配置。apache2.conf会加载这个文件。
  • magic为mod_mime_magic模块服务。
  • mods-enabledmods-available mods-enabled会被apache2.conf加载,里面包含*.load和*.conf文件。*.load文件中是加载相应的模块(位于/usr/lib/apache2/modules/中),而*.conf中是对应的基本配置。但这些文件其实都是链接到mods-available中相应的文件上。当我们通过a2enmod操作时,实际上正是操作了这些软链接。
  • sites-availablesites-enabled 与 mods-enabledmods-available的关系类似,只是其中包含的是站点内容。

罗嗦了一堆,下面配置负载均衡部分。 
执行修改: 
Shell代码   收藏代码
  1. sudo vi /etc/apache2/mods-available/proxy.conf  

nginx+tomcat 使用教程_第4张图片  
上图红框中的内容是原始内容,白框中的内容是我新加的部分。 
注意红框中的配置: 
Conf代码   收藏代码
  1. <Proxy *>  
  2.         AddDefaultCharset off  
  3.         Order deny,allow  
  4.         #Deny from all  
  5.         Allow from localhost ip6-localhost    
  6. </Proxy>  

在默认配置中,Deny from all处于可用状态。当我们配置其他代理节点时,将导致杜绝访问! 使用 Allow from localhost ip6-localhost  限制仅允许本机访问! 
再说,白框中的内容: 
Conf代码   收藏代码
  1. <Proxy balancer://zlex>  
  2.         BalancerMember http://localhost:8080/  
  3.         BalancerMember http://192.168.49.1:8080/  
  4. </Proxy>  

这里,我配置了一个负载均衡节点 balancer://zlex ,其中包含了两个服务 http://localhost:8080/ http://192.168.49.1:8080/ ,一个是虚拟机上的Tomcat、一个是真机上的Tomcat。这里使用的Http的转发方式,当然,使用AJP未尝不可,稍后详述! 
这里的节点次序会有一个先后关系,Apache会将请求按照FIFO的方式调度顺次分配到各个节点上!如果其中有一个节点挂掉,将跳过该节点顺次寻找可用节点。  
再说代理和反向代理: 
Conf代码   收藏代码
  1. ProxyPass /zlex balancer://zlex  
  2. ProxyPassReverse /zlex balancer://zlex  

这里配置,如果要访问 /zlex 路径时,将跳转到 balancer://zlex 上,也就是享受负载均衡! 

2.后台监控  
我们如何知道某个节点负载多少,响应时间多久,服务是否正常呢?Apache提供了负载均衡监控平台: http://localhost/balancer-manager 。但是,这个服务默认是不存在。 
除了用于负载均衡配置、监控的 balancer-manager 还有 http://localhost/server-status http://localhost/server-info  
我们需要添加info模块: 
Shell代码   收藏代码
  1. #系统信息模块  
  2. sudo a2enmod info  

同时,为了能够使用 balancer-manager ,我们需要配置: 
Conf代码   收藏代码
  1. <Location /balancer-manager>  
  2.         SetHandler balancer-manager  
  3.         Order Deny,Allow  
  4.         #Deny from all  
  5.         Allow from localhost ip6-localhost  
  6. </Location>  

把这段代码放到哪?由于它同属系统信息配置,我把它放到了 info.conf 中,说白了就是照猫画虎: 
Shell代码   收藏代码
  1. sudo vi /etc/apache2/mods-available/info.conf  

nginx+tomcat 使用教程_第5张图片  
注意,这段代码放到了 <IfModule mod_info.c> </IfModule> 之间! 
现在,我们重启Apache: 
Shell代码   收藏代码
  1. sudo /etc/init.d/apache2 restart  

来看看管理界面 http://localhost/balancer-manager : 
nginx+tomcat 使用教程_第6张图片

我们再来看看服务器基本信息 http://localhost/server-info : 
nginx+tomcat 使用教程_第7张图片

上述两个服务需要加载info模块,而服务器状态(server-status)不需要 http://localhost/server-status : 
nginx+tomcat 使用教程_第8张图片

3.负载均衡简单测试  
疯狂访问 http://localhost/zlex ,直到手酸眼烦! 
我这里故意使用不同了Tomcat界面,来验证自己的配置是否生效。更疯狂的是,我甚至把节点指向了百度、搜狐,来测试负载均衡的效果。如果你细致观察,Apache是将请求顺次分配到各个节点上的。 
如果其中一个节点发生问题(例如,强行关闭一个Tomcat,或配置一个错误节点)Apache将会经过几次尝试后,绕过这个问题节点,寻找可以成功访问的节点。如果这个节点恢复正常使用,Apache将在该Tomcat恢复正常工作后大约1分钟内将该节点标识为可用!  
现在,再看看现在的后台( http://localhost/balancer-manager )啥样子: 
 
如果我们控制一个节点的状态是否可用,该怎么做: 
nginx+tomcat 使用教程_第9张图片
涉及到负载量,session同步等等,我们最后讨论! 

4.配置Tomcat相关模块(AJP)  
基于Http协议分发并不复杂,但AJP效果更好!一次诡异事件中,内网访问正常,外网访问多次失败,最后通过AJP方式完美解决了! 
在Tomcat中配置AJP也很简单,修改 server.xml 开启AJP模块: 
Shell代码   收藏代码
  1. sudo vi /etc/tomcat6/server.xml   

开启AJP配置: 
Xml代码   收藏代码
  1. <Connector  port="8009" protocol="AJP/1.3"   
  2.                 URIEncoding="UTF-8"  
  3.                 redirectPort="8443" />  

注意使用的端口port="8009",字符集URIEncoding="UTF-8",这是输入框、请求字符集乱码的入口!  
接下里就可以通过AJP方式进行节点分发了。修改 /etc/apache2/mods-available/proxy.conf  : 
Shell代码   收藏代码
  1. sudo vi /etc/apache2/mods-available/proxy.conf    

http 改为 ajp ,将 8080 改为 8009 : 
Conf代码   收藏代码
  1. <Proxy balancer://zlex>  
  2.         BalancerMember ajp://localhost:8009/  
  3.         BalancerMember ajp://192.168.49.1:8009/  
  4. </Proxy>  

重启Apache: 
Shell代码   收藏代码
  1. sudo /etc/init.d/apache2 restart   

再看看管理界面 http://localhost/balancer-manager  
nginx+tomcat 使用教程_第10张图片  
至此,我们完成了基本负载均衡的基本配置!  

/etc/apache2/mods-available/proxy.conf 还有一些属性: 
noFailOver 是否打开失败转移, On | Off ,默认为Off,添加在 ProxyPass 后面,如: 
Conf代码   收藏代码
  1. ProxyPass /zlex balancer://zlex  stickySession=JSESSIONID noFailOver=On  

如果这样配置,当提供给你服务的服务器发生异常,那么你将一直看着它返回给你503,直到系统恢复正常!  

loadfactor 表示后台服务器负载到由Apache发送请求的权值,默认值为1添加在 BalancerMember 后面: 
Conf代码   收藏代码
  1. <Proxy balancer://zlex>  
  2.         BalancerMember ajp://localhost:8009/  
  3.         BalancerMember ajp://192.168.49.1:8009/  
  4. </Proxy>  


可以实现三种策略: 
  1. 轮询均衡策略的配置
  2. 按权重分配均衡策略的配置
  3. 权重请求响应负载均衡策略的配置


5.Session唯一,粘性会话  
Apache已经可以轻松将内容处理的工作分配给各个Tomcat了! 
当然,这还不够,Session还是个问题! 
WHY?
 
我们来做一系列修改,来检测Session到底出现了什么问题! 
先来改造Tomcat,修改 server.xml : 
Shell代码   收藏代码
  1. sudo vi /etc/tomcat6/server.xml   

修改 <Engine /> 节点,增加 jvmRoute 属性: 
Xml代码   收藏代码
  1. <Engine   
  2.              name="Catalina"   
  3.              defaultHost="localhost"   
  4.              jvmRoute="tomcat1">  

另一个Tomcat设置改为 
Xml代码   收藏代码
  1. <Engine   
  2.              name="Catalina"   
  3.              defaultHost="localhost"   
  4.              jvmRoute="tomcat2">  

通过 jvmRoute ,指定了Tomcat唯一标识! 
然后修改 /etc/apache2/mods-available/proxy.conf  
Shell代码   收藏代码
  1. sudo vi /etc/apache2/mods-available/proxy.conf  

如下: 
Java代码   收藏代码
  1. <Proxy balancer://zlex>  
  2.         BalancerMember ajp://localhost:8009/zlex     route=tomcat1   
  3.         BalancerMember ajp://192.168.49.1:8009/zlex        route=tomcat2   
  4. </Proxy>  
  5.   
  6. ProxyPass /zlex balancer://zlex  
  7. ProxyPassReverse /zlex balancer://zlex  


这里需要通过修改 route 属性,将Apache与Tomcat关联起来! 
注意,Tomcat中定义的jvmRoute需要与Apache定义的route相对应!  
我们来看一下 http://localhost/balancer-manager 发生了什么变化: 
nginx+tomcat 使用教程_第11张图片
我们注意到route字段有了新的标识,当然,我们也可以通过这个配置界面修改这些信息,但当前修改不会真的修改/etc/apache2/mods-available/proxy.conf文件,Apache重启后将丢失。  

为了更细致的对比进过复杂均衡的结果,这里增加了zlex应用!主要是监控Session的变化! 
只看核心代码: 
Jsp代码   收藏代码
  1. <b>当前SessionID:</b>  
  2. <br />  
  3. <%  
  4.         String sessionID = session.getId();  
  5.         out.println(sessionID);  
  6.         System.err.println("sessionID = " + sessionID);  
  7.   
  8.         // 如果有新的 Session 属性设置  
  9.         String dataName = request.getParameter("dataName");  
  10.         if (dataName != null && !dataName.isEmpty()) {  
  11.                 String dataValue = request.getParameter("dataValue");  
  12.                 session.setAttribute(dataName, dataValue);  
  13.         }  
  14. %>  
  15. <br />  
  16. <br />  
  17. <b>Session属性列表:</b>  
  18.   
  19. <br />  
  20. <%  
  21.         Enumeration<String> e = (Enumeration<String>) session  
  22.                         .getAttributeNames();  
  23.         while (e.hasMoreElements()) {  
  24.                 String name = e.nextElement();  
  25.                 String value = (String) session.getAttribute(name);  
  26.                 out.println(name + " = " + value + "<br>");  
  27.                 System.err.println(name + " = " + value);  
  28.         }  
  29. %>  
  30. <form method="POST">  
  31. <ul style="list-style-type: none;">  
  32.         <li><label for="dataName">键:</label><input size="20" id="dataName"  
  33.                 name="dataName"></li>  
  34.         <li><label for="dataValue">值:</label><input size="20"  
  35.                 id="dataValue" name="dataValue"></li>  
  36.         <li><input type="submit" value="提交" /></li>  
  37. </ul>  
  38. </form>  

将其做成一个名为zlex的web应用,分别部署到两个Tomcat上! 
然后重启Apache: 
Shell代码   收藏代码
  1. sudo /etc/init.d/apache2 restart  

不断刷新 http://localhost/zlex ,看看真正的结果: 
第1次: nginx+tomcat 使用教程_第12张图片  
第2次: nginx+tomcat 使用教程_第13张图片  
第3次: nginx+tomcat 使用教程_第14张图片  
第4次: nginx+tomcat 使用教程_第15张图片  
仔细观察,每次请求都按照负载均衡配置的节点次序依次请求到不同的Tomcat上。尤其是当我们通过 jvmRoute route 做了绑定之后,信息更加准确。但是, 仔细观察,每次请求的SessionID都是不一样! 对于纯Web应用,尤其是依靠SessionID区分唯一用户的应用,这将是一场噩梦——解决了服务器压力均衡问题,却带来了SessionID不唯一问题!这就需要SessionID绑定,或者说叫做“会话复制”。 
如果这时候你在页面上提交表单,将键值对保持在session中,在页面刷新后,将无法获得该信息,因为Seesion丢失了!  
接着修改 /etc/apache2/mods-available/proxy.conf ,让SeesionID保持唯一: 
Shell代码   收藏代码
  1. sudo vi /etc/apache2/mods-available/proxy.conf  

增加 stickySession 属性: 
Conf代码   收藏代码
  1. ProxyPass /zlex balancer://zlex  stickySession=JSESSIONID  

stickySession 粘性会话,根据这一属性,浏览器将通过cookie绑定SeesionID。如果这个时候再次访问 http://localhost/zlex ,你会发现,页面不会来回跳转了! 
sticky是什么?  
引用
sticky模式 
利用负载均衡器的sticky模式的方式把所有同一session的请求都发送到相同的Tomcat节点。这样不同用户的请求就被平均分配到集群中各个tomcat节点上,实现负载均衡的能力。这样做的缺点是没有灾难恢复的能力。一旦一个节点发生故障,这个节点上所有的session信息全部丢失; 
同一用户同一session只和一个webServer交互,一旦这个webserver发生故障,本次session将丢失,用户不能继续使用 !



提交一个Session设定看看 http://localhost/zlex : 
nginx+tomcat 使用教程_第16张图片  
观察后台日志: 
 
再看看返回页面,这相当于一次页面刷新,如果正常粘性会话,我们将获得当前SessionID对应的一切信息: 
nginx+tomcat 使用教程_第17张图片  
这说明粘性会话生效了! 
我们得到了形如
引用
50DAF14C6CDF8ACFBDC1095A5EE8E2CF.tomcat1
的SessionID。这样,我们就能知道当前访问的是哪台服务器了!  
如果,换一个浏览器打开该页面http://localhost/zlex,将会获得一个新的SessionID,并且,根据Apache中配置的负载均衡节点列表依次访问下一个节点! 
如果这时候负载均衡节点列表中某一节点发生异常,那么Apache将按照惯例,跳转该节点,并在该节点恢复正常后约1分钟内重新将其纳入可用节点!
 
修改刚才的jsp页面,看看Http头中都有些什么: 
Jsp代码   收藏代码
  1. <b>Cookie信息:</b>  
  2. <br />  
  3. ${header["cookie"]}  
  4. <br />  
  5. <b>Host信息:</b>  
  6. <br />  
  7. ${header["host"]}  
  8. <br />  

 
sticky模式的根本在于浏览器支持cookie,如果浏览器不支持cookie,则需要修改server.xml文件中的<Context />节点,将cookie置为false,关闭cookie功能,让jsessionid显式传递!  

6.Tomcat集群,Session复制  
经过两天反复研究,两只互不相认的Tomcat终于在网络上“资源共享”了——Session复制成功!  
关于 Tomcat集群以及Session复制 ,网上已经有很多很多,但是否真的能用?!为了确认这一问题,周末还跑到书店翻了翻《Apache Tomcat 高级编程》,参考 Clustering/Session Replication HOW-TO (有点小错误),经过两天苦战,克服种种小问题,终于拿下! 

整理概念: 
引用

群集,是包含多个服务器实例的指定集合,这些服务器实例共享相同的应用程序、资源以及配置信息。您可以将不同计算机上的服务器实例分组到一个逻辑群集中并将其作为一个单元来管理。您可以使用 DAS 轻松控制多机群集的生命周期。 

群集可以实现水平可伸缩性、负载平衡和故障转移保护。根据定义,群集中的所有实例都具有相同的资源和应用程序配置。当群集中的服务器实例或计算机出现故障时,负载平衡器检测到该故障,会将通信从出现故障的实例重定向至群集中的其他实例,并恢复用户会话状态。由于群集中所有实例上的应用程序和资源都相同,因此一个实例可以故障转移至群集中的任何其他实例。


引用
Session复制,主要是指集群环境下,多台应用服务器之间同步Session,确保Session保持一致,且Session中的内容保持一致,对外透明——看起来就像是一台应用服务器! 
如果其中一台服务器发生故障,根据负载均衡的原理,Apache会遍历寻找可用节点,分发请求。与此同时,当前用户Session不能发生数据丢失,其余各节点服务器应保证用户Session数据同步。

Session复制核心内容主要是: 
  1. Session内容序列化(serialize),会消耗系统性能。
  2. Session内容通过广播同步给成员,会造成网络流量瓶颈,即便是内网瓶颈。


因此,Session复制的这两个潜在问题,致使复杂均衡节点最多不会超过4个。因为,当节点数大于4时,整个集群的吞吐量将不再上升!  

为了搭建Tomcat集群,我将两个Tomcat分别部署到两台虚拟机上,确保网段一致。(这一步很关键,我最初将 Tomcat1(192.168.49.132) 部署在虚拟机上,将 Tomcat2(192.168.49.128) 部署在本机上,结果,网络总有问题,耽误了很多时间。  ) 

由于变换了IP,我需要修改Apache的 /etc/apache2/mods-available/proxy.conf 文件: 
Shell代码   收藏代码
  1. sudo vi /etc/apache2/mods-available/proxy.conf   


修改负载均衡节点如下: 
Conf代码   收藏代码
  1. <Proxy balancer://zlex>  
  2.         BalancerMember ajp://192.168.49.128:8009/zlex        route=tomcat1  
  3.         BalancerMember ajp://192.168.49.132:8009/zlex        route=tomcat2  
  4. </Proxy>  


对于windows系统,不需要考虑网络问题,广播地址(这里用到 224.0.0.0 240.0.0.0 )默认开放,对于linux则需要通过命令开放地址。 
Ubuntu上开放广播地址(eth0网卡): 
Shell代码   收藏代码
  1. sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0  

然后通过 -v 参数查看当前开放的广播地址: 
Shell代码   收藏代码
  1. route -v  

nginx+tomcat 使用教程_第18张图片  

注意,重启后,该路由设置将丢失!  

在Ubuntu下,可以考虑修改 /etc/networks 文件! 


如果有必要,Windows上开放广播地址(192.168.49.128本机地址): 
Cmd代码   收藏代码
  1. route add 224.0.0.0 mask 240.0.0.0 192.168.49.128  

然后通过 print 参数查看当前开放的广播地址: 
Shell代码   收藏代码
  1. route print  

nginx+tomcat 使用教程_第19张图片  

然后,修改tomcat的server.xml文件: 
Shell代码   收藏代码
  1. sudo vi /etc/tomcat6/server.xml   

<Engine />  节点中加入如下内容: 
Xml代码   收藏代码
  1.   <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"  
  2.          channelSendOptions="8">  
  3.   
  4.   <Manager className="org.apache.catalina.ha.session.DeltaManager"  
  5.            expireSessionsOnShutdown="false"  
  6.            notifyListenersOnReplication="true"/>  
  7.   
  8.   <Channel className="org.apache.catalina.tribes.group.GroupChannel">  
  9.     <Membership className="org.apache.catalina.tribes.membership.McastService"  
  10.                 address="224.0.0.0"  
  11.                 port="45564"  
  12.                 frequency="500"  
  13.                 dropTime="3000"/>  
  14.     <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"  
  15.               address="192.168.49.1"  
  16.               port="4000"  
  17.               autoBind="100"  
  18.               selectorTimeout="5000"  
  19.               maxThreads="6"/>  
  20.   
  21.     <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">  
  22.       <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>  
  23.     </Sender>  
  24.     <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>  
  25.     <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>  
  26.   </Channel>  
  27.   
  28.   <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"  
  29.          filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>  
  30.   <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>  
  31.   
  32.   <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>  
  33.   <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>  
  34. </Cluster>   


这里需要注意 <Membership /> Receiver <Membership /> 节点的 address 属性是广播地址; Receiver 节点的 address 属性是本地绑定地址。当然,默认为 auto 。由于我在启动Tomcat时,Tomcat频频将地址指向 127.0.0.1 ,无奈只好使用固定IP。 
此外,为了降低Session复制的成本,Tomcat通过 <Valve /> 节点,以过滤器的方式控制哪些请求可以忽略Session复制: 
Xml代码   收藏代码
  1. <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"  
  2.        filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>  


同时,在<Host>节点中加入如下内容: 
Xml代码   收藏代码
  1. <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"  
  2.           tempDir="/tmp/war-temp/"  
  3.           deployDir="/tmp/war-deploy/"  
  4.           watchDir="/tmp/war-listen/"  
  5.           watchEnabled="false"/>  


在Tomcat的官方文档(Tomcat 6)中,对于<Deployer  />节点的部署位置是错误的,通过观察Tomcat启动日志,确认该节点应当不属于<Host />节点中!  

注意:Tomcat 6与Tomcat5在上述节点中使用的类包(包中名称由cluster变化为ha)有所不同,且结构有所调整。  

先别急着重启,我们需要修改应用中的 web.xml 文件,将 <distributable /> 节点部署到 <web-app /> 节点中,开启分布式服务: 
nginx+tomcat 使用教程_第20张图片

注意:如果没有设置该节点,SessionID将不能保持同步,不同的服务器将各自建立独立的SessionID!  

监控Tomcat日志: 
Shell代码   收藏代码
  1. tail -f /var/lib/tomcat6/logs/catalina.out   

然后重启Tomcat1: 
Shell代码   收藏代码
  1. sudo /etc/init.d/tomcat6 restart  

观察日志: 
nginx+tomcat 使用教程_第21张图片
注意两处红框: 
第一处,Cluster启动,并绑定192.168.49.132:4000上,进行TCP通讯,并等待其它成员(Member)。 
第二处,在管理器中注册/zlex,绑定JvmRouteBinderValve
 

至此,说明集群设置已经生效,但不能说明集群配置成功! 

接着我们启动Tomcat2,观察其日志: 
nginx+tomcat 使用教程_第22张图片  
Cluster启动,并绑定192.168.49.128:4000上并发现成员[b]192.168.49.132 ![/b] 
再看Tomcat1的日志: 

Tomcat1发现其成员Tomcat2!这说明TCP通讯已建立,Tomcat成员可以进行Session同步!  
同时,Tomcat成员直接会每隔一个时间段相互侦测/验证其他成员是否正常: 
nginx+tomcat 使用教程_第23张图片  

现在,开始访问 http://localhost/zlex ,并不断刷新当前页面: 
nginx+tomcat 使用教程_第24张图片  
除了两处标识主机来源的tomcatX不同外,session是完全一致的! 
提交一次修改,并不断刷新当前页: 
nginx+tomcat 使用教程_第25张图片  
如果仔细观察,当前SessionID在不断交替变化,这说明负载均衡在起作用! 
我们再来看看2个Tomcat后台日志都做了什么! 
Tomcat1:  

Tomcat2: nginx+tomcat 使用教程_第26张图片  
两只猫都打印了相同的内容( a=1 )不同的细节在于,sessionID带有服务器标识! 

如果我们强行关闭Tomcat2: 
首先,Tomcat1会很快侦测到Tomcat2离线,因为这是TCP通讯,成员之间很容易检测到其他成员是否离线!Tomcat1后台日志如下: 

其次,Apache会侦测到Tomcat2发生异常,将其余请求转交给其他节点,即交由Tomcat1处理! 
继续刷新 http://localhost/zlex 当前页面,耐心等待几秒。你会发现,即便再次刷新页面,sessionID仍旧绑定在标识tomcat1服务器上。 
然后,我们恢复Tomcat2服务,Tomcat1会马上侦测到Tomcat2已经恢复正常: 

最后,我们再次刷新当前页,Apache已经将请求分发给Tomcat2了,从后台日志可以看到session信息会很快被同步了! 

如果带有tomcatX标识的sessionID有很多不便之处,可以关闭粘性会话。简单的讲,就是取消Tomcat中[b]server.xml <Engine/ > 节点的 jvmRoute 属性![/b]然后,重启tomcat、apache! 
nginx+tomcat 使用教程_第27张图片  
页面提交一个 b=3 ! 

左边为Tomcat1,右边为Tomcat2!SessionID一致! 

除了上述几种方案外,还有 Terracotta 模式。一种第三方集群组件,2009年收购了缓存组件EhCache,可以结合Tomcat、JBoss等多种服务器,提供多种负载均衡、集群等功能实现,且当负载均衡节点超过8个时,仍然能够保持集群吞吐量的线性增长。 
Eclipse插件地址: 
http://download.terracotta.org/eclipse/update  
下载地址: 
http://www.terracotta.org/dl/oss-download-catalog  

至此,Apache + Tomcat成功完成,征服Apache系列暂告一段落! 

作为开博以来的第100帖,算是很成功了


Apache + SVN + LDAP


对于开发服务器,如果只有subersion,虽然也可以通过 svn://host/svn 方式访问svn;但是,这样的访问方式,终归不方便,尤其是外网访问,于是,我们整合Apache中的WebDAV,使之可以通过 http://host/svn 方式访问svn;可是,如果直接通过外网访问svn,于是,我们结合SSL,以 https://host/svn 方式访问svn;当然,如果有很多人要使用svn,svn帐号管理就成为一个麻烦事!如何解决?帐号分配,密码更迭等等,这些事情都很麻烦。LDAP,正好解决这个问题! 

在搭建Apache+SVN+LDAP服务前,先使用ldapSearch命令检测下LDAP服务连接: 
Shell代码   收藏代码
  1. ldapsearch -h ldap.zlex.org -p 389 -x -b "cn=users,dc=zlex,dc=org" -D "[email protected]" -w 11111111  

简要描述: 
-h  主机地址 
-p  端口号(默认389) 
-x  简单授权 
-b  BaseDN 
-D  BindDN 这里就是邦定的用户帐号了 
-w  显式输入密码 
-W  隐式输入密码 

这是基于绑定用户帐号方式访问LDAP服务,如果可以匿名访问LDAP,这事情就更简单了,也就更不安全了!  

如果连接成功,可以获得一堆该账户下的所有LDAP信息。如果不成功,只能向LDAP服务提供者咨询了!  
PS:这一个月,我被LDAP服务搞死了,公司负责LDAP服务维护的同事也不懂如何配置相关应用中的LDAP连接。我只能误打误撞,挨个参数测验了。 

如果你的Ubuntu正好不能使用这个命令,那么就执行下面的命令安装好了! 

Shell代码   收藏代码
  1. sudo apt-get install ldap-utils  


在Ubuntu下,想要搭建Apache+SVN+LDAP,除了完成Apache+SVN服务搭建,只需要通过追加ldap模块的方式,就可以很快完成这套服务的搭建工作。 

如果不知道哪些apache模块跟ldap有关,我们可以使用这个命令找到相关模块: 
Shell代码   收藏代码
  1. sudo apt-cache search apache2 ldap  


如下提示: 
引用
libapache2-mod-ldap-userdir - Apache module that provides UserDir lookups via LDAP 
libapache2-mod-vhost-ldap - Apache 2 module for Virtual Hosting from LDAP


这下,事情就简单了,直接安装相关模块: 
Shell代码   收藏代码
  1. sudo apt-get install libapache2-mod-ldap-userdir libapache2-mod-vhost-ldap  


接下来,修改我们上次的SVN配置文件: 

Shell代码   收藏代码
  1. sudo vi /etc/apache2/mods-available/dav_svn.conf  


找到 AuthType Basic ,追加 AuthBasicProvider ldap ,同时注释掉 AuthUserFile 。 
以下是配置片段: 
Conf代码   收藏代码
  1.     AuthType Basic  
  2.     AuthName "Subversion Repository"  
  3.   
  4. #使用LDAP服务校验用户身份,废弃密码文本  
  5. #    AuthUserFile /etc/apache2/dav_svn.passwd  
  6.   
  7.     AuthzSVNAccessFile /etc/apache2/dav_svn.authz  
  8.   
  9. #以下为LDAP服务配置  
  10.     AuthBasicProvider ldap  
  11.   
  12.     AuthzLDAPAuthoritative off  
  13.   
  14. #LDAP连接  
  15.         AuthLDAPURL "ldap://ldap.zlex.org/dc=zlex,dc=org?sAMAccountName?sub?(objectClass=user)"   
  16.   
  17. #邦定用户  
  18.         AuthLDAPBindDN "[email protected]"  
  19.   
  20. #邦定密码        
  21.         AuthLDAPBindPassword "11111111"  
  22.   
  23.         Require valid-user  


悲哀地是,LDAP服务现在还不知道如何构建。对于LDAP服务,我也知之甚少,更无法深入了解每一个参数的含义。构建LDAP服务,将是我下一个技术攻坚标杆

Apache + SVN

选用Ubuntu Server 10.04,Apache 2.2.14,Subversion 1.6.6。 

步骤: 
  1. 安装SVN相关模块
  2. 配置SVN版本库
  3. 配置APACHE
  4. 简单测试

1.安装SVN相关模块  
这里主要用到的是Subversion 以及Apache与SVN相关的模块(DAV_SVN)! 
执行命令,安装: 
Shell代码   收藏代码
  1. sudo apt-get install subversion libapache2-svn  

注意观察安装后的结果: 
引用
Considering dependency dav for dav_svn: 
Enabling module dav. 
Enabling module dav_svn. 
Run '/etc/init.d/apache2 restart' to activate new configuration!

如果看到这样的提示,那说明DAV_SVN模块已经成功安装,可以重启Apache看看是否正常启动: 
Shell代码   收藏代码
  1. sudo /etc/init.d/apache2 restart  

或者,使用
Shell代码   收藏代码
  1. sudo service apache2 restart  

以我本机为例,正常启动了: 
引用
zlex@localhost:~$ sudo /etc/init.d/apache2 restart 
* Restarting web server apache2                 
apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName 
... waiting .apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName

当然,稍后你可能需要修改 /etc/apache2/mods-available/dav_svn.conf 文件,配置SVN版本库等。 

2.配置SVN版本库  
完成上述操作,只是为Apache获知SVN给出了一种途径,最关键的还是要配置SVN相关部分! 
首先,我们要创建subversion组并把www-data作为subversion中的一员。因为,apache是通过www-data账户启动的,我们需要让它能够访问subversion组的文件! 
Shell代码   收藏代码
  1. sudo addgroup subversion  
  2. sudo usermod -G subversion -a www-data  

然后,我们要配置版本库: 
我们可以在 /var/lib 目录下构建一个svn目录,作为SVN版本库根目录: 
Shell代码   收藏代码
  1. cd /var/lib  
  2. sudo mkdir svn  

假设我们要创建版本库zlex: 
Shell代码   收藏代码
  1. cd svn  
  2. sudo svnadmin create zlex  

更改版本库所属用户、组: 
Shell代码   收藏代码
  1. sudo chown -R root:subversion zlex  

赋予组成员对所有新加入文件仓库的文件拥有相应的权限: 
Shell代码   收藏代码
  1. sudo chmod -R g+rws zlex  


试试
Shell代码   收藏代码
  1. svn co file://localhost/var/lib/svn/zlex  
,这时候应该可以访问了! 

3.配置Apache  
接下来,我们需要修改 /etc/apache2/mods-available/dav_svn.conf 文件,配置SVN版本库: 
Shell代码   收藏代码
  1. sudo vi /etc/apache2/mods-available/dav_svn.conf  

nginx+tomcat 使用教程_第28张图片  
打开红框中的注释, 
Conf代码   收藏代码
  1. # dav_svn.conf - Example Subversion/Apache configuration  
  2. #  
  3. # For details and further options see the Apache user manual and  
  4. # the Subversion book.  
  5. #  
  6. # NOTE: for a setup with multiple vhosts, you will want to do this  
  7. # configuration in /etc/apache2/sites-available/*, not here.  
  8.   
  9. # <Location URL> ... </Location>  
  10. # URL controls how the repository appears to the outside world.  
  11. # In this example clients access the repository as http://hostname/svn/  
  12. # Note, a literal /svn should NOT exist in your document root.  
  13. <Location /svn>  
  14.   
  15.   # Uncomment this to enable the repository  
  16.   DAV svn  
  17.   
  18.   # Set this to the path to your repository  
  19.   #SVNPath /var/lib/svn  
  20.   # Alternatively, use SVNParentPath if you have multiple repositories under  
  21.   # under a single directory (/var/lib/svn/repo1, /var/lib/svn/repo2, ...).  
  22.   # You need either SVNPath and SVNParentPath, but not both.  
  23.   SVNParentPath /var/lib/svn  
  24.   
  25.   # Access control is done at 3 levels: (1) Apache authentication, via  
  26.   # any of several methods.  A "Basic Auth" section is commented out  
  27.   # below.  (2) Apache <Limit> and <LimitExcept>, also commented out  
  28.   # below.  (3) mod_authz_svn is a svn-specific authorization module  
  29.   # which offers fine-grained read/write access control for paths  
  30.   # within a repository.  (The first two layers are coarse-grained; you  
  31.   # can only enable/disable access to an entire repository.)  Note that  
  32.   # mod_authz_svn is noticeably slower than the other two layers, so if  
  33.   # you don't need the fine-grained control, don't configure it.  
  34.   
  35.   # Basic Authentication is repository-wide.  It is not secure unless  
  36.   # you are using https.  See the 'htpasswd' command to create and  
  37.   # manage the password file - and the documentation for the  
  38.   # 'auth_basic' and 'authn_file' modules, which you will need for this  
  39.   # (enable them with 'a2enmod').  
  40.   AuthType Basic  
  41.   AuthName "Subversion Repository"  
  42.   AuthUserFile /etc/apache2/dav_svn.passwd  
  43.   
  44.   # To enable authorization via mod_authz_svn  
  45.   AuthzSVNAccessFile /etc/apache2/dav_svn.authz  
  46.   
  47.   # The following three lines allow anonymous read, but make  
  48.   # committers authenticate themselves.  It requires the 'authz_user'  
  49.   # module (enable it with 'a2enmod').  
  50.   #<LimitExcept GET PROPFIND OPTIONS REPORT>  
  51.     Require valid-user  
  52.   #</LimitExcept>   
  53.   
  54. </Location>  

分述: 
<Location /svn> </Location> 成对儿出现! 
DAV svn 开启DAV模块支持! 
SVNPath /var/lib/svn SVNParentPath /var/lib/svn 选其一, 不可同时出现! 建议使用 SVNParentPath ,可以在SVN根目录下创建多个SVN版本库! 
引用
  AuthType Basic 
  AuthName "Subversion Repository" 
  AuthUserFile /etc/apache2/dav_svn.passwd
定义了授权类型、并指定了密码文件( /etc/apache2/dav_svn.passwd )。 
AuthzSVNAccessFile /etc/apache2/dav_svn.authz 授权配置文件,规定了路径访问权限! 
引用
#<LimitExcept GET PROPFIND OPTIONS REPORT> 
   Require valid-user 
#</LimitExcept>
建议只使用Require valid-user,打开<LimitExcept />注释,将允许匿名访问!  

现在通过命令设置SVN账户: 
Shell代码   收藏代码
  1. sudo htpasswd -c /etc/apache2/dav_svn.passwd <username>  

这里用到参数-c,是因为/etc/apache2/dav_svn.passwd文件不存在,如果文件存在,则无需该参数!否则,将覆盖掉原有密码文件!  
形如: 
引用
sudo htpasswd -c /etc/apache2/dav_svn.passwd snowolf 
New password: 
Re-type new password: 
Updating password for user snowolf

可以追加多个账户! 
引用
sudo htpasswd /etc/apache2/dav_svn.passwd zlex 
New password: 
Re-type new password: 
Updating password for user zlex


现在,需要设置路径访问权限文件 AuthzSVNAccessFile /etc/apache2/dav_svn.authz 。 
我们先做一个默认的配置,当前这个文件还不存在: 
Shell代码   收藏代码
  1. sudo vi /etc/apache2/dav_svn.authz  

然后追加: 
引用

[zlex:/] 
* = r 

这样,所有授权用户就都能够看到zlex项目了!  
然后访问 http://localhost/svn/zlex : 
nginx+tomcat 使用教程_第29张图片  
试试检出: 
Shell代码   收藏代码
  1. svn co http://localhost/svn/zlex --username snowolf  


我们通过组方式管理项目,修改 /etc/apache2/dav_svn.authz  文件: 
Shell代码   收藏代码
  1. sudo vi /etc/apache2/dav_svn.authz    

我们定义一个超级用户组admin,组中成员为snowolf;开发组developer,组中成员为snowolf,zlex,多个用户用逗号分隔。 
引用

[groups] 
admin = snowolf 
developer = snowolf, zlex 


让admin和developer组成员有创建项目版本库的权限,其余用户只有查看权限: 
引用

[zlex:/] 
*=r 
@admin = rw 
@developer = rw 



给出一个完整配置: 
引用

[groups] 
admin = snowolf 
developer = zlex 

[zlex:/] 
@admin = rw 
@developer = rw 
* = 

有关Subversion详细配置,参照 Subversion官方中文文档 ! 
修改这个配置文件时,不需要重启apache! 

4.简单测试  
我们之前构建了一个项目仓库——zlex,现在项目有了,我们需要构建相应的版本库管理,及trunk、tags以及branches! 
用命令创建: 
Shell代码   收藏代码
  1. svn mkdir "http://localhost/svn/zlex/branches" "http://localhost/svn/zlex/tags" "http://localhost/svn/zlex/trunk" -m "create a new project zlex" --username "snowolf"      


nginx+tomcat 使用教程_第30张图片


这时,我们用zlex账号提交一个docs目录: 
Shell代码   收藏代码
  1. svn mkdir "http://192.168.49.132/svn/zlex/docs" -m "文档目录" --username zlex  

系统会提示输入密码: 
nginx+tomcat 使用教程_第31张图片
192.168.49.132 是我本机的IP地址! 
我们可以使用命令将项目签出: 
Shell代码   收藏代码
  1. svn checkout "http://localhost/svn/zlex/trunk@HEAD" -r HEAD --depth infinity zlex-svn --username zlex  


我们随便修改一个文件: 
 
提交修改: 
 


如果你参照 征服 Apache + SSL 完成了HTTPS平台搭建,这时候,也可以使用HTTPS方式访问了: 

nginx+tomcat 使用教程_第32张图片  

Apache + SSL


动手,在Ubuntu上做Apache+SSL! 

步骤 
  1. 安装Apache
  2. 安装SSL模块
  3. 生成证书
  4. 配置生效

1.安装Apache  
这里系统选用Ubuntu Server 10.04版本,Apache选用2.2.14,OpenSSL选用0.98k。 
安装Apache2 
Shell代码   收藏代码
  1. sudo apt-get install apache2   

这个就不用解释了,不明白可以查Ubuntu/Debian技术手册。 

2.安装SSL模块  
Ubuntu版Apache提供了两个命令用于配置SSL站点、模块: 
Shell代码   收藏代码
  1. sudo a2ensite default-ssl   

这个命令建立了基本ssl站点配置 
也就是构建了文件 /etc/apache2/sites-available/default-ssl 。 
我们稍后需要修改这个文件,添加自己的证书! 
Shell代码   收藏代码
  1. sudo a2enmod ssl  

这个命令用于配置SSL模块,完成操作后,可以在 /etc/ssl 中,发现一些目录、文件。并且 /etc/ssl/private目录仅限root帐户才能查看 ! 
 

完成上述操作后一定要重新启动Apache!  
Shell代码   收藏代码
  1. sudo /etc/init.d/apache2 restart  


详细内容参考 /usr/share/doc/apache2.2-common/README.Debian.gz  

其实,这个时候你可以访问 https://localhost ,查看配置是否生效。如果你打开浏览器会提示你证书(localhost)未验证! 

3.生成证书  
这里我们需要通过OpenSSL生成证书,如果还没有OpenSSL,那就下载安装: 
Shell代码   收藏代码
  1. sudo apt-get install openssl  

然后,我们切换到 /etc/ssl 路径下,构建自签名证书: 
Shell代码   收藏代码
  1. sudo openssl req -x509 -newkey rsa:1024 -keyout private/zlex.key -out certs/zlex.pem -nodes -days 3650 -subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=www.zlex.org"  

req 请求证书 
-x509 签发X.509格式证书 
-newkey rsa:1024 非对称加密算法为RSA,密钥长度为1024bits 
-keyout private/zlex.key 私钥输出到private中,名为zlex.key 
-out certs/zlex.pem 密钥库输出至certs中,名为zlex.pem 
-nodes 显示详情 
-days 3650 有效期时间,为3650天 
-subj "/C=CN/ST=BJ/L=BJ/O=zlex/OU=zlex/CN=www.zlex.org" 证书主题,注意CN指向主机域名,这里为‘www.zlex.org’ 

再看看certs和private里原始的证书、密钥库,以及我们刚刚生成的证书密钥库。 
 

 

4.配置生效  
由于这里使用www.zlex.org,这就需要通过修改 /etc/hosts ,让系统绑定www.zlex.org到本机: 
Shell代码   收藏代码
  1. sudo /etc/hosts  

追加如下内容: 
引用
127.0.0.1       www.zlex.org

然后,我们需要修改证书路径,指向刚才生成证书: 
Shell代码   收藏代码
  1. sudo vi /etc/apache2/sites-available/default-ssl  

找到 SSLCertificateFile SSLCertificateKeyFile ,修改指向刚才生成的证书和密钥库: 
nginx+tomcat 使用教程_第33张图片  
然后,重启apache: 
Shell代码   收藏代码
  1. sudo /etc/init.d/apache2 restart  

访问 https://www.zlex.org , 
nginx+tomcat 使用教程_第34张图片
点击添加例外: 
nginx+tomcat 使用教程_第35张图片  
然后看到如下内容: 
nginx+tomcat 使用教程_第36张图片  
现在再看看浏览器标识: 
nginx+tomcat 使用教程_第37张图片  
点开看看: 
nginx+tomcat 使用教程_第38张图片  
我们可以点击查看证书,看看庐山真面目: 
nginx+tomcat 使用教程_第39张图片  
这里用的是自签名证书,所以无法验证!  

好了,这样就可以完成简单Apache+SSL平台搭建了,当然,这仅仅是基于单向认证服务!  
更多细节如下图红框标注,建立证书链,双向认证服务,验证深度等等: 
nginx+tomcat 使用教程_第40张图片  
命令 
Shell代码   收藏代码
  1. sudo vi /etc/apache2/sites-available/default-ssl 


Nginx调用 HTTP模块


nginx+tomcat 使用教程_第41张图片


worker进程会在一个for循环语句里反复调用事件模块检测网络事件。当事件模块检测到某个客户端发起的TCP请求时(接收到SYN包),将会为它建立TCP连接,成功建立连接后根据nginx.conf文件中的配置会交由HTTP框架处理。HTTP框架会试图接受完整的HTTP头部,并在接收到完整的HTTP头部后将请求分发到具体的HTTP模块中处理。这种分发策略是多样化的,其中最常见的是根据请求的URI和nginx.conf里location配置项的匹配度来决定如何分发。

HTTP模块在处理请求结束时,大多会向客户端发生响应,此时会自动地依次调用所有的HTTP过滤模块,每个过滤模块可以根据配置文件决定自己的行为。

HTTP处理模块在返回时会将控制权交还给HTTP框架,如果在返回前设置了subrequest,那么HTTP框架还会继续异步地调用适合的HTTP模块处理子请求。


你可能感兴趣的:(nginx+tomcat 使用教程)