1. 程式人生 > >SpringCloud之大資料轉移RequestTooBigException:Connection terminated as request was larger than 10485760

SpringCloud之大資料轉移RequestTooBigException:Connection terminated as request was larger than 10485760

在SpringCloud微服務專案中,應公司專案需求,需要將A伺服器的大資料(他們給我們oracle資料庫地址、使用者名稱密碼、檢視等資訊)轉移到公司伺服器mysql資料庫中。

首先,新加兩個服務:分別是獲取資料的服務(Spring-Cloud-GetData)、儲存資料的服務(Spring-Cloud-SaveData)。

在我的專案中(Spring-Cloud-Web)服務先去呼叫Spring-Cloud-GetData獲取資料的介面,再通過宣告式服務呼叫Spring-Cloud-SaveData儲存資料。在此期間,由於資料量太大,導致獲取資料後報錯:

server.RequestTooBigException: UT000020: Connection terminated as request was larger than 10485760

原因:我的宣告式服務呼叫feign配置了請求壓縮

#請求壓縮
feign.compression.request.enabled=true
feign.compression.response.enabled=true

但是未指定max-request-size:預設是10485760,對於大資料來說,顯然是不夠的。

解決:

#請求壓縮
feign.compression.request.enabled=true
feign.compression.response.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
feign.compression.request.max-request-size=2097152000

同時,由於資料量大,請求時間肯定要加多一點,不然肯定會被熔斷的:

#ribbon請求連線的超時時間- 限制3秒內必須請求到服務,並不限制服務處理的返回時間
ribbon.ConnectTimeout=14400000
#請求處理的超時時間 下級服務響應最大時間,超出時間消費方(路由也是消費方)返回timeout
ribbon.ReadTimeout=14400000

同時,我將需要轉移的資料的時間段分割,以達到分批轉移的目的:

/**
 * 資料轉移 oracle-->mysql
 */
@RequestMapping(value = "/saveStudentData")
public EqResult saveStudentData(@RequestParam String startTime,@RequestParam String endTime) throws Exception{
    EqResult eqResult = new EqResult();
        List<KeyValueForDate> list = SplitDateUtil.getKeyValueForDate(startTime,endTime);
        if(!list.isEmpty()){
            for(KeyValueForDate date : list){
                System.out.println("時間段:"+date.getStartDate()+"--至---"+date.getEndDate()+"--轉移開始,第一步獲取資料...");
                EqResult<List<Getstudent>> listEqResult = getStudentService.getStudent(date.getStartDate(),date.getEndDate());
                if(CheckUtil.checkEqresultSuccessAndData(listEqResult)){
                    System.out.println("時間段:"+date.getStartDate()+"--至---"+date.getEndDate()+"--轉移開始,第二步轉移資料...");
                    eqResult = indexService.saveStudent(listEqResult.getData());
                    if(CheckUtil.checkEqresultSuccess(eqResult)){
                        System.out.println("時間段:"+date.getStartDate()+"--至---"+date.getEndDate()+"--轉移完成,判斷是否進入下一輪...");
                    }else{
                        System.out.println("時間段:"+date.getStartDate()+"--至---"+date.getEndDate()+"--轉移失敗,第二步未轉移資料...");
                    }
                }else{
                    System.out.println("時間段:"+date.getStartDate()+"--至---"+date.getEndDate()+"--轉移失敗,第一步未獲取到資料...");
                }
            }
        }else{
            System.out.println("-----------時間段為空-------------");
        }
        return eqResult;
}

 工具類SplitDateUtil:

public class SplitDateUtil {
 
    @Test
    public void demo(){
        List<KeyValueForDate> list = SplitDateUtil.getKeyValueForDate("2015-08-23","2016-06-10");
        System.out.println("開始日期--------------結束日期");
        for(KeyValueForDate date : list){
            System.out.println(date.getStartDate()+"-----"+date.getEndDate());
        }
    }
 
