关于如何降低从我的NodeJS后端服务器上的数据库中获取和处理大量iot时间序列数据的延迟的一般性问题。有时即使我将超时设置为15秒,我也会超时。
我目前的设置使用Google Datastore作为流数据的数据库,NodeJS后端服务器用于在将数据传递到前端之前与数据存储进行交互。我还在后端服务器上本地托管的MongoDB作为缓存层。
1个请求的数据检索工作流程大致如下:从MySQL数据库中查询设备mac地址,并用于查询所请求时间范围的缓存,然后从时间间隔(未被缓存覆盖)中检索数据数据存储区,并聚合到请求的时间分辨率,并根据需要对某些类型的数据进行额外的解析。
我能想到的一些提高性能的策略包括。欢迎对以下策略提出任何其他建议/意见。
异步 - 使用async从缓存中获取数据以及数据存储(已实现)
Streams - 使用fs返回流中的数据以提高内存利用率
压缩 - 使用压缩等库来减少数据大小 发送到前端
群集 - 使用群集来使用多核处理器(更多用于提供多个请求,而不是减少每个请求的延迟)
答案 0 :(得分:1)
您希望专注于减少延迟,但是从我的角度来看,您使用的是太复杂的系统,涉及无法有效使用的技术。你说你的工作流程是这样的:
MySQL - >缓存(使用MongoDB)/数据存储 - > NodeJS - >前端
首先,看看您使用的是三种不同的数据库解决方案。我知道它们是用于不同的任务,但它似乎并不是最有效的方法。我会说Datastore + MongoDB的组合似乎也不自然。你为什么不使用下列之一?:
无论你选择第一个还是第二个选项:你不能使用MySQL吗?
如果您使用Google App Engine使用其中任何一种解决方案,您可以更轻松地回答与群集相关的问题。增加服务器CPU / RAM。您可以尝试使用different configurations of your app.yaml来检查最适合您的内核和内存量。
我不介意与异步和压缩策略有关。根据Streams,您可以使用@MichałCzapracki关于使用scramjet的建议。
答案 1 :(得分:0)
在我的应用程序中,我需要提供一个选项,以连接多个mongodb集合(所有数据)并导出到一个CSV文件中,并附带导出进度百分比
exports.getExportData = (req, res) => {
req.setTimeout(2400000);
console.log('------------------------------------------ getExportData ----------------------------------------------')
let url = urls.baseUrl + urls.endpoints.getData;
url = postParamsToGet(url, req.query);
let postContents = {
headers: {
'content-type': 'application/json'
},
url: url,
form: req.body,
timeout: 2400000
},
receivedData = '', totalData = 0, column = false, lastEntry = 0,
successResponse = {
status: {
success: true,
code: 200,
message: 'processing...'
},
data: {
url: [],
processed: 0
}
},
errorResponse = {
status: {
success: false,
code: 400,
message: 'something went wrong'
},
data: {
url: ''
}
},
exitChunk = false, filesRows = 0, fileCount = 0, pending = true;
console.log(JSON.stringify(postContents))
try {
req.on("close", function() {
console.log("close the connection!");
errorResponse.status.message = 'connection broke'
res.end(JSON.stringify(errorResponse));
exitChunk = true
});
let fileName = `${md5((new Date()).getTime())}.csv`
let email = 'abc@example.com'
if (req.query.emailId) {
fileName = md5((new Date()).getTime() + req.query.emailId) + '.csv';
email = req.query.emailId
}
let tempFile = path.resolve('./modules/utils/temp/' + fileName);
if (!fs.existsSync(path.resolve('./modules/utils/temp')))
fs.mkdirSync(path.resolve('./modules/utils/temp'))
if (!fs.existsSync(path.resolve(tempFile))) {
fs.appendFileSync(tempFile, '');
}
const uploadChunkFile = (tf, fn) => {
console.log('uploading to S3...', totalData, tf)
return new Promise(async (resolve, reject) => {
try {
if (!fs.existsSync(path.resolve(tf)))
return reject('unable to create file')
await exportCsv.uploadToS3(tf, fn, async function(err, status, url) {
if (err) {
console.log(err)
return resolve(fileName)
}
if (!status) {
console.log(status)
return resolve(fileName)
}
return resolve(url)
});
}
catch(err) {
console.log(err.message)
return reject(err.message)
}
})
}
// chunk process start from here
request.post(postContents)
.on('response', async function(response) {
if (response.statusCode != 200) {
errorResponse.status.message = response.statusMessage
errorResponse.status.code = response.statusCode
res.statusMessage = response.statusMessage
console.log(errorResponse)
return res.status(response.statusCode).send(errorResponse)
}
})
.on('data', async function(d) {
if (exitChunk) {
res.removeAllListeners('data');
return res.destroy();
}
let receive = d.toString()
if (receive.indexOf("END_OF_STRING") > -1) {
let receiveString = receivedData, pendingString = ''
receivedData = ''
receiveString += receive
receiveString = pendingString + receiveString
let splitedStringChunk = receiveString.split("END_OF_STRING")
if (splitedStringChunk.length > 1) {
for (let str = 1; str < splitedStringChunk.length; str++) {
pendingString += splitedStringChunk[str]
}
}
let substrReceiveString = splitedStringChunk[0].split("START_OF_STRING")[1]
let result = JSON.parse(substrReceiveString)
let jsonData = []
totalData = result.total
lastEntry = result.o + result.l
filesRows += result.l
if (!column) {
column = Object.keys(result.data[0]);
jsonData.push(`"${column.join('","')}"\n`)
}
result.data.forEach(d => {
let joinRows = [];
column.forEach(c => {
if (d[c] !== undefined) {
joinRows.push(d[c].toString())
} else {
joinRows.push('-');
}
});
joinRows = joinRows.join('","')
jsonData.push(`"${joinRows.substring(0, joinRows.length)}"\n`)
});
await fs.appendFileSync(tempFile, jsonData.join(''));
pending = true
if (filesRows >= 1000000) {
filesRows = 0
try {
let uploadedUrl = await uploadChunkFile(tempFile, fileName)
successResponse.data.url.push(uploadedUrl)
}
catch(err) {
console.log(err.message)
console.log('chunk file upload error')
}
fileName = `${fileName.split('.')[0]}_${++fileCount}.csv`
tempFile = path.resolve('./modules/utils/temp/' + fileName);
if (!fs.existsSync(path.resolve(tempFile))) {
fs.appendFileSync(tempFile, '');
}
pending = false
}
console.log(`${result.processed}% completed${'.'.repeat(Math.round(result.processed/2))}, ${lastEntry}, ${result.o}, ${result.l}-- ${totalData}`)
successResponse.data.processed = (parseInt(result.processed) == 100) ? '99': result.processed.toString()
await res.write(JSON.stringify(successResponse))
await res.flush()
if (totalData > 0 && totalData == lastEntry) {
console.log('entered ....')
if (pending) {
try {
let uploadedUrl = await uploadChunkFile(tempFile, fileName)
successResponse.data.url.push(uploadedUrl)
}
catch(err) {
console.log(err.message)
console.log('chunk file upload error last time')
}
}
console.log('upload completed')
successResponse.data.processed = '100'
successResponse.status.message = 'url created successfully'
await res.write(JSON.stringify(successResponse))
await res.flush()
return await res.end()
}
}
else {
receivedData += d.toString()
}
})
.on('end', async function() {
if (totalData > 0 && totalData == lastEntry) {
// response already sent
}
else {
successResponse.data.processed = '-1'
successResponse.status.message = 'no data found'
await res.write(JSON.stringify(successResponse))
await res.flush()
return await res.end()
}
})
.on('error', async function(err) {
console.log('on error', err.Error)
errorResponse.status.message = err
return res.status(errorResponse.status.code).send(errorResponse)
})
}
catch(err) {
console.log('on try catch', err.message)
errorResponse.status.message = err.message
return res.status(errorResponse.status.code).send(errorResponse)
}
}