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

【Redis實戰(zhàn)五】Redisson鎖機制源碼分析

開發(fā) 前端
Redisson實現(xiàn)分布式鎖,就是使用lua腳本保證原子性和互斥性的。每次都判斷是不是自己持有鎖,才進行操作,這就保證了同一性。

1、了解分布式鎖的特性

1、鎖的互斥性

也就是說,在任意時刻,只能有一個客戶端能獲取到鎖,不能同時有兩個或多個客戶端獲取到鎖。

簡單來說,就比如上廁所,一個廁所只有一個坑位,只能一個人上,不能同時兩個人或多個人上。

2、鎖的同一性

也就是說,鎖只能被持有該鎖的客戶端進行刪除(釋放鎖),不能由其他客戶端刪除。

簡單倆說,就是誰加的鎖,就只能誰來解鎖。也就是解鈴還須系鈴人。

3、鎖的可重入性

也就是說,持有某個鎖的客戶端,可以繼續(xù)對該鎖進行加鎖,實現(xiàn)鎖的續(xù)租。

簡單來說,就是你上廁所的按時間收費的,時間快到了會按照時間給你續(xù)租,而會給你價錢。

而Redisson則會增大的你的續(xù)租次數(shù),也就是可重入次數(shù)。但絕不收費,因為Redis是開源的嘛。

4、鎖的容錯性

鎖超過了最大續(xù)租時間后,會自動釋放鎖,其他客戶端會繼續(xù)獲得該鎖,從而防止死鎖的發(fā)生。

簡單來說,比如你上個廁所上了五小時,廁管員覺得不對勁,就來測試,發(fā)現(xiàn)你悄悄逃票了,此時測試會自動變成解鎖狀態(tài),其他人就可以去上了,只是廁管員血虧5塊大洋。

2、帶著幾個特性去看Redisson源碼

先回顧一下Redisson加解鎖代碼如何寫的

public TestEntity getById2(Long id){
    RLock lock = redissonClient.getLock("demo2_lock");
    lock.lock(20, TimeUnit.SECONDS);
    index++;
    log.info("current index is : {}", index);
    TestEntity testEntity = new TestEntity(new Random().nextLong(), UUID.randomUUID().toString(), new Random().nextInt(20) + 10);
    log.info("模擬查詢數(shù)據(jù)庫:{}", testEntity);
    lock.unlock();
    return testEntity;
}

2.1、關(guān)注Redisson.getLock()方法

@Override
public RLock getLock(String name) {
    return new RedissonLock(commandExecutor, name);
}

其實就是創(chuàng)建一個RedissonLock對象, 所以加鎖的邏輯就在RedissonLock.lock()中,解鎖的邏輯就在RedissonLock.unlock()。

2.2、關(guān)注RedissonLock.lock()方法

// RedissonLock.lock()的方法體
public void lock(long leaseTime, TimeUnit unit) {
    try {
        // 調(diào)用了lock的重載方法
        lock(leaseTime, unit, false);
    } catch (InterruptedException e) {
        throw new IllegalStateException();
    }
}

關(guān)注lock的重載方法

