搭建 Keepalived + Nginx + Tomcat 的高可用负载均衡架构

1、概述

初期的互联网企业由于业务量较小,所以一般单机部署,实现单点访问即可满足业务的需求,这也是最简单的部署方式,但是随着业务的不断扩大,系统的访问量逐渐的上升,单机部署的模式已无法承载现有的业务量,需要进行服务集群化部署,本文主要介绍服务端 Tomcat 多实例部署,以及如何保证 web 服务的高可用方案。

  • Nginx 是一个高性能的 HTTP 反向代理服务器
  • Keepalived 是一个基于 VRRP 协议来实现的 LVS 服务高可用方案,可以利用其来避免服务的单点故障
  • Tomcat 是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器。

2、Nginx 的高可用负载均衡架构

如下图:为典型的 Tomcat 服务多实例部署的架构图

未分类

  1. 用户通过域名请求到 DNS,由 DNS 解析域名后返回对应的 IP 地址,该 IP 及为 Keepalived 映射服务器的虚拟 IP

  2. 通过该虚拟 IP 访问到对应的负载均衡器(Nginx),这里 Nginx 部署两个,然后通过 Keepalived 来保证 NG 的高可用,正常情况下由 Keepalived-M 将虚拟 IP 映射转发至 Nginx-M,如果 Nginx-M 出现故障,此时 Keepalived 会切换至 Keepalived-S 开始工作,从而保证了 NG 的单点故障问题。

  3. 通过 Nginx 负载均衡器,将请求路由到对应的 Tomcat 服务。

3、搭建 Keepalived + Nginx + Tomcat 的高可用负载均衡架构

3.1 需要准备的软件

(1)apache-tomcat-8.5.16.tar.gz

(2)nginx-1.12.2.tar.gz

(3)keepalived-1.3.9.tar.gz

3.2 服务器准备

两台服务器如:192.168.10.11,192.168.10.12

3.3 安装需要的依赖包

yum -y install gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel open openssl-devel

3.4 安装

3.4.1 安装 Tomcat

(1)分别在两台服务器中安装 Tomcat,解压 apache-tomcat-8.5.16.tar.gz 及可完成安装。

3.4.2 安装 Nginx

(1)解压安装包:tar -zxvf nginx-1.12.2.tar.gz

(2)进入到 nginx-1.12.2 目录:cd nginx-1.12.2

(3)编译:

./configure --with-http_stub_status_module --with-http_ssl_module  --prefix=/usr/local/nginx
make && sudo make install

3.4.3 安装 Keepalived

(1)解压安装包:tar -zxvf keepalived-1.3.9.tar.gz

(2)进入到 keepalived-1.3.9 目录:cd keepalived-1.3.9

(3)执行编译:

./configure --prefix=/usr/local/keepalived --sysconf=/etc
make && sudo make install

3.5 配置

3.5.1 分别配置两台服务器的 Nginx

(1)分别修改两台服务器 nginx 配置文件,vi /usr/local/nginx/conf/nginx.conf

(2)内容如下:

#nginx进程数
worker_processes  1;

#单个进程最大连接数
events {
    worker_connections  1024;
}

