写点什么

斟茶兵——远程进程管理

作者:白粥
  • 2021 年 12 月 09 日
  • 本文字数:2335 字

    阅读完需:约 8 分钟

事先声明:如果你在阅读本文的过程中产生任何异议,不要怀疑,你一定是对的。

1. 背景

事情的起因是我们的系统前几天刚经历了一次严重的事故。准确的说这是我们帮其他公司(B 公司)维护的一套系统,它托管的 IDC 机房的维护人员误将机柜的电源断电,导致所有服务器下线了。看到这里如果你想笑,那就笑吧。


B 公司的这套系统是我们刚接手维护的,唯一的一份应用部署文档的修改日期还是几年前。就在事故发送的前一天下午,我逐一登录 90 多台服务器,由于时间紧,我没有逐个核对验证文档的准确性,而是先用ps命令将进程信息保存到了文件里。真是无巧不成书,第二天我们就是参照着这个进程快照文件才得以将系统完整复原。


如今的经济形式不容乐观,不少半大不小的公司在苦苦维护系统的时候都选择压缩人工成本。伴随着人员的离职,系统的脉络开始模糊、细节开始丢失,不可避免的开始腐化。作为从业人员,我们需要一个快速有效的办法来面对这一现状。

2. 设计思路

2.1 思路


基本原则:(尽量)不要在服务器上做(过多的)修改。


最小目标:确定部署命令和位置;尽量梳理依赖关系



2.2 方案

Linux 中的 ps 命令是 Process Status 的缩写。ps 命令用来列出系统中当前运行的那些进程。


netstat 是一个基于命令行界面的网络实用工具,可显示当前的网络状态,包括传输控制协议层的连线状况、路由表、网络接口状态和网络协议的统计信息等。

远程采集

在每台服务器上部署一个采集脚本。通过ps命令收集进程信息,通过netstat命令收集网络信息,将两组信息发送给本地的 web 系统,由 web 系统进行解析和统计。 这样我们就实现了最小的服务器端改动



采集脚本:


#!/bin/bash
host=''
if [ ! $1 ]; then echo "monitor 127.0.0.1" exit 1else host=$1fi
# 将输出信息先base64然后在gzip压缩running_process=`ps aux | base64 -w 0 | gzip > ./running_process.gz`
netstat=`netstat -tanpl | base64 -w 0 | gzip > ./netstat.gz`
curl -X POST -F "host=$host" -F "process=@running_process.gz" -F "netstat=@netstat.gz" http://your.website
复制代码

本地解析

  1. 解析网络信息,根据 State 状态解析出当前进程的角色,LISTEN 状态的进程定义为“服务方(Provider)”;其他的进程为“消费方(Consumer)”;

  2. 解析进程信息,抽象成“进程(Process)”对象;

  3. 通过关键字段构建服务方、消费方和进程之间的联系。


3. 实现效果





4. 技术要点

4.1 技术栈

前端

后端

4.2 技术要点

使用 gzip 压缩

如果服务器运行的进程较多,我们采集的信息就会很大,使用 curl post 发送的时候就会报错:


-bash: /usr/bin/curl: Argument list too long
复制代码


这时我们需要将结果先用 base64 转码然后再用 gzip 压缩成文件,最后通过 curl post 上传文件的方式,将信息传递给后台。


running_process=`ps aux | base64 -w 0 | gzip > ./running_process.gz`
复制代码


后台解压过程如下:


public String collect(@RequestParam String host, @RequestParam MultipartFile process,   @RequestParam MultipartFile netstat) throws IOException {     String processContent = getZipContent(process.getInputStream());     String netstatContent = getZipContent(netstat.getInputStream());   processContent = processContent.replaceAll("\n", "");     processContent = new String(Base64Utils.decodeFromString(processContent));}
// gzip解压缩String getZipContent(InputStream gis){ GZIPInputStream gisProcess = null; try { gisProcess = new GZIPInputStream(gis); BufferedReader in = new BufferedReader(new InputStreamReader(gisProcess)); StringBuffer sb = new StringBuffer(); String line = null; while((line = in.readLine()) != null){ sb.append(line); } return sb.toString(); } catch (IOException e) { e.printStackTrace(); } return ""; }
复制代码

vue 前后分离项目,后端使用 jpa 动态查询问题

例如前端定义 form 如下:


// process表单数据
form: { host: '', pid: '', userName: '', command:''}
复制代码


后台 Process 定义如下:


public class Process {      private String id;     private String host;     private String pid;     private String userName;     private String startDate;     private String creatDate;     private String command;     private String remark;      // getter&setter}
复制代码


当我们通过 controller 接收到请求后,空字段是“”而不是 null。


public Result getProcess(Process process) {     return new Result(20000, processService.findAll(process));  }
// 忽略空值ExampleMatcher.matching().withIgnoreNullValues();
复制代码


在 jpa 中,可以指定忽略为 null 的参数不作为查询条件,但是不能忽略空字符串,下面通过反射,将空字符串转换成 null,即可。


// 值替换Class clazz = process.getClass();  Field[] fields = clazz.getDeclaredFields();  for (Field field : fields) {     field.setAccessible(true);   try {        if ("".equals(String.valueOf(field.get(process)))) {           field.set(process, null);   }     } catch (Exception e) {        e.printStackTrace();   }  }
// 忽略null值ExampleMatcher matcher = ExampleMatcher.matching().withIgnoreNullValues() // 模糊查询 .withMatcher("command",GenericPropertyMatchers.contains());
Example<Process> example = Example.of(process, matcher);List<Process> datas = processRepository.findAll(example);
复制代码


发布于: 2021 年 12 月 09 日阅读数: 12
用户头像

白粥

关注

还未添加个人签名 2018.05.02 加入

还未添加个人简介

评论

发布
暂无评论
斟茶兵——远程进程管理