最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java通過RESTful API實(shí)現(xiàn)兩個(gè)項(xiàng)目之間相互傳輸數(shù)據(jù)

 更新時(shí)間:2025年09月17日 09:16:17   作者:Luffy船長  
一些特殊場景中,兩個(gè)項(xiàng)目發(fā)布在不同的服務(wù)器,并且由于服務(wù)器限制特殊情況ip無法相通時(shí)進(jìn)行開放接口方式進(jìn)行數(shù)據(jù)傳輸,下面我們就來看看java通過RESTful API實(shí)現(xiàn)這一需求吧

項(xiàng)目場景

一些特殊場景中,兩個(gè)項(xiàng)目發(fā)布在不同的服務(wù)器,并且由于服務(wù)器限制特殊情況ip無法相通時(shí)進(jìn)行開放接口方式進(jìn)行數(shù)據(jù)傳輸

問題描述

兩個(gè)服務(wù)器之間ip無法相互訪問,數(shù)據(jù)沒法進(jìn)行數(shù)據(jù)傳輸

解決方案

通過RESTful API方式定義開放接口實(shí)現(xiàn)數(shù)據(jù)傳輸

1.開放接口定義

@RequestMapping(value = "/getss", produces = "application/json;charset=UTF-8")
public void getTestChunked(HttpServletResponse response) {
    log.info("getTest分塊傳輸接口調(diào)用");
    long startTime = System.currentTimeMillis();
    OutputStream outputStream = null;
    JsonGenerator generator = null;

    // 調(diào)用 GetTime 類的 getFSKTime 方法
    GetTime getTime = new GetTime();
    HashMap<Object, Object> timeData = getTime.getFSKTime();

    try {
        // 獲取查詢參數(shù)
        String formattedDate = (String) timeData.get("formattedDate");
        String formattedEnd = (String) timeData.get("formattedEnd");
        log.info("查詢時(shí)間范圍:{} ~ {}", formattedDate, formattedEnd);

        // 獲取所有數(shù)據(jù)
        DataSourceUtil.setDB("db2");
        List<HbCcsPolicyDataRc> dataList = synchronizationService.getSynchronization(formattedDate, formattedEnd);

        if (dataList == null) {
            dataList = Collections.emptyList();
        }

        log.info("查詢完成:共{}條數(shù)據(jù),準(zhǔn)備分塊傳輸", dataList.size());

        // 設(shè)置響應(yīng)頭
        response.setContentType("application/json;charset=UTF-8");
        response.setHeader("Connection", "keep-alive");
        response.setHeader("Transfer-Encoding", "chunked");
        response.setHeader("X-Total-Count", String.valueOf(dataList.size()));
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");

        // 使用Jackson流式API分塊寫入響應(yīng)
        ObjectMapper objectMapper = new ObjectMapper();
        outputStream = response.getOutputStream();
        generator = objectMapper.getFactory().createGenerator(outputStream);

        generator.writeStartArray(); // 開始數(shù)組

        int chunkSize = 500; // 減小每塊的大小,避免緩沖區(qū)溢出
        int totalSize = dataList.size();

        for (int i = 0; i < totalSize; i++) {
            // 檢查客戶端是否仍然連接
            try {
                response.getOutputStream(); // 這將拋出異常如果客戶端斷開
            } catch (IOException e) {
                log.warn("客戶端已斷開連接,終止傳輸");
                break;
            }

            // 寫入單個(gè)對(duì)象
            objectMapper.writeValue(generator, dataList.get(i));

            // 每chunkSize條數(shù)據(jù)刷新一次緩沖區(qū)
            if ((i + 1) % chunkSize == 0) {
                generator.flush();
                log.info("已傳輸{}條數(shù)據(jù),進(jìn)度: {}%", i + 1, (i + 1) * 100 / totalSize);

                // 添加小延遲,避免 overwhelming 客戶端
                try {
                    Thread.sleep(10);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }

        generator.writeEndArray(); // 結(jié)束數(shù)組
        generator.flush();

        log.info("分塊傳輸完成:共{}條數(shù)據(jù),耗時(shí){}ms",
                dataList.size(), System.currentTimeMillis() - startTime);

    } catch (ClientAbortException e) {
        log.warn("客戶端中止了連接: {}", e.getMessage());
    } catch (Exception e) {
        log.error("getTest分塊傳輸接口執(zhí)行失敗", e);
        if (!response.isCommitted()) {
            try {
                response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(), "數(shù)據(jù)獲取失敗");
            } catch (IOException ex) {
                log.error("發(fā)送錯(cuò)誤響應(yīng)失敗", ex);
            }
        }
    } finally {
        // 確保資源被正確關(guān)閉
        try {
            if (generator != null) {
                generator.close();
            }
        } catch (IOException e) {
            log.warn("關(guān)閉JsonGenerator時(shí)發(fā)生錯(cuò)誤", e);
        }
        try {
            if (outputStream != null) {
                outputStream.close();
            }
        } catch (IOException e) {
            log.warn("關(guān)閉OutputStream時(shí)發(fā)生錯(cuò)誤", e);
        }

        // 幫助GC回收大對(duì)象
        data1 = null;
        System.gc();
    }
}

2.開放接口調(diào)用

public void fetchDataFromServer1() {
    log.info("===== 開始執(zhí)行 fetchDataFromServer1(分塊傳輸模式)=====");
    RestTemplate chunkedRestTemplate = createChunkedRestTemplate();

    String url = server1BaseUrl + "/getReceive/getsss";
    log.info("調(diào)用 getTestChunked 分塊傳輸接口,URL: {}", url);

    try {
        // 使用自定義的ResponseExtractor來處理流式響應(yīng)
        List<HbCcsPolicyDataRc> dataList = chunkedRestTemplate.execute(
                url,
                HttpMethod.GET,
                null,
                new ResponseExtractor<List<HbCcsPolicyDataRc>>() {
                    @Override
                    public List<HbCcsPolicyDataRc> extractData(ClientHttpResponse response) throws IOException {
                        return processStreamingResponse(response, HbCcsPolicyDataRc.class);
                    }
                }
        );

        log.info("===== fetchDataFromServer1 完成,共處理{}條數(shù)據(jù) =====", dataList != null ? dataList.size() : 0);
        getCCSCroData();
    } catch (Exception e) {
        log.error("fetchDataFromServer1 整體失敗", e);
        throw new RuntimeException("fetchDataFromServer1 失敗:" + e.getMessage(), e);
    } finally {
        // 幫助GC回收資源
        System.gc();
    }
}
// 修改流式響應(yīng)處理方法
private <T> List<T> processStreamingResponse(ClientHttpResponse response, Class<T> valueType) throws IOException {
    List<T> dataList = new ArrayList<>();
    ObjectMapper objectMapper = getConfiguredObjectMapper();
    InputStream inputStream = response.getBody();

    try (JsonParser parser = objectMapper.getFactory().createParser(inputStream)) {
        if (parser.nextToken() != JsonToken.START_ARRAY) {
            throw new IOException("Expected data to start with an Array");
        }

        int count = 0;
        long lastLogTime = System.currentTimeMillis();

        while (parser.nextToken() != JsonToken.END_ARRAY) {
            T record = objectMapper.readValue(parser, valueType);
            dataList.add(record);
            count++;

            // 每1000條或每30秒日志輸出一次
            long currentTime = System.currentTimeMillis();
            if (count % 1000 == 0 || currentTime - lastLogTime > 30000) {
                log.info("已解析{}條數(shù)據(jù)", count);
                lastLogTime = currentTime;
            }

            // 定期批量處理,避免內(nèi)存占用過高
            if (count % 5000 == 0) {
                processBatchData(dataList, valueType);
                dataList.clear(); // 清空列表,避免內(nèi)存占用過高
                System.gc(); // 建議垃圾回收
            }
        }

        // 處理最后一批數(shù)據(jù)
        if (!dataList.isEmpty()) {
            processBatchData(dataList, valueType);
        }
    }

    log.info("共解析{}條數(shù)據(jù)", dataList.size());
    return dataList;
}

// 批量處理數(shù)據(jù)的方法
private <T> void processBatchData(List<T> dataList, Class<T> valueType) {
    try {
        DataSourceUtil.setDB("db3");

        if (valueType == HbCcsPolicyDataRc.class) {
            dmService.setHbCcsPolicyDataRc((List<HbCcsPolicyDataRc>) dataList);
        } else if (valueType == HbCcsCrosssellPEMIUM.class) {
            dmService.setHbCcsCrosssellPEMIUM((List<HbCcsCrosssellPEMIUM>) dataList);
        } else if (valueType == HbCcsLpDataRc.class) {
            dmService.HbCcsLpDataRc((List<HbCcsLpDataRc>) dataList);
        }

        log.info("成功處理一批數(shù)據(jù),數(shù)量: {}", dataList.size());
    } catch (Exception e) {
        log.error("處理批量數(shù)據(jù)時(shí)發(fā)生錯(cuò)誤", e);
        // 這里可以添加重試邏輯或錯(cuò)誤記錄
    }
}


// 6. 工具方法:復(fù)用 JSON 解析器配置(避免重復(fù)設(shè)置容錯(cuò)屬性)
private ObjectMapper getConfiguredObjectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    // 容錯(cuò)配置:忽略未知字段、允許單值數(shù)組
    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
    return objectMapper;
}





