수영장 파티! Node.js의 MySQL 풀 연결 💦🔌

20102 단어
애플리케이션에서 MySQL 데이터베이스를 사용하려면 당연히 해당 데이터베이스에 대한 연결이 필요합니다. 소규모 개발 또는 실험의 경우 단일 연결을 만드는 것만으로도 문제가 없을 수 있습니다. 그러나 프로덕션 또는 복잡한 쿼리와 같은 보다 광범위한 작업에는 더 많은 유연성이 필요할 수 있습니다. 단일 연결과 연결 풀의 차이점을 보기 위해 전자의 간단한 예부터 시작하겠습니다.

const express = require('express');
const app = express();
const mysql = require('mysql');

const db_connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: '',
  database: 'test'
});

db_connection.connect((err) => {
  if (err) console.error(err);
  console.log('MySQL Connection Established.');
});

const getUsers = () => new Promise((resolve, reject) => {
  db_connection.query('SELECT * FROM USERS', (err, results) => {
    if (err) console.error(err);
    console.log('User Query Results: ', results);
    resolve(results);
    db_connection.end(err => { if (err) console.error(err) });
  });
});

app.get('/', (req, res) => {
  getUsers()
    .then(users => res.send(users))
    .catch(err => console.error(err));
});

app.listen(8080, () => {
  console.log('Server listening on port 8080.');
});


이 간단한 서버는 데이터베이스 연결을 설정하고 "users"테이블(하나의 항목이 미리 로드됨)의 모든 행에 대해 "test"(이전에 생성됨)라는 데이터베이스를 쿼리합니다. node app.js 로 서버를 시작한 후 http://localhost:8080 에 대한 Postman GET 호출은 예상 응답을 다시 보냅니다.

[
    {
        "id": 1,
        "username": "gdup",
        "location": "New Orleans"
    }
]


그리고 터미널에서:

Server listening on port 8080.
MySQL Connection Established.
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]


그러나 후속 Postman 호출은 빈 응답을 생성하고 오류와 함께 서버를 종료합니다.

User Query Results:  undefined
Error: Cannot enqueue Quit after invoking quit.
    at Protocol._validateEnqueue (/Users/Work/immersion/database-test/node_modules/mysql/lib/protocol/Protocol.js:215:16)
    at Protocol._enqueue (/Users/Work/immersion/database-test/node_modules/mysql/lib/protocol/Protocol.js:138:13)
    at Protocol.quit (/Users/Work/immersion/database-test/node_modules/mysql/lib/protocol/Protocol.js:91:23)
    at Connection.end (/Users/Work/immersion/database-test/node_modules/mysql/lib/Connection.js:239:18)
    at Query.<anonymous> (/Users/Work/immersion/database-test/app.js:22:19)
    at Query.<anonymous> (/Users/Work/immersion/database-test/node_modules/mysql/lib/Connection.js:526:10)
    at Query._callback (/Users/Work/immersion/database-test/node_modules/mysql/lib/Connection.js:488:16)
    at Query.Sequence.end (/Users/Work/immersion/database-test/node_modules/mysql/lib/protocol/sequences/Sequence.js:83:24)
    at /Users/Work/immersion/database-test/node_modules/mysql/lib/protocol/Protocol.js:236:14
    at processTicksAndRejections (internal/process/task_queues.js:79:11) {
  code: 'PROTOCOL_ENQUEUE_AFTER_QUIT',
  fatal: false
}


여기서 문제는 데이터베이스 연결의 db_connection.end() 부분입니다. 연결이 종료되면 다시 시작할 수 없으며 다시 사용할 수 없습니다. "그냥 연결을 끊지 않는 게 어때?"스스로에게 물어볼 수도 있습니다. 글쎄요, 데이터베이스에 대한 단일 생명선에 문제가 없는 한 괜찮습니다. 그러나 그 외에도 더 많은 유연성을 원하는 다른 이유가 있습니다. 하나는 결국 여러 동시 사용자에 대해 여러 연결을 가질 수 있어야 할 것입니다. 마찬가지로, 결국 둘 이상의 연결이 필요한 쿼리를 구현하려고 할 수 있습니다. 연결을 통해 수행되는 모든 쿼리는 순차적이라는 점에 유의하십시오. 각각 1초가 걸리는 쿼리가 10개 있는 경우 총 실행 시간은 물론 병렬로 실행되지 않는 한 10초가 됩니다.
siege를 설치하여 데모의 한 부분을 더 설정해 보겠습니다. 그러면 동시 get 요청으로 서버를 폭발시킬 수 있습니다. benchmark.js 파일에서 다음 코드를 사용하면 node benchmark.js를 실행하여 이를 수행할 수 있습니다.

