diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f7740fa --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +# EditorConfig is awesome: https://EditorConfig.org +# top-most EditorConfig file +root = true + +[*] +end_of_line = lf +indent_style = tab +tab_width = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.json] +insert_final_newline = ignore + +[**.{min,bundle}.js] +indent_style = ignore +insert_final_newline = ignore + +[Makefile] +indent_style = tab + +[*.bat] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..302e6db --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +## folders +/node_modules + +## files diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8ed474c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Program", + "type": "node", + "request": "launch", + "program": "${workspaceFolder}/bin/hotnode", + "cwd": "${workspaceFolder}", + "args": ["-a", "./index.js"] + } + ] +} diff --git a/Readme b/Readme deleted file mode 100644 index a57a248..0000000 --- a/Readme +++ /dev/null @@ -1 +0,0 @@ -作业 diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..77f9b67 --- /dev/null +++ b/Readme.md @@ -0,0 +1,28 @@ +# tasts + +1,写一个 web 服务,提供以下服务: +GET 接口 `/device?pw=cpu&sort=desc`,返回: + +```js +{ + cpu:[], // 服务器cpu状态 + mem:[], // 服务器内存状态 + proc:[] // 全量进程列表(默认按cpu和降序排列) +} +``` + +2,写一个守护进程启动器: +同目录下写一个 bin/hotnode 最终通过`hotnode app.js`启动服务。 +在 app.js 文件变更后自动重启 app.js 进程 + +## 接口 + +## 守护进程启动器 + +### 参数设计: + +```cmd +// 参数说明 +-a, --application // 应用文件路径, "./index.js" +-w, --watch // 监听变化路径, "./"); +``` diff --git a/bin/hotloader.js b/bin/hotloader.js new file mode 100644 index 0000000..9cc1b85 --- /dev/null +++ b/bin/hotloader.js @@ -0,0 +1,52 @@ +const fs = require("fs"); +const path = require("path"); +const { spawn, exec } = require("child_process"); +const chokidar = require("chokidar"); + +class HotLoader { + static getInstance() { + if (!HotLoader.instance) { + HotLoader.instance = new HotLoader(); + } + return HotLoader.instance; + } + constructor() {} + + run(program) { + this.commander = program; + const watcher = chokidar + .watch(".", { ignored: /(^|[\/\\])\../, atomic: 500 }) + .on("change", filepath => { + console.log( + "these files has been changed:\n", + path.resolve(__dirname, filepath), + "\n" + ); + this.restartProcess(); + }); + this.startProgress(); + } + + startProgress() { + this.process = exec(`node ${this.commander.application}`); + this.process.stdout.on("data", data => { + console.log(data.toString()); + }); + this.process.stderr.on("data", data => { + console.log(data.toString()); + }); + } + + restartProcess() { + if (this.process !== null) { + try { + this.process.kill("SIGKILL"); + } catch (error) { + console.log(error.message); + } + } + this.startProgress(); + } +} + +module.exports = HotLoader.getInstance(); diff --git a/bin/hotnode b/bin/hotnode new file mode 100755 index 0000000..f201623 --- /dev/null +++ b/bin/hotnode @@ -0,0 +1,23 @@ +#!/usr/bin/env node + +const fs = require("fs"); +const path = require("path"); +const commander = require("commander"); +const hotnode = require("./hotloader.js"); + +const program = new commander.Command(); + +let configData = { + application: "./index1.js", + config: "./.hotnoderc", + watch: "./" +}; + +program + .version("0.0.1") + .option("-a, --application ", "应用文件路径", configData.application) + .option("-w, --watch ", "监听变化路径", configData.watch); + +program.parse(process.argv); + +hotnode.run(program); diff --git a/config.js b/config.js new file mode 100644 index 0000000..c8bdfb3 --- /dev/null +++ b/config.js @@ -0,0 +1,13 @@ +module.exports = { + // 服务器配置 + server: { + host: "127.0.0.1", + port: 3000 + }, + // 默认参数 + query: { + sort: "asce", // Asce升序,desc降序 + keyword: "cpu", // cpu/mem/pid/start + len: 10 // 显示条数 + } +}; diff --git a/device.js b/device.js new file mode 100644 index 0000000..e5bd49e --- /dev/null +++ b/device.js @@ -0,0 +1,68 @@ +const os = require("os"); +const util = require("util"); +const querystring = require("querystring"); +const exec = util.promisify(require("child_process").exec); + +/* 获取系统信息 */ +const getData = async function(opt) { + const cpu = await getInfo("cpu"), + mem = await getInfo("mem"), + proc = await getInfo("proc", opt); + + return { + cpu, + mem, + proc + }; +}; + +const getInfo = async function(type, opt) { + const command = getCommand(type, opt); + const { stdout, stderr } = await exec(command); + if (stderr) throw stderr; + return stdout.split("\n").slice(1, -1); +}; + +const getCommand = function(type, opt) { + // 非linux系统时,命令暂时瞎写 + const commands = + os.platform() == "linux" + ? { + cpu: "cat /proc/cpuinfo", + mem: "cat /proc/meminfo" + } + : { + cpu: "ls -l", + mem: "ls -al", + proc: "ps aux | head -n 5" + }; + + if (os.platform() == "linux" && type === "proc") + commands.proc = `ps aux --sort=${opt.sort === "asce" ? "" : "-"}%${ + opt.keyword + } | head -n ${opt.len - 0 + 1}`; + + return commands[type]; +}; + +const requestParse = function(req) { + const [host, port] = req.headers.host.split(":"), + urlObj = req.url.indexOf("?") === -1 ? [req.url, null] : req.url.split("?"), + urlPath = /.+\/$/.test(urlObj[0]) + ? urlObj[0].substr(0, urlObj[0].length - 1) + : urlObj[0], + urlQuery = urlObj[1] === null ? {} : querystring.parse(urlObj[1]); + + return { + method: req.headers.method, + host: host[0], + port: port[1] || 80, + path: urlPath, + query: urlQuery + }; +}; + +module.exports = { + getData, + requestParse +}; diff --git a/index.js b/index.js new file mode 100644 index 0000000..0a1b670 --- /dev/null +++ b/index.js @@ -0,0 +1,30 @@ +const http = require("http"); +const config = require("./config.js"); +const device = require("./device.js"); + +http + .createServer(async (req, res) => { + const parsedReq = device.requestParse(req), + query = Object.assign({}, config.query, parsedReq.query); + let data = {}; + + if (parsedReq.path === "/device") { + res.statusCode = 200; + res.setHeader("content-type", "application/json"); + data = await device.getData(query); + try { + data = JSON.stringify(data); + } catch (err) { + data = "{}"; + } + res.end(data); + } + + res.statusCode = 404; + res.setHeader("content-type", "text/html"); + res.end("

404

"); + }) + .listen(config.server.port, config.server.host, () => { + const svr = config.server; + console.log(`server is running at ${svr.host} : ${svr.port}`); + }); diff --git a/package.json b/package.json new file mode 100644 index 0000000..904a500 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "homework-nodejs", + "version": "1.0.0", + "description": "练习写一个接口和一个守护进程", + "scripts": { + "start": "node ./index.js", + "debug": "node --inspect-brk ./index.js", + "hot": "./bin/hotnode -a ./index.js -w ./" + }, + "repository": { + "type": "git", + "url": "git@github.com:qyingkou/homework-nodejs.git" + }, + "dependencies": { + "chokidar": "^3.0.2", + "commander": "^2.20.0" + } +}