精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

SpringBoot + ResponseBodyEmitter 實時異步流式推送,優雅!

開發 開發工具
SpringBoot 的 ResponseBodyEmitter 是一個非常實用的工具,用好了能給咱們的項目帶來很大的便利,提升用戶體驗。它不像有些技術那樣晦澀難懂,只要理解了它的基本原理和用法,上手還是挺容易的。

兄弟們,今天咱們來聊個挺酷的技術 —— 用 SpringBoot 結合 ResponseBodyEmitter 搞實時異步流式推送。估計不少人看到 “流式推送” 這四個字,腦子里已經開始浮現各種復雜的場景了,比如實時聊天、數據監控啥的。別急,咱們今天就用大白話,配上點小幽默,把這玩意兒給盤明白。

先來說說為啥需要這東西。咱們平時寫的 Web 接口,大多是 “一問一答” 的模式。客戶端發個請求,服務器哐哐哐處理完,打包成一個響應扔回去,完事。這就好比你去便利店買瓶可樂,付了錢拿了貨,轉身就走,整個過程干凈利落。

但有些場景就不一樣了。比如說,你在看直播的時候,主播那邊的畫面和聲音是一幀一幀、一秒一秒不斷傳過來的,總不能等直播結束了,一次性給你整個視頻文件吧?再比如,你在用某個數據分析工具,想實時看到數據處理的進度,總不能每隔兩秒就手動刷新一次頁面吧?這時候,“流式推送” 就派上用場了 —— 服務器可以像流水一樣,一點一點把數據推給客戶端,不用等所有事情都做完。

在 ResponseBodyEmitter 出現之前,咱們想搞這種實時推送,要么用 WebSocket,要么用輪詢。WebSocket 是個好東西,全雙工通信,但是配置起來有點麻煩,對于一些簡單的場景來說,有點 “殺雞用牛刀” 的感覺。輪詢就更別說了,客戶端傻乎乎地每隔一段時間就問服務器 “有新數據嗎?有新數據嗎?”,不僅效率低,還浪費資源,服務器估計都想拉黑這種客戶端。

那 ResponseBodyEmitter 是啥呢?簡單說,它就是 SpringBoot 提供的一個工具,能讓服務器一邊處理數據,一邊把處理好的部分一點點推給客戶端,不用等全部處理完。就像你點了一份小龍蝦,廚師不是等所有蝦都做好了才端上來,而是炒好一盤先給你端一盤,讓你先吃著,他繼續炒下一盤。這體驗,是不是比干等著強多了?

咱們先來看個簡單的例子,感受一下 ResponseBodyEmitter 的用法。

首先,得在 SpringBoot 項目里引入 Web 依賴,這個不用多說,搞 SpringBoot 的都懂。然后,寫一個 Controller:

@RestController
public class EmitterController {
    @GetMapping("/stream")
    public ResponseBodyEmitter streamData() {
        // 創建一個ResponseBodyEmitter對象,這里可以設置超時時間,比如30秒
        ResponseBodyEmitter emitter = new ResponseBodyEmitter(30000L);
        // 開個線程處理數據,模擬耗時操作
        new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    // 每隔1秒推送一次數據
                    Thread.sleep(1000);
                    // 推送數據,這里可以是字符串、對象等
                    emitter.send("這是第" + (i + 1)次推送的數據");
                }
                // 推送完成,關閉emitter
                emitter.complete();
            } catch (Exception e) {
                // 發生異常時,通知客戶端
                emitter.completeWithError(e);
            }
        }).start();
        return emitter;
    }
}

就這么幾行代碼,一個簡單的流式推送接口就搞定了。客戶端請求/stream接口后,不會馬上收到一個完整的響應,而是會每隔 1 秒收到一段數據,一共收到 5 段,最后連接關閉。是不是很簡單?可能有兄弟會問,這玩意兒是怎么實現的呢?其實原理也不復雜。當服務器返回 ResponseBodyEmitter 對象時,Spring 并不會馬上把響應發給客戶端,而是會保持這個連接。然后,當我們調用 emitter.send () 方法時,Spring 就會把數據一點點寫到響應流里,客戶端就能實時收到了。直到調用 emitter.complete (),或者發生異常調用 emitter.completeWithError (),這個連接才會被關閉。

這里有個小細節,就是 ResponseBodyEmitter 的超時時間。如果服務器在規定時間內沒做任何操作(既沒 send 數據,也沒 complete),這個連接就會超時關閉。所以在實際使用中,得根據業務場景合理設置超時時間,別太短也別太長。太短了,可能數據還沒推完就斷了;太長了,會占用服務器連接資源。

