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

太強了!輕松搞定 SpringBoot 接口防抖!

開發
所謂防抖,一是防用戶手抖,二是防網絡抖動。在Web系統中,表單提交是一個非常常見的功能,如果不加控制,容易因為用戶的誤操作或網絡延遲導致同一請求被發送多次,進而生成重復的數據記錄。

作為一名老碼農,在開發后端Java業務系統,包括各種管理后臺和小程序等。在這些項目中,我設計過單/多租戶體系系統,對接過許多開放平臺,也搞過消息中心這類較為復雜的應用,但幸運的是,我至今還沒有遇到過線上系統由于代碼崩潰導致資損的情況。

這其中的原因有三點:

  • 一是業務系統本身并不復雜;
  • 二是我一直遵循某大廠代碼規約,在開發過程中盡可能按規約編寫代碼;
  • 三是經過多年的開發經驗積累,我成為了一名熟練工,掌握了一些實用的技巧。

一、啥是防抖

所謂防抖,一是防用戶手抖,二是防網絡抖動。在Web系統中,表單提交是一個非常常見的功能,如果不加控制,容易因為用戶的誤操作或網絡延遲導致同一請求被發送多次,進而生成重復的數據記錄。

要針對用戶的誤操作,前端通常會實現按鈕的loading狀態,阻止用戶進行多次點擊。而對于網絡波動造成的請求重發問題,僅靠前端是不行的。為此,后端也應實施相應的防抖邏輯,確保在網絡波動的情況下不會接收并處理同一請求多次。

一個理想的防抖組件或機制,我覺得應該具備以下特點:

  • 邏輯正確,也就是不能誤判;
  • 響應迅速,不能太慢;
  • 易于集成,邏輯與業務解耦;
  • 良好的用戶反饋機制,比如提示“您點擊的太快了”

二、思路解析

前面講了那么多,我們已經知道接口的防抖是很有必要的了,但是在開發之前,我們需要捋清楚幾個問題。

1. 哪一類接口需要防抖?

接口防抖也不是每個接口都需要加,一般需要加防抖的接口有這幾類:

  • 用戶輸入類接口: 比如搜索框輸入、表單輸入等,用戶輸入往往會頻繁觸發接口請求,但是每次觸發并不一定需要立即發送請求,可以等待用戶完成輸入一段時間后再發送請求。
  • 按鈕點擊類接口: 比如提交表單、保存設置等,用戶可能會頻繁點擊按鈕,但是每次點擊并不一定需要立即發送請求,可以等待用戶停止點擊一段時間后再發送請求。
  • 滾動加載類接口: 比如下拉刷新、上拉加載更多等,用戶可能在滾動過程中頻繁觸發接口請求,但是每次觸發并不一定需要立即發送請求,可以等待用戶停止滾動一段時間后再發送請求。

2. 如何確定接口是重復的?

防抖也即防重復提交,那么如何確定兩次接口就是重復的呢?

  • 首先,我們需要給這兩次接口的調用加一個時間間隔,大于這個時間間隔的一定不是重復提交;
  • 其次,兩次請求提交的參數比對,不一定要全部參數,選擇標識性強的參數即可;
  • 最后,如果想做的更好一點,還可以加一個請求地址的對比。

三、分布式部署下如何做接口防抖?

有兩個方案:

1. 使用共享緩存

流程圖如下:

2. 使用分布式鎖

流程圖如下:

常見的分布式組件有Redis、Zookeeper等,但結合實際業務來看,一般都會選擇Redis,因為Redis一般都是Web系統必備的組件,不需要額外搭建。

四、具體實現

現在有一個保存用戶的接口:

@PostMapping("/add")
@RequiresPermissions(value = "add")
@Log(methodDesc = "添加用戶")
public ResponseEntity<String> add(@RequestBody AddReq addReq) {
        return userService.add(addReq);
}

AddReq.java:

package com.summo.demo.model.request;
import java.util.List;
import lombok.Data;
@Datapublic class AddReq {
    /**     * 用戶名稱     */    private String userName;
    /**     * 用戶手機號     */    private String userPhone;
    /**     * 角色ID列表     */    private List<Long> roleIdList;
}

目前數據庫表中沒有對userPhone字段做UK索引,這就會導致每調用一次add就會創建一個用戶,即使userPhone相同。

五、請求鎖

根據上面的要求,我定了一個注解@RequestLock,使用方式很簡單,把這個注解打在接口方法上即可。

RequestLock.java:

package com.summo.demo.model.request;

import java.util.List;

import lombok.Data;

@Data
publicclass AddReq {

    /**
     * 用戶名稱
     */
    private String userName;

    /**
     * 用戶手機號
     */
    private String userPhone;

    /**
     * 角色ID列表
     */
    private List<Long> roleIdList;
}

@RequestLock注解定義了幾個基礎的屬性,redis鎖前綴、redis鎖時間、redis鎖時間單位、key分隔符。其中前面三個參數比較好理解,都是一個鎖的基本信息。

key分隔符是用來將多個參數合并在一起的,比如userName是張三,userPhone是123456,那么完整的key就是"張三&123456",最后再加上redis鎖前綴,就組成了一個唯一key。

六、唯一key生成

這里有些同學可能就要說了,直接拿參數來生成key不就行了嗎?額,不是不行,但我想問一個問題:如果這個接口是文章發布的接口,你也打算把內容當做key嗎?要知道,Redis的效率跟key的大小息息相關。所以,我的建議是選取合適的字段作為key就行了,沒必要全都加上。

要做到參數可選,那么用注解的方式最好了,注解如下:

RequestKeyParam.java:

package com.example.requestlock.lock.annotation;

import java.lang.annotation.*;

/**
 * @description 加上這個注解可以將參數設置為key
 */
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RequestKeyParam {

}

這個注解加到參數上就行,沒有多余的屬性。

接下來就是lockKey的生成了,代碼如下:

RequestKeyGenerator.java:

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

publicclass RequestKeyGenerator {
    /**
     * 獲取LockKey
     *
     * @param joinPoint 切入點
     * @return
     */
    public static String getLockKey(ProceedingJoinPoint joinPoint) {
        //獲取連接點的方法簽名對象
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        //Method對象
        Method method = methodSignature.getMethod();
        //獲取Method對象上的注解對象
        RequestLock requestLock = method.getAnnotation(RequestLock.class);
        //獲取方法參數
        final Object[] args = joinPoint.getArgs();
        //獲取Method對象上所有的注解
        final Parameter[] parameters = method.getParameters();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < parameters.length; i++) {
            final RequestKeyParam keyParam = parameters[i].getAnnotation(RequestKeyParam.class);
            //如果屬性不是RequestKeyParam注解,則不處理
            if (keyParam == null) {
                continue;
            }
            //如果屬性是RequestKeyParam注解,則拼接 連接符 "& + RequestKeyParam"
            sb.append(requestLock.delimiter()).append(args[i]);
        }
        //如果方法上沒有加RequestKeyParam注解
        if (StringUtils.isEmpty(sb.toString())) {
            //獲取方法上的多個注解(為什么是兩層數組:因為第二層數組是只有一個元素的數組)
            final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            //循環注解
            for (int i = 0; i < parameterAnnotations.length; i++) {
                final Object object = args[i];
                //獲取注解類中所有的屬性字段
                final Field[] fields = object.getClass().getDeclaredFields();
                for (Field field : fields) {
                    //判斷字段上是否有RequestKeyParam注解
                    final RequestKeyParam annotation = field.getAnnotation(RequestKeyParam.class);
                    //如果沒有,跳過
                    if (annotation == null) {
                        continue;
                    }
                    //如果有,設置Accessible為true(為true時可以使用反射訪問私有變量,否則不能訪問私有變量)
                    field.setAccessible(true);
                    //如果屬性是RequestKeyParam注解,則拼接 連接符" & + RequestKeyParam"
                    sb.append(requestLock.delimiter()).append(ReflectionUtils.getField(field, object));
                }
            }
        }
        //返回指定前綴的key
        return requestLock.prefix() + sb;
    }
}