#http服务器配置
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    #长连接超时时间,单位是秒
    keepalive_timeout  65;
    #upstream负载均衡配置,配置路由到tomcat的服务地址以及权重
    upstream localhost{
       server 192.168.10.11:8080 weight=2;
       server 192.168.10.12:8080 weight=2;
    }

    #虚拟主机的配置
    server {
        #监听端口
        listen       80;
         #域名可以有多个,用空格隔开
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
            #nginx跟后端服务器连接超时时间(代理连接超时)
            proxy_connect_timeout 3;
            #后端服务器数据回传时间(代理发送超时)
            proxy_send_timeout 30;
            #连接成功后,后端服务器响应时间(代理接收超时)
            proxy_read_timeout 30;
            proxy_pass http://localhost;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

3.5.2 主 Keepalived 配置

(1)修改 11 服务器的 keepalived 配置文件,vi /etc/keepalived/keepalived.conf

(2)内容如下:

! Configuration File for keepalived
#全局配置
global_defs {
   #keepalived切换的时候,发消息到指定的email,可配置多个email
   notification_email {
     [email protected]
     [email protected]
   }
   #通知邮件从哪个地址发出
   notification_email_from [email protected]
   #通知邮件的smtp地址
   smtp_server smtp.exmail.qq.com
   #连接smtp服务器的超时时间,单位秒
   smtp_connect_timeout 30
   #Keepalived的机器标识,一个网络内保持唯一
   router_id nginx-master
}

#执行脚本配置
vrrp_script chk_nginx {
    #脚本所在路径
    script "/home/project/keepalived/check_nginx.sh"
    #脚本执行间隔时间,秒
    interval 2
    #优先级
    weight 2
}
#keepalived实例配置
vrrp_instance VI_1 {
    #指定实例的初始状态,MASTER或BACKUP两种状态,并且需要大写
    state MASTER
    #实例绑定的网卡
    interface ens33
    #虚拟路由标识,是一个数字,整个VRRP内唯一,如果keepalived配置了主备,需要相同
    virtual_router_id 51
    #优先级,数值愈大,优先级越高
    priority 100
    #MASTER与BACKUP之间同步检查的时间间隔,单位为秒
    advert_int 1
    #通信验证
    authentication {
        auth_type PASS
        auth_pass feinik
    }
    #追踪外围脚本
    track_script {
        #这里配置vrrp_script的名称
        chk_nginx
    }
    #虚拟ip配置,可配置多个
    virtual_ipaddress {
        192.168.10.200
    }
}

3.5.3 备 Keepalived 配置

(1)修改 12 服务器的 keepalived 配置文件,vi /etc/keepalived/keepalived.conf

(2)内容如下:

! Configuration File for keepalived
#全局配置
global_defs {
   #keepalived切换的时候,发消息到指定的email,可配置多个email
   notification_email {
     [email protected]
     [email protected]
   }
   #通知邮件从哪个地址发出
   notification_email_from [email protected]
   #通知邮件的smtp地址
   smtp_server smtp.exmail.qq.com
   #连接smtp服务器的超时时间,单位秒
   smtp_connect_timeout 30
   #Keepalived的机器标识,一个网络内保持唯一
   router_id nginx-master
}

#执行脚本配置
vrrp_script chk_nginx {
    #脚本所在路径
    script "/home/project/keepalived/check_nginx.sh"
    #脚本执行间隔时间,秒
    interval 2
    #优先级
    weight 2
}
#keepalived实例配置
vrrp_instance VI_1 {
    #指定实例的初始状态,MASTER或BACKUP两种状态,并且需要大写
    state BACKUP
    #实例绑定的网卡
    interface ens33
    #虚拟路由标识,是一个数字,整个VRRP内唯一,如果keepalived配置了主备,需要相同
    virtual_router_id 51
    #优先级,数值愈大,优先级越高
    priority 99
    #MASTER与BACKUP之间同步检查的时间间隔,单位为秒
    advert_int 1
    #通信验证
    authentication {
        auth_type PASS
        auth_pass feinik
    }
    #追踪外围脚本
    track_script {
        #这里配置vrrp_script的名称
        chk_nginx
    }
    #虚拟ip配置,可配置多个
    virtual_ipaddress {
        192.168.10.200
    }
}

3.5.4 Nginx 状态检查脚本创建

(1)新建 Nginx 的状态检查脚本:check_nginx.sh

(2)内容如下:

#!/bin/sh
NGINX=/usr/common/nginx/sbin/nginx
PORT=80
nmap localhost -p $PORT | grep "$PORT/tcp open"
#echo $?
if [ $? -ne 0 ];then
    $NGINX -s stop
    #这里再次尝试启动NG
    $NGINX
    sleep 5
    nmap localhost -p $PORT | grep "$PORT/tcp open"
    [ $? -ne 0 ] && cd /usr/common/keepalived/sbin && pkill keepalived
    echo "stoped"
fi

4、运行测试

(1)为了更直观的查看到 keepalived 切换的效果,将 11 服务器中的 nginx 的 upstream 服务只配置 11 的 tomcat 服务地址,12 服务器中的 upstream 服务只配置 12 的 tomcat 服务地址,这样只需要观察将 11 服务器中的 nginx 关闭看使用虚拟 ip 是否可以访问到 12 服务器的 tomcat。

(2)分别启动两个服务器中的 tomcat、nginx、keepalived,访问虚拟 ip:192.168.10.200,可以查看到访问的是主 keepalived 服务器的 tomcat

未分类

3)关闭 11 服务器的 nginx,nginx -s stop,再次访问虚拟 ip,如下:说明主 keepalived 通过配置的脚本检测到了本服务的 nginx 服务挂掉了,所以立马切换至了备的 keepalived,这时 12 服务器的 keepalived 升为了主,所以就访问到了 12 服务器的 tomcat。

