com.mongodb.MongoSocketReadTimeoutException: Timeout while receiving message
阿新 • • 發佈:2019-01-09
1、可能是遇到了查詢時間過長的Read,不妨看看profiling或者server log日誌的資訊,找到查詢時間比較長的語句,例如超過10秒的語句,看是否能優化;
2、適當提高socketTimeout一些,看是否能緩解這種出錯的情形。
供參考。
業務需求:將mongo庫裡的全量資料相關資訊跑出來寫入檔案中,供合作方拉取。
業務思路:用DBCursor取得庫的連線,移動遊標每讀5w條資料進行多執行緒非同步寫檔案操作。
測試庫1500多w資料執行沒有異常,可是到了正式庫3000多w就報如題錯誤。
後來根據參考1方案優化了Read語句,解決問題。
優化前程式碼:
public List<FileInfo> handle(long pageSize, String collectionName, BasicDBObject query, BasicDBObject fields,Date time,String path) {
DBCursor dbCursor = this.mongoTemplate.getCollection(collectionName).find(query,fields).sort(new BasicDBObject("_id", 1)).limit((int)pageSize);
List<FileInfo> fileInfos = new LinkedList<FileInfo>();
String filename = Constants.FILE_NAME;
int nThreads = Constants.nThreads;
ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
while (dbCursor.hasNext()){
FileInfo fileInfo = new FileInfo();
fileInfos.add(JavaBeanUtils.dbObject2Bean(dbCursor.next(),fileInfo));
if (fileInfos.size() == Constants.FLAG_VALUE){//每到5w條寫一次到檔案
final List<FileInfo> fileInfos2 = new LinkedList<FileInfo>(fileInfos);
fileInfos.clear();
threadPoolHandler(executorService,fileInfos2,path,filename,time);
}
}
threadPoolHandler(executorService,fileInfos,path,filename,time);
//判斷任務是否結束
executorService.shutdown();
while (true){
if (executorService.isTerminated()){
endingFlag = true;
break;
}
}
return null;
}
發現Read語句中的sort limit欄位沒有,之前設計是為了分頁用,現在沒用上(一次取非同步寫入檔案),大大降低查詢效率,去掉效率高了很多。
優化後代碼:
public List<FileInfo> handle(String collectionName, BasicDBObject query, BasicDBObject fields,Date time,String path) {
DBCursor dbCursor = this.mongoTemplate.getCollection(collectionName).find(query,fields);
List<FileInfo> fileInfos = new LinkedList<FileInfo>();
String filename = Constants.FILE_NAME;
int nThreads = Constants.nThreads;
ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
while (dbCursor.hasNext()){
FileInfo fileInfo = new FileInfo();
fileInfos.add(JavaBeanUtils.dbObject2Bean(dbCursor.next(),fileInfo));
if (fileInfos.size() == Constants.FLAG_VALUE){//每到5w條寫一次到檔案
final List<FileInfo> fileInfos2 = new LinkedList<FileInfo>(fileInfos);
fileInfos.clear();
threadPoolHandler(executorService,fileInfos2,path,filename,time);
}
}
threadPoolHandler(executorService,fileInfos,path,filename,time);
//判斷任務是否結束
executorService.shutdown();
while (true){
if (executorService.isTerminated()){
endingFlag = true;
break;
}
}
return null;
}
最終方案:
最終查詢mongo添加了有索引能縮小範圍的條件createtime和batchSize(如果cursor空閒一定時間後(10分鐘),server端是否將其移除,預設為false,即server會將空閒10分鐘的cursor移除以節約記憶體。如果為true,則表示server端不需要移除空閒的cursor,而是等待使用者手動關閉),迴圈createtime多執行緒獲取DBCursor,FLAG_VALUE調整到5000,保證資料及時寫入檔案
private void handle(String collectionName, BasicDBObject query, BasicDBObject fields, boolean isFull) {
DBCursor dbCursor = this.mongoTemplate.getCollection(collectionName).find(query,fields).batchSize(30);
List<FileInfo> fileInfos = new CopyOnWriteArrayList<>();
try {
while (dbCursor.hasNext()){
FileInfo fileInfo = new FileInfo();
fileInfos.add(JavaBeanUtils.dbObject2Bean(dbCursor.next(),fileInfo));
if (fileInfos.size()%100==0){
logger.info("=========我還活著,已經遍歷了{}條資料",fileInfos.size());
}
if (fileInfos.size() == searchConfig.getFlag_value()){//每到5k條寫一次到檔案
final List<FileInfo> fileInfos2 = new CopyOnWriteArrayList<FileInfo>(fileInfos);
fileInfos.clear();
task(fileInfos2,isFull);
}
}
if (fileInfos.size()>0){
task(fileInfos,isFull);
}
} finally {
//關閉遊標
dbCursor.close();
}
}
搞定!!!