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

K8s Informer 是如何保證事件不丟失的?

云計算 云原生
k8s 是如何 "監視" 資源對象,以確保其始終保持我們聲明的狀態的呢?答案就是 -- Controller。除了組件中的 kube-controller-manager,我們可以編寫自己的 Controller,也叫自定義控制器(為了方便下文統稱為自定義 Controller)。

1、資源 Controller 主要作用

我們知道 k8s 里重要概念之一就是 聲明式 API,比如 kubectl apply 就是聲明式 API的實現。

效果就是資源對象的運行狀態要與我們聲明的一致。比如kubectl apply 一個 deployment 的 yml,他要求的狀態就是: 該 deployment 成功運行。

那么問題來了,k8s 是如何 "監視" 資源對象,以確保其始終保持我們聲明的狀態的呢?答案就是 -- Controller。除了組件中的 kube-controller-manager,我們可以編寫自己的 Controller,也叫自定義控制器(為了方便下文統稱為自定義 Controller)。

接下來,我們就來剖析一下 Controller 背后的"秘密"

2、流程大覽

我們先看看社區給出的 Controller 的架構圖:

其中有幾個主要對象(結構體) -- Reflector、Informer、Indexer。Reflector 和 Indexer 我們會在之后的文章中會一一講解 。

本文主要是講解一下 Informer。

從圖中可以看到主要有9個步驟,這里我將9個步驟合并成3個大步驟:

(畫的有點丑-__- !!!)

大步驟1: Reflector 將資源對象的事件添加進 Delta FIFO queue 中。

這里先提前介紹一下 Delta FIFO queue。所謂 Delta 就是變化的意思,什么的變化呢?就是資源對象的變化。

即 資源對象的變化都會被添加到 Delta FIFO queue 中!這樣是不是就很好理解了。

大步驟2: Informer 將 Delta FIFO queue 中的對象數據 添加到本地 cache 中。

補充一下這個本地 cache 緩存的就是監聽資源對象的最新版。就是緩存的當前集群里面的資源信息。

大步驟3: 使用 workqueue 處理業務邏輯。

3、步驟分析

咱們結合社區給的編寫的 自定義Controller用例 來做源碼分析。這里使用的版本是 client-go v0.20.5。

用例中用到的是普通 informer,介紹的也是普通 informer。但很更多用的是sharedInformer,比如 manager、SharedInformerFactory 都是對普通 informer 的一個再封裝,本質的東西是一樣的。感興趣的話,后面再出介紹 sharedInformer、manager 的文章。

大步驟1

我們看到架構圖中間有一個分界線,將流程分割為上下兩半, 而上半部主要包括大步驟 1、2。

這兩個步驟其實是連在一起的,其入口代碼就是這一行 : informer.Run(),可以先不管這。

我們先看用例中Informer的初始化入口代碼。

NewIndexerInformer  的代碼如下:

再真正的Informer初始化,就是 newInformer :

注意第381行 就是 Delta FIFO 的初始化,架構圖中的 Delta FIFO queue 就是在這實例化的。

我們發現 newInformer 返回的 是一個 low-level Controller 接口。這個接口抽象的很簡單,就三個方法:

Run(stopCh <-chan struct{}):

運行邏輯。

HasSynced() bool :

數據同步完成與否

LastSyncResourceVersion() string:

資源最近一次的ResourceVersion

接下來我們看看三個方法是如何在 controller 中看到這實現的。

咱們直接跳轉到 419 行里面的代碼,low-level Controller 的初始化, 可以很方便就看到了 Run 方法的實現:

大部分代碼是 Reflector 的初始化。

第152行 啟動了一個協程,*r.Run* 就是 Reflector 的執行邏輯:List & Watch 資源對象,然后 Add object to Delta FIFO queue 。

咱們點擊跳轉,直接跳到 ListAndWatch 方法中, 雖然這兒的代碼又多又亂(忍不住吐槽),但它要的做的事很簡單,就四件事。這里我們就把重點代碼拷貝出來說。

第一件事

用你初始化好的 cache.ListWatch 對象 的ListFunc拉取資源對象,然后將對象同步到 Delta FIFO queue:

func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
......
......
list, paginatedResult, err = pager.List(context.Background(), options)
if isExpiredError(err) || isTooLargeResourceVersionError(err) {
  r.setIsLastSyncResourceVersionUnavailable(true) 
  // 拉取資源列表
  list, paginatedResult, err = pager.List(context.Background(), metav1.ListOptions{ResourceVersion: r.relistResourceVersion()})
}
.....
resourceVersion = listMetaInterface.GetResourceVersion()
.....
items, err := meta.ExtractList(list) // 轉換成對象
......
if err := r.syncWith(items, resourceVersion); err != nil { // 將拉取到資源對象都添加到 Delta FIFO queue
  return fmt.Errorf("unable to sync list result: %v", err)
}
......
r.setLastSyncResourceVersion(resourceVersion) // 設置最近一次的版本
......
}

這里再簡單說明一下,r.syncWith(items, resourceVersion) 主要是通過 Delta FIFO queue 中的 Replace() 來同步資源。 其中有一個關鍵的邏輯如下:

if !f.populated {
  f.populated = true
  f.initialPopulationCount = len(list) + queuedDeletions
}

f.populated =true 就是確定資源對象進入隊列的動作已經發生;f.initialPopulationCount 就是確定已經有多少對象在隊列中了。

然后我們看 informer HasSynced() 的底層邏輯:

func (f *DeltaFIFO) HasSynced() bool {
 f.lock.Lock()
 defer f.lock.Unlock()
 return f.populated && f.initialPopulationCount == 0
}

而 f.initialPopulationCount-- 發生在下文的 pop 中。

LastSyncResourceVersion() string 返回的版本,就是r.setLastSyncResourceVersion(resourceVersion) 設置的。

第二件事

再次同步資源。

go func() {
  resyncCh, cleanup := r.resyncChan()
  defer func() {
   cleanup() // Call the last one written into cleanup
  }()
  for {
   select {
   case <-resyncCh:
   case <-stopCh:
    return
   case <-cancelCh:
    return
   }
   if r.ShouldResync == nil || r.ShouldResync() {
    klog.V(4).Infof("%s: forcing resync", r.name)
    if err := r.store.Resync(); err != nil {
     resyncerrc <- err
     return
    }
   }
   cleanup()
   resyncCh, cleanup = r.resyncChan()
  }
 }()

用例代碼 中 cache.NewIndexerInformer() 會設置一個 resyncPeriod 參數就是在這起作用。

設置的是 0,所以這個協程會永遠阻塞在 case<-resyncCh。

這的詳細邏輯會放在之后講 Delta FIFO queue 的時候再講,簡單理解就是將 indexer 緩存的數據用同步到 Delta FIFO queue 中。

第三件事

用你初始化好的 cache.ListWatch 對象的 WatchFunc watch 對象。

這里的 watch 功能是底層就是 etcd 的 watch 特性功能,感興趣的同學可以自己了解一下,這里就不展開說明了。

w, err := r.listerWatcher.Watch(options)
if err != nil {   
  if utilnet.IsConnectionRefused(err) {
    <-r.initConnBackoffManager.Backoff().C()
    continue
  }
  return err
}

if err := r.watchHandler(start, w, &resourceVersion, resyncerrc, stopCh); err != nil {
......
......
}

第四件事

將watch到的對象,加入到Delta FIFO queue中。

