zoukankan      html  css  js  c++  java
  • Tomcat学习总结(10)——Tomcat多实例冗余部署

    昨天在跟群友做技术交流的时候,了解到,有很多大公司都是采用了高可用的,分布式的,实例沉余1+台。但是在小公司的同学也很多,他们反映并不是所有公司都有那样的资源来供你调度。往往公司只会给你一台机器,因为有些应用挂了公司也不会有损失的,我们往往一台机器就可以搞定。
    但是,我们也要为我们做出来的应用负责,毕竟东西做出来是为了给人用的,如果做出来的东西经常挂了,谈何使用,在前期,如果公司资源紧张的情况下,可以不可以做高可用,多机器的沉余部署。但是至少是在但机上有2个进程在跑。so,在这里我们就说说这个,如何做单机多实例的部署。
    在这里谈谈,在只有单机的资源下,如何把单机的资源压榨出来,用好单机。
    • 通常,我们对tomcat单机部署需求可以分为几种:
    • 单实例单应用 (一个tomcat 一个web应用)
    • 单实例多应用 (一个tomcat多个应用)
    • 多实例单应用 (多个tomcat都部署一个应用)
    • 多实例多应用 (多个tomcat部署多个不同的应用)

    第一种场景:这是我们开发中经常用到的,如果不要求周期性地维护tomcat版本,一般的做法是把打好的war包丢到webapps目录下,然后执行startup.sh脚本,并且可以在浏览器里访问就行了。
    第二种场景:是把多个应用程序的war包放在同一个tomcat的webapps目录,这样一来,关闭和启动tomca,或tomcat挂掉会影响所有项目。
    第三种场景: 各个tomcat都运行同一个应用程序,对应地需要修改不同的监听端口,这种方式通常会和apache httpd或者nginx整合使用,做一些负载均衡的处理。
    第四种场景: 相当于第一种场景的复数形式,除了修改不同的监听端口,没有本质区别。

    一般来说,多实例部署tomcat,可以充分利用系统资源,不过这种方式,也有几个方面需要考虑:
    多实例tomcat的更新维护,例如对tomcat进行升级等操作,我们需要考虑如何能“优雅”地对所有实例进行升级
    尽量不要影响应用程序,在更新tomcat时,一不小心就把conf目录等全部覆盖,所以尽量要把配置文件和安装目录隔离
    对于单应用来说,如果将war包分别置于各个tomcat的webapps目录,那么在发布新版本的war时,可能会出现某个实例更新失败,导致用户在访问时可能会访问到不同版本的web app,因此,比较好的方式就是所有tomcat实例都统一指向同一个应用程序,这样做,就可以多个tomcat用一份应用源码,简单部署,单机高可用也能实现(要配合nginx).
    本文重点阐述多实例应用的部署方案,但是为了解决上述几个问题,我们需要先来了解一下tomcat的一些基本情况。

    2、我们的目标

    tomcat架构


    3、tomcat架构

    整体架构图

    tomcat架构

    这里有一台服务器,3台tomcat服务,以及一台tomcat的解构图。

    分离目录

    目录 作用
    bin 主要存放脚本文件,例如比较常用的windows和linux系统中启动和关闭脚本
    conf 主要存放配置文件,其中最重要的两个配置文件是server.xml和web.xml
    lib 主要存放tomcat运行所依赖的包
    logs 主要存放运行时产生的日志文件,例如catalina.{date}.log等
    temp 存放tomcat运行时产生的临时文件,例如开启了hibernate缓存的应用程序,会在该目录下生成一些文件
    webapps 部署web应用程序的默认目录
    work 主要存放由JSP文件生成的servlet(java文件以及最终编译生成的class文件)

    再介绍两个tomcat中比较重要的概念(通常也是两个系统变量)——CATALINA_HOME和CATALINA_BASE:

    CATALINA_HOME:即指向Tomcat安装路径的系统变量
    CATALINA_BASE:即指向活跃配置路径的系统变量通过设置这两个变量,就可以将tomcat的安装目录和工作目录分离,从而实现tomcat多实例的部署。
    Tomcat官方文档指出,CATALINA_HOME路径的路径下只需要包含bin和lib目录,这也就是支持tomcat软件运行的目录,而CATALINA_BASE设置的路径可以包括上述所有目录,不过其中bin和lib目录并不是必需的,缺省时会使用CATALINA_HOME中的bin和conf。如此,我们就可以使用一个tomcat安装目录部署多个tomcat实例,这样的好处在于方便升级,就可以在不影响tomcat实例的前提下,替换掉CATALINA_HOME指定的tomcat安装目录。

    tomcat架构

    tomcat serve.xml 配置结构
    Container容器子容器间关系图

    tomcat架构

    交互图

    tomcat架构

    对比下Tomcat serve.xml 的配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <?xml version="1.0" encoding="UTF-8"?>
    <Server port="8005" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    <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">
    <Connector port="8080" protocol="HTTP/1.1"
    connectionTimeout="20000"
    redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">
    <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">
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    prefix="localhost_access_log" suffix=".txt"
    pattern="%h %l %u %t &quot;%r&quot; %s %b" />
    </Host>
    </Engine>
    </Service>
    </Server>


    4、实战

    端口配置

    修改server.xml

    在server.xml中配置了四个监听端口,分别是:
    Server Port:该端口用于监听关闭tomcat的shutdown命令,默认为8005.
    Connector Port:该端口用于监听HTTP的请求,默认为8080.
    AJP Port:该端口用于监听AJP( Apache JServ Protocol )协议上的请求,通常用于整合Apache Server等其他HTTP服务器,默认为8009
    Redirect Port:重定向端口,出现在Connector配置中,如果该Connector仅支持非SSL的普通http请求,那么该端口会把https的请求转发到这个Redirect Port指定的端口,默认为8443

    虚拟主机配置
    再来说Host配置,Host就是所谓的虚拟主机,对应包含了一个或者多个web应用程序,默认的Host配置如下

    1
    <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">

    其中:
    name: 虚拟主机的名称,一台主机表示了完全限定的域名或IP地址,默认为localhost,同时也是唯一的host,进入tomcat的所有http请求都会映射到该主机上
    appBase:web应用程序目录的路径,可以是CATALINA_HOME的相对路径,也可以写成绝对路径,默认情况下为$CATALINA_HOME/webappsunpackWARs: 表示是否自动解压war包
    autoDeploy:所谓的热部署,即在tomcat正在运行的情况下,如果有新的war加入,则会立即执行部署操作
    另外再介绍一个Host中的属性—deployOnStartup:表示tomcat启动时是否自动部署appBase目录下所有的Web应用程序,默认为true。这个属性和autoDeploy会产生两次部署的“副作用”:一次是tomcat启动时就开始部署,第二次就是autoDeploy引起的热部署。因此最好将autoDeploy置为false
    在部署多实例单应用的时候,默认的$CATALINA/webapps会因为tomcat安装目录升级产生不必要的麻烦,我们考虑将appBase的目录统一到另外的路径下。

    Context的配置
    最后再说明一下Context的配置,它出现在Host配置内,一个Context的配置就代表了一个web应用程序,如果配置多应用程序,就需要在Host下配置多个Context,一个简单的Context配置如下

    1
    <Context path="/some" docBase="someapp.war" >

    path:表示访问入口,例如,path=”/abc”,则访问localhost:8080/abc时,就可以访问该Context对应的应用程序。如果path=””,则直接用localhost:8080就可以访问
    docBase:表示应用程序的解包目录或者war文件路径,是Host的appBase配置目录的相对路径,也可以是直接写成绝对路径,但是不要将appBase的值,作为docBase配置路径的前缀,例如appBase=”somedir”,docBase=”somedir-someapp.war”,这样的配置会导致部署错误
    通过配置Host的appBase和Context的docBase两个属性,可以将应用程序的文件和tomcat相关的目录进行分离,这样webapps目录也就没有作用了。

    跟我来实施该方案

    • 现在假设我们有一台已经配置好Java环境的服务器:(我用的是阿里云)
    • 我已经有一个已经完成的shop.war 应用程序

    步骤1:
    下载并解压tomcat

    tomcat架构

    步骤2:
    对Tomcat目录作以下调整:
    在tomcat安装目录下创建a.ttlsa.com、b.ttlsa.com,并且将conf、logs、webapp、temp、work目录拷贝到这两个目录,然后tomcat安装目录只需要留下bin、a.ttlsa.com、b.ttlsa.com、lib这4个目录即可。配置后的目录结构如下:

    tomcat架构

    如果要度tomcat 进行升级,我们只是需要对tomcat的lib 和 bin 目录进行升级即可。

    步骤3:
    配置站点server.xml
    配置a.ttlsa.com 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- 8005 改为8005 -->
    <Server port="8005" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    <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">
    <Connector port="8081" protocol="HTTP/1.1"
    connectionTimeout="20000"
    redirectPort="8443" />
    <!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
    <Engine name="Catalina" defaultHost="localhost">
    <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">
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    prefix="localhost_access_log" suffix=".txt"
    pattern="%h %l %u %t &quot;%r&quot; %s %b" />
    </Host>
    -->
    <Host name="localhost" appBase="F:datawwwa.ttlsa.com"
    unpackWARs="true" autoDeploy="true"
    xmlValidation="false" xmlNamespaceAware="false">
    <Context path="" docBase="" reloadable="true">
    <valve className="org.apache.catalina.valves.RemoteAddrValve" />
    </Context>
    </Host>
    </Engine>
    </Service>
    </Server>

    配置b.ttlsa.com

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- 8005 改为8006 -->
    <Server port="8002" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    <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">
    <Connector port="8082" protocol="HTTP/1.1"
    connectionTimeout="20000"
    redirectPort="8443" />
    <!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
    <Engine name="Catalina" defaultHost="localhost">
    <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">
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
    prefix="localhost_access_log" suffix=".txt"
    pattern="%h %l %u %t &quot;%r&quot; %s %b" />
    </Host>
    -->
    <Host name="localhost" appBase="F:datawwwa.ttlsa.com"
    unpackWARs="true" autoDeploy="true"
    xmlValidation="false" xmlNamespaceAware="false">
    <Context path="" docBase="" reloadable="true">
    <valve className="org.apache.catalina.valves.RemoteAddrValve" />
    </Context>
    </Host>
    </Engine>
    </Service>
    </Server>

    创建多实例启动脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    # description: 启动tomcat多实例.#
    . /etc/init.d/functions
    RETVAL=$?
    # tomcat实例目录
    export CATALINA_BASE="$PWD"
    # tomcat安装目录
    export CATALINA_HOME="/usr/local/tomcat-7.0.50"
    # 可选
    export JVM_OPTIONS="-Xms128m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=512m"
    case "$1" in
    start)
    if [ -f $CATALINA_HOME/bin/startup.sh ];then
    echo $"Start Tomcat"
    $CATALINA_HOME/bin/startup.sh
    fi
    ;;
    stop)
    if [ -f $CATALINA_HOME/bin/shutdown.sh ];then
    echo $"Stop Tomcat"
    $CATALINA_HOME/bin/shutdown.sh
    fi
    ;;
    *)
    echo $"Usage: $0 {start|stop}"
    exit 1
    ;;
    esac
    exit $RETVAL

    这段shell 脚本比较简单,主要是设置环境变量,接受命令参数 RETVAL=?RETVAL=start/stopexportCATALINABASE=?,来执行不同的命令。RETVAL=start/stop等exportCATALINABASE=”PWD” 表示设置当前路径为 CATALINA_BASE 的环境变量,一般情况下CATALINA_BASE 和 CATALINA_HOME 是默认一样的。

    启动脚本赋权限

    1
    # chmod a+x tomcat.sh

    5、启动测试

    启动/关闭a.ttlsa.com

    1
    2
    3
    4
    5
    6
    启动
    # cd /usr/local/tomcat-7.0.50/a.ttlsa.com/
    # ./tomcat.sh start
    关闭
    # cd /usr/local/tomcat-7.0.50/a.ttlsa.com/
    # ./tomcat.sh stop

    启动/关闭b.ttlsa.com

    1
    2
    3
    4
    5
    6
    启动
    # cd /usr/local/tomcat-7.0.50/a.ttlsa.com/
    # ./tomcat.sh start
    关闭
    # cd /usr/local/tomcat-7.0.50/a.ttlsa.com/
    # ./tomcat.sh stop

    备注:一定需要cd到tomcat.sh的当前目录下执行才可以

    在win7 下,需要创建在a.ttlsa.com 和b.ttlsa.com下面创建 startup.bat 来启动

    1
    2
    3
    4
    5
    6
    @echo off
    set JAVA_HOME=D:Program FilesJavajdk1.8.0_112
    set PATH=%JAVA_HOME%in;%PATH%
    set CATALINA_BASE=%CD%
    cd E: omcat-8.5.6in
    catalina.bat start

    这段是在win7 下云的bat脚本,于shell脚本同理,set CATALINA_BASE=%CD% 也是设置环境变量,CD 可以获取当前的路径。

    shell 脚本入门参考:http://sishuok.com/forum/blogPost/list/5655.html

    6、结果

    单个Tomcat应用多个tomcat实例的配置到此,就结束了。
    此外,我们在这里的基础上进行系统的扩展,比如如果我的Tomcat应用挂掉了,我的整个应用都将不可用了,我们应该如何处理?
    我们可以把Tomcat复制多份,在单机的情况下,开多一个Tomcat进程,在配合Nginx 来配置,就能实现Tomcat的自动切换,这些内容,有空再写。

    如果需要操作多个实例显得比较麻烦,大家可以自行写统一的脚本。

    tomcat架构

    tomcat架构

    Linux 下的实现基本一致。

    这样的好处是,显而易见的,这样能开启Tomcat的多个进程,即多台tomcat,挂了也不太怕,还有其他tomcat应用支撑,代码实例我们发版本的时候,只需要发布一份,实例代码易于维护。
    但是,我们网站的域名和端口一般是同一采用80端口,统一的域名,而现在我们开启tomcat只能一个使用80端口,显然是不合适的·,为此我们会引入负载均衡的nginx来配置。
    nginx 采用80 端口,tomcat分别采用8080, 8081, 8082 这样就能让我们的程序稳定的运行。
    这样,我们就能进最大的限度来压榨单机的性能,保证应用程序的稳定的运行。
    而然,单机不然有单机的瓶颈,毕竟单机中的cpu 已经各种硬件的限制,会大大影响实例程序的跑动,在这时,就不再是单机能抗的动的了,我们需要分析程序的瓶颈在那?数据库,那就把数据库单独分出去,单独一台机器,是文件图片服务器,就把他分出去。如果是应用程序太大,就要考虑把应用实例进行拆解为不同哦那个的组件,单独部署,这就是分布式部署。
    当然,这都是后话,只有程序复杂到一定的程度,并体量很大的话,才会做这种架构的演变,成本和技术投入的难度也会相应的变大。
    本章,只局限于如何玩好单机的基础上来讨论,对于分布式的那块,笔者能力有限,尚且还不能完全驾驭,不做分享。

    7、nginx+tomcat热备

    在上面的配置的基础上,我们在进一步进行扩展,进行实例的均衡和热备。
    可以在一个服务器挂了的情况下连到另外一个,那怎么弄呢?
    其实很简单,在upstream中的local_tomcat中配置多一个server。
    在上面,我的a.ttlsa.com 和 b.ttlsa.com 都是访问 F:datawwwa.ttlsa.com 下的源码的index.jsp 页面,
    为了能观察,nginx 的keepAlive 的效果,我做一下修改:
    a.ttlsa.com —> F:datawwwa.ttlsa.com index.jsp 中文字是 1
    b.ttlsa.com —> F:datawww.ttlsa.com index.jsp 中文字是 2

    1
    2
    3
    4
    upstream local_tomcat {
    server localhost:8081 weight=1;
    server localhost:8082 weight=5;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    server {
    listen 80;
    server_name localhost:8081;
    #charset koi8-r;
    #access_log logs/host.access.log main;
    #location / {
    # root html;
    # index index.html index.htm;
    #}
    location / {
    proxy_pass http://local_tomcat;
    }

    tomcat架构

    在通常的情况下,我们一般是指向一份源码就足够了,并且设置权值,减轻应用的压力。同时也不会出现单点的情况。

    补充:nginx.con 配置

    #user  nobody;
    worker_processes  1;
    events {
        worker_connections  1024;
    }
    http {
        include       mime.types;
        default_type  application/octet-stream;
    
        sendfile        on;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        #gzip  on;
    
        upstream local_tomcat {  
            server localhost:8081 weight=1;  
            server localhost:8082 weight=5;  
        } 
    
        server {
            listen       80;
            server_name  localhost;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            #location / {
            #    root   html;
            #    index  index.html index.htm;
            #}        
            location / {  
                proxy_pass http://local_tomcat;  
            }  
    
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    
    }

    综上:我们做到了多台tomcat 但是我们也能做到tomcat的简单升级,并且实现实例的负载均衡,已经应用的主备,在也不用担心应用挂掉而睡不了觉了。

  • 相关阅读:
    eclipse中文乱码问题解决方案
    修改Tomcat的JDK目录
    Tomcat 5.5 修改服务器的侦听端口
    HTML DOM教程 27HTML DOM Button 对象
    HTML DOM教程 24HTML DOM Frameset 对象
    Navicat for MySQL v8.0.27 的注册码
    HTML DOM教程 25HTML DOM IFrame 对象
    Tomcat 5.5 的下载和安装
    android manifest相关属性
    ubuntu10.04 下 eclipse 小结
  • 原文地址:https://www.cnblogs.com/zhanghaiyang/p/7212733.html
Copyright © 2011-2022 走看看