Skip to content

Instantly share code, notes, and snippets.

@luckydrq
Created August 25, 2013 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luckydrq/6334033 to your computer and use it in GitHub Desktop.
Save luckydrq/6334033 to your computer and use it in GitHub Desktop.
mo publish操作的并发任务优化
var Q = require('q')
var _ = require('underscore')
var exec = require('child_process').exec
var path = require('path')
var fs = require('fs')
//模拟Express App: `get`、`set`
var app = {
_data:{},
get: function(key){
return this._data[key]
},
set: function(key, value){
this._data[key] = value
}
}
app.set('tasks', [])
//模拟并发请求
var CONCURRENT_REQUESTS = 10
for (var i = 0; i < CONCURRENT_REQUESTS; i++) {
var task = createTask()
task.run()
};
function createTask(){
var id = _.uniqueId('task_')
var tasks = app.get('tasks')
tasks.push(id)
return {
id: id,
//执行所有任务操作
run: function(){
var self = this
//检查当前任务是否允许执行
//当上一个任务完成了git_Step1(提交到本地repo),才允许开始执行下一个任务。
Q.fcall(function(){
var d = Q.defer()
function check(){
if(id !== tasks[0]){
setImmediate(check)
}
else{
console.log('%s: Permitted', id)
d.resolve()
}
}
check()
return d.promise
})
//执行git_Step1,视为原子操作
.then(function(){
return Q
.fcall(function(){
return cleanup()
})
.then(function(){
if(fs.existsSync(vpath)){
var oldVersion = parseInt(fs.readFileSync(vpath, {encoding: 'utf-8'}), 10)
self.gitVersion = version = ++oldVersion
fs.writeFileSync(vpath,version + '')
}
})
.then(function(){
return git_Step1(self)
})
.fail(function(err){
cleanup()
if(err) throw err
})
.fin(function(){
//执行完毕后释放
tasks.shift()
})
})
//执行git_Step2,耗时操作,并发执行
.then(function(){
return git_Step2(self)
})
.fail(function(err){
if(err) console.log(err.message)
})
}
}
}
var vpath = path.join(__dirname, 'VERSION')
var version
//Step1: 提交到本地repo
//可视为原子操作,不能并发,否则会搞乱。
//而且,这些操作耗时很少,one by one执行没有降低太多效率
function git_Step1(task){
//Simplely use id as file name
var id = task.id
var version = task.gitVersion
var file = id
console.log('%s: Start git step1', id)
return Q
.fcall(run('echo "' + Math.random() + '" > ' + file))
.then(run('git add -A'))
.then(run('git cm -m "update ' + file + '"'))
.fail(function(err){
if(err) throw err
})
.fin(function(){
console.log('%s: Finish git step1', id)
})
}
//Step2: 上传CDN
//耗时操作,允许多任务并发执行以提高效率
//这些操作是互不影响的
function git_Step2(task){
var id = task.id
var version = task.gitVersion
return Q
.fcall(function(){
console.log('%s: Start git step2', id)
})
.then(run('git checkout -b daily/' + version))
.then(run('git push origin daily/' + version))
.then(run('git tag publish/' + version))
.then(run('git push origin publish/' + version))
.fail(function(err){
if(err) throw err
})
.fin(function(){
console.log('%s: Finish git step2', id)
})
}
/*********************************************************/
function run(cmd, cwd) {
return function() {
return doRun(cmd, cwd)
}
}
function doRun(cmd, cwd) {
var d = Q.defer()
process.nextTick(function() {
d.notify(cmd + '\n')
})
var proc = exec(cmd, { cwd: cwd || '.' })
var successData = ''
var errorData = ''
proc.on('close', function(code) {
if (code === 0) {
d.resolve(successData)
}
else {
console.log(code, errorData)
d.reject(new Error(errorData))
}
})
proc.stderr.on('data', function(data) {
d.notify(data)
errorData += data
})
proc.stdout.on('data', function(data) {
d.notify(data)
successData += data
})
return d.promise
}
function cleanup(){
return Q
.fcall(run('git reset HEAD --hard'))
.then(run('git clean -d -f'))
.then(run('git checkout master'))
.then(run('git pull origin master'))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment