<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          本地緩存之王,Caffeine保姆級(jí)教程

          共 23639字,需瀏覽 48分鐘

           ·

          2023-10-13 05:36

          一、Caffeine介紹

          1、緩存介紹

          緩存(Cache)在代碼世界中無(wú)處不在。從底層的CPU多級(jí)緩存,到客戶(hù)端的頁(yè)面緩存,處處都存在著緩存的身影。緩存從本質(zhì)上來(lái)說(shuō),是一種空間換時(shí)間的手段,通過(guò)對(duì)數(shù)據(jù)進(jìn)行一定的空間安排,使得下次進(jìn)行數(shù)據(jù)訪問(wèn)時(shí)起到加速的效果。

          就Java而言,其常用的緩存解決方案有很多,例如數(shù)據(jù)庫(kù)緩存框架EhCache,分布式緩存Memcached等,這些緩存方案實(shí)際上都是為了提升吞吐效率,避免持久層壓力過(guò)大。

          對(duì)于常見(jiàn)緩存類(lèi)型而言,可以分為本地緩存以及分布式緩存兩種,Caffeine就是一種優(yōu)秀的本地緩存,而Redis可以用來(lái)做分布式緩存

          2、Caffeine介紹

          Caffeine官方:

          • https://github.com/ben-manes/caffeine

          Caffeine是基于Java 1.8的高性能本地緩存庫(kù),由Guava改進(jìn)而來(lái),而且在Spring5開(kāi)始的默認(rèn)緩存實(shí)現(xiàn)就將Caffeine代替原來(lái)的Google Guava,官方說(shuō)明指出,其緩存命中率已經(jīng)接近最優(yōu)值。實(shí)際上Caffeine這樣的本地緩存和ConcurrentMap很像,即支持并發(fā),并且支持O(1)時(shí)間復(fù)雜度的數(shù)據(jù)存取。二者的主要區(qū)別在于:

          • ConcurrentMap將存儲(chǔ)所有存入的數(shù)據(jù),直到你顯式將其移除;
          • Caffeine將通過(guò)給定的配置,自動(dòng)移除“不常用”的數(shù)據(jù),以保持內(nèi)存的合理占用。

          因此,一種更好的理解方式是:Cache是一種帶有存儲(chǔ)和移除策略的Map。

          二、Caffeine基礎(chǔ)

          使用Caffeine,需要在工程中引入如下依賴(lài)

          <dependency>
              <groupId>com.github.ben-manes.caffeine</groupId>
              <artifactId>caffeine</artifactId>
              <!--https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeinez找最新版-->
              <version>3.0.5</version>
          </dependency>

          1、緩存加載策略

          1.1 Cache手動(dòng)創(chuàng)建

          最普通的一種緩存,無(wú)需指定加載方式,需要手動(dòng)調(diào)用put()進(jìn)行加載。需要注意的是put()方法對(duì)于已存在的key將進(jìn)行覆蓋,這點(diǎn)和Map的表現(xiàn)是一致的。在獲取緩存值時(shí),如果想要在緩存值不存在時(shí),原子地將值寫(xiě)入緩存,則可以調(diào)用get(key, k -> value)方法,該方法將避免寫(xiě)入競(jìng)爭(zhēng)。調(diào)用invalidate()方法,將手動(dòng)移除緩存。

          在多線程情況下,當(dāng)使用get(key, k -> value)時(shí),如果有另一個(gè)線程同時(shí)調(diào)用本方法進(jìn)行競(jìng)爭(zhēng),則后一線程會(huì)被阻塞,直到前一線程更新緩存完成;而若另一線程調(diào)用getIfPresent()方法,則會(huì)立即返回null,不會(huì)被阻塞。

          Cache<Object, Object> cache = Caffeine.newBuilder()
                    //初始數(shù)量
                    .initialCapacity(10)
                    //最大條數(shù)
                    .maximumSize(10)
                    //expireAfterWrite和expireAfterAccess同時(shí)存在時(shí),以expireAfterWrite為準(zhǔn)
                    //最后一次寫(xiě)操作后經(jīng)過(guò)指定時(shí)間過(guò)期
                    .expireAfterWrite(1, TimeUnit.SECONDS)
                    //最后一次讀或?qū)懖僮骱蠼?jīng)過(guò)指定時(shí)間過(guò)期
                    .expireAfterAccess(1, TimeUnit.SECONDS)
                    //監(jiān)聽(tīng)緩存被移除
                    .removalListener((key, val, removalCause) -> { })
                    //記錄命中
                    .recordStats()
                    .build();

            cache.put("1","張三");
            //張三
            System.out.println(cache.getIfPresent("1"));
            //存儲(chǔ)的是默認(rèn)值
            System.out.println(cache.get("2",o -> "默認(rèn)值"));
          1.2 Loading Cache自動(dòng)創(chuàng)建

          LoadingCache是一種自動(dòng)加載的緩存。其和普通緩存不同的地方在于,當(dāng)緩存不存在/緩存已過(guò)期時(shí),若調(diào)用get()方法,則會(huì)自動(dòng)調(diào)用CacheLoader.load()方法加載最新值。調(diào)用getAll()方法將遍歷所有的key調(diào)用get(),除非實(shí)現(xiàn)了CacheLoader.loadAll()方法。使用LoadingCache時(shí),需要指定CacheLoader,并實(shí)現(xiàn)其中的load()方法供緩存缺失時(shí)自動(dòng)加載。

          在多線程情況下,當(dāng)兩個(gè)線程同時(shí)調(diào)用get(),則后一線程將被阻塞,直至前一線程更新緩存完成。

          LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
                  //創(chuàng)建緩存或者最近一次更新緩存后經(jīng)過(guò)指定時(shí)間間隔,刷新緩存;refreshAfterWrite僅支持LoadingCache
                  .refreshAfterWrite(10, TimeUnit.SECONDS)
                  .expireAfterWrite(10, TimeUnit.SECONDS)
                  .expireAfterAccess(10, TimeUnit.SECONDS)
                  .maximumSize(10)
                  //根據(jù)key查詢(xún)數(shù)據(jù)庫(kù)里面的值,這里是個(gè)lamba表達(dá)式
                  .build(key -> new Date().toString());
          1.3 Async Cache異步獲取

          AsyncCache是Cache的一個(gè)變體,其響應(yīng)結(jié)果均為CompletableFuture,通過(guò)這種方式,AsyncCache對(duì)異步編程模式進(jìn)行了適配。默認(rèn)情況下,緩存計(jì)算使用ForkJoinPool.commonPool()作為線程池,如果想要指定線程池,則可以覆蓋并實(shí)現(xiàn)Caffeine.executor(Executor)方法。synchronous()提供了阻塞直到異步緩存生成完畢的能力,它將以Cache進(jìn)行返回。

          在多線程情況下,當(dāng)兩個(gè)線程同時(shí)調(diào)用get(key, k -> value),則會(huì)返回同一個(gè)CompletableFuture對(duì)象。由于返回結(jié)果本身不進(jìn)行阻塞,可以根據(jù)業(yè)務(wù)設(shè)計(jì)自行選擇阻塞等待或者非阻塞。

          AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
                  //創(chuàng)建緩存或者最近一次更新緩存后經(jīng)過(guò)指定時(shí)間間隔刷新緩存;僅支持LoadingCache
                  .refreshAfterWrite(1, TimeUnit.SECONDS)
                  .expireAfterWrite(1, TimeUnit.SECONDS)
                  .expireAfterAccess(1, TimeUnit.SECONDS)
                  .maximumSize(10)
                  //根據(jù)key查詢(xún)數(shù)據(jù)庫(kù)里面的值
                  .buildAsync(key -> {
                      Thread.sleep(1000);
                      return new Date().toString();
                  });

          //異步緩存返回的是CompletableFuture
          CompletableFuture<String> future = asyncLoadingCache.get("1");
          future.thenAccept(System.out::println);

          2、驅(qū)逐策略

          驅(qū)逐策略在創(chuàng)建緩存的時(shí)候進(jìn)行指定。常用的有基于容量的驅(qū)逐和基于時(shí)間的驅(qū)逐。

          基于容量的驅(qū)逐需要指定緩存容量的最大值,當(dāng)緩存容量達(dá)到最大時(shí),Caffeine將使用LRU策略對(duì)緩存進(jìn)行淘汰;基于時(shí)間的驅(qū)逐策略如字面意思,可以設(shè)置在最后訪問(wèn)/寫(xiě)入一個(gè)緩存經(jīng)過(guò)指定時(shí)間后,自動(dòng)進(jìn)行淘汰。

          驅(qū)逐策略可以組合使用,任意驅(qū)逐策略生效后,該緩存條目即被驅(qū)逐。

          • LRU 最近最少使用,淘汰最長(zhǎng)時(shí)間沒(méi)有被使用的頁(yè)面。
          • LFU 最不經(jīng)常使用,淘汰一段時(shí)間內(nèi)使用次數(shù)最少的頁(yè)面
          • FIFO 先進(jìn)先出

          Caffeine有4種緩存淘汰設(shè)置

          • 大小 (LFU算法進(jìn)行淘汰)
          • 權(quán)重 (大小與權(quán)重 只能二選一)
          • 時(shí)間
          • 引用 (不常用,本文不介紹)
          @Slf4j
          public class CacheTest {
              /**
               * 緩存大小淘汰
               */

              @Test
              public void maximumSizeTest() throws InterruptedException {
                  Cache<Integer, Integer> cache = Caffeine.newBuilder()
                          //超過(guò)10個(gè)后會(huì)使用W-TinyLFU算法進(jìn)行淘汰
                          .maximumSize(10)
                          .evictionListener((key, val, removalCause) -> {
                              log.info("淘汰緩存:key:{} val:{}", key, val);
                          })
                          .build();

                  for (int i = 1; i < 20; i++) {
                      cache.put(i, i);
                  }
                  Thread.sleep(500);//緩存淘汰是異步的

                  // 打印還沒(méi)被淘汰的緩存
                  System.out.println(cache.asMap());
              }

              /**
               * 權(quán)重淘汰
               */

              @Test
              public void maximumWeightTest() throws InterruptedException {
                  Cache<Integer, Integer> cache = Caffeine.newBuilder()
                          //限制總權(quán)重,若所有緩存的權(quán)重加起來(lái)>總權(quán)重就會(huì)淘汰權(quán)重小的緩存
                          .maximumWeight(100)
                          .weigher((Weigher<Integer, Integer>) (key, value) -> key)
                          .evictionListener((key, val, removalCause) -> {
                              log.info("淘汰緩存:key:{} val:{}", key, val);
                          })
                          .build();

                  //總權(quán)重其實(shí)是=所有緩存的權(quán)重加起來(lái)
                  int maximumWeight = 0;
                  for (int i = 1; i < 20; i++) {
                      cache.put(i, i);
                      maximumWeight += i;
                  }
                  System.out.println("總權(quán)重=" + maximumWeight);
                  Thread.sleep(500);//緩存淘汰是異步的

                  // 打印還沒(méi)被淘汰的緩存
                  System.out.println(cache.asMap());
              }


              /**
               * 訪問(wèn)后到期(每次訪問(wèn)都會(huì)重置時(shí)間,也就是說(shuō)如果一直被訪問(wèn)就不會(huì)被淘汰)
               */

              @Test
              public void expireAfterAccessTest() throws InterruptedException {
                  Cache<Integer, Integer> cache = Caffeine.newBuilder()
                          .expireAfterAccess(1, TimeUnit.SECONDS)
                          //可以指定調(diào)度程序來(lái)及時(shí)刪除過(guò)期緩存項(xiàng),而不是等待Caffeine觸發(fā)定期維護(hù)
                          //若不設(shè)置scheduler,則緩存會(huì)在下一次調(diào)用get的時(shí)候才會(huì)被動(dòng)刪除
                          .scheduler(Scheduler.systemScheduler())
                          .evictionListener((key, val, removalCause) -> {
                              log.info("淘汰緩存:key:{} val:{}", key, val);

                          })
                          .build();
                  cache.put(12);
                  System.out.println(cache.getIfPresent(1));
                  Thread.sleep(3000);
                  System.out.println(cache.getIfPresent(1));//null
              }

              /**
               * 寫(xiě)入后到期
               */

              @Test
              public void expireAfterWriteTest() throws InterruptedException {
                  Cache<Integer, Integer> cache = Caffeine.newBuilder()
                          .expireAfterWrite(1, TimeUnit.SECONDS)
                          //可以指定調(diào)度程序來(lái)及時(shí)刪除過(guò)期緩存項(xiàng),而不是等待Caffeine觸發(fā)定期維護(hù)
                          //若不設(shè)置scheduler,則緩存會(huì)在下一次調(diào)用get的時(shí)候才會(huì)被動(dòng)刪除
                          .scheduler(Scheduler.systemScheduler())
                          .evictionListener((key, val, removalCause) -> {
                              log.info("淘汰緩存:key:{} val:{}", key, val);
                          })
                          .build();
                  cache.put(12);
                  Thread.sleep(3000);
                  System.out.println(cache.getIfPresent(1));//null
              }
          }

          3、刷新機(jī)制

          refreshAfterWrite()表示x秒后自動(dòng)刷新緩存的策略可以配合淘汰策略使用,注意的是刷新機(jī)制只支持LoadingCache和AsyncLoadingCache

          private static int NUM = 0;

          @Test
          public void refreshAfterWriteTest() throws InterruptedException {
              LoadingCache<Integer, Integer> cache = Caffeine.newBuilder()
                      .refreshAfterWrite(1, TimeUnit.SECONDS)
                      //模擬獲取數(shù)據(jù),每次獲取就自增1
                      .build(integer -> ++NUM);

              //獲取ID=1的值,由于緩存里還沒(méi)有,所以會(huì)自動(dòng)放入緩存
              System.out.println(cache.get(1));// 1

              // 延遲2秒后,理論上自動(dòng)刷新緩存后取到的值是2
              // 但其實(shí)不是,值還是1,因?yàn)閞efreshAfterWrite并不是設(shè)置了n秒后重新獲取就會(huì)自動(dòng)刷新
              // 而是x秒后&&第二次調(diào)用getIfPresent的時(shí)候才會(huì)被動(dòng)刷新
              Thread.sleep(2000);
              System.out.println(cache.getIfPresent(1));// 1

              //此時(shí)才會(huì)刷新緩存,而第一次拿到的還是舊值
              System.out.println(cache.getIfPresent(1));// 2
          }

          4、統(tǒng)計(jì)

          LoadingCache<String, String> cache = Caffeine.newBuilder()
                  //創(chuàng)建緩存或者最近一次更新緩存后經(jīng)過(guò)指定時(shí)間間隔,刷新緩存;refreshAfterWrite僅支持LoadingCache
                  .refreshAfterWrite(1, TimeUnit.SECONDS)
                  .expireAfterWrite(1, TimeUnit.SECONDS)
                  .expireAfterAccess(1, TimeUnit.SECONDS)
                  .maximumSize(10)
                  //開(kāi)啟記錄緩存命中率等信息
                  .recordStats()
                  //根據(jù)key查詢(xún)數(shù)據(jù)庫(kù)里面的值
                  .build(key -> {
                      Thread.sleep(1000);
                      return new Date().toString();
                  });


          cache.put("1""shawn");
          cache.get("1");

          /*
           * hitCount :命中的次數(shù)
           * missCount:未命中次數(shù)
           * requestCount:請(qǐng)求次數(shù)
           * hitRate:命中率
           * missRate:丟失率
           * loadSuccessCount:成功加載新值的次數(shù)
           * loadExceptionCount:失敗加載新值的次數(shù)
           * totalLoadCount:總條數(shù)
           * loadExceptionRate:失敗加載新值的比率
           * totalLoadTime:全部加載時(shí)間
           * evictionCount:丟失的條數(shù)
           */

          System.out.println(cache.stats());

          5、總結(jié)

          上述一些策略在創(chuàng)建時(shí)都可以進(jìn)行自由組合,一般情況下有兩種方法

          • 設(shè)置 maxSizerefreshAfterWrite,不設(shè)置 expireAfterWrite/expireAfterAccess,設(shè)置expireAfterWrite當(dāng)緩存過(guò)期時(shí)會(huì)同步加鎖獲取緩存,所以設(shè)置expireAfterWrite時(shí)性能較好,但是某些時(shí)候會(huì)取舊數(shù)據(jù),適合允許取到舊數(shù)據(jù)的場(chǎng)景
          • 設(shè)置 maxSizeexpireAfterWrite/expireAfterAccess,不設(shè)置 refreshAfterWrite 數(shù)據(jù)一致性好,不會(huì)獲取到舊數(shù)據(jù),但是性能沒(méi)那么好(對(duì)比起來(lái)),適合獲取數(shù)據(jù)時(shí)不耗時(shí)的場(chǎng)景

          三、SpringBoot整合Caffeine

          1、@Cacheable相關(guān)注解

          1.1 相關(guān)依賴(lài)

          如果要使用@Cacheable注解,需要引入相關(guān)依賴(lài),并在任一配置類(lèi)文件上添加@EnableCaching注解

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-cache</artifactId>
          </dependency>
          1.2 常用注解
          • @Cacheable:表示該方法支持緩存。當(dāng)調(diào)用被注解的方法時(shí),如果對(duì)應(yīng)的鍵已經(jīng)存在緩存,則不再執(zhí)行方法體,而從緩存中直接返回。當(dāng)方法返回null時(shí),將不進(jìn)行緩存操作。
          • @CachePut:表示執(zhí)行該方法后,其值將作為最新結(jié)果更新到緩存中,每次都會(huì)執(zhí)行該方法。
          • @CacheEvict:表示執(zhí)行該方法后,將觸發(fā)緩存清除操作。
          • @Caching:用于組合前三個(gè)注解,例如:
          @Caching(cacheable = @Cacheable("CacheConstants.GET_USER"),
                   evict = {@CacheEvict("CacheConstants.GET_DYNAMIC",allEntries = true)}
          public User find(Integer id) {
              return null;
          }
          1.3 常用注解屬性
          • cacheNames/value:緩存組件的名字,即cacheManager中緩存的名稱(chēng)。
          • key:緩存數(shù)據(jù)時(shí)使用的key。默認(rèn)使用方法參數(shù)值,也可以使用SpEL表達(dá)式進(jìn)行編寫(xiě)。
          • keyGenerator:和key二選一使用。
          • cacheManager:指定使用的緩存管理器。
          • condition:在方法執(zhí)行開(kāi)始前檢查,在符合condition的情況下,進(jìn)行緩存
          • unless:在方法執(zhí)行完成后檢查,在符合unless的情況下,不進(jìn)行緩存
          • sync:是否使用同步模式。若使用同步模式,在多個(gè)線程同時(shí)對(duì)一個(gè)key進(jìn)行l(wèi)oad時(shí),其他線程將被阻塞。
          1.4 緩存同步模式

          sync開(kāi)啟或關(guān)閉,在Cache和LoadingCache中的表現(xiàn)是不一致的:

          • Cache中,sync表示是否需要所有線程同步等待
          • LoadingCache中,sync表示在讀取不存在/已驅(qū)逐的key時(shí),是否執(zhí)行被注解方法

          2、實(shí)戰(zhàn)

          2.1 引入依賴(lài)
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-cache</artifactId>
          </dependency>

          <dependency>
              <groupId>com.github.ben-manes.caffeine</groupId>
              <artifactId>caffeine</artifactId>
          </dependency>
          2.2 緩存常量CacheConstants

          創(chuàng)建緩存常量類(lèi),把公共的常量提取一層,復(fù)用,這里也可以通過(guò)配置文件加載這些數(shù)據(jù),例如@ConfigurationProperties@Value

          public class CacheConstants {
              /**
               * 默認(rèn)過(guò)期時(shí)間(配置類(lèi)中我使用的時(shí)間單位是秒,所以這里如 3*60 為3分鐘)
               */

              public static final int DEFAULT_EXPIRES = 3 * 60;
              public static final int EXPIRES_5_MIN = 5 * 60;
              public static final int EXPIRES_10_MIN = 10 * 60;

              public static final String GET_USER = "GET:USER";
              public static final String GET_DYNAMIC = "GET:DYNAMIC";

          }
          2.3 緩存配置類(lèi)CacheConfig
          @Configuration
          @EnableCaching
          public class CacheConfig {
              /**
               * Caffeine配置說(shuō)明:
               * initialCapacity=[integer]: 初始的緩存空間大小
               * maximumSize=[long]: 緩存的最大條數(shù)
               * maximumWeight=[long]: 緩存的最大權(quán)重
               * expireAfterAccess=[duration]: 最后一次寫(xiě)入或訪問(wèn)后經(jīng)過(guò)固定時(shí)間過(guò)期
               * expireAfterWrite=[duration]: 最后一次寫(xiě)入后經(jīng)過(guò)固定時(shí)間過(guò)期
               * refreshAfterWrite=[duration]: 創(chuàng)建緩存或者最近一次更新緩存后經(jīng)過(guò)固定的時(shí)間間隔,刷新緩存
               * weakKeys: 打開(kāi)key的弱引用
               * weakValues:打開(kāi)value的弱引用
               * softValues:打開(kāi)value的軟引用
               * recordStats:開(kāi)發(fā)統(tǒng)計(jì)功能
               * 注意:
               * expireAfterWrite和expireAfterAccess同事存在時(shí),以expireAfterWrite為準(zhǔn)。
               * maximumSize和maximumWeight不可以同時(shí)使用
               * weakValues和softValues不可以同時(shí)使用
               */

              @Bean
              public CacheManager cacheManager() {
                  SimpleCacheManager cacheManager = new SimpleCacheManager();
                  List<CaffeineCache> list = new ArrayList<>();
                  //循環(huán)添加枚舉類(lèi)中自定義的緩存,可以自定義
                  for (CacheEnum cacheEnum : CacheEnum.values()) {
                      list.add(new CaffeineCache(cacheEnum.getName(),
                              Caffeine.newBuilder()
                                      .initialCapacity(50)
                                      .maximumSize(1000)
                                      .expireAfterAccess(cacheEnum.getExpires(), TimeUnit.SECONDS)
                                      .build()));
                  }
                  cacheManager.setCaches(list);
                  return cacheManager;
              }
          }
          2.4 調(diào)用緩存

          這里要注意的是Cache和@Transactional一樣也使用了代理,類(lèi)內(nèi)調(diào)用將失效

          /**
           * value:緩存key的前綴。
           * key:緩存key的后綴。
           * sync:設(shè)置如果緩存過(guò)期是不是只放一個(gè)請(qǐng)求去請(qǐng)求數(shù)據(jù)庫(kù),其他請(qǐng)求阻塞,默認(rèn)是false(根據(jù)個(gè)人需求)。
           * unless:不緩存空值,這里不使用,會(huì)報(bào)錯(cuò)
           * 查詢(xún)用戶(hù)信息類(lèi)
           * 如果需要加自定義字符串,需要用單引號(hào)
           * 如果查詢(xún)?yōu)閚ull,也會(huì)被緩存
           */

          @Cacheable(value = CacheConstants.GET_USER,key = "'user'+#userId",sync = true)
          @CacheEvict
          public UserEntity getUserByUserId(Integer userId){
              UserEntity userEntity = userMapper.findById(userId);
              System.out.println("查詢(xún)了數(shù)據(jù)庫(kù)");
              return userEntity;
          }

          來(lái)源:blog.csdn.net/lemon_TT/article/

          details/122905113

              
              

          程序汪資料鏈接

          程序汪接的7個(gè)私活都在這里,經(jīng)驗(yàn)整理

          Java項(xiàng)目分享  最新整理全集,找項(xiàng)目不累啦 07版

          堪稱(chēng)神級(jí)的Spring Boot手冊(cè),從基礎(chǔ)入門(mén)到實(shí)戰(zhàn)進(jìn)階

          臥槽!字節(jié)跳動(dòng)《算法中文手冊(cè)》火了,完整版 PDF 開(kāi)放下載!

          臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開(kāi)放下載!

          字節(jié)跳動(dòng)總結(jié)的設(shè)計(jì)模式 PDF 火了,完整版開(kāi)放下載!

          歡迎添加程序汪個(gè)人微信 itwang007  進(jìn)粉絲群或圍觀朋友圈

          瀏覽 1202
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久一区二区三区四区五区 | 美女国产网站 | 激情乱伦视频 | 操老女人视频在线观看 | 日韩成人三级 |