// 創(chuàng)建專用的分塊傳輸RestTemplate
private RestTemplate createChunkedRestTemplate() {
    SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();

    // 設(shè)置超時(shí)時(shí)間(單位:毫秒)
    factory.setConnectTimeout(120000); // 2分鐘連接超時(shí)
    factory.setReadTimeout(3600000);   // 1小時(shí)讀取超時(shí)(大數(shù)據(jù)傳輸需要更長時(shí)間)

    // 禁用緩沖
    factory.setBufferRequestBody(false);

    RestTemplate restTemplate = new RestTemplate(factory);

    // 配置消息轉(zhuǎn)換器
    List<HttpMessageConverter<?>> converters = new ArrayList<>();

    // 字符串轉(zhuǎn)換器
    StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
    stringConverter.setWriteAcceptCharset(false);
    converters.add(stringConverter);

    // JSON轉(zhuǎn)換器
    MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
    jsonConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON));
    converters.add(jsonConverter);

    restTemplate.setMessageConverters(converters);

    return restTemplate;
}

3.核心配置

這個(gè)配置兩個(gè)項(xiàng)目的yml都需要加

server1:
  base-url: http://127.0.0.1:8060#根據(jù)情況而變
  auth:
    username: admin
    password: admin

server:
  max-http-header-size: 1000000
  servlet:
    multipart:
      max-file-size: 1000MB
      max-request-size: 1000MB
  tomcat:
    max-swallow-size: 1000MB
    max-http-form-post-size: 1000MB
    threads:
      max: 200
      min-spare: 20
    keep-alive-timeout: 300000