未分类

关于Tomcat中前台传值乱码的解决方式

在java SSM开发过程中,有时遇到前台传值到后台出现中文乱码,这是因为Tomcat默认西文编码,应更改编码,具体方法如下:

1、找到Tomcat文件夹下conf/server.xml

2、打开server.xml并找到以下内容

<Connector port="8888" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"/>

3、在上段内容中添加

URIEncoding="UTF-8

变成:

<Connector port="8888" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" URIEncoding="UTF-8"/>

Tomcat生命周期

Lifecycle接口

Lifecycle接口统一管理Tomcat生命周期。一共做了4件事:

  • 定义13个string类型常量,用于LifecycleEvent时间的type属性中,用于区分组件发出的LifecycleEvent事件时的状态。

  • 定义三个管理监听器的方法,addLifecycleListener、findLifecycleListeners、removeLifecycleListener。

  • 定义4个生命周期的方法,init、start、stop、destory,用于执行生命周期的各个阶段的操作。

  • 定义了获取当前状态的两个方法,getState、getStateName、用于获取当前的状态。

public interface Lifecycle {
    // 13个状态常量值
    public static final String BEFORE_INIT_EVENT = "before_init";
    public static final String AFTER_INIT_EVENT = "after_init";
    public static final String START_EVENT = "start";
    public static final String BEFORE_START_EVENT = "before_start";
    public static final String AFTER_START_EVENT = "after_start";
    public static final String STOP_EVENT = "stop";
    public static final String BEFORE_STOP_EVENT = "before_stop";
    public static final String AFTER_STOP_EVENT = "after_stop";
    public static final String AFTER_DESTROY_EVENT = "after_destroy";
    public static final String BEFORE_DESTROY_EVENT = "before_destroy";
    public static final String PERIODIC_EVENT = "periodic";
    public static final String CONFIGURE_START_EVENT = "configure_start";
    public static final String CONFIGURE_STOP_EVENT = "configure_stop";
    // 3个监听器方法
    public void addLifecycleListener(LifecycleListener listener);
    public LifecycleListener[] findLifecycleListeners();
    public void removeLifecycleListener(LifecycleListener listener);
    // 4个生命周期方法
    public void init() throws LifecycleException;
    public void start() throws LifecycleException;
    public void stop() throws LifecycleException;
    public void destroy() throws LifecycleException;
    // 2个当前状态方法
    public LifecycleState getState();
    public String getStateName();

生命周期的状态转化

Tomcat中的事件触发是通过这些状态来判定的。

未分类

生命周期事件监听机制

事件监听器需要三个参与者:

  • 事件对象:用于封装事件的信息,在事件监听器接口的同一方法中作为参数使用,继承自java.util.EventObject类。
  • 事件源:触发事件的源头,不同事件源触发不同事件类型。
  • 事件监听器:负责监听事件源发出的事件。实现 java.util.EventListener 接口。

LifecyleBase类

LifecycleBase 类是Lifecycle 接口的默认实现,所有实现了生命周期的组件都直接或者间接的继承自LifecycleBase。

生命周期方法

@Override
public final synchronized void init() throws LifecycleException {
    // 通过设置不同的启动状态
    try {
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
            sm.getString("lifecycleBase.initFail",toString()), t);
    }
}

// 不同状态时,触发不同事件
private synchronized void setStateInternal(LifecycleState state,
                                           Object data, boolean check) {
    this.state = state;
    String lifecycleEvent = state.getLifecycleEvent();
    if (lifecycleEvent != null) {
        fireLifecycleEvent(lifecycleEvent, data);
    }
}

