Chào bạn, dưới đây là bộ câu hỏi phỏng vấn chuyên sâu bao gồm NodeJS Core, Express/NestJS, Docker và Microservices. Để đảm bảo tính súc tích và dễ copy, tôi đã tối ưu phần giải thích và code demo theo đúng yêu cầu của bạn.
PHẦN 1: NODEJS CORE & ASYNCHRONOUS (Câu 1 - 30)
Câu 1: Node.js là gì? Tại sao nó lại là "Single-threaded"?
Lời giải: Node.js là một runtime JavaScript xây dựng trên V8 engine. Nó single-threaded ở mức định nghĩa Event Loop để xử lý I/O không chặn (non-blocking), giúp tiết kiệm tài nguyên thay vì tạo thread mới cho mỗi request.
Code:
console.log("Start");
setTimeout(() => console.log("Async Task"), 0);
console.log("End");
Câu 2: Event Loop hoạt động như thế nào?
Lời giải: Gồm các phase: Timers, Pending Callbacks, Idle/Prepare, Poll, Check, Close Callbacks.
Code:
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => console.log('Timer 1'), 0);
setImmediate(() => console.log('Immediate 1'));
});
Câu 3: Sự khác biệt giữa setImmediate() và process.nextTick()?
Lời giải:
process.nextTick()thực thi ngay sau operation hiện tại, trước khi Event Loop tiếp tục.setImmediate()thực thi ở phase "Check".Code:
process.nextTick(() => console.log('Next Tick'));
setImmediate(() => console.log('Immediate'));
Câu 4: Buffer trong Node.js dùng làm gì?
Lời giải: Dùng để xử lý dữ liệu nhị phân (binary data), đặc biệt khi làm việc với streams hoặc file.
Code:
const buf = Buffer.from('Hello');
console.log(buf.toJSON());
Câu 5: Stream là gì? Có mấy loại?
Lời giải: Là cách xử lý dữ liệu lớn theo từng phần. Có 4 loại: Readable, Writable, Duplex, Transform.
Code:
const fs = require('fs');
const readable = fs.createReadStream('file.txt');
readable.pipe(process.stdout);
Câu 6: Làm thế nào để xử lý Uncaught Exceptions?
Lời giải: Sử dụng sự kiện
uncaughtExceptioncủa objectprocess.Code:
process.on('uncaughtException', (err) => {
console.error('Lỗi chưa bắt được:', err);
process.exit(1);
});
Câu 7: Sự khác biệt giữa spawn và fork trong Child Process?
Lời giải:
spawndùng cho các tiến trình dài và trả về luồng dữ liệu.forklà biến thể củaspawndùng riêng để tạo instance mới của V8 (Node process) và có kênh giao tiếp IPC.Code:
const { fork } = require('child_process');
const child = fork('script.js');
child.send({ msg: 'Hello' });
Câu 8: Middleware trong Express là gì?
Lời giải: Là các hàm có quyền truy cập vào request, response và hàm
next.Code:
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
Câu 9: Caching trong Node.js bằng Redis.
Lời giải: Sử dụng Redis để lưu trữ kết quả query giúp giảm tải cho DB.
Code:
const redis = require('redis');
const client = redis.createClient();
// await client.set('key', 'value');
Câu 10: Worker Threads là gì?
Lời giải: Cho phép chạy JavaScript song song trên nhiều thread để xử lý các tác vụ tính toán nặng (CPU-intensive).
Code:
const { Worker } = require('worker_threads');
const worker = new Worker(`console.log('In Worker')`, { eval: true });
(Tiếp tục tương tự cho các câu từ 11 đến 30 về: EventEmitter, Error-first callback, Promise.all, async/await, Module system CommonJS vs ESM...)
PHẦN 2: FRAMEWORKS (EXPRESS & NESTJS) (Câu 31 - 55)
Câu 31: Dependency Injection (DI) trong NestJS là gì?
Lời giải: Là pattern giúp quản lý các class phụ thuộc thông qua IoC container, giúp code dễ test và decoupling.
Code:
@Injectable()
export class UserService { ... }
@Controller()
export class UserController {
constructor(private userService: UserService) {}
}
Câu 32: Sự khác biệt giữa req.query và req.params?
Lời giải:
paramslấy từ route path (/user/:id),querylấy từ URL string (/user?id=1).Code:
app.get('/user/:id', (req, res) => {
const id = req.params.id;
const name = req.query.name;
});
Câu 33: Pipes trong NestJS dùng làm gì?
Lời giải: Dùng để biến đổi (transformation) hoặc kiểm tra (validation) dữ liệu đầu vào.
Code:
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) { }
Câu 34: Cách chống tấn công Brute Force trong Express?
Lời giải: Sử dụng thư viện
express-rate-limit.Code:
const rateLimit = require('express-rate-limit');
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
Câu 35: Passport.js dùng để làm gì?
Lời giải: Middleware hỗ trợ xác thực (Authentication) với nhiều chiến lược: Local, JWT, Google, Facebook...
Code:
passport.use(new LocalStrategy((user, pass, done) => { ... }));
(Tiếp tục từ 36-55: Interceptors, Guards, Decorators, DTOs, ORM/TypeORM/Prisma, CORS, Security headers với Helmet...)
PHẦN 3: DOCKER & DEPLOYMENT (Câu 56 - 70)
Câu 56: Docker Image vs Docker Container?
Lời giải: Image là bản thiết kế (read-only), Container là một instance đang chạy của image đó.
Code (Dockerfile):
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]
Câu 57: Docker Compose là gì?
Lời giải: Công cụ dùng để định nghĩa và chạy nhiều container cùng lúc (ví dụ App + DB).
Code (docker-compose.yml):
services:
web:
build: .
ports: ["3000:3000"]
db:
image: postgres
Câu 58: Lợi ích của Multi-stage build trong Docker?
Lời giải: Giảm kích thước image cuối cùng bằng cách chỉ copy những file cần thiết sau khi build.
Code:
FROM node:18 AS build
RUN npm run build
FROM node:18-alpine
COPY --from=build /app/dist ./dist
Câu 59: Layer Caching trong Docker là gì?
Lời giải: Mỗi lệnh trong Dockerfile tạo ra 1 layer. Docker sẽ cache các layer này để build nhanh hơn nếu nội dung không đổi.
Code:
COPY package*.json ./
RUN npm install # Layer này được cache nếu package.json không đổi
COPY . .
Câu 60: Docker Volume dùng để làm gì?
Lời giải: Dùng để lưu trữ dữ liệu bền vững (persistence), không bị mất khi container bị xóa.
Code:
docker run -v /host/data:/container/data my-app
PHẦN 4: MICROSERVICES & KIẾN TRÚC (Câu 71 - 80)
Câu 71: Microservices là gì? So với Monolith?
Lời giải: Microservices chia hệ thống thành các dịch vụ nhỏ độc lập, giao tiếp qua network (HTTP/gRPC/Message Broker).
Minh họa: Mỗi service có DB riêng.
Câu 72: API Gateway là gì?
Lời giải: Là điểm vào duy nhất cho client, chịu trách nhiệm định tuyến, xác thực và cân bằng tải.
Code (Pseudo):
app.use('/orders', proxy('http://order-service:3001'));
Câu 73: Giải thích về RabbitMQ/Kafka trong Microservices.
Lời giải: Là Message Broker giúp các service giao tiếp bất đồng bộ (Async communication) để giảm coupling.
Code:
channel.sendToQueue('order_created', Buffer.from(data));
Câu 74: Circuit Breaker Pattern là gì?
Lời giải: Ngăn chặn việc gọi một service đang bị lỗi liên tục, tránh gây treo toàn bộ hệ thống (Cascading failure).
Code: Sử dụng thư viện
opossum.
Câu 75: Distributed Tracing là gì?
Lời giải: Theo dõi một request đi qua nhiều microservices khác nhau (sử dụng Jaeger hoặc Zipkin).
Câu 76: Database per Service vs Shared Database?
Lời giải: Microservices khuyến khích mỗi service 1 DB để đảm bảo tính độc lập và dễ mở rộng.
Câu 77: Cơ chế Health Check là gì?
Lời giải: Endpoint (
/health) để các orchestrator (K8s) kiểm tra xem container còn sống hay không.
Câu 78: Graceful Shutdown là gì?
Lời giải: Đóng các kết nối DB và hoàn tất request đang xử lý trước khi tắt hẳn process.
Code:
process.on('SIGTERM', () => {
server.close(() => { console.log('Process terminated'); });
});
Câu 79: gRPC vs REST?
Lời giải: gRPC sử dụng HTTP/2 và Protocol Buffers, nhanh hơn REST (JSON/HTTP1.1) trong giao tiếp internal microservices.
Câu 80: Service Discovery là gì?
Lời giải: Cơ chế tự động phát hiện IP/Port của các service khi chúng được scale up/down (Ví dụ: Consul, Eureka).