-
Notifications
You must be signed in to change notification settings - Fork 2
๐ค RabbitMQ๋ก ๋ถ์ฐ ์๋ฒ์๊ฒ ๋ฉ์์ง๋ฅผ ๋ถ๋ฐฐํ๊ธฐ
๋ถ์ผ | ์์ฑ์ | ์์ฑ์ผ |
---|---|---|
BE | ๊น๋ฏผ์ | 24๋ 11์ 17์ผ |
ํ์ฌ ํ๋ก์ ํธ์์ ์ค์๊ฐ์ผ๋ก ํ๊ตญํฌ์์ฆ๊ถ API๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ณ ์์ง๋ง, ๋จ์ผ ์ธ์คํด์ค๋ก๋ ํ๊ณ๋ฅผ ๋๋ผ๊ฒ ๋์๊ณ ์ด๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํด ์คํ ์ค์ผ์ผ๋ง์ ๊ณ ๋ คํ๊ฒ ๋์๋ค. ์ด๋ ๊ฐ ์๋ฒ๋ง๋ค ๋ฐ์์ผํ๋ ์ฃผ์์ ํ ๋นํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์ ๊ณ ๋ฏผํ๋ค๊ฐ ๋ฉ์์ง ํ๋ฅผ ํ์ฉํ์ฌ ๋ถ๋ฐฐํ๊ธฐ๋ก ํ๋ค.
์คํ ์์ค ๋ฉ์์ง ๋ธ๋ก์ปค๋ก, ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ ๋ฉ์์ง๋ฅผ ์ก์์ ํ๋๋ฐ ์ฌ์ฉ๋๋ค. ์ฃผ๋ก ๋ฉ์์ง ํ ์ญํ ์ ์ํํ๋ค.
-
Publisher
: ๋ฉ์์ง๋ฅผ ๋ณด๋ด๋ ์ฃผ์ฒด -
Consumer
:Publisher
๋ก๋ถํฐ ๋ฉ์์ง๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ๋ ์ฃผ์ฒด -
Exchange
:Publisher
๋ก๋ถํฐ ์ ๋ฌ ๋ฐ์ ๋ฉ์์ง๋ฅผ ํ๋ก ์ ๋ฌํ๋ ๊ณณ -
Queue
:Consumer
๊ฐ ๋ฉ์์ง๋ฅผ ์๋นํ๊ธฐ ์ ๊น์ง ๋ณด๊ดํ๋ ์ฅ์ -
Binding
:Exchange
์Queue
์ ๊ด๊ณ, ๋ณดํต ์ฌ์ฉ์๊ฐ ํน์ exchange
๊ฐ ํน์ queue
๋ฅผbinding
ํ๋๋ก ์ ์ํ๋ค.
์ฐ์ rabbitMQ๋ฅผ docket์ ์ค์นํ๊ธฐ ์ํด ์๋์ ๋ช ๋ น์ด๋ฅผ ํตํด์ ์ด๋ฏธ์ง๋ฅผ ๋ค์ด๋ฐ์ ํ ์ปจํ ์ด๋๋ฅผ ์คํํ๋ค.
// rabbitmq ์ด๋ฏธ์ง ์ค์น
docker pull rabbitmq
// ์ปจํ
์ด๋ ์คํ
docker run -d -p 15672:15672 -p 5672:5672 --name rabbitmq rabbitmq
// rabbitmq_management ์ค์น
docker exec rabbitmq rabbitmq-plugins enable rabbitmq_management
์ดํ localhost:15672
์ ์ ์ํ๋ฉด ๊ด๋ฆฌ์ ํ์ด์ง์ ์ ์ํ ์ ์๋ค. ์ด๋ ์์ด๋์ ๋น๋ฐ๋ฒํธ๋ ๋ชจ๋ guest์ด๋ค.
์ฐ์ ์์ฐ์๋ ๊ฐ๋จํ๊ฒ node๋ง์ผ๋ก ๋ง๋ค์๋ค. amqplib
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด์ ํ๋ฅผ ์ ํ์ฌ ๋ฉ์์ง๋ฅผ ์ ๋ฌํ๋๋ก ํ๋ค.
๋จผ์ RabbitMQ ์๋ฒ์ ์ฐ๊ฒฐํ๊ณ ์ฑ๋์ ์์ฑํ๋ค. ์ด๋ amqp://localhost
์ localhost์ rabbitMQ์ ๊ธฐ๋ณธ ํฌํธ(5672)๋ก ์ ์ํ๊ฒ ๋๋ฏ๋ก ํฌํธ๊ฐ ๋ค๋ฅด๋ฉด ์์ ํด์ผํ๋ค.
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
์ฑ๋์ ์์ฑํ ํ assertQueue๋ก ํ๋ฅผ ํ ๋นํ๋ค. ์ด๋ ํ๊ฐ ์์ผ๋ฉด ์์ฑํ๋ค.
await channel.assertQueue(queue, {
durable: true, // ํ๊ฐ ์๋ฒ๊ฐ ์ฌ์์ํด๋ ์ ์ง๋๋๋ก ์ค์
});
์ดํ ์ํ๋ ๋ก์ง์ ์คํํ๋ค. ์ด๋ฒ์ ์งํํ ๋ก์ง์ 100๊ฐ์ ๋ฉ์์ง๋ฅผ ํ์ ๋ฃ๋ ๊ฒ์ด๋ค.
// ๋ฉ์์ง ์ ์ก
for (let i = 0; i< 100; i++) {
const message = `Hello World! ${i}`;
channel.sendToQueue(queue, Buffer.from(Buffer.from(
JSON.stringify({
pattern: 'hello',
data: {source: message},
})
)), {
persistent: true
});
console.log(`sent: ${message}`);
}
๋ฉ์์ง ๋ด์ฉ์ pattern
๊ณผ data
๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
- pattern: ์ด๋ฒคํธ ํจํด์ ๋ํ๋ด๋ฉฐ nestjs์์ ํน์ ์ด๋ฒคํธ ํจํด ๋ฉ์์ง๋ฅผ ๋ฐ๊ฒ๋๋ค.
- data: ์ ๋ฌํ๋ ๋ฉ์์ง
๋ํ ๋ฉ์์ง๋ฅผ ํ์ ์ ๋ฌํ ๋ persistent๋ผ๋ ์ต์ ์ ์ค์ ํ๋๋ฐ ๋ฉ์์ง ๋ธ๋ก์ปค๊ฐ ์ฌ์์ํด๋ ์ ์งํ๋ ์ต์ ์ด๋ค.
const amqp = require('amqplib');
async function sendTask() {
const queue = 'kospi';
try {
// RabbitMQ ์๋ฒ์ ์ฐ๊ฒฐ
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
// ํ ์์ฑ (์์ผ๋ฉด ์์ฑ)
await channel.assertQueue(queue, {
durable: true, // ํ๊ฐ ์๋ฒ๊ฐ ์ฌ์์ํด๋ ์ ์ง๋๋๋ก ์ค์
});
// ๋ฉ์์ง ์ ์ก
for (let i = 0; i< 100; i++) {
const message = `Hello World! ${i}`;
channel.sendToQueue(queue, Buffer.from(Buffer.from(
JSON.stringify({
pattern: 'hello',
data: {source: message},
})
)), {
persistent: true,
});
console.log(`sent: ${message}`);
}
// ์ฐ๊ฒฐ ์ข
๋ฃ
await channel.close();
await connection.close();
} catch (error) {
console.error('Error sending task:', error);
}
}
sendTask();
์๋น์๋ ๊ธฐ์กด ์๋ฒ๊ฐ ์ฌ์ฉํ๋ nestjs๋ก ๊ตฌ์ฑํ๋๋ก ํ๋ค. ๋จผ์ ์ฐ๊ด๋ ํจํค์ง๋ฅผ ์ค์นํ๋ค.
npm i --save amqplib amqp-connection-manager @nestjs/microservices
์ดํ main ํ์ผ์์ ๋ง์ดํฌ๋ก์๋น์ค๋ฅผ ์ค์ ํ ํ ์์ํ๋๋ก ํ๋ค.
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.RMQ,
options: {
urls: ['amqp://localhost'],
queue: 'kospi',
noAck: false,
queueOptions: {
durable: true,
},
},
});
await app.startAllMicroservices();
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
์ด๋ rabbitmq์ ๊ด๋ จ๋ ์ต์ ์ ์ค์ ํ๋๋ฐ ๊ฐ ์ต์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- urls - rabbitmq ์ฃผ์
- queue - ํ ์ด๋ฆ
- noAck - ๋ก์ง์ด ์๋ฃ๋ ํ ack ํธ์ถ ์์ด ์๋์ผ๋ก ๋ณด๋ด๋ ์ต์ ์ด๋ค.
- queueOptions - ํ์ ์ฐ๊ด๋ ์ต์
์ดํ ์์ฐ์์ ์ ๋ฌํ๋ ์ด๋ฒคํธ ํจํด ๋ฉ์์ง๋ฅผ ๋ฐ๋ ์ปจํธ๋กค๋ฌ๋ฅผ ๊ตฌํํ๋ค.
@Controller()
export class AppController {
@EventPattern('hello')
async handleMessage(@Payload() data: number[], @Ctx() context: RmqContext) {
const channel = context.getChannelRef();
const originalMsg = context.getMessage();
console.log('data:', data);
channel.ack(originalMsg);
}
}
์ดํ 2๊ฐ์ nestjs ์ ํ๋ฆฌ์ผ์ด์ ์ ์คํ ํ 100๊ฐ์ ๋ฉ์์ง๋ฅผ ๋ฐ์ ๋ ์๋์ ๊ฐ์ด ์ ๋ถ๋ฐฐ๊ฐ ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ด์ ์ noAck
๋ฅผ false
๋ก ์ค์ ํ๋ค. ์ด๋ ๊ฒ ํ ์ด์ ๋ ๋ก์ง์ด ์คํจํ์ ๋ ๋ฉ์์ง๊ฐ ์ฌ๋ผ์ง์ง ์๊ณ ๋ค์ ๋ฉ์์ง ํ๋ก ์ ๋ฌํ๊ธฐ ์ํจ์ด๋ค.
noAck
๋ฅผ false
๋ก ํ ์ํ๋ก ack
๋ฅผ ํธ์ถํ์ง ์์ผ๋ฉด ์๋์ผ๋ก rabbitMQ๋ก ๋์๊ฐ ๊ฒ์ด๋ผ ์์๋์ง๋ง ๊ทธ๋ ์ง ์๋ค. ์ฐ์ ์ด์ ์ ์ฝ๋์์ 0.5ํ๋ฅ ๋ก ack๋ฅผ ํธ์ถํ์ง ์๋๋ก ํ๊ณ , 1๊ฐ์ ์๋ฒ๋ง ์คํํ๋ค
@EventPattern('hello')
async handleMessage(@Payload() data: number[], @Ctx() context: RmqContext) {
const channel = context.getChannelRef();
const originalMsg = context.getMessage();
if (Math.random() > 0.5) {
return;
}
console.log('data:', data);
channel.ack(originalMsg);
}
์ดํ ์คํํ์ ๋ ์๋์ ๊ฒฐ๊ณผ์ฒ๋ผ ์ผ๋ถ ๋ฉ์์ง๊ฐ ack
๊ฐ ๋์ง ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๊ทธ๋ ๋ค๋ฉด RabbitMQ
์ ๋ฉ์์ง๊ฐ ๋์์๋์ง ํ์ธํด๋ณด์
RabbitMQ
์์๋ ๋ชจ๋ ๋ฉ์์ง๊ฐ ์ ๋ฌ๋์ด ๋น์ด์๋ ์ํ์ด๋ค. ์ด๋ฒ์๋ ์๋ฒ๋ฅผ ๊ฐ์ ๋ก ์ค์งํ๊ณ ๋ค์ ํ์ธํด๋ณด์
๋๋๊ฒ๋ ์๋ฒ๊ฐ ์ข
๋ฃ๋ ๋ ๋ค์ ๋ฉ์์ง๊ฐ ์ฑ์์ง ๊ฒ์ ๋ณผ ์ ์๋ค. ํด๋น ๋ฉ์์ง๋ ์๋ง๋ ack
๊ฐ ํธ์ถ๋์ง ์์ ๋ฉ์์ง๋ก ๋ณด์ธ๋ค. ์ด๋ฌํ ๊ฒฐ๊ณผ๋ฅผ ํตํด์ ack
๋ฅผ ํธ์ถํ์ง ์์ผ๋ฉด ์๋ฒ์ ๊ณ์ ์์ด๊ฒ ๋์ด ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ญ๋น๋๊ฒ ๋๋ค.
๊ทธ๋ ๋ค๋ฉด ๋ฉ์์ง์ ๋ํ ๋ก์ง์ด ์คํจํ์ฌ ๋ฉ์์ง ํ๋ก ๋ค์ ๋๋๋ฆด ๋ ์ด๋ป๊ฒ ํด์ผํ๋๊ฐ? ๋ฐ๋ก nack
๋ฅผ ํธ์ถํ๋ฉด ๋๋ค. ํด๋น ๋ฉ์๋์ ์๊ทธ๋์ฒ๋ ๋ค์๊ณผ ๊ฐ๋ค.
channel.nack(message, allUpTo, requeue);
-
message
: ๋ฉ์์ง ๊ฐ์ฒด์ด๋ค. ์ฑ๋์ ์ ์ก๋ ๋ฉ์์ง๋ฅผ ์ฐธ์กฐํ๋ค. -
allUpTo
(boolean):true
๋ก ์ค์ ํ๋ฉด, ์ฃผ์ด์ง ๋ฉ์์ง ๊น์ง์ ๋ชจ๋ ์ด์ ๋ฉ์์ง๋ค๋ ํจ๊ป ๋ถ์ ํ์ธ๋๋ค. ๊ธฐ๋ณธ๊ฐ์false
-
requeue
(boolean):true
๋ก ์ค์ ํ๋ฉด, ๋ฉ์์ง๋ฅผ ํ์ ์ฒ์์ผ๋ก ๋ค์ ๋ฃ์ด ์ฌ์ฒ๋ฆฌํ ์ ์๋๋ก ํ๋ค.false
๋ก ์ค์ ํ๋ฉด ๋ฉ์์ง๊ฐ ๋ฒ๋ ค์ง๋ค. ๊ธฐ๋ณธ๊ฐ์true
์ด๋ฅผ ์ฐธ๊ณ ํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ ํ๋ค.
@EventPattern('hello')
async handleMessage(@Payload() data: number[], @Ctx() context: RmqContext) {
const channel = context.getChannelRef();
const originalMsg = context.getMessage();
if (Math.random() > 0.5) {
return channel.nack(originalMsg, false, true);
}
console.log('data:', data);
channel.ack(originalMsg);
}
์ดํ ์๋์ ๊ฒฐ๊ณผ๋ฅผ ํตํด ack
๊ฐ ๋์ง ์๋ ๋ฉ์์ง๋ ํ์ ๋ค์ ์ฑ์์ง๊ณ ์ฌ์ฒ๋ฆฌ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
- ๐ฉ FE ๊ธฐ์ ์ ํ์ด์
- โจ ์ฐจํธ์ ๋ฐ์ํ ๊ตฌํ๊ณผ useRef ํ์ ๋ฌธ์
- ๐ฃ ๋ถ๋ชจ ์์์ ์ํ์ ๋ฐ๋ผ ์์ ์์๋ ์คํ์ผ ๋ณํ ๋ถ์ฌํ๊ธฐ
- ๐ zod ๋์ ํ๊ธฐ
- ๐ useInfiniteQuery๋ฅผ ์ฌ์ฉํ ๊ทธ๋ํ ๋ฌดํ์คํฌ๋กค ๊ตฌํ
- ๐ซ ์ฌ์ฉ์์ ์์ ๋ณํ ์๋ ๊ทธ๋ํ ์คํฌ๋กค ๊ตฌํํ๊ธฐ
- ๐งช ์๋ง์ ๊ทธ๋ํ ๋ฐ์ดํฐ ์์ฒญ์ ์ด๋ป๊ฒ ์ค์ผ๊น
- ๐ ๋คํฌ๋ชจ๋์์ ์๋ก๊ณ ์นจ ์ ๋ผ์ดํธ๋ชจ๋๊ฐ ์ ๊น ๋ณด์ด๋ ๋ฌธ์
- ๐ ์น์์ผ์ ์ฑํ ๋ฐ์ดํฐ์ REST API์ ์ฑํ ๋ฐ์ดํฐ๋ฅผ ํจ๊ป ๊ด๋ฆฌํ๊ธฐ
- ๐ก BE ๊ธฐ์ ์ ํ ์ด์
- โ๏ธ Node WebSocket ํ๊ณ ๋ค๊ธฐ
- โ๏ธ TypeORM Datasource mock ๋ง๋ค๊ธฐ
- โ๏ธ oauth ID range ๋ฌธ์
- ๐ custom pipe์์ Nan์ด ๋ฐ์์ง๋ ๋ฌธ์
- ๐ช nest Websocket์ ์ธ์ ์ด ์๋๋ค๊ณ ?
- ๐ด nginx websocket ์ฐ๊ฒฐ ์ ๋ฌธ์ ๋ฐ์
- ๐ WebPush ๊ตฌํ
- ๐ง ์ฐ์ ์์ ํ๋ก ์์ฒญ ์ ์ดํ๊ธฐ
- ๐ websocket์ด ๋ฆ๊ฒ ํ ๋น๋์ด ๋ฐ์๋๋ ๋ฌธ์
- ๐ฅณ typeorm์ ์ด์ฉํ FCM ์๋ฆผ ์๋น์ค
- ๐ฆ ๋ค์ค ์ ์ ๋์์ฑ ์ ์ด โ ์ฑ๊ธํค, ๋ฎคํ ์ค
- ๐ ๊ทธ๋ํ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ ๊ณตํ๊ธฐ์ํ ์ ๋ต
- ๐ ๏ธ ์ธํ๋ผ ๊ธฐ์ ์คํ ์ ํ ์ด์
- ๐ Ncloud ์ค์ ๊ณผ์
- ๐ ORM ๊ธฐ์ ์คํ ๋น๊ต
- ๐ค RabbitMQ๋ก ๋ถ์ฐ ์๋ฒ์๊ฒ ๋ฉ์์ง๋ฅผ ๋ถ๋ฐฐํ๊ธฐ
- ๐ข private DB ์๋ฒ์ ์ ์ํ์ง ๋ชปํ๋ ํ์
- ๐ 1์ฃผ์ฐจ ๋ฐํ
- ๐ 2์ฃผ์ฐจ ๋ฐํ
- ๐ 3์ฃผ์ฐจ ๋ฐํ
- ๐ 4์ฃผ์ฐจ ๋ฐํ
- ๐ 5์ฃผ์ฐจ ๋ฐํ
- ๐ ์ต์ข ๋ฐํ