// 调用注册的监听器的 lifecycleEvent 方法
protected void fireLifecycleEvent(String type, Object data) {
    LifecycleEvent event = new LifecycleEvent(this, type, data);
    for (LifecycleListener listener : lifecycleListeners) {
        listener.lifecycleEvent(event);
    }
}


@Override
public final synchronized void start() throws LifecycleException {
    // 验证生命周期状态
    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
        LifecycleState.STARTED.equals(state)) {
        return;
    }

    // 不同生命周期执行不同方法
    if (state.equals(LifecycleState.NEW)) {
        init();
    } else if (state.equals(LifecycleState.FAILED)) {
        stop();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
               !state.equals(LifecycleState.STOPPED)) {
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }

    // 设置生命周期状态 STARTING_PREP, 并调用 startInternal方法。
    try {
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        startInternal();
        if (state.equals(LifecycleState.FAILED)) {
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
    }
}

监听管理方法

// 由 standardContext#startInternal来注入监听器
private final List<LifecycleListener> lifecycleListeners = 
                    new CopyOnWriteArrayList<>();

@Override
public void addLifecycleListener(LifecycleListener listener) {
    lifecycleListeners.add(listener);
}

@Override
public LifecycleListener[] findLifecycleListeners() {
    return lifecycleListeners.toArray(new LifecycleListener[0]);
}

@Override
public void removeLifecycleListener(LifecycleListener listener) {
    lifecycleListeners.remove(listener);
}

状态管理方法

@Override
public LifecycleState getState() {
    return state;
}

@Override
public String getStateName() {
    return getState().toString();
}

监听机制

事件监听器需要三个参与者:

  • 事件对象—用于封装事件的信息,在事件监听器接口的统一方法中作为参数,一般继承 java.util.EventObjecct类。
  • 事件源—触发事件对的源头,不同事件源触发不同事件。
  • 事件监听器—负责监听事件源发出的事件,发生事件时,事件源调用事件监听器的统一方法处理。监听器一般实现java.util.EventListener接口。
public final class LifecycleEvent extends java.util.EventObject {
    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
        super(lifecycle);
        this.type = type;
        this.data = data;
    }
}
public interface LifecycleListener {
    public void lifecycleEvent(LifecycleEvent event);
}
public class HostConfig implements LifecycleListener {
    @Override
    public void lifecycleEvent(LifecycleEvent event) {
        try {
            host = (Host) event.getLifecycle();
            if (host instanceof StandardHost) {
                setCopyXML(((StandardHost) host).isCopyXML());
                setDeployXML(((StandardHost) host).isDeployXML());
                setUnpackWARs(((StandardHost) host).isUnpackWARs());
                setContextClass(((StandardHost) host).getContextClass());
            }
        } catch (ClassCastException e) {
            return;
        }

        // Process the event that has occurred
        if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
            check();
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();
        } else if (event.getType().equals(Lifecycle.START_EVENT)) {
            start();
        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
            stop();
        }
    }
}
// LifecycleBase.startInternal
this.state = state;
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
    fireLifecycleEvent(lifecycleEvent, data);
}

protected void fireLifecycleEvent(String type, Object data) {
    LifecycleEvent event = new LifecycleEvent(this, type, data);
    for (LifecycleListener listener : lifecycleListeners) {
        listener.lifecycleEvent(event);
    }
}

Tomcat重复请求,log4j2日志重复显示问题排查

前提:

公司项目用的是nginx+tomcat+java,线上环境和线下环境版本配置都是一样,采用的是jenkins自动化部署。

问题:

前段时间突然发现浏览器访问一个页面,后端日志会出现重复的两条日志记录一模一样,请求时间都是一样的,经过测试发现所有的请求都会有两条重复的日志记录。如下图:

未分类

排查:

1、一开始认为是前端重复请求了,查看nginx访问日志,发现刷新一次页面的确nginx会出现两条请求记录,一次是OPTIONS请求,一次是GET请求,但是监控tomcat日志,发现刷新一次页面有4次请求,两次完全相同(包括时间)的OPTIONS和两次完全相同的GET请求。

