Node-Project

Node Project DADA

DaDa

Redis

  • Google Vision API와 S3upload를 Promise.All로 처리하면서 sharp로 이미지를 변환하였으나 CPU의 부하를 줄여주기 위하여 Kue + Redis를 사용 하였다.

  • Redis는 In memory Database라고 불리며 배열 형식의 데이터 처리에 특화되어있다.

  • 먼저 promise.all로 구현이 되어있던 부분 중에서 sharp로 처리하는 부분을 제거하고 새로운 promise로 queue.create()를 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// queue를 사용하기 위해서는 createQueue()를 해주어야 한다. 
const kue = require('kue')
const queue = kue.createQueue()

/*
createQueue를 설정하는 방법이다.
const queue = kue.createQueue({
prefix: 'q',
redis: {
port: 1234,
host: '10.0.50.20',
auth: 'password',
db: 3, // if provided select a non-default redis db
options: {
// see https://github.com/mranney/node_redis#rediscreateclient
}
}
})
*/

router.post('/', upload.single('upload_img'), (req, res) => {
// jpg image/jpeg << [ ext, mime ]
const { ext, mime } = fileType(req.file.buffer)
if (!supportImageExt.includes(ext)) {
res.status(400)
res.send('지원하지 않는 파일입니다.')
} else if (req.file.size > maxFileSize) {
res.status(400)
res.send('파일 용량은 3mb 까지 입니다.')
}

const fileName = `${uuid.v4()}.${ext}`

Promise.all([googleVision(req.file.buffer), s3upload(req.file.buffer, fileName, mime)])
.then(result => {
const filterText = ['food', 'cuisine', 'american food', 'baking', 'flavor', 'recipe', 'fast food', 'dessert', 'dish', 'cookie', 'organism', 'snack', 'font', 'baked goods', 'finger food', 'junk food', 'side dish', 'vegetarian food']

const out = result[0].filter(item => {
if (filterText.indexOf(item.description) < 0) {
return item
}
})
const output = {
'visionAnalysis': out,
'imgUrl': result[1].Location
}
res.send(output)
return output
})
.then(result => {
// sharp로 처리하였던 부분을 redis로 처리하기 위하여 새로운 promise 작성
return new Promise((resolve, reject) => {
// queue.create를 생성시 queue.process와 같게 해야하며 2번째 인자로는 전달할 값을 넣어주어야 한다.
queue.create('thumbnail', { 'imgUrl': result.imgUrl })
.removeOnComplete(true)
.save(err => {
if (err) {
reject(err)
} else {
resolve()
}
})
})
})
.catch(err => {
res.status(400)
res.send(err)
})
})
  • redis를 실행시킬 파일
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const kue = require('kue')
const queue = kue.createQueue()

// queue.create와 같게 만들어야한다.
// 생성시 2번째로 보내주는 값이 url이기 때문에 axios를 통하여 값을 get해야한다.

queue.process('thumbnail', (job, done) => {
// image를 다운받기 때문에 responseType에 유의해야한다.
axios({
'method': 'get',
'url': `${job.data.imgUrl}`,
// / options are 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream' responseType: 'json', // default
// 이곳에서는 arraybuffer가 사용된다.
'responseType': 'arraybuffer'
})
.then(response => {
// `data` is the response that was provided by the server
console.log('response.body', response.data)
const { ext, mime } = fileType(response.data)
console.log('ext!!!!!!!!!!!Mim11', ext, mime)
const fileName = `${uuid.v4()}.${ext}`
return sharp(response.data)
.resize(200, 200)
.crop(sharp.gravity.center)
.toBuffer()
.then(resizeFile => {
console.log('done')
return s3upload(resizeFile, `thumb/${fileName}`)
})
.then(() => {
done()
})
})
})

팀원의 요청으로 인하여 S3의 가공전 upload filename과 가공후의 filename의 이름을 맞춰달라고 하여서 추가 작업을 진행하였다.

queue.create 할때 fileName을 함께보내준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
router.post('/', upload.single('upload_img'), (req, res) => {
// jpg image/jpeg << [ ext, mime ]
const { ext, mime } = fileType(req.file.buffer)
if (!supportImageExt.includes(ext)) {
res.status(400)
res.send('지원하지 않는 파일입니다.')
} else if (req.file.size > maxFileSize) {
res.status(400)
res.send('파일 용량은 3mb 까지 입니다.')
}

const fileName = `${uuid.v4()}.${ext}`

Promise.all([googleVision(req.file.buffer), s3upload(req.file.buffer, fileName, mime)])
.then(result => {
const output = {
'visionAnalysis': result[0],
'imgUrl': result[1].Location
}
res.send(output)
return output
})
.then(result => {
return new Promise((resolve, reject) => {
queue.create('thumbnail', { 'imgUrl': result.imgUrl, 'fileName': fileName })
.removeOnComplete(true)
.save(err => {
if (err) {
reject(err)
} else {
resolve()
}
})
})
})
.catch(err => {
res.status(400)
res.send(err)
})
})

  • worker.js 파일에서는 queue.process부분에서 fileName을 추가해주었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
queue.process('thumbnail', (job, done) => {
axios({
'method': 'get',
'url': `${job.data.imgUrl}`,
'fileName': `${job.data.fileName}`,
'responseType': 'arraybuffer'
})
.then(response => {
// response.data안에 파일의 정보가 저장되어있다.
// fileName은 response.config안에 저장되어있다.
const fileName = `${response.config.fileName}`
const { ext, mime } = fileType(response.data)
return sharp(response.data)
.resize(200, 200)
.crop(sharp.gravity.center)
.toBuffer()
.then(resizeFile => {
return s3upload(resizeFile, `thumb/${fileName}`)
})
.then(() => {
done()
})
})
})
Share