// leaseTime表示最大續(xù)時間,unit表示續(xù)約時間單位,interruptibly表示是否可以中斷
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
    // 獲取當前線程的線程ID
    long threadId = Thread.currentThread().getId();
    // 嘗試獲取鎖,結(jié)果為null表示此時沒有客戶端占用鎖,絕不矯情,直接拿到鎖就返回。
    // 結(jié)果ttl>0的話,表示此時已經(jīng)有了其他不識好歹的客戶端暫用了鎖,那么就只能絕望的等待了    
    Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
    // lock acquired
    if (ttl == null) {
        return;
    }

    // 等待時訂閱一個渠道,如果鎖被其他客戶端釋放了,會通過發(fā)布訂閱模式在publish上發(fā)一個消息,表示鎖已經(jīng)釋放了
    CompletableFuture<RedissonLockEntry> future = subscribe(threadId);
    pubSub.timeout(future);
    RedissonLockEntry entry;
    if (interruptibly) {
        entry = commandExecutor.getInterrupted(future);
    } else {
        entry = commandExecutor.get(future);
    }

    try {
        // 我干等這不是辦法,我還是要不斷去嘗試看能不能獲取鎖
        while (true) {
            ttl = tryAcquire(-1, leaseTime, unit, threadId);
            // 如果TTL為空了,表示獲取到了鎖,那還等什么,長驅(qū)直入就是。
            if (ttl == null) {
                // 結(jié)束循環(huán)等待
                break;
            }

            // 如果ttl還是大于0的,表示其他客戶端真的是過于不識好歹,還不肯釋放鎖。但好歹還是說了它還要持有錯多久。
            if (ttl >= 0) {
                try {
                    // 既然如此,那么我就等待你的時間到達吧,除非我突然有啥事被中斷了,否則我就等到你過期
                    entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    // 如果傳入了中斷標識,直接拋出異常,中斷了,干別的事情去
                    if (interruptibly) {
                        throw e;
                    }
                    // 否則還是老老實實的繼續(xù)等待時間到來
                    entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                }
            }
            // 鎖過期時間小于0, 表示那個殺千刀的客戶端居然沒有設置超時時間,它包場了,這可咋整。
            else {
                // 如果不被中斷,那么我也只有無期限的等待下去了,我不希望這個期限是一萬年
                if (interruptibly) {
                    entry.getLatch().acquire();
                } else {
                    entry.getLatch().acquireUninterruptibly();
                }
            }
        }
    } finally {
        // 最后,不管如何,我無論如何都要去取消訂閱這個publish的消息,因為這會浪費我的精力,這已經(jīng)是我最后的堅持了。
        // 其實是釋放資源
        unsubscribe(entry, threadId);
    }
//        get(lockAsync(leaseTime, unit));
}

關(guān)注tryAcquire加鎖方法

private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    return get(tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}

該方法調(diào)用了tryAcquireAsync來實現(xiàn)的,所以我們關(guān)注tryAcquireAsync方法,繼續(xù)跟進。

關(guān)注tryAcquireAsync加鎖方法

private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    RFuture<Long> ttlRemainingFuture;
    // 首先判斷租約時間是否大于0
    if (leaseTime > 0) {
        // 大于零,調(diào)用tryLockInnerAsync獲取鎖
        ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
    } else {
        // 否則,使用默認的租約時間 追溯下去發(fā)現(xiàn)private long lockWatchdogTimeout = 30 * 1000;  也就是30s的租約時間
        ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,
                TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
    }
    // 
    CompletionStage<Long> f = ttlRemainingFuture.thenApply(ttlRemaining -> {
        // lock acquired
        // 結(jié)果為空,如果leaseTime大于哦,更新internalLockLeaseTime為指定的超時時間,并且不會啟動看門狗(watch dog)
        if (ttlRemaining == null) {
            if (leaseTime > 0) {
                internalLockLeaseTime = unit.toMillis(leaseTime);
            } else {
                // 使用定時任務,自動續(xù)約(使用看門狗(watch dog))
                scheduleExpirationRenewal(threadId);
            }
        }
        return ttlRemaining;
    });
    return new CompletableFutureWrapper<>(f);
}

可以看到,加鎖最終會調(diào)用tryLockInnerAsync進行加鎖,而續(xù)約會使用scheduleExpirationRenewal進行續(xù)約。

關(guān)注tryLockInnerAsync實現(xiàn)真正的加鎖邏輯

<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
    return evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,
            "if (redis.call('exists', KEYS[1]) == 0) then " +
                    "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                    "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                    "return nil; " +
                    "end; " +
                    "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                    "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                    "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                    "return nil; " +
                    "end; " +
                    "return redis.call('pttl', KEYS[1]);",
            Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
}

這里執(zhí)行了一段lua腳本(整個lua腳本保障原子性),我們將腳本內(nèi)容復制出來,詳細解釋一下。

