(二)egg-基础功能

(一)egg-基础功能

    • 一、定时任务
    • (二)框架扩展
      • (1)Application
      • (2)Context
      • (3)request & response
    • (三)启动

一、定时任务

  • (1)编写定时任务
    所有的定时任务均放在app/shedule目录下,每一个文件都是一个独立的定时任务,可以配置定时任务和要执行的方法。
    例如:更新远程数据到内存缓存的定时任务,可以在app/schedule目录下创建一个文件:
const Subscription = require('egg').Subscription;

class UpdateCache extends Subscription {
  // 通过 schedule 属性来设置定时任务的执行间隔等配置
  static get schedule() {
    return {
      interval: '1m', // 1 分钟间隔
      type: 'all', // 指定所有的 worker 都需要执行
    };
  }

  // subscribe 是真正定时任务执行时被运行的函数
  async subscribe() {
    const res = await this.ctx.curl('http://www.api.com/cache', {
      dataType: 'json',
    });
    this.ctx.app.cache = res.data;
  }
}

module.exports = UpdateCache;
//或者使用如下写法
module.exports = {
  schedule: {
    interval: '1m', // 1 分钟间隔
    type: 'all', // 指定所有的 worker 都需要执行
  },
  //ctx可以调用service
  async task(ctx) {
    const res = await ctx.curl('http://www.api.com/cache', {
      dataType: 'json',
    });
    ctx.app.cache = res.data;
  },
};

上述定时任务会在每一个worker进程上每一分钟执行一次,将远程数据挂载在app.cache上。

  • (2)定时方式
    定时任务可以指定interval或者cron两种不同的定时方式。
    interval:可以配置为数字或字符类型
    cron:配置定时任务的执行时机,cron表达式可通过cron-parser进行解析。
module.exports = {
  schedule: {
    // 每三小时准点执行一次
    cron: '0 0 */3 * * *',
  },
};
  • (3)定时任务类型
    默认支持两种类型:worker和all
    worker类型:每台机器上只有一个worker会执行定时任务
    all类型:机器上的每个worker均会执行该定时任务。

  • (4)其他参数

cronOption:配置cron的时区
immediate:设置定时任务是否在应用启动后立即执行定时任务
disable:设为true时,该定时任务不会被启动
env:数组类型,在指定的环境下才会启动该定时任务。

  • (5)其他
    可以通过app.runSchedule(schedulePath)来运行定时任务,其会执行对应的定时任务,返回一个promise。

扩展定时任务:
框架默认的定时任务只支持每台机器的单个进程执行和全部进程执行,有些情况下可能有一个集群的某一个进程执行一个定时任务的需求。
可在上层框架中扩展新的定时任务类型:
在agent.js中继承agent.ScheduleStrategy,通过agent.schedule.use()注册即可。

module.exports = agent => {
  class ClusterStrategy extends agent.ScheduleStrategy {
    start() {
      // 订阅其他的分布式调度服务发送的消息,收到消息后让一个进程执行定时任务
      // 用户在定时任务的 schedule 配置中来配置分布式调度的场景(scene)
      // this.sendOne()随机通知一个worker执行task
      //this.sendAll()通知所有的worker执行task
      agent.mq.subscribe(schedule.scene, () => this.sendOne());
    }
  }
  agent.schedule.use('cluster', ClusterStrategy);
};

(二)框架扩展

(1)Application

app对象是全局应用对象,在应用启动的时候被创建。
在controller、Middlewaere、helper、Service中都可以通过this.app访问到application对象。

扩展方式:框架会把app/extend/application.js中定义的对象与Application的prototype对象进行合并,应用启动后会生成合并后的app对象。
1、方法扩展

// app/extend/application.js
module.exports = {
  foo(param) {
    // this 就是 app 对象,在其中可以调用 app 上的其他方法,或访问属性
  },
};

2、属性扩展
属性的计算只需进行一次,所以需进行缓存

// app/extend/application.js
const BAR = Symbol('Application#bar');