由于@RequestKeyParam可以放在方法的參數上,也可以放在對象的屬性上,所以這里需要進行兩次判斷,一次是獲取方法上的注解,一次是獲取對象里面屬性上的注解。

七、重復提交判斷

1. Redis緩存方式

RedisRequestLockAspect.java:

import java.lang.reflect.Method;
import com.summo.demo.exception.biz.BizException;
import com.summo.demo.model.response.ResponseCodeEnum;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.util.StringUtils;

/**
 * @description 緩存實現
 */
@Aspect
@Configuration
@Order(2)
publicclass RedisRequestLockAspect {

    privatefinal StringRedisTemplate stringRedisTemplate;

    @Autowired
    public RedisRequestLockAspect(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Around("execution(public * * (..)) && @annotation(com.summo.demo.config.requestlock.RequestLock)")
    public Object interceptor(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        RequestLock requestLock = method.getAnnotation(RequestLock.class);
        if (StringUtils.isEmpty(requestLock.prefix())) {
            thrownew BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "重復提交前綴不能為空");
        }
        //獲取自定義key
        final String lockKey = RequestKeyGenerator.getLockKey(joinPoint);
        // 使用RedisCallback接口執行set命令,設置鎖鍵;設置額外選項:過期時間和SET_IF_ABSENT選項
        final Boolean success = stringRedisTemplate.execute(
            (RedisCallback<Boolean>)connection -> connection.set(lockKey.getBytes(), newbyte[0],
                Expiration.from(requestLock.expire(), requestLock.timeUnit()),
                RedisStringCommands.SetOption.SET_IF_ABSENT));
        if (!success) {
            thrownew BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "您的操作太快了,請稍后重試");
        }
        try {
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            thrownew BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "系統異常");
        }
    }
}

這里的核心代碼是stringRedisTemplate.execute里面的內容,正如注釋里面說的“使用RedisCallback接口執行set命令,設置鎖鍵;設置額外選項:過期時間和SET_IF_ABSENT選項”,有些同學可能不太清楚SET_IF_ABSENT是個啥?

這里我解釋一下:SET_IF_ABSENT是 RedisStringCommands.SetOption 枚舉類中的一個選項,用于在執行 SET 命令時設置鍵值對的時候,如果鍵不存在則進行設置,如果鍵已經存在,則不進行設置。

2. Redisson分布式方式

Redisson分布式需要一個額外依賴,引入方式:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.10.6</version>
</dependency>

由于我之前的代碼有一個RedisConfig,引入Redisson之后也需要單獨配置一下,不然會和RedisConfig沖突。

RedissonConfig.java:

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
publicclass RedissonConfig {

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        // 這里假設你使用單節點的Redis服務器
        config.useSingleServer()
            // 使用與Spring Data Redis相同的地址
            .setAddress("redis://127.0.0.1:6379");
        // 如果有密碼
        //.setPassword("xxxx");
        // 其他配置參數
        //.setDatabase(0)
        //.setConnectionPoolSize(10)
        //.setConnectionMinimumIdleSize(2);
        // 創建RedissonClient實例
        return Redisson.create(config);
    }
}

配好之后,核心代碼如下:

RedissonRequestLockAspect.java:

import java.lang.reflect.Method;

import com.summo.demo.exception.biz.BizException;
import com.summo.demo.model.response.ResponseCodeEnum;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;

/**
 * @description 分布式鎖實現
 */