-- KEYS[1] 加鎖的對象(也就是我們傳入的的鎖名稱)
-- ARGV[1] 表示鎖的過期時間
-- ARGV[2]:UUID+當前線程id
-- 如果鎖不存在。 == 0表示不存在 == 1表示存在
if (redis.call('exists', KEYS[1]) == 0) then
    -- 對我自己的鎖執(zhí)行一個incrby(自增,表示鎖的可重入次數(shù))操作
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    -- 對key設置一個過期時間(過期時間就是保證鎖的容錯性)
    redis.call('pexpire', KEYS[1], ARGV[1]);
    -- 返回nil, 相當于null, 表示獲取鎖成功
    return nil;
end ;
-- 繼續(xù)判斷鎖名成+UUID+當前線程id是否存在,其實就是判斷我自己有沒有已經(jīng)拿到鎖(保證鎖的可重入性)
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    -- 自己已經(jīng)持有鎖,執(zhí)行一個incrby(自增,表示鎖的可重入次數(shù))操作
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    -- 重新設置過期時間
    redis.call('pexpire', KEYS[1], ARGV[1]);
    -- 返回nil, 相當于null, 表示獲取鎖成功
    return nil;
end ;
-- 都不是,表示已經(jīng)有其他客戶端獲取到了鎖,此時返回key的過期時間,也就是別人釋放鎖的時間(但其他客戶端可能出現(xiàn)續(xù)約,存在會等待更久的可能)
return redis.call('pttl', KEYS[1]);

整個lua腳本保障原子性,從而只會有一個客戶端能夠獲取到鎖,這樣就保證了鎖的互斥性。

打一個斷點看獲取到的鎖信息

hash表中的第一個值表示UUID+線程ID,這二個值表示鎖的重入次數(shù),如果鎖被多次獲取,那么這個值就是大于1。

關(guān)注scheduleExpirationRenewal實現(xiàn)自動續(xù)約

protected void scheduleExpirationRenewal(long threadId) {
    ExpirationEntry entry = new ExpirationEntry();
    ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);
    // 不為空表示已經(jīng)開啟了續(xù)約操作    
    if (oldEntry != null) {
        oldEntry.addThreadId(threadId);
    } else {
        // 如果沒有開啟續(xù)約操作
        entry.addThreadId(threadId);
        try {
            // 自動續(xù)約
            renewExpiration();
        } finally {
            if (Thread.currentThread().isInterrupted()) {
                cancelExpirationRenewal(threadId);
            }
        }
    }
}

關(guān)注renewExpiration()方法

private void renewExpiration() {
    ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
    if (ee == null) {
        return;
    }
    // 創(chuàng)建一個定時任務去實現(xiàn)自動續(xù)約
    Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
        @Override
        public void run(Timeout timeout) throws Exception {
            // 獲取當前鎖的ExpirationEntry 對象。
            ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
            if (ent == null) {
                return;
            }
            // 獲取第一個線程ID
            Long threadId = ent.getFirstThreadId();
            if (threadId == null) {
                return;
            }
            // 鎖續(xù)期
            CompletionStage<Boolean> future = renewExpirationAsync(threadId);
            future.whenComplete((res, e) -> {
                if (e != null) {
                    log.error("Can't update lock " + getRawName() + " expiration", e);
                    EXPIRATION_RENEWAL_MAP.remove(getEntryName());
                    return;
                }
                // 續(xù)約成功,遞歸自己無限續(xù)約下去
                if (res) {
                    // reschedule itself
                    renewExpiration();
                } else {
                    // 續(xù)約失敗,表示鎖已釋放,取消續(xù)約任務
                    cancelExpirationRenewal(null);
                }
            });
        }
    }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); // internalLockLeaseTime / 3表示每隔鎖時間的三分之一,去續(xù)約一次
    
    ee.setTimeout(task);
}

關(guān)注renewExpirationAsync方法

protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {
    return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
            "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                    "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                    "return 1; " +
                    "end; " +
                    "return 0;",
            Collections.singletonList(getRawName()),
            internalLockLeaseTime, getLockName(threadId));
}

