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

Spring Boot 中 MQTT 的高級用法:從可靠性到精細化管控

開發 前端
MQTT的核心優勢之一是通過QoS(Quality of Service)級別保障消息可靠性,但不同QoS的實現邏輯與資源消耗差異顯著。在項目中,需結合業務場景選擇合適的QoS,并處理好消息重發、重復消息去重等問題。

引言

在基礎的MQTT集成(發布/訂閱)之上,實際生產場景往往對消息可靠性、連接穩定性、權限安全性、消息處理效率有更高要求。

本文將圍繞Spring Boot環境,深入講解MQTT的高級用法,包括QoS級別實戰、遺囑消息、持久化配置、主題過濾與權限控制、消息積壓處理等。

一、消息可靠性進階:QoS 級別實戰與消息重發機制

MQTT的核心優勢之一是通過QoS(Quality of Service)級別保障消息可靠性,但不同QoS的實現邏輯與資源消耗差異顯著。在項目中,需結合業務場景選擇合適的QoS,并處理好消息重發、重復消息去重等問題。

1.1 QoS 級別深度解析(對比與場景選型)

MQTT定義3級QoS,需根據 “數據重要性”“網絡穩定性”“資源成本” 綜合選型:

QoS 級別

核心邏輯

適用場景

資源消耗

QoS 0(最多一次)

客戶端發送消息后不等待確認,消息可能丟失或重復

非關鍵數據(如設備心跳、實時日志)

最低(無重發、無確認)

QoS 1(至少一次)

客戶端發送消息后等待服務端確認(PUBACK),未確認則重發;消息可能重復

關鍵數據但允許重復處理(如傳感器數據、訂單狀態通知)

中等(需存儲消息、處理重發)

QoS 2(恰好一次)

客戶端與服務端通過“四步握手”(PUBLISH→PUBREC→PUBREL→PUBCOMP)確保消息僅送達一次

核心數據(如支付指令、設備控制指令)

最高(需雙向確認、存儲消息狀態)

1.2 Spring Boot 中 QoS 2 的實現與重復消息處理

QoS 2雖能保障恰好一次,但服務端或客戶端異常時仍可能出現重復消息(如網絡延遲導致的重發)。需在項目中通過消息ID去重機制解決。

(1)QoS 2 的發布與訂閱配置

// 1. 發布端:使用QoS 2發布消息(核心是設置setQos(2))
@Service
public class HighReliabilityPublisher {
    @Resource
    private MqttClient mqttClient;
    // 存儲已處理的消息ID(用于去重,key:消息ID,value:處理時間)
    private final ConcurrentHashMap<Integer, Long> processedMsgIds = new ConcurrentHashMap<>();
    // 消息ID過期時間(如5分鐘,避免內存溢出)
    private static final long MSG_ID_EXPIRE_TIME = 5 * 60 * 1000;

    public void publishWithQos2(String topic, String payload) throws MqttException {
        if (!mqttClient.isConnected()) {
            mqttClient.reconnect();
        }
        MqttMessage message = new MqttMessage(payload.getBytes());
        message.setQos(2); // 關鍵:設置QoS 2
        // 發布消息并獲取消息ID(用于后續去重)
        IMqttDeliveryToken token = mqttClient.publish(topic, message);
        token.waitForCompletion(3000); // 等待發布完成(超時3秒)
        System.out.println("QoS 2消息發布成功,消息ID:" + token.getMessageId());
    }

    // 2. 訂閱端:處理QoS 2消息并去重
    @PostConstruct
    public void subscribeWithQos2() throws MqttException {
        mqttClient.setCallback(new MqttCallbackExtended() { // 用MqttCallbackExtended增強回調
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                // 連接完成回調(重連后可重新訂閱)
                try {
                    mqttClient.subscribe("critical/control/#", 2); // 訂閱QoS 2
                } catch (MqttException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                int msgId = message.getId(); // 獲取消息ID(QoS 1/2才有)
                // 1. 先清理過期的消息ID(避免內存泄漏)
                long currentTime = System.currentTimeMillis();
                processedMsgIds.entrySet().removeIf(entry -> currentTime - entry.getValue() > MSG_ID_EXPIRE_TIME);
                
                // 2. 檢查消息ID是否已處理(去重)
                if (processedMsgIds.containsKey(msgId)) {
                    System.out.println("重復消息,已忽略,消息ID:" + msgId);
                    return;
                }

                // 3. 處理消息(如設備控制指令)
                String payload = new String(message.getPayload());
                System.out.println("處理QoS 2消息,ID:" + msgId + ",內容:" + payload);
                // 模擬處理邏輯(如控制工業設備停機)
                processControlCommand(payload);

                // 4. 標記消息ID為已處理
                processedMsgIds.put(msgId, currentTime);
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {}

            @Override
            public void connectionLost(Throwable cause) {}
        });
    }

    private void processControlCommand(String payload) {
        // 實際業務邏輯:如解析指令、調用設備API
    }
}

(2)關鍵注意點

