日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

Golang 狀態(tài)機設計模式,你知道多少?

來源: 責編: 時間:2024-05-27 17:24:30 236觀看
導讀導言在我們開發(fā)的許多項目中,都需要依賴某種運行狀態(tài)從而實現(xiàn)連續(xù)操作。這方面的例子包括:解析配置語言、編程語言等在系統(tǒng)、路由器、集群上執(zhí)行操作...ETL(Extract Transform Load,提取轉換加載)很久以前,Rob Pike 有一個

導言

在我們開發(fā)的許多項目中,都需要依賴某種運行狀態(tài)從而實現(xiàn)連續(xù)操作。mml28資訊網(wǎng)——每日最新資訊28at.com

這方面的例子包括:mml28資訊網(wǎng)——每日最新資訊28at.com

  • 解析配置語言、編程語言等
  • 在系統(tǒng)、路由器、集群上執(zhí)行操作...
  • ETL(Extract Transform Load,提取轉換加載)

很久以前,Rob Pike 有一個關于 Go 中詞法掃描[2]的演講,內容很講座,我看了好幾遍才真正理解。但演講中介紹的最基本知識之一就是某個版本的 Go 狀態(tài)機。mml28資訊網(wǎng)——每日最新資訊28at.com

該狀態(tài)機利用了 Go 的能力,即從函數(shù)中創(chuàng)建類型并將函數(shù)賦值給變量。mml28資訊網(wǎng)——每日最新資訊28at.com

他在演講中介紹的狀態(tài)機功能強大,打破了讓函數(shù)執(zhí)行 if/else 并調用下一個所需函數(shù)的邏輯。取而代之的是,每個狀態(tài)都會返回下一個需要調用的函數(shù)。mml28資訊網(wǎng)——每日最新資訊28at.com

這樣就能將調用鏈分成更容易測試的部分。mml28資訊網(wǎng)——每日最新資訊28at.com

mml28資訊網(wǎng)——每日最新資訊28at.com

調用鏈

下面是一個用簡單的調用鏈來完成任務的例子:mml28資訊網(wǎng)——每日最新資訊28at.com

func Caller(args Args) {  callA(args)  callB(args)}

mml28資訊網(wǎng)——每日最新資訊28at.com

func Caller(args Args) {  callA(args)}func callA(args Args) {  callB(args)}func callB(args Args) {  return}

兩種方法都表示調用鏈,其中 Caller() 調用 callA(),并最終調用 callB(),從中可以看到這一系列調用是如何執(zhí)行的。mml28資訊網(wǎng)——每日最新資訊28at.com

當然,這種設計沒有任何問題,但當調用者遠程調用其他系統(tǒng)時,必須對這些遠程調用進行模擬/打樁,以提供密封測試。mml28資訊網(wǎng)——每日最新資訊28at.com

你可能還想實現(xiàn)條件調用鏈,即根據(jù)某些參數(shù)或狀態(tài),在特定條件下通過 if/else 調用不同函數(shù)。mml28資訊網(wǎng)——每日最新資訊28at.com

這就意味著,要對 Caller() 進行密封測試,可能需要處理整個調用鏈中的樁函數(shù)。如果有 50 個調用層級,則可能需要對被測函數(shù)下面每個層級的所有函數(shù)進行模擬/打樁。mml28資訊網(wǎng)——每日最新資訊28at.com

這正是 Pike 的狀態(tài)機設計大顯身手的地方。mml28資訊網(wǎng)——每日最新資訊28at.com

狀態(tài)機模式

首先定義狀態(tài):mml28資訊網(wǎng)——每日最新資訊28at.com

type State[T any] func(ctx context.Context, args T) (T, State[T], error)

狀態(tài)表示為函數(shù)/方法,接收一組參數(shù)(任意類型 T),并返回下一個狀態(tài)及其參數(shù)或錯誤信息。mml28資訊網(wǎng)——每日最新資訊28at.com

如果返回的狀態(tài)為 nil,那么狀態(tài)機將停止運行。如果設置了 error,狀態(tài)機也將停止運行。因為返回的是下一個要運行的狀態(tài),所以根據(jù)不同的條件,會有不同的下一個狀態(tài)。mml28資訊網(wǎng)——每日最新資訊28at.com

