• Tomcat(五):nginx/httpd + tomcat及负载均衡tomcat


    Tomcat系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html


    tomcat一般只提供动态资源处理功能,而静态资源的请求则交给独立的apache/httpd或nginx来处理。但tomcat与外界通信的唯一组件是连接器Connector,因此动态请求要转发给tomcat时,需要和Connector通信。Connector与外界通信的协议有两种:http/ajp。

    1. Tomcat连接器协议类型

    tomcat的Connector组件支持两种协议类型的连接:http、ajp。

    其中http又分为http/1.1和http/2,但因为Servlet的阻塞特性,使得每个http/2的请求都需要单独的容器线程负责处理,因此目前使用http/2的性能还很差。而AJP又有ajp12、ajp13和ajp14,其中ajp12的功能太简陋,ajp14又处于试验期,因此目前都适用ajp13,或者称为ajp 1.3。

    基于IO模型,Connector又分为NIO/NIO2/APR三种类型。

    设置连接器协议的方式如下:

    <!--  定义使用Http协议的连接器,其中"HTTP/1.1"表示自动选择NIO/NIO2/APR  -->
    <connector port="8080" protocol="HTTP/1.1">
    <connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol">
    <connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol">
    <connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol">
    
    <!--  定义使用AJP协议的连接器,其中"AJP/1.3"表示自动选择NIO/NIO2/APR  -->
    <Connector port="8009" protocol="AJP/1.3"/>
    <Connector port="8009" protocol="org.apache.coyote.ajp.AjpNioProtocol"/>
    <Connector port="8009" protocol="org.apache.coyote.ajp.AjpNio2Protocol"/>
    <Connector port="8009" protocol="org.apache.coyote.ajp.AjpAprProtocol"/>
    

    2. Tomcat与httpd/nginx结合的方式

    Tomcat连接器与外界通信的两种协议中,ajp只能和apache基金会开发的部分项目通信,例如httpd。要与非apache基金会开发的程序通信,必须使用http协议。因此,tomcat+nginx时,tomcat连接器的协议类型只能是http。

    对于tomcat+httpd,tomcat端的连接器既可以使用http,也可以使用ajp协议。但在httpd端,也支持多种通信模块,最常用的是mod_proxy和mod_jk。

    tomcat+httpd时,几个需要注意的点:

    • (1).mod_jk一般只和tomcat的ajp协议配合来通信。而mod_proxy则不要求tomcat端的协议类型是http还是ajp。
    • (2).配置时,使用mod_jk比使用mod_proxy要复杂的多。
    • (3).使用mod_jk可以配置成只处理动态请求,而不处理静态请求。但mod_proxy则只能将所有请求都转发给tomcat。因此,在性能上mod_jk比mod_proxy好一些。不过由于前端可以使用反向代理工具,因此这种情况下的mod_jk和mod_proxy,性能无差别。

    因此,如果满足业务需求,建议使用mod_proxy,它配置起来比mod_jk要简便的多,且可定制的功能更多。

    稍作总结,apache和tomcat通信的实现方式有3种:

    apache:mod_jk  <--> tomcat:ajp(虽然可以和http配合,但不建议)
    apache:mod_proxy <--> tomcat:ajp或http

    下图是httpd/nginx+tomcat在动静分离时通常使用的架构模型。

    左边的模型中,nginx/httpd需要处理静态请求,并将动态请求转发给tomcat,同时实现负载均衡。右边的模型中,添加一层反向代理层,无论是静态请求还是动态请求都负载到各自的服务器组。后文的实验都采用左边的架构模型来完成。

    3. Tomcat + nginx

    测试环境如下:

    [root@xuexi ~]# echo "[nginx]
    name=nginx
    baseurl=http://nginx.org/packages/centos/6/x86_64/
    enable=1
    gpgcheck=0" >/etc/yum.repos.d/nginx.repo
    
    [root@xuexi ~]# yum install -y nginx
    [root@xuexi ~]# vim /etc/nginx/conf.d/tomcat.conf
    upstream tomcat_servers {
        server 192.168.100.22:8080 weight=1 max_fails=2 fail_timeout=2;
        server 192.168.100.23:8080 weight=2 max_fails=2 fail_timeout=2;
    }
    
    server {
        listen       80;
        server_name  192.168.100.17;
        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
        location ~* .(jsp|jspx|do) {
            proxy_pass http://tomcat_servers;
        }
    }
    

    启动nginx和tomcatA、tomcatB。然后在浏览器中分别访问静态资源和动态资源。

    http://192.168.100.17
    http://192.168.100.17/index.jsp
    

    其中第二个访问的是动态资源,页面结果如下:

    发现这个欢迎页面缺失了很多内容。其实这些缺失的内容都是定义在index.jsp中的静态资源,例如某些图片。之所以会缺失,是因为客户端发送请求给nginx后,nginx将请求转发给tomcat,tomcat翻译index.jsp为java源文件,然后执行该servlet。执行servlet时,将所需响应数据都回应给nginx,包括index.jsp中定义的图片链接标签。当客户端收到这一次的响应数据后,还会继续去请求图片,但是这是静态请求,nginx会自己处理而不会转发给tomcat,但nginx本身不知道图片在何处(tomcat才知道)。因此对于图片部分,nginx将返回404错误,使得客户端显示的页面缺失了一部分。

    在生产环境下,不需要担心这样的二次静态请求缺失问题,因为会将图片等静态数据存放在某个位置,并配置好nginx如何找到这些静态数据。例如,上面的页面中缺失了tomcat.{png,css,gif}等静态文件,将它们从tomcat服务器的$CATALINA_HOME/webapps/ROOT/目录下拷贝到nginx服务器的/usr/share/nginx/html目录(上面配置文件的root指令指定的位置)下,并赋予读取权限。再去访问,就能正确显示图片。

    4. Tomcat + httpd(mod_jk)

    测试环境如下:

    使用mod_jk模块和tomcat连接时,tomcat的连接器一般都使用ajp协议类型。

    mod_jk不是apache httpd的原生模块,而是类似于第三方模块,因此需要额外编译mod_jk模块到httpd中,就像将php模块添加到httpd中一样。

    4.1 编译mod_jk模块

    当前最新稳定版的mod_jk是1.2.42版本。

    mod_jk下载地址:http://tomcat.apache.org/download-connectors.cgi
    mod_jk官方手册:http://tomcat.apache.org/connectors-doc/

    httpd要扩展模块需要借助apxs,它是httpd的开发包httpd-devel中工具,所以先要安装httpd-devel。如果是编译安装的httpd,则devel包已经装好,如果是yum安装,则需要额外安装httpd-devel包。

    此处为了方便,httpd使用yum安装。所以编译mod_jk的方式如下:

    yum -y install httpd httpd-devel
    tar xf tomcat-connectors-1.2.42-src.tar.gz
    cd tomcat-connectors-1.2.42-src/native/
    ./configure --with-apxs=/usr/bin/apxs --prefix=/usr/local/tomcat/mod_jk
    make && make install
    

    4.2 配置httpd与tomcat的ajp连接

    此处暂先配置httpd与其中一个tomcat(192.168.100.22)连接。后文在说明负载均衡时再引入另一个tomcat。

    先提供一个额外的httpd配置文件。

    [root@xuexi ~]# cat /etc/httpd/conf.d/mod_jk.conf
    LoadModule  jk_module  modules/mod_jk.so
    JkWorkersFile  /etc/httpd/conf.d/workers.properties
    JkLogFile  logs/mod_jk.log
    JkLogLevel  debug
    ######### "JkMount /* TomcatA" will send all request to TomcatA   ########
    JkMount   /*.jsp        TomcatA
    JkMount   /status/*     statA
    JkUnMount /images/*     TomcatA
    JkUnMount /css/*.*      TomcatA
    JkUnMount /css_js/*     TomcatA
    JkUnMount /*.html       TomcatA
    JkUnMount /*.js         TomcatA
    

    mod_jk的配置文件官方手册:http://tomcat.apache.org/connectors-doc/reference/apache.html。以下是几个常用的指令说明。

    • LoadModule指令用于装载mod_jk相关模块,除此之外还需要在httpd的配置文件中设置其它一些指令来配置其工作属性。如:
    • JkWorkersFile用于指定保存了worker相关工作属性定义(见下文)的配置文件。
    • JkLogFile用于指定mod_jk模块的日志文件。
    • JkLogLevel用于指定日志级别(info,error,debug),此外还可以使用JkRequestLogFormat自定义日志信息格式。
    • JkMount(格式:JkMount )则用于控制URL与Tomcat workers的对应关系。可以理解为转发请求的意思,例如"/status/*"表示url地址后加上/status/可转发至statA这个worker上。注意,JkMount匹配的URL是相对的。如果JkMount指令放在Location指令中,如<Location /app>,则JkMount将从/app的后面开始匹配。

    JkMount和JkUnMount是很重要的指令,mod_jk性能之所以比mod_proxy好,就是因为通过这两个指令可以实现动静分离,使得只将动态请求转发给tomcat。其中JkMount指定要转发给tomcat处理的请求,JkUnMount指定明确不转发给tomcat而是在本地处理的请求虽然不指定JkUnMount时,也表示不转发给tomcat,但如果有重叠时,则应该指定JkUnMount。例如下面的例子,除了/myapp/下的js文件,其他都转发给tomcat1处理。

    JkMount /myapp/* tomcat1
    JkUnMount /myapp/*.js tomcat1
    

    对于apache来说,每一个后端Tomcat实例中的engine都可以视作一个worker,而每一个worker的地址、Connector的端口等信息都需要在apache端指定以便可以识别并使用这些worker。配置这些信息的文件通常为"workers.properties",其具体路径是使用前面介绍过的JkWorkersFile指定的。在apache启动时,mod_jk会扫描此文件获取每一个worker配置信息。如这里使用/etc/httpd/conf.d/workers.properties

    workers.properties文件一般由两类指令组成:一是mod_jk可以连接的各worker名称列表,二是每一个worker的属性配置信息。详细的配置方法见官方手册:http://tomcat.apache.org/connectors-doc/reference/workers.html

    以下是和上述/etc/httpd/conf.d/mod_jk.conf中配置相对应的/etc/httpd/conf.d/workers.properties。

    [root@xuexi tomcat]# cat /etc/httpd/conf.d/workers.properties
    worker.list=TomcatA,statA
    worker.TomcatA.type=ajp13
    worker.TomcatA.host=192.168.100.22
    worker.TomcatA.port=8009
    worker.TomcatA.lbfactor=1
    worker.statA.type = status
    

    关于worker的配置,它们分别遵循如下使用语法。

    worker.list = <a comma separated list of worker_name>
    worker.<worker_name>.<property>=<property value>
    

    其中worker.list指令可以重复指定多次。worker_name是Tomcat中engine组件中jvmRoute属性的值(jvmRoute可以不指定,此时worker_name仅用于标识worker)。

    根据工作机制的不同,worker有多种不同的类型,每个worker都需要指定其类型,即设定woker..type项。常见的类型如下:其中ajp13是默认值。

    • ajp13:此类型是web server和tomcat首选的类型。此外,还有ajp12和ajp14,但它们一个废弃一个处于测试阶段。
    • lb:lb用于负载均衡场景中的worker;此worker并不真正负责处理用户请求,而是将用户请求调度给其它类型为ajp13的worker。
    • status:用户显示负载均衡中各worker工作状态的特殊worker,它不处理任何请求,也不关联到任何实际工作的tomcat实例。

    由于status是状态监控页面,所以应该保证其安全性,可以在httpd的配置文件中加入以下控制列表:

    # 注意,必须加上尾随斜线,因为在mod_jk.conf中已经明确了"/status/*"
    # For http 2.2
    <Location /status/>
        Order deny,allow
        Deny from all
        Allow from 192.168.100.0/24
    </Location>
    
    # For http 2.4
    <Location /status/>
        Requrie ip 192.168.100
    </Location>
    

    除了type属性外,worker其它常见的属性有:

    除了type属性外,worker其它常见的属性有:

    • host:worker所在的主机,更具体的是tomcat上connector组件设置的ajp监听地址;
    • port:worker的AJP1.3连接器监听的端口;
    • connection_pool_minsize:最少要保存在连接池中的连接的个数;默认为pool_size/2;
    • connection_pool_timeout:连接池中连接的超时时长;
    • mount:由当前worker提供的context路径,如果有多个则使用空格格开;可考虑在httpd端使用JkMount替代。
    • retries:错误发生时的重试次数;
    • socket_timeout:mod_jk等待worker响应的时长,默认为0,即无限等待;
    • socket_keepalive:是否启用keep alive的功能,1表示启用,0表示禁用;
    • lbfactor:worker的权重,可以在负载均衡的应用场景中为worker定义此属性;

    另外,在负载均衡模式中专用的属性还有:

    • balance_workers:用于负载均衡模式中的各worker的名称列表,需要注意的是,出现在此处的worker名称一定不能在任何worker.list属性列表中定义过,并且worker.list属性中定义的worker名字必须包含负载均衡worker。
    • method:可以设定为R、T或B;默认为R,即根据请求的个数进行调度(wrr);T表示根据已经发送给worker的实际流量大小进行调度;B表示根据实际负载情况进行调度(leastconn)。
    • sticky_session:将某请求调度至某worker后,此地址后续所有请求都将直接调度至此worker,实现将用户session与某worker绑定。默认为值为true,即启用此功能。如果后端的各worker之间支持session复制,则可设为false。

    至此,一个基于mod_jk模块与后端名为TomcatA的worker通信的配置已经完成,重启httpd服务即可生效。

    测试:在浏览器中输入

    http://192.168.100.17/
    http://192.168.100.17/index.jsp
    http://192.168.100.17/status/
    

    如果都能获取页面,则表示apache通过mod_jk和tomcat基于ajp协议类型的连接已经成功。

    4.3 通过mod_jk负载均衡tomcat

    使用mod_jk实现tomcat的负载均衡有一个好处,tomcat上可以禁用http协议(将监听此协议的Connector配置删除即可),防止外界直接通过http请求tomcat。

    配置apache,使其支持负载均衡,修改/etc/httpd/conf.d/mod_jk.conf为如下内容:

    LoadModule      jk_module  modules/mod_jk.so
    JkWorkersFile   /etc/httpd/conf.d/workers.properties
    JkLogFile       logs/mod_jk.log
    JkLogLevel      notice
    JkMount         /*.jsp     TomcatLB
    JkMount         /status/*  statA
    

    编辑/etc/httpd/conf.d/workers.properties,修改为如下内容:为测试负载效果,不启用stick_session。

    worker.list=TomcatLB,statA
    worker.statA.type=status
    worker.TomcatLB.type=lb
    worker.TomcatLB.sticky_session=false
    worker.TomcatLB.balance_workers=TomcatA,TomcatB
    worker.TomcatA.type=ajp13
    worker.TomcatA.host=192.168.100.22
    worker.TomcatA.port=8009
    worker.TomcatA.lbfactor=5
    worker.TomcatB.type=ajp13
    worker.TomcatB.host=192.168.100.23
    worker.TomcatB.port=8009
    worker.TomcatB.lbfactor=10
    

    在mod_jk负载均衡中,后端tomcat的engine组件需要添加jvmRoute参数,该参数会为当前server实例设置全局惟一标识符,因此每一个实例的jvmRoute的值均不能相同,且jvmRoute的值必须等于balance_workers的成员值。对于上面的配置,Engine应该如下设置:此处还修改了name,但这不是要求要修改的。

    <!--  在tomcatA上设置  -->
    <Engine name="Standalone" defaultHost="localhost" jvmRoute="TomcatA">
    <!--  在tomcatB上设置  -->
    <Engine name="Standalone" defaultHost="localhost" jvmRoute="TomcatB">
    

    为了演示效果,在TomcatA部署一个应用程序test。

    [root@xuexi tomcat]# mkdir -p /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}
    

    添加index.jsp,内容如下:

    [root@xuexi tomcat]# cat /usr/local/tomcat/webapps/test/index.jsp
    <%@ page language="java" %>
    <html>
      <head><title>TomcatA</title></head>
      <body>
        <h1><font color="red">TomcatA </font></h1>
        <table align="centre" border="1">
          <tr>
            <td>Session ID</td>
        <% session.setAttribute("abc","abc"); %>
            <td><%= session.getId() %></td>
          </tr>
          <tr>
            <td>Created on</td>
            <td><%= session.getCreationTime() %></td>
         </tr>
        </table>
      </body>
    </html>
    

    在TomcatB同样也部署一个应用程序test。如下:

    [root@xuexi tomcat]# mkdir -p /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}
    [root@xuexi tomcat]# cat /usr/local/tomcat/webapps/test/index.jsp
    <%@ page language="java" %>
    <html>
      <head><title>TomcatB</title></head>
      <body>
        <h1><font color="blue">TomcatB </font></h1>
        <table align="centre" border="1">
          <tr>
            <td>Session ID</td>
        <% session.setAttribute("abc","abc"); %>
            <td><%= session.getId() %></td>
          </tr>
          <tr>
            <td>Created on</td>
            <td><%= session.getCreationTime() %></td>
         </tr>
        </table>
      </body>
    </html>
    

    重启httpd、tomcatA、tomcatB对应的服务程序。在浏览器中输入192.168.100.17/test/index.jsp测试负载均衡是否生效。

    测试时,轮调两次tomcatB后轮调一次tomcatA。而且可以发现每次轮询时Session ID每次都是变化的,因为没有开启sticky_session,所以session没有进行绑定。

    要绑定会话,将worker.properties中的sticky_session设置为true即可。

    5. Tomcat+httpd(mod_proxy)

    测试环境如下:

    当httpd端采用mod_proxy和tomcat连接时,可以采用ajp或http协议进行连接。

    要使用mod_proxy与Tomcat连接,需要apache已经装载mod_proxy、mod_proxy_http、mod_proxy_ajp和proxy_balancer_module(实现Tomcat负载均衡时用到)等模块。使用rpm包安装的httpd一般默认已经启用它们,如果是编译httpd,则在编译选项中加上以下对应几项:

    --enable-proxy --enable-proxy-http --enable-proxy-ajp --enable-proxy-balancer
    

    如果是已经编译好的Httpd,则可以使用apxs工具,向httpd中添加这几个新模块。添加方法见:httpd添加新模块

    确保proxy相关的模块已经加载了。

    [root@xuexi ~]# httpd -M | grep proxy 
    Syntax OK
     proxy_module (shared)
     proxy_balancer_module (shared)
     proxy_ftp_module (shared)
     proxy_http_module (shared)
     proxy_ajp_module (shared)
     proxy_connect_module (shared)
    

    5.1 配置httpd代理到tomcat

    基于ajp连接协议和tomcat连接时,向httpd添加以下配置文件。如果要基于http协议连接tomcat,将上面配置文件的ajp协议改为http协议,并修改端口即可。

    [root@xuexi ~]# cat /etc/httpd/conf.d/ajp.conf
    <Location /status>
      SetHandler balancer-manager
      Proxypass !              # 表示此Location的URL不进行反向代理
      Require ip 192.168.100
    </Location>
    
    ProxyVia Off
    ProxyRequests Off
    ProxyPreserveHost Off
      ProxyPassMatch    "^/(.*.jsp)$"  ajp://192.168.100.22:8009/$1
      ProxyPassReverse  "^/(.*.jsp)$"  ajp://192.168.100.22:8009/$1
    
    <Proxy *>
      Require all granted
    </Proxy>
    

    重启httpd。注意,重启前将前面mod_jk实验的配置文件删除掉。

    关于如上apache配置的几个指令,解释如下。httpd反向代理的详细内容,可参见:详细分析apache httpd反向代理的用法

    • ProxyVia {On|Off|Full|Block}:用于控制在http首部是否使用"Via:",主要用于在多级代理中控制代理请求的流向。默认为Off,即不启用此功能;On表示每个请求和响应报文均添加"Via:";Full表示每个"Via:"行都会添加当前apache服务器的版本号信息;Block表示每个代理请求报文中的"Via:"都会被移除。
    • ProxyRequests {On|Off}:是否开启apache正向代理的功能;如果为apache设置了ProxyPass即反向代理,则必须将ProxyRequests设置为Off。
    • ProxyPreserveHost {On|Off}:如果启用此功能,代理会将用户请求报文中的"Host:"行发送给后端的服务器,而不再使用ProxyPass指定的服务器IP地址。如果后端一个IP上可能会有多个虚拟主机,则需要开启此项明确转发给哪台虚拟主机,否则就无需打开此功能。
    • ProxyPassReverse:在反向代理环境中必须使用此指令避免重定向报文绕过proxy服务器,属性设置为ProxyPass一样基本上就可以。
    • ProxyPass [path] !|[url [key=value key=value ...]]:将后端服务器某URL与当前服务器的某虚拟路径关联起来作为提供服务的路径,path为当前服务器上的某虚拟路径,url为后端服务器上某URL路径。使用此指令时必须将ProxyRequests的值设置为Off。需要注意的是,如果path以"/"结尾,则对应的url也必须以"/"结尾,反之亦然。
    • ProxyPassMatch [regex] !|url [key=value [key=value ...]]:正则格式的ProxyPass。

    5.2 通过mod_proxy负载均衡tomcat

    关于httpd反向代理的负载均衡配置方式,参见详细分析apache httpd反向代理的用法

    在httpd.conf中配置如下内容:

    <proxy balancer://TomcatLB>
    BalancerMember ajp://192.168.100.38:8009 loadfactor=5
    BalancerMember ajp://192.168.100.36:8009 loadfactor=10
    </proxy>
    
    ProxyVia Off
    ProxyRequests Off
    ProxyPreserveHost Off
    ProxyPassMatch   "^/(.*.jsp)$" balancer://TomcatLB/$1 
    ProxyPassReverse "^/(.*.jsp)$" balancer://TomcatLB/$1
    
    <Proxy *>
      Require all granted
    </Proxy>
    
    <Location /status>
      SetHandler balancer-manager
      Proxypass ! 
      Require ip 192.168.100
    </Location>
    

    重启httpd并在浏览器中输入192.168.100.17/test/index.jsp测试,测试时会轮调两次tomcatB再轮调一次tomcatA。

    如果要实现session粘滞(绑定),则修改httpd配置文件如下:

    <proxy balancer://TomcatLB>
    BalancerMember ajp://192.168.100.22:8009 loadfactor=5  route=TomcatA
    BalancerMember ajp://192.168.100.23:8009 loadfactor=10 route=TomcatB
    ProxySet  lbmethod=byrequets
    </proxy>
    
    ProxyVia Off
    ProxyRequests Off
    ProxyPreserveHost Off
    ProxyPassMatch   "^/(.*.jsp)$" balancer://TomcatLB/$1 stickysession=JSESSIONID
    ProxyPassReverse "^/(.*.jsp)$" balancer://TomcatLB/$1 stickysession=JSESSIONID
    
    <Proxy *>
      Require all granted
    </Proxy>
    
    <Location /status>
      SetHandler balancer-manager
      Proxypass ! 
      Require ip 192.168.100
    </Location>
    

    然后分别配置tomcatA和tomcatB的engine组件,分别加上jvmRoute="TomcatA"和jvmRoute="TomcatB"属性。

    <Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatA">
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatB">
    

    重启httpd和tomcatA、tomcatB,然后测试结果,再测试时同一客户端将总是得到同一个结果,不会出现负载均衡。

  • 相关阅读:
    C#面向对象的三大特性概述
    SQL Server 低版本还原高版本的数据库
    将表转化成脚本的存储过程
    iis安装失败解决方法
    kafka consumer 配置详解
    C#解析XML文件
    blat
    REST接口POST方法发送文件到服务器(C#)
    http://www.codeproject.com/Questions/117324/uploadfileincwithHttpWebRequest
    PDF Password Remover
  • 原文地址:https://www.cnblogs.com/f-ck-need-u/p/8414043.html
Copyright © 2020-2023  润新知