1. 程式人生 > >com.mongodb.MongoSocketReadTimeoutException: Timeout while receiving message

com.mongodb.MongoSocketReadTimeoutException: Timeout while receiving message

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();
        }
    }

搞定!!!