docker里Java服务执行ping命令模拟流式输出

文章目录

    • 业务场景
    • 处理解决
      • 实现ping功能并实时返回输出
      • 实现长ping和中断请求
      • docker容器找不到ping命令处理

业务场景

  • 我们某市的客户,一直使用CS版本的信控平台,直接安装客户Windows server服务器上,主要对信号机设备进行在线管理、方案配时、管控等
  • 其中有一项功能,在网络波动情况,对信号机管控失败,判断信号机是否在线。大致方法是直接调用Windows的dos窗口,发送 ping ip的命令,显示网络情况
  • 我们新的信控平台使用Spring Cloud微服务架构,使用Spring Boot构建Java服务,使用google的jib插件打成docker镜像包
  • 我们使用docker虚拟化部署,使用docker-compose统一管理所有服务,部署在Linux服务器里
  • 客户很喜欢之前的功能,需要我们在新平台里实现这个功能,调用dos窗口,ping网络
  • 而我们新平台是B/S架构,浏览器是很难调用Windows组件去弹出窗口实现ping功能,而且我们也没有限制一定使用的是Windows电脑访问,有网有浏览器就行
  • ping功能无论Windows还是Linux,都是有的,至于界面展现,只能自己实现了
    在这里插入图片描述

处理解决

实现ping功能并实时返回输出

  • 代码实现,有两个核心功能点
  • 一是根据不同的操作系统,执行对应的系统命令,进行结果接收与解析
    • 对于第一个问题,Java有现成的类库,使用Runtime.getRuntime().exec(ping命令)即可
    • 对于Windows服务器,需要使用GB2312解析命令执行结果
    • 对于Linux 服务器,需要使用UTF_8解析命令执行结果
    • 对于ServletOutputStream.println输出中文字符串报错Not an ISO 8859-1 character问题,可以使用PrintWriter.println输出代替
    • 也可以在ServletOutputStream.println输出时输出字符数组(string.getBytes()
  • 二是流式输出到请求端,模拟再现一秒一次的逐步展示的效果
    • 对于第二个问题,核心是命令执行的结果输出流,要实时的返回给请求端,请求端能接收到
    • 主要是获取流,然后按行读取,按行flush()即可返回给请求端
    • 对于请求端实时渲染,需要在代码的response里指定ContentTypetext/event-stream,这样flush刷新的返回流,才能实时被前端浏览器接收到(ChatGPT流式输出也是使用的这种content-type
    • 一开始是考虑使用multipart,完全不行,流flush后,浏览器无法获取,只能在流输出完成后,浏览器才能获取到
  • 具体代码如下:
    /**
     * 获取信号机的网络状态
     * @param ip
     * @param count
     * @param response
     */
    @GetMapping("/ping/start")
    public void ping(String ip, Integer count, HttpServletResponse response) {
        logger.info("ping 信号机【{}】 开始 ......", ip);
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("UTF-8");
        String line = null;
        Process pro = null;
        BufferedReader buf = null;
        try {
            if (null == count) {
                count = 4;
            }
            String osName = System.getProperty("os.name");
            if (osName.toLowerCase().contains("windows")){
                pro = Runtime.getRuntime().exec("ping -n " + count + " " + ip);
                buf = new BufferedReader(new InputStreamReader(pro.getInputStream(), "GB2312"));
            } else if (osName.toLowerCase().contains("linux")){
                pro = Runtime.getRuntime().exec("ping -c " + count + " " + ip);
                buf = new BufferedReader(new InputStreamReader(pro.getInputStream(), StandardCharsets.UTF_8));
            }
            PrintWriter out = response.getWriter();
            while (null != buf && (line = buf.readLine()) != null){
                out.println(line);
                out.flush();
            }
            logger.info("执行ping请求结束!");
            out.close();
        } catch (Exception e){
            logger.error("执行ping命令出现异常");
            e.printStackTrace();
        }finally {
            if (null != pro){
                pro.destroy();
            }
        }
    }

实现长ping和中断请求

  • 主要是在请求时传输一个唯一命令id,缓存到内存里
  • 当命令执行完成,或者接收到打断请求时,调用destroy()打断循环,结束请求
  • 当然,可以尝试使用kill -2去模拟CTRL + C的打断,可以使用Runtime.getRuntime().exec(中断命令)打断试下,我的代码已经满足自己的需求了,就没再尝试,有兴趣的小伙伴可以试一下
  • 具体代码如下:

package com.newatc.api.rest;


import com.newatc.api.signalcontrol.dto.PingRequestVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 调用系统命令并返回
 *
 * @author yanyulin
 * @date 2024-1-16 15:11:55
 */
@RestController
@RequestMapping(value = "/api/syscmd")
public class SysCmdController {

    private static final Logger logger = LoggerFactory.getLogger(SysCmdController.class);

    /**
     * 命令id-执行过程map
     */
    public static final Map<String, Boolean> COMMAND_REQUEST_MAP = new HashMap<>();
    /**
     * 开始信号机的网络状态诊断
     */
    @PostMapping("/ping/start")
    public void ping(@RequestBody PingRequestVO pingRequest, HttpServletResponse response) {
        String ip = pingRequest.getIp();
        String cmdId = pingRequest.getCmdId();
        Integer count = pingRequest.getCount();
        logger.info("ping 信号机【{}】 开始 ......", ip);
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("UTF-8");
        String line = null;
        Process pro = null;
        BufferedReader buf = null;
        try {
            if (null == count) {
                count = 4;
            }
            if (count > 50) {
                count = 50;
            }
            String osName = System.getProperty("os.name");
            if (osName.toLowerCase().contains("windows")){
                pro = Runtime.getRuntime().exec("ping -n " + count + " " + ip);
                buf = new BufferedReader(new InputStreamReader(pro.getInputStream(), "GB2312"));
            } else if (osName.toLowerCase().contains("linux")){
                pro = Runtime.getRuntime().exec("ping -c " + count + " " + ip);
                buf = new BufferedReader(new InputStreamReader(pro.getInputStream(), StandardCharsets.UTF_8));
            }
            COMMAND_REQUEST_MAP.put(cmdId, true);
            PrintWriter out = response.getWriter();
            while (null != buf && (line = buf.readLine()) != null){
                out.println(line);
                out.flush();
                if (!COMMAND_REQUEST_MAP.get(cmdId)) {
                    pro.destroy();
                }
            }
            logger.info("执行ping请求结束!");
            out.close();
        } catch (Exception e){
            logger.error("执行ping命令出现异常");
            e.printStackTrace();
        }finally {
            if (null != pro){
                pro.destroy();
            }
            COMMAND_REQUEST_MAP.remove(cmdId);
        }
    }

    /**
     * 打断命令执行状态
     */
    @PostMapping("/ping/stop")
    public void ping(@RequestBody PingRequestVO requestVO) {
        COMMAND_REQUEST_MAP.put(requestVO.getCmdId(), false);
    }
}

docker容器找不到ping命令处理

  • 我们打包导出的docker镜像,无法使用ping命令,报错,找不到这个命令bash: ping:command not found
  • 我们使用的是极简镜像eclipse-temurin:11-jre-focal,这个版本里的ubuntu没有安装不需要的命令
  • 具体可以参考我的这篇博文:《自制Java镜像发布到dockerhub公网使用》
  • 也可以直接使用我发布到公网的包含ping命令的jre11镜像文件1363241277/jre11:11-jre-focal
  • 主要思路,就是打包使用的原始Java镜像里,要已经安装ping等需要的命令

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/333182.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

输电线路分布式故障定位监测装置:保障电力系统的稳定运行

随着电力系统的不断发展和电力需求的日益增长&#xff0c;输电线路的稳定性和安全性显得尤为重要。为了确保电力系统的正常运行&#xff0c;我国科研人员研发出了一种新型的输电线路分布式故障定位监测装置&#xff0c;它能够实时监测输电线路的运行状态&#xff0c;及时发现并…

全网最全Stable diffusion保姆级教程「安装-配置-画图」,小白必收藏!!

随着chat gpt爆火之后&#xff0c;越来越多的人开始关注人工智能&#xff0c;人工智能相关的其他应用如AI绘画&#xff0c;也再次得到人们的关注。AI绘画的确很上头&#xff0c;最近几天小编也研究一下&#xff0c;这里把研究的过程以及中间遇到的问题整理一下&#xff0c;我这…

【办公类-21-02】20240118育婴员操作题word打印2.0

作品展示 把12页一套的操作题批量制作10份&#xff0c;便于打印 背景需求 将昨天整理的育婴师操作题共享&#xff0c; 因为题目里面有大量的红蓝颜色文字&#xff0c;中大班办公室都是黑白单面手动翻页打印。只有我待的教务室办公室有彩色打印机打印&#xff08;可以自动双面…

每日一题——LeetCode1252.奇数值单元格的数目

进阶&#xff1a;你可以设计一个时间复杂度为 O(n m indices.length) 且仅用 O(n m) 额外空间的算法来解决此问题吗&#xff1f; 方法一 直接模拟&#xff1a; 创建一个n x m的矩阵&#xff0c;初始化所有元素为0&#xff0c;对于indices中的每一对[ri,ci]&#xff0c;将矩…

精品量化公式——“低位擒妖”,擒妖指标终极版,成功率百分99,无未来

不多说&#xff0c;直接上效果如图&#xff1a; ► 日线表现 代码评估 技术指标代码评估&#xff1a; X_1:55; X_2:34; 这两行设定了两个变量 X_1 和 X_2 的值分别为 55 和 34。这些值可能用于后续计算的时间周期参数。 X_3:100*(HHV(HIGH,X_1)-CLOSE)/(HHV(HIGH,X_1)-LLV(…

鹅厂有料有趣的程序员交流圈重磅官宣!加入立享福利

号外&#xff01;腾讯云开发者社区重磅上线海量社群&#xff0c;覆盖开发者技术学习交流、工作成长、生活分享等多元场景需求&#xff0c;用最新鲜的内容&#xff0c;最好玩的互动&#xff0c;与你一起共创最有料有趣的技术人交流圈&#xff5e; 最有料有趣交流圈在这里你可以畅…

oracle中常用的一些函数,巧用函数,减少代码量

1.sign(x) 【功能】参数x为数值型或者字符串数值型&#xff0c;若为正值返回1,负值回-1&#xff0c;0返回0 2.floor(x) 【功能】返回小于等于x的最大整数值 3.ceil(x) 【功能】返回大于等于x的最小整数值 4.round(x[,y]) 【功能】返回四舍五入后的值 参数x,y数字型表达式,…

微信小程序(四)页面跳转

注释很详细&#xff0c;直接上代码 上一篇 新增内容 1.相对路径页面跳转 2. 绝对路径页面跳转 index.wxml <!-- navigator是块级元素&#xff0c;占一整行 --> <!-- 页面跳转url&#xff0c;相对路径 --> <navigator url"../logs/logs"><butto…

MySQL主从复制原理与实践:从配置到故障监控

文章目录 前言主从复制原理复制源主节点的工作从节点的工作复制流程的设计 主从复制环境搭建一、主从节点配置二、从节点开启复制步骤1、备份主节点的数据2、将数据同步到从节点3、从节点复制参数配置 三、验证复制环境 主从复制故障监控监控主从复制状态监控主从复制延迟 总结…

Improving Generative Modelling in VAEs Using Multimodal Prior

local representation vector r&#xff0c; ϵ \epsilon ϵ is i.i.d Gaussian 额外信息 作者未提供代码

【机组】时序与启停实验的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 &#x1f33a;一、 实验目…

ABAP IDOC 相关报表

上代码 干货来了 *&---------------------------------------------------------------------* *& REPORT ZRPT_FI_IDOC_R *& *&---------------------------------------------------------------------* *& author ABAP02 *& date 20240103 *&…

K8S-YAML

一、Kubernetes对象的描述 kubernetes中资源可以使用YAML描述&#xff08;如果您对YAML格式不了解&#xff0c;可以参考YAML语法&#xff09;&#xff0c;也可以使用JSON。其内容可以分为如下四个部分&#xff1a; typeMeta&#xff1a;对象类型的元信息&#xff0c;声明对象…

共同学习|Spring Cloud Alibaba一一Nacos介绍

接着上篇我们介绍的Spring Cloud Alibaba&#xff0c;下面来继续学习构建云原生应用的动态服务发现、配置管理和服务管理平台——Nacos介绍。 共同学习|Spring Cloud Alibaba一一简介篇-CSDN博客 3、Nacos介绍 Redirecting to: https://nacos.io/ 什么是nacos&#xff1f; …

高级RAG(八): 自动合并检索(Auto-merging Retrieval)

自动合并检索(Auto-merging Retrieval)是LlamaIndex的另外一种高级RAG技术&#xff0c;它有点类似与我们之间介绍的从小到大的检索&#xff0c;不过自动合并检索要比“从小到大的检索”稍微复杂一些&#xff0c;它首先将文档按一定的层次结构进行切割&#xff0c;然后在检索的时…

Docker 安装 MongoDb4

Docker 安装mongoDb 获取mongodb安装问题汇总参考 获取mongodb 注意&#xff1a; WARNING: MongoDB 5.0 requires a CPU with AVX support, and your current system does not appear to have that! **hub官网&#xff08;需要梯子&#xff09;&#xff1a;**https://hub.dock…

医生都是越老越吃香,为啥程序员却不是?

知乎上有个问题&#xff1a;明明是工作经验越久越吃香&#xff0c;为什么程序员却不是&#xff1f; 仔细一想&#xff0c;好像确实是这样啊。 你们看&#xff0c;大家去医院挂号的时候&#xff0c;都喜欢挂年纪大一点的医生&#xff0c;因为年纪大的经验更丰富。 我们装修选设…

常见问答解析:人工智能在智能时代的潜力与挑战

在智能时代&#xff0c;人工智能&#xff08;AI&#xff09;被视为推动社会和科技进步的关键引擎。让我们通过问答的形式&#xff0c;深入探讨人工智能的潜力与面临的挑战。 问&#xff1a;人工智能在当前社会中扮演什么角色&#xff1f; 答&#xff1a;人工智能已成为现代科技…

Java-NIO篇章(4)——Selector选择器详解

Selector介绍 选择器&#xff08;Selector&#xff09;是什么呢&#xff1f;选择器和通道的关系又是什么&#xff1f;这里详细说明&#xff0c;假设不用选择器&#xff0c;那么一个客户端请求数据传输那就需要建立一个连接&#xff0c;为了避免线程阻塞&#xff0c;那么每个客…

一文说明白 MySQL 的 ACID 和 几种日志的关系

1、简介 我们对于MySQL 很熟悉&#xff0c;关于其特性都有一定的了解&#xff0c;但是关于一些具体的实现原理&#xff0c;有的小伙伴可能不太熟悉&#xff0c;而且这部分知识在我们互联网大厂面试中是经常涉及的&#xff0c;因此&#xff0c;本文将带你深入底层&#xff0c;顺…