  • 消息ID的作用:QoS 1/2的消息會攜帶唯一ID(message.getId()),是去重的核心依據;QoS 0無消息ID,無法去重。
  • 內存與過期策略:使用ConcurrentHashMap存儲已處理的消息ID,并定期清理過期數據(如5分鐘),避免內存溢出。
  • 回調選擇:使用MqttCallbackExtended替代基礎的MqttCallback,新增connectComplete回調,支持重連后自動重新訂閱。

二、連接穩定性增強:遺囑消息、自動重連與斷線恢復

在物聯網、工業監控等場景中,設備或客戶端可能因網絡波動斷開連接。需通過遺囑消息(Last Will and Testament) 、自動重連、斷線后消息恢復等機制,保障系統穩定性。

2.1 遺囑消息:異常斷開的 “狀態通知”

當客戶端異常斷開(如斷電、網絡中斷),服務端會自動向預設的遺囑主題發布消息,通知其他訂閱者該客戶端的離線狀態。

(1)Spring Boot 中配置遺囑消息

@Configuration
public class MqttAdvancedConfig {
    @Value("${spring.mqtt.broker-url}")
    private String brokerUrl;
    @Value("${spring.mqtt.client-id}")
    private String clientId;

    @Bean
    public MqttClient mqttClient() throws MqttException {
        MqttConnectOptions options = new MqttConnectOptions();
        options.setUserName("admin");
        options.setPassword("123456".toCharArray());
        options.setAutomaticReconnect(true); // 開啟自動重連
        options.setConnectionTimeout(3000);
        options.setKeepAliveInterval(60);

        // 核心:配置遺囑消息
        String willTopic = "client/status/" + clientId; // 遺囑主題(包含客戶端ID,便于識別)
        String willPayload = "{\"clientId\":\"" + clientId + "\",\"status\":\"offline\"}"; // 遺囑內容(離線狀態)
        options.setWill(willTopic, willPayload.getBytes(), 1, false); // QoS 1,不保留消息

        // 禁用Clean Session(關鍵:確保斷線后重連能恢復訂閱和未處理消息)
        options.setCleanSession(false);

        MqttClient client = new MqttClient(brokerUrl, clientId, new MemoryPersistence());
        client.setCallback(new MqttCallbackExtended() {
            @Override
            public void connectComplete(boolean reconnect, String serverURI) {
                if (reconnect) {
                    System.out.println("客戶端重連成功,服務器地址:" + serverURI);
                    // 重連后發布“在線”狀態(與遺囑消息的“離線”對應)
                    try {
                        String onlinePayload = "{\"clientId\":\"" + clientId + "\",\"status\":\"online\"}";
                        client.publish(willTopic, onlinePayload.getBytes(), 1, false);
                    } catch (MqttException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println("客戶端首次連接成功");
                }
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                // 處理消息
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {}

            @Override
            public void connectionLost(Throwable cause) {
                System.out.println("客戶端連接丟失:" + cause.getMessage());
            }
        });

        client.connect(options);
        // 首次連接成功后發布“在線”狀態
        String onlinePayload = "{\"clientId\":\"" + clientId + "\",\"status\":\"online\"}";
        client.publish(willTopic, onlinePayload.getBytes(), 1, false);
        return client;
    }
}

(2)遺囑消息的核心配置參數

  • setWill(topic, payload, qos, retained):
  • topic:遺囑主題(需明確,如client/status/device-001);
  • payload:遺囑內容(JSON格式便于解析,包含客戶端ID、狀態等);
  • qos:遺囑消息的QoS級別(建議1,確保通知可靠);
  • retained:是否保留遺囑消息(建議false,避免新訂閱者誤判狀態)。
  • setCleanSession(false):禁用 “清除會話”,服務端會保留客戶端的訂閱關系和未處理的QoS 1/2消息,重連后可恢復。

2.2 自動重連與斷線恢復

通過MqttConnectOptions.setAutomaticReconnect(true)開啟自動重連,但需注意:

  • 重連觸發條件:網絡恢復、服務端重啟后,客戶端會自動嘗試重連(默認重試間隔遞增,如 1 秒、2 秒、4 秒...);
  • 重連后的操作:在connectComplete回調中處理重連后的邏輯(如重新發布在線狀態、重新訂閱動態主題);
  • 消息恢復:僅當setCleanSession(false)且QoS≥1時,重連后服務端會補發客戶端斷線期間未確認的消息。

三、主題精細化管控:通配符、動態訂閱與權限控制

MQTT 的主題支持通配符,結合Spring Boot的權限管理,可實現 “按角色訂閱主題”“動態創建主題” 等精細化管控需求。

3.1 主題通配符實戰(單層 / 多層匹配)

MQTT支持兩種通配符,需在訂閱時合理使用:

  • +:單層通配符(匹配一個層級,如home/+/status可匹配home/lighting/status、home/aircon/status);
  • #:多層通配符(匹配所有子層級,需放在最后,如home/#可匹配home/lighting/status、home/aircon/control)。

(1)Spring Boot 中使用通配符訂閱

@Service
public class TopicWildcardService {
    @Resource
    private MqttClient mqttClient;

    // 訂閱“家庭所有設備的狀態”(多層通配符#)
    public void subscribeHomeAllStatus() throws MqttException {
        String topic = "home/#";
        mqttClient.subscribe(topic, 1, (topic1, message) -> {
            // 根據不同子主題處理消息
            if (topic1.startsWith("home/lighting/")) {
                processLightingMessage(new String(message.getPayload()));
            } elseif (topic1.startsWith("home/aircon/")) {
                processAirconMessage(new String(message.getPayload()));
            }
        });
        System.out.println("已訂閱家庭所有設備主題:" + topic);
    }

    // 訂閱“所有房間的燈光狀態”(單層通配符+)
    public void subscribeAllRoomLightingStatus() throws MqttException {
        String topic = "home/+/lighting/status"; // 匹配home/bedroom/lighting/status、home/living/lighting/status
        mqttClient.subscribe(topic, 1, (topic1, message) -> {
            // 解析房間名稱(從主題中提取,如“bedroom”)
            String room = topic1.split("/")[1];
            String payload = new String(message.getPayload());
            System.out.println("房間【" + room + "】燈光狀態:" + payload);
        });
        System.out.println("已訂閱所有房間燈光狀態主題:" + topic);
    }

    private void processLightingMessage(String payload) {}
    private void processAirconMessage(String payload) {}
}

3.2 動態訂閱與主題權限控制

在多租戶、多角色系統中,需根據用戶角色動態分配訂閱權限(如 “管理員可訂閱所有設備,普通用戶僅訂閱自己的設備”)。

(1)基于 Spring Security 的主題權限控制

@Service
public class DynamicSubscriptionService {
    @Resource
    private MqttClient mqttClient;
    @Resource
    private UserDetailsService userDetailsService;

    // 根據用戶角色動態訂閱主題
    public void subscribeByUserRole(String username) throws MqttException {
        // 1. 獲取用戶角色(如ADMIN、USER)
        UserDetails user = userDetailsService.loadUserByUsername(username);
        Set<String> roles = user.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toSet());

        // 2. 根據角色訂閱不同主題
        if (roles.contains("ROLE_ADMIN")) {
            // 管理員:訂閱所有設備主題
            mqttClient.subscribe("device/#", 1);
            System.out.println("管理員【" + username + "】已訂閱所有設備主題");
        } elseif (roles.contains("ROLE_USER")) {
            // 普通用戶:僅訂閱自己的設備(主題包含用戶名,如“device/user123/+/status”)
            String userTopic = "device/" + username + "/+/status";
            mqttClient.subscribe(userTopic, 1);
            System.out.println("用戶【" + username + "】已訂閱個人設備主題:" + userTopic);
        }
    }

    // 動態發布主題(僅允許發布自己的設備控制指令)
    public void publishByUserRole(String username, String deviceId, String command) throws MqttException {
        // 校驗權限:僅允許發布自己設備的控制主題
        String allowedTopic = "device/" + username + "/" + deviceId + "/control";
        MqttMessage message = new MqttMessage(command.getBytes());
        message.setQos(2); // 控制指令用QoS 2確保可靠
        mqttClient.publish(allowedTopic, message);
        System.out.println("用戶【" + username + "】向設備【" + deviceId + "】發送指令:" + command);
    }
}

(2)服務端權限配合(以 Mosquitto 為例)

客戶端權限控制需服務端配合,如Mosquitto可通過acl_file配置主題訪問權限:

# Mosquitto的acl.conf配置
user admin
topic write device/#
topic read device/#

user user123
topic write device/user123/+/control
topic read device/user123/+/status

客戶端需在MqttConnectOptions中設置用戶名/密碼,服務端驗證通過后才允許訂閱/發布。

四、消息處理優化:異步消費、積壓處理與批量發布

當消息量較大(如每秒數百條)時,需通過異步消費、消息積壓監控、批量發布等機制優化處理效率,避免客戶端阻塞。

4.1 異步消費消息(線程池隔離)

基礎的messageArrived回調運行在 MQTT 客戶端的線程中,若處理邏輯耗時(如數據庫寫入、API調用),會導致消息堆積。需通過線程池異步處理消息。

@Service
public class AsyncMessageHandler {
    // 1. 定義線程池(核心參數需結合業務場景調優)
    private final ExecutorService messageExecutor;

    // 構造方法:初始化線程池(避免硬編碼,支持參數化配置)
    public AsyncMessageHandler() {
        // 線程工廠:設置線程名稱,便于日志排查
        ThreadFactory threadFactory = new ThreadFactoryBuilder()
                .setNameFormat("mqtt-message-handler-%d") // 線程名格式:mqtt-message-handler-1、2...
                .setDaemon(true) // 設為守護線程:JVM退出時自動關閉,避免阻塞應用 shutdown
                .build();

        // 線程池核心參數說明:
        // corePoolSize:核心線程數(默認活躍的線程數,即使空閑也不銷毀)
        // maximumPoolSize:最大線程數(核心線程不夠時,最多再創建的線程數)
        // keepAliveTime:非核心線程空閑超時時間(超時后銷毀)
        // workQueue:任務隊列(核心線程滿時,任務暫存的隊列)
        // handler:任務拒絕策略(隊列滿+最大線程數滿時,如何處理新任務)
        this.messageExecutor = new ThreadPoolExecutor(
                8, // corePoolSize:根據CPU核心數/消息量調整(如8核CPU設為8)
                16, // maximumPoolSize:核心線程的2倍,避免線程過多導致上下文切換
                30, // keepAliveTime:30秒(非核心線程空閑30秒后銷毀)
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(2048), // 有界隊列:避免無界隊列導致內存溢出(容量設為2048)
                threadFactory,
                new ThreadPoolExecutor.CallerRunsPolicy() // 拒絕策略:調用者(MQTT客戶端線程)自己執行任務,避免消息丟失
        );
    }

    @Autowired
    private MqttClient mqttClient;

    @Autowired
    private JdbcTemplate jdbcTemplate; // 模擬耗時操作:數據庫寫入

    // 2. 初始化MQTT回調,綁定異步消費邏輯
    @PostConstruct
    public void initAsyncHandler() throws MqttException {
        // 訂閱需要異步處理的主題(如傳感器數據主題,消息量較大)
        String targetTopic = "sensor/data/#";
        mqttClient.subscribe(targetTopic, 1, (topic, message) -> {
            // 3. 將消息處理邏輯提交到線程池異步執行
            submitAsyncTask(topic, message);
        });
        System.out.println("已訂閱主題【" + targetTopic + "】,啟用異步消費(線程池隔離)");
    }

    // 封裝異步任務提交邏輯(含異常捕獲)
    private void submitAsyncTask(String topic, MqttMessage message) {
        try {
            // 提交任務到線程池
            messageExecutor.submit(() -> {
                try {
                    // 4. 實際消息處理邏輯(如解析數據、寫入數據庫)
                    processMessage(topic, message);
                } catch (Exception e) {
                    // 5. 異常捕獲:避免單個任務異常導致線程池線程銷毀
                    System.err.println("異步處理消息失敗,主題:" + topic + ",異常信息:" + e.getMessage());
                    // 可選:失敗重試(需限制重試次數,避免死循環)
                    retryFailedTask(topic, message, 3); // 最多重試3次
                }
            });
        } catch (RejectedExecutionException e) {
            // 6. 捕獲任務拒絕異常(隊列滿+最大線程數滿時觸發)
            System.err.println("線程池任務隊列已滿,無法接收新消息,主題:" + topic + ",異常信息:" + e.getMessage());
            // 可選:將消息存入本地磁盤/Redis,后續重試(避免消息丟失)
            saveFailedMessageToLocal(topic, message);
        }
    }