這個版本與 Pike 的狀態(tài)機的不同之處在于這里包含了泛型并返回 T。這樣我們就可以創(chuàng)建純粹的函數(shù)式狀態(tài)機(如果需要的話),可以返回某個類型,并將其傳遞給下一個狀態(tài)。Pike 最初實現(xiàn)狀態(tài)機設計時還沒有使用泛型。mml28資訊網(wǎng)——每日最新資訊28at.com

為了實現(xiàn)這一目標,需要一個狀態(tài)驅動程序:mml28資訊網(wǎng)——每日最新資訊28at.com

func Run[T any](ctx context.Context, args T, start State[T] "T any") (T, error) {  var err error  current := start  for {    if ctx.Err() != nil {      return args, ctx.Err()    }    args, current, err = current(ctx, args)    if err != nil {      return args, err    }    if current == nil {      return args, nil    }  }}

寥寥幾行代碼,我們就有了一個功能強大的狀態(tài)驅動程序。mml28資訊網(wǎng)——每日最新資訊28at.com

下面來看一個例子,在這個例子中,我們?yōu)榧褐械姆贞P閉操作編寫了狀態(tài)機:mml28資訊網(wǎng)——每日最新資訊28at.com

package remove...// storageClient provides the methods on a storage service// that must be provided to use Remove().type storageClient interface {  RemoveBackups(ctx context.Context, service string, mustKeep int) error  RemoveContainer(ctx context.Context, service string) error}// serviceClient provides methods to do operations for services // within a cluster.type servicesClient interface {  Drain(ctx context.Context, service string) error  Remove(ctx context.Context, service string) error  List(ctx context.Context) ([]string, error)  HasStorage(ctx context.Context, service string) (bool, error)}

這里定義了幾個需要客戶實現(xiàn)的私有接口,以便從集群中移除服務。mml28資訊網(wǎng)——每日最新資訊28at.com

我們定義了私有接口,以防止他人使用我們的定義,但會通過公有變量公開這些接口。這樣,我們就能與客戶保持松耦合,保證只使用我們需要的方法。mml28資訊網(wǎng)——每日最新資訊28at.com

// Args are arguments to Service().type Args struct {  // Name is the name of the service.  Name string    // Storage is a client that can remove storage backups and storage  // containers for a service.  Storage storageClient  // Services is a client that allows the draining and removal of  // a service from the cluster.  Services servicesClient}func (a Args) validate(ctx context.Context) error {  if a.Name == "" {    return fmt.Errorf("Name cannot be an empty string")  }  if a.Storage == nil {    return fmt.Errorf("Storage cannot be nil")  }  if a.Services == nil {    return fmt.Errorf("Services cannot be nil")  }  return nil}

這里設置了要通過狀態(tài)傳遞的參數(shù),可以將在一個狀態(tài)中設置并傳遞到另一個狀態(tài)的私有字段包括在內。mml28資訊網(wǎng)——每日最新資訊28at.com

請注意,Args 并非指針。mml28資訊網(wǎng)——每日最新資訊28at.com

由于我們修改了 Args 并將其傳遞給每個狀態(tài),因此不需要給垃圾回收器增加負擔。對于像這樣操作來說,這點節(jié)約微不足道,但在工作量大的 ETL 管道中,節(jié)約的時間可能就很明顯了。mml28資訊網(wǎng)——每日最新資訊28at.com

實現(xiàn)中包含 validate() 方法,用于測試參數(shù)是否滿足使用的最低基本要求。mml28資訊網(wǎng)——每日最新資訊28at.com

// Service removes a service from a cluster and associated storage.// The last 3 storage backups are retained for whatever the storage retainment// period is.func Service(ctx context.Context, args Args) error {  if err := args.validate(); err != nil {    return err  }    start := drainService  _, err := Run[Args](ctx, args, start "Args")  if err != nil {    return fmt.Errorf("problem removing service %q: %w", args.Name, err)  }  return nil}