@Aspect
@Configuration
@Order(2)
publicclass RedissonRequestLockAspect {
    private RedissonClient redissonClient;

    @Autowired
    public RedissonRequestLockAspect(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Around("execution(public * * (..)) && @annotation(com.summo.demo.config.requestlock.RequestLock)")
    public Object interceptor(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        RequestLock requestLock = method.getAnnotation(RequestLock.class);
        if (StringUtils.isEmpty(requestLock.prefix())) {
            thrownew BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "重復提交前綴不能為空");
        }
        //獲取自定義key
        final String lockKey = RequestKeyGenerator.getLockKey(joinPoint);
        // 使用Redisson分布式鎖的方式判斷是否重復提交
        RLock lock = redissonClient.getLock(lockKey);
        boolean isLocked = false;
        try {
            //嘗試搶占鎖
            isLocked = lock.tryLock();
            //沒有拿到鎖說明已經有了請求了
            if (!isLocked) {
                thrownew BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "您的操作太快了,請稍后重試");
            }
            //拿到鎖后設置過期時間
            lock.lock(requestLock.expire(), requestLock.timeUnit());
            try {
                return joinPoint.proceed();
            } catch (Throwable throwable) {
                thrownew BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "系統異常");
            }
        } catch (Exception e) {
            thrownew BizException(ResponseCodeEnum.BIZ_CHECK_FAIL, "您的操作太快了,請稍后重試");
        } finally {
            //釋放鎖
            if (isLocked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

    }
}

Redisson的核心思路就是搶鎖,當一次請求搶到鎖之后,對鎖加一個過期時間,在這個時間段內重復的請求是無法獲得這個鎖,也不難理解。

測試一下。

第一次提交,"添加用戶成功"

短時間內重復提交,"BIZ-0001:您的操作太快了,請稍后重試"

過幾秒后再次提交,"添加用戶成功"

從測試的結果上看,防抖是做到了,但是隨著緩存消失、鎖失效,還是可以發起同樣的請求,所以要真正做到接口冪等性,還需要業務代碼的判斷、設置數據庫表的UK索引等操作。

我在文章里面說到生成唯一key的時候沒有加用戶相關的信息,比如用戶ID、IP屬地等,真實生產環境建議加上這些,可以更好地減少誤判。

責任編輯:趙寧寧 來源: 碼猿技術專欄
相關推薦

2025-06-09 01:22:00

2025-01-22 14:02:35

2025-04-14 04:01:00

2025-09-01 02:00:00

2025-04-08 01:00:00

Spring開發系統

2022-05-30 16:31:08

CSS

2025-02-08 08:00:00

JavaDeepSeekIDEA

2024-08-29 15:26:21

2021-03-04 09:31:42

開源技術 項目

2024-05-28 09:26:46

2025-01-13 13:47:13

2023-12-10 20:33:50

Redis搜索全文

2024-09-13 10:21:50

2025-04-10 00:25:00

Spring@JsonView注解

2024-06-14 09:30:58

2022-06-08 08:01:28

模板字面量類型

2025-08-07 09:30:57

2024-01-30 09:21:29

CSS文字效果文字裝飾

2021-08-05 16:25:37

Windows 11Windows微軟

2025-05-09 08:00:00

JavaScript代碼防抖節流
點贊
收藏

51CTO技術棧公眾號

