2014-02-19 1 views
0

미들웨어보고를 작성했습니다.이 미들웨어는 모든 GET 및 POST 처리 (app.use (app.router) 이후)를 거친 후에 표시됩니다. 아래를 참조하십시오. PostGIS와 데이터베이스 등여러 child_process를 생성하기 위해 ExpressJS에서 미들웨어를 처리하는 중

하지만 하나를 생성 한 후 디렉토리의 무리, 파일의 숫자를 작성하고 설계 한 POST 요청이 간다

이 간단한 빠른 GET 위대한 작품과 POST -> 8 child_processes 작업

childProcess.execFile(job.config.FMEPath, ["PARAMETER_FILE", job.fmeParamFile], { cwd: job.root }, 

그 모든 설정이 두 번째보다 많은 시간을 (하지 않으며, 내가 5 단계 (아래 참조) 시퀀스 한 지점에서 비동기 라이브러리를 사용 (모든 비동기입니다

을.

제 문제는 오류 처리입니다. 지금 당장 모든 파일을 작성하고 모든 단계를 수행하기 직전에 응답을 보내십시오. 이것은 next (err)가 예상대로 작동하지 않는다는 것을 의미합니다. 오류를보고하는 좋은 패러다임은 무엇입니까? WINSTON을 사용하여 [logger.log()] 오류를 기록하지만 서버에 오류를 기록해야합니까, 아니면 원래 요청으로보고해야합니까? 여기에 (그리고 ERR (I 나머지를 유지하는 것, 기억하고 REQ 및 다음 개체 주위에 꽤 다음에 호출 할 수 할) 현재 포스트 요청입니다.

exports.build = function (req, res, next) { 
    var config = global.app.settings.config; 
    var jobBatch = groupJobs(req.body.FrameList); 
    var ticket = tools.newGuid("", true); 
    var fileCount = req.body.FrameList.length * nitfMultiplier; 
    var ts = timespan.fromSeconds(fileCount/config.TileRate); 
    var estimate = ts.hours + ":" + tools.pad(ts.minutes, 2) + ":" + tools.pad(ts.seconds, 2); 
    res.set({ 'Content-Type': 'application/json; charset=utf-8' }); 
    res.send({ ticket: ticket, maxTiles: fileCount, timeEstimate: estimate, tileRate: config.TileRate, wwwURL: config.WWWUrl }); 
    jobBatchRoot(req, res, jobBatch, config, ticket, next); 
}; 

jobBatchRoot() (I 전원을 껐다 가서 처리를 많이 할 것, 나는 모든 코드를 포함하지 않았다. 아마도

exports.bugs = function (err, req, res, next) { 
    global.app.settings.stats.errors += 1; 
    if (err.code == undefined) { 
     err.code = 500; 
     err.message = "Server Error"; 
    } 
    res.status(err.code); 
    logger.log('error', '%s url: %s status: %d \n', req.method, req.url, err.code, { query: req.query, body: req.body, message: err.message, stack: err.stack }); 
    var desc = req.method + " " + req.url; 
    var body = util.format("%j", req.body); 
    var query = util.format("%j", req.query); 
    var stack = err.stack.split('\n'); 
    res.format({ 
     text: function() { 
      res.send(util.format("%j", { title: err.message, code: err.code, desc: desc, query: query, message: err.message, stack: err.stack, body: body})); 
     }, 

     html: function() { 
      query = tools.pretty(req.query); 
      res.render('error', { title: err.message, code: err.code, desc: desc, query: query, message: err.message, stack: stack, body: body }); 
     }, 

     json: function() { 
      res.send({ title: err.message, code: err.code, desc: desc, query: query, message: err.message, stack: err.stack, body: body }); 
     } 
    }); 

}; 

답변

0

나는 이것을 다시 고려했다. module.exports = function (..) {...}

과 함께 모듈을 만든 다음 클래스를 만드는 데 많은 상태와 메서드를 추가했습니다. 여기에는 작업 정의가 들어 있습니다. 이제 최상위 디렉토리를 만들고 응답을 반환하고 하위 작업을 생성합니다. 그들은 모두 명시 적 응답 후에 비동기로 실행됩니다. 하지만 오류가 발생해서는 안되며, 그렇다면 WINSTON을 사용하여 서버에서 로그를 남기고 모든 빌드가 완료되면 작업 완료 정보를 사용자에게 반환합니다.

0

I해야한다 (아마도 객체 지향), 어쨌든 내가 여기에 전체 모듈을 게시 할 것이라고 생각이 다시 인수 분해 할 수 내가 찾고있는 건 구조, 우수 사례에 관한 몇 가지 팁입니다.

var util = require('util'); 
var query = require("pg-query"); 
var timespan = require('timespan'); 
var _ = require('lodash'); 
var path = require('path'); 
var fs = require('fs'); 
var query = require("pg-query"); 
var async = require("async"); 
var childProcess = require("child_process"); 
var tools = require("../tools/tools"); 
var nitfMultiplier = 99; 
var manifestVersionID = 5; 

exports.setup = function (app) { 
}; 

exports.estimate = function (req, res, next) { 
    var config = global.app.settings.config; 
    var fileCount = req.body.FrameList.length * nitfMultiplier; 
    var ts = timespan.fromSeconds(fileCount/config.TileRate); 
    var estimate = ts.hours + ":" + tools.pad(ts.minutes, 2) + ":" + tools.pad(ts.seconds, 2); 
    res.set({ 'Content-Type': 'application/json; charset=utf-8' }); 
    res.send({ ticket: "Estimate", maxTiles: fileCount, timeEstimate: estimate, tileRate: config.TileRate, wwwURL: config.WWWUrl }); 
}; 

exports.build = function (req, res, next) { 
    var config = global.app.settings.config; 
    var jobBatch = groupJobs(req.body.FrameList); 
    var ticket = tools.newGuid("", true); 
    var fileCount = req.body.FrameList.length * nitfMultiplier; 
    var ts = timespan.fromSeconds(fileCount/config.TileRate); 
    var estimate = ts.hours + ":" + tools.pad(ts.minutes, 2) + ":" + tools.pad(ts.seconds, 2); 
    res.set({ 'Content-Type': 'application/json; charset=utf-8' }); 
    res.send({ ticket: ticket, maxTiles: fileCount, timeEstimate: estimate, tileRate: config.TileRate, wwwURL: config.WWWUrl }); 
    jobBatchRoot(req, res, jobBatch, config, ticket, next); 
}; 

function groupJobs(list) { 
    var jobBatch = {}; 
    _.forEach(list, function (obj) { 
     if (jobBatch[obj.type] == undefined) { 
      jobBatch[obj.type] = []; 
     } 
     jobBatch[obj.type].push(obj); 
    }); 
    return jobBatch; 
}; 

function jobBatchRoot(req, res, jobBatch, config, ticket, next) { 
    var batchRoot = path.join(config.JobsPath, ticket); 
    fs.mkdir(batchRoot, function (err) { 
     if (err) return next(err); 
     var mapInfoFile = path.join(batchRoot, "MapInfo.json"); 
     var mapInfo = { 
      Date: (new Date()).toISOString(), 
      Version: manifestVersionID, 
      Zoom: req.body.Zoom, 
      CenterLat: req.body.CenterLat, 
      CenterLon: req.body.CenterLon 
     }; 
     fs.writeFile(mapInfoFile, tools.pretty(mapInfo), function (err) { 
      if (err) return next(err); 
      spawnJobs(req, res, batchRoot, mapInfo, config, ticket, jobBatch, next); 
     }); 
    }); 
}; 

function spawnJobs(req, res, root, mapInfo, config, ticket, jobBatch, next) { 
    _.forEach(jobBatch, function (files, key) { 
     var job = { 
      req: req, 
      res: res, 
      type: key, 
      files: files, 
      batchRoot: root, 
      mapInfo: mapInfo, 
      config: config, 
      ticket: ticket, 
      missingFiles: [], 
      run: true, 
      next: next 
     }; 
     setup(job); 
    }); 
}; 

function setup(job) { 
    job.root = path.join(job.batchRoot, job.type); 
    job.fmeParamFile = path.join(job.root, "fmeParameters.txt"); 
    job.fmeWorkSpace = path.join(job.config.LibrarianPath, "TileBuilder.fmw"); 
    job.fmeLogFile = path.join(job.root, "jobLog.log"); 
    job.errorLog = path.join(job.root, "errorLog.log"); 
    job.jobFile = path.join(job.root, "jobFile.json"); 
    job.manifestFile = path.join(job.root, "manifest.json"); 
    async.series({ 
     one: function (callback) { 
      maxZoom(job, callback); 
     }, 
     two: function (callback) { 
      fs.mkdir(job.root, function (err) { 
       if (err) return job.next(err); 
       callback(null, "Job Root Created"); 
      }); 
     }, 
     three: function (callback) { 
      makeParamFile(job, callback); 
     }, 
     four: function (callback) { 
      delete job.req; 
      delete job.res; 
      fs.writeFile(job.jobFile, tools.pretty(job), function (err) { 
       if (err) return job.next(err); 
       callback(null, "Wrote Job File"); 
      }); 
     }, 
     five: function (callback) { 
      runJob(job, callback); 
     }, 
     six: function (callback) { 
      tileList(job, callback); 
     }, 
     seven: function (callback) { 
      finish(job, callback); 
     }, 
    }, 
    function (err, results) { 
     if (err) return job.next(err); 
     console.log(tools.pretty(results)); 
    }); 
} 

function maxZoom(job, callback) { 
    var qString = util.format('SELECT type, "maxZoom" FROM portal.m_type WHERE type=\'%s\'', job.type); 
    query(qString, function (err, rows, result) { 
     if (err) { 
      var err = new Error(queryName); 
      err.message = err.message + " - " + qString; 
      err.code = 400; 
      return job.next(err); 
     } 
     job.maxZoom = rows[0].maxZoom - 1; // kludge for 2x1 root layer in leaflet 
     return callback(null, "Got MaxZoom"); 
    }); 
} 

function makeParamFile(job, callback) { 
    var text = util.format("%s\n", job.fmeWorkSpace); 
    text += util.format("--OutputDir %s\n", job.root); 
    text += util.format("--LogFile %s\n", job.fmeLogFile); 
    var source = ""; 
    _.forEach(job.files, function (file) { 
     var path = ('development' == process.env.NODE_ENV) ? file.path.replace(job.config.SourceRootRaw, job.config.SourceRoot) : file.path; 
     if (fs.existsSync(path)) { 
      source += wrap(path, '\\"') + " "; 
     } 
     else { 
      job.missingFiles.push(path); 
     } 
    }); 
    source = wrap(wrap(source, '\\"'), '"'); 
    text += "--Sources " + source; 
    if (job.missingFiles.length == job.files.length) job.run = false; 
    fs.writeFile(job.fmeParamFile, text, function (err) { 
     if (err) return job.next(err); 
     return callback(null, "Wrote Paramaters File"); 
    }) 
}; 

function wrap(content, edge) { 
    return edge+content+edge; 
    } 

function runJob(job, callback) { 
    if (!job.run) return callback(null, "Skipped JOB, no files"); 
    childProcess.execFile(job.config.FMEPath, ["PARAMETER_FILE", job.fmeParamFile], { cwd: job.root }, 
     function (err, stdout, stderr) { 
      if (err) return job.next(err); 
      job.stdout = stdout; 
      job.stderr = stderr; 
      var bar = "\n--------------------------------------------------------------------------------------------------------\n"; 
      var results = util.format("%s STDOUT: \n %s%s STDERR: \n %s", bar, job.stdout, bar, job.stderr); 
      fs.appendFile(job.fmeLogFile, results, function (err) { 
       return callback(err, "FME JOB " + job.type + " run completed"); 
      }); 
     }); 
} 

function tileList(job, callback) { 
    var tiles = []; 
    var byteCount = 0; 
    fs.readdir(job.root, function (err, files) { 
     if (err) { 
      logger.log('error', 'tileList directory read: %s \n', job.root, { message: err.message, stack: err.stack }); 
      return job.next(err); 
     } 
     async.each(files, function (file, done) { 
      var fileName = file.split("."); 
      fs.lstat(job.root + "\\" + file, function (err, stats) { 
       if (!err && stats.isFile() && (fileName[1] == "png")) { 
        tiles.push({ id: fileName[0], size: stats.size }); 
        byteCount += stats.size; 
       }; 
       done(null); 
      }); 
     }, function (err) { 
     job.tileList = tiles; 
     job.byteCount = byteCount; 
     return callback(null, "got tile list");} 
     ); 
    }); 
} 

function finish(job, callback) { 
    var manifest = { 
     Date: (new Date()).toISOString(), 
     Version: manifestVersionID, 
     MaxZoom: job.maxZoom, 
     Class: "OVERLAY", 
     FileCount: job.tileList.length, 
     Size: job.byteCount/(1024 * 1024), // Mbytes 
     files: job.tileList 
    }; 
    fs.writeFile(job.manifestFile, tools.pretty(manifest), function (err) { 
     if (err) { 
      logger.log('error', 'manifest write: %s \n', job.manifestFile, { message: err.message, stack: err.stack }); 
      return job.next(err); 
     } 
     return callback(null, "JOB " + job.type + " completed"); 
    }); 
}