用戶只需調用 Service(),傳入 Args,如果出錯就會收到錯誤信息。用戶不需要看到狀態(tài)機模式,也不需要理解狀態(tài)機模式就能執(zhí)行操作。mml28資訊網(wǎng)——每日最新資訊28at.com

我們只需驗證 Args 是否正確,將狀態(tài)機的起始狀態(tài)設置為名為 drainService 的函數(shù),然后調用上面定義的 Run() 函數(shù)即可。mml28資訊網(wǎng)——每日最新資訊28at.com

func drainService(ctx context.Context, args Args) (Args, State[Args], error) {  l, err := args.Services.List(ctx)  if err != nil {    return args, nil, err  }  found := false  for _, entry := range l {    if entry == args.Name {      found = true      break    }  }  if !found {    return args, nil, fmt.Errorf("the service was not found")  }  if err := args.Services.Drain(ctx, args.Name); err != nil {    return args, nil, fmt.Errorf("problem draining the service: %w", err)  }  return args, removeService, nil}

我們的第一個狀態(tài)叫做 drainService(),實現(xiàn)了上面定義的狀態(tài)類型。mml28資訊網(wǎng)——每日最新資訊28at.com

它使用 Args 中定義的 Services 客戶端列出集群中的所有服務,如果找不到服務,就會返回錯誤并結束狀態(tài)機。mml28資訊網(wǎng)——每日最新資訊28at.com

如果找到服務,就會對服務執(zhí)行關閉。一旦完成,就進入下一個狀態(tài),即 removeService()。mml28資訊網(wǎng)——每日最新資訊28at.com

func removeService(ctx context.Context, args Args) (Args, State[Args], error) {  if err := args.Services.Remove(ctx, args.Name); err != nil {    return args, nil, fmt.Errorf("could not remove the service: %w", err)  }  hasStorage, err := args.Services.HasStorage(ctx, args.Name)  if err != nil {    return args, nil, fmt.Errorf("HasStorage() failed: %w", err)  }  if hasStorage{    return args, removeBackups, nil  }  return args, nil, nil}

removeService() 使用我們的 Services 客戶端將服務從群集中移除。mml28資訊網(wǎng)——每日最新資訊28at.com

調用 HasStorage() 方法確定是否有存儲,如果有,就會進入 removeBackups() 狀態(tài),否則就會返回 args, nil, nil,這將導致狀態(tài)機在無錯誤的情況下退出。mml28資訊網(wǎng)——每日最新資訊28at.com

這個示例說明如何根據(jù) Args 中的信息或代碼中的遠程調用在狀態(tài)機中創(chuàng)建分支。mml28資訊網(wǎng)——每日最新資訊28at.com

其他狀態(tài)調用由你自行決定。我們看看這種設計如何更適合測試此類操作。mml28資訊網(wǎng)——每日最新資訊28at.com

測試優(yōu)勢

這種模式首先鼓勵的是小塊的可測試代碼,模塊變得很容易分割,這樣當代碼塊變得太大時,只需創(chuàng)建新的狀態(tài)來隔離代碼塊。mml28資訊網(wǎng)——每日最新資訊28at.com

但更大的優(yōu)勢在于無需進行大規(guī)模端到端測試。由于操作流程中的每個階段都需要調用下一階段,因此會出現(xiàn)以下情況:mml28資訊網(wǎng)——每日最新資訊28at.com

  • 頂層調用者按一定順序調用所有子函數(shù)
  • 每個調用者都會調用下一個函數(shù)
  • 兩者的某種混合

兩者都會導致某種類型的端到端測試,而這種測試本不需要。mml28資訊網(wǎng)——每日最新資訊28at.com

如果我們對頂層調用者方法進行編碼,可能看起來像這樣:mml28資訊網(wǎng)——每日最新資訊28at.com

func Service(ctx context.Context, args Args) error {  ...  if err := drainService(ctx, args); err != nil {    return err  }  if err := removeService(ctx, args); err != nil {    return err  }  hasStorage, err := args.Services.HasStorage(ctx, args.Name)  if err != nil {    return err  }  if hasStorage{    if err := removeBackups(ctx, args); err != nil {      return err    }    if err := removeStorage(ctx, args); err != nil {      return err    }  }  return nil} 

如你所見,可以為所有子函數(shù)編寫單獨的測試,但要測試 Service(),現(xiàn)在必須對調用的所有客戶端或方法打樁。這看起來就像是端到端測試,而對于這類代碼來說,通常不是好主意。mml28資訊網(wǎng)——每日最新資訊28at.com

如果轉到功能調用鏈,情況也不會好到哪里去:mml28資訊網(wǎng)——每日最新資訊28at.com

func Service(ctx context.Context, args Args) error {  ...  return drainService(ctx, args)}func drainService(ctx context.Context, args Args) (Args, error) {  ...  return removeService(ctx, args)}func removeService(ctx context.Context, args Args) (Args, error) {  ...  hasStorage, err := args.Services.HasStorage(ctx, args.Name)  if err != nil {    return args, fmt.Errorf("HasStorage() failed: %w", err)  }    if hasStorage{    return removeBackups(ctx, args)  }  return nil}...

當我們測試時,越接近調用鏈的頂端,測試的實現(xiàn)就變得越困難。在 Service() 中,必須測試 drainService()、removeService() 以及下面所有調用。mml28資訊網(wǎng)——每日最新資訊28at.com

有幾種方法可以做到,但都不太好。mml28資訊網(wǎng)——每日最新資訊28at.com

如果使用狀態(tài)機,只需測試每個階段是否按要求運行,并返回想要的下一階段。mml28資訊網(wǎng)——每日最新資訊28at.com

頂層調用者甚至不需要測試,它只是調用 validate() 方法,并調用應該能夠被測試的 Run() 函數(shù)。mml28資訊網(wǎng)——每日最新資訊28at.com

我們?yōu)?nbsp;drainService() 編寫一個表驅動測試,這里會拷貝一份 drainService() 代碼,這樣就不用返回到前面看代碼了。mml28資訊網(wǎng)——每日最新資訊28at.com

func drainService(ctx context.Context, args Args) (Args, State[Args], error) {  l, err := args.Services.List(ctx)  if err != nil {    return args, nil, err  }  found := false  for _, entry := range l {    if entry == args.Name {      found = true      break    }  }  if !found {    return args, nil, fmt.Errorf("the service was not found")  }  if err := args.Services.Drain(ctx, args.Name); err != nil {    return args, nil, fmt.Errorf("problem draining the service: %w", err)  }  return args, removeService, nil}func TestDrainSerivce(t *testing.T) {  t.Parallel()  tests := []struct {    name      string    args      Args    wantErr   bool    wantState State[Args]  }{    {      name: "Error: Services.List() returns an error",      args: Args{        Services: &fakeServices{          list: fmt.Errorf("error"),        },      },      wantErr: true,    },    {      name: "Error: Services.List() didn't contain our service name",      args: Args{        Name: "myService",        Services: &fakeServices{          list: []string{"nope", "this", "isn't", "it"},        },      },      wantErr: true,    },    {      name: "Error: Services.Drain() returned an error",      args: Args{        Name: "myService",        Services: &fakeServices{          list:  []string{"yes", "mySerivce", "is", "here"},          drain: fmt.Errorf("error"),        },      },      wantErr: true,    },    {      name: "Success",      args: Args{        Name: "myService",        Services: &fakeServices{          list:  []string{"yes", "myService", "is", "here"},          drain: nil,        },      },      wantState: removeService,    },  }  for _, test := range tests {    _, nextState, err := drainService(context.Background(), test.args)    switch {    case err == nil && test.wantErr:      t.Errorf("TestDrainService(%s): got err == nil, want err != nil", test.name)      continue    case err != nil && !test.wantErr:      t.Errorf("TestDrainService(%s): got err == %s, want err == nil", test.name, err)      continue    case err != nil:      continue    }      gotState := methodName(nextState)    wantState := methodName(test.wantState)    if gotState != wantState {      t.Errorf("TestDrainService(%s): got next state %s, want %s", test.name, gotState, wantState)    }  }}

可以在 Go Playground[3]玩一下。mml28資訊網(wǎng)——每日最新資訊28at.com

