커넥션 풀(Connection Pool)
- 데이터베이스와의 연결을 미리 여러개 생성하여 풀에 저장한다.
- 필요할 때마다 재사용하여 성능을 향상시킨다.
- 미리 생성된 연결을 사용함으로써 응답 시간을 감소시킨다.
커넥션 풀의 장단점
- 장점
- 더 많은 연결이 더 많은 처리를 동시에 할 수 있어 동시 처리 능력이 향상된다.
- 동접자가 많은 환경에서 유리하다.
- 여러개의 연결을 유지함으로써 갑작스러운 트래피증가에 고가성용성을 제공한다.
- 단점
- 사용하지 않는 커넥션으로 자원이 낭비된다.
- 데이터베이스 서버에 과도한 부하를 줄 수 있다. = 성능저하
- 네트워크 성능에 영향을 미친다. (대역폭 사용 증가)
** 실제 고성능 게임서버의 경우 수천개 이상의 커넥션을 유지하기도 한다.
서버에 DB 연동
환경변수에 DB 정보를 세팅한다. 현재는 로컬 환경에 MySQL로 연결
//환경변수들을 선언해서 사용하기위한 env.js파일에 DB의 내용을 추가해준다.
export const DB1_NAME = process.env.DB1_NAME || "database1"; //1번 DB이름
export const DB1_USER = process.env.DB1_USER || "user1"; //1번 유저 이름
export const DB1_PASSWORD = process.env.DB1_PASSWORD || "password1"; //1번 비밀번호
export const DB1_HOST = process.env.DB1_HOST || "localhost";// 호스트 주소
export const DB1_PORT = process.env.DB1_PORT || 3306; //db포트번호
export const DB2_NAME = process.env.DB2_NAME || "database2";
export const DB2_USER = process.env.DB2_USER || "user2";
export const DB2_PASSWORD = process.env.DB2_PASSWORD || "password2";
export const DB2_HOST = process.env.DB2_HOST || "localhost";
export const DB2_PORT = process.env.DB2_PORT || 3306;
환경변수 설정이 끝났으므로 config 객체에 DB정보를 등록한다.
//환경 설정과 관련된 모든것을 사용할때는 config파일에서 호출에서 사용한다.
import {
//.env에 지정해둔 값들
CLIENT_VERSION,
DB1_HOST,
DB1_NAME,
DB1_PASSWORD,
DB1_PORT,
DB1_USER,
DB2_HOST,
DB2_NAME,
DB2_PASSWORD,
DB2_PORT,
DB2_USER,
HOST,
PORT,
} from "../constants/env.js";
import { PACKET_TYPE_LENGTH, TOTAL_LENGTH } from "../constants/header.js";
export const config = {
server: {
port: PORT,
host: HOST,
},
client: {
version: CLIENT_VERSION,
},
packet: {
totalLength: TOTAL_LENGTH,
typeLength: PACKET_TYPE_LENGTH,
},
//여기부터 DB
//여기에 환경변수들을 가져와 DB를 등록한다.
database: {
GAME_DB: {
name: DB1_NAME,
user: DB1_USER,
password: DB1_PASSWORD,
host: DB1_HOST,
port: DB1_PORT,
},
USER_DB: {
name: DB2_NAME,
user: DB2_USER,
password: DB2_PASSWORD,
host: DB2_HOST,
port: DB2_PORT,
},
},
};
이제 db/database.js파일을 생성하고 코드를 작성해준다.
import mysql from "mysql2/promise";
import { config } from "../config/config.js";
import { formatDate } from "../utils/dateFomatter.js";
const { database } = config;
const createPool = (dbConfig) => {
const pool = mysql.createPool({
host: dbConfig.host,
port: dbConfig.port,
user: dbConfig.user,
password: dbConfig.password,
database: dbConfig.name,
waitForConnections: true,
connectionLimit: 10, // 커넥션 풀에서 최대 연결 수
queueLimit: 0, // 0일 경우 무제한 대기열
});
const originQuery = pool.query;
pool.query = (sql, params) => {
const date = new Date();
console.log(
`[${formatDate(date)}] Executing query : ${sql} ${params ? `${JSON.stringify(params)}` : ``}`,
);
return originQuery.call(pool, sql, params);
};
return pool;
};
//여기에 여러 DB커넥션 풀 생성
const pools = {
GAME_DB: createPool(database.GAME_DB),
USER_DB: createPool(database.USER_DB),
//예시) LOG_DB: createPool(database.LOG_DB),
};
export default pools;
이렇게 하면 pools객체가 가지고 있는 커넥션풀을 사용하여 DB에 쿼리를 전송할 수 있게 해준다.
로그를 남길때 날짜 포맷을 위해 dataFomatter.js도 만둘어준다.
export function formatDate(date) {
//getFullYear메서드를 사용해 입력된 날짜의 연도를 가져옴
const year = date.getFullYear();
//getMonth()는 0부터 시작하는 월(0=1월,1=2월...)을 반환하므로 1을 더해 실제 월을 얻는다.
//String(...).padStart(2, '0')를 사용하여 한 자리 수인 경우 앞에 0을 추가하여 두 자리로 만듬
// 1인경우 (1 => 01)
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
//템플릿 리터럴을 사용하여 연,월,일,시간,분,초를 조합한 최종 문자열을반환
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
//사용예시
const now = new Date();
console.log(formatDate(now)); // 예: "2024-10-28 14:05:09"
서버 시동시 DB에 대한 테스트 코드 = DB생존여부를 확인하는 코드
const testDbConnection = async (pool, dbName) => {
try {
const [rows] = await pool.query(`select 1+1 AS solution`);
console.log(`${dbName}테스트 쿼리 결과 : ${rows[0].solution}`);
} catch (err) {
console.error(`${dbName}테스트 쿼리 실행중 오류났단거임 : ${err}`);
}
};
const testAllConnection = async (pools) => {
await testDbConnection(pools.GAME_DB, "GAME_DB");
await testDbConnection(pools.USER_DB, "USER_DB");
await testDbConnection(pools.LOG_DB, "LOG_DB");
};
export { testDbConnection, testAllConnection };
//성공시 로그
[2024-10-28 21:50:39] Executing query : select 1+1 AS solution
GAME_DB테스트 쿼리 결과 : 2
[2024-10-28 21:50:39] Executing query : select 1+1 AS solution
USER_DB테스트 쿼리 결과 : 2
//실패시 로그
[2024-10-28 21:27:45] Executing query : select 1+1 AS solution
GAME_DB테스트 쿼리 실행중 오류났단거임 : Error: Unknown database 'game_db'
[2024-10-28 21:27:45] Executing query : select 1+1 AS solution
USER_DB테스트 쿼리 실행중 오류났단거임 : Error: Unknown database 'user_db'
서버 초기화 함수에 DB테스트 함수를 추가
// 서버 초기화 작업
import { loadGameAssets } from './assets.js';
import { loadProtos } from './loadProtos.js';
import { testAllConnections } from '../utils/db/testConnection.js';
import pools from '../db/database.js';
const initServer = async () => {
try {
await loadGameAssets();
await loadProtos();
await testAllConnections(pools);//DB테스트 함수
// 다음 작업
} catch (e) {
console.error(e);
process.exit(1); // 오류 발생 시 프로세스 종료
}
};
export default initServer;
'Node' 카테고리의 다른 글
process.exit() 메서드 (0) | 2024.11.01 |
---|---|
객체지향 패턴(Object-Oriented Patterns) (0) | 2024.10.29 |
TCP 이해하기 (0) | 2024.10.28 |
socket 이벤트 / 버퍼(Buffer) (0) | 2024.10.23 |
Socket.io 기본 기능 (0) | 2024.09.30 |