    // 實際消息處理邏輯(模擬耗時操作:解析+數據庫寫入)
    private void processMessage(String topic, MqttMessage message) throws Exception {
        // 步驟1:解析消息(如傳感器ID、采集時間、數值)
        String payload = new String(message.getPayload(), "UTF-8");
        String[] topicParts = topic.split("/");
        String sensorId = topicParts[2]; // 從主題提取傳感器ID(如主題“sensor/data/sensor-001”)
        long timestamp = System.currentTimeMillis(); // 模擬采集時間

        // 步驟2:耗時操作:寫入數據庫(模擬200ms耗時)
        String sql = "INSERT INTO sensor_data (sensor_id, payload, collect_time, create_time) " +
                     "VALUES (?, ?, ?, NOW())";
        jdbcTemplate.update(sql, sensorId, payload, new java.sql.Timestamp(timestamp));

        // 日志記錄(可選:通過MDC添加追蹤ID,便于鏈路排查)
        System.out.println("異步處理完成,傳感器ID:" + sensorId + ",消息內容:" + payload + ",線程名:" + Thread.currentThread().getName());
    }

    // 失敗重試邏輯(限制重試次數)
    private void retryFailedTask(String topic, MqttMessage message, int remainingRetries) {
        if (remainingRetries <= 0) {
            System.err.println("消息重試次數耗盡,主題:" + topic + ",內容:" + new String(message.getPayload()));
            // 重試耗盡:存入失敗隊列,人工排查
            saveFailedMessageToLocal(topic, message);
            return;
        }

        try {
            // 重試間隔:指數退避(1s、2s、4s...),避免頻繁重試壓垮服務
            long retryDelay = (long) Math.pow(2, 3 - remainingRetries) * 1000;
            Thread.sleep(retryDelay);

            // 重新提交任務
            messageExecutor.submit(() -> {
                try {
                    processMessage(topic, message);
                    System.out.println("消息重試成功,剩余次數:" + (remainingRetries - 1) + ",主題:" + topic);
                } catch (Exception e) {
                    System.err.println("消息重試失敗,剩余次數:" + (remainingRetries - 1) + ",主題:" + topic);
                    retryFailedTask(topic, message, remainingRetries - 1); // 遞歸重試
                }
            });
        } catch (InterruptedException | RejectedExecutionException e) {
            System.err.println("重試任務提交失敗,剩余次數:" + (remainingRetries - 1) + ",主題:" + topic);
            retryFailedTask(topic, message, remainingRetries - 1);
        }
    }

    // 保存失敗消息到本地(模擬:實際可存入Redis/磁盤文件)
    private void saveFailedMessageToLocal(String topic, MqttMessage message) {
        // 簡化實現:實際需序列化消息,存入持久化存儲(如Redis的list)
        String failedMsg = "topic:" + topic + ", payload:" + new String(message.getPayload()) + ", timestamp:" + System.currentTimeMillis();
        System.out.println("保存失敗消息到本地:" + failedMsg);
        // 示例:jdbcTemplate.update("INSERT INTO mqtt_failed_msg (content, create_time) VALUES (?, NOW())", failedMsg);
    }

