이 글에서는 주로 Node.js 비동기 예외 처리와 도메인 모듈 분석에 대해 소개하는데, 관심 있는 친구들은 참고할 수 있습니다
비동기 예외 처리
비동기 예외의 특징
노드의 콜백 비동기 특성으로 인해 try catch를 통해 모든 예외를 catch하는 것은 불가능합니다. 예외:
try { process.nextTick(function () { foo.bar(); }); } catch (err) { //can not catch it }
웹 서비스의 경우 실제로 다음을 갖는 것이 매우 바람직합니다.
//express风格的路由 app.get('/index', function (req, res) { try { //业务逻辑 } catch (err) { logger.error(err); res.statusCode = 500; return res.json({success: false, message: '服务器异常'}); } });
try catch가 모든 예외를 포착할 수 있으면 코드에서 예상치 못한 오류를 방지할 수 있습니다. 오류를 기록하고 호출자에게 친근한 방식으로 500 오류를 반환할 수 있습니다. 불행하게도 try catch는 비동기 상황에서 예외를 포착할 수 없습니다. 따라서 우리가 할 수 있는 일은 다음과 같습니다:
app.get('/index', function (req, res) { // 业务逻辑 }); process.on('uncaughtException', function (err) { logger.error(err); });
이때 오류 로그를 기록할 수 있고 프로세스가 비정상적으로 종료되지는 않지만 잘못된 요청이 반환되었는지 확인할 방법은 없습니다. 친절하며 시간 초과가 발생한 경우에만 반환될 수 있습니다.
도메인
node v0.8+에서는 모듈 도메인이 출시되었습니다. 이 모듈은 try catch가 할 수 없는 작업, 즉 비동기 콜백에서 발생하는 예외를 catch하는 작업을 수행합니다.
위의 무력한 예에는 해결책이 있는 것 같습니다.
var domain = require('domain'); //引入一个domain的中间件,将每一个请求都包裹在一个独立的domain中 //domain来处理异常 app.use(function (req,res, next) { var d = domain.create(); //监听domain的错误事件 d.on('error', function (err) { logger.error(err); res.statusCode = 500; res.json({sucess:false, messag: '服务器异常'}); d.dispose(); }); d.add(req); d.add(res); d.run(next); }); app.get('/index', function (req, res) { //处理业务 });
비동기 예외를 처리하기 위해 미들웨어 형태로 도메인을 도입합니다. 물론 도메인이 예외를 포착했더라도 예외로 인한 스택 손실은 여전히 메모리 누수로 이어지기 때문에 관심 있는 학생은 도메인 미들웨어 도메인 미들웨어를 확인할 수 있습니다. .조각.
이상한 실패
저희 테스트는 모두 정상이었습니다. 정식으로 프로덕션 환경에서 사용해보니 갑자기 도메인이 실패하는 걸 발견했습니다! 결국 프로세스가 비정상적으로 종료되는 비동기 예외를 포착하지 못했습니다. 몇번의 조사 끝에, store session에 redis를 도입하여 문제가 발생한 것으로 확인되었습니다.
var http = require('http'); var connect = require('connect'); var RedisStore = require('connect-redis')(connect); var domainMiddleware = require('domain-middleware'); var server = http.createServer(); var app = connect(); app.use(connect.session({ key: 'key', secret: 'secret', store: new RedisStore(6379, 'localhost') })); //domainMiddleware的使用可以看前面的链接 app.use(domainMiddleware({ server: server, killTimeout: 30000 }));
이때, 저희 비즈니스 로직 코드에서 Exception이 발생했을 때 도메인에서 캡쳐가 되지 않은 것을 발견했습니다! 몇 번 시도한 끝에 마침내 문제를 발견했습니다.
var domain = require('domain'); var redis = require('redis'); var cache = redis.createClient(6379, 'localhost'); function error() { cache.get('a', function () { throw new Error('something wrong'); }); } function ok () { setTimeout(function () { throw new Error('something wrong'); }, 100); } var d = domain.create(); d.on('error', function (err) { console.log(err); }); d.run(ok); //domain捕获到异常 d.run(error); //异常被抛出
이상해요! 둘 다 비동기 호출입니다. 전자는 캡처되지만 후자는 캡처되지 않는 이유는 무엇입니까?
도메인 분석
돌아보면 비동기 요청을 캡처하기 위해 도메인이 어떤 역할을 하는지 살펴보겠습니다. (코드는 노드 v0.10.4에서 가져온 것입니다. 이 부분은 신속한 변경 최적화가 진행 중일 수 있음).
노드 이벤트루프메커니즘
도메인의 원리를 살펴보기 전에 먼저 nextTick과 _tickCall뒤로 돌아가는 방법은 2가지가 있습니다.
function laterCall() { console.log('print me later'); } process.nextTick(laterCallback); console.log('print me first');위 코드에서 노드를 작성해 본 사람이라면 누구나 익숙할 것입니다. nextTick의 기능은 laterCallback을 다음 이벤트 루프에 넣어서 실행하는 것입니다. _tickCallback 메서드는 비공개 메서드입니다. 이 메서드는 현재 시간 루프가 끝난 후 다음 이벤트 루프를 계속하기 위해 호출되는 항목
함수 입니다.
즉, 노드는 이벤트 루프에 대한 대기열을 유지하고 nextTick은 대기열에 추가되며 _tickCallback은 대기열에서 제거됩니다.도메인 구현
노드의 이벤트 루프 메커니즘을 이해한 후, 도메인이 어떤 일을 하는지 살펴보겠습니다. 도메인 자체는 실제로 이벤트를 통해 캡처된 오류를 전달하는 EventEmitter 객체입니다. 이런 식으로 연구할 때 다음 두 가지 사항으로 단순화합니다. 도메인의 오류 이벤트가 언제 발생합니까? 프로세스에서 예외가 발생했지만 어떤 프로세스에서도 포착되지 않았습니다. try catch 이때 전체 프로세스의 processFatal이 트리거되고, 도메인 패키지에 있으면 해당 도메인에서 오류 이벤트가 트리거됩니다.- 도메인이 인스턴스화되면 일반적으로 실행 메소드(예: 이전에 웹에서 사용됨)를 호출합니다. 서비스), 이 도메인 예제의 패키지에 있는 기능을 실행합니다. 래핑된 함수가 실행되면 process.domain의 전역
변수 가 이 도메인 인스턴스를 가리킵니다. 이 이벤트 루프에서 가 예외를 발생시키고 processFatal을 호출하여 process.domain이 존재하는 것을 발견하면 해당 도메인에서 오류 이벤트가 트리거됩니다.
- require가 도메인 모듈을 도입한 후 전역 nextTick 및 _tickCallback이 다시 작성되고 일부 도메인 관련 코드가 삽입됩니다.
//简化后的domain传递部分代码 function nextDomainTick(callback) { nextTickQueue.push({callback: callback, domain: process.domain}); } function _tickDomainCallback() { var tock = nextTickQueue.pop(); //设置process.domain = tock.domain tock.domain && tock.domain.enter(); callback(); //清除process.domain tock.domain && tock.domain.exit(); } };
这个是其在多个事件循环中传递domain的关键:nextTick入队的时候,记录下当前的domain,当这个被加入队列中的事件循环被_tickCallback启动执行的时候,将新的事件循环的process.domain置为之前记录的domain。这样,在被domain所包裹的代码中,不管如何调用process.nextTick, domain将会一直被传递下去。
当然,node的异步还有两种情况,一种是event形式。因此在EventEmitter的构造函数有如下代码:
if (exports.usingDomains) { // if there is an active domain, then attach to it. domain = domain || require('domain'); if (domain.active && !(this instanceof domain.Domain)) { this.domain = domain.active; } }
实例化EventEmitter的时候,将会把这个对象和当前的domain绑定,当通过emit触发这个对象上的事件时,像_tickCallback执行的时候一样,回调函数将会重新被当前的domain包裹住。
而另一种情况,是setTimeout和setInterval,同样的,在timer的源码中,我们也可以发现这样的一句代码:
if (process.domain) timer.domain = process.domain;
跟EventEmmiter一样,之后这些timer的回调函数也将被当前的domain包裹住了。
node通过在nextTick, timer, event三个关键的地方插入domain的代码,让它们得以在不同的事件循环中传递。
更复杂的domain
有些情况下,我们可能会遇到需要更加复杂的domain使用。
domain嵌套:我们可能会外层有domain的情况下,内层还有其他的domain,使用情景可以在文档中找到
// create a top-level domain for the server var serverDomain = domain.create(); serverDomain.run(function() { // server is created in the scope of serverDomain http.createServer(function(req, res) { // req and res are also created in the scope of serverDomain // however, we'd prefer to have a separate domain for each request. // create it first thing, and add req and res to it. var reqd = domain.create(); reqd.add(req); reqd.add(res); reqd.on('error', function(er) { console.error('Error', er, req.url); try { res.writeHead(500); res.end('Error occurred, sorry.'); } catch (er) { console.error('Error sending 500', er, req.url); } }); }).listen(1337); });
为了实现这个功能,其实domain还会偷偷的自己维持一个domain的stack,有兴趣的童鞋可以在这里看到。
回头解决疑惑
回过头来,我们再来看刚才遇到的问题:为什么两个看上去都是同样的异步调用,却有一个domain无法捕获到异常?理解了原理之后不难想到,肯定是调用了redis的那个异步调用在抛出错误的这个事件循环内,是不在domain的范围之内的。我们通过一段更加简短的代码来看看,到底在哪里出的问题。
var domain = require('domain'); var EventEmitter = require('events').EventEmitter; var e = new EventEmitter(); var timer = setTimeout(function () { e.emit('data'); }, 10); function next() { e.once('data', function () { throw new Error('something wrong here'); }); } var d = domain.create(); d.on('error', function () { console.log('cache by domain'); }); d.run(next);
此时我们同样发现,错误不会被domain捕捉到,原因很清晰了:timer和e两个关键的对象在初始化的时候都时没有在domain的范围之内,因此,当在next函数中监听的事件被触发,执行抛出异常的回调函数时,其实根本就没有处于domain的包裹中,当然就不会被domain捕获到异常了!
其实node针对这种情况,专门设计了一个API:domain.add。它可以将domain之外的timer和event对象,添加到当前domain中去。对于上面那个例子:
d.add(timer); //or d.add(e);
将timer或者e任意一个对象添加到domain上,就可以让错误被domain捕获了。
再来看最开始redis导致domain无法捕捉到异常的问题。我们是不是也有办法可以解决呢?
其实对于这种情况,还是没有办法实现最佳的解决方案的。现在对于非预期的异常产生的时候,我们只能够让当前请求超时,然后让这个进程停止服务,之后重新启动。graceful模块配合cluster就可以实现这个解决方案。
domain十分强大,但不是万能的。希望在看过这篇文章之后,大家能够正确的使用domian,避免踩坑。
【相关推荐】
1. 免费js在线视频教程
3. php.cn独孤九贱(3)-JavaScript视频教程
위 내용은 js의 비동기 예외 처리 및 domian 튜토리얼 사용에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

JavaScript 문자열 교체 방법 및 FAQ에 대한 자세한 설명 이 기사는 JavaScript에서 문자열 문자를 대체하는 두 가지 방법 인 내부 JavaScript 코드와 웹 페이지의 내부 HTML을 탐색합니다. JavaScript 코드 내부의 문자열을 교체하십시오 가장 직접적인 방법은 대체 () 메소드를 사용하는 것입니다. str = str.replace ( "find", "replace"); 이 메소드는 첫 번째 일치 만 대체합니다. 모든 경기를 교체하려면 정규 표현식을 사용하고 전역 플래그 g를 추가하십시오. str = str.replace (/fi

기사는 JavaScript 라이브러리 작성, 게시 및 유지 관리, 계획, 개발, 테스트, 문서 및 홍보 전략에 중점을 둡니다.

이 기사는 브라우저에서 JavaScript 성능을 최적화하기위한 전략에 대해 설명하고 실행 시간을 줄이고 페이지로드 속도에 미치는 영향을 최소화하는 데 중점을 둡니다.

매트릭스 영화 효과를 페이지에 가져 오십시오! 이것은 유명한 영화 "The Matrix"를 기반으로 한 멋진 jQuery 플러그인입니다. 플러그인은 영화에서 클래식 그린 캐릭터 효과를 시뮬레이션하고 사진을 선택하면 플러그인이 숫자로 채워진 매트릭스 스타일 사진으로 변환합니다. 와서 시도해보세요. 매우 흥미 롭습니다! 작동 방식 플러그인은 이미지를 캔버스에로드하고 픽셀 및 색상 값을 읽습니다. data = ctx.getImageData (x, y, settings.grainsize, settings.grainsize) .data 플러그인은 그림의 직사각형 영역을 영리하게 읽고 jQuery를 사용하여 각 영역의 평균 색상을 계산합니다. 그런 다음 사용하십시오

이 기사는 브라우저 개발자 도구를 사용하여 효과적인 JavaScript 디버깅, 중단 점 설정, 콘솔 사용 및 성능 분석에 중점을 둡니다.

이 기사에서는 jQuery 라이브러리를 사용하여 간단한 사진 회전 목마를 만들도록 안내합니다. jQuery를 기반으로 구축 된 BXSLIDER 라이브러리를 사용하고 회전 목마를 설정하기위한 많은 구성 옵션을 제공합니다. 요즘 그림 회전 목마는 웹 사이트에서 필수 기능이되었습니다. 한 사진은 천 단어보다 낫습니다! 그림 회전 목마를 사용하기로 결정한 후 다음 질문은 그것을 만드는 방법입니다. 먼저 고품질 고해상도 사진을 수집해야합니다. 다음으로 HTML과 일부 JavaScript 코드를 사용하여 사진 회전 목마를 만들어야합니다. 웹에는 다양한 방식으로 회전 목마를 만드는 데 도움이되는 라이브러리가 많이 있습니다. 오픈 소스 BXSLIDER 라이브러리를 사용할 것입니다. BXSLIDER 라이브러리는 반응 형 디자인을 지원 하므로이 라이브러리로 제작 된 회전 목마는

JavaScript를 사용하여 강화 된 구조적 태그를 향상 시키면 파일 크기를 줄이면 웹 페이지 컨텐츠의 접근성 및 유지 관리 가능성을 크게 향상시킬 수 있습니다. JavaScript는 인용 속성을 사용하여 참조 링크를 블록 참조에 자동으로 삽입하는 등 HTML 요소에 기능을 동적으로 추가하는 데 효과적으로 사용될 수 있습니다. 구조화 된 태그와 JavaScript를 통합하면 페이지 새로 고침이 필요하지 않은 탭 패널과 같은 동적 사용자 인터페이스를 만들 수 있습니다. JavaScript가 웹 페이지의 기본 기능을 방해하지 않도록하는 것이 중요합니다. 고급 JavaScript 기술을 사용할 수 있습니다 (

데이터 세트는 API 모델 및 다양한 비즈니스 프로세스를 구축하는 데 매우 필수적입니다. 그렇기 때문에 CSV 가져 오기 및 내보내기가 자주 필요한 기능인 이유입니다.이 자습서에서는 각도 내에서 CSV 파일을 다운로드하고 가져 오는 방법을 배웁니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

Dreamweaver Mac版
시각적 웹 개발 도구

DVWA
DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

드림위버 CS6
시각적 웹 개발 도구

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

SublimeText3 Linux 새 버전
SublimeText3 Linux 최신 버전

뜨거운 주제



