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等需要的命令

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Peter:经济形势不好,一个最大的原因就是诚信道德的缺失 | 程客有话说002

《程客有话说》是我们最新推出的一个访谈栏目&#xff0c;邀请了一些国内外有趣的程序员来分享他们的经验、观点与成长故事&#xff0c;我们尝试建立一个程序员交流与学习的平台&#xff0c;也欢迎大家推荐朋友或自己来参加我们的节目&#xff0c;一起加油。本期我们邀请的程序…

实验一 安装和使用Oracle数据库

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

基于人工蚁群、蚁群、遗传算法的多目标任务分配

matlab2020a可运行 基于人工蚁群、蚁群、遗传算法的多目标任务分配资源-CSDN文库

MAC磁盘空间不足怎么清理?MAC清理磁盘空间的五种方法

MAC磁盘空间不足怎么清理&#xff1f;当我们使用苹果MAC一段时间后&#xff0c;就会有大量的垃圾文件占用磁盘空间&#xff0c;例如系统缓存文件、应用程序缓存文件、备份和重复文件、旧版的应用程序及其部件等&#xff0c;为了不影响电脑的后续使用&#xff0c;我们需要经常清…

对java的interface的理解

一个例子来让我们理解更加深刻 这是我们的整体文件布局 ①A是接口 ②B和C是用来实现接口的类 ③show是我们的运行函数&#xff0c;用来展示 A接口 接口中定义的方法可以不用去实现,用其他类去实现(必须实现) 关键字:interface public interface A { // public static …

恭喜所有纺织人,你最想要的小程序来了

随着互联网的普及和电子商务的快速发展&#xff0c;越来越多的商家开始涉足线上销售。而小程序商城作为一种轻量级的应用程序&#xff0c;正逐渐成为商家们热衷选择的销售平台。本文将通过实用指南的形式&#xff0c;为商家们详细介绍如何通过乔拓云网后台&#xff0c;自助搭建…

C语言:预处理详解

创作不易&#xff0c;来个三连呗&#xff01; 一、预定义符号 C语⾔设置了⼀些预定义符号&#xff0c;可以直接使⽤&#xff0c;预定义符号也是在预处理期间处理的。 __FILE__ //进⾏编译的源⽂件 __LINE__ //⽂件当前的⾏号 __DATE__ //⽂件被编译的⽇期 __TIME__ //⽂件被编…

【前后端的那些事】评论功能实现

文章目录 聊天模块1. 数据库表2. 后端初始化2.1 controller2.2 service2.3 dao2.4 mapper 3. 前端初始化3.1 路由创建3.2 目录创建3.3 tailwindCSS安装 4. tailwindUI5. 前端代码编写 前言&#xff1a;最近写项目&#xff0c;发现了一些很有意思的功能&#xff0c;想写文章&…

最新 生成pdf文字和表格

生成pdf文字和表格 先看效果 介绍 java项目&#xff0c;使用apache的pdfbox工具&#xff0c;可分页&#xff0c;自定义列 依赖 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.22<…

css 前端实现通过css动画实现进度条动态加载效果

效果图 代码 CommonProcess.vue 进度条动态加载组件代码 <!-- 进度条组件 --> <template><div class"common_process"><div v-for"(item, index) in dataList" :key"processType index" class"common_process_item…

SPI传感器接口设计与优化:基于STM32的实践

SPI&#xff08;串行外设接口&#xff09;是一种常用的串行通信协议&#xff0c;用于在微控制器和外部设备之间进行全双工的高速数据传输。在本文中&#xff0c;我们将探讨如何基于STM32微控制器设计和优化SPI传感器接口&#xff0c;并提供相应的代码示例。 1. SPI传感器接口设…

首届PolarDB开发者大会在京举办,阿里云李飞飞:云数据库加速迈向智能化

1月17日&#xff0c;阿里云PolarDB开发者大会在京举办&#xff0c;中国首款自研云原生数据库PolarDB发布“三层分离”新版本&#xff0c;基于智能决策实现查询性能10倍提升、节省50%成本。此外&#xff0c;阿里云全新推出数据库场景体验馆、训练营等系列新举措&#xff0c;广大…

【openwrt】【overlayfs】Openwrt系统overlayfs挂载流程

overlayfs是一种叠加文件系统&#xff0c;在openwrt和安卓系统中都有很广泛的应用&#xff0c;overlayfs通常用于将只读根文件系统(rootfs)和可写文件系统(jffs2)进行叠加后形成一个新的文件系统&#xff0c;这个新的文件系统“看起来”是可读写的&#xff0c;这种做法的好处是…

汽车美容行业研究:预计2029年将达到127亿美元

车体保养又习惯称汽车美容。主要目的是清除车体外和车体内的各种氧化和腐蚀&#xff0c;污染物等。然后加以保护&#xff0c;尽量突出车的“美”。它主要包括&#xff1a;车漆保养&#xff0c;内饰保养&#xff0c;电镀加工保养&#xff0c;皮革塑料保养&#xff0c;轮胎、轮毂…

UE C++打印文本的两种方式

UE C打印文本的两种方式 第一种方式UE_LOG打印打印出的内容在**输出日志**中显示 第二种方式GEngine->AddOnScreenDebugMessage打印&#xff08;常用&#xff09;打印出的内容在**控制台**中显示 第一种方式UE_LOG打印 官方文档&#xff1a; https://docs.unrealengine.com…

1、node.js安装

文章目录 node.js下载及安装node.js安装验证node执行js代码 node.js下载及安装 https://nodejs.org/en 访问官网&#xff0c;下载LTS版本 下载完成后&#xff0c;双击安装&#xff0c;安装过程基本不用动什么&#xff0c;包括盘符也尽量不要改。 node.js安装验证 cmd运行nod…

大师学SwiftUI第6章 - 声明式用户界面 Part 4

步进器视图 ​​Stepper​​视图创建一个带递增和递减按钮的控件。该结构体提供了多个初始化方法&#xff0c;包含不同的配置参数组合。以下是最常用的一部分。 Stepper(String, value: Binding, in: Range, step: Float, onEditingChanged: Closure)&#xff1a;此初始化方法…

Tuxera NTFS2024下载使用详细操作教程

你是否还在为Mac不能正常读写NTFS格式分区而感到苦恼呢&#xff1f;想要适合Mac系统使用来回转换磁盘格式又十分麻烦&#xff0c;这该怎么办呢&#xff0c;有了这款软件Tuxera ntfs就能马上帮你解决目前遇到的问题。 Tuxera NTFS2024最新免费版下载如下&#xff1a; https://…

如何给新华网投稿发稿?新华网的媒体发稿方法步骤

现如今&#xff0c;互联网已经成为了人们获取信息的主要途径&#xff0c;各大媒体网站也成为了发布自己作品的首选平台。其中&#xff0c;新华网作为中国最具影响力的新闻媒体之一&#xff0c;其内容覆盖面广、触及人群众多&#xff0c;因此&#xff0c;能够在新华网上发表文章…

Vue-26、Vue内置指令v-cloak与v-once以及v-pre

1、v-cloak 本质上是一个特殊属性&#xff0c;Vue实例创建完毕并接管容器后&#xff0c;会删掉v-cloak属性使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF…