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

一篇帶給你Go并發編程Singleflight

開發 前端
這篇文章從使用場景,到使用方法,再到源碼分析和可能存在的坑給大家介紹了 singleflight,希望你能有所收獲,沒事看看官方的代碼還是很有收獲的,這次又學到了一個騷操作,用雙重 defer 來避免死鎖.

[[389247]]

 這一篇文章的內容是在 Week05: 評論系統架構設計 當中的可用性設計當中提到的,但是這個屬于 Go 官方擴展同步包 (golang.org/x/sync/singleflight) 的一個庫,為了讓內容統一就放到這里了。

SingleFlight

為什么我們需要 SingleFlight(使用場景)?

一般情況下我們在寫一寫對外的服務的時候都會有一層 cache 作為緩存,用來減少底層數據庫的壓力,但是在遇到例如 redis 抖動或者其他情況可能會導致大量的 cache miss 出現。

如下圖所示,可能存在來自桌面端和移動端的用戶有 1000 的并發請求,他們都訪問的獲取文章列表的接口,獲取前 20 條信息,如果這個時候我們服務直接去訪問 redis 出現 cache miss 那么我們就會去請求 1000 次數據庫,這時可能會給數據庫帶來較大的壓力(這里的 1000 只是一個例子,實際上可能遠大于這個值)導致我們的服務異常或者超時。

這時候就可以使用 singleflight 庫了,直譯過來就是單飛,這個庫的主要作用就是將一組相同的請求合并成一個請求,實際上只會去請求一次,然后對所有的請求返回相同的結果。如下圖所示,使用 singleflight 之后,我們在一個請求的時間周期內實際上只會向底層的數據庫發起一次請求大大減少對數據庫的壓力。

SingleFlight 包怎么用(使用教程)?

函數簽名

主要是一個 Group 結構體,三個方法,具體信息看下方注釋

  1. type Group 
  2.     // Do 執行函數, 對同一個 key 多次調用的時候,在第一次調用沒有執行完的時候 
  3.  // 只會執行一次 fn 其他的調用會阻塞住等待這次調用返回 
  4.  // v, err 是傳入的 fn 的返回值 
  5.  // shared 表示是否真正執行了 fn 返回的結果,還是返回的共享的結果 
  6.     func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) 
  7.  
  8.  // DoChan 和 Do 類似,只是 DoChan 返回一個 channel,也就是同步與異步的區別 
  9.  func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result 
  10.  
  11.     // Forget 用于通知 Group 刪除某個 key 這樣后面繼續這個 key 的調用的時候就不會在阻塞等待了 
  12.  func (g *Group) Forget(key string) 

使用示例

接下來我們看看實際上我們是怎么使用的,先使用一個普通的例子,這時一個獲取文章詳情的函數,我們在函數里面使用一個 count 模擬不同并發下的耗時的不同,并發越多請求耗時越多

  1. func getArticle(id int) (article string, err error) { 
  2.  // 假設這里會對數據庫進行調用, 模擬不同并發下耗時不同 
  3.  atomic.AddInt32(&count, 1) 
  4.  time.Sleep(time.Duration(count) * time.Millisecond) 
  5.  
  6.  return fmt.Sprintf("article: %d", id), nil 

我們使用 singleflight 的時候就只需要 new(singleflight.Group) 然后調用一下相對應的 Do 方法就可了,是不是很簡單

  1. func singleflightGetArticle(sg *singleflight.Group, id int) (string, error) { 
  2.  v, err, _ := sg.Do(fmt.Sprintf("%d", id), func() (interface{}, error) { 
  3.   return getArticle(id) 
  4.  }) 
  5.  
  6.  return v.(string), err 

效果測試

光說不練假把式,寫一個簡單的測試代碼,下面我們啟動 1000 個 Goroutine 去并發調用這兩個方法

  1. var count int32 
  2.  
  3. func main() { 
  4.  time.AfterFunc(1*time.Second, func() { 
  5.   atomic.AddInt32(&count, -count
  6.  }) 
  7.  
  8.  var ( 
  9.   wg  sync.WaitGroup 
  10.   now = time.Now() 
  11.   n   = 1000 
  12.   sg  = &singleflight.Group{} 
  13.  ) 
  14.  
  15.  for i := 0; i < n; i++ { 
  16.   wg.Add(1) 
  17.   go func() { 
  18.    // res, _ := singleflightGetArticle(sg, 1) 
  19.    res, _ := getArticle(1) 
  20.    if res != "article: 1" { 
  21.     panic("err"
  22.    } 
  23.    wg.Done() 
  24.   }() 
  25.  } 
  26.  
  27.  wg.Wait() 
  28.  fmt.Printf("同時發起 %d 次請求,耗時: %s", n, time.Since(now)) 

可以看到這個是調用 getArticle 方法的耗時,花費了 1s 多

  1. # 直接調用的請求耗時 
  2. ❯ go run ./1.go 
  3. 同時發起 1000 次請求,耗時: 1.0022831s 

而使用 singleflight 的方法,花費了不到 3ms

  1. # 使用 singleflight 的請求耗時 
  2. ❯ go run ./1.go 
  3. 同時發起 1000 次請求,耗時: 2.5119ms 

當然每個庫都有自己的使用場景,軟件領域里面沒有銀彈,如果我們用的不太好的話甚至可能會得到適得其反的效果,而多看源碼不僅能夠幫助我們進行學習,也可以盡量少踩坑

它是如何實現的(源碼分析)?

本文基于 [https://pkg.go.dev/golang.org/x/sync@v0.0.0-20210220032951-036812b2e83c/singleflight](https://pkg.go.dev/golang.org/x/sync@v0.0.0-20210220032951-036812b2e83c/singleflight) 進行分析,這個庫的實現很簡單,但是功能很強大,還有一些小技巧,非常值得學習

Group

  1. type Group struct { 
  2.  mu sync.Mutex       // protects m 
  3.  m  map[string]*call // lazily initialized 

Group 結構體由一個互斥鎖和一個 map 組成,可以看到注釋 map 是懶加載的,所以 Group 只要聲明就可以使用,不用進行額外的初始化零值就可以直接使用。call 保存了當前調用對應的信息,map 的鍵就是我們調用 Do 方法傳入的 key

  1. type call struct { 
  2.  wg sync.WaitGroup 
  3.  
  4.  // 函數的返回值,在 wg 返回前只會寫入一次 
  5.  val interface{} 
  6.  err error 
  7.  
  8.  // 使用調用了 Forgot 方法 
  9.  forgotten bool 
  10.  
  11.     // 統計調用次數以及返回的 channel 
  12.  dups  int 
  13.  chans []chan<- Result 

Do

  1. func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) { 
  2.  g.mu.Lock() 
  3.  
  4.     // 前面提到的懶加載 
  5.     if g.m == nil { 
  6.   g.m = make(map[string]*call) 
  7.  } 
  8.  
  9.     // 會先去看 key 是否已經存在 
  10.  if c, ok := g.m[key]; ok { 
  11.         // 如果存在就會解鎖 
  12.   c.dups++ 
  13.   g.mu.Unlock() 
  14.  
  15.         // 然后等待 WaitGroup 執行完畢,只要一執行完,所有的 wait 都會被喚醒 
  16.   c.wg.Wait() 
  17.  
  18.         // 這里區分 panic 錯誤和 runtime 的錯誤,避免出現死鎖,后面可以看到為什么這么做 
  19.   if e, ok := c.err.(*panicError); ok { 
  20.    panic(e) 
  21.   } else if c.err == errGoexit { 
  22.    runtime.Goexit() 
  23.   } 
  24.   return c.val, c.err, true 
  25.  } 
  26.  
  27.     // 如果我們沒有找到這個 key 就 new call 
  28.  c := new(call) 
  29.  
  30.     // 然后調用 waitgroup 這里只有第一次調用會 add 1,其他的都會調用 wait 阻塞掉 
  31.     // 所以這要這次調用返回,所有阻塞的調用都會被喚醒 
  32.  c.wg.Add(1) 
  33.  g.m[key] = c 
  34.  g.mu.Unlock() 
  35.  
  36.     // 然后我們調用 doCall 去執行 
  37.  g.doCall(c, key, fn) 
  38.  return c.val, c.err, c.dups > 0 

doCall

這個方法的實現有點意思,使用了兩個 defer 巧妙的將 runtime 的錯誤和我們傳入 function 的 panic 區別開來避免了由于傳入的 function panic 導致的死鎖

  1. func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) { 
  2.  normalReturn := false 
  3.  recovered := false 
  4.  
  5.     // 第一個 defer 檢查 runtime 錯誤 
  6.  defer func() { 
  7.  
  8.  }() 
  9.  
  10.     // 使用一個匿名函數來執行 
  11.  func() { 
  12.   defer func() { 
  13.    if !normalReturn { 
  14.                 // 如果 panic 了我們就 recover 掉,然后 new 一個 panic 的錯誤 
  15.                 // 后面在上層重新 panic 
  16.     if r := recover(); r != nil { 
  17.      c.err = newPanicError(r) 
  18.     } 
  19.    } 
  20.   }() 
  21.  
  22.   c.val, c.err = fn() 
  23.  
  24.         // 如果 fn 沒有 panic 就會執行到這一步,如果 panic 了就不會執行到這一步 
  25.         // 所以可以通過這個變量來判斷是否 panic 了 
  26.   normalReturn = true 
  27.  }() 
  28.  
  29.     // 如果 normalReturn 為 false 就表示,我們的 fn panic 了 
  30.     // 如果執行到了這一步,也說明我們的 fn  recover 住了,不是直接 runtime exit 
  31.  if !normalReturn { 
  32.   recovered = true 
  33.  } 

再來看看第一個 defer 中的代碼

  1. defer func() { 
  2.  // 如果既沒有正常執行完畢,又沒有 recover 那就說明需要直接退出了 
  3.  if !normalReturn && !recovered { 
  4.   c.err = errGoexit 
  5.  } 
  6.  
  7.  c.wg.Done() 
  8.  g.mu.Lock() 
  9.  defer g.mu.Unlock() 
  10.  
  11.        // 如果已經 forgot 過了,就不要重復刪除這個 key 了 
  12.  if !c.forgotten { 
  13.   delete(g.m, key
  14.  } 
  15.  
  16.  if e, ok := c.err.(*panicError); ok { 
  17.   // 如果返回的是 panic 錯誤,為了避免 channel 死鎖,我們需要確保這個 panic 無法被恢復 
  18.   if len(c.chans) > 0 { 
  19.    go panic(e) 
  20.    select {} // Keep this goroutine around so that it will appear in the crash dump. 
  21.   } else { 
  22.    panic(e) 
  23.   } 
  24.  } else if c.err == errGoexit { 
  25.   // 已經準備退出了,也就不用做其他操作了 
  26.  } else { 
  27.   // 正常情況下向 channel 寫入數據 
  28.   for _, ch := range c.chans { 
  29.    ch <- Result{c.val, c.err, c.dups > 0} 
  30.   } 
  31.  } 
  32. }() 

DoChan

Do chan 和 Do 類似,其實就是一個是同步等待,一個是異步返回,主要實現上就是,如果調用 DoChan 會給 call.chans 添加一個 channel 這樣等第一次調用執行完畢之后就會循環向這些 channel 寫入數據

  1. func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result { 
  2.  ch := make(chan Result, 1) 
  3.  g.mu.Lock() 
  4.  if g.m == nil { 
  5.   g.m = make(map[string]*call) 
  6.  } 
  7.  if c, ok := g.m[key]; ok { 
  8.   c.dups++ 
  9.   c.chans = append(c.chans, ch) 
  10.   g.mu.Unlock() 
  11.   return ch 
  12.  } 
  13.  c := &call{chans: []chan<- Result{ch}} 
  14.  c.wg.Add(1) 
  15.  g.m[key] = c 
  16.  g.mu.Unlock() 
  17.  
  18.  go g.doCall(c, key, fn) 
  19.  
  20.  return ch 

Forget

forget 用于手動釋放某個 key 下次調用就不會阻塞等待了

  1. func (g *Group) Forget(key string) { 
  2.  g.mu.Lock() 
  3.  if c, ok := g.m[key]; ok { 
  4.   c.forgotten = true 
  5.  } 
  6.  delete(g.m, key
  7.  g.mu.Unlock() 

有哪些注意事項(避坑指南)?

單飛雖好但也不要濫用哦,還是存在一些坑的

1. 一個阻塞,全員等待

使用 singleflight 我們比較常見的是直接使用 Do 方法,但是這個極端情況下會導致整個程序 hang 住,如果我們的代碼出點問題,有一個調用 hang 住了,那么會導致所有的請求都 hang 住

還是之前的例子,我們加一個 select 模擬阻塞

  1. func singleflightGetArticle(sg *singleflight.Group, id int) (string, error) { 
  2.  v, err, _ := sg.Do(fmt.Sprintf("%d", id), func() (interface{}, error) { 
  3.   // 模擬出現問題,hang 住 
  4.   select {} 
  5.   return getArticle(id) 
  6.  }) 
  7.  
  8.  return v.(string), err 

執行就會發現死鎖了

  1. fatal error: all goroutines are asleep - deadlock! 
  2.  
  3. goroutine 1 [select (no cases)]: 

這時候我們可以使用 DoChan 結合 select 做超時控制

  1. func singleflightGetArticle(ctx context.Context, sg *singleflight.Group, id int) (string, error) { 
  2.  result := sg.DoChan(fmt.Sprintf("%d", id), func() (interface{}, error) { 
  3.   // 模擬出現問題,hang 住 
  4.   select {} 
  5.   return getArticle(id) 
  6.  }) 
  7.  
  8.  select { 
  9.  case r := <-result: 
  10.   return r.Val.(string), r.Err 
  11.  case <-ctx.Done(): 
  12.   return "", ctx.Err() 
  13.  } 

調用的時候傳入一個含 超時的 context 即可,執行時就會返回超時錯誤

  1. ❯ go run ./1.go 
  2. panic: context deadline exceeded 

2. 一個出錯,全部出錯

這個本身不是什么問題,因為 singleflight 就是這么設計的,但是實際使用的時候 如果我們一次調用要 1s,我們的數據庫請求或者是 下游服務可以支撐 10rps 的請求的時候這會導致我們的錯誤閾提高,因為實際上我們可以一秒內嘗試 10 次,但是用了 singleflight 之后只能嘗試一次,只要出錯這段時間內的所有請求都會受影響

這種情況我們可以啟動一個 Goroutine 定時 forget 一下,相當于將 rps 從 1rps 提高到了 10rps

  1. go func() { 
  2.        time.Sleep(100 * time.Millisecond) 
  3.        // logging 
  4.        g.Forget(key
  5.    }() 

總結

這篇文章從使用場景,到使用方法,再到源碼分析和可能存在的坑給大家介紹了 singleflight,希望你能有所收獲,沒事看看官方的代碼還是很有收獲的,這次又學到了一個騷操作,用雙重 defer 來避免死鎖,你學廢了么?

我們下一篇會開啟一個新的系列,Go 可用性,敬請期待!

文章博客地址:https://lailin.xyz 

 

責任編輯:姜華 來源: mohuishou
相關推薦

2021-06-24 06:35:00

Go語言進程

2021-04-30 09:04:11

Go 語言結構體type

2021-07-12 06:11:14

SkyWalking 儀表板UI篇

2021-04-09 10:38:59

Go 語言數組與切片

2021-04-06 10:19:36

Go語言基礎技術

2020-11-24 09:53:38

Shell

2022-04-29 14:38:49

class文件結構分析

2021-04-08 11:00:56

CountDownLaJava進階開發

2021-07-21 09:48:20

etcd-wal模塊解析數據庫

2022-02-17 08:53:38

ElasticSea集群部署

2021-03-12 09:21:31

MySQL數據庫邏輯架構

2021-06-21 14:36:46

Vite 前端工程化工具

2021-04-01 10:51:55

MySQL鎖機制數據庫

2021-01-28 08:55:48

Elasticsear數據庫數據存儲

2021-04-14 14:16:58

HttpHttp協議網絡協議

2024-06-13 08:34:48

2022-03-22 09:09:17

HookReact前端

2023-03-29 07:45:58

VS編輯區編程工具

2021-07-08 07:30:13

Webpack 前端Tree shakin

2023-03-13 09:31:04

點贊
收藏

51CTO技術棧公眾號

在线精品播放av| 在线免费观看不卡av| 99在线观看| 国产黄网在线观看| **女人18毛片一区二区| 日韩女同互慰一区二区| 欧美日韩亚洲第一| 国产激情视频在线| 91美女视频网站| 亚洲精品免费一区二区三区| 日韩色图在线观看| 欧美在线日韩| 亚洲日本中文字幕免费在线不卡| 一级黄色片国产| 日产福利视频在线观看| 亚洲美女偷拍久久| 欧美在线一二三区| 日本成人动漫在线观看| 久久99精品一区二区三区| 2019中文字幕在线| 69av视频在线| 欧美一区电影| 日韩高清人体午夜| 中文字幕99页| 在线播放成人| 欧美午夜一区二区三区免费大片| 东北少妇不带套对白| 国产三区在线观看| 日本一区二区动态图| 精品一区二区三区日本| 成人免费观看在线视频| 精品一区二区三区免费视频| 全亚洲最色的网站在线观看| 国产精品suv一区二区69| 欧美国产偷国产精品三区| 亚洲日本中文字幕| 国内精品久久99人妻无码| 中文字幕日本一区| 欧美日产国产精品| chinese少妇国语对白| 麻豆mv在线看| 五月天一区二区| 成人国产一区二区三区| 精品麻豆一区二区三区| 国产精品国产三级国产普通话蜜臀 | 在线电影看在线一区二区三区| 免费黄网站在线观看| 99国产麻豆精品| 精品中文字幕人| 午夜视频在线播放| 2020国产精品| 久久久99爱| 天堂成人在线| 99精品视频一区二区三区| 国产精品免费看一区二区三区| 朝桐光av在线一区二区三区| 国产一区二区三区在线观看免费| 成人免费观看网址| 国产精品美女一区| 国产一区二区三区免费| 91在线观看免费高清完整版在线观看 | 合欧美一区二区三区| 欧美激情18p| 久久高清免费视频| 亚洲欧美日韩专区| 国产成人激情视频| 中文字幕在线播放av| 六月丁香婷婷久久| 91中文字幕在线| 亚洲奶汁xxxx哺乳期| 91视频在线看| 亚洲精品乱码久久久久久蜜桃91| 麻豆系列在线观看| 亚洲女性喷水在线观看一区| 国产91沈先生在线播放| 亚洲国产欧美日本视频| 精品视频一区三区九区| 日批视频在线看| 久久超级碰碰| 中文字幕日韩av电影| wwwav国产| 国产精品一卡| 成人欧美一区二区三区在线| 欧美天堂在线视频| 国产日产欧美一区二区视频| 乱子伦一区二区| 午夜伦理福利在线| 欧美日本在线播放| 三级视频网站在线观看| 欧美日韩一二| 久久久久九九九九| 中文文字幕一区二区三三| 国产成人自拍在线| 日本午夜一区二区三区| 欧美日韩经典丝袜| 精品视频一区三区九区| 亚洲自拍偷拍精品| 97视频热人人精品免费| 97视频国产在线| 国产又粗又猛又爽又黄91| 极品美女一区| 国模精品一区| www国产精品视频| 动漫精品一区一码二码三码四码| 久久精品一区二区国产| 91视频国产精品| 男人天堂综合| 亚洲精品成a人| 鲁一鲁一鲁一鲁一av| 精品网站aaa| 国产高清一区二区三区四区| 精品国产日韩欧美| 欧美国产日本在线| 瑟瑟视频在线免费观看| 国产99久久久国产精品潘金网站| 色综合电影网| 爱啪啪综合导航| 欧美一卡二卡三卡四卡| 国产精久久一区二区三区| 久久久久久久久久久久久久| 91国内在线视频| va婷婷在线免费观看| 久久久亚洲综合| 国产精品一线二线三线| www一区二区三区| 中文字幕九色91在线| 免费黄色网址在线| 成人性生交大片免费看中文| 最新av在线免费观看| 国产精品原创视频| 亚洲最新在线视频| 亚洲自拍一区在线观看| www.一区二区| 被灌满精子的波多野结衣| 精品国产一区二| 久久久成人精品视频| 亚洲无码精品国产| 国产精品麻豆网站| 牛夜精品久久久久久久| 欧美一区电影| 国产精品一区av| 亚洲搞黄视频| 6080亚洲精品一区二区| 中文乱码字幕高清一区二区| 青青青爽久久午夜综合久久午夜| 日本不卡久久| 天堂久久午夜av| 中文欧美在线视频| 中文字幕欧美在线观看| 国产免费观看久久| 亚洲 欧美 另类人妖| 91综合久久| 亚洲xxxxx电影| 中文在线字幕免费观看| 日韩欧美一级二级| 久久黄色免费视频| 不卡av在线网| 欧美日韩黄色一级片| 偷拍一区二区| 国产精品福利在线| 色哟哟免费在线观看 | 欧美疯狂xxxx大交乱88av| aaa一区二区| 亚洲国产日韩a在线播放性色| 亚洲精品无码一区二区| 99国内精品| 日本不卡二区| 在线免费成人| 国内精品久久久久久久| 欧美精品少妇| 欧美区在线观看| 九九九久久久久| 99国产精品久| www.亚洲高清| 狠狠色综合网| 日韩欧美激情一区二区| 国产免费区一区二区三视频免费| 国内偷自视频区视频综合| 日本中文字幕一区二区有码在线| 欧美性一区二区| 青青青在线免费观看| 不卡电影免费在线播放一区| 国产又大又黄又粗的视频| 91精品婷婷色在线观看| 精品国产综合久久| 国产毛片精品久久| 久久久久久com| 成人h小游戏| 精品欧美乱码久久久久久 | 欧美1234区| 亚洲毛茸茸少妇高潮呻吟| 亚洲天堂久久久久| 亚洲18色成人| 貂蝉被到爽流白浆在线观看| 丁香桃色午夜亚洲一区二区三区| 亚洲精品高清无码视频| 国产精品成人一区二区网站软件| 日本高清久久一区二区三区| 亚洲精选av| 国产精品电影一区| 黄页网站大全在线免费观看| 国产一区二区三区在线看| 亚洲AV午夜精品| 欧美综合欧美视频| 国产无精乱码一区二区三区| 中文字幕制服丝袜成人av| 制服丝袜第二页| 国产乱色国产精品免费视频| 男人透女人免费视频| 狠狠色综合网| 公共露出暴露狂另类av| 国产免费av一区二区三区| 99热在线国产| 成人黄色理论片| 国产精品6699| 日韩精品av| 欧美激情视频播放| 看黄网站在线观看| 国产亚洲一区二区精品| 日本一卡二卡四卡精品| 欧美不卡123| 国产精品一区二区免费视频| 在线视频中文字幕一区二区| 日韩成人免费在线观看| 亚洲蜜臀av乱码久久精品蜜桃| 538精品视频| 2021中文字幕一区亚洲| 国产精品久久AV无码| 成人美女视频在线观看18| 人妻巨大乳一二三区| 久久99精品久久久| 亚洲天堂2018av| 日本不卡视频在线观看| 91视频免费版污| 丝袜亚洲另类欧美综合| 女人和拘做爰正片视频| 日韩午夜免费| 国产素人在线观看| 伊人久久大香线蕉综合热线 | 女同一区二区| 小说区图片区色综合区| 精品1区2区| 先锋影音国产精品| 欧美日韩免费观看一区| 国产探花在线精品一区二区| 欧美不卡在线一区二区三区| 香蕉久久精品| 日本a级片久久久| 国产免费av一区二区三区| 神马影院午夜我不卡| 日韩电影在线视频| 亚洲亚洲精品三区日韩精品在线视频| 欧美一区二区麻豆红桃视频| 亚洲精品国产精品久久| 国产精品国产三级国产在线观看| 中文字幕不卡每日更新1区2区| 欧美国产一区二区三区激情无套| 伊人久久99| 欧美黄色大片网站| 国产女主播自拍| 亚洲欧美日韩国产一区二区| 妞干网在线免费视频| 奇米四色…亚洲| 日日干日日操日日射| 国产精品一二三区| 伊人网综合视频| 国产三级精品在线| 欧美肥妇bbwbbw| 亚洲综合激情小说| 国产又大又黄视频| 欧美亚洲国产bt| 国产成人三级在线播放| 精品日韩在线观看| 人成免费电影一二三区在线观看| 国产亚洲精品美女久久久久| 成码无人av片在线观看网站| 97免费视频在线| 99久久er| 成人免费视频观看视频| 啪啪亚洲精品| 神马午夜伦理影院| 午夜综合激情| 国产美女视频免费看| 成人综合激情网| 免费视频91蜜桃| 亚洲一区免费视频| 波多野结衣午夜| 欧美一区三区二区| 青青色在线视频| 精品中文字幕在线2019| 久久人体大尺度| 91丝袜脚交足在线播放| 蜜桃精品噜噜噜成人av| 久久久久久久久网| 日韩国产成人精品| 欧美夫妇交换xxx| 亚洲欧洲色图综合| av片免费观看| 精品女同一区二区| 欧美一区二区三区| 97超级碰碰碰| 日韩视频在线直播| 午夜老司机精品| 香蕉久久夜色精品国产| 91亚洲一区二区| 国产欧美日韩另类一区| 国产午夜精品无码| 欧美精品vⅰdeose4hd| 先锋av资源站| 久热爱精品视频线路一| 日韩一级二级| 欧美裸体网站| 99在线精品视频在线观看 | 2021中文字幕一区亚洲| 一区二区视频免费看| 欧美视频一二三区| 欧洲亚洲精品视频| 隔壁老王国产在线精品| 国产一区二区三区免费观看在线| 日韩国产美国| 国产精品社区| 日韩av手机在线播放| 亚洲伦在线观看| 国产又大又长又粗| 中文在线不卡视频| 免费观看成人性生生活片 | 国外av在线| 欧洲成人免费视频| 麻豆精品av| 麻豆tv在线播放| 国产91富婆露脸刺激对白| 天天鲁一鲁摸一摸爽一爽| 欧美日韩专区在线| av基地在线| 国产精品中文字幕在线| 狠狠色丁香婷婷综合影院| 成人在线观看黄| 久久精品亚洲国产奇米99| 免费黄色网址在线| 亚洲精品一区二区久| 亚洲国产福利| 欧洲亚洲一区二区三区四区五区| 欧美亚洲一区| 色一情一交一乱一区二区三区| 色婷婷久久久亚洲一区二区三区 | 久久九九有精品国产23| 99久久久国产| 精品国产三级a∨在线| 国产老妇另类xxxxx| 麻豆视频在线观看| 精品国产污污免费网站入口| 免费污视频在线| 九九热久久66| 日韩 欧美一区二区三区| 亚洲精品国产精品国自产网站| 在线看国产日韩| 91大神xh98hx在线播放| 91精品国产综合久久久久久久久| 午夜av一区| 免费啪视频在线观看| 欧美日韩激情视频| 免费国产在线观看| 国产精品免费观看在线| 婷婷另类小说| 日本少妇一级片| 欧美日韩国产在线看| 国产在线高清| 成人信息集中地欧美| 影音先锋中文字幕一区| 老司机福利av| 欧美日韩精品一区视频| 菠萝菠萝蜜在线视频免费观看| 99影视tv| 日韩av在线免费观看不卡| 天海翼在线视频| 精品少妇一区二区三区在线视频 | 91久久久久久久一区二区| 国产精品激情| 亚洲精品成人无码| 91超碰这里只有精品国产| heyzo中文字幕在线| 日韩一二三区不卡在线视频| 国产乱色国产精品免费视频| 久久久久久91亚洲精品中文字幕| 亚洲小视频在线| 亚洲国产高清在线观看| 爱福利视频一区二区| 亚洲另类在线视频| 亚洲欧美日韩综合在线| 国产精品一区二区3区| 91精品秘密在线观看| 中文精品在线观看| 91精品国产手机| 成人软件在线观看| 日本a级片在线播放| 久久久精品欧美丰满| 国产高清免费在线观看| 国产精品69精品一区二区三区| 国产综合精品| 午夜国产福利视频|