2、排除前端页面问题以后,开始怀疑是nginx重复请求导致,于是直接通过IP+tomcat端口访问,发现还是会有完全一模一样的两条日志。然后通过IP+端口测试其他几个后端api,都会出现一样的问题。

3、排查nginx和前端以后,把问题锁定在了tomcat环境上面,于是开始一步一步排查:

a、对比了线上和线下的tomcat版本、java版本以及diff对比了线上和线下的实例配置文件,都是完全一样的。

b、百度搜索以后,有网友说重复请求问题是因为在tomcat的默认的server.xml里面,错误的配置了Host或者Context标签导致两个对象同时持有日志文件,导致重复加载。

①如果新增了Host,那Host/appBase的值是不能和其他Host/appBase的值一样的。比如appBase都是webapps,那么两个Host会导致webapps下面的工程被加载两遍。

②如果配置了Context标签那么就一定要配置name属性值为:工程名(/web-sys),或者配置path值为工程名(/web-sys),否则就会引起重复加载工程的问题。

③标签,允许我们把多个域名配置在一个Host下面,例如:xxx.com、xxx.cn、xxx.com.cn,都可以配置在一个Host下面

需要配置成下面这样:

<Host name="www.xxx.com" appBase="mywebapps" autoDeploy="true" unpackWARs="true">

<Alias>www.xxx.cn</Alias> 

<Alias>www.xxx.com.cn</Alias> 

<Context docBase="web-sys" path="" name="/web-sys"/>

</Host>

我对比了我的server.xml文件,于是添加了name属性,然后删除了默认的ROOT目录,进行了一番测试,结果还是会出现显示两条重复日志的问题。

4、经过上面的排查以后,开始有点迷茫,于是把线上的实例war包直接拷贝到了本地环境,然后修改了数据库信息,经测试还是会有同样的问题。此时瞬间又找到了方向,应该是war包封装过程中的问题。

5、于是去查看jenkins 线上和线下构建过程,发现配置文件有个redis参数不一样、log4j2.xml日志级别不一样,swagger开启状态不一样,然后马上把新建一个实例,把jenkins构建过程改成和本地测试环境一样,重新构建部署到生产环境,访问测试终于正常了。

6、这次终于找到了原因,于是依次对上面几个不同的参数一个一个修改发布测试,最后发现是因为log4j2.xml里面少了additivity=”false”这个属性导致。(一旦一个日志输出到一个Logger,这个Logger的additivity设置为false,那么这个日志不会再继续向父Logger进行传递,忽略其他Logger的additivity的设置。)

7、最后再去排查为何会少一个属性,才发现是jenkins构建过程中,使用sed命令替换时,使用了.*通配符去替换日志等级level=”DEBUG”,导致把additivity=”false”替换为了空。

 <Logger name="cn.amd5.community" level="DEBUG" additivity="false">

            <AppenderRef ref="Console"/>

            <AppenderRef ref="DailyLogFile"/>

</Logger>

加上additivity=”false”以后,重启实例,测试正常,如下图:

未分类

idea使用tomcat启动webapp报404

使用工具idea创建一个maven的webapp项目,然后配置tomcat环境,点击tomcat运行,浏览器打开默认网页,但是报了404。我们知道404代码访问路径有问题,找到对应的资源,这种情况需要检查一下环境:

一、看项目代码结构

未分类

里面的out文件夹和target文件夹是自动生成的

二、检查一下artifact

未分类

上面的META-INF是自动生成不用管,但是新增artifact选中的是war exploded

三、检查一下module设置

未分类

四、url中的路径应该跟context配置保持一致

1、url中的路径

未分类

2、context路径设置

未分类

以上设置就是最简洁的设置检查方式,最常见的问题是只修改了url的路径没有修改context的路径,因此报了404。

eclipse配置Tomcat服务器server locations的方法

我在使用eclipse配置Tomcat服务器的时候发现,默认情况下Tocmat把我们部署的项目放在了workspaces下面,而不是像Myeclipse默认的那样放在tomcat的安装路径下

未分类

