代码拉取完成,页面将自动刷新
同步操作将从 lianzt/light-gateway 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
const fs = require('fs');
const pro = require('process');
const nodeUtil = require('util');
const config = require('./config')
const util = require('./util')
const classifier = require('./classifier')
/**
* 文件控制器
*
* 关于 request 与 response 文件如何对应的问题。
* 问题描述:请求生成的 request 经过光闸传输后,与返回的 response 被分隔到了两个事件中,如何确认哪个请求对应的哪个响应是一个难题
* 解决方案一:在 file_controller 中增加一个 map 对象,保存请求时的序号与其它内容,使用 response 文件的序号可找到对应的请求
* 解决方案二:不关心请求与响应的对应关系,全部使用无状态请求,把请求中的 back_url 一并写入到 response 文件中,收到响应后根据 back_url 通知相应系统
*/
const readFile = nodeUtil.promisify(fs.readFile);
const rename = nodeUtil.promisify(fs.rename);
const logger = util.logger
const REQUEST = util.REQUEST;
const RESPONSE = util.RESPONSE;
const FILE_BROKEN_ERR_CODE = 'file_broken';
// 文件是否存在
const exists = util.exists
pro.on('uncaughtException', (e)=> {
logger.error('domain catch error : ' , e);
});
/**
* 检验文件是否完成
* @param file 文件路径
* @return 文件内容的 json 对象
*/
const checkFile = async (filepath, filename)=>{
let fix = filepath.split('.').pop().toLowerCase(); //扩展名
if (fix !== REQUEST && fix !== RESPONSE){
logger.warn('文件不是被监听的类型');
return undefined;
}
let sign = filepath.split('-').pop();
sign = sign.split('.').shift();
if (sign && sign.length === 32){
try{
//在读取文件时,有可能会出现 EBUSY 异常导致系统崩溃,产生原因不明
let fileContent = await readFile(filepath, {encoding : 'utf-8'});
if (checkSign(fileContent, sign)){
try{
return JSON.parse(fileContent);
}catch(e){
logger.error(`文件 ${filename} 格式错误:`, e);
}
}else{
logger.error(`文件 ${filename} 签名验证失败,文件有可能被损坏,尝试重传。`);
await fileBroken(filename); //生成文件损坏的 response 文件
}
}catch(e){
logger.error(`文件 ${filename}读取异常,文件有可能被损坏,尝试重传:`, e);
await fileBroken(filename); //生成文件损坏的 response 文件
}
}else{
logger.warn('文件名不规范,删除文件。');
}
await util.removeFile(filepath);
};
/**
* 文件损坏响应,发起端收到该响应后会重发请求
* 该函数可异步执行,不需要观察返回结果
*/
const fileBroken = async (filename)=>{
//request 与 response 文件的处理逻辑是一样的,两种文件都可以支持。
let fileJson = {
header: util.getErrMsg(FILE_BROKEN_ERR_CODE), //文件被损坏
content: {
filename
}
}
await util.createResponseFile(fileJson);
}
/**
* 从已发送的目录中查找文件,重新发送
*/
const resendFile = async (filename)=>{
//从已发送与发送异常目录中寻找文件
for(dir of config.sent_dir){
let path = `${config.request_path}/${dir}/${filename}`;
logger.debug(`检查 ${path}`);
if (await exists(path)){
//找到文件
logger.debug(`开始把文件 ${path} 转移到共享目录`);
await rename(path, `${config.request_path}/${filename}`);
return;
}
}
logger.warn(`未找到需要重发的文件 ${filename} `);
}
/**
* 检验文件内容
* @return true|false
*/
const checkSign = (content, sign) => {
let md5Str = util.hexMd5(content);
logger.debug(`提交的签名:${sign},验证的签名:${md5Str}`);
return md5Str === sign;
}
logger.info(`打开目录监听:${config.response_path}`);
// 打开文件监听器
fs.watch(config.response_path, {persistent : false}, async (eventType, filename) =>{
logger.debug(`监听到文件变化:${eventType},文件名:${filename}`);
if (eventType === 'rename'){
let filePath = config.response_path + '/' + filename;
//只监听文件创建事件,创建和删除都会触发rename事件,需要判断文件是否存在
if (await exists(filePath)){
//文件创建事件
//1.校验文件完整性
let fileJson = await checkFile(filePath, filename);
if (fileJson){
logger.debug('文件内容:', fileJson);
//判断是否是重传请求
if (fileJson.header && fileJson.header.code === FILE_BROKEN_ERR_CODE){
//寻找原始文件,重新发送
resendFile(fileJson.content.filename);
util.removeFile(filePath); //删除收到的文件
}else{
//正常请求,分发请求
classifier.send(filePath, fileJson);
}
}
//2.判断是请求文件还是响应文件
//2.1如果是请求文件,分发到相应的系统服务
//2.2如果是响应文件,根据回调地址发送响应
}else{
logger.debug('文件删除事件,忽略');
}
}
});
//已发送目录文件清理
setInterval(async () => {
logger.info(`开始清理已发送目录 ${config.sent_dir}`);
for(dir of config.sent_dir){
try{
await util.emptyDir(`${config.request_path}/${dir}`);
}catch(e){
logger.error(`清理目录 ${dir} 异常:`, e);
}
}
//删除失败的文件缓存队列
await util.cleanQueue();
}, config.clean_cycle);
module.exports = {};
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。