国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Node.js 指南(域模塊剖析)

ymyang / 3313人閱讀

摘要:快速檢查可能告訴我們,簡(jiǎn)單地從的域處理程序拋出將允許然后捕獲異常并執(zhí)行其自己的錯(cuò)誤處理程序,雖然情況并非如此,檢查后,你會(huì)看到堆棧只包含。

域模塊剖析 可用性問題 隱式行為

開發(fā)人員可以創(chuàng)建新域,然后只需運(yùn)行domain.enter(),然后,它充當(dāng)將來拋出者無法觀察到的任何異常的萬能捕捉器,允許模塊作者攔截不同模塊中不相關(guān)代碼的異常,防止代碼的發(fā)起者知道自己的異常。

以下是一個(gè)間接鏈接模塊如何影響另一個(gè)模塊的示例:

// module a.js
const b = require("./b");
const c = require("./c");


// module b.js
const d = require("domain").create();
d.on("error", () => { /* silence everything */ });
d.enter();


// module c.js
const dep = require("some-dep");
dep.method();  // Uh-oh! This method doesn"t actually exist.

由于模塊b進(jìn)入域但從不退出,任何未捕獲的異常都將被吞噬,不讓模塊c知道它為什么沒有運(yùn)行整個(gè)腳本,留下可能部分填充的module.exports。這樣做與監(jiān)聽"uncaughtException"不同,因?yàn)楹笳呙鞔_意味著全局捕獲錯(cuò)誤,另一個(gè)問題是在任何"uncaughtException"處理程序之前處理域,并阻止它們運(yùn)行。

另一個(gè)問題是,如果事件發(fā)射器上沒有設(shè)置"error"處理程序,域會(huì)自動(dòng)路由錯(cuò)誤,對(duì)此沒有可選的插入機(jī)制,而是自動(dòng)跨整個(gè)異步鏈傳播。這看起來似乎很有用,但是一旦異步調(diào)用深度為兩個(gè)或更多模塊,其中一個(gè)不包含錯(cuò)誤處理程序,域的創(chuàng)建者將突然捕獲意外異常,并且拋出者的異常將被作者忽視。

以下是一個(gè)簡(jiǎn)單的示例,說明缺少"error"處理程序如何允許活動(dòng)域攔截錯(cuò)誤:

const domain = require("domain");
const net = require("net");
const d = domain.create();
d.on("error", (err) => console.error(err.message));

d.run(() => net.createServer((c) => {
  c.end();
  c.write("bye");
}).listen(8000));

即使通過d.remove(c)手動(dòng)刪除連接也不會(huì)阻止連接的錯(cuò)誤被自動(dòng)攔截。

困擾錯(cuò)誤路由和異常處理的失敗是錯(cuò)誤被冒出的不一致,以下是嵌套域如何根據(jù)它們何時(shí)發(fā)生以及不會(huì)使異常冒出的示例:

const domain = require("domain");
const net = require("net");
const d = domain.create();
d.on("error", () => console.error("d intercepted an error"));

d.run(() => {
  const server = net.createServer((c) => {
    const e = domain.create();  // No "error" handler being set.
    e.run(() => {
      // This will not be caught by d"s error handler.
      setImmediate(() => {
        throw new Error("thrown from setImmediate");
      });
      // Though this one will bubble to d"s error handler.
      throw new Error("immediately thrown");
    });
  }).listen(8080);
});

可以預(yù)期嵌套域始終保持嵌套,并始終將異常傳播到域堆棧中,或者異常永遠(yuǎn)不會(huì)自動(dòng)冒出,不幸的是,這兩種情況都會(huì)發(fā)生,導(dǎo)致可能令人困惑的行為甚至可能難以調(diào)試時(shí)序沖突。

API差距

雖然基于使用EventEmitter的 API可以使用bind(),而errback風(fēng)格的回調(diào)可以使用intercept(),但是隱式綁定到活動(dòng)域的替代API必須在run()內(nèi)部執(zhí)行。這意味著如果模塊作者想要使用替代那些提到的機(jī)制來支持域,則他們必須自己手動(dòng)實(shí)現(xiàn)域支持,而不是能夠利用現(xiàn)有的隱式機(jī)制。