咱們再來說說客戶端怎么接收這些流式數據。如果是瀏覽器端,可以用 JavaScript 的 Fetch API 或者 XMLHttpRequest 來接收。比如用 Fetch:

fetch('/stream')
  .then(response => {
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    function read() {
      return reader.read().then(({ done, value }) => {
        if (done) {
          console.log('推送完成');
          return;
        }
        // 解碼收到的二進制數據為字符串
        const data = decoder.decode(value, { stream: true });
        console.log('收到數據:', data);
        return read();
      });
    }
    return read();
  })
  .catch(error => {
    console.error('發生錯誤:', error);
  });

這樣,瀏覽器控制臺就會每隔 1 秒打印出服務器推送的數據,體驗還是挺不錯的。不過,上面那個例子還是太簡單了,實際項目中用起來,還得考慮不少問題。比如說,怎么管理多個 Emitter 實例?如果一個客戶端打開了多個頁面,或者多個客戶端同時連接,總不能每個請求都 new 一個 Emitter 吧?這時候,咱們就需要一個 Emitter 的管理器了。

咱們可以搞一個 EmitterManager 類,用來保存和管理所有的 Emitter 實例:

@Component
public class EmitterManager {
    // 用一個Map來保存Emitter,key可以是用戶標識,value是對應的Emitter
    private final Map<String, ResponseBodyEmitter> emitters = new ConcurrentHashMap<>();
    // 添加Emitter
    public void addEmitter(String userId, ResponseBodyEmitter emitter) {
        // 當Emitter完成或出錯時,自動從Map中移除
        emitter.onCompletion(() -> emitters.remove(userId));
        emitter.onError(e -> emitters.remove(userId));
        emitters.put(userId, emitter);
    }
    // 根據用戶標識獲取Emitter
    public ResponseBodyEmitter getEmitter(String userId) {
        return emitters.get(userId);
    }
    // 移除Emitter
    public void removeEmitter(String userId) {
        emitters.remove(userId);
    }
    // 向所有用戶推送數據
    public void sendToAll(Object data) throws IOException {
        for (ResponseBodyEmitter emitter : emitters.values()) {
            emitter.send(data);
        }
    }
    // 向指定用戶推送數據
    public void sendToUser(String userId, Object data) throws IOException {
        ResponseBodyEmitter emitter = emitters.get(userId);
        if (emitter != null) {
            emitter.send(data);
        }
    }
}

有了這個管理器,咱們就可以在 Controller 里根據用戶標識來管理 Emitter 了。比如,用戶登錄后,建立一個 Emitter 連接,服務器可以向這個用戶單獨推送數據,也可以向所有用戶廣播數據。

@RestController
public class UserEmitterController {
    @Autowired
    private EmitterManager emitterManager;
    @GetMapping("/user/stream/{userId}")
    public ResponseBodyEmitter userStream(@PathVariable String userId) {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter(30000L);
        emitterManager.addEmitter(userId, emitter);
        return emitter;
    }
    @PostMapping("/send/to/user")
    public String sendToUser(@RequestParam String userId, @RequestParam String message) {
        try {
            emitterManager.sendToUser(userId, message);
            return "發送成功";
        } catch (IOException e) {
            return "發送失敗:" + e.getMessage();
        }
    }
    @PostMapping("/send/to/all")
    public String sendToAll(@RequestParam String message) {
        try {
            emitterManager.sendToAll(message);
            return "發送成功";
        } catch (IOException e) {
            return "發送失敗:" + e.getMessage();
        }
    }
}

這樣一來,就實現了針對用戶的實時推送功能。比如在一個在線聊天系統里,A 用戶給 B 用戶發消息,服務器就可以通過 B 用戶的 Emitter 把消息推過去;如果有系統公告,就可以通過 sendToAll 推給所有在線用戶。不過,這里有個潛在的問題:如果用戶長時間不操作,Emitter 超時關閉了,這時候再給這個用戶推送消息,就會失敗。所以,咱們得想個辦法,讓客戶端在連接超時前,自動重新建立連接。

客戶端可以這么搞:當檢測到連接關閉后,隔一小會兒就重新發起請求,建立新的 Emitter 連接。比如在 JavaScript 里:

