英文:
Get custom status of a running application
问题
我想获取正在运行的应用程序的应用程序状态。例如:
# 启动我的应用程序
./my_app
# 请求状态
./my_app status
# 它打印有关应用程序的信息
等待队列中的作业:120
处理中的作业:3414
已完成的作业:300
收到的错误请求数量:120
[...]
我目前只看到一种实现这个结果的方法:使用外部进程。
外部进程通过文件与主应用程序进行通信(它通过touch
一个新文件,以便主应用程序“知道”它必须准备状态报告),然后当状态报告准备好时,它通过文件发送回外部进程,最后外部进程将状态打印到命令行界面。
但我个人认为这个解决方案不够优雅。
你有其他的想法来实现这个吗?该应用程序是用Golang编写的,也许在Go中有一些技巧可以做到这一点吗?
英文:
I would like to get the application status of a running application. For example:
# start my app
./my_app
# require status
./my_app status
# it prints information about the application
jobs waiting in queue : 120
processing jobs : 3414
jobs done : 300
number of bad request received : 120
[...]
I see for the moment only one option to accomplish this result: using an external process.
The external process "speak" with the main application through a file (it touch
a new file, so the main application "knows" that it must prepare the status report), then when the status report is ready, it send back to the external process (through a file), finally this one print the status to the cli.
But personally I find this solution not so elegant.
Do you have any others ideas to accomplish this? The application is written in Golang, are there maybe any tricks in Go to do this?
答案1
得分: 3
文件系统并不是提供应用程序输入的唯一方式,还有许多其他方式,例如将数据发送到其标准输入,应用程序可以监听一个端口以接收传入连接,或者使用任何其他外设(如麦克风、传感器等)。
由于Go语言内置了一个Web服务器,因此可以方便地利用它:Go应用程序可以启动Web服务器(如果尚未使用),并监听一个端口以接收传入连接,当请求到达时,简单地以纯文本或HTML格式发送状态回复。
以下是一个示例:
package main
import (
"fmt"
"net/http"
)
var (
waiting = 120
processing = 3414
done = 300
)
func ServeStats(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%-22s : %d\n", "jobs waiting in queue", waiting)
fmt.Fprintf(w, "%-22s : %d\n", "processing jobs", processing)
fmt.Fprintf(w, "%-22s : %d\n", "jobs done", done)
}
func main() {
http.HandleFunc("/", ServeStats)
err := http.ListenAndServe("localhost:8081", nil)
if err != nil {
fmt.Println("An instance of the application is already running!",
"Provide the 'status' parameter to see stats.")
}
}
想要获取正在运行的应用程序状态的进程只需发送一个HTTP GET
请求(例如在Linux中使用wget)即可获取状态。上述应用程序将以下状态文本作为响应返回:
jobs waiting in queue : 120
processing jobs : 3414
jobs done : 300
重要提示:
我在地址"localhost:8081"
上启动了Web服务器,这意味着状态只能从本地主机(同一台计算机)访问。如果您的应用程序已经使用了Web服务器,您可以使用它,但请注意,如果它可以从“外部”访问,那么您的状态也将可以被访问到。如果不希望这样,您必须检查请求(客户端)是否来自本地主机,如果不是,则可以拒绝提供服务。
不使用外部进程
查询状态的“外部进程”甚至不需要是真正的外部进程,您可以在应用程序本身中实现它。
使用您的示例:应用程序在启动时可以检查是否存在status
命令行参数,如果存在,则不会继续正常的启动过程,而是执行HTTP GET
以获取其他运行实例的状态,将其打印到控制台,并在此之后退出。
以下是如何实现的。修改后的main()
函数:
func main() {
if len(os.Args) > 1 && os.Args[1] == "status" {
getStatus()
return
}
http.HandleFunc("/", ServeStats)
err := http.ListenAndServe("localhost:8081", nil)
if err != nil {
fmt.Println("An instance of the application is already running!",
"Provide the 'status' parameter to see stats.")
}
}
以及getStatus()
的实现:
func getStatus() {
resp, err := http.Get("http://localhost:8081/")
if err != nil {
fmt.Println("No running instance found!")
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Running instance found but failed to get status!")
return
}
fmt.Println("Status of the running instance:")
fmt.Println(string(body))
}
如果不提供参数(或不是"status"
),应用程序尝试正常启动。
如果提供了参数"status"
,它尝试从正在运行的实例获取状态。如果成功,打印接收到的状态。如果失败,打印未找到正在运行的实例。无论哪种情况,运行在此之后终止。
英文:
The file system is not the only way to provide input for an application, there are many other ways, e.g. sending data to its standard input, the application may listen on a port for incoming connections or using any other peripherals (like microphone, sensors etc.).
Since Go has a built-in webserver, it may be convenient to take advantage of it: the Go app can start the webserver (if it doesn't yet use it), and listen on a port for incoming connections, and when requests come, simply send back the status as simple text or in HTML format.
Here is an example of it:
package main
import (
"fmt"
"net/http"
)
var (
waiting = 120
processing = 3414
done = 300
)
func ServeStats(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%-22s : %d\n", "jobs waiting in queue", waiting)
fmt.Fprintf(w, "%-22s : %d\n", "processing jobs", processing)
fmt.Fprintf(w, "%-22s : %d\n", "jobs done", done)
}
func main() {
http.HandleFunc("/", ServeStats)
err := http.ListenAndServe("localhost:8081", nil)
if err != nil {
fmt.Println("An instance of the application is already running!",
"Provide the 'status' parameter to see stats.")
}
}
The process that wants to get the status of your running app only has to make an HTTP GET
request (e.g. wget in linux) to get the status. The above application serves the following status text as the response:
jobs waiting in queue : 120
processing jobs : 3414
jobs done : 300
Important note:
I started the webserver on address "localhost:8081"
which means the stats are only available from localhost (the same computer). If your app already uses the webserver, you may use that, but note that if it is reachable from the "outside" so will be your stats. If this is unwanted, you must check if the request (client) is originating from localhost, and if not you can deny serving the request.
Without an External Process
The "external process" that queries the status doesn't even have to be external, you can implement it in the application itself.
Using your example: the application on startup can check if the status
command line argument is present, and if so, it will not continue the normal startup process but perform the HTTP GET
to get the status of the other running instance of the app, print it to the console and exit right after.
Here's how you can do it. The modified main()
function:
func main() {
if len(os.Args) > 1 && os.Args[1] == "status" {
getStatus()
return
}
http.HandleFunc("/", ServeStats)
err := http.ListenAndServe("localhost:8081", nil)
if err != nil {
fmt.Println("An instance of the application is already running!",
"Provide the 'status' parameter to see stats.")
}
}
And the implementation of getStatus()
:
func getStatus() {
resp, err := http.Get("http://localhost:8081/")
if err != nil {
fmt.Println("No running instance found!")
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Running instance found but failed to get status!")
return
}
fmt.Println("Status of the running instance:")
fmt.Println(string(body))
}
If you provide no arguments (or not the "status"
one), the application tries to start normally.
If you provide the argument "status"
, it tries to get the status from a running instance. If succeeds, prints the received status. If not, prints that there is no running instance found. Either way, run terminates after that.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论