一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java 利用DeferredResult實現http輪詢實時返回數據接口

Java 利用DeferredResult實現http輪詢實時返回數據接口

2021-08-31 11:21bridgeli Java教程

這篇文章主要介紹了Java 利用 DeferredResult 實現 http 輪詢實時返回數據接口,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下

今天這篇文章呢,不難,其實是解答我一直以來心里的一個疑問。是這樣的,之前看五八技術委員會主席沈劍老師的公眾號架構師之路的一篇文章:http 如何像 tcp 一樣實時的收消息,里面其中的一個方案是用 http 短連接輪詢的方式實現“偽長連接”。但是對于輪詢,我們的第一反應肯定是有延時,但是標題不是說的是實時嗎?當然我們可以把輪詢的時長縮短一些,先不說這樣大部分時間的輪詢調用,可能都沒消息返回,造成服務器資源浪費,輪詢時間再短也是有延時啊,所以難道是偽實時?反正一般消息延時個三五秒,甚至十秒八秒一分鐘,大家也不會在意,只會認為對方返回慢,對不起,這是我們程序員的鍋,但是 http 真的不能實現實時嗎?沈劍老師提出了一種方法:首選 webim 和 webserver 之間建立一條 http 連接,專門用作消息通道,這條連接叫 http 消息連接。然后會有如下處理:

1. 沒有消息到達的時候,這個 http 消息連接將被夯住,不返回,由于 http 是短連接,這個 http 消息連接最多被夯住 90 秒,就會被斷開(這是瀏覽器或者 webserver 的行為);

2. 在 1 的情況下,如果 http 消息連接被斷開,立馬再發起一個 http 消息連接;

此時在在 1 和 2 的配合下,瀏覽器與 webserver 之間將永遠有一條消息連接在,然后還有一種情況

3. 每次收到消息時,這個消息連接就能及時將消息帶回瀏覽器頁面,并且在返回后,會立馬再發起一個 http 消息連接

這樣就能做到使用 http 端連接輪詢的方式實現了實時收消息。不過需要說明的是,其實還有一種情況:消息到達時,上一個 http 消息連接正在返回,也就是第二種情況的時候突然來了一個消息,此時沒有 http 消息連接可用。雖然理論上 http 消息連接的返回是瞬時的,沒有消息連接可用出現的概率極小,但是根據墨菲定律我們知道,這種情況肯定會出現,所以這種情況下我們可以將消息暫存入消息池中,下一個消息連接到達后,無需等待,直接去消息池中取消息,將將消息帶回,然后立刻返回生成新的消息連接即可。

不過以上都不是今天這篇文章的重點,和今天這篇文章的標題也沒有任何關系。重點是當時看了沈劍老師的這篇文章后我一直有一個疑問:第一步的時候如何夯?。靠偛荒?sleep 吧,這多不優雅啊,由于一直以為沒有遇到過類似的需求,所以這么幾年來我也沒深究這個問題,但是心里確實一直記著,直到前一段時間,聽馬士兵教育的公開課,當時再講類似的問題的時候提到了夯住 http 的連接(具體是哪個問題,還真不記得了),雖然當時上課的老師沒提怎么實現,但是評論區我問了一下,如何夯住不返回?然后有一個同學回復說,用 DeferredResult,然后下課后搜了一下資料,果然可以,如下是實現的筆記,所以這才是重點,希望對有這個疑問的同學也有一點幫助。

1. 消息返回實體類,大家可以根據實際情況,自己定義即可:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package cn.bridgeli.deferredresulttest.entity;
 
import lombok.Data;
import lombok.Getter;
 
/**
 * @author bridgeli
 */
@Data
public class DeferredResultResponse {
    private Integer code;
    private String msg;
 
    public enum Msg {
        TIMEOUT("超時"),
        FAILED("失敗"),
        SUCCESS("成功");
 
        @Getter
        private String desc;
 
        Msg(String desc) {
            this.desc = desc;
        }
    }
}

2. controller 接口:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package cn.bridgeli.deferredresulttest.controller;
 
import cn.bridgeli.deferredresulttest.entity.DeferredResultResponse;
import cn.bridgeli.deferredresulttest.service.DeferredResultService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
 
import javax.annotation.Resource;
 
 
/**
 * @author bridgeli
 */
@RestController
@RequestMapping(value = "/deferred-result")
public class DeferredResultController {
 
    @Resource
    private DeferredResultService deferredResultService;
 
    /**
     * 為了方便測試,簡單模擬一個
     * 多個請求用同一個requestId會出問題
     */
    private final String requestId = "test";
 
 
    @GetMapping(value = "/get")
    public DeferredResult<DeferredResultResponse> get(@RequestParam(value = "timeout", required = false, defaultValue = "10000") Long timeout) {
        DeferredResult<DeferredResultResponse> deferredResult = new DeferredResult<>(timeout);
 
        deferredResultService.process(requestId, deferredResult);
 
        return deferredResult;
    }
 