function connect(userId) {
  fetch(`/user/stream/${userId}`)
    .then(response => {
      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      function read() {
        return reader.read().then(({ done, value }) => {
          if (done) {
            console.log('連接關閉,重新連接...');
            // 連接關閉后,1秒后重新連接
            setTimeout(() => connect(userId), 1000);
            return;
          }
          const data = decoder.decode(value, { stream: true });
          console.log('收到消息:', data);
          return read();
        });
      }
      return read();
    })
    .catch(error => {
      console.error('連接出錯,重新連接...', error);
      setTimeout(() => connect(userId), 1000);
    });
}
// 頁面加載時,建立連接
connect('當前登錄用戶的ID');

這樣就能保證客戶端和服務器之間一直有一個活躍的連接了。再來說說數據格式的問題。上面的例子里,咱們推送的都是字符串,實際項目中,可能需要推送 JSON 格式的數據。沒關系,ResponseBodyEmitter 支持發送各種類型的對象,Spring 會自動幫我們進行序列化。

比如,咱們定義一個消息類:

public class Message {
    private String type; // 消息類型
    private String content; // 消息內容
    private long timestamp; // 時間戳
    // 構造方法、getter、setter省略
}

然后在發送的時候,直接 send 這個對象就行:

Message message = new Message();
message.setType("chat");
message.setContent("你好啊,老鐵");
message.setTimestamp(System.currentTimeMillis());
emitter.send(message);

Spring 會默認把它序列化成 JSON 格式的字符串推送給客戶端,客戶端收到后再解析成 JSON 對象就行,非常方便。可能有細心的兄弟發現了,咱們在發送數據的時候,用的是 emitter.send () 方法,那這個方法是線程安全的嗎?答案是:是的。ResponseBodyEmitter 的 send () 方法是線程安全的,所以我們可以在多個線程里同時調用 send () 方法推送數據,不用擔心并發問題。這一點還是挺貼心的,省得咱們自己加鎖了。

不過,雖然 send () 方法是線程安全的,但也不能瞎用。如果推送的數據量特別大,或者推送頻率特別高,還是可能會造成性能問題。這時候,咱們可能需要考慮用線程池來管理發送線程,避免創建過多的線程。

比如,可以在 EmitterManager 里注入一個線程池:

@Component
public class EmitterManager {
    private final ExecutorService executor = Executors.newFixedThreadPool(10);
    // 其他代碼省略...
    public void sendToUser(String userId, Object data) {
        ResponseBodyEmitter emitter = emitters.get(userId);
        if (emitter != null) {
            executor.submit(() -> {
                try {
                    emitter.send(data);
                } catch (IOException e) {
                    // 處理異常
                    emitter.completeWithError(e);
                    emitters.remove(userId);
                }
            });
        }
    }
}

這樣,發送數據的操作就會交給線程池里的線程去處理,不會阻塞主線程,也能控制并發量。另外,咱們還得考慮一個問題:如果服務器重啟了,或者 Emitter 因為某些原因意外關閉了,客戶端怎么知道?除了客戶端自動重連之外,服務器也可以在推送一些 “心跳” 數據,告訴客戶端 “我還活著”。比如,每隔一段時間,給所有客戶端推送一個空消息或者特定格式的心跳消息。

@Component
publicclass HeartbeatTask {

    @Autowired
    private EmitterManager emitterManager;

    @Scheduled(fixedRate = 10000) // 每10秒執行一次
    public void sendHeartbeat() {
        try {
            Message heartbeat = new Message();
            heartbeat.setType("heartbeat");
            heartbeat.setContent("");
            heartbeat.setTimestamp(System.currentTimeMillis());
            emitterManager.sendToAll(heartbeat);
        } catch (IOException e) {
            // 處理異常
        }
    }
}

客戶端收到心跳消息后,就知道連接還是正常的,不用重新連接。如果超過一定時間沒收到心跳,就主動重新連接。說到這里,可能有兄弟會拿 ResponseBodyEmitter 和 WebSocket 做比較。其實,這倆各有各的適用場景。WebSocket 適合那種需要雙向通信、實時性要求特別高的場景,比如在線游戲、實時協作編輯等。而 ResponseBodyEmitter 更適合那種服務器單向推送數據,客戶端主要是接收的場景,比如實時數據監控、消息通知等。而且,ResponseBodyEmitter 用起來比 WebSocket 簡單多了,不需要額外的協議支持,直接在 HTTP 協議上就能玩。

