成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

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

ymyang / 3366人閱讀

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

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

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

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

// 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進入域但從不退出,任何未捕獲的異常都將被吞噬,不讓模塊c知道它為什么沒有運行整個腳本,留下可能部分填充的module.exports。這樣做與監(jiān)聽"uncaughtException"不同,因為后者明確意味著全局捕獲錯誤,另一個問題是在任何"uncaughtException"處理程序之前處理域,并阻止它們運行。

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

以下是一個簡單的示例,說明缺少"error"處理程序如何允許活動域攔截錯誤:

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)手動刪除連接也不會阻止連接的錯誤被自動攔截。

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

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ù)期嵌套域始終保持嵌套,并始終將異常傳播到域堆棧中,或者異常永遠不會自動冒出,不幸的是,這兩種情況都會發(fā)生,導(dǎo)致可能令人困惑的行為甚至可能難以調(diào)試時序沖突。

API差距

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

錯誤傳播

如果可能的話,跨嵌套域傳播錯誤并不是直截了當?shù)?,現(xiàn)有文檔顯示了如果請求處理程序中存在錯誤,如何close() http服務(wù)器的簡單示例,它沒有解釋的是如果請求處理程序為另一個異步請求創(chuàng)建另一個域?qū)嵗?,如何關(guān)閉服務(wù)器,使用以下作為錯誤傳播失敗的簡單示例:

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ū)嵗糜诒镜卮鎯Φ那闆r下,也可以訪問資源,仍然無法讓錯誤繼續(xù)從d2傳播回d1??焖贆z查可能告訴我們,簡單地從d2的域"error"處理程序拋出將允許d1然后捕獲異常并執(zhí)行其自己的錯誤處理程序,雖然情況并非如此,檢查domain._stack后,你會看到堆棧只包含d2。

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

異常資源清理

以下腳本包含在給定連接或其任何依賴項中發(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) { }
});

當新連接發(fā)生時,同時:

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

打開管道到獨唯一的socket

異步讀取文件的塊

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

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

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

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

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

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

資源傳播

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

下面是一個使用域沿著異步堆棧傳播數(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);
    });
  });
};

以上顯示,很難有多個異步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實例化必須在d.run()中運行,而不是簡單地使用d.add(c),否則將沒有活動域。

簡而言之,為此祈禱有機會使用,需要嚴格遵守一套難以執(zhí)行或測試的準則。

性能問題

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

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

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

當然,這是針對基準測試的優(yōu)化方案,但我相信這演示了域等機制盡可能廉價運行的必要性。

展望未來

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

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

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

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

相關(guān)文章

  • Node.js 指南(目錄)

    Node.js 指南 Node.js?是基于Chrome的V8 JavaScript引擎構(gòu)建的JavaScript運行時。 常規(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)、定時器和process.nextTick() 不要阻塞事...

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

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

    JiaXinYi 評論0 收藏0
  • 4月份前端資源分享

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

    jsdt 評論0 收藏0
  • JavaScript - 收藏集 - 掘金

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

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

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

    ASCH 評論0 收藏0

發(fā)表評論

0條評論

ymyang

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<