Maison  >  Article  >  interface Web  >  Explication détaillée des exemples d'utilisation de l'écoute d'événements et de la publication d'événements dans Node.js

Explication détaillée des exemples d'utilisation de l'écoute d'événements et de la publication d'événements dans Node.js

伊谢尔伦
伊谢尔伦original
2017-07-24 10:29:261798parcourir

node.js est basé sur des E/S asynchrones non bloquantes à thread unique, ce qui signifie que lorsqu'il rencontre une opération d'E/S, le thread ne bloque pas mais effectue les opérations suivantes. une fois l'opération d'E/S terminée, comment le thread sait-il que l'opération est terminée ?

Lorsque l'opération termine l'opération d'E/S fastidieuse, le thread de l'opération d'E/S sera notifié sous la forme d'un événement. Le thread traitera cet événement à un moment précis. et passez à l'étape suivante. Afin d'effectuer des E/S asynchrones, le thread doit disposer d'un mécanisme de boucle d'événements, insistant constamment sur la présence d'événements inachevés et complétant le traitement de ces événements dans l'ordre.

Pour bloquer les E/S, lorsqu'un thread rencontre une opération d'E/S fastidieuse, il arrêtera de s'exécuter et attendra la fin de l'opération. À ce stade, le thread ne peut pas accepter d'autres demandes d'opération. Afin de fournir un débit, plusieurs threads doivent être créés, chaque thread pour répondre à la demande d'un client, mais en même temps, un seul thread peut s'exécuter sur un cœur de processeur. Si plusieurs threads veulent s'exécuter, ils doivent basculer entre différents threads.

Par conséquent, node.js réduit le coût de création de threads et de changement de thread dans les multi-threads. Le coût du changement de thread est très élevé. Il doit lui allouer de la mémoire et l'inclure dans le planning. en même temps, lors du changement de thread. Parfois, il est nécessaire d'effectuer une pagination de la mémoire et d'autres opérations. Ces opérations peuvent être réduites en utilisant un seul thread. Cependant, cette méthode de programmation présente également des inconvénients et n'est pas conforme au design thinking des gens.

node.js est basé sur le mode événement pour implémenter des E/S asynchrones. Lorsqu'il est démarré, il déterminera en continu s'il y a des événements inachevés, puis l'exécutera une fois l'exécution terminée, un autre événement. sera Notifier le thread sous la forme de Les tâches sont divisées en petits événements, et node.js convient également à la gestion de certains scénarios d'E/S élevées et de logique faible.

L'exemple suivant illustre la lecture de fichiers asynchrone :


var fs = require('fs'); 
fs.readFile('file.txt', 'utf-8', function(err, data) { 
if (err) { 
<span style="white-space:pre"> </span>console.error(err); 
} else { 
<span style="white-space:pre"> </span>console.log(data); 
} 
}); 
[javascript] view plain copy
console.log("end");

Comme ci-dessusfs.readFile Lire le fichier de manière asynchrone, puis le processus continuera sans attendre la fin de la lecture du fichier. Lorsque le fichier est lu, un événement sera libéré et le thread d'exécution exécutera l'opération correspondante lorsqu'il traversera l'événement. la fonction de rappel correspondante Dans l'exemple, la fin de la chaîne sera imprimée avant le contenu du fichier.

API d'événement node.js

events.EventEmitter : EventEmitter fournit l'encapsulation des fonctions d'émission et d'écoute d'événements dans node.js, chaque événement se compose d'une chaîne identifiant le nom de l'événement et l'opération correspondante.

Suivi des événements :


var events = require("events"); 
var emitter = new events.EventEmitter(); 
 <span style="font-family: Arial, Helvetica, sans-serif;">emitter.on("eventName", function(){</span> 
  console.log("eventName事件发生") 
})

Publication des événements :


emitter.emit("eventName");

Lors de la publication d'un événement, nous pouvons transmettre plusieurs paramètres. Le premier paramètre représente le nom de l'événement et les paramètres suivants représentent les paramètres transmis. Ces paramètres seront transmis. dans la fonction de rappel d'événement.

EventEmitter.once("eventName", listener)  : Enregistrez un écouteur pour un événement qui n'est exécuté qu'une seule fois. Lorsque l'événement se produit pour la première fois et que l'écouteur est déclenché, l'écouteur sera libéré si l'événement se produit par la suite. , l'écouteur sera libéré. ​​Le serveur ne s'exécutera pas.

EventEmitter.removeListener(event, listener)  : Supprimer l'écouteur d'événement

EventEmitter.removeAllListeners(event) : Supprimer tous les écouteurs d'événement