還有一個和 ResponseBodyEmitter 類似的東西,叫 SseEmitter,它是 ResponseBodyEmitter 的子類,專門用來支持 Server-Sent Events(SSE)。SSE 是一種 HTML5 規范,專門用于服務器向客戶端推送事件流。如果你的客戶端是瀏覽器,用 SseEmitter 可能更合適,因為瀏覽器原生支持 SSE 的接收。

SseEmitter 的用法和 ResponseBodyEmitter 差不多,就是 send 方法可以發送 SseEventBuilder 對象,能設置事件 ID、事件類型等信息:

@GetMapping("/sse/stream")
public SseEmitter sseStream() {
    SseEmitter emitter = new SseEmitter(30000L);

    new Thread(() -> {
        try {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(1000);
                // 構建一個SSE事件
                SseEmitter.SseEventBuilder event = SseEmitter.event()
                        .id(String.valueOf(i)) // 事件ID
                        .name("message") // 事件類型
                        .data("這是第" + (i + 1)次SSE推送的數據");
                emitter.send(event);
            }
            emitter.complete();
        } catch (Exception e) {
            emitter.completeWithError(e);
        }
    }).start();

    return emitter;
}

客戶端用瀏覽器原生的 EventSource 來接收:

const source = new EventSource('/sse/stream');

// 監聽message類型的事件
source.addEventListener('message', function(event) {
    console.log('收到SSE數據:', event.data);
});

// 監聽錯誤
source.onerror = function(error) {
    console.error('SSE錯誤:', error);
    source.close();
};

是不是也挺簡單的?所以,如果你的應用主要面向瀏覽器客戶端,SseEmitter 可能是個更好的選擇,畢竟是瀏覽器原生支持的。咱們再來說說 ResponseBodyEmitter 在實際項目中可能遇到的一些坑。

第一個坑就是超時問題。如果推送數據的時間間隔比較長,超過了設置的超時時間,Emitter 就會自動關閉。這時候,要么把超時時間設置長一點,要么在超時前發送一個心跳消息,重置超時計時器。

第二個坑是內存泄漏。如果 Emitter 關閉后沒有從管理器中移除,就會造成內存泄漏。所以,一定要在 Emitter 的 onCompletion 和 onError 回調里,把它從 Map 中刪掉,就像咱們在 EmitterManager 里做的那樣。

第三個坑是數據亂序。因為 send () 方法可以在多個線程中調用,如果多個線程同時發送數據,客戶端收到的數據可能是亂序的。如果你的業務要求數據必須有序,那就要自己想辦法保證,比如給數據加個序號,客戶端收到后再排序。

第四個坑是大數據量推送。如果一次性推送的數據量特別大,可能會導致客戶端處理不過來,或者服務器內存占用過高。這時候,可以把大數據拆分成小塊,分多次推送,客戶端收到后再拼接起來。

除了這些坑,還有一些性能優化的點可以說說。

首先,Emitter 的數量不能太多。每個 Emitter 都會占用一個服務器連接,如果并發用戶特別多,Emitter 數量就會很多,會消耗大量的服務器資源。這時候,可能需要考慮其他方案,比如結合消息隊列,或者用 WebSocket 的廣播功能。

其次,推送的數據要盡量精簡。別把沒用的字段都往客戶端推,不僅浪費帶寬,還增加了序列化和反序列化的開銷。

再者,可以考慮使用壓縮。如果推送的文本數據比較多,可以開啟 Gzip 壓縮,能大大減少數據傳輸量。在 SpringBoot 里,開啟 Gzip 壓縮很簡單,在 application.properties 里配置一下就行:

server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/plain

最后,要做好監控和告警。比如監控 Emitter 的數量、發送數據的頻率、連接超時的次數等,一旦出現異常,及時告警,方便排查問題。咱們再來舉個實際點的例子,看看 ResponseBodyEmitter 在項目中具體能怎么用。

假設咱們要做一個實時日志查看功能,用戶在網頁上可以實時看到服務器的日志輸出。這時候,就可以用 ResponseBodyEmitter 來實現。

服務器端可以這樣搞:

@RestController
publicclass LogController {

    @Autowired
    private EmitterManager emitterManager;

    @GetMapping("/logs/stream/{userId}")
    public ResponseBodyEmitter logStream(@PathVariableString userId) {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter(60000L);
        emitterManager.addEmitter(userId, emitter);

        // 啟動一個線程,實時讀取日志文件,并推送給客戶端
        new Thread(() -> {
            try {
                // 這里只是示例,實際中可以用FileTailer之類的工具實時讀取日志
                BufferedReader reader = new BufferedReader(new FileReader("application.log"));
                String line;
                while ((line = reader.readLine()) != null) {
                    // 如果Emitter已經關閉,就退出循環
                    if (emitter.isCompleted() || emitter.isClosed()) {
                        break;
                    }
                    emitter.send(line);
                    // 稍微休眠一下,避免推送太快
                    Thread.sleep(100);
                }
                reader.close();
                emitter.complete();
            } catch (Exception e) {
                emitter.completeWithError(e);
            }
        }).start();

        return emitter;
    }
}