錯(cuò)誤傳播

如果可能的話,跨嵌套域傳播錯(cuò)誤并不是直截了當(dāng)?shù)模F(xiàn)有文檔顯示了如果請(qǐng)求處理程序中存在錯(cuò)誤,如何close() http服務(wù)器的簡(jiǎn)單示例,它沒有解釋的是如果請(qǐng)求處理程序?yàn)榱硪粋€(gè)異步請(qǐng)求創(chuàng)建另一個(gè)域?qū)嵗绾侮P(guān)閉服務(wù)器,使用以下作為錯(cuò)誤傳播失敗的簡(jiǎn)單示例:

const d1 = domain.create();
d1.foo = true;  // custom member to make more visible in console
d1.on("error", (er) => { /* handle error */ });

d1.run(() => setTimeout(() => {
  const d2 = domain.create();
  d2.bar = 43;
  d2.on("error", (er) => console.error(er.message, domain._stack));
  d2.run(() => {
    setTimeout(() => {
      setTimeout(() => {
        throw new Error("outer");
      });
      throw new Error("inner");
    });
  });
}));

即使在域?qū)嵗糜诒镜卮鎯?chǔ)的情況下,也可以訪問資源,仍然無法讓錯(cuò)誤繼續(xù)從d2傳播回d1。快速檢查可能告訴我們,簡(jiǎn)單地從d2的域"error"處理程序拋出將允許d1然后捕獲異常并執(zhí)行其自己的錯(cuò)誤處理程序,雖然情況并非如此,檢查domain._stack后,你會(huì)看到堆棧只包含d2

這可能被認(rèn)為是API的失敗,但即使它確實(shí)以這種方式運(yùn)行,仍然存在傳遞異??步執(zhí)行中的分支失敗的事實(shí)的問題,并且該分支中的所有進(jìn)一步操作必須停止。在http請(qǐng)求處理程序的示例中,如果我們觸發(fā)多個(gè)異步請(qǐng)求,然后每個(gè)異步請(qǐng)求將write()的數(shù)據(jù)發(fā)送回客戶端,則嘗試將write()發(fā)送到關(guān)閉的句柄會(huì)產(chǎn)生更多錯(cuò)誤,

異常資源清理

以下腳本包含在給定連接或其任何依賴項(xiàng)中發(fā)生異常的情況下在小資源依賴關(guān)系樹中正確清理的更復(fù)雜示例,將腳本分解為基本操作:

"use strict";

const domain = require("domain");
const EE = require("events");
const fs = require("fs");
const net = require("net");
const util = require("util");
const print = process._rawDebug;

const pipeList = [];
const FILENAME = "/tmp/tmp.tmp";
const PIPENAME = "/tmp/node-domain-example-";
const FILESIZE = 1024;
let uid = 0;

// Setting up temporary resources
const buf = Buffer.alloc(FILESIZE);
for (let i = 0; i < buf.length; i++)
  buf[i] = ((Math.random() * 1e3) % 78) + 48;  // Basic ASCII
fs.writeFileSync(FILENAME, buf);

function ConnectionResource(c) {
  EE.call(this);
  this._connection = c;
  this._alive = true;
  this._domain = domain.create();
  this._id = Math.random().toString(32).substr(2).substr(0, 8) + (++uid);

  this._domain.add(c);
  this._domain.on("error", () => {
    this._alive = false;
  });
}
util.inherits(ConnectionResource, EE);

ConnectionResource.prototype.end = function end(chunk) {
  this._alive = false;
  this._connection.end(chunk);
  this.emit("end");
};

ConnectionResource.prototype.isAlive = function isAlive() {
  return this._alive;
};

ConnectionResource.prototype.id = function id() {
  return this._id;
};

ConnectionResource.prototype.write = function write(chunk) {
  this.emit("data", chunk);
  return this._connection.write(chunk);
};