// watchHandler watches w and keeps *resourceVersion up to date.
func (r *Reflector) watchHandler(start time.Time, w watch.Interface, resourceVersion *string, errc chan error, stopCh <-chan struct{}) error {
....
....
loop:
 for {
  select {
  case <-stopCh:
   return errorStopRequested
  case err := <-errc:
   return err
  case event, ok := <-w.ResultChan():
....
....
   switch event.Type {
   case watch.Added:
    err := r.store.Add(event.Object)
    if err != nil {
     utilruntime.HandleError(fmt.Errorf("%s: unable to add watch event object (%#v) to store: %v", r.name, event.Object, err))
    }
   case watch.Modified:
    err := r.store.Update(event.Object)
    if err != nil {
     utilruntime.HandleError(fmt.Errorf("%s: unable to update watch event object (%#v) to store: %v", r.name, event.Object, err))
    }
   case watch.Deleted:
    // TODO: Will any consumers need access to the "last known
    // state", which is passed in event.Object? If so, may need
    // to change this.
    err := r.store.Delete(event.Object)
    if err != nil {
     utilruntime.HandleError(fmt.Errorf("%s: unable to delete watch event object (%#v) from store: %v", r.name, event.Object, err))
    }
                       }
                       ......
                       r.setLastSyncResourceVersion(newResourceVersion) // 設置最近一次的版本
    
......
......
}

再簡單歸納一下,就兩件事:

  • 一開始,拉取資源列表然后加入到Delta FIFO queue
  • watch 資源對象的變化,加入到Delta FIFO queue

大步驟2

Indexerer 其實算是一個內存數據庫的抽象接口。其中Store當然就代表的存儲,其他的就是索引相關的。

// client-go/tools/cache/store.go
type cache struct {
 // cacheStorage bears the burden of thread safety for the cache
 cacheStorage ThreadSafeStore
 // keyFunc is used to make the key for objects stored in and retrieved from items, and
 // should be deterministic.
 keyFunc KeyFunc
}

cache 就是接口的實現,就是一個緩存。索引肯定是用作搜索的,其使用咱們下文在 作死的優化 那一節可以看到。

然后我們退回看 Run 方法截圖的第154 行代碼,看看第二大步驟的邏輯。

wait.Until 就是一個定時器,簡化成下面的代碼:

func Util(stopCh <-chan struct{}) {
    dur := 1 * time.Second
 timer := time.NewTimer(dur)
 defer timer.Stop()

 for {
          select {
         case <-stopCh:
         return
          case <-t.C():
         f()
         timer.Reset(dur)
          }
 }
}

執行的邏輯就是 c.processLoop:

其實代碼很容易理解,就是將隊列 (Delta FIFO queue)的item 彈出,然后調用處理函數執行ResourceEventHandler中的方法。

先看跳轉到 Pop 代碼:

func (f *DeltaFIFO) Pop(process PopProcessFunc) (interface{}, error) {
 ......
 ......
 id := f.queue[0]
 f.queue = f.queue[1:]
        if f.initialPopulationCount > 0 {
   f.initialPopulationCount-- // 同步數據減1
 }
 ......
 item, ok := f.items[id]
 ......
 err := process(item)
 ......
}

它的內容其實比較簡單,這里只羅列出了最主要的邏輯,相信大伙兒能看明白。而且也看到了上文提到確定同步的關鍵邏輯 f.initialPopulationCount--

也就是說只有 Delta FIFO queue 中的所有數據都同步到了 Indexer 中,informer 的數據同步才算完成。

然后咱們再來看 process ,就是 newInformer截圖 圖中我們第393行的 Process ,展開的方法:

Process: func(obj interface{}) error {
   for _, d := range obj.(Deltas) {
    switch d.Type {
    case Sync, Replaced, Added, Updated:
     if old, exists, err := clientState.Get(d.Object); err == nil && exists {
      if err := clientState.Update(d.Object); err != nil {
       return err
      }
      h.OnUpdate(old, d.Object)
     } else {
      if err := clientState.Add(d.Object); err != nil {
       return err
      }
      h.OnAdd(d.Object)
     }
    case Deleted:
     if err := clientState.Delete(d.Object); err != nil {
      return err
     }
     h.OnDelete(d.Object)
    }
   }
   return nil
  }

我們看 Process 匿名函數的行參,obj 就是 Pop 出的對象。根據 Delta類型d.Type 來判斷對對象的處理方式。clientState 就是 Indexer,h 就是 ResourceEventHandler。

所以 Pop出來的對象,馬上就進入了 Indexer中,然后再調用 ResourceEventHandler 對應的方法,這里我們就是將 object 的 key 加入到 workqueue 中。

它各種方法的對應的操作就是 這段代碼。

大步驟3

最后就是我們自己的應用程序,來處理各種資源事件(Add、Update、Delete)。由于Workqueue的存在,就簡化成處理隊列里面的元素。

我們直接可以看這個processNextItem 函數。

第55行,獲取隊列里面的數據。

第65行,就是我們處理對象的業務邏輯。syncToStdout 只是打印一些日志, 但其中 obj, exists, err := c.indexer.GetByKey(key) 這行代碼很關鍵,就是從 indexer 中獲取資源對象。有了它我們就能處理各種業務邏輯,比如我自己工作一般就是將與ResourceEventHandler定義的變化(AddFunc、UpdateFunc、DeleteFunc 你可以只有AddFunc)的對象寫回我們自己的云平臺。

類似代碼如下(syncToStdout 換成了 action):

func (d *Deployment) action(key string) error {
 obj, exists, err := d.indexer.GetByKey(key)
 if err != nil {
  return fmt.Errorf("fetching object with key %s from store failed with %w", key, err)
 }
 ns, deploymentName, err := cache.SplitMetaNamespaceKey(key)
 if err != nil {
  return err
 }
 
 if exists {
  deployment, ok := obj.(*apps_v1.Deployment) // 一定要斷言資源類型,這里類型要同 list & watch 方法中的一致。github的例子是pod,這里用的是deployment
  
  if !ok {
   return fmt.Errorf("type asset fault")
  }
  
  post(deployment) // 將資源傳回的偽代碼
 }
 
 return nil
}

到這,3大步驟就結束了。

4、補充一個知識:

第三大步驟主要就是對 workqueue 的調用。而 workqueue 有三大類:

  • 普通隊列
  • 延遲隊列
  • 限速隊列

延遲隊列是對普通隊列的封裝。而限速隊列是對延遲對列的封裝,外加一個限速器。

我們一般使用限速隊列,方便我們在處理錯誤的時候重試。

處理完后,還要記得從隊列中移除正在處理的 key

defer c.queue.Done(key)

重試與移除在 用例代碼 中寫的非常清楚,一定不要漏掉這兩塊重要的邏輯。

作死的“優化”

我們可能會發現 workqueue 有點多余。我們完全可以直接在ResourceEventHandler中處理業務邏輯嘛!代碼如下:

func NewPodWithOutWorkQueue(ctx context.Context, clientset *kubernetes.Clientset) {
 //workQueue := workqueue.NewDelayingQueue()
 namespace := meta_v1.NamespaceAll
 listWatcher := &cache.ListWatch{
  ListFunc: func(options meta_v1.ListOptions) (runtime2.Object, error) {
   //options.LabelSelector = requireLabel.String()
   return clientset.CoreV1().Pods(namespace).List(ctx, options)
  },
  WatchFunc: func(options meta_v1.ListOptions) (watch.Interface, error) {
   //options.LabelSelector = requireLabel.String()
   return clientset.CoreV1().Pods(namespace).Watch(ctx, options)
  },
 }

 indexer, informer := cache.NewIndexerInformer(listWatcher, &core_v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
  AddFunc: func(obj interface{}) {
   key, err := cache.MetaNamespaceKeyFunc(obj)
   if err == nil {
    fmt.Println("add: ", key)
   }
  },
  DeleteFunc: func(obj interface{}) {
   key, err := cache.MetaNamespaceKeyFunc(obj)
   if err == nil {
    fmt.Println("delete: ", key)
   }
  },
 }, cache.Indexers{
  cache.NamespaceIndex: cache.MetaNamespaceIndexFunc,
 })

 go informer.Run(ctx.Done())

 go GetIndexer(indexer)
}

func GetIndexer(idx cache.Indexer)  {
 for {
  time.Sleep( 3 * time.Second)
  fmt.Println("GetIndexers:", idx.ListIndexFuncValues(cache.NamespaceIndex))
 }
}

這里我們還借機查看了Indexer里面的信息。

其中 GetIndexer 就會打印以 namespace 聚合數據。可以簡單理解成下面的 sql 語句select namespace from xx_table。

為什么說是作死呢?我們有些小伙伴就是這樣寫的,以為不依賴一個組件就是“優化”,但卻沒有思考過為什么官方用例、 manager 中都會用到 workqueue。

所以這就要引申一個問題 為什么要用 workqueue ?原因如下:

  • 在不依賴 Delta FIFO queue 的情況下,將資源事件變得有序。
  • workqueue 也可以當作緩存看。將要處理的事件以 key 的方式先緩存在 workqueue 中。
緩存的作用相信很多人都清楚:解決兩個組件處理速度不匹配的問題,如 cpu 和 硬盤之間經常是用 內存做緩存。
我們的業務處理邏輯大概率肯定是慢于事件的生成的,而且還延遲隊列類型做選擇

方便失敗后重試。

加個煎蛋

這可以算個番外系列,不感興趣的朋友可以直接跳過。

有些同學其實已經發現,我們完全不可以不用那么多隊列的(Delta FIFO queue,Workqueue),甚至還用了個小數據庫(Indexer)!

我們可不可以直接Watch對象?即相當于直接調用 etcd 的 watch API。答案是可以的。

我們借鑒一下這里的代碼。

實現一個pod的watch, 代碼如下:

func NewPodOnlyWithWatch(ctx context.Context, clientset *kubernetes.Clientset) {
 onlyWatch := &cache.ListWatch{
  WatchFunc: func(options meta_v1.ListOptions) (watch.Interface, error) {
   //options.LabelSelector = requireLabel.String()
   //options.ResourceVersion = ""
   return clientset.CoreV1().Pods("devops").Watch(ctx, meta_v1.ListOptions{})
  },
 }

 watcher, err := watch2.NewRetryWatcher("1", onlyWatch)
 if err != nil {
  panic(err)
 }

 // Give the watcher a chance to get to sending events (blocking)
 time.Sleep(10 * time.Millisecond)

 for {
  select {
  case event, ok := <-watcher.ResultChan():
   if !ok {
    fmt.Println("ResultChan closed")
    return
   }

   //fmt.Println("get event")
   if pod, ok := event.Object.(*core_v1.Pod); ok {
    switch event.Type {
    case watch.Added:
     fmt.Printf("新增事件:%s/%s\n", pod.Namespace, pod.Name)
    case watch.Deleted:
     fmt.Printf("刪除事件:%s/%s\n", pod.Namespace, pod.Name)
    case watch.Modified:
     fmt.Printf("更新事件:%s/%s\n", pod.Namespace, pod.Name)
    default:
     fmt.Printf("%s事件:%s\n", event.Type, pod.Name)

    }
   }

  case <-watcher.Done():
   fmt.Println("watcher down")
   return
  }
 }
}

但不建議直接watcher。其中之一就是:從業務視角會看到的重復性事件。即資源對象的一個更新動作,收到多個事件。

5、總結

我們常說的Controller 他最核心的能力就是能監控到資源的任何變化,也就是 聲明式 概念中保證狀態的關鍵技術 --  Informer,流程是:

  • Reflector 將對象加入到Delta FIFO queue中。
  • 然后 informer 將其 pop 出,加入到 Indexer中,以及 resourceEventHandler。
  • 最后就是我們自己的業務邏輯, 即:我們自己先到workqueue中,拿到 key,然后用 key 去Indexer 中換取對象,最后處理對象。

然后我們又通過 一個錯誤的*優化* 的例子,講清楚了 workqueue 的重要性。

我們還可以再 geek 一點,選擇直接watch對象變化的事件,但個人不建議這樣做。

這一篇文章主要是介紹了 資源事件通過 informer 扭轉到 ResourceEventHandler 中的大體流程,并沒有講很多細節的部分。

因為我們還需要掌握一些關鍵的組件:Delta FIFO queue、Indexer、workqueue

當這些都清楚了后,再來了解流程的細節,那就非常輕松了。

當然了除了知道了上面的內容,我們還應該掌握 sharedInformer 以及寫 Controller 的“神器” -- controller-runtime 再封裝的 manager。

如果大家感興趣的話再后面的文章再作詳細介紹。當了解完了這些后,相信 Controller 中的任何技術細節問題都難不倒你了。

責任編輯:姜華 來源: 運維開發故事
相關推薦

2022-04-22 13:32:01

K8s容器引擎架構

2024-11-11 07:05:00

Redis哨兵模式主從復制

2024-02-26 08:10:00

Redis數據數據庫

2021-01-12 08:03:19

Redis數據系統

2024-08-06 09:55:25

2022-04-05 09:24:57

K8s安全網絡安全時間響應

2020-11-10 07:05:41

DockerK8S云計算

2023-11-06 07:16:22

WasmK8s模塊

2019-03-13 09:27:57

宕機Kafka數據

2024-06-18 08:26:22

2020-07-30 09:10:21

DockerK8s容器

2023-11-27 13:18:00

Redis數據不丟失

2021-10-22 08:37:13

消息不丟失rocketmq消息隊列

2022-04-29 10:40:38

技術服務端K8s

2024-02-23 14:53:10

Redis持久化

2023-11-02 08:01:22

2025-07-21 09:02:45

2020-12-31 07:34:04

Redis數據宕機

2023-09-06 08:12:04

k8s云原生

2024-08-30 08:23:06

點贊
收藏

51CTO技術棧公眾號

少妇高清精品毛片在线视频| 国产美女精品在线观看| 国产美女视频免费观看下载软件| 国产777精品精品热热热一区二区| 99久久精品国产毛片| 91国内在线视频| 林心如三级全黄裸体| 亚洲精品一区二区三区在线| 欧美日韩免费观看中文| 亚洲一区综合| 午夜视频www| 久久99精品久久久久| 久久久久久久久久婷婷| 人人人妻人人澡人人爽欧美一区| 国产精一区二区| 精品久久久久久久久久久| 视频一区三区| 黄色一级大片在线免费看国产一| 久热精品视频| 欧美激情一区二区三区久久久 | 久久久黄色大片| 成人久久久久| 亚洲国产精品久久| 三上悠亚在线一区二区| 久久男人av资源站| 亚洲色图欧美偷拍| 日韩精品一区二区三区丰满| 国产综合视频在线| 韩国三级中文字幕hd久久精品| 91sao在线观看国产| 亚洲综合网在线| 国内精品视频在线观看| 欧美成人a在线| 亚洲第一天堂久久| 国产成人免费9x9x人网站视频 | 久久夜色精品国产噜噜av| 91精品国产综合久久男男| av资源免费观看| 伊人激情综合| 欧美国产极速在线| 麻豆视频在线免费看| 国产调教一区二区三区| 精品免费国产二区三区| 欧美成人手机在线视频| 高清亚洲高清| 在线观看视频91| 精品久久久久av| 色在线中文字幕| 亚洲777理论| 日本一本中文字幕| 日本孕妇大胆孕交无码| 一区二区三区在线播| 91xxx视频| 免费a级在线播放| 国产欧美日韩精品一区| 欧美三日本三级少妇三99| 日韩精品一二| 国产亚洲自拍一区| 日韩精品极品视频在线观看免费| 日本午夜在线视频| 久久综合99re88久久爱| 久久精品国产一区二区三区日韩 | 成人爽a毛片免费啪啪红桃视频| 91精品在线免费| 制服.丝袜.亚洲.中文.综合懂| 亚洲精选av| 精品88久久久久88久久久| 亚洲精品乱码久久| 综合亚洲自拍| 国产亚洲激情在线| 国产高清视频免费在线观看| 91成人看片| 欧美激情精品久久久久久变态| 国产一二三四在线| 亚洲一区成人| 国产精品88a∨| 国产精品无码天天爽视频| 国产精品 欧美精品| 国产精品日韩一区二区三区| 亚洲三区在线观看无套内射| 国产清纯白嫩初高生在线观看91| 亚洲第一页在线视频| 日韩特级毛片| 一本一道综合狠狠老| 99热这里只有精品在线播放| 精品视频一区二区三区| 亚洲精品国产精品久久清纯直播| 国产精品揄拍100视频| 日韩免费高清| 欧美肥老妇视频| 国产精品xxxx喷水欧美| 第四色成人网| 毛片精品免费在线观看| 精品无码人妻一区二区三区品| 亚洲国产国产亚洲一二三| 97热在线精品视频在线观看| 国产伦精品一区二区三区视频网站| 一区二区高清| 成人做爰www免费看视频网站| www.黄色国产| 97超碰欧美中文字幕| 日本三级中国三级99人妇网站| 77导航福利在线| 一区二区高清免费观看影视大全| 青青草视频在线免费播放| 极品美女一区| 日韩一区二区精品在线观看| 国产污在线观看| 国产精品一国产精品| 日韩视频第一页| 久久久久久久久久免费视频| 日本美女视频一区二区| 成人精品一二区| 亚洲第一页在线观看| 中文字幕精品一区二区三区精品| av 日韩 人妻 黑人 综合 无码| 中文字幕在线视频网站| 欧美精品一卡两卡| 在线观看av中文字幕| 日韩大片在线| 午夜精品一区二区三区视频免费看| 国产一卡二卡三卡| 盗摄精品av一区二区三区| 久久婷婷开心| 日本aa在线| 欧美日韩日本视频| 欧美bbbbb性bbbbb视频| 欧美.日韩.国产.一区.二区| 国产精品电影一区| 香港三日本三级少妇66| 国产午夜精品久久久久久免费视| 免费看欧美一级片| 日本国产一区| 国产视频一区在线| 国产又黄又爽又无遮挡| 热久久免费视频| 久久福利电影| 超碰在线资源| 制服丝袜亚洲网站| 公肉吊粗大爽色翁浪妇视频| 99伊人成综合| 成人在线观看网址| 成人短视频在线| 欧美日韩午夜影院| 波多野吉衣中文字幕| 一本一本久久| 加勒比在线一区二区三区观看| 爆操欧美美女| 在线不卡一区二区| 国产无遮挡在线观看| 亚洲欧美日韩国产综合精品二区| 成人h视频在线观看| 波多一区二区| 亚洲国产福利在线| 日韩欧美三级在线观看| 粉嫩高潮美女一区二区三区| 国产树林野战在线播放| 少妇高潮一区二区三区99| 一区二区福利视频| 91丨porny丨在线中文| 91亚洲精品视频在线观看| 国产一区二区三区毛片| 精品人妻一区二区三区免费看| 成人国产精品视频| 丝袜人妻一区二区三区| 999在线精品| 久久久久久国产精品三级玉女聊斋 | 亚洲一区二区蜜桃| 国产va免费精品观看精品视频 | 精品小视频在线| av资源吧首页| 久久蜜臀精品av| 91视频免费版污| 欧美韩日一区| 91九色国产社区在线观看| 成人毛片av在线| 精品久久久久一区| 在线免费观看毛片| 久久久99免费| 五月婷婷丁香综合网| 水蜜桃久久夜色精品一区| 成人黄色在线播放| 成a人片在线观看| 精品国产一区二区精华| 欧美日韩在线国产| 26uuu色噜噜精品一区二区| 成人观看免费完整观看| 波多野结衣在线播放一区| 国产精自产拍久久久久久| 国产激情在线观看| 欧美精品一区男女天堂| 日本熟女毛茸茸| 亚洲激情一二三区| 国产又粗又长又爽| 日韩在线卡一卡二| 精品国产一区二区三区在线| www.成人网| 国产精品高潮呻吟久久av无限| 成人黄视频在线观看| 日韩激情视频在线播放| 成人免费一区二区三区| 亚洲精品视频观看| 一二三不卡视频| 国内精品伊人久久久久av一坑| 男人天堂新网址| 日韩成人免费| 国产精品一区二区三区在线| 免费观看成人性生生活片| 久久久精品影院| 凸凹人妻人人澡人人添| 欧美日韩不卡在线| 亚洲精品午夜久久久久久久| 亚洲视频网在线直播| 99久久人妻无码中文字幕系列| 久久99精品久久久久| 又粗又黑又大的吊av| 日韩在线理论| 久久99久久精品国产| 蜜桃精品一区二区三区| 国产成人福利网站| 欧美hdxxx| 日韩亚洲欧美成人| 欧美精品少妇| 精品va天堂亚洲国产| 国产又黄又粗又长| 岛国av一区二区三区| 久久中文字幕在线观看| 欧美国产激情二区三区| 亚洲午夜久久久久久久久| 精品在线免费观看| 黄色国产小视频| 日韩网站在线| 青青青免费在线| 欧美啪啪一区| 色呦呦网站入口| 欧美午夜精品一区二区三区电影| 国内精品久久久久久久果冻传媒| 先锋影音网一区二区| 国产精品日韩精品| 台湾佬成人网| 欧美一级片久久久久久久| 精品日韩av| 超薄丝袜一区二区| av在线首页| 在线精品国产成人综合| 黄网在线观看| 亚洲免费视频网站| 亚洲色图狠狠干| 亚洲精品97久久| 性少妇videosexfreexxx片| 91麻豆精品91久久久久同性| 91影院在线播放| 欧美色视频在线| 色屁屁影院www国产高清麻豆| 午夜视频在线观看一区二区| 久久国产视频播放| 欧美日韩精品在线| 成人免费a视频| 狠狠躁18三区二区一区| www.国产高清| 欧美性猛交xxxx久久久| 午夜一级黄色片| 欧美午夜宅男影院| 老熟妇一区二区三区啪啪| 在线免费观看日本欧美| 亚洲 小说区 图片区| 欧美夫妻性生活| 国产特黄一级片| 日韩一级完整毛片| 成人午夜免费在线观看| 精品欧美黑人一区二区三区| 天天操天天操天天干| 国产一区二区三区在线观看视频 | 国产精品18| 97久草视频| 伊人春色之综合网| 色综合电影网| 婷婷六月综合| 蜜桃视频一区二区在线观看| 国内精品嫩模av私拍在线观看| 免费的一级黄色片| 麻豆成人精品| 日韩在线不卡一区| 国产精品一区在线| 日韩Av无码精品| 久久久99免费| 久草视频免费在线播放| 亚洲国产乱码最新视频| 日韩av在线播| 在线观看区一区二| 国产99久久九九精品无码免费| 欧美日韩一区二区在线观看| 可以免费观看的毛片| 亚洲欧洲国产伦综合| 欧美一区二区三区| 国内精品久久久久久久| 性感美女一区二区在线观看| 91麻豆精品秘密入口| 色婷婷av一区二区三区丝袜美腿| 日韩欧美亚洲日产国| 亚洲欧洲日韩| 丰满爆乳一区二区三区| 国产电影一区二区三区| 国产色视频一区二区三区qq号| 国产精品初高中害羞小美女文| 国产一级在线观看视频| 色播五月激情综合网| 国产熟女一区二区三区四区| 一二美女精品欧洲| 成年人视频免费在线播放| 国产精品久久久久久av福利| 嫩呦国产一区二区三区av| 国产91一区二区三区| 久久影视一区| 男人天堂999| 国产精品一区一区三区| 亚洲无人区码一码二码三码的含义| 亚洲另类色综合网站| 在线播放国产一区| 日韩激情av在线免费观看| a视频在线免费看| 国产精品久久久久久久久久免费| 亚洲2区在线| 男人草女人视频| 麻豆成人av在线| 日本黄色特级片| 亚洲激情在线激情| 亚洲自拍偷拍另类| 亚洲毛片在线免费观看| 麻豆福利在线观看| 亚洲va久久久噜噜噜| 成人网18免费网站| 凹凸国产熟女精品视频| 国产成人亚洲精品青草天美| 国产精品嫩草影院俄罗斯| 91久久香蕉国产日韩欧美9色| 免费av网站在线播放| 日韩在线播放av| 永久免费毛片在线播放| 久久精品国产综合精品 | 亚洲毛片在线免费观看| 成人观看网址| 国产成人女人毛片视频在线| 五月婷婷亚洲| 波多野结衣天堂| 国产精品嫩草影院av蜜臀| www五月天com| 精品一区精品二区| 嗯啊主人调教在线播放视频 | 欧美丝袜一区| 已婚少妇美妙人妻系列| 99精品欧美一区二区三区综合在线| 国产在线视频二区| 欧美成人一区二区| 草莓福利社区在线| 91老司机在线| 欧美成人国产| 插我舔内射18免费视频| 亚洲愉拍自拍另类高清精品| www.久久久久久| 欧美情侣性视频| 99热这里有精品| 99久久99久久精品| 国产成人av自拍| 久久精品视频国产| 亚洲国产欧美自拍| 成人日韩在线观看| 亚洲精品国产精品国自产| 青娱乐精品在线视频| 午夜激情福利电影| 欧美成va人片在线观看| aa国产成人| 久久伊人资源站| 美女精品在线| 女~淫辱の触手3d动漫| 欧美亚洲国产怡红院影院| 99中文字幕一区| 97久久天天综合色天天综合色hd| 欧美视频不卡| 精品人妻无码一区二区三区| 在线视频一区二区三区| 日本成a人片在线观看| 亚洲在线观看视频网站| 伊人狠狠色j香婷婷综合| 一区二区三区免费在线观看视频| 欧美最猛黑人xxxxx猛交| 欧美激情二区| 国产精品播放| 久久久久久自在自线| 国产在线综合视频| 日韩精品一区二区三区在线 | 日韩美女免费线视频| 日韩伦理视频| 久久久久亚洲av无码麻豆| 欧美日韩亚洲视频一区| 中国日本在线视频中文字幕| 97人人澡人人爽| 亚洲人成人一区二区三区| 国产精品理论在线| 精品国产伦一区二区三区观看体验 |