    // 7. 應用關閉時,優雅關閉線程池(避免任務丟失)
    @PreDestroy
    public void shutdownExecutor() {
        System.out.println("開始關閉MQTT消息消費線程池...");
        messageExecutor.shutdown(); // 禁止接收新任務
        try {
            // 等待已提交的任務執行完成(最多等待60秒)
            if (!messageExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
                messageExecutor.shutdownNow(); // 超時未完成,強制關閉
                System.out.println("線程池強制關閉,可能存在未完成任務");
            } else {
                System.out.println("線程池優雅關閉完成");
            }
        } catch (InterruptedException e) {
            messageExecutor.shutdownNow();
            Thread.currentThread().interrupt(); // 恢復中斷狀態
        }
    }
}

責任編輯:武曉燕 來源: 一安未來
相關推薦

2011-03-18 14:38:25

云數據精細化流控

2009-07-21 10:08:42

綠色無線網絡網絡精細化建設

2010-09-01 11:32:07

無線網絡

2013-03-11 15:14:53

網絡虛擬化企業網絡運維IP網絡技術

2014-11-12 09:05:49

2013-05-14 13:36:01

華為校園網絡網絡架構

2010-12-28 19:50:21

可靠性產品可靠性

2023-07-05 08:36:24

2009-12-17 10:58:38

代理路由器設置步驟

2014-04-22 10:00:09

手游數據分析精細化運營

2017-11-02 14:23:04

易觀方舟數據分析

2014-05-09 10:03:09

上方花園

2009-01-04 16:50:54

2014-08-08 16:56:13

APP精細化運營

2011-06-09 17:43:57

佳能復印機

2009-12-03 09:43:54

2019-08-30 12:10:05

磁盤數據可靠性RAID

2020-12-06 14:51:23

物聯網可靠性IOT

2010-12-28 19:55:20

軟件架構可靠性

2017-10-26 13:02:11

大數據人民法院審判
點贊
收藏

51CTO技術棧公眾號

在线男人天堂| 日韩一区二区三区在线观看视频| 日韩在线观看一区| 91麻豆精品国产91久久久资源速度| 中文精品视频一区二区在线观看| 国产成人精品白浆久久69| 一区二区精品| 日韩在线观看免费av| 国产人妖在线观看| 高清不卡亚洲| 亚洲欧美日韩久久精品| 国产chinese精品一区二区| 在线天堂中文字幕| 亚洲高清资源在线观看| 日韩精品在线视频观看| 免费网站在线观看黄| 亚洲人体视频| 亚洲激情六月丁香| 色狠狠久久av五月综合| 韩国av在线免费观看| 青草国产精品久久久久久| 欧美国产日韩精品| 成人精品一二三区| 欧美一性一交| 日韩一级免费观看| 九热视频在线观看| 日韩在线伦理| 一区二区三区国产| 在线观看精品视频| 男人天堂亚洲二区| 成人毛片视频在线观看| 成人国产精品一区二区| 性色av免费观看| 精品999网站| 久久精品91久久香蕉加勒比| 六月婷婷七月丁香| 欧美美女黄色| 精品国免费一区二区三区| 91丨九色丨蝌蚪| 精品视频一区二区三区四区五区| 亚洲一区二区综合| 强开小嫩苞一区二区三区网站| 大胆av不用播放器在线播放 | 香蕉视频黄在线观看| 国产在线精品一区二区不卡了| 国产精品国产自产拍高清av水多 | 欧美日韩精品一区二区三区蜜桃| 国产综合中文字幕| 成人在线高清免费| 亚洲国产人成综合网站| 国产91porn| 成人高清免费在线| 亚洲特级片在线| 自拍偷拍亚洲色图欧美| 免费在线观看av| 国产精品麻豆一区二区| 亚洲国产激情一区二区三区| 国产精品秘入口| 国产三级精品视频| 亚洲精品影院| 黄色免费在线看| 亚洲久本草在线中文字幕| 日韩精品福利片午夜免费观看| 国产精品剧情一区二区在线观看| 亚洲人成网站色在线观看| 日本xxx免费| 污污的视频在线观看| 亚洲影视在线播放| 久色视频在线播放| 欧美日韩123区| 欧美网站一区二区| 午夜宅男在线视频| 国产成人久久精品一区二区三区| 欧美一区二区日韩一区二区| 亚洲av午夜精品一区二区三区| 天堂久久av| 日韩av在线影院| 亚洲一区视频在线播放| 91超碰成人| 欧美精品电影免费在线观看| 国产精品黄色大片| 日本一区中文字幕| 亚洲在线免费视频| 手机看片一区二区| 国产欧美一区二区在线观看| 一区高清视频| 俺来也官网欧美久久精品| 午夜av电影一区| av五月天在线| 2023国产精华国产精品| 精品视频久久久久久久| 亚洲欧美另类日本| 极品av少妇一区二区| 欧洲中文字幕国产精品| 国产原创中文av| 成人av在线一区二区| 日韩av电影免费在线观看| 国产传媒在线播放| 精品久久久免费| 一起操在线视频| **爰片久久毛片| 国产一区二区三区在线看| 波多野结衣爱爱视频| 亚洲一区欧美二区| 97中文在线观看| 成人在线观看免费| 婷婷久久综合九色国产成人 | 五月婷婷中文字幕| 久久99日本精品| 久久精品国产一区二区三区日韩 | 精品国产精品| 久久在线观看视频| 青草视频在线观看免费| 国产精品资源在线看| 欧美一区二区三区四区夜夜大片 | 亚洲第一黄色网址| 成人写真视频| 欧美亚洲国产成人精品| 国产伦子伦对白视频| 国产欧美在线观看一区| 又粗又黑又大的吊av| 欧美视频精品全部免费观看| 国产一区二区日韩精品欧美精品| 日韩特黄一级片| 国产成人免费av在线| 欧美一进一出视频| www.综合| 亚洲第一级黄色片| 久久久久久久久久网站| 久久成人麻豆午夜电影| 日本在线播放不卡| 在线观看特色大片免费视频| 日韩精品一区二区三区视频在线观看 | 综合网五月天| 国产电影一区二区三区爱妃记| 亚洲国产精品视频在线观看| 欧美黄色一级网站| 国产麻豆一精品一av一免费| 国产成年人在线观看| 欧美成人福利| 在线观看欧美成人| 日本黄色中文字幕| 久久久久久久精| 99爱视频在线| 免费看av成人| 人人做人人澡人人爽欧美| 日韩精品视频无播放器在线看 | 成人免费网站入口| 18国产精品| 久久久久久久一区二区| 刘亦菲久久免费一区二区| 亚洲一区二区视频在线观看| 性感美女一区二区三区| 欧美亚洲不卡| 国产亚洲一区二区三区在线播放 | 亚洲最大黄网| 亚洲自拍另类欧美丝袜| 伊人电影在线观看| 精品日韩欧美在线| 久久伊人成人网| www.66久久| 欧美v在线观看| 精品国产日韩欧美| 国产拍精品一二三| 成人ww免费完整版在线观看| 日韩久久免费av| 精品久久免费视频| 久久久不卡网国产精品一区| 日本888xxxx| 99热国内精品| 俄罗斯精品一区二区| 天天综合av| 中文日韩在线观看| 亚洲av无码国产精品永久一区| 亚洲国产成人va在线观看天堂 | 国产精品视频你懂的| 国产欧美一区二| 亚洲私拍自拍| 日韩欧美激情一区二区| 成人亚洲精品| 97国产精品视频| аⅴ资源新版在线天堂| 欧美一级艳片视频免费观看| 成人毛片18女人毛片| 国产精品理论片在线观看| 中国老熟女重囗味hdxx| 国产欧美日韩一级| 亚洲精品视频一区二区三区| 日本一区二区三区播放| 青青在线视频一区二区三区| 午夜精品一区| 亚洲第一男人av| 中文字幕 日韩有码| 亚洲一区二区三区四区的| 久久久久久久久久久国产精品| 久久99久久精品| 精品视频免费在线播放| 欧美电影一区| 久久免费视频1| 日韩亚洲精品在线观看| 国产精品99久久99久久久二8| av软件在线观看| 亚洲图片在区色| 蜜桃视频久久一区免费观看入口| 91成人网在线| 日韩污视频在线观看| |精品福利一区二区三区| 国产熟妇久久777777| 福利电影一区二区三区| 亚洲免费黄色网| 国产精品久久久久久久久久妞妞| 亚洲小说欧美另类激情| 少妇精品久久久一区二区| 北条麻妃高清一区| 欧洲午夜精品| 国产成人精品一区| 爱啪视频在线观看视频免费| 久热国产精品视频| 亚洲天天影视| 亚洲午夜精品久久久久久性色| 免费av网站观看| 538prom精品视频线放| 免费无码国产精品| 欧美日韩国产一区在线| 久久久久成人片免费观看蜜芽| 国产精品美女久久久久aⅴ | 亚洲精品一区二区妖精| 奇米888一区二区三区| 国产精品久久久久av蜜臀| 91精品啪aⅴ在线观看国产| 三上悠亚激情av一区二区三区| 久久久久久久一| 伊人在我在线看导航| 久久国产精品久久久| 黄色成人影院| 日韩有码视频在线| av国产在线观看| 国产小视频国产精品| 欧美日韩伦理片| 国产视频久久网| 天堂av在线免费| 亚洲高清在线观看| 天堂av资源网| 日韩激情在线视频| 你懂的在线播放| 国产亚洲视频在线观看| 国际av在线| 伊人青青综合网站| 亚洲图片88| 久久久999精品| 18videosex性欧美麻豆| 欧美激情视频一区二区| 91超碰在线播放| 97超级碰碰人国产在线观看| h片在线观看下载| 国内揄拍国内精品| 国产伦久视频在线观看| 51精品国产黑色丝袜高跟鞋 | 国内久久视频| 久久久性生活视频| 新67194成人永久网站| 蜜臀久久99精品久久久酒店新书| 视频一区视频二区中文字幕| 天堂av在线网站| 激情国产一区二区| 亚洲成人激情小说| 99精品国产热久久91蜜凸| 巨胸大乳www视频免费观看| 欧美国产成人在线| 精品人妻伦九区久久aaa片| 亚洲一区中文日韩| 日韩精品一区二区亚洲av| 色欧美88888久久久久久影院| japanese国产在线观看| 欧美日韩国产综合久久| www.国产精品视频| 亚洲美女动态图120秒| 97人人在线| 欧美激情日韩图片| 毛片无码国产| 91人人爽人人爽人人精88v| 激情亚洲另类图片区小说区| 日本免费高清一区二区| 亚洲啊v在线观看| 欧美国产日韩激情| 久久亚洲不卡| 色哟哟免费视频| 久久综合资源网| 老熟妇高潮一区二区三区| 婷婷夜色潮精品综合在线| 中文字幕人妻丝袜乱一区三区| 欧美成人猛片aaaaaaa| 久久手机免费观看| 久久综合久中文字幕青草| 涩涩视频网站在线观看| 成人性生交xxxxx网站| 奇米777国产一区国产二区| 一区二区精品在线观看| 在线日韩欧美| 婷婷激情小说网| 国产日韩欧美精品一区| 久久人人爽人人爽人人| 欧美性生活影院| 天天干天天插天天操| 毛片精品免费在线观看| 校园春色亚洲色图| 激情小说网站亚洲综合网| 91久久电影| www.欧美日本| 成人91在线观看| 国产精品嫩草影院俄罗斯| 日本高清不卡aⅴ免费网站| 黄色片一区二区三区| 日韩视频永久免费观看| 深夜视频一区二区| 精品伦精品一区二区三区视频| 伊人青青综合网| 色噜噜狠狠永久免费| www国产成人免费观看视频 深夜成人网 | av免费在线不卡| 中文字幕日本精品| 欧美特大特白屁股xxxx| 国产综合av一区二区三区| 欧美激情在线| 亚洲第一天堂久久| 国产精品午夜在线观看| 一级黄色在线视频| 亚洲国产91色在线| 国内小视频在线看| 亚洲自拍偷拍视频| 亚洲精品二区三区| www.污网站| 中文字幕综合网| 国产视频在线观看视频| 日韩在线观看精品| 亚洲日本中文| 国产又黄又爽免费视频| 久久精品国产99国产| 日本黄色激情视频| 欧美性感一类影片在线播放| 岛国视频免费在线观看| 国产精品91久久久久久| 成人情趣视频| 911福利视频| 国产精品福利一区| 国产露脸91国语对白| 久久不射热爱视频精品| 美女国产精品久久久| 国产911在线观看| 国产二区国产一区在线观看| 久久久精品人妻一区二区三区四| 欧美成人性福生活免费看| 国产理论电影在线| 国产精品伊人日日| 亚洲中字在线| 国产精品理论在线| 欧美精品视频www在线观看| 黄黄的网站在线观看| 96精品久久久久中文字幕| 欧美精品大片| 成人性生活免费看| 色婷婷精品久久二区二区蜜臀av| 精品视频一二三| 国产精品亚发布| 围产精品久久久久久久| 色哟哟免费视频| 欧美日韩国产中文字幕| 成年人在线视频| 亚洲影视九九影院在线观看| 国内精品99| 色婷婷在线影院| 欧美精品v日韩精品v韩国精品v| 性欧美video高清bbw| 久99久在线| 蜜桃av一区二区| 麻豆chinese极品少妇| 日韩精品福利在线| 国产91在线播放精品| 蜜臀在线免费观看| 99riav久久精品riav| 中文字幕一区2区3区| 久久91亚洲精品中文字幕| 天堂av一区二区三区在线播放| a在线观看免费视频| 夜色激情一区二区| 国产福利片在线| www 成人av com| 日本人妖一区二区| 久久婷婷综合国产| 中文字幕亚洲国产| 都市激情久久| 三上悠亚在线一区二区| 亚洲综合成人网| 国产九色在线| 国产伦精品一区二区三毛| 久久国产麻豆精品| 国产精品二区一区二区aⅴ| 在线视频欧美性高潮| 都市激情久久| 久久精品视频在线观看免费|