// Example begin
net.createServer((c) => {
  const cr = new ConnectionResource(c);

  const d1 = domain.create();
  fs.open(FILENAME, "r", d1.intercept((fd) => {
    streamInParts(fd, cr, 0);
  }));

  pipeData(cr);

  c.on("close", () => cr.end());
}).listen(8080);

function streamInParts(fd, cr, pos) {
  const d2 = domain.create();
  const alive = true;
  d2.on("error", (er) => {
    print("d2 error:", er.message);
    cr.end();
  });
  fs.read(fd, Buffer.alloc(10), 0, 10, pos, d2.intercept((bRead, buf) => {
    if (!cr.isAlive()) {
      return fs.close(fd);
    }
    if (cr._connection.bytesWritten < FILESIZE) {
      // Documentation says callback is optional, but doesn"t mention that if
      // the write fails an exception will be thrown.
      const goodtogo = cr.write(buf);
      if (goodtogo) {
        setTimeout(() => streamInParts(fd, cr, pos + bRead), 1000);
      } else {
        cr._connection.once("drain", () => streamInParts(fd, cr, pos + bRead));
      }
      return;
    }
    cr.end(buf);
    fs.close(fd);
  }));
}

function pipeData(cr) {
  const pname = PIPENAME + cr.id();
  const ps = net.createServer();
  const d3 = domain.create();
  const connectionList = [];
  d3.on("error", (er) => {
    print("d3 error:", er.message);
    cr.end();
  });
  d3.add(ps);
  ps.on("connection", (conn) => {
    connectionList.push(conn);
    conn.on("data", () => {});  // don"t care about incoming data.
    conn.on("close", () => {
      connectionList.splice(connectionList.indexOf(conn), 1);
    });
  });
  cr.on("data", (chunk) => {
    for (let i = 0; i < connectionList.length; i++) {
      connectionList[i].write(chunk);
    }
  });
  cr.on("end", () => {
    for (let i = 0; i < connectionList.length; i++) {
      connectionList[i].end();
    }
    ps.close();
  });
  pipeList.push(pname);
  ps.listen(pname);
}

process.on("SIGINT", () => process.exit());
process.on("exit", () => {
  try {
    for (let i = 0; i < pipeList.length; i++) {
      fs.unlinkSync(pipeList[i]);
    }
    fs.unlinkSync(FILENAME);
  } catch (e) { }
});

當(dāng)新連接發(fā)生時(shí),同時(shí):

在文件系統(tǒng)上打開一個(gè)文件

打開管道到獨(dú)唯一的socket

異步讀取文件的塊

將塊寫入TCP連接和任何監(jiān)聽sockets

如果這些資源中的任何一個(gè)發(fā)生錯(cuò)誤,請(qǐng)通知所有其他附加資源,他們需要清理和關(guān)閉它們

正如我們從這個(gè)例子中可以看到的,當(dāng)出現(xiàn)故障時(shí),必須采取更多措施來正確清理資源,而不是通過域API嚴(yán)格完成,所有域提供的都是異常聚合機(jī)制。即使在域中傳播數(shù)據(jù)的潛在有用能力也容易被抵消,在本例中,通過將需要的資源作為函數(shù)參數(shù)傳遞。

盡管存在意外的異常,但應(yīng)用領(lǐng)域的一個(gè)問題仍然是能夠繼續(xù)執(zhí)行(與文檔所述相反)的簡(jiǎn)單性,這個(gè)例子證明了這個(gè)想法背后的謬論。

隨著應(yīng)用程序本身的復(fù)雜性增加,嘗試對(duì)意外異常進(jìn)行適當(dāng)?shù)馁Y源清理會(huì)變得更加復(fù)雜,此示例僅具有3個(gè)基本資源,并且所有資源都具有明確的依賴路徑,如果應(yīng)用程序使用共享資源或資源重用之類的東西,那么清理能力和正確測(cè)試清理工作的能力就會(huì)大大增加。

最后,就處理錯(cuò)誤而言,域不僅僅是一個(gè)美化的"uncaughtException"處理程序,除了第三方更隱式和不可觀察的行為。

資源傳播