我們發(fā)現(xiàn),又是一段lua腳本,還是復制出來,格式化后詳細解釋下代碼。

-- KEYS[1] 加鎖的對象(也就是我們傳入的的鎖名稱)
-- ARGV[1] 表示鎖的過期時間
-- ARGV[2]:UUID+當前線程id
-- 使用hexists判斷鎖是不是自己持有的, == 1表示是自己持有,== 0 表示被其他客戶端持有
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    -- 重新設置過期時間
    redis.call('pexpire', KEYS[1], ARGV[1]);
    -- 返回1 表示續(xù)約成功
    return 1;
end ;
-- 返回0 表示續(xù)約失敗,也意味著鎖已經(jīng)被釋放或者被其他客戶端獲取了
return 0;

所以續(xù)約的邏輯就是,啟動一個定時任務,每隔續(xù)約時間的三分之一次就執(zhí)行一次。嘗試去續(xù)約,續(xù)約成功則會一直遞歸續(xù)約下去。續(xù)約失敗表示鎖已被釋放,則停止續(xù)約任務。

而續(xù)約的操作就是,判斷是否是自己持有鎖,是的話就重新設置過期時間,并且返回1表示續(xù)約成功,否則返回0表示續(xù)約失敗。

2.3、關(guān)注RedissonLock.unlock()方法

@Override
public void unlock() {
    try {
        // 其實就是調(diào)用了unlockAsync進行解鎖
        get(unlockAsync(Thread.currentThread().getId()));
    } catch (RedisException e) {
        if (e.getCause() instanceof IllegalMonitorStateException) {
            throw (IllegalMonitorStateException) e.getCause();
        } else {
            throw e;
        }
    }
    
//        Future<Void> future = unlockAsync();
//        future.awaitUninterruptibly();
//        if (future.isSuccess()) {
//            return;
//        }
//        if (future.cause() instanceof IllegalMonitorStateException) {
//            throw (IllegalMonitorStateException)future.cause();
//        }
//        throw commandExecutor.convertException(future);
}

我們可以看到,會使用unlockAsync方法進行解鎖,并且在這里傳入了當前的線程ID。

關(guān)注unlockAsync方法

@Override
public RFuture<Void> unlockAsync(long threadId) {
    // 調(diào)用unlockInnerAsync實現(xiàn)異步解鎖
    RFuture<Boolean> future = unlockInnerAsync(threadId);

    // 釋放之后再處理一些事情
    CompletionStage<Void> f = future.handle((opStatus, e) -> {
        // 取消(停止)續(xù)約任務,這里也會停止watch dog
        cancelExpirationRenewal(threadId);
        
        if (e != null) {
            throw new CompletionException(e);
        }
        if (opStatus == null) {
            IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "
                    + id + " thread-id: " + threadId);
            throw new CompletionException(cause);
        }

        return null;
    });

    return new CompletableFutureWrapper<>(f);
}

關(guān)注解鎖的核心邏輯unlockInnerAsync方法