module.exports = {
  get bar() {
    // this 就是 app 对象,在其中可以调用 app 上的其他方法,或访问属性
    if (!this[BAR]) {
      // 实际情况肯定更复杂
      this[BAR] = this.config.xx + this.config.yy;
    }
    return this[BAR];
  },
};

(2)Context

context是请求级别的对象,每次请求生成一个ctx实例。
中间件中:this即为ctx
controller中:类的写法是this.ctx, 方法中直接通过ctx
helper service中:this指向helper、service对象本身,通过this.ctx访问contex对象。

扩展方式:框架会把app/extend/context.js中定义的对象与context的prototype的对象进行合并,在处理请求时会基于扩展后的prototype生成ctx对象。
(1)方法扩展

// app/extend/context.js
module.exports = {
  foo(param) {
    // this 就是 ctx 对象,在其中可以调用 ctx 上的其他方法,或访问属性
  },
};

(2)属性扩展

// app/extend/context.js
const BAR = Symbol('Context#bar');

module.exports = {
  get bar() {
    // this 就是 ctx 对象,在其中可以调用 ctx 上的其他方法,或访问属性
    if (!this[BAR]) {
      // 例如,从 header 中获取,实际情况肯定更复杂
      this[BAR] = this.get('x-bar');
    }
    return this[BAR];
  },
};

(3)request & response

1、request
request访问方式:使用ctx和ctx.request访问属性和方法是等价的。ctx.url === ctx.request.url

扩展方法:

// app/extend/request.js
module.exports = {
  get foo() {
    return this.get('x-request-foo');
  },
};

2、response
使用ctx和ctx.response访问是等价的。
扩展方式:

// app/extend/response.js
module.exports = {
  set foo(value) {
    this.set('x-response-foo', value);
  },
};

(三)启动

框架提供统一的入口文件(app.js)进行启动过程自定义,文件返回一个boot类,可以通过定义一些生命周期方法来启动应用过程中的初始化工作:

  • 配置文件即将加载,这是最后动态修改配置的时机(configWillLoad)
  • 配置文件加载完成(configDidLoad)
  • 文件加载完成(didLoad)
  • 插件启动完毕(willReady)
  • worker 准备就绪(didReady)
  • 应用启动完成(serverDidReady)
  • 应用即将关闭(beforeClose)
// app.js
class AppBootHook {
  constructor(app) {
    this.app = app;
  }

  configWillLoad() {
    // 此时 config 文件已经被读取并合并,但是还并未生效
    // 这是应用层修改配置的最后时机
    // 注意:此函数只支持同步调用

    // 例如:参数中的密码是加密的,在此处进行解密
    this.app.config.mysql.password = decrypt(this.app.config.mysql.password);
    // 例如:插入一个中间件到框架的 coreMiddleware 之间
    const statusIdx = this.app.config.coreMiddleware.indexOf('status');
    this.app.config.coreMiddleware.splice(statusIdx + 1, 0, 'limit');
  }

  async didLoad() {
    // 所有的配置已经加载完毕
    // 可以用来加载应用自定义的文件,启动自定义的服务

    // 例如:创建自定义应用的示例
    this.app.queue = new Queue(this.app.config.queue);
    await this.app.queue.init();

    // 例如:加载自定义的目录
    this.app.loader.loadToContext(path.join(__dirname, 'app/tasks'), 'tasks', {
      fieldClass: 'tasksClasses',
    });
  }

  async willReady() {
    // 所有的插件都已启动完毕,但是应用整体还未 ready
    // 可以做一些数据初始化等操作,这些操作成功才会启动应用

    // 例如:从数据库加载数据到内存缓存
    this.app.cacheData = await this.app.model.query(QUERY_CACHE_SQL);
  }

  async didReady() {
    // 应用已经启动完毕

    const ctx = await this.app.createAnonymousContext();
    await ctx.service.Biz.request();
  }

  async serverDidReady() {
    // http / https server 已启动,开始接受外部请求
    // 此时可以从 app.server 拿到 server 的实例

    this.app.server.on('timeout', socket => {
      // handle socket timeout
    });
  }
}

module.exports = AppBootHook;

你可能感兴趣的:(nodejs)