我这里的概念/语言可能完全错误,所以如果我的想法是错误的,请随时指出我正确的方向。
我使用一款不再受支持的家庭流媒体应用,名为 StreamBaby。它旨在通过 LAN 向较旧的 TiVo DVR 进行流媒体传输,我非常喜欢它,所以仍然每天使用它。它是 Java,执行时会打开一个终端(我在 macOS 上运行它)并运行一个简单的脚本,然后启动 Java,我想它应该会执行它正在执行的操作。
不幸的是,该应用程序几乎每天都会在某个时候崩溃(我在终端中看到异常),我不得不从终端关闭它并重新启动它。这些崩溃没有规律,我猜它只是有一些错误。我希望发生的事情是,如果它崩溃,那么脚本就会终止并重新启动,因为无论如何我手动使用它所做的就是这些。主要原因是我的妻子经常通过这个看她的节目,当它崩溃时,她不得不要求我“修复它”,而我并不总是有空这样做。希望它只是重新启动。
有没有办法通过终端中包含的批处理脚本执行此操作?该脚本是:
@echo off
set LAUNCHDIR="%CD%"
pushd "%~dp0\native"
java -Djava.net.preferIPv4Stack=true -Xmx256m -Xmx256m -jar "%~dp0/jbin/streambaby.jar" %1 %2 %3 %4 %5 %6 %7 %8
echo Exited.
pause
popd
或者这是否需要 Java 应用程序本身具有这种异常处理?
编辑:这是崩溃时的错误示例:
WARNING: run() exception
java.io.IOException: No route to host (sendto failed)
at java.net.PlainDatagramSocketImpl.send(Native Method)
at java.net.DatagramSocket.send(DatagramSocket.java:693)
at javax.jmdns.impl.JmDNSImpl.send(JmDNSImpl.java:1130)
at javax.jmdns.impl.tasks.Responder.run(Responder.java:279)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
我不知道它是否总是相同的,但它始终是run() exception
。
答案1
每种脚本语言都支持的最简单的解决方案是循环。只需一直重复调用即可java
:
@echo off
set LAUNCHDIR="%CD%"
pushd "%~dp0\native"
:start
java -Djava.net.preferIPv4Stack=true -Xmx256m -Xmx256m -jar "%~dp0/jbin/streambaby.jar" %1 %2 %3 %4 %5 %6 %7 %8
echo Exited.
goto start
pause
popd
更复杂的解决方案可能涉及将应用程序作为服务运行。如果服务崩溃,服务管理器可以简单地重新启动服务。在 Linux 上,这相对容易,但在 Windows 上,程序必须符合特殊接口。幸运的是,有国家安全监测中心,一个服务包装工具。它可用于将此 Java 应用程序作为 Windows 服务运行。
当然,如果您对手动启动媒体服务器并拥有控制台窗口感到满意,那么也可以。
答案2
有多种可能的方法:
方法 1:
@DanielB 确实给出了最简单的答案。在脚本周围添加一个循环。
方法 2:
更优雅的方法是制作一个 launchd .plist 文件并将其放入(而不是链接进去)/Library/LaunchDaemons
。
基本.plist
形式如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd >
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.jvc.streambaby</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/your/script</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardErrorPath</key>
<string>/dev/null</string>
<key>StandardOutPath</key>
<string>/dev/null</string>
</dict>
</plist>
当然,sudo launchctl load -w /Library/LaunchDaemons/com.jvc.streambaby.plist
如果您已将 .plist 文件命名为,则可以使用 来加载 plist !com.jvc.streambaby.plist
注意:/dev/null
如果您需要将错误和输出流保存为日志,请更改为某些真实文件路径。
方法 3:
最令人厌恶和直观的方法是使用 Automator 来执行脚本。其中肯定有一种方法可以重新启动脚本。但是,既然可以编写脚本,谁还会使用 GUI 呢!
可能还有其他方法,但是我不是 Apple 用户,所以不知道。
方法 4:
这是一个小脚本:
一定要改变变量,特别是streambaby根目录,并用python3运行它(在linux上使用Python 3.9.x制作)。
#!/usr/bin/env python3
# set the path to the streambaby directory
# do not add a trailing slash (/)
streambaby_home = "/home/<username>/streambaby"
import subprocess
import signal
import sys
import os
from pathlib import Path
# variable to keep track if the app should restart
# True by default
should_run = True
# everything should be made visible immediately
os.environ["PYTHONUNBUFFERED"] = "1"
# copy the current environment
proc_env = os.environ.copy()
# is this really necessary, but was in the batch file so....
proc_env["LAUNCHDIR"] = str(Path.home())
# split the whole command into a list
exec_cmd = [
'java',
'-Djava.net.preferIPv4Stack=true',
'-Xmx256m', # don't know why this argument is added twice
'-Xmx256m',
'-jar',
'{0}/jbin/streambaby.jar'.format(streambaby_home)
]
# put the subprocess execution in a function for cleanliness
# return the process handle
def runapp(cmd, env):
return subprocess.Popen(
cmd, # the command to execute
env=env, # setup the environment variables
shell=False, # not using a shell, change to True if doesn't work
stdout = subprocess.PIPE, # pipe the console output so we can read it
stderr = subprocess.STDOUT, # somethings pop up on stderr, we send them to stdout anyway
start_new_session=True # using a session makes the whole set of processes easier to kill
# works only on *nix type systems
)
# now we start the program in a loop and poll() the hell out of it
while should_run:
proc = runapp(exec_cmd, proc_env)
while True:
stdout_string = str(proc.stdout.readline())
if proc.poll() is not None: # poll returns None while the process is executing
break
if stdout_string != '':
print(stdout_string) # if someone wants to see the output anyway
if "run() exception" in stdout_string.strip(): # only checking for those 2 words
# we have hit a snag; kill it
# ...(evil laugh)...
# SIGKILL is my favorite type of signal
# sounds badass
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
elif " Could not create the Java Virtual Machine" in stdout_string:
# if the Java VM fails to start, there is no point in trying anymore
should_run = False
# get the return code
return_code = proc.poll()
print("The process exited with exit_code ( {0} )".format(return_code))
# a normal process which terminates properly should return 0
# I think we may need to change this
# don't know what streambaby should be returning
if return_code == 0 and should_run:
# if the process exits normally, don't restart the process
should_run = False