域的另一個(gè)用例是使用它來沿異步數(shù)據(jù)路徑傳播數(shù)據(jù),一個(gè)問題在于,當(dāng)堆棧中有多個(gè)域時(shí)(如果異步堆棧與其他模塊一起工作,則必須假定),何時(shí)期望正確的域是模糊的。此外,能夠依賴域進(jìn)行錯(cuò)誤處理同時(shí)還可以檢索必要的數(shù)據(jù)之間存在沖突。

下面是一個(gè)使用域沿著異步堆棧傳播數(shù)據(jù)失敗的示例:

const domain = require("domain");
const net = require("net");

const server = net.createServer((c) => {
  // Use a domain to propagate data across events within the
  // connection so that we don"t have to pass arguments
  // everywhere.
  const d = domain.create();
  d.data = { connection: c };
  d.add(c);
  // Mock class that does some useless async data transformation
  // for demonstration purposes.
  const ds = new DataStream(dataTransformed);
  c.on("data", (chunk) => ds.data(chunk));
}).listen(8080, () => console.log("listening on 8080"));

function dataTransformed(chunk) {
  // FAIL! Because the DataStream instance also created a
  // domain we have now lost the active domain we had
  // hoped to use.
  domain.active.data.connection.write(chunk);
}

function DataStream(cb) {
  this.cb = cb;
  // DataStream wants to use domains for data propagation too!
  // Unfortunately this will conflict with any domain that
  // already exists.
  this.domain = domain.create();
  this.domain.data = { inst: this };
}

DataStream.prototype.data = function data(chunk) {
  // This code is self contained, but pretend it"s a complex
  // operation that crosses at least one other module. So
  // passing along "this", etc., is not easy.
  this.domain.run(() => {
    // Simulate an async operation that does the data transform.
    setImmediate(() => {
      for (let i = 0; i < chunk.length; i++)
        chunk[i] = ((chunk[i] + Math.random() * 100) % 96) + 33;
      // Grab the instance from the active domain and use that
      // to call the user"s callback.
      const self = domain.active.data.inst;
      self.cb(chunk);
    });
  });
};

以上顯示,很難有多個(gè)異步API嘗試使用域來傳播數(shù)據(jù),可以通過在DataStream構(gòu)造函數(shù)中分配parent: domain.active來修復(fù)此示例,然后在調(diào)用用戶的回調(diào)之前通過domain.active = domain.active.data.parent恢復(fù)它。另外,"connection"回調(diào)中的DataStream實(shí)例化必須在d.run()中運(yùn)行,而不是簡(jiǎn)單地使用d.add(c),否則將沒有活動(dòng)域。

簡(jiǎn)而言之,為此祈禱有機(jī)會(huì)使用,需要嚴(yán)格遵守一套難以執(zhí)行或測(cè)試的準(zhǔn)則。

性能問題

使用域的重要威脅是開銷,使用node的內(nèi)置http基準(zhǔn)測(cè)試http_simple.js,沒有域,它可以處理超過22,000個(gè)請(qǐng)求/秒。如果它在NODE_USE_DOMAINS=1下運(yùn)行,那么該數(shù)字會(huì)下降到低于17,000個(gè)請(qǐng)求/秒,在這種情況下,只有一個(gè)全局域。如果我們編輯基準(zhǔn)測(cè)試,那么http請(qǐng)求回調(diào)會(huì)創(chuàng)建一個(gè)新的域?qū)嵗阅軙?huì)進(jìn)一步下降到15,000個(gè)請(qǐng)求/秒。

雖然這可能不會(huì)影響僅服務(wù)于每秒幾百甚至一千個(gè)請(qǐng)求的服務(wù)器,但開銷量與異步請(qǐng)求的數(shù)量成正比,因此,如果單個(gè)連接需要連接到其他幾個(gè)服務(wù),則所有這些服務(wù)都會(huì)導(dǎo)致將最終產(chǎn)品交付給客戶端的總體延遲。

使用AsyncWrap并跟蹤在上述基準(zhǔn)測(cè)試中調(diào)用init/pre/post/destroy的次數(shù),我們發(fā)現(xiàn)所有被調(diào)用事件的總和超過每秒170,000次,這意味著即使為每種調(diào)用增加1微秒的開銷,任何類型的設(shè)置或拆除都會(huì)導(dǎo)致17%的性能損失。

