基础配置

首先Java项目使用docker方式部署,其次开发的同事已搭建好dockerFile和docker-compose,效果是每次会批量自动执行生成镜像并执行项目。

需求背景

想要监测MySQL和Redis的进程是否挂掉的需求,如果挂掉就需抛出异常信息给消息队列。

需求整体思路

遇到这个问题,最先想到的肯定是直接ps -ef|grep xxx或者netstat来观察项目是否挂掉,然后建立Java与Linux系统的连接使得可以定时在Java代码中执行

初版遇到的困难导图

请添加图片描述

引入正题

首先先看一下第一版设计遇到了哪些困难,我会一一介绍遇到的问题。
话不多说直接登录linux服务器,由于项目部署是通过docker的,那直接输入下面的命令是无效的。别看有返回,这个只是你执行的命令。那明明启动了,为什么查不到mysql的动向呢?

ps-ef |grep mysql

在这里插入图片描述
原因就是项目中MySQL也是在docker容器的,那这样就得换一种写法。如下图所示,

docker ps |grep mysql

在这里插入图片描述
这里不扩展判断依据,比如可以查询条数,如下所示,

docker ps |grep mysql|wc-l

那就引出我们最重要的核心问题集,如上图所示项目就是我们的出发点,那我们知道docker容器直接无法直接通信,那我们该如何访问呢?那我们可以借助Linux容器来帮我们解决。归纳出下面两个问题,
第一个问题:如何解决在容器内使用容器外的脚本问题(难)
第二个问题:如何在Java里面使用执行Linux的cmd指令(难)
第二个问题:如何定时任务(易)

解决方案

1、首先容器内访问容器外该如何解决呢?
在这里插入图片描述
a.在resources下添加mysql.exp与redis.exp脚本
在这里插入图片描述
b.在dockerFile中添加如下内容

RUN apt-get update && apt-get -y install expect vim lrzsz
COPY ./target/classes/mysql.exp mysql.exp
COPY ./target/classes/redis.exp redis.exp

c.我们以mysql.exp为例,其对应在命令行输入的格式应为expect mysql.exp + host + port + passwd,其中host例如op@192.168.1.1,port例如22,passwd例如123456

#!/usr/bin/expect -f
#ssh连接服务器
set host [ lindex $argv 0 ]
set port [ lindex $argv 1 ]
set passwd [ lindex $argv 2 ]
spawn ssh -o "StrictHostKeyChecking no" $host -p $port
#等待带有password字样,并输入密码
expect "*password*" {send "$passwd\r"}
#执行命令
expect "*op@dev-test*" {
    send "cd\r"   
    send "docker ps|grep mysql\r"
}
#退出
expect eof

2、Java里面使用执行Linux的cmd指令,这个可以直接放到Java代码中

    public static String exeCmd(String commandStr) {

        String result = null;
        try {
            String[] cmd = new String[]{"/bin/sh", "-c", commandStr};
            Process ps = Runtime.getRuntime().exec(cmd);
            BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
            StringBuffer sb = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) {
                //执行结果加上回车
                sb.append(line).append("\n");
            }
            result = sb.toString();
            System.out.println(result);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;

    }

3、定时任务实现,这一块儿用伪代码表示

@Component
@Slf4j
public class MonitorByLinuxJob {
    @Value("${working.mysql.host}")
    private String host;
    @Value("${working.mysql.port}")
    private String port;
    @Value("${working.mysql.passwd}")
    private String passwd;

    @Scheduled(fixedDelayString = "${working.fixedDelay:5000}")
    public void reportCurrentTime() throws InterruptedException {
        if (ObjectUtils.isNotEmpty(exeCmd("expect mysql.exp" + " " + host + " " + port + " " + passwd))) {
            log.info("mysql is OK");
        } else {
            log.error("mysql is disconnected");
        }
    }  
}

注意事项

加上定时间隔10秒,预计每30秒返回一次查询结果。如果发现某个连接断开,比如MySQL挂了,那么可以在log.error(“mysql is disconnected”)这个分支中增加Thread.sleep,使其检测到中断也不会频繁报错。

项目可扩展点

虽然我们需求只是想要监测MySQL和Redis的进程是否挂掉的需求,但只需稍加改变就可以检测它的性能(top),其他在docker中的对象如zbus等……

拓展思路

我看到网上有人用SSH-client-pool连接需要发送的服务器,然后在Java代码里生成脚本文件,再用executeCommand执行。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