    /**
     * 設置DeferredResult對象的result屬性,模擬異步操作
     *
     * @param desired
     * @return
     */
    @GetMapping(value = "/result")
    public String settingResult(@RequestParam(value = "desired", required = false, defaultValue = "成功") String desired) {
        DeferredResultResponse deferredResultResponse = new DeferredResultResponse();
        if (DeferredResultResponse.Msg.SUCCESS.getDesc().equals(desired)) {
            deferredResultResponse.setCode(HttpStatus.OK.value());
            deferredResultResponse.setMsg(desired);
        } else {
            deferredResultResponse.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
            deferredResultResponse.setMsg(DeferredResultResponse.Msg.FAILED.getDesc());
        }
        deferredResultService.settingResult(requestId, deferredResultResponse);
 
        return "Done";
    }
}

其中:/get 接口模擬沈劍老師說的消息連接,/result 接口模擬有一條新消息來了,然后 /get 接口會立即返回。主要注意的是 requestId,在實際項目中不能使用同一個,否則會出現問題,這個測一下就知道了,也很容易想到原因。

3. service 實現:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package cn.bridgeli.deferredresulttest.service;
 
import cn.bridgeli.deferredresulttest.entity.DeferredResultResponse;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.async.DeferredResult;
 
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
 
/**
 * @author bridgeli
 */
@Service
public class DeferredResultService {
 
    private Map<String, Consumer<DeferredResultResponse>> taskMap;
 
    public DeferredResultService() {
        taskMap = new ConcurrentHashMap<>();
    }
 
    /**
     * 將請求id與setResult映射
     *
     * @param requestId
     * @param deferredResult
     */
    public void process(String requestId, DeferredResult<DeferredResultResponse> deferredResult) {
        // 請求超時的回調函數
        deferredResult.onTimeout(() -> {
            taskMap.remove(requestId);
            DeferredResultResponse deferredResultResponse = new DeferredResultResponse();
            deferredResultResponse.setCode(HttpStatus.REQUEST_TIMEOUT.value());
            deferredResultResponse.setMsg(DeferredResultResponse.Msg.TIMEOUT.getDesc());
            deferredResult.setResult(deferredResultResponse);
        });
 
        Optional.ofNullable(taskMap)
                .filter(t -> !t.containsKey(requestId))
                .orElseThrow(() -> new IllegalArgumentException(String.format("requestId=%s is existing", requestId)));
 
        taskMap.putIfAbsent(requestId, deferredResult::setResult);
    }
 
    /**
     * 這里相當于異步的操作方法
     * 設置DeferredResult對象的setResult方法
     *
     * @param requestId
     * @param deferredResultResponse
     */
    public void settingResult(String requestId, DeferredResultResponse deferredResultResponse) {
        if (taskMap.containsKey(requestId)) {
            Consumer<DeferredResultResponse> deferredResultResponseConsumer = taskMap.get(requestId);
            // 這里相當于DeferredResult對象的setResult方法
            deferredResultResponseConsumer.accept(deferredResultResponse);
            taskMap.remove(requestId);
        }
    }
 
}

文章最后,我想在說明另外一個問題,我們利用 DeferredResult 實現了 http 輪詢返回,其實換個思路想問題,我們是不是也實現了 http 接口延時返回?所以如果你有延時返回的需求,同樣可以借助 DeferredResult 實現。

以上就是Java 利用 DeferredResult 實現 http 輪詢實時返回數據接口的詳細內容,更多關于Java 實現 http 輪詢實時返回數據接口的資料請關注服務器之家其它相關文章!

原文鏈接:http://www.bridgeli.cn/archives/689

延伸 · 閱讀

精彩推薦
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7472021-02-04
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
主站蜘蛛池模板: 日本人成大片在线 | 精品无码久久久久久久久 | 全彩孕交漫画福利啪啪吧 | 青青草成人在线观看 | 男人机机桶女人机机 | 日本在线播放视频 | 青草青草伊人精品视频 | 全黄毛片 | 91精品91| 国产成人亚洲综合a∨婷婷 国产成人亚洲精品乱码在线观看 | 91在线免费看 | 国产自在自拍 | 四虎麻豆 | 亚洲欧美天堂综合久久 | 国产精品久久久久久久久久久久久久 | 国产精品29页 | 四虎成人www国产精品 | 精品人人做人人爽久久久 | 毛片资源| 情侣奴伺候女王第2部分小说 | 成人免费淫片95视频观看网站 | 2020年最新国产精品视频免费 | 亚洲国产成人综合 | 武侠古典久久亚洲精品 | h卡通第一页 | 亚洲精品动漫免费二区 | 欧美坐爱| 久久精品中文闷骚内射 | 国产成人免费在线观看 | 韩国最新理论三级在线观看 | 国产亚洲精品一区在线播 | 高清一区高清二区视频 | 午夜无码片在线观看影院 | 99热在这里只有精品 | 成人精品一区二区三区 | 精品老司机在线视频香蕉 | 经典三级四虎在线观看 | 欧美3p大片在线观看完整版 | 国产亚洲精品一区二区在线播放 | 美女被狂揉下部羞羞动漫 | 亚洲人成网站在线观看播放青青 |