又污又黄的网站| 国精产品一区一区| 日本不卡1234视频| 久久久亚洲国产美女国产盗摄| 国产91在线视频| 日韩在线一卡二卡| 一区二区三区视频播放| 色狠狠一区二区三区香蕉| 一区二区视频国产| 成人毛片视频免费看| 久久午夜影视| 欧美另类高清videos| 久久丫精品国产亚洲av不卡 | 色妞欧美日韩在线| 日韩精品视频一区二区| 成人综合网站| 亚洲成人av福利| 亚洲国产激情一区二区三区| 国产成人手机在线| 美女视频黄频大全不卡视频在线播放| 欧美日韩福利电影| 人妻互换一区二区激情偷拍| 第四色中文综合网| 欧美二区三区91| 久久国产乱子伦免费精品| 国产成人无吗| 国产情人综合久久777777| 91偷拍精品一区二区三区| 午夜精品免费观看| 伊人影院久久| 欧美成人免费网| 国产在视频线精品视频| 天堂俺去俺来也www久久婷婷 | 高清欧美性猛交xxxx| 欧美激情精品久久久久久免费| 亚洲人成精品久久久| 亚洲高清久久久久久| 樱花草www在线| 日本一区免费网站| 欧美日韩视频免费播放| 久久这里只有精品8| 毛片av在线| 国产精品久久看| 日本免费一区二区三区| 天堂成人在线| 成人午夜激情在线| 2020国产精品久久精品不卡| 国产又粗又黄又爽视频| 热久久国产精品| 国产精品久久久久久久av大片| 欧美成人精品欧美一级乱黄| 在线日韩av| 欧美激情久久久久久| 欧美日韩精品在线观看视频 | av在线不卡观看免费观看| 99re资源| 亚洲乱色熟女一区二区三区| 国产成人精品1024| 成人综合色站| 久久精品国产一区二区| 欧美精品一区二区三区很污很色的| 九九热免费在线观看| 成人久久网站| 欧美日韩在线播放一区| 午夜免费看视频| 玖玖精品在线| 欧美卡1卡2卡| 国产亚洲色婷婷久久| 久久免费福利| 日韩精品一区二区三区四区| 亚洲性图第一页| 国内精品国产成人国产三级粉色| 精品久久久网站| 中文字幕乱码在线| 自拍欧美一区| 色婷婷av一区二区三区在线观看 | 欧美黑白配在线| 日韩精品福利在线| 亚洲精品国产熟女久久久| 日韩电影一区| 久久99久久亚洲国产| 四虎永久在线精品| 亚洲在线网站| 国产欧美日韩精品专区| 99精品在线看| 91在线视频免费观看| 水蜜桃亚洲一二三四在线| 黄色在线免费网站| 五月综合激情网| 另类小说第一页| 少妇精品在线| 亚洲精品日韩丝袜精品| 可以免费看av的网址| 国一区二区在线观看| 日产精品久久久一区二区福利 | 91精品国产色综合久久不卡蜜臀 | 日韩国产成人在线| 国产一区二区三区日韩| 久久久免费看| 成人毛片av在线| 欧美性20hd另类| 精品国产乱码久久久久久1区二区| 动漫3d精品一区二区三区乱码| 亚洲老板91色精品久久| 欧美日韩黄色网| 性色一区二区| 91精品国产91久久久久青草| 青春草在线观看| 亚洲精品中文字幕乱码三区| 动漫av网站免费观看| 精品视频一二| 亚洲丝袜av一区| 久久久全国免费视频| 日韩电影在线免费看| 国产精品免费在线播放| 老司机av在线免费看| 欧美色欧美亚洲高清在线视频| 欧美激情第一区| 国产一区二区欧美| 97在线免费视频| 99国产精品一区二区三区 | 日韩欧美一区中文| 国产又黄又粗视频| 国产亚洲综合精品| 亚洲va欧美va国产综合剧情| 国产天堂在线| 一本一道综合狠狠老| 欧美做受高潮中文字幕| 久久久久久久久99精品大| 国产精品99久久久久久久久久久久 | 91视频国产精品| 99青草视频在线播放视| 日韩欧美一区二区在线| 韩国三级视频在线观看| 99久久婷婷| 国产精品麻豆va在线播放| 美丽的姑娘在线观看免费动漫| 亚洲一区二区三区影院| 免费看的av网站| 亚洲成人三区| 成人在线免费观看视视频| 97视频在线观看网站| 色老头久久综合| 深爱五月激情网| 国产免费成人| 麻豆av一区二区| 国产在线天堂www网在线观看| 亚洲精品一区二区三区福利| 久草视频在线资源| 国产大陆a不卡| 国产女教师bbwbbwbbw| 国产美女视频一区二区| 久久夜色精品亚洲噜噜国产mv| 伊人久久国产精品| 国产精品人妖ts系列视频| 欧美午夜aaaaaa免费视频| 日韩av大片| 成人在线国产精品| 肉肉视频在线观看| 欧美v日韩v国产v| 国产在线一区视频| 99久久国产综合精品色伊| 免费看一级大黄情大片| 亚洲区小说区图片区qvod| 日本免费久久高清视频| 国产视频三级在线观看播放| 欧洲中文字幕精品| 最新日韩免费视频| 国产乱对白刺激视频不卡| 中文字幕日韩精品无码内射| 国内精品免费| 国产精品com| 麻豆传媒视频在线| 91精品午夜视频| 国产一级二级三级视频| 99久久精品免费看国产 | 奇米综合一区二区三区精品视频| 午夜精品区一区二区三| 免费观看在线一区二区三区| 高清视频欧美一级| 超碰97在线免费观看| 在线播放日韩导航| 久久精品久久国产| 久久精品视频在线免费观看| 亚洲欧美日韩精品一区| 国产精品sm| 免费成人深夜夜行视频| 亚洲二区av| 91国内在线视频| 91社区在线观看播放| 日韩欧美色综合网站| 九九热在线免费观看| 亚洲欧洲精品天堂一级| 久久久无码人妻精品无码| 久久国产99| 国产a级片免费看| 日韩有码av| 亚洲一区二区在线| 成人性生交大片免费网站| 久久成人精品电影| 牛牛澡牛牛爽一区二区| 欧美一卡在线观看| 久久人妻免费视频| 亚洲黄网站在线观看| 亚洲无人区码一码二码三码的含义| 国产成人无遮挡在线视频| 黄色成人免费看| 亚洲看片一区| ijzzijzzij亚洲大全| 亚洲女娇小黑人粗硬| 99精彩视频| 高清欧美日韩| 91高清视频免费| 伊人福利在线| 在线丨暗呦小u女国产精品| 高清一区二区三区四区| 91精品欧美福利在线观看| jizz国产在线观看| 亚洲午夜一区二区三区| 日本爱爱小视频| 国产欧美日韩三级| 亚洲国产精品成人综合久久久| 国产一二精品视频| 成人亚洲精品777777大片| 日韩一区二区久久| 免费看黄色a级片| 久久人体视频| 图片区小说区区亚洲五月| 日韩精品福利一区二区三区| 91久久精品国产91久久性色tv | 亚洲精品女av网站| 97欧美成人| 日韩av理论片| 在线最新版中文在线| 久久久久久亚洲精品中文字幕| 黄色网页在线免费观看| 中文字幕日韩欧美| 高清中文字幕一区二区三区| 精品视频一区在线视频| 少妇高潮一区二区三区99小说 | 丁香婷婷在线| 国产一区二区三区中文| 日韩欧美电影在线观看| 亚洲精品电影在线观看| 隣の若妻さん波多野结衣| 欧美一区二区三区电影| 国产在成人精品线拍偷自揄拍| 欧美天堂一区二区三区| 中文无码精品一区二区三区| 色噜噜狠狠色综合欧洲selulu| 99久久久久久久久| 色乱码一区二区三区88 | 这里只有精品视频在线观看| 91麻豆成人精品国产| 欧美精品日韩综合在线| 91肉色超薄丝袜脚交一区二区| 欧美三级电影一区| 国产又大又粗又硬| 91精品国产一区二区三区蜜臀| 国产av无码专区亚洲av| 日韩丝袜情趣美女图片| 亚洲精品成av人片天堂无码| 欧美成人性战久久| 四虎精品一区二区三区| 亚洲精品美女在线观看| 黄色大片在线免费观看| 最近更新的2019中文字幕| 日本在线观看网站| 色综合久久中文字幕综合网小说| 女人黄色免费在线观看| 911国产网站尤物在线观看| 亚洲最大成人| 国产女同一区二区| 国内精品视频| 国产综合av一区二区三区| 国产一区二区精品福利地址| 一区二区三区四区欧美| 综合av在线| 国产精品333| 免费高清视频精品| 国产sm在线观看| 26uuu另类欧美| 战狼4完整免费观看在线播放版| 亚洲猫色日本管| 色网站在线播放| 欧美三级日韩三级| 亚洲精品一区二区三区蜜桃 | 香蕉视频网站在线观看| 欧美疯狂性受xxxxx另类| 在线日韩影院| 成人在线播放av| 亚洲图区在线| 蜜桃视频成人在线观看| 夜夜嗨网站十八久久| 国产三级三级看三级| 大尺度一区二区| 精品人体无码一区二区三区| 亚洲综合免费观看高清完整版 | av免费在线免费| 欧美在线一级va免费观看| 亚洲人成777| 蜜桃臀一区二区三区| 亚洲乱码免费伦视频| 69堂免费视频| 国产传媒久久文化传媒| 久久婷婷五月综合| 亚洲国产中文字幕| 一区二区久久精品66国产精品| 精品久久一区二区| 久久久久久国产精品免费无遮挡| 555www成人网| 亚洲天堂中文字幕在线观看| 亚洲国内在线| 亚洲自拍另类| 最新国产精品自拍| 成人欧美一区二区三区小说| 国产婷婷色一区二区在线观看| 日韩欧美视频一区| 欧美成人性生活视频| 日韩美女激情视频| 加勒比中文字幕精品| 亚洲区成人777777精品| 蜜桃传媒麻豆第一区在线观看| 成人h动漫精品一区| 亚洲一区欧美一区| 国产免费一区二区三区最新不卡| 一个色综合导航| xxxxxx欧美| 精品综合在线| 亚洲大片在线| 免费黄色a级片| 亚洲精品日产精品乱码不卡| 91福利在线观看视频| 亚洲视频专区在线| 日韩精品专区| 免费看成人片| 久久国产精品亚洲77777| 少妇精品一区二区| 精品久久久香蕉免费精品视频| 亚洲黄色a级片| 欧美激情18p| 91麻豆精品激情在线观看最新| ijzzijzzij亚洲大全| 精品中文字幕一区二区| 国产一区第一页| 欧美精品日韩一本| 激情影院在线观看| 91精品视频在线看| 中文精品久久| 成年人看片网站| 亚洲一区二区精品3399| 开心激情综合网| 久久久亚洲欧洲日产国码aⅴ| 国产在线播放精品| 免费观看日韩毛片| 久久久久久久综合日本| 午夜久久久久久久久久影院| 亚洲欧美综合区自拍另类| 欧洲精品一区二区三区| 午夜精品短视频| 久久99精品国产麻豆不卡| 在线免费观看亚洲视频| 精品少妇一区二区三区免费观看 | 亚洲精品国产无套在线观| 国产黄色片免费观看| 欧美激情中文字幕乱码免费| 欧美精品国产白浆久久久久| 国产肥臀一区二区福利视频| 国产亚洲成aⅴ人片在线观看| 中文字幕一二三四| 久久久精品一区二区三区| 伊色综合久久之综合久久| 国产成人无码精品久久久性色| 2024国产精品视频| 中文字幕你懂的| 欧美福利视频网站| 亚洲人成网亚洲欧洲无码| 高清av免费看| 亚洲综合一二三区| 欧美一区二区少妇| 国产这里只有精品| 亚洲国产三级| 久操视频在线观看免费| 日韩一二在线观看| 在线女人免费视频| 欧美日韩亚洲国产成人| 成人av在线播放网址| 中国一级片黄色一级片黄| 欧美xxxx做受欧美| 亚洲婷婷丁香| 丰满人妻一区二区三区大胸| 欧美日韩午夜视频在线观看| 午夜视频在线看| 国产综合欧美在线看| 久久激五月天综合精品| 国产无套粉嫩白浆内谢| 在线视频欧美日韩精品| 成人资源在线| 日本肉体xxxx裸体xxx免费| 亚洲一区二区三区四区的 |