protected RFuture<Boolean> unlockInnerAsync(long threadId) {
    return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
            "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
                    "return nil;" +
                    "end; " +
                    "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
                    "if (counter > 0) then " +
                    "redis.call('pexpire', KEYS[1], ARGV[2]); " +
                    "return 0; " +
                    "else " +
                    "redis.call('del', KEYS[1]); " +
                    "redis.call('publish', KEYS[2], ARGV[1]); " +
                    "return 1; " +
                    "end; " +
                    "return nil;",
            Arrays.asList(getRawName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}

可以看到,其實又是一段lua腳本,繼續(xù)復制出來分析一下。

-- KEYS[1] 加鎖的對象(也就是我們傳入的的鎖名稱)
-- KEYS[2] 監(jiān)聽該鎖的頻道 也就是publish要發(fā)送鎖被釋放的頻道,用于在鎖釋放時通知其他客戶端可以重新獲取鎖了
-- ARGV[1]:解鎖消息
-- ARGV[2] 表示鎖的過期時間
-- ARGV[3]:UUID+當前線程id
-- 先判斷自己的鎖是不是已經(jīng)釋放了 ==0 表示key不存在了,也就是鎖被釋放了
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
    -- 返回nil,也就是null, 表示釋放鎖成功
    return nil;
end ;
-- 對鎖的重入次數(shù)減一  因為重入一次counter會+1,所以釋放時每次也只能-1,跟重入次數(shù)匹配
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
-- 如果重入次數(shù)仍然大于0,續(xù)約過期時間
if (counter > 0) then
    redis.call('pexpire', KEYS[1], ARGV[2]);
    -- 返回解說失敗
    return 0;
else
    -- 表示重入次數(shù)已經(jīng)為0了,刪除鎖的key
    redis.call('del', KEYS[1]);
    -- 使用publish發(fā)布一個消息,其他訂閱了的客戶端收到消息,就說明解鎖成功了餓、然后可以重新獲取鎖了
    redis.call('publish', KEYS[2], ARGV[1]);
    -- 返回1 表示解鎖成功
    return 1;
end ;
return nil;

其實就是在解鎖的時候,已經(jīng)解鎖了直接返回成功,可重入次數(shù)沒有到0,將會解鎖失敗,直到可重入次數(shù)重新減到0后,開始刪除鎖的key.

并且此時會使用publish發(fā)送一個消息在渠道上,訂閱者們訂閱到了,就說明鎖已經(jīng)被釋放了,然后可以從重新獲取鎖了。

3、小結(jié)

Redisson實現(xiàn)分布式鎖,就是使用lua腳本保證原子性和互斥性的。每次都判斷是不是自己持有鎖,才進行操作,這就保證了同一性。

在加鎖時使用incrby對key對應的value值進行自增,減鎖時自減實現(xiàn)鎖的可重入性。

使用redis的超時自動過期來保證鎖的容錯性,不會一直鎖死下去。所以鎖的最大續(xù)約時間是防止思索的一個有效的方法。

責任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2022-06-30 08:04:16

Redis分布式鎖Redisson

2021-07-06 08:37:29

Redisson分布式

2021-06-30 14:56:12

Redisson分布式公平鎖

2021-07-02 08:51:09

Redisson分布式鎖公平鎖

2021-11-11 17:40:08

WatchdogAndroid源碼分析

2021-07-01 09:42:08

Redisson分布式

2025-09-08 02:35:00

Redisson分布式鎖WatchDog

2021-06-27 21:24:55

RedissonJava數(shù)據(jù)

2021-07-03 17:45:57

分布式Redisson MultiLock

2022-12-27 07:39:28

RedisRedissonLettuce

2021-07-07 07:09:49

Redisson分布式鎖源碼

2010-11-02 16:25:55

DB2鎖機制

2021-11-29 00:18:30

Redis分布式

2021-09-17 07:51:24

RedissonRedis分布式

2021-06-28 10:51:55

Redisson分布式鎖Watchdog

2020-08-24 08:13:25

非公平鎖源碼

2012-05-31 02:54:07

HadoopJava

2022-08-04 08:45:50

Redisson分布式鎖工具

2021-12-06 14:52:08

動畫Android補間動畫

2024-08-30 10:40:12

點贊
收藏

51CTO技術(shù)棧公眾號

99久久亚洲精品| 偷拍自拍在线看| 久久av中文字幕片| 久久国产色av| 国产大学生视频| а√天堂8资源中文在线| 国产午夜亚洲精品理论片色戒| 国产精品久久久久影院日本| 永久免费看片视频教学| 大桥未久女教师av一区二区| 色婷婷av一区二区三区gif| 亚洲bbw性色大片| 亚洲va天堂va欧美ⅴa在线| 99精品视频免费观看视频| 亚洲三级免费看| 日韩欧美色视频| 成人勉费视频| 亚洲蜜桃精久久久久久久| 国产在线一区二区三区播放| 亚洲欧美日韩一区二区三区四区| 欧美国产另类| 亚洲一级免费视频| 黄页网站在线看| 欧美黑人巨大xxxxx| 亚洲精品乱码久久久久久| 快播亚洲色图| 国产超碰人人模人人爽人人添| 性感少妇一区| 欧美韩日一区二区| 2019男人天堂| 国产精伦一区二区三区| 欧美久久久影院| 人妻有码中文字幕| av网址在线免费观看| 久久综合狠狠综合久久综合88 | www视频在线观看| 国产精品第一页第二页第三页| 免费av一区二区三区| 精品国产一级片| 奇米影视7777精品一区二区| 2019最新中文字幕| 久久久久久久久久91| 日韩中文欧美| 亚洲天堂网站在线观看视频| 五十路六十路七十路熟婆| 亚洲图色一区二区三区| 91精品欧美一区二区三区综合在 | 77thz桃花论族在线观看| 国产精品午夜在线观看| 欧美重口乱码一区二区| 无码国产色欲xxxx视频| 国产91丝袜在线18| 1卡2卡3卡精品视频| 在线观看毛片视频| 青娱乐精品视频在线| 国产盗摄xxxx视频xxx69 | 欧美亚洲愉拍一区二区| 男女av免费观看| 涩涩视频在线免费看| 亚洲福利电影网| 国产一二三区在线播放| 日韩经典av| 亚洲一区二区四区蜜桃| 嫩草影院中文字幕| 青春草视频在线观看| 日韩毛片一二三区| 先锋影音男人资源| 含羞草www国产在线视频| 亚洲少妇最新在线视频| 国产高清免费在线| 91蜜桃在线视频| 亚洲影院久久精品| 亚洲精品蜜桃久久久久久| 欧美家庭影院| 午夜精品爽啪视频| 亚洲美免无码中文字幕在线| 日韩伦理在线一区| 一本一本大道香蕉久在线精品| 无人在线观看的免费高清视频| 免费观看成人性生生活片| 色欧美片视频在线观看 | 黄色一级片中国| 欧美日韩亚洲国产精品| 97国产精品人人爽人人做| 国产午夜精品久久久久| 美女国产一区二区三区| 91视频8mav| 韩国av永久免费| 91丨九色porny丨蝌蚪| 欧美日韩视频在线一区二区观看视频| 国产青青草在线| 日韩毛片视频在线看| 男人天堂a在线| 成人性生活视频| 欧美性生活影院| 成人在线观看一区二区| 亚洲激情77| 日韩中文字幕免费| 国产系列精品av| 日本美女一区二区三区视频| 亚洲综合在线做性| 午夜性色福利影院| 国产精品久久综合| 97在线国产视频| 亚洲电影有码| 欧美tk丨vk视频| 一区二区伦理片| 欧美日韩视频| 国产成人精品在线观看| 国产黄色片免费| 久久婷婷色综合| 欧美 国产 精品| 中文字幕av一区二区三区佐山爱| 日韩欧美亚洲一区二区| 谁有免费的黄色网址| 欧美日韩亚洲三区| 国产精品99一区| 成人午夜精品福利免费| 国产精品天美传媒| 给我免费播放片在线观看| 国产91在线精品| 亚洲精品乱码久久久久久金桔影视 | 一区二区视频国产| 国产高清视频色在线www| 欧美日韩电影一区| 熟妇高潮精品一区二区三区| 综合av在线| 国产精品18久久久久久首页狼| 亚洲xxxx天美| 亚洲欧洲精品一区二区三区| 那种视频在线观看| 国产美女撒尿一区二区| 欧美另类极品videosbestfree| 欧美 亚洲 另类 激情 另类| 91色综合久久久久婷婷| 精品人妻人人做人人爽| 91精品国产自产观看在线| 国产午夜精品一区理论片飘花| 国产精品美女久久久久av爽| 成人免费观看视频| 精品免费久久久久久久| 国产精品xnxxcom| 日韩中文字幕网站| 中文无码av一区二区三区| 久久综合给合久久狠狠狠97色69| 国产精品成人久久电影| 成人av影音| 欧美极品在线视频| 国产av无码专区亚洲a∨毛片| 成人免费一区二区三区视频| 日韩大片一区二区| 日韩精品91| 国产精品爽黄69| 91亚洲欧美| 欧美色男人天堂| 91免费在线看片| 免费成人在线影院| 亚洲欧洲日本国产| 91精品国产66| www亚洲欧美| 6—12呦国产精品| 亚洲欧美自拍偷拍色图| 婷婷激情5月天| 亚洲欧美亚洲| 国产女人水真多18毛片18精品| 免费不卡av| 精品日韩欧美在线| 国产精品6666| 99久久久久久| 成人在线免费观看av| 日韩大片在线免费观看| 欧美影院久久久| 国产有码在线| 欧美女孩性生活视频| 成人免费黄色小视频| 国产福利视频一区二区三区| www..com日韩| 九色精品国产蝌蚪| 国产精品丝袜白浆摸在线| 快射视频在线观看| 精品毛片乱码1区2区3区| 中日韩黄色大片| 国产调教视频一区| 午夜剧场高清版免费观看 | 久久亚洲精华国产精华液 | 伊人久久综合一区二区| 亚洲一区二区国产| 国产露脸国语对白在线| 亚洲自拍偷拍欧美| 级毛片内射视频| 国产成人亚洲综合a∨猫咪| 无码精品a∨在线观看中文| 欧美精品一区二区三区精品| 亚洲va欧美va国产综合久久| 538在线观看| 在线电影中文日韩| 成人激情四射网| 91久久香蕉国产日韩欧美9色| 亚洲天堂网av在线| 波波电影院一区二区三区| 天天操天天爱天天爽| 激情国产一区| 亚洲7777| 首页亚洲中字| 91在线视频成人| 精品国产第一福利网站| 欧美美最猛性xxxxxx| 国产永久免费高清在线观看视频| 精品国产区一区| 亚洲天堂视频在线| 精品久久久一区| 永久久久久久久| 国产亚洲短视频| 国产伦精品一区二区三区精品| 美女一区二区久久| 久久成人免费观看| 中文字幕午夜精品一区二区三区| 欧美日韩在线一区二区三区| 一区二区三区免费在线看| 国产热re99久久6国产精品| 亚洲v.com| 久久久久亚洲精品成人网小说| 求av网址在线观看| 亚洲精品自拍偷拍| 亚洲第一页综合| 欧美片网站yy| wwwwww在线观看| 精品国产福利在线| 久久久久久久蜜桃| 亚洲欧美在线另类| 国产黄色大片免费看| 99久久婷婷国产| 极品白嫩的小少妇| 国产精品综合av一区二区国产馆| 日韩大片一区二区| 日韩高清在线不卡| 国产精品无码av无码| 男人的天堂成人在线| 黄色一级在线视频| 欧美国产激情| 91精品国产毛片武则天| 婷婷精品进入| 在线精品日韩| 久久精品一区二区不卡| 一区二区视频在线免费| 日韩成人a**站| 亚洲免费久久| 成人精品久久| 亚洲午夜激情| 欧美aaaa视频| 亚洲乱码一区二区三区三上悠亚| 欧美美女一区| 亚洲一二区在线| 日韩在线观看一区| 宅男一区二区三区| 国产精品毛片久久| 潘金莲一级淫片aaaaaa播放1| 亚洲经典一区| 欧美少妇在线观看| 国模吧视频一区| 日本丰满少妇xxxx| 性欧美精品高清| 一区二区三区国产免费| 毛片一区二区三区| 波多野结衣网页| av毛片久久久久**hd| 少妇毛片一区二区三区| 久久精品欧美一区二区三区麻豆 | 成人免费观看在线视频| 日韩一区二区电影在线| 免费av一级片| 亚洲欧美国产va在线影院| 国产区av在线| 久久香蕉国产线看观看网| 在线heyzo| 8x海外华人永久免费日韩内陆视频| 原纱央莉成人av片| 国产精自产拍久久久久久蜜| 99综合久久| 国产一区高清视频| 不卡在线一区二区| 国产卡一卡二在线| 在线播放日韩| 人人爽人人av| 粉嫩av一区二区三区粉嫩| 亚洲一区二区三区无码久久| 国产片一区二区三区| 激情无码人妻又粗又大| 亚洲午夜影视影院在线观看| 一级成人黄色片| 7777女厕盗摄久久久| 黄色av中文字幕| 亚洲精品午夜精品| 日本高清视频在线播放| 国内自拍欧美激情| 成人黄色图片网站| 国产精品久久国产精品| 欧美一区二区三区激情视频| www.国产二区| 久久亚洲图片| 国产清纯白嫩初高中在线观看性色| 久久久久国产精品麻豆| 四虎免费在线视频| 色八戒一区二区三区| 午夜精品久久久久久久91蜜桃| 伊人久久综合97精品| 久草在线视频福利| 国产精品久久综合av爱欲tv| 国产精品毛片av| 一区二区三区四区五区精品| 国产欧美欧美| 中文字幕第10页| 中文字幕久久午夜不卡| 在线观看免费国产视频| 日韩欧美一级二级三级 | 欧美精品亚州精品| www.久久.com| 欧美高清视频一区| 影音先锋中文字幕一区| 欧美日韩理论片| 国产色91在线| 欧美在线观看不卡| 亚洲第一福利网站| 污污片在线免费视频| 国产精品吴梦梦| 亚洲午夜久久| 黄色网页免费在线观看| 成人美女在线视频| 欧美日韩亚洲国产另类| 欧美日韩精品电影| 成年午夜在线| 日韩免费在线视频| 日韩激情网站| 精品无码国产一区二区三区av| 国产伦精品一区二区三区免费迷| 9.1片黄在线观看| 日本精品视频一区二区三区| 欧洲亚洲在线| 青青久久av北条麻妃海外网| 久久这里只有精品一区二区| 免费看日本黄色| 国产不卡在线一区| 人妻少妇精品一区二区三区| 在线播放一区二区三区| 中文字幕在线视频区| 国产精品免费福利| 精品国产精品| 97公开免费视频| 日本一区二区三级电影在线观看 | 91精品国自产在线| 日韩欧美在线视频免费观看| 青青草在线免费视频| 日本久久久久久久久| 视频精品在线观看| 国产真人无码作爱视频免费| 国产女人水真多18毛片18精品视频| 亚洲国产成人精品女人久久| 国产一区二区三区精品久久久 | 最新日本中文字幕| 亚洲图片一区二区| 蜜臀av中文字幕| 91av在线看| blacked蜜桃精品一区| 超碰在线97免费| 亚洲欧美日韩国产一区二区三区| 国产丰满美女做爰| 久久久久久久久久国产| 欧美黄色录像| www日韩在线观看| 中文字幕一区av| 精品久久久久久亚洲综合网站| 欧美激情国产精品| 欧美日韩导航| 天美星空大象mv在线观看视频| 亚洲天堂久久久久久久| 亚洲欧美另类一区| 4k岛国日韩精品**专区| 欧美在线电影| 欧美熟妇精品一区二区| 精品人伦一区二区三区蜜桃免费| 精品欧美不卡一区二区在线观看 | 亚洲自拍第二页| 欧美xxxx做受欧美| 欧美精品中文| 日韩高清第一页| 亚洲一区二区三区不卡国产欧美| 日韩欧美在线观看一区二区| 国产欧美婷婷中文| 极品中文字幕一区| 色综合99久久久无码国产精品| 日韩一区二区三区在线视频| 欧亚av在线| 性欧美18一19内谢| 99久久精品情趣| 一二三区在线播放| 高清在线视频日韩欧美| 成人免费在线播放| 一级黄色片毛片| 欧美年轻男男videosbes| 乱人伦视频在线|