Docker-Java-Api操控Docker,并向容器中的程序传递参数(标准输入)
目录
问题描述
如果我有一个程序,运行后会从标准输入中获取参数,例如:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int a = Integer.parseInt(scan.next());
int b = Integer.parseInt(scan.next());
System.out.println((a + b));
}
}
在正常情况下,我们只需要执行以下命令:
javac Main.java
java Main
程序就会在当前的终端执行上述代码对应的字节码(字节码执行过程由JVM
负责),我们输入的内容就会传递给该程序。
可是,当上述字节码文件在一个容器中,我们需要使用Java
连接Docker
且我们的输入也由Java
提供时,该如何实现?
两种方式可以实现。
解决方案一
借助echo
命令重定向:
@Test
void testDefault() throws InterruptedException {
final String DOCKER_HOST = "192.168.1.1";
final int DOCKER_PORT = 2375;
String serverUrl = String.format("tcp://%s:%d", DOCKER_HOST, DOCKER_PORT);
DockerClient dockerClient = DockerClientBuilder.getInstance(serverUrl).build();
// 容器ID
String containerId = "613ed37d5346";
// 模拟输入
String inputString = "2 99\n";
String[] userCodeCmd = {"sh", "-c", String.format("echo \"%s\" | %s", inputString, "java Main")};
System.out.println(Arrays.stream(userCodeCmd)
.collect(Collectors.joining(" ")));
ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId)
.withAttachStdout(true)
.withAttachStderr(true)
.withAttachStdin(true)
.withCmd(userCodeCmd)
.exec();
// 由于连接容器是一个异步操作,因此要传入回调函数
ExecStartResultCallback execStartResultCallback = new ExecStartResultCallback() {
@Override
public void onNext(Frame frame) {
if (frame.getStreamType() == StreamType.STDERR) {
System.out.println("ERROR -> " + new String(frame.getPayload()));
} else if (frame.getStreamType() == StreamType.STDOUT) {
System.out.println("STDOUT -> " + new String(frame.getPayload()));
}
super.onNext(frame);
}
};
dockerClient.execStartCmd(execCreateCmdResponse.getId())
.exec(execStartResultCallback).awaitCompletion();
}
实际上我们向容器中传递的命令是:
sh -c echo "2 99
" | java Main
这样的方式简单粗暴,但是不是很优雅,这是因为在Linux中,命令行长度有限制,在Linux系统中,命令行更大长度的限制是由内核参数设置的,通常情况下,这个限制的默认值是4096个字符,一旦超过这个上限就会出现错误信息。
解决方案二(推荐)
更好的方式其实是使用流(stream
)的方式,也就是将我们输入的字符串转为输入流,然后直接将该输入流传递给程序,但是默认的连接对象不支持流操作,因此我要换一种方式初始化连接对象:
@Test
void testHttpClient() throws URISyntaxException {
DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();
// 初始化连接
URI uri = new URI("tcp://192.168.1.1:2375");
DockerHttpClient dockerHttpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(uri)
.maxConnections(3000)
.build();
DockerClient dockerClient = DockerClientBuilder.getInstance(config).withDockerHttpClient(dockerHttpClient).build();
// 容器ID
String containerId = "613ed37d5346";
// 进入容器后要执行的命令
String[] cmdArgs = new String[] { "java", "Main"};
ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId)
.withCmd(cmdArgs)
.withAttachStdin(true)
.withAttachStdout(true)
.withAttachStderr(true)
.exec();
System.out.println( execCreateCmdResponse.getRawValues());
// 由于连接容器是一个异步操作,因此要传入回调函数
ExecStartResultCallback execStartResultCallback = new ExecStartResultCallback() {
@Override
public void onNext(Frame frame) {
if (frame.getStreamType() == StreamType.STDERR) {
System.out.println("ERROR -> " + new String(frame.getPayload()));
} else {
System.out.println("STDOUT -> " + new String(frame.getPayload()));
}
super.onNext(frame);
}
};
// 模拟输入
String inputString = "2 5\n";
// 转为输入流
byte[] inputBytes = inputString.getBytes(StandardCharsets.UTF_8);
InputStream inputStream = new ByteArrayInputStream(inputBytes);
// 传入回调函数并执行
try {
dockerClient.execStartCmd(execCreateCmdResponse.getId())
.withStdIn(inputStream)
.exec(execStartResultCallback)
.awaitCompletion();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
参考文献
本文由「黄阿信」创作,创作不易,请多支持。
如果您觉得本文写得不错,那就点一下「赞赏」请我喝杯咖啡~
商业转载请联系作者获得授权,非商业转载请附上原文出处及本链接。
关注公众号,获取最新动态!
历史评论
开始评论