美文网首页
web端作业控制系统简易实现

web端作业控制系统简易实现

作者: 尽情的嘲笑我吧 | 来源:发表于2018-03-12 15:48 被阅读0次
    服务一览

    [toc]
    如上图所示,这篇文章基于此图进行实现。其功能就是,可以在WEB端对后台的一些服务进行控制。最初的想法来自于近期将要做的毕设项目。

    因为是爬虫程序,所以有可能会长期跑一些脚本,又不能随时带着电脑(阿里云APP上倒是有一个ssh工具,很方便),所以做成一个web服务,这样直接用浏览器就可以简单的完成一些操作了。

    项目结构

    因为类似的服务,有点绕。所以简单的分下层,理解起来会更容易点。其实说白了,就是一个带有了WEB服务的IPC实现。具体的流程为:
    客户端通过web请求,调用到服务脚本执行对应的信号处理,完成对目标脚本的控制

    下面展示下目录结构,方便理解:

    developer@aliyun:~/control$ tree
    .
    ├── api.py  # flask 服务相关
    ├── control.sh  # 信号处理相关 
    ├── father.log  # 日志文件
    ├── get-process-status.sh # 脚本运行状态相关
    ├── jobs.list    # 可操作的服务列表
    ├── log          # 日志文件,测试用
    ├── py.log       # 日志文件,测试用
    ├── scripts      # 存放服务脚本的目录
    │   ├── testforbash.sh  # 测试bash脚本
    │   ├── testforphp.php  # 测试PHP脚本
    │   └── testforpy.py    # 测试Python脚本
    ├── start-program.py     # 被control.sh调用,根据拓展名找到对应解释器并开启服务
    ├── static  # flask 默认静态文件目录
    │   ├── css
    │   │   ├── detail.css
    │   │   └── index.css
    │   ├── imgs
    │   │   ├── background.jpg
    │   │   ├── favicon.ico
    │   │   └── header.jpg
    │   └── js
    │       ├── detail.js
    │       ├── index.js
    │       └── jquery-2.2.4.min.js
    └── templates  # flask 默认模板目录
        └── index.html
    
    

    代码段

    下面按照上述目录结构,贴一下代码。代码都很简单,了解了流程之后也就不难理解了。这里不再做过多的赘述。

    api.py

    developer@aliyun:~/control$ cat api.py
    #!/usr/bin python
    # coding: utf8
    import sys
    reload(sys)
    sys.setdefaultencoding("utf8")
    import os
    import commands
    import json
    from flask import Flask,request,render_template
    # for easy usage, just all in. begin
    # 获取可以控制的脚本列表
    def get_programs():
        with open("./jobs.list", "r") as file:
            programs = file.readlines()
            file.close()
        return [line.strip("\n") for line in programs]
    # 根据脚本名称获取当前脚本的执行状态
    def get_status_by_name(program=""):
        cmd = "bash get-process-status.sh {}".format(program)
        status, output = commands.getstatusoutput(cmd)
        return status, output
    
    # 控制脚本执行,暂停,或者杀死
    def control_program(program="", command=""):
        cmd = "bash control.sh {} {} &".format(program, command)
        return os.system(cmd)
    
    # end
    app = Flask(__name__)
    
    @app.route("/")
    def index():
        return render_template("index.html")
    
    @app.route("/program")
    def program():
        res = {"data":[], "code":-1}
        try:
            programs = get_programs()
            res = {"data": programs, "code":0}
        except exception as e:
            res = {"data": e, "code":-1}
        return json.dumps(res)
    @app.route("/status", methods=["POST", "GET"])
    def status():
        params = {}
        if request.method == "POST":
            params = request.form.to_dict()
        elif request.method == "GET":
            params = request.args.to_dict()
        else:
            params = {}
        program = params.get("program", "")
        status = get_status_by_name(program)
        return json.dumps({"data":status})
    
    @app.route("/control", methods=["POST", "GET"])
    def control():
        params = {}
        if request.method == "POST":
            params = request.form.to_dict()
        elif request.method == "GET":
            params = request.args.to_dict()
        else:
            pass
        program = params.get("program")
        command = params.get("command")
        print program, "==", command
        res = control_program(program, command)
        return render_template("index.html")
    
    if __name__ == "__main__":
        #print get_programs()
        #program = "/home/developer/control/scripts/testforphp.php"
        #print get_status_by_name(program)
        #print control_program(program, "start")
        #print get_status_by_name(program)
        app.run(host="0.0.0.0", port=9999, debug=True)
    

    control.sh

    developer@aliyun:~/control$ cat control.sh 
    #!/usr/bin bash
    # 获取$1 内容对应的进程id
    echo "命令为" $1
    PID=$(ps aux | grep -v grep | grep $1 | awk '{print $2}'| head -1)
    echo "捕获的进程号为" $PID "具体的命令为" $2
    # 根据$2参数来控制进程
    if [[ $2 == "stop" ]];then
        echo "暂停" $1
        kill -SIGSTOP $PID
    elif [[ $2 == "resume" ]];then
        echo "继续" $1
        kill -SIGCONT $PID
    elif [[ $2 == "kill" ]];then
        echo "终止" $PID
        kill -9 $PID
    elif [[ $2 == "start" ]];then
        #echo "可以交给python脚本根据对应的拓展名来开启对应的服务"
        echo "正在开启脚本..."
        /usr/bin/python /home/developer/control/start-program.py $1 > /home/developer/control/father.log
    else
        echo "暂不支持的操作~"
    fi
    echo "OVER!"
    
    

    get-process-status.sh

    developer@aliyun:~/control$ cat get-process-status.sh 
    #!/usr/bin bash
    
    PROGRAM=$1
    STATUS=$(ps aux | grep -v grep| grep $PROGRAM | head -1 | awk '{print $8}')
    echo $STATUS
    exit
    if [[ $STATUS == "T"  ]];then
        echo $PROGRAM is Stopping...
    elif [[ $STATUS == "S"  ]];then
        echo $PROGRAM is S
    elif [[ $STATUS == "R" ]];then
        echo $PROGRAM is Running.
    else
        echo $PROGRAM is $STATUS
    fi
    
    

    jobs.list

    developer@aliyun:~/control$ cat jobs.list 
    /home/developer/control/scripts/testforpy.py
    /home/developer/control/script/testforbash.sh
    /home/developer/control/scripts/testforphp.php
    

    scripts

    这个目录用于拓展服务的列表,需要添加新的可控脚本的话,只需要在这个文件夹和jobs.list下进行添加即可。

    testforbash.sh

    developer@aliyun:~/control$ cat scripts/testforbash.sh 
    #!/usr/bin bash
    for ((i=1; i<10;i++))
    do
        echo $i "carried."
        sleep 3
    done
    

    testforphp.php

    developer@aliyun:~/control$ cat scripts/testforphp.php 
    <?php
    
    while(true) {
        $date = date("Y-m-d H:i:s");
        echo "PHP 当前时间是[{$date}].\n";
        sleep(10);
    }
    

    testforpy.py

    developer@aliyun:~/control$ cat scripts/testforpy.py 
    #!/usr/bin python
    #coding: utf8
    import sys
    import time
    reload(sys)
    sys.setdefaultencoding("utf8")
    index = 0
    while True:
        with open("/home/developer/control/py.log", "a") as file:
            content = str("current time is: "+str(time.ctime())+"!\n")
            file.write(content)
            time.sleep(10)
            index+=1
            if index==10:
                break
            file.close()
    
    

    start-program.py

    developer@aliyun:~/control$ cat start-program.py 
    #!/usr/bin python
    #coding: utf8
    import sys
    reload(sys)
    sys.setdefaultencoding("utf8")
    import os
    # 也可以使用subprocess库,这样效果貌似会更好
    path = str(sys.argv[1])
    print(path)
    # 默认使用最后一个拓展名
    extension = path.split(".")[-1]
    # 根据拓展名找到对应的处理器
    mapping = {
            "php": "php",
            "py": "python",
            "sh": "bash"
            }
    exts = ['php', 'py', 'sh']
    print("extension is: " + extension)
    print("commander is:" + mapping.get(extension))
    if extension in exts:
        cmd = "{} {}  >> log &".format(mapping.get(extension), path)
        print(cmd)
        os.system(cmd)
    else:
        print("不支持的控制脚本")
    
    
    

    index.js

    developer@aliyun:~/control$ cat static/js/index.js 
    $(document).ready(function(){
        loading_data_for_selector($("#programs"));
        get_program_status($("#programs"), $("#previewer"));
        //commit_command($("#btn_click"), $("#programs"), $("#command"))
    });
    
    
    function commit_command(btn, programs, commander) {
        btn.click(function(){
            var program = "";
            var command = "";
            programs.change(function(){
                program = $(this).children("option:selected").val();
            });
            commander.change(function(){
                command = $(this).children("option:selected").val();
            });
            console.log(program+"<>"+command)
            $.ajax({
                url: "/control",
                method: "POST",
                dataType: "JSON",
                data: {program: program, command: command},
                success: function(res) {
                    console.log(res);
                },
                error: function(err) {
                    console.log(err);
                }
            });
        });
    }
    
    function loading_data_for_selector(obj){
        $.ajax({
            url: "/program",
            method: "GET",
            dataType: "JSON",
            success: function(res) {
                console.log(res);
                var data = res.data;
                for(var index=0; index<data.length; index++) {
                    var child = "<option value='"+data[index]+"'>"+data[index]+"</value>";
                    obj.append(child);
                }
            },
            error: function(err) {
                console.log(err);
            }
        });
    }
    
    /**
     * 当selector的选中值发生变化的时候,就会触发对应的请求,用于及时将
     * 脚本的运行状态显示到页面上。
     */
    function get_program_status(obj, previewer) {
        obj.change(function(){
            previewer.children().remove();
            var value = $(this).children("option:selected").val();
            $.ajax({
                url: "/status",
                method: "POST",
                dataType: "JSON",
                data: {program: value},
                success: function(res) {
                    console.log(res);
                    var data = res.data[1];
                    var child = "<p><code>"+value+"</code>的运行状态为"+data+"</p>";
                    previewer.append(child);
                },
                error: function(err){
                    console.log(err);
                }
            });
        });
    
    

    index.html

    developer@aliyun:~/control$ cat templates/index.html 
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>web端作业控制系统</title>
        <script type="text/javascript" src="/static/js/jquery-2.2.4.min.js"></script>
        <script type="text/javascript" src="/static/js/index.js"></script>
        <link rel="stylesheet" type="text/css" href="/static/css/index.css">
    </head>
    <body>
    <div class="container">
        <div class="operator">
            <form action="/control" method="POST">
            <fieldset>
                <legend>填写相关操作</legend>
                <select id="programs" name="program"></select>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                <select id="command" name="command">
                    <option value="start">开启</option>
                    <option value="stop">暂停</option>
                    <option value="resume">继续</option>
                    <option value="kill">杀死</option>
                </select>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                <input type="submit" id="btn_click" name="" value="提交">
            </fieldset>
        </form>
        </div>
        <div id="previewer" ="preview">
            
        </div>
    </div>
    </body>
    </html>
    

    到此,核心的文件都已经罗列完毕了。虽然功能上没什么问题了,但是原生的页面看起来确实不能让人有赏心悦目的感觉。有兴趣的话可以在此基础上进行优化,写一个更好看的页面。

    最终效果

    开启web服务

    开启web服务

    查看可控脚本以及可用操作

    查看有哪些脚本可被控制

    在web页面开启一个测试的服务脚本

    开启一个测试服务

    在web页面暂停一个测试服务脚本

    在web页面暂停一个正在运行的脚本

    确认脚本真的是暂停了。


    实际验证,脚本真的是被暂停住了

    继续之前暂停了的服务

    继续之前暂停了的服务

    终止一个服务

    终止一个服务

    下拉框选中后会出现当前脚本的服务状态

    下拉框选中事件下的脚本状态更新

    总结

    到此,这个在web页面控制服务行为的需求,马马虎虎算是做完了。但是不得不说,界面有点难看。在完成这个小工具的过程中,也算是遇到了各种各样的坑。比如:

    • python文件写操作锁没释放的
    • Bashsh某些命令不兼容的
    • flask请求参数一致性保证的问题

    等等吧。

    虽然看起来这个小工具,用到的语言特别乱,也特别杂,但大部分代码都是很简单的。从最上面的那张图入手,相信理解起来会更容易一点吧。

    相关文章

      网友评论

          本文标题:web端作业控制系统简易实现

          本文链接:https://www.haomeiwen.com/subject/cngnfftx.html