node.js は、シングルスレッドの非ブロッキング非同期 I/O に基づいています。非同期 I/O とは、I/O 操作が発生したときに、スレッドがブロックせずに次の操作を実行し、その後 I/O を実行することを意味します。操作が完了しました。スレッドは操作が完了したことをどのようにして知るのでしょうか?
時間のかかる I/O 操作が完了すると、I/O 操作のスレッドはイベントの形式で通知され、スレッドは特定の時間にイベントを処理し、次の操作を実行します。非同期 I/O を完了するには、スレッドが未完了のイベントの有無を常に確認し、それらのイベントの処理を順番に完了するイベント ループ機構を備えている必要があります。
I/O をブロックする場合、スレッドは時間のかかる I/O 操作に遭遇すると、実行を停止し、その操作が完了するまで待機します。このとき、スレッドは他の操作リクエストを受け入れることができません。スループットを高めるには、複数の操作を作成する必要があります。各スレッドは顧客のリクエストに応答しますが、同時に CPU コア上で実行できるスレッドは 1 つだけです。複数のスレッドを実行する場合は、異なるスレッド間で切り替える必要があります。
したがって、node.js は、マルチスレッドでのスレッド作成とスレッド切り替えのコストを削減し、同時にメモリを割り当ててスケジュールに組み込む必要があります。スレッド切り替え時に実行する処理をシングルスレッドにすることでページ変更などの操作を軽減できます。ただし、このプログラミング方法には欠点もあり、人々の設計思考と一致していません。
node.js はイベントモードに基づいて非同期 I/O を実装します。起動すると、未完了のイベントがあるかどうかを継続的に走査し、実行が完了するとスレッドに通知されます。別のイベントの形式の場合、この操作は完了しており、このイベントは次回のある時点でこのイベントを走査し、このメカニズムでは大きなタスクを実行します。小規模なイベントの場合、node.js は、一部の高 I/O および低ロジック シナリオの処理にも適しています。
次の例は、非同期ファイルの読み取りを示しています:
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");
上記のように、fs.readFile
はファイルを非同期的に読み取り、その後、他のファイルの読み取り後、プロセスは待機せずに続行されます。ファイルが読み取られると、イベントが解放されます。実行スレッドはイベントを走査し、対応する操作を実行します。この例では、ファイルの内容が来る前に文字列の end が出力されます。外。 fs.readFile
异步读取文件,之后流程就会继续走,并不会等待其读取完文件,当文件读取完毕之后,会发布一个事件,执行线程遍历到该事件就会去执行对应的操作,这里是执行相应的回调函数,例子中字符串end会比文件内容先打印出来。
node.js的事件API
events.EventEmitter
:EventEmitter对node.js中的事件发射与事件监听功能提供了封装,每个事件由一个标识事件名的字符串和对应的操作组成。
事件的监听:
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事件发生") })
事件的发布:
emitter.emit("eventName");
发布事件的时候我们可以传入多个参数,第一个参数表示事件的名称,其后的参数表示传入的参数,这些参数会被传入到事件的回调函数中。
EventEmitter.once("eventName", listener)
:为事件注册一个只执行一次的监听器,当事件第一次发生并触发监听器之后,该监听器就会解除,之后如果事件发生,该监听器不会执行。
EventEmitter.removeListener(event, listener)
:移除掉事件的监听器
EventEmitter.removeAllListeners(event)
:移除掉事件的所有的监听器
EventEmitter.setMaxListeners(n)
:node.js默认单个事件最大的监听器个数是10,如果超过10会给予警告,这么做是为了防止内存的溢出,我们可以更改这种限制设置为其他的数字,如果设置为0表示不进行限制。
EventEmitter.listeners(event)
node.js イベント APIevents.EventEmitter
: EventEmitter は、node.js でのイベント発行機能とイベントリスニング機能のカプセル化を提供します。各イベントはイベント名によって識別されます。文字列と対応する操作。
イベント監視:
api.getUser("username", function (profile) { // Got the profile }); api.getTimeline("username", function (timeline) { // Got the timeline }); api.getSkin("username", function (skin) { // Got the skin });
イベント公開:
api.getUser("username", function (profile) { api.getTimeline("username", function (timeline) { api.getSkin("username", function (skin) { // TODO }); }); });
EventEmitter.once("eventName",listener)
: 1 回だけ実行されるイベントのリスナーを登録します。イベントが初めて発生し、リスナーがトリガーされると、リスナーはが解放されると、イベントが発生してもリスナーは実行されません。 🎜🎜🎜EventEmitter.removeListener(event,listener)
: イベントのリスナーを削除します 🎜🎜🎜EventEmitter.removeAllListeners(event)
: イベント リスナーのすべてのリスナーを削除します🎜🎜🎜EventEmitter.setMaxListeners(n)
:node.js のデフォルトでは、単一イベントのリスナーの最大数は 10 です。10 を超えると、メモリの使用を防ぐため、警告が表示されます。この制限設定を他の数値に変更できます。0 に設定すると、制限がないことを意味します。 🎜🎜🎜EventEmitter.listeners(event)
: イベントのリスナーのリストを返します🎜🎜🎜🎜複数のイベント間のコラボレーション🎜🎜🎜少し大きなアプリケーションでは、データと Web サーバーの間の分離は避けられません、新浪微博、フェイスブック、ツイッターなど。この利点は、データ ソースが統一され、同じデータ ソースに対してさまざまなリッチ クライアント プログラムを開発できることです。 🎜🎜Web アプリケーションを例に挙げると、ページをレンダリングするときは、通常、複数のデータ ソースからデータを取得し、最終的にクライアントにレンダリングする必要があります。このシナリオでは、Node.js は複数のデータ ソースへのリクエストを同時に並行して自然かつ便利に開始できます。 🎜🎜🎜🎜🎜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); });
var select = function (callback) { db.select("SQL", function (results) { callback(results); }); };
为解决这类问题,我曾写作一个模块来实现多事件协作,以下为上面代码的改进版:
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)
移除掉警告,或者设更大的警告阀值。
以上がNode.jsでのイベントリスニングとイベントパブリッシングの使用例の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。