const siege = require('siege');

siege()
  .on(8080)
  .concurrent(10)
  .get('/')
  .attack();


서버를 시작하고 실행node benchmark.js하면 예상대로 서버가 종료되고 위에서 Postman이 수행한 것과 동일한 오류가 생성됩니다. 이제 연결을 리팩토링해 보겠습니다.

const express = require('express');
const app = express();
const mysql = require('mysql');

const db_connection = mysql.createPool({
  host: 'localhost',
  user: 'root',
  password: '',
  database: 'test'
});

const getUsers = () => new Promise((resolve, reject) => {
  db_connection.getConnection((err, connection) => {
    if (err) console.error(err);
    console.log('MySQL Connection Established: ', connection.threadId);
    connection.query('SELECT * FROM USERS', (err, results) => {
      if (err) console.error(err);
      console.log('User Query Results: ', results);
      resolve(results);
      connection.release(err => { if (err) console.error(err) });
    });
  });
});

app.get('/', (req, res) => {
  getUsers()
  .then(users => res.send(users))
    .catch(err => console.error(err));
});

app.listen(8080, () => {
  console.log('Server listening on port 8080.');
});


이제 createConnection 대신 createPool 를 사용하여 연결 풀을 빌릴 준비가 되었습니다. 때가 되면 getConnectionconnection를 콜백 함수의 매개변수로 사용합니다. 풀이 제공하는 연결이 쿼리에 사용할 연결입니다. 이제 시즈 테스트를 실행하면 서버를 중단하지 않고 여러 쿼리 결과 집합이 생성됩니다. 다음은 몇 가지입니다.

Server listening on port 8080.
MySQL Connection Established:  124
MySQL Connection Established:  123
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]
MySQL Connection Established:  123
MySQL Connection Established:  124
MySQL Connection Established:  125
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]
MySQL Connection Established:  126
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]

console.logconnection.threadId 는 각 연결에 대해 고유한 번호를 표시합니다. 사용된 연결 범위를 보여줍니다. 실제로 생성할 개수를 지정하지 않았지만 기본값은 100입니다. 연결 매개변수에서 제한을 1로 설정하면:

const db_connection = mysql.createPool({
  host: 'localhost',
  user: 'root',
  password: '',
  database: 'test',
  connectionLimit: 1,
});


...공성 공격을 다시 실행하면 동일한 단일 풀 연결이 계속해서 사용됩니다.

Server listening on port 8080.
MySQL Connection Established:  131
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]
MySQL Connection Established:  131
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]
MySQL Connection Established:  131
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]
MySQL Connection Established:  131
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]
MySQL Connection Established:  131
User Query Results:  [ RowDataPacket { id: 1, username: 'gdup', location: 'New Orleans' } ]


쿼리 끝에 있는 connection.release()를 기록해 둡니다. 이것은 연결이 풀에 다시 제공되어 재사용되는 방정식의 중요한 부분입니다. 마치 도서관에서 책을 빌린 후 반환하는 것과 같습니다. 그리고 이전과 마찬가지로 필요한 경우 db_connection.end()로 모든 연결을 (동시에) 닫을 수 있습니다.

MySQL/Node.js 풀 연결에 대한 이 튜토리얼이 도움이 되었기를 바랍니다. 풀 연결은 처음에는 신비하게 보일 수 있으며 "내부"에서 작동하는 방식은 즉시 명확하지 않습니다. 그러나 위의 실험을 통해 기능에 대한 구체적인 이해를 얻었고 이제 풀 연결을 구현하는 것이 쉬울 뿐만 아니라 매우 유익하다는 데 동의할 수 있기를 바랍니다.

좋은 웹페이지 즐겨찾기