當(dāng)然,這是針對(duì)基準(zhǔn)測(cè)試的優(yōu)化方案,但我相信這演示了域等機(jī)制盡可能廉價(jià)運(yùn)行的必要性。

展望未來

域模塊自2014年12月以來一直被軟棄用,但尚未被刪除,因?yàn)閚ode目前沒有提供替代功能,在撰寫本文時(shí),正在進(jìn)行構(gòu)建AsyncWrap API的工作以及為TC39準(zhǔn)備區(qū)域的提議,在這種情況下,有適當(dāng)?shù)墓δ軄硖鎿Q域,它將經(jīng)歷完全棄用周期并最終從核心中刪除。

上一篇:流中的背壓 下一篇:如何發(fā)布N-API包

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/100372.html

相關(guān)文章

  • Node.js 指南(目錄)

    Node.js 指南 Node.js?是基于Chrome的V8 JavaScript引擎構(gòu)建的JavaScript運(yùn)行時(shí)。 常規(guī) 關(guān)于Node.js 入門指南 輕松分析Node.js應(yīng)用程序 Docker化Node.js Web應(yīng)用程序 遷移到安全的Buffer構(gòu)造函數(shù) Node.js核心概念 阻塞與非阻塞概述 Node.js事件循環(huán)、定時(shí)器和process.nextTick() 不要阻塞事...

    未東興 評(píng)論0 收藏0
  • Node.js 指南(如何發(fā)布N-API包)

    摘要:如何發(fā)布包使用包說明了以下步驟首先,發(fā)布非版本更新中的版本,對(duì)于,版本變?yōu)椤g覽發(fā)布清單確保測(cè)試演示文檔正常。因此,如果軟件包維護(hù)者選擇使用相同的標(biāo)記標(biāo)記軟件包的更高版本,則將收到更高版本的版本。 如何發(fā)布N-API包 使用包iotivity-node說明了以下步驟: 首先,發(fā)布非N-API版本: 更新package.json中的版本,對(duì)于iotivity-node,版本變?yōu)?....

    JiaXinYi 評(píng)論0 收藏0
  • 4月份前端資源分享

    摘要:更多資源請(qǐng)文章轉(zhuǎn)自月份前端資源分享關(guān)于的思考一款有趣的動(dòng)畫效果跨站資源共享之二最流行的編程語言能做什么到底什么是閉包的第三個(gè)參數(shù)跨域資源共享詳解阮一峰前端要給力之語句在中的值周愛民中國第二屆視頻花絮編碼規(guī)范前端工程師手冊(cè)奇舞周刊被忽視的 更多資源請(qǐng)Star:https://github.com/maidishike... 文章轉(zhuǎn)自:https://github.com/jsfron...

    jsdt 評(píng)論0 收藏0
  • JavaScript - 收藏集 - 掘金

    摘要:插件開發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實(shí)現(xiàn)文件分片斷點(diǎn)續(xù)傳。 Vue.js 插件開發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。插....

    izhuhaodev 評(píng)論0 收藏0
  • Node.js 指南(HTTP事務(wù)的剖析

    摘要:為了處理請(qǐng)求流上的錯(cuò)誤,我們將錯(cuò)誤記錄到并發(fā)送狀態(tài)碼以指示,但是,在實(shí)際應(yīng)用程序中,我們需要檢查錯(cuò)誤以確定正確的狀態(tài)碼和消息是什么,與通常的錯(cuò)誤一樣,你應(yīng)該查閱錯(cuò)誤文檔。通過對(duì)象發(fā)送狀態(tài)碼和數(shù)據(jù)。 HTTP事務(wù)的剖析 本指南的目的是讓你充分了解Node.js HTTP處理的過程,我們假設(shè)你在一般意義上知道HTTP請(qǐng)求的工作方式,無論語言或編程環(huán)境如何,我們還假設(shè)你對(duì)Node.js Ev...

    ASCH 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

ymyang

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<