如你所見,這避免了測試整個調用鏈,同時還能確保測試調用鏈中的下一個函數(shù)。mml28資訊網(wǎng)——每日最新資訊28at.com

這些測試很容易劃分,維護人員也很容易遵循。mml28資訊網(wǎng)——每日最新資訊28at.com

其他可能性

這種模式也有變種,即根據(jù) Args 中設置的字段確定狀態(tài),并跟蹤狀態(tài)的執(zhí)行以防止循環(huán)。mml28資訊網(wǎng)——每日最新資訊28at.com

在第一種情況下,狀態(tài)機軟件包可能是這樣的:mml28資訊網(wǎng)——每日最新資訊28at.com

type State[T any] func(ctx context.Context, args T) (T, State[T], error)type Args[T] struct {  Data T  Next State}func Run[T any](ctx context.Context, args Args[T], start State[T] "T any") (T, error) {  var err error  current := start  for {    if ctx.Err() != nil {      return args, ctx.Err()    }    args, current, err = current(ctx, args)    if err != nil {      return args, err    }    current = args.Next // Set our next stage    args.Next = nil // Clear this so to prevent infinite loops    if current == nil {      return args, nil    }  }}

可以很容易的將分布式跟蹤或日志記錄集成到這種設計中。mml28資訊網(wǎng)——每日最新資訊28at.com

如果希望推送大量數(shù)據(jù)并利用并發(fā)優(yōu)勢,不妨試試 stagedpipe 軟件包[4],其內置了大量高級功能,可以看視頻和 README 學習如何使用。mml28資訊網(wǎng)——每日最新資訊28at.com

希望這篇文章能讓你充分了解 Go 狀態(tài)機設計模式,現(xiàn)在你的工具箱里多了一個強大的新工具。mml28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-91031-0.htmlGolang 狀態(tài)機設計模式,你知道多少?

聲明:本網(wǎng)頁內容旨在傳播知識,若有侵權等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Python 字符串格式化方法性能與可讀性對比

下一篇: Node 之父新作:一個全新的 NPM 下載源工具!

標簽:
  • 熱門焦點
  • 從零到英雄:高并發(fā)與性能優(yōu)化的神奇之旅

    作者 | 波哥審校 | 重樓作為公司的架構師或者程序員,你是否曾經(jīng)為公司的系統(tǒng)在面對高并發(fā)和性能瓶頸時感到手足無措或者焦頭爛額呢?筆者在出道那會為此是吃盡了苦頭的,不過也得
  • WebRTC.Net庫開發(fā)進階,教你實現(xiàn)屏幕共享和多路復用!

    WebRTC.Net庫:讓你的應用更親民友好,實現(xiàn)視頻通話無痛接入! 除了基本用法外,還有一些進階用法可以更好地利用該庫。自定義 STUN/TURN 服務器配置WebRTC.Net 默認使用 Google 的
  • 雅柏威士忌多款單品價格大跌,泥煤頂流也不香了?

    來源 | 烈酒商業(yè)觀察編 | 肖海林今年以來,威士忌市場開始出現(xiàn)了降溫跡象,越來越多不斷暴漲的網(wǎng)紅威士忌也開始悄然回歸市場理性。近日,LVMH集團旗下蘇格蘭威士忌品牌雅柏(Ardbeg
  • Temu起訴SHEIN,跨境電商戰(zhàn)事升級

    來源 | 伯虎財經(jīng)(bohuFN)作者 | 陳平安日前據(jù)外媒報道,拼多多旗下跨境電商平臺Temu正對競爭對手SHEIN提起新訴訟,訴狀稱Shein“利用市場支配力量強迫服裝廠商與之簽訂獨家
  • 中國家電海外掘金正當時|出海專題

    作者|吳南南編輯|胡展嘉運營|陳佳慧出品|零態(tài)LT(ID:LingTai_LT)2023年,出海市場戰(zhàn)況空前,中國創(chuàng)業(yè)者在海外紛紛摩拳擦掌,以期能夠把中國的商業(yè)模式、創(chuàng)業(yè)理念、戰(zhàn)略打法輸出海外,他們依
  • ESG的面子與里子

    來源 | 光子星球撰文 | 吳坤諺編輯 | 吳先之三伏大幕拉起,各地高溫預警不絕,但處于厄爾尼諾大“烤”之下的除了眾生,還有各大企業(yè)發(fā)布的ESG報告。ESG是“環(huán)境保
  • 年輕人的“職場羞恥感”,無處不在

    作者:馮曉亭 陶 淘 李 欣 張 琳 馬舒葉來源:燃次元“人在職場,應該選擇什么樣的著裝?”近日,在網(wǎng)絡上,一個與著裝相關的帖子引發(fā)關注,在該帖子里,一位在高級寫字樓亞洲金
  • 到手價3099元起!iQOO Neo8 Pro今日首銷:安卓性能最強旗艦

    5月23日,iQOO如期舉行了新品發(fā)布會,全新的iQOO Neo8系列也正式與大家見面,包含iQOO Neo8和iQOO Neo8 Pro兩個版本,其中標準版搭載高通驍龍8+,而Pro版更
  • 榮耀Magic4 至臻版 首創(chuàng)智慧隱私通話 強勁影音系統(tǒng)

    2022年第一季度臨近尾聲,在該季度內,許多品牌陸續(xù)發(fā)布自己的最新產品,讓大家從全新的角度來了解當今的手機技術。手機是電子設備中,更新迭代十分迅速的一款產品,基
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
99人久久精品视频最新地址| 国产农村妇女毛片精品久久麻豆| 亚洲丶国产丶欧美一区二区三区| 欧美性猛交一区二区三区精品| 久久精品国语| 久久综合给合| 欧美日韩精品一区二区三区四区| 欧美激情视频免费观看| 国产精品高潮呻吟久久av无限| 欧美日韩国产色综合一二三四| 欧美大尺度在线| 免费在线亚洲欧美| 欧美系列亚洲系列| 激情欧美一区二区| 99视频一区| 久久激情中文| 欧美极品一区二区三区| 国产精品夜夜嗨| 亚洲黄色在线视频| 亚洲在线视频观看| 久热这里只精品99re8久| 欧美日韩中文字幕在线| 国产一区二区三区高清| 日韩一区二区精品在线观看| 欧美一区二区三区日韩视频| 欧美护士18xxxxhd| 国产又爽又黄的激情精品视频| 好吊成人免视频| 99re视频这里只有精品| 久久精品国产综合精品| 欧美丝袜一区二区三区| 亚洲成在线观看| 亚洲一区二区三区高清| 欧美高清在线观看| 国产在线不卡| 亚洲视频大全| 欧美高清自拍一区| 韩国福利一区| 亚洲国产成人av好男人在线观看| 亚洲精品永久免费| 久久国产视频网站| 欧美视频中文字幕| 91久久精品一区| 久久久精彩视频| 国产精品亚洲片夜色在线| 亚洲精品影院在线观看| 欧美在线在线| 国产精品成人v| 韩国三级电影一区二区| 亚洲无亚洲人成网站77777 | 一区福利视频| 亚洲欧美久久久久一区二区三区| 久久精品网址| 国产精品日韩一区二区三区| 黄色国产精品一区二区三区| 亚洲一区二区视频| 久久精品国产亚洲a| 国产精品老女人精品视频| 日韩网站在线看片你懂的| 免费在线播放第一区高清av| 国产亚洲视频在线观看| 午夜精品久久久99热福利| 欧美日韩综合一区| 亚洲精品男同| 欧美www视频| 在线精品在线| 久久亚洲风情| 韩日欧美一区二区三区| 欧美一区二区三区四区视频| 欧美国产精品中文字幕| 在线看日韩av| 久久综合一区二区三区| 影音先锋亚洲视频| 久久青草福利网站| 国语对白精品一区二区| 欧美一区二区三区免费看 | 久久理论片午夜琪琪电影网| 国产三区精品| 久久高清福利视频| 黄色亚洲在线| 女生裸体视频一区二区三区| 亚洲福利视频一区| 欧美成人精品激情在线观看 | 欧美伦理一区二区| 亚洲精品在线观看视频| 欧美精品国产精品| 亚洲精品在线免费观看视频| 欧美日韩1234| 一区二区三区蜜桃网| 美女脱光内衣内裤视频久久影院| 国产精品久久久久99| 亚洲一区二区三区乱码aⅴ| 国产精品va在线播放我和闺蜜| 亚洲国产va精品久久久不卡综合| 欧美亚洲在线| 黑人操亚洲美女惩罚| 亚洲综合首页| 欧美日韩精品一区二区天天拍小说| 一区二区三区在线不卡| 久久尤物视频| 亚洲精品免费网站| 欧美少妇一区二区| 亚洲永久免费精品| 国产视频在线观看一区| 久久亚洲精品欧美| 国产亚洲毛片| 鲁大师影院一区二区三区| 亚洲欧洲精品一区二区三区| 欧美日韩国产一区二区三区地区| 亚洲激情av在线| 欧美日韩和欧美的一区二区| 亚洲欧美日韩国产| 国语自产精品视频在线看一大j8 | 欧美人与禽猛交乱配| 亚洲一区二区欧美| 国产午夜精品一区理论片飘花 | 久久成人免费电影| 亚洲国产精品va在线观看黑人| 久久久欧美一区二区| 亚洲黄网站黄| 欧美系列电影免费观看| 欧美在线视频免费观看| 亚洲黑丝一区二区| 国产精品二区在线| 久久青草欧美一区二区三区| 日韩视频免费观看高清在线视频| 欧美国产视频在线| 亚洲欧美国产精品专区久久| 激情视频一区二区三区| 欧美日韩精品免费| 一区二区电影免费观看| 欧美日韩一区二区免费视频| 欧美一区视频| 经典三级久久| 欧美日韩免费在线观看| 久久精品国产久精国产爱| 亚洲精选视频免费看| 国产日韩亚洲欧美综合| 欧美另类69精品久久久久9999| 一本色道**综合亚洲精品蜜桃冫 | 亚洲免费av网站| 国产乱码精品一区二区三区五月婷 | 欧美成人综合网站| 午夜在线视频观看日韩17c| 国产精品美女久久福利网站| 久久久久久久欧美精品| 一区二区三区视频在线| 伊人成综合网伊人222| 国产精品青草久久久久福利99| 欧美一区二区视频观看视频| 亚洲人在线视频| 国产在线不卡精品| 免费观看欧美在线视频的网站| 亚洲欧洲日产国产综合网| 国产欧美日韩精品a在线观看| 久久久国际精品| 中文一区二区| 亚洲国产日日夜夜| 国产专区精品视频| 国产精品国色综合久久| 欧美国产三区| 久久婷婷国产综合尤物精品| 亚洲欧美日韩视频一区| 亚洲人成网站色ww在线| 韩国女主播一区| 欧美精品1区2区3区| 久久欧美肥婆一二区| 亚洲欧美在线观看| 99精品欧美一区二区三区 | 国产在线欧美| 国产精品久久久久婷婷| 欧美屁股在线| 男人的天堂成人在线| 久久精品国产久精国产思思| 亚洲精品乱码久久久久久久久| 国产精品久久久久久久午夜| 久久精品网址| 欧美一级片在线播放| 亚洲一品av免费观看| 夜夜嗨av色一区二区不卡| 亚洲经典自拍| 亚洲风情亚aⅴ在线发布| 精品69视频一区二区三区| 国产一区二区高清不卡| 国产农村妇女毛片精品久久莱园子| 猛干欧美女孩| 亚洲在线播放电影| 在线亚洲一区观看| 99亚洲一区二区| 99精品国产99久久久久久福利| 国产日韩精品一区二区浪潮av| 免费永久网站黄欧美| 午夜精品久久久99热福利| 中文av一区二区| 亚洲色诱最新| 亚洲一区二区三区高清| 亚洲视频在线一区| 中文欧美在线视频| av成人激情| av成人黄色| 亚洲一区二区三区免费观看| 亚洲欧美久久久| 欧美伊久线香蕉线新在线| 久久成人精品电影|