最近搞express框架的记录

本文记录了一位安卓开发者如何自学并应用Node.js进行后端开发的过程,包括环境安装、Express框架搭建、Java jar命令执行、文件下载及服务监控等实战经验。在遇到问题时,如请求内容解析、服务重启等,作者给出了相应的解决方案。
摘要由CSDN通过智能技术生成

背景

​ 本人安卓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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值