从上图1中可以看到,Server Locations配置是灰色的,无法去修改,当然,这里我已经勾选成了Use Tomcat installation,即部署在Tomcat安装目录下。eclipse默认是勾选的第一项,即Use workspace metadata。那么,怎么去修改该配置呢?

未分类

如上图2所示,想要Tomcat的Server配置可以修改,那么首先将server下面部署的所有项目都先remove掉,然后再在server上右键鼠标,选择Clean,clean完以后,再次双击server进入配置,即可发现配置可修改。

未分类

如上图3所示,配置已经可以修改了。

有时候,eclipse启动tomcat的时候会出现:

WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.jee.server:firstProject' did not find a matching property

这样的问题,我也遇到过这样的报错信息。出现这个问题,可能是由于没有勾选server options下的Publish module contexts to separate XML files,我是通过勾选了该配置项解决了这个问题。

最后,提醒下,修改了配置以后,别忘了保存修改。

Java Tomcat Demo配置

简单写了个 Java MVC + Tomcat Demo 和 SpringBoot Demo 配置,小demo仅仅是用来测试的,各家还得根据自己公司的调调来配置。

未分类

高级任务-Deploy前置任务

在宿主机未检出代码前的前置任务,常为安装依赖、配置环境变量等

高级任务-Deploy后置任务

# 打包编译
${MVN_HOME}/bin/mvn clean package -Dmaven.test.skip=true
cp target/walle-web.war .

高级任务-Release前置任务

# 停服
${TOMCAT_HOME}/bin/shutdown.sh

高级任务-Release后置任务

# tomcat 更新服务
cp ${WEBROOT}/walle-web.war ${TOMCAT_HOME}/webapps/
# 服务启动
${TOMCAT_HOME}/bin/startup.sh

未分类

SpringBoot的部署的方式有多种,有java -jar xx.jar,也有配置Tomcat部署方式,结合热部署、配置中心,以及ansible等方案。

Tomcat下的SSL证书安装方法

一、证书导入

导入中级证书: keytool -import -alias intermediate -keystore c:server.jks -chinasslcrt –file c:intermediate.crt 提示“认证已添加至keystore中”则导入成功。

导入交叉证书: keytool -import -alias cross -keystore c:server.jks -chinasslcrt -file c:chain.crt 提示“认证已添加至keystore中” 则导入成功。

导入服务器SSL证书: keytool -import -alias mykey -keystore c:server.jks -chinasslcrt -file c:server.crt 输入密码后 提示:“认证回复已安装在 keystore中”说明导入成功。

二、修改配置文件server.xml 将已正确导入认证回复的server.jks文件复制到tomcat安装目录下的conf目用文本编辑器打开Server.xml并更新以下内容

maxThreads="150" SSLEnabled="true" scheme="https" secure="true"

clientAuth="false" sslProtocol="TLS" keystoreFile="D:/jcy/Tomcat7/conf/server.jks"?keystorePass="123456" />

下面为配置文件参数说明: port=”443″?SSL访问端口号为443 keystoreFile 私钥库文件 server.jks keystorePass私钥库密码 123456

对于Tomcat服务器,如果需要支持使用https访问,可以将服务器证书生成以后,配置server.xml文件,开放https访问方式。但有些时候,可能我们需要将整站或者一些特定的URL限制为只能使用https方式进行访问,那么需要在WEB应用的web.xml文件中进行进一步的配置。 在 tomcat /conf/web.xml 中的 后面加上下面代码:

需要重新启动tomcat,命令如下:

service tomcat stop

service tomcat start

备注:

一:Tomcat 默认http端口是8080,https一般默认443端口。修改默认网站端口方法,编辑server.xml修改下面代码:

connectionTimeout="20000"??URIEncoding="UTF-8"

redirectPort="443" />

二:“F5负载均衡产品设备的面板中健康检测还是应该是8080 而不是 443 否则认为不对,导致不能外网访问“,??.jks文件安装所以不需要加keystoreType=”PKCS12″这个代码。

server.xml配置文件参考模板:

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" />

connectionTimeout="20000"??URIEncoding="UTF-8"

redirectPort="443" />

maxThreads="150" SSLEnabled="true" scheme="https" secure="true"