    /**
     * 根據一段時間區間,按月份拆分成多個時間段
     * @param startDate 開始日期
     * @param endDate  結束日期
     * @return
     */
    @SuppressWarnings("deprecation")
    public static List<KeyValueForDate> getKeyValueForDate(String startDate,String endDate) {
        List<KeyValueForDate> list = null;
        try {
            list = new ArrayList<KeyValueForDate>();
            String firstDay = "";
            String lastDay = "";
            Date d1 = new SimpleDateFormat("yyyy-MM-dd").parse(startDate);// 定義起始日期
            Date d2 = new SimpleDateFormat("yyyy-MM-dd").parse(endDate);// 定義結束日期
            Calendar dd = Calendar.getInstance();// 定義日期例項
            dd.setTime(d1);// 設定日期起始時間
            Calendar cale = Calendar.getInstance();
            Calendar c = Calendar.getInstance();
            c.setTime(d2);
            int startDay = d1.getDate();
            int endDay = d2.getDate();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            KeyValueForDate keyValueForDate = null;
            while (dd.getTime().before(d2)) {// 判斷是否到結束日期
                keyValueForDate = new KeyValueForDate();
                cale.setTime(dd.getTime());
                if(dd.getTime().equals(d1)){
                    cale.set(Calendar.DAY_OF_MONTH, dd.getActualMaximum(Calendar.DAY_OF_MONTH));
                    lastDay = sdf.format(cale.getTime());
                    keyValueForDate.setStartDate(sdf.format(d1));
                    keyValueForDate.setEndDate(lastDay);
                }else if(dd.get(Calendar.MONTH) == d2.getMonth() && dd.get(Calendar.YEAR) == c.get(Calendar.YEAR)){
                    cale.set(Calendar.DAY_OF_MONTH,1);//取第一天
                    firstDay = sdf.format(cale.getTime());
                    keyValueForDate.setStartDate(firstDay);
                    keyValueForDate.setEndDate(sdf.format(d2));
                }else {
                    cale.set(Calendar.DAY_OF_MONTH,1);//取第一天
                    firstDay = sdf.format(cale.getTime());
                    cale.set(Calendar.DAY_OF_MONTH, dd.getActualMaximum(Calendar.DAY_OF_MONTH));
                    lastDay = sdf.format(cale.getTime());
                    keyValueForDate.setStartDate(firstDay);
                    keyValueForDate.setEndDate(lastDay);
                }
                list.add(keyValueForDate);
                dd.add(Calendar.MONTH, 1);// 進行當前日期月份加1
            }
            if(endDay<startDay){
                keyValueForDate = new KeyValueForDate();
                cale.setTime(d2);
                cale.set(Calendar.DAY_OF_MONTH,1);//取第一天
                firstDay = sdf.format(cale.getTime());
                keyValueForDate.setStartDate(firstDay);
                keyValueForDate.setEndDate(sdf.format(d2));
                list.add(keyValueForDate);
            }
        } catch (Exception e) {
            return null;
        }
        return list;
    }
 
}

 實體類KeyValueForDate:

public class KeyValueForDate{
    private String startDate;
    private String endDate;
    public String getStartDate() {
        return startDate;
    }
    public void setStartDate(String startDate) {
        this.startDate = startDate;
    }
    public String getEndDate() {
        return endDate;
    }
    public void setEndDate(String endDate) {
        this.endDate = endDate+" 23:59:59";
    }
}

備註,到這裡我已經成功解決了所有問題,同時,將我在網上找的解決方案提供參考(未解決我的專案需求):

#限制3000MB大小
spring.http.multipart.max-file-size=3000MB
spring.http.multipart.max-request-size=3000MB
server.max-http-post-size=1048576000
spring.sleuth.keys.http.request-size=1048576000
spring.sleuth.keys.http.response-size=1048576000
server.max-http-header-size=1048576000

# 以下的配置會影響buffer,這些buffer會用於伺服器連線的IO操作,有點類似netty的池化記憶體管理
# 每塊buffer的空間大小,越小的空間被利用越充分,不要設定太大,以免影響其他應用,合適即可

server.undertow.buffer-size=2048
server.undertow.max-http-post-size=1048576000
# 每個區分配的buffer數量 , 所以pool的大小是buffer-size * buffers-per-region

server.undertow.buffers-per-region=2048

# 是否分配的直接記憶體(NIO直接分配的堆外記憶體)

 到了這裡,你還不點贊嗎?    嘻嘻^-^