【8.12】nodejs 原理学习 - 异步编程(3) 发布/订阅模式

356 阅读3分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

这个系列是 nodejs 的异步编程学习记录,前两篇文章是:

上一篇文章讲述了异步编程的优势和难点,这篇文章对于异步编程的难点,说明可以参考的解决方式,整体来看主要有三种方式:事件发布/订阅模式;Promise/Deferred模式;流程控制库。

这篇文章发布/订阅模式的介绍和应用。

发布/订阅模式基本介绍

发布/订阅模式是一种使用很广的设计模式,在 nodejs 中 events 模块 就是发布订阅模式的一个典型的例子。具体代码如下:

const EventEmitter = require('events');

const myEmitter = new EventEmitter();

// 订阅
myEmitter.on('event', (message) => {
  console.log(`an event occurred! ${message}`);
});

// 发布
myEmitter.emit('event', 'hello!');

发布订阅模型在应用上,可以进行组件的封装,把不变的部分封装到组件内部,将容易变化,需要自定义的部分封装交由组件外部处理,是一种典型的逻辑分离方式。

以 http 请求为例,我们只需要监听 data、error、end 这些事件,并做出相应的处理,不需要关注背后的处理流程。

var options = {
    host: 'www.google.com', port: 80,
    path: '/upload', method: 'POST'
};
var req = http.request(options, function (res) {
    res.on('data', function (chunk) {});
    res.on('end', function () {}); 
});

req.on('error', function (e) {});
req.end();

另外,需要注意的是 nodejs 对发布/订阅模式做了一些额外的处理,如果对一个事件绑定了超过10个监听器,则会触发一条警告,因为设计者认为监听器过多可能会导致内存泄露。可以通过增加 emitter.setMaxListeners(0) 的方式把这个限制去掉;另一方面,事件监听器过多,可能会过多占用 CPU。

另外,如果在程序运行过程中触发了 error 事件,nodejs 会有一些特殊处理,首先会检查是否有 error 事件的监听器,如果没有会抛出异常,如果没有捕获这个

继承 event 模块

nodejs 中近半数的核心模块都继承自 EventEmitter,nodejs 中 utils 封装了方法,可以快速的继承 EventEmitter,使用事件机制解决业务问题,下面是 Stream 继承的例子:

var events = require('events');

function Stream() { 
    events.EventEmitter.call(this);
}
util.inherits(Stream, events.EventEmitter);

利用事件队列解决雪崩问题

雪崩问题是指在高并发的场景下,缓存失效,大量的请求涌入到数据库中,数据库无法承受这么大的查询请求,影响到网站的整体速度。

var select = function (callback) { 
    db.select("SQL", function (results) {
        callback(results); 
    });
};

如果这站点刚启动,缓存中是没有数据的,这时如果访问量巨大,会进行大量的数据库查询,我们可以使用状态锁+发布/订阅模式解决这个问题。

var proxy = new events.EventEmitter(); 
var status = "ready";

var select = function (callback) {
    proxy.once("selected", callback);  // 每次请求新增一个订阅者,once 的意思是订阅者被触发一次后,就会将监听器移除
    if (status === "ready") {
        status = "pending";  // 除第一次进入请求,status 设为 pending,避免重复查询数据库
        db.select("SQL", function (results) {
            proxy.emit("selected", results);  // 数据库查询完成后发布者发布完成的消息消息
            status = "ready"; 
        });
    } 
};

这样做之后,除了第一次进入之外的请求,会通过订阅者的方式,等待数据查询完成后发布消息,触发监听器,执行自己的业务逻辑。

以上是发布/订阅模式的介绍和应用,是对 深入浅出 nodejs 这本书的第四章 - 异步编程的学习笔记和练习,欢迎点赞和评论~