客戶端頁面上,就可以用 JavaScript 實時接收日志,并顯示在頁面上:

<div id="logContainer" style="white-space: pre;"></div>

<script>
function connectLogStream(userId) {
    fetch(`/logs/stream/${userId}`)
        .then(response => {
            const reader = response.body.getReader();
            const decoder = new TextDecoder();
            const logContainer = document.getElementById('logContainer');

            function read() {
                return reader.read().then(({ done, value }) => {
                    if (done) {
                        console.log('日志流結束,重新連接...');
                        setTimeout(() => connectLogStream(userId), 1000);
                        return;
                    }
                    const line = decoder.decode(value, { stream: true });
                    // 把新的日志行添加到頁面上
                    logContainer.textContent += line + '\n';
                    // 自動滾動到底部
                    logContainer.scrollTop = logContainer.scrollHeight;
                    return read();
                });
            }

            return read();
        })
        .catch(error => {
            console.error('日志流出錯,重新連接...', error);
            setTimeout(() => connectLogStream(userId), 1000);
        });
}

// 假設當前用戶ID是123
connectLogStream('123');
</script>

這樣,用戶打開頁面后,就能實時看到服務器日志不斷地在頁面上刷新,就像在服務器上用 tail -f 命令看日志一樣,體驗還是挺不錯的。再比如,在一個數據導入的場景中,用戶上傳一個大文件,服務器在后臺異步處理,同時可以通過 ResponseBodyEmitter 實時向客戶端推送處理進度,比如 “已處理 10%”、“已處理 50%”、“處理完成” 等。客戶端收到這些進度信息后,就可以在頁面上顯示一個進度條,讓用戶知道當前的處理狀態,不至于以為頁面卡了。

說了這么多,總結一下 ResponseBodyEmitter 的優點:

  1. 用法簡單,集成在 SpringBoot 里,不用引入額外的依賴。
  2. 基于 HTTP 協議,兼容性好,客戶端不用做太多特殊處理。
  3. 支持異步流式推送,能提升用戶體驗。
  4. 線程安全,多個線程可以同時發送數據。

當然,它也有一些局限性:

  1. 主要是服務器單向推送,客戶端不能通過這個連接向服務器發送數據(除非再發一個 HTTP 請求)。
  2. 并發量大的時候,會占用較多的服務器連接資源。
  3. 依賴 HTTP 長連接,可能會被代理服務器、防火墻等中途斷開。

所以,在選擇技術方案的時候,要根據實際的業務場景來決定。如果是簡單的實時推送場景,ResponseBodyEmitter 是個不錯的選擇,足夠優雅,也足夠好用。如果是復雜的雙向實時通信場景,可能還是 WebSocket 更合適。

最后,再給大家提幾個在實際項目中使用 ResponseBodyEmitter 的小建議:

  1. 做好異常處理。無論是服務器發送數據出錯,還是客戶端連接斷開,都要妥善處理,避免程序崩潰,或者出現資源泄漏。
  2. 合理設置超時時間。根據業務的實際情況,設置一個合適的超時時間,既保證連接的穩定性,又不會浪費服務器資源。
  3. 考慮斷線重連機制。客戶端和服務器都要做好斷線重連的準備,保證推送的連續性。
  4. 進行充分的測試。特別是在高并發場景下,要測試 Emitter 的性能和穩定性,看看它能不能扛住壓力。
  5. 不要過度使用。不是所有場景都需要流式推送,簡單的 “一問一答” 模式能解決的問題,就別用這么復雜的東西。

總的來說,SpringBoot 的 ResponseBodyEmitter 是一個非常實用的工具,用好了能給咱們的項目帶來很大的便利,提升用戶體驗。它不像有些技術那樣晦澀難懂,只要理解了它的基本原理和用法,上手還是挺容易的。


責任編輯:武曉燕 來源: 石杉的架構筆記
相關推薦

2025-03-10 00:00:11

Spring框架數據

2025-07-22 02:10:00

2025-03-10 08:44:17