@Value("${server1.base-url}")
private String server1BaseUrl;

這段代碼加到controller類中這是將yml配置文件的路徑注冊(cè)到controller了

這種適合小數(shù)據(jù)傳輸,大數(shù)據(jù)傳輸需要配置服務(wù)器的Nginx配置需要將接口配置上盡量統(tǒng)一接口前綴不然可能會(huì)報(bào)錯(cuò)具體就不展示了

到此這篇關(guān)于java通過RESTful API實(shí)現(xiàn)兩個(gè)項(xiàng)目之間相互傳輸數(shù)據(jù)的文章就介紹到這了,更多相關(guān)java項(xiàng)目互傳輸數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot-mysql-HikariCP集成過程

    springboot-mysql-HikariCP集成過程

    HiKariCP opens new window是數(shù)據(jù)庫連接池的一個(gè)后起之秀,號(hào)稱性能最好,可以完美地 PK 掉其他連接池,這篇文章主要介紹了springboot-mysql-HikariCP集成過程,需要的朋友可以參考下
    2023-07-07
  • Java設(shè)計(jì)模式之單例模式

    Java設(shè)計(jì)模式之單例模式

    這篇文章主要給大家介紹了關(guān)于Java單例模式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-10-10
  • Java中使用File類創(chuàng)建文件方法總結(jié)

    Java中使用File類創(chuàng)建文件方法總結(jié)

    Java File類是I/O操作基礎(chǔ),用于表示文件路徑,提供構(gòu)造方法、文件創(chuàng)建、信息獲取(如名稱、路徑、大小)及目錄操作,這篇文章主要介紹了Java中使用File類創(chuàng)建文件方法的相關(guān)資料,需要的朋友可以參考下
    2025-05-05
  • Java?Web中Ajax技術(shù)使用方法介紹

    Java?Web中Ajax技術(shù)使用方法介紹

    ajax技術(shù)是使頁面能局部刷新的一種技術(shù),下面這篇文章主要給大家介紹了關(guān)于JavaWeb之Ajax的基本使用與實(shí)戰(zhàn)案例的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-10-10
  • Spring Gateway動(dòng)態(tài)路由的實(shí)現(xiàn)方案

    Spring Gateway動(dòng)態(tài)路由的實(shí)現(xiàn)方案

    Spring Cloud Gateway (下文簡稱 Gateway)作為微服務(wù)網(wǎng)關(guān),動(dòng)態(tài)配置路由是剛需,畢竟沒人想路由一改就要重啟網(wǎng)關(guān),本文結(jié)合源碼,提供一種動(dòng)態(tài)路由配置的思路,需要的朋友可以參考下
    2025-09-09
  • Java?并發(fā)編程之深入理解"鎖可中斷"機(jī)制

    Java?并發(fā)編程之深入理解"鎖可中斷"機(jī)制

    在Java并發(fā)編程中,死鎖(Deadlock)和線程阻塞(Blocking)是開發(fā)者最頭疼的問題之一,本文給大家介紹Java?并發(fā)編程之深入理解“鎖可中斷”機(jī)制,感興趣的朋友跟隨小編一起看看吧
    2026-03-03
  • Java線程安全和鎖Synchronized知識(shí)點(diǎn)詳解

    Java線程安全和鎖Synchronized知識(shí)點(diǎn)詳解

    在本篇文章里小編給大家分享的是關(guān)于Java線程安全和鎖Synchronized相關(guān)知識(shí)點(diǎn),有需要的朋友們可以參考下。
    2019-08-08
  • RabbitMQ實(shí)現(xiàn)消費(fèi)端限流的步驟

    RabbitMQ實(shí)現(xiàn)消費(fèi)端限流的步驟

    消費(fèi)者端限流的主要目的是控制消費(fèi)者每次從 RabbitMQ 中獲取的消息數(shù)量,從而實(shí)現(xiàn)消息處理的流量控制,這篇文章主要介紹了RabbitMQ如何實(shí)現(xiàn)消費(fèi)端限流,需要的朋友可以參考下
    2024-03-03
  • Servlet實(shí)現(xiàn)共享數(shù)據(jù)JavaWeb組件的幾種方法

    Servlet實(shí)現(xiàn)共享數(shù)據(jù)JavaWeb組件的幾種方法

    本文將結(jié)合實(shí)例代碼,介紹Servlet實(shí)現(xiàn)共享數(shù)據(jù)JavaWeb組件的幾種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • springboot集成elasticsearch7的圖文方法

    springboot集成elasticsearch7的圖文方法

    本文記錄springboot集成elasticsearch7的方法,本文通過圖文實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-05-05

最新評(píng)論

乐至县| 雷州市| 如皋市| 浏阳市| 镇雄县| 新建县| 余姚市| 景泰县| 临西县| 剑河县| 舞阳县| 吉林市| 宜兰县| 陕西省| 景宁| 昌乐县| 交城县| 榆树市| 邵武市| 孝昌县| 湘潭县| 北宁市| 大港区| 巴马| 大城县| 海兴县| 突泉县| 灵丘县| 晋州市| 延安市| 兖州市| 泰安市| 南京市| 平湖市| 鸡泽县| 新丰县| 二连浩特市| 张家港市| 德化县| 石林| 永定县|