From ba78878e43fb734700fb8183a57edd665b23594a Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Fri, 14 Oct 2016 02:06:35 +0800 Subject: [PATCH] feat: support background task on ctx ```js ctx.runAtBackground(function* (ctx) { // async actions here }); ``` --- app/extend/context.js | 32 +++++++++++--- test/app/extend/context.test.js | 44 +++++++++++++++++++ .../ctx-background/app/controller/error.js | 9 ++++ .../ctx-background/app/controller/home.js | 10 +++++ .../apps/ctx-background/app/router.js | 6 +++ .../fixtures/apps/ctx-background/package.json | 3 ++ 6 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 test/fixtures/apps/ctx-background/app/controller/error.js create mode 100644 test/fixtures/apps/ctx-background/app/controller/home.js create mode 100644 test/fixtures/apps/ctx-background/app/router.js create mode 100644 test/fixtures/apps/ctx-background/package.json diff --git a/app/extend/context.js b/app/extend/context.js index de17a0a7fe..d779e0b27a 100644 --- a/app/extend/context.js +++ b/app/extend/context.js @@ -1,15 +1,10 @@ -/** - * 对 koa context 的所有扩展,都放在此文件统一维护。 - * - * - koa context: https://github.com/koajs/koa/blob/master/lib/context.js - */ - 'use strict'; const delegate = require('delegates'); const jsonpBody = require('jsonp-body'); const ContextLogger = require('egg-logger').EggContextLogger; const Cookies = require('egg-cookies'); +const co = require('co'); const util = require('../../lib/core/util'); const LOGGER = Symbol('LOGGER'); @@ -345,6 +340,31 @@ const proto = module.exports = { set state(val) { this.locals = val; }, + + /** + * Run generator function in the background + * @param {Generator} scope - generator function, the first args is ctx + * ```js + * this.body = 'hi'; + * + * this.runInBackground(function* saveUserInfo(ctx) { + * yield ctx.mysql.query(sql); + * yield ctx.curl(url); + * }); + * ``` + */ + runInBackground(scope) { + const ctx = this; + const start = Date.now(); + const taskName = scope.name || '-'; + co(function* () { + yield scope(ctx); + ctx.coreLogger.info('[egg:background] task:%s success (%dms)', taskName, Date.now() - start); + }).catch(err => { + ctx.coreLogger.info('[egg:background] task:%s fail (%dms)', taskName, Date.now() - start); + ctx.coreLogger.error(err); + }); + }, }; /** diff --git a/test/app/extend/context.test.js b/test/app/extend/context.test.js index 4bbe6d78b4..f92e648e10 100644 --- a/test/app/extend/context.test.js +++ b/test/app/extend/context.test.js @@ -305,4 +305,48 @@ describe('test/app/extend/context.test.js', () => { context.state.should.equal(context.locals); }); }); + + describe('ctx.runInBackground(scope)', () => { + let app; + before(() => { + app = utils.app('apps/ctx-background'); + }); + after(() => app.close()); + + it('should run background task success', done => { + request(app.callback()) + .get('/') + .expect(200) + .expect('hello') + .end(err => { + if (err) return done(err); + setTimeout(() => { + const logdir = app.config.logger.dir; + const log = fs.readFileSync(path.join(logdir, 'ctx-background-web.log'), 'utf8'); + log.should.match(/background run result status: 200/); + fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8') + .should.match(/\[egg:background\] task:saveUserInfo success \(\d+ms\)/); + done(); + }, 3000); + }); + }); + + it('should run background task error', done => { + request(app.callback()) + .get('/error') + .expect(200) + .expect('hello error') + .end(err => { + if (err) return done(err); + setTimeout(() => { + const logdir = app.config.logger.dir; + const log = fs.readFileSync(path.join(logdir, 'common-error.log'), 'utf8'); + log.should.match(/getaddrinfo ENOTFOUND registry-not-exists\.npm/); + fs.readFileSync(path.join(logdir, 'egg-web.log'), 'utf8') + .should.match(/\[egg:background\] task:mockError fail \(\d+ms\)/); + done(); + }, 2000); + }); + }); + }); }); diff --git a/test/fixtures/apps/ctx-background/app/controller/error.js b/test/fixtures/apps/ctx-background/app/controller/error.js new file mode 100644 index 0000000000..622b916312 --- /dev/null +++ b/test/fixtures/apps/ctx-background/app/controller/error.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = function* () { + this.body = 'hello error'; + this.runInBackground(function* mockError(ctx) { + const r = yield ctx.curl('http://registry-not-exists.npm.taobao.org/pedding/latest', { dataType: 'json' }); + ctx.logger.warn('background run result status: %s', r.status); + }); +}; diff --git a/test/fixtures/apps/ctx-background/app/controller/home.js b/test/fixtures/apps/ctx-background/app/controller/home.js new file mode 100644 index 0000000000..b2ee389a4c --- /dev/null +++ b/test/fixtures/apps/ctx-background/app/controller/home.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = function* () { + this.body = 'hello'; + this.runInBackground(function* saveUserInfo(ctx) { + const domain = process.env.CI ? 'registry.npmjs.com' : 'registry.npm.taobao.org'; + const r = yield ctx.curl(`http://${domain}/pedding/latest`, { dataType: 'json' }); + ctx.logger.warn('background run result status: %s', r.status); + }); +}; diff --git a/test/fixtures/apps/ctx-background/app/router.js b/test/fixtures/apps/ctx-background/app/router.js new file mode 100644 index 0000000000..1b2c9c53bf --- /dev/null +++ b/test/fixtures/apps/ctx-background/app/router.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = app => { + app.get('/', app.controller.home); + app.get('/error', app.controller.error); +}; diff --git a/test/fixtures/apps/ctx-background/package.json b/test/fixtures/apps/ctx-background/package.json new file mode 100644 index 0000000000..4c9d13d40c --- /dev/null +++ b/test/fixtures/apps/ctx-background/package.json @@ -0,0 +1,3 @@ +{ + "name": "ctx-background" +}