clientAuth="false" sslProtocol="TLS" keystoreFile="D:/jcy/Tomcat7/conf/server.jks" keystoreType="PKCS12" keystorePass="123456" />

resourceName="UserDatabase"/>

unpackWARs="true" >

prefix="localhost_access_log." suffix=".txt"

pattern="%h %l %u %t "%r" %s %b" />

以上由安信SSL证书整理发出,欢迎大家实践测试!

tomcat 修改部署web项目的路径

tomcat 发布自己项目操作实际很简单,修改路径是为了方便我们发布项目,不需要频繁操作。

1. 首先当然是去tomcat官网选择你需要的版本下载

https://link.juejin.im/?target=http%3A%2F%2Ftomcat.apache.org%2Fdownload-70.cgi

2. 下载需要的JDK

https://link.juejin.im/?target=https%3A%2F%2Fwww.oracle.com%2Ftechnetwork%2Fjava%2Fjavase%2Fdownloads%2Findex.html

安装之后设置环境变量, JAVA_HOME = “你安装的java地址”

例如: JAVA_HOME="C:Program FilesJavajdk-10.0.1"

3. 解压tomcat,并且配置环境变量

CATALINA_HOME = “安装的tomcat地址”

例如: CATALINA_HOME="F:Program Files (x86)apache-tomcat-9.0.14"

4. 找到 tomcat下面的conf文件夹下面的server.xml

找到节点里面添加配置

<Context docBase="G:webapptest" reloadable="true" path="/test" />

docBase:  你电脑指定的web项目的路径

reloadable=”true” 相关文件改变的时候会重新加载web app

path: 虚拟文件目录,可以为空,配置的话访问地址就要加上它再访问

5. 找到 tomcat 下面的 bin 文件夹里面的 startup.bat 启动tomcat服务

输入 localhost:8080/test 即可看到你的web项目

6. 修改tomcat的端口号

找到server.xml 中的 节点的port=8080修改成你需要的端口,记得不要冲突

例:<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
  • netstat -ano 命令,列出所有端口情况,查看是否有冲突的端口被占用
  • netstat -aon|findstr “8080” 具体被占用8080端口对应的PID
  • tasklist|findstr “端口占用的PID” 就知道哪个进程占用了8080端口

Tomcat 日志切割(logrotate)详细介绍

这篇文章主要介绍了Tomcat 日志切割(logrotate)详细介绍的相关资料,需要的朋友可以参考下

Tomcat 日志切割

logrotate是个强大的系统软件,它对日志文件有着一套完整的操作模式,譬如:转储、邮件和压缩等,并且默认logrotate加到cron(/etc/cron.daily/logrotate)作为每日任务执行。自动有了logrotate,我想不用再自己写日志切割脚本。

如下对Tomcat日志catalina.out日志切割

# ls -lh /usr/local/tomcat/logs/catalina.out
-rw-r--r-- 1 www www 14M Aug 28 15:55 /usr/local/tomcat/logs/catalina.out

配置logrotate对catalina.out日志切割

# cat /etc/logrotate.d/tomcat
/usr/local/tomcat/logs/catalina.out {
daily
rotate 5
missingok
dateext
compress
notifempty
copytruncate
}

参数详解:

  • daily 指定转储周期为每天
  • rotate 5 指定日志文件删除之前转储的次数,0指没有备份,5指保留5个备份
  • missingok 如果日志不存在则忽略该警告信息
  • dateext 文件后缀是日期格式,也就是切割后文件是:xxx.log-20150828.gz
  • compress 通过gzip压缩转储以后的日志(gzip -d xxx.gz解压)
  • notifempty 如果是空文件的话,不转储
  • copytruncate 用于还在打开中的日志文件,把当前日志备份并截断

立即截断日志:

# logrotate --force /etc/logrotate.d/tomcat

效果如下:

# ls -lh /usr/local/tomcat/logs/catalina.out*
-rw-r--r-- 1 www www  0 Aug 28 16:00 /usr/local/tomcat/logs/catalina.out
-rw-r--r-- 1 www www 1.1M Aug 28 16:00 /usr/local/tomcat/logs/catalina.out-20150828.gz