Node.js 비동기 이상 처리 및 domain 모듈 분석
비동기 이상 특징
node 의 반전 비동기 특성 으로 인해 try 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);
});
이 때,비록 우 리 는 이 잘못된 로 그 를 기록 할 수 있 고,프로 세 스 도 이상 하 게 종료 되 지 않 지만,우 리 는 잘못된 요청 을 발견 한 것 에 대해 우호 적 으로 돌아 갈 수 없고,시간 을 초과 해서 돌아 올 수 있 을 뿐이다.domain
node v 0.8+버 전에 서 모듈 domain 을 발 표 했 습 니 다.이 모듈 은 try 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) {
//
});
우 리 는 중간 부품 의 형식 을 통 해 domain 을 도입 하여 비동기 중의 이상 을 처리 합 니 다.물론 domain 은 이상 을 포 착 했 지만 이상 으로 인 한 스 택 손실 로 인해 메모리 가 누 출 될 수 있 기 때문에 이런 상황 이 발생 했 을 때 이 프로 세 스 를 다시 시작 해 야 합 니 다.관심 있 는 학생 들 은 domain-middleware 라 는 domain 미들웨어 를 보 러 갈 수 있 습 니 다.기괴 한 실효
우리 의 테스트 는 모든 것 이 정상 입 니 다.정식으로 생산 환경 에서 사용 할 때 domain 이 갑자기 효력 을 잃 었 습 니 다!비동기 의 이상 을 포착 하지 못 해 프로 세 스 가 이상 하 게 종료 되 었 습 니 다.한 차례 의 조 사 를 통 해,마지막 으로 redis 를 도입 하여 session 을 저장 하기 때문에 발생 한 것 을 발견 하 였 다.
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
}));
이때,우리 의 업무 논리 코드 에 이상 이 생 겼 을 때,뜻밖에도 domain 에 의 해 포착 되 지 않 았 음 을 발견 하 였 습 니 다!한 번 의 시 도 를 거 쳐 마침내 문 제 를 찾 았 다.
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); //
이상 하 다!모두 비동기 호출 인 데 왜 전 자 는 잡 히 고 후 자 는 잡 을 수 없 습 니까?도 메 인 분석
돌 이 켜 보면 domain 이 무엇 을 했 는 지 알 아 보 겠 습 니 다.
node 이벤트 순환 메커니즘
Domain 의 원 리 를 보기 전에 nextTick 과 에 대해 알 아 보 겠 습 니 다.tick Callback 의두 가지 방법.
function laterCall() {
console.log('print me later');
}
process.nextTick(laterCallback);
console.log('print me first');
위의 코드 에 node 라 고 쓰 여 있 는 사람들 은 모두 잘 알 고 있 습 니 다.nextTick 의 역할 은 later Callback 을 다음 이벤트 순환 에 두 고 실행 하 는 것 입 니 다.그리고tickcallback 방법 은 비공개 적 인 방법 입 니 다.이 방법 은 현재 시간 순환 이 끝 난 후에 다음 이벤트 순환 을 계속 하 는 입구 함수 로 호출 됩 니 다.다시 말 하면 node 는 이벤트 순환 을 위해 하나의 대열 을 유 지 했 습 니 다.nextTick 입 대 는tick Callback 이 나 옵 니 다.
domain 의 실현
node 의 이벤트 순환 체 제 를 알 게 된 후에 domain 이 무엇 을 했 는 지 다시 봅 시다.
domain 자 체 는 이벤트 Emitter 대상 으로 이벤트 방식 으로 캡 처 된 오 류 를 전달 합 니 다.이렇게 해서 우 리 는 그것 을 연구 할 때 두 가지 점 으로 간소화 한다.
domain 의 error 이 벤트 를 언제 실행 합 니까?
프로 세 스 가 이상 을 던 졌 습 니 다.try catch 에 잡 히 지 않 았 습 니 다.이 때 전체 프로 세 스 의 processFatal 을 촉발 합 니 다.이 때 domain 패키지 에 있 으 면 domain 에서 error 사건 을 촉발 합 니 다.반대로 process 에서 uncaughtException 사건 을 촉발 합 니 다.
domain 은 여러 이벤트 순환 에서 어떻게 전달 합 니까?
// 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 의 비동기 에는 두 가지 상황 이 있 는데 하 나 는 이벤트 형식 이다.따라서 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 와 이벤트 대상 을 현재 domain 에 추가 할 수 있 습 니 다.위의 그 예 에 대해:
d.add(timer);
//or
d.add(e);
timer 나 e 의 임의의 대상 을 domain 에 추가 하면 오 류 를 domain 에 캡 처 할 수 있 습 니 다.다시 보면 처음에 redis 로 인해 domain 에서 이상 한 문 제 를 포착 하지 못 했 습 니 다.우리 도 해결 할 방법 이 있 지 않 을까요?
사실 이런 상황 에 대해 서 는 최선 의 해결 방안 을 실현 할 방법 이 없다.현재 예상 치 못 한 이상 이 발생 했 을 때,우 리 는 현재 요청 이 시간 을 초과 한 후에 이 프로 세 스 가 서 비 스 를 중단 한 후에 다시 시작 할 수 있 습 니 다.graceful 모듈 은 cluster 와 결합 하면 이 해결 방안 을 실현 할 수 있 습 니 다.
__domain 은 매우 강하 지만 만능 은 아 닙 니 다.이 글 을 본 후에 도 메 인 을 정확하게 사용 하여 구 덩이 를 밟 지 않 기 를 바 랍 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Node.js를 AWS서버에서 사용하는 실습간단한 예제와 함께 AWS에서 Node.js를사용하는 법을 배워보도록 하겠다. 해당 github에 있는 레포지토리로 사용을 할 것이다. 3000번 포트로 Listen되는 예제이고 간단히 GET, POST, DELET...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.