반응형
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 {}
}
}
반응형
'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 |