EventEmitter.setMaxListeners(n) : Le maximum par défaut le nombre d'écouteurs pour un seul événement dans node.js est de 10. S'il dépasse 10, un avertissement sera émis afin d'éviter un débordement de mémoire. Nous pouvons modifier cette limite par d'autres nombres. Si elle est définie, une valeur de 0 signifie aucune restriction. .

EventEmitter.listeners(event)  : Renvoie la liste des auditeurs d'un événement

La collaboration entre plusieurs événements
est légèrement plus grande Dans certains applications, la séparation entre les données et les serveurs Web est inévitable, comme Sina Weibo, Facebook, Twitter, etc. L'avantage est que les sources de données sont unifiées et que divers programmes clients riches peuvent être développés pour les mêmes sources de données.

Prenons l'exemple des applications Web. Lors du rendu d'une page, il est généralement nécessaire d'extraire des données de plusieurs sources de données et enfin de les restituer au client. Dans ce scénario, Node.js peut naturellement et facilement lancer des requêtes vers plusieurs sources de données en parallèle en même temps.


api.getUser("username", function (profile) {
 // Got the profile
});
api.getTimeline("username", function (timeline) {
 // Got the timeline
});
api.getSkin("username", function (skin) {
 // Got the skin
});

Node.js utilise un mécanisme asynchrone pour rendre les requêtes non bloquantes, atteindre l'objectif des requêtes parallèles et appeler efficacement les ressources sous-jacentes. Cependant, le problème dans ce scénario est que la coordination de plusieurs résultats de réponses à des événements n'est pas prise en charge de manière élégante et native par Node.js.

Afin d'obtenir des résultats pour les trois demandes avant de passer à l'étape suivante, le programme peut être modifié dans la situation suivante :


api.getUser("username", function (profile) {
 api.getTimeline("username", function (timeline) {
  api.getSkin("username", function (skin) {
   // TODO
  });
 });
});

Cela entraînera des requêtes effectuées en série et ne pourra pas maximiser l'utilisation du serveur API sous-jacent.

为解决这类问题,我曾写作一个模块来实现多事件协作,以下为上面代码的改进版:


var proxy = new EventProxy();
proxy.all("profile", "timeline", "skin", function (profile, timeline, skin) {
 // TODO
});
api.getUser("username", function (profile) {
 proxy.emit("profile", profile);
});
api.getTimeline("username", function (timeline) {
 proxy.emit("timeline", timeline);
});
api.getSkin("username", function (skin) {
 proxy.emit("skin", skin);
});

EventProxy也是一个简单的事件侦听者模式的实现,由于底层实现跟Node.js的EventEmitter不同,无法合并进Node.js中。但是却提供了比EventEmitter更强大的功能,且API保持与EventEmitter一致,与Node.js的思路保持契合,并可以适用在前端中。
这里的all方法是指侦听完profile、timeline、skin三个方法后,执行回调函数,并将侦听接收到的数据传入。

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

所谓雪崩问题,是在缓存失效的情景下,大并发高访问量同时涌入数据库中查询,数据库无法同时承受如此大的查询请求,进而往前影响到网站整体响应缓慢。

那么在Node.js中如何应付这种情景呢。


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

以上是一句数据库查询的调用,如果站点刚好启动,这时候缓存中是不存在数据的,而如果访问量巨大,同一句SQL会被发送到数据库中反复查询,影响到服务的整体性能。一个改进是添加一个状态锁。


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

但是这种情景,连续的多次调用select发,只有第一次调用是生效的,后续的select是没有数据服务的。所以这个时候引入事件队列吧:


var proxy = new EventProxy();
var status = "ready";
var select = function (callback) {
  proxy.once("selected", callback);
  if (status === "ready") {
   status = "pending";
   db.select("SQL", function (results) {
    proxy.emit("selected", results);
    status = "ready";
   });
  }
 };

这里利用了EventProxy对象的once方法,将所有请求的回调都压入事件队列中,并利用其执行一次就会将监视器移除的特点,保证每一个回调只会被执行一次。对于相同的SQL语句,保证在同一个查询开始到结束的时间中永远只有一次,在这查询期间到来的调用,只需在队列中等待数据就绪即可,节省了重复的数据库调用开销。由于Node.js单线程执行的原因,此处无需担心状态问题。这种方式其实也可以应用到其他远程调用的场景中,即使外部没有缓存策略,也能有效节省重复开销。此处也可以用EventEmitter替代EventProxy,不过可能存在侦听器过多,引发警告,需要调用setMaxListeners(0)移除掉警告,或者设更大的警告阀值。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn