Promise.allSettled()
사용법을 익혀 비동기 처리 병렬처리 코드를 작성해본다.Node.js version: 14.10.5
ECMAScript: 2020 // 2020 이상 필수
AWS API Gateway (WebSocket0 + Lambda + SQS + DynamoDB
3,000개의 WebSocket Connection 존재 (동시 접속자 3천명)
AWS Lambda call을 통해 Broadcast Message 전파
이 때 Broadcast가 완료되기 까지의 소요 시간 측정
[코드 구성]
try catch + if in loop
동시 접속자 한명 한명에게 메시지를 보내는 방식으로, 성공적으로 보내면 다음 connectionId 에 메시지를 송신하고 웹소켓 연결이 stale 상태 (유휴 상태) 면 statusCode 410 에러를 발생시킨다.
메시지 성공/실패여부가 결정된 다음에 다음 사람에게 메시지를 보내는 동기적 처리다.
for (const connectionId of connections) {
try {
// 겉보기엔 await 걸어서 비동기로 보이지만
// 하나씩 수행하면서 에러걸리면 넘기는식으로 동기적 처리 방식.
await apiGatewayManager.postToConnection({
ConnectionId: connectionId,
Data: JSON.stringify({
message: '~~~',
})
}).promise();
} catch (error) {
// stale state connection error
if (error.statusCode === 410) {
staleConnectionIdArray.push(connectionId);
} else {
next(error);
}
}
}
[코드 구성]
Promise.allSettled
는 보통의 Promise API와 달리 reject 된 결과또한 catch
가 아닌 then
으로 받아온다.
동기적 처리와 달리 메시지 송신의 성공/실패 여부와 상관없이 3,000명 모두에게 메시지를 따발총쏘듣 다 쏴버리고 송신이 모두 완료된 다음 결과를 Promise.allSettled().then()
으로 받아온다.
Promise.allSettled().then() 에서 받아오는 객체는 '배열 (iterable)' 이다. 이 배열엔
Promise.resolve()
=>{status: fulfilled}
//성공
Promise.reject()
=>{status: rejected}
//실패
성공, 실패 결과가 모두 배열에 담아져온다.
const broadCastPromises : Promise<any>[] = [];
// Broad cast amen total count
for (const connectionId of connections) {
// promises 배열에 담기만하고 기다리지 않고 다음 사람에게 메시지 전송
broadCastPromises.push(apiGatewayManager.postToConnection({
ConnectionId: connectionId,
Data: JSON.stringify({
message: '~~~',
})
}).promise()
.catch((error : AWSError)=>{
//@ts-ignore
error.connectionId = connectionId;
// 여기서 던진 error 는 Promise.allSettled의 catch 가 아닌 `then`에서 받는다.
throw error;
})
);
}
// 모든 메시지 전송이 완료되면
await Promise.allSettled(broadCastPromises)
.then((results) => {
// throw error 로 위에서 던진 에러를 promiseResult.status : rejected 로 받는다.
const failResults = results.filter(({status}) => status === 'rejected')
.map((failResult) => {
//@ts-ignore
return failResult.reason;
});
console.log('===================================');
failResults.forEach((error: AWSError)=>{
if (error.code === 'GoneException') {
//@ts-ignore
staleConnectionIdArray.push(error.connectionId);
}
});
})
.catch(next);
- 비동기로 많은 작업을 처리하고
- ⭐ 성공-실패 결과 별로 분기 처리가 필요할 때⭐
브로드캐스트 수행 속도만으로 대략 10배 차이를 보였다.
3천명 접속자일 때 이정도면 접속자 수가 3만명 단위로 넘어간다고 생각해보면 동기적 처리는 쓸 수 없을 정도다.
웹에서의 비동기처리는 프론트 백 구분할 것 없이 트랜드가 아니라 기본 소양이 아닌가 싶다.