-
express 와 singleton 패턴을 사용한 api server 구현Tech/NodeJs 2020. 1. 11. 13:05반응형
MSA 와 무중단 배포 특성에 맞는 api server 를 구축 해보도록한다.
//server.js 'use strict' const express = require('express') const http = require('http') const cookieParser = require('cookie-parser') //http.Server 를 통해 확장하여 싱글톤 패턴으로 만들도록 한다. //서버가 이중으로 생성되는것을 막기 위해 싱글톤으로 구현하도록 한다. //MSA 환경을 고려했을때 일관성있게 분산된 서버들의 상태를 관리하기 위함이다. //즉 생성자 내부에서 express 생성을 한번만 생성하도록 싱글톤 패턴을 적용하는 기법이다. class ApiServer extends http.Server { constructor(config) { const app = express() super(app) this.config = config this.app = app //현재 연결된 커넥션의 상태를 관리하기 위해 set 으로 관리한다. this.currentConnections = new Set() //무중단 배포 환경에서 사용중인 커넥션들을 관리하기 위해 선언 this.busy = new WeakSet() //해당 서버가 무중단으로 배포되었을때 중단되어지는 상태인지 관리하기 위해 선언 this.stopping = false } //비동기 메서드로서 시작 메서드를 생성한다 async start() { this.app.use((req, res, next) => { //현재 요청 소켓을 커넥션 관리 변수(busy)에 담는다. this.busy.add(req.socket) //finish 로 응답 받았을 경우 커넥션 관리에서 삭제 한다 res.on('finish', () => { if (this.stopping) req.socket.end() this.busy.delete(req.socket) }) next() }) this.app.use(cookieParser()) //헬스체크를 위해 추가 this.app.get('/_health', (req, res) => { res.sendStatus(200) }) //에러 처리 this.app.use((err, req, res, next) => { res.status(500).send(generateApiError('Api::Error')) }) //커넥션에 대해 관리 this.on('connection', c => { this.currentConnections.add(c) c.on('close', () => this.currentConnections.delete(c)) }) return this } shutdown() { //종료중인가? if (this.stopping) { return } this.stopping = true //종료(정상조료) this.close(() => { process.exit(0) }) //종료되는 동안 블로킹되지 않도록 하기 위해 setTimeout 사용 this.setTimeout(() => { console.error('비정상 종료 (강제종료)') process.exit(1) }, this.config.shutdownTimeout).unref() //현재 커넥션이 있을 경우 if (this.currentConnections.size > 0) { console.log(`현재 동시 접속중인 연결 (${this.currentConnections.size}) 을 대기중입니다.`) for (const con of this.currentConnections) { //단순 접속자일 경우에는 종료 시키도록 한다. if (!this.busy.has(con)) { con.end() } } } } } //usage: //초기화 (start 를 호출 하기 때문에 async 사용) const init = async (config = {}) => { //새로운 서버를 생성 const server = new ApiServer(config) return await server.start() } module.exports = { init }
//main.js 'use strict' const { init } = require('./server') const { getConfig } = require('./config') const env = process.env.NODE_ENV const main = async () => { const config = await getConfig() const server = await init() server.listen(config.port, () => { console.log(`server listening on port ${config.port}`) }) process.on('SIGTERM', () => server.shutdown()) process.on('SIGINT', () => server.shutdown()) }
//config.js 'use strict' module.config = { getConfig: () => { return {} } }
반응형'Tech > NodeJs' 카테고리의 다른 글
how to change function to arrow function (0) 2020.01.11 static method factory pattern (0) 2020.01.10 TDD - frameworks 소개 (0) 2020.01.10 file (0) 2020.01.10 destructing (0) 2020.01.10