2018-09-18 16:20:08

Asyncjavascript前端

2025-03-26 00:00:00

Spring服務器推送技術

2025-06-26 04:10:00

2024-08-06 09:43:54

Java 8工具編程

2025-09-08 03:15:00

JavaScript數據流接口

2011-12-30 13:50:21

流式計算Hadoop

2024-12-26 07:47:05

Spring管理配置

2021-11-10 10:03:18

SpringBootJava代碼

2023-06-28 08:25:14

事務SQL語句

2023-01-30 07:41:43

2013-04-27 10:32:51

大數據全球技術峰會大數據流計算

2017-09-05 15:30:00

JavascriptSocket.ioNode.js

2025-03-17 00:00:00

2024-05-29 08:12:55

接口參數格式

2024-10-18 08:53:49

SpringMybatis微服務

2024-03-18 14:06:00

停機Spring服務器

2025-07-10 07:24:54

Spring支付策略類
點贊
收藏

51CTO技術棧公眾號

色综合www| 国产羞羞视频在线播放| 麻豆成人在线观看| 欧美国产精品va在线观看| 黄色国产在线观看| 日韩一级特黄| 精品女厕一区二区三区| 日韩一区国产在线观看| 国产精品热久久| 一本久久综合| 毛片精品免费在线观看| 91精彩刺激对白露脸偷拍| 久久久久毛片免费观看| 欧美性xxxxx极品娇小| 欧美精品一区二区性色a+v| 视频在线不卡| 三级小视频在线观看| 一区二区三区自拍视频| 日本韩国精品在线| 800av在线免费观看| 成人好色电影| 99久久免费视频.com| 韩国成人免费视频| 66视频精品| 亚洲电影免费观看高清| 激情五月俺来也| 亚洲啊v在线| 亚洲欧美日本韩国| 色一情一乱一伦一区二区三欧美 | 国内成人免费视频| 日本欧美在线视频| 国产午夜小视频| 久久一本综合| 亚洲视频999| 在线免费观看污视频| 日韩成人在线观看视频| 777奇米成人网| 中文字幕网av| 福利一区和二区| 91国产精品成人| 亚洲国产精品久久久久爰色欲| 中文字幕资源网在线观看| 国产精品久久久久久久久动漫 | 黄色精品一区| 九九精品在线观看| 欧美成人免费看| 午夜久久久久| 久久久久久91| 日本熟妇乱子伦xxxx| 在线观看的日韩av| 久久频这里精品99香蕉| 国产精品变态另类虐交| 亚洲国产91| 欧美精品少妇videofree| 欧美日韩午夜视频| 在线成人激情| 欧美国产中文字幕| 久久精品国产亚洲av无码娇色| 午夜精品偷拍| 午夜精品久久久久久久99黑人| 香蕉视频一区二区| 国产精品日韩精品欧美精品| 欧洲美女7788成人免费视频| 欧美人一级淫片a免费播放| 久久精品天堂| 国产精品三级网站| 97人妻精品一区二区三区视频 | 精品国产aⅴ一区二区三区东京热 久久久久99人妻一区二区三区 | 国产美女视频免费看| 亚洲高清国产拍精品26u| 欧美精品一二三四| 性折磨bdsm欧美激情另类| 草草视频在线一区二区| 国产视频精品在线| 快播亚洲色图| 国产尤物视频在线| 国产精品国产三级国产普通话99| 中文字幕av日韩精品| 婷婷色在线播放| 欧美日韩国产在线| 三级a在线观看| 精品一区二区三区中文字幕视频| 亚洲高清一区二| 微拍福利一区二区| 欧美在线亚洲综合一区| 欧美一区二区三区艳史| 在线观看免费视频a| 国产精品99久久久| 久久99欧美| jzzjzzjzz亚洲成熟少妇| 亚洲精品一二三| 欧美牲交a欧美牲交aⅴ免费下载| 亚洲一区二区av| 日韩av中文在线| 精品在线观看一区| 中文一区在线| 91在线网站视频| 少妇人妻一区二区| 国产精品三级电影| 青青草视频在线免费播放| 成人18视频在线观看| 日韩精品最新网址| 波多野在线播放| 亚洲激情自拍| 成人久久一区二区| 免费动漫网站在线观看| 国产精品久久久久久久久果冻传媒 | 国产模特精品视频久久久久| 成人国产在线激情| 免费福利在线观看| 亚洲国产精品综合小说图片区| 亚洲欧美激情网| 欧美偷窥清纯综合图区| 久热在线中文字幕色999舞| 无码人妻一区二区三区线| 国产999精品久久久久久绿帽| 亚洲乱码一区二区三区| 成人小电影网站| 亚洲精品一线二线三线| 日本中文字幕免费在线观看| 丝袜亚洲另类欧美综合| 精品国产乱码一区二区三区四区| 99在线播放| 精品视频一区二区三区免费| 少妇饥渴放荡91麻豆| 欧美日一区二区在线观看| 国产精品专区一| 成人在线二区| 91福利视频在线| 亚洲一级中文字幕| 国产一区二区精品| 国产一区免费| 丁香花在线电影小说观看| 日韩一区二区免费在线电影| 三上悠亚在线观看视频| 秋霞av亚洲一区二区三| 日本一区免费观看| 最新中文字幕在线播放| 亚洲美女久久久| 伦av综合一区| 国产视频视频一区| 久久久久国产精品熟女影院| 国产成人手机高清在线观看网站| 欧美在线观看网址综合| 日本福利在线观看| 色婷婷精品大在线视频| 亚洲第一成人网站| 久久婷婷一区| 神马影院我不卡午夜| 蜜桃视频成人m3u8| 中文字幕亚洲综合久久| 91丨九色丨丰满| 亚洲天堂2016| 国产免费a级片| 99国产成+人+综合+亚洲欧美| 国产视频在线观看一区| 综合久久2023| 这里只有精品丝袜| 97超碰人人草| 伊人性伊人情综合网| 任你躁av一区二区三区| 亚洲中午字幕| 亚洲狠狠婷婷综合久久久| 亚洲成人高清| 欧美另类在线观看| 人妻夜夜爽天天爽| 色婷婷一区二区三区四区| 91视频免费看片| 国产精品亚洲第一区在线暖暖韩国| 青青草国产免费| 亚洲另类av| 国产一区私人高清影院| 免费污视频在线观看| 日韩极品精品视频免费观看| 亚洲婷婷久久综合| 亚洲人成影院在线观看| 亚洲精品国产成人av在线| 久久久久免费| 日韩video| 校花撩起jk露出白色内裤国产精品| 国产精品免费观看在线| 日韩精品卡一| 亚洲香蕉成人av网站在线观看| 91丨九色丨蝌蚪丨对白| 亚洲国产一区二区a毛片| 干b视频在线观看| 国产一区二区三区香蕉| 久久免费视频3| 久久精品久久久| 国内一区二区在线视频观看| 国产精品久久久久77777丨| 欧美成人精品一区| 国产在线视频资源| 精品久久久久久久久久久久久久久 | 亚洲一区二区在线免费| 日韩va亚洲va欧美va久久| 国产激情在线看| 精品久久一区| 国产亚洲一区在线播放| 午夜精品久久久久久毛片| 2019中文字幕在线| 影音先锋中文在线视频| 一区二区三区高清国产| 天堂成人在线| 精品奇米国产一区二区三区| 中文字幕久久久久| 精品国产91久久久| 青青草原免费观看| 国产精品久99| 我和岳m愉情xxxⅹ视频| 丁香激情综合五月| 国产精品久久久久久9999| 久久天天综合| www.爱色av.com| 亚洲美女毛片| 国产91沈先生在线播放| 91精品一区二区三区综合| 亚洲ai欧洲av| 综合伊思人在钱三区| 国产精品久久久久久久天堂第1集| 999久久久国产999久久久| 99电影在线观看| 欧美性受xxxx狂喷水| 欧美高清一级片在线| 超碰超碰超碰超碰| 一区二区免费看| 丰满的亚洲女人毛茸茸| 久久美女高清视频| xxxx黄色片| 91在线小视频| 人妻体内射精一区二区三区| 国产精品77777竹菊影视小说| 香蕉视频999| 日本在线不卡视频| 国产精品亚洲二区在线观看| 超碰国产在线观看| 欧美日韩成人| 一本一本a久久| 日韩国产一区二区| 亚洲激情一区二区| 欧美在线观看视频一区| 日韩免费电影一区二区三区| 国产欧美日韩影院| 欧美亚洲一级二级| 久久99国产精一区二区三区| 乱色588欧美| 一道本一区二区三区| 欧美精品二区三区四区免费看视频| 国产福利资源一区| 国产一区福利视频| 亚洲97av| 日韩福利视频| 日本女优一区| 中文字幕日韩一区二区三区 | 日韩精品一区二区三区在线 | 成人av网站免费观看| 性猛交╳xxx乱大交| 成人黄色国产精品网站大全在线免费观看 | 黄网站色欧美视频| 91精品国产高潮对白| 精品国产老师黑色丝袜高跟鞋| 国产福利片一区二区| 91欧美精品| 国产精品久久久久久av福利软件| 欧美影视资讯| 国产精品视频公开费视频| 日韩欧美1区| 日韩精品国内| 青青草综合网| 夜夜爽99久久国产综合精品女不卡 | 丝袜美腿av在线| 色偷偷av一区二区三区乱| 18+视频在线观看| 韩国视频理论视频久久| 神马久久资源| 成人女保姆的销魂服务| 极品一区美女高清| 亚洲成人蜜桃| 欧美日韩ab| 国产免费人做人爱午夜视频| 国产真实乱对白精彩久久| 波多野结衣加勒比| crdy在线观看欧美| 国产精品欧美风情| 玖玖玖视频精品| 久久综合九色欧美狠狠| 97人人精品| 成人一级片网站| 国产麻豆一精品一av一免费| 狠狠人妻久久久久久综合蜜桃| 国产精品传媒在线| 日韩av大片在线观看| 欧美久久一区二区| 三级黄视频在线观看| 久久久999精品视频| 婷婷六月国产精品久久不卡| 亚洲xxxxx性| 欧美美女视频| 国产成人在线小视频| 蜜乳av一区二区| 国产精品嫩草av| 亚洲精品日产精品乱码不卡| 波多野结衣绝顶大高潮| 亚洲第一区在线| 求av网址在线观看| 国产精品91久久久久久| 麻豆成人入口| 久久这里只有精品8| 久久亚洲电影| 日本护士做爰视频| 亚洲精品国产一区二区精华液| 日本三级一区二区三区| 日韩精品极品毛片系列视频| 手机在线免费av| 亚洲aⅴ男人的天堂在线观看| 欧美三级三级| 妺妺窝人体色www在线小说| 高清在线成人网| 亚洲天堂黄色片| 欧美日韩国产不卡| 精品av中文字幕在线毛片| 26uuu亚洲国产精品| 国产厕拍一区| a级免费在线观看| 国产精品伊人色| 日本一二三区在线观看| 欧美老肥妇做.爰bbww| 91caoporm在线视频| 国产精品入口日韩视频大尺度| 精品久久成人| 成人在线免费播放视频| 久久久亚洲高清| 国产精品人人人人| 亚洲国产一区自拍| 美女av在线免费看| 亚洲性日韩精品一区二区| 变态调教一区二区三区| 国产91色在线|亚洲| 影音先锋久久资源网| 男人女人拔萝卜视频| 一区二区三区欧美亚洲| 性一交一乱一透一a级| 欧美精品在线看| 99这里只有精品视频| av网站大全免费| 99精品欧美一区二区蜜桃免费| 日韩欧美三级视频| 亚洲女成人图区| 91精品国产66| 国产91av视频在线观看| 国产麻豆成人传媒免费观看| 欧美黄色免费在线观看| 亚洲成人av资源网| 92国产精品| 亚洲国产精品一区二区第四页av| 免费高清成人在线| 99自拍视频在线| 欧美va亚洲va| 天天综合av| 亚洲欧美日韩国产yyy| 韩国一区二区在线观看| 久草视频免费在线| 精品在线小视频| 成人黄色在线| 久久免费一级片| 91日韩一区二区三区| 国产成人精品一区二区色戒| 草民午夜欧美限制a级福利片| 国产 日韩 欧美 综合 一区| 亚洲爆乳无码专区| 中文字幕亚洲一区二区va在线| www.色亚洲| 秋霞av国产精品一区| 婷婷激情综合| v天堂中文在线| 欧美日韩一区二区电影| 欧美1234区| 日本视频一区在线观看| 国产一区二三区好的| www.国产高清| 日韩资源在线观看| 精品丝袜久久| 老司机久久精品| 五月开心婷婷久久| 1769在线观看| 九九九九九九精品| 久久精品国产99国产| 五月婷婷激情网| 久久精品欧美视频| 亚洲涩涩av| 日韩精品国产一区| 欧美亚洲国产一区二区三区va | 91国产精品电影| 99久久夜色精品国产亚洲96| 日本69式三人交| 欧美日韩国产精品成人| 国产一二三在线| 国产精品一区在线免费观看| 久久久99免费| 欧美一级一区二区三区|