背景
本人安卓framework开发,项目中没人搞后端了。。但是有啥办法呢,那我就撸nodejs吧。嗯,因为有个前端的小demo,最后选择express了来搞。由于项目保密性原因,很多玩意不方便透露,只是在此记录一下日常,以后方便查阅
nodejs环境安装配置
直接去官网下载LTS版本
我的机器是debianx86_64,所以下载了一个tar.xz格式的压缩包
1.下载
wget https://nodejs.org/dist/v16.13.0/node-v16.13.0-linux-x64.tar.xz -o node-v16.13.0-linux-x64.tar.xz
2.解压
tar -jxvf https://nodejs.org/dist/v16.13.0/node-v16.13.0-linux-x64.tar.xz
3.添加环境变量
首先将解压出来的文件夹移动到~/env文件夹
mkdir ~/env
mv node-v16.13.0-linux-x64 ~/env/
然后添加软链接至~/bin
ln -s ~/env/node-v16.13.0-linux-x64/bin ~/bin/mynd
修改~/.bashrc文件
#!bin/bash
MYND=~/bin/mynd
export PATH=${MYND}:~/bin:$PATH
最后在终端执行source ~/.bashrc即可
4.生成express模板demo
参考(https://www.expressjs.com.cn/starter/generator.html)
npm start后开启服务,就可以在浏览器中访问了
liuzhuangzhuang@n227-085-009:~/project/xx/myapp$ DEBUG=myapp:* npm start
> myapp@0.0.0 start
> node ./bin/www
myapp:server Listening on port 8866 +0ms
GET / 200 249.282 ms - 170
GET /stylesheets/style.css 200 4.389 ms - 111
GET /favicon.ico 404 18.116 ms - 1312
访问后的一个效果
实战1,添加路由,执行java -jar命令并返回结果
先写一个工具类exec,可以执行其他程序
var exec = require('child_process').exec;
class ExecService {
async exec(params) {
let ret = "Unknown";
await new Promise(function(resolve, reject) {
exec(params, function(err,stdout,stderr){
if(err) {
reject(stderr);
} else {
resolve(stdout);
}
});
}).then(function(result) {
ret = result;
}).catch(function(err) {
ret = err;
})
return ret;
}
};
module.exports = new ExecService()
再写一个控制器,负责执行java -jar命令
该demo是为了执行java -jar retrace指令
const execService = require("../utils/ExecService");
const fs = require("fs");
const downloadFile = require("../utils/DownloadFile")
const path = require('path');
const retraceRootDir = path.join(__dirname, '../../public/resource/');
class RetraceController {
async retrace(req, res) {
let mapFile = retraceRootDir + "mappings/" + req.params.id + "/mapping.txt"
let stackFile = retraceRootDir + "cache/" + new Date().getTime();
fs.writeFileSync(stackFile, req.body);
let cmd = "java -jar " + retraceRootDir
+ "jar/retrace.jar" + " "
+ mapFile + " " + stackFile;
let ret = await execService.exec(cmd);
fs.unlink(stackFile, function(err) {
if (err) {
console.log('unlink', stackFile, "failed!", err);
}
});
res.send(ret);
}
async downloadMap(req, res) {
let mapCacheParentFile = retraceRootDir + "mappings/" + req.params.id;
try {
fs.accessSync(mapCacheParentFile, fs.constants.F_OK);
} catch(err) {
fs.mkdirSync(mapCacheParentFile);
}
let mapCacheFile = retraceRootDir + "mappings/" + req.params.id + "/mapping.txt";
try {
fs.accessSync(mapCacheFile, fs.constants.F_OK);
res.send("already download:" + mapCacheFile);
return;
} catch(err) {
}
let url = req.body.url;
await downloadFile(url, mapCacheFile).then(function(result) {
console.log(result);
res.send(result);
}).catch(function(reason) {
console.error(reason);
res.status(404).send(reason);
});
}
};
module.exports = new RetraceController();
实战2,下载文件至缓存
路由可参考实战1中的downloadMap函数,也就是缓存mapping文件
工具类代码如下:
const fs = require('fs')
const https = require('https')
const download = (url, path) => new Promise((resolve, reject) => {
https.get(url, response => {
const statusCode = response.statusCode;
if (statusCode !== 200) {
return reject('Download error!');
}
const writeStream = fs.createWriteStream(path);
response.pipe(writeStream);
writeStream.on('error', function() {
fs.unlinkSync(path);
reject('Error download ' + url);
});
writeStream.on('finish', function() {
writeStream.close(resolve);
resolve("Success download " + url);
});
});}).catch(err => console.error(err));
module.exports = download;
踩坑路
Axis post请求,需要明确ContentType,否则会影响服务端的解析,默认为application/json
async fetchRetrace(item) {
if (item.retraceStack != "") {
return item.retraceStack;
}
let url = '/retrace/'+this.id;
let originStack = item.stack;
const { data: retraceStack, status: code} = await window.axios({
method: "POST",
headers: { "Content-Type": "text/plain; charset=UTF-8"},
url: url,
data: originStack
})
console.log(retraceStack);
return retraceStack;
},
如果不指明为text/plain,后端控制器获取req.body会是一个json对象,实际上我们只需要一个原始堆栈内容,不需要是一个json
遇到这种问题,可以打印typeof req.body即可
req.body为空。。。
需要使用body-parser
var bodyParser = require('body-parser');
const app = express();
...
...
app.use(bodyParser.text());
使用Promise获取某个接口的json数据
var https=require("https");
var detailInfoPromise = function(id) {
return new Promise(function(resolve, reject){
let options={
headers:{
"Accept":"application/json",
"Authorization":"Bearer *******************"
}
}
let chunks = ''
https.get(
new URL('https://******/detail?id=' + id),
options,
function (res) {
res.on("data", function (chunk) {
chunks += chunk
});
res.on("end", function () {
resolve(chunks)
});
console.log(res.statusCode)
})
})
}
async function fetchDetailInfo(id) {
let success = false
let ret = ''
await detailInfoPromise(id).then(function(chunks) {
ret = JSON.parse(chunks)
success = true
}).catch(function(reason) {
console.error("fetchDetailInfo failed", reason)
})
if (success) {
return ret
}
return 'Unknown'
}
曾经出现过JSON.parse失败的问题,我记得错误指明了chunks不是一个json格式。。。好像是最开始,没有使用res.on(“end”)导致的
实战3,监控自己开发机上服务的状态
自己开发机是docker云主机,服务老是莫名kill掉,所以自己写了个脚本会监控并重启
因为开发机是在另一台固定ip地址的电脑上,所以省去了很多麻烦
先来了简单的命令热热身,打印远程主机相应程序的pid
ssh admin@10.***.***.*** ps aux|grep "node .*server.js" | tr -s ' '|cut -d ' ' -f2
34854
嗯,上述脚本只是为了测试
实际上,我们只需要grep,搜到就可以简单的表示程序还在运行
#!/bin/bash
declare PROCESS_NAME="node /home/admin/project/server.js"
:() {
ssh admin@10.***.***.*** ps aux |grep "$PROCESS_NAME"
if [ $? -eq 0 ]; then
echo -e "\033[1;32m${PROCESS_NAME} process is running\033[0m"
sleep 5
else
echo -e "\033[1;31m${PROCESS_NAME} process not found! will restart\033[0m"
ssh admin@10.***.***.*** nohup "$PROCESS_NAME" >> tmp.log 2>&1 &
if [ $? -eq 0 ]; then
echo -e "\033[1;32m${PROCESS_NAME} process restart success\033[0m"
fi
fi
}
:
最后,稍微又完善了一下,当重启任务失败时,发送邮件给自己的邮箱
if [ $? -eq 0 ]; then
echo -e "\033[1;32m${PROCESS_NAME} process restart success\033[0m"
else
node email.js
fi
发送邮件的js代码
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
//查看支持列表:https://nodemailer.com/smtp/well-known/
service: 'qq',
port: 465,
secureConnection: true, // 使用了 SSL
auth: {
user: '****@qq.com',
//qq邮箱smtp授权码
pass: '****',
}
});
let mailOptions = {
from: '****@qq.com', // sender address
to: 'toemail@qq.com;', // list of receivers
subject: 'Hello', // Subject line
html: '<h1>restart failed</h1>' // html body
};
// send mail with defined transport object
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
// console.log('Message sent: %s', info.messageId);
console.log(info)
});
另外还可以打一针,感觉还挺有效的
echo -17 > /proc/<pid>/oom_adj