麻豆传媒视频在线,国产91精品不卡视频,欧美jizz19性欧美,污视频网站在线观看,91涩漫在线观看,伊人发布在线,九色porny丨首页在线,福利视频一区,久久av网址,久久人人视频

當(dāng)前位置:首頁 > 網(wǎng)站舊欄目 > 學(xué)習(xí)園地 > 設(shè)計軟件教程 > 如何寫超強(qiáng)伸縮性的多游戲玩家服務(wù)器

如何寫超強(qiáng)伸縮性的多游戲玩家服務(wù)器
2010-01-13 23:14:35  作者:  來源:
介紹
本文以我的OpenPoker項目為例子,講述了一個構(gòu)建超強(qiáng)伸縮性的在線多游戲玩家系統(tǒng)。
OpenPoker是一個超強(qiáng)多玩家紙牌服務(wù)器,具有容錯、負(fù)載均衡和無限伸縮性等特性。
源代碼位于我的個人站點上,大概10,000行代碼,其中1/3是測試代碼。

在OpenPoker最終版本敲定之前我做了大量調(diào)研,我嘗試了Delphi、Python、C#、C/C++和Scheme。我還用Common Lisp寫了紙牌引擎。
雖然我花費(fèi)了9個月的時間研究原型,但是最終重寫時只花了6個星期的時間。
我認(rèn)為我所節(jié)約的大部分時間都得益于選擇Erlang作為平臺。

相比之下,舊版本的OpenPoker花費(fèi)了一個4~5人的團(tuán)隊9個月時間。

Erlang是什么東東?
我建議你在繼續(xù)閱讀本文之前瀏覽下Erlang FAQ,這里我給你一個簡單的總結(jié)...

Erlang是一個函數(shù)式動態(tài)類型編程語言并自帶并發(fā)支持。它是由Ericsson特別為控制開關(guān)、轉(zhuǎn)換協(xié)議等電信應(yīng)用設(shè)計的。
Erlang十分適合構(gòu)建分布式、軟實時的并發(fā)系統(tǒng)。

由Erlang所寫的程序通常由成百上千的輕量級進(jìn)程組成,這些進(jìn)程通過消息傳遞來通訊。
Erlang進(jìn)程間的上下文切換通常比C程序線程的上下文切換要廉價一到兩個數(shù)量級。

使用Erlang寫分布式程序很簡單,因為它的分布式機(jī)制是透明的:程序不需要了解它們是否分布。

Erlang運(yùn)行時環(huán)境是一個虛擬機(jī),類似于Java虛擬機(jī)。這意味著在一個價格上編譯的代碼可以在任何地方運(yùn)行。
運(yùn)行時系統(tǒng)也允許在一個運(yùn)行著的系統(tǒng)上不間斷的更新代碼。
如果你需要額外的性能提升,字節(jié)碼也可以編譯成本地代碼。

請移步Erlang site,參考Getting started、Documentation和Exampes章節(jié)等資源。

為何選擇Erlang?
構(gòu)建在Erlang骨子里的并發(fā)模型特別適合寫在線多玩家服務(wù)器。

一個超強(qiáng)伸縮性的多玩家Erlang后端構(gòu)建為擁有不同“節(jié)點”的“集群”,不同節(jié)點做不同的任務(wù)。
一個Erlang節(jié)點是一個Erlang VM實例,你可以在你的桌面、筆記本電腦或服務(wù)器上上運(yùn)行多個Erlang節(jié)點/VM。
推薦一個CPU一個節(jié)點。

Erlang節(jié)點會追蹤所有其他和它相連的節(jié)點。向集群里添加一個新節(jié)點所需要的只是將該新節(jié)點指向一個已有的節(jié)點。
一旦這兩個節(jié)點建立連接,集群里所有其他的節(jié)點都會知曉這個新節(jié)點。

Erlang進(jìn)程使用一個進(jìn)程id來相互發(fā)消息,進(jìn)程id包含了節(jié)點在哪里運(yùn)行的信息。進(jìn)程不需要知道其他進(jìn)程在哪里就可以通訊。
連接在一起的Erlang節(jié)點集可以看作一個網(wǎng)格或者超級計算設(shè)備。

超多玩家游戲里玩家、NPC和其他實體最好建模為并行運(yùn)行的進(jìn)程,但是并行很難搞是眾所皆知的。Erlang讓并行變得簡單。

Erlang的位語法∞讓它在處理結(jié)構(gòu)封裝/拆解的能力上比Perl和Python都要強(qiáng)大。這讓Erlang特別適合處理二進(jìn)制網(wǎng)絡(luò)協(xié)議。

OpenPoker架構(gòu)
OpenPoker里的任何東西都是進(jìn)程。玩家、機(jī)器人、游戲等等多是進(jìn)程。
對于每個連接到OpenPoker的客戶端都有一個玩家“代理”來處理網(wǎng)絡(luò)消息。
根據(jù)玩家是否登錄來決定部分消息忽略,而另一部分消息則發(fā)送給處理紙牌游戲邏輯的進(jìn)程。

紙牌游戲進(jìn)程是一個狀態(tài)機(jī),包含了游戲每一階段的狀態(tài)。
這可以讓我們將紙牌游戲邏輯當(dāng)作堆積木,只需將狀態(tài)機(jī)構(gòu)建塊放在一起就可以添加新的紙牌游戲。
如果你想了解更多的話可以看看cardgame.erl的start方法。

紙牌游戲狀態(tài)機(jī)根據(jù)游戲狀態(tài)來決定不同的消息是否通過。
同時也使用一個單獨(dú)的游戲進(jìn)程來處理所有游戲共有的一些東西,如跟蹤玩家、pot和限制等等。
當(dāng)在我的筆記本電腦上模擬27,000個紙牌游戲時我發(fā)現(xiàn)我擁有大約136,000個玩家以及總共接近800,000個進(jìn)程。

下面我將以O(shè)penPoker為例子,專注于講述怎樣基于Erlang讓實現(xiàn)伸縮性、容錯和負(fù)載均衡變簡單。
我的方式不是特別針對紙牌游戲。同樣的方式可以用在其他地方。

伸縮性
我通過多層架構(gòu)來實現(xiàn)伸縮性和負(fù)載均衡。
第一層是網(wǎng)關(guān)節(jié)點。
游戲服務(wù)器節(jié)點組成第二層。
Mnesia“master”節(jié)點可以認(rèn)為是第三層。

Mnesia是Erlang實時分布式數(shù)據(jù)庫。Mnesia FAQ有一個很詳細(xì)的解釋。Mnesia基本上是一個快速的、可備份的、位于內(nèi)存中的數(shù)據(jù)庫。
Erlang里沒有對象,但是Mnesia可以認(rèn)為是面向?qū)ο蟮,因為它可以存儲任何Erlang數(shù)據(jù)。

有兩種類型的Mnesia節(jié)點:寫到硬盤的節(jié)點和不寫到硬盤的節(jié)點。除了這些節(jié)點,所有其他的Mnesia節(jié)點將數(shù)據(jù)保存在內(nèi)存中。
在OpenPoker里Mnesia master節(jié)點會將數(shù)據(jù)寫入硬盤。網(wǎng)關(guān)和游戲服務(wù)器從Mnesia master節(jié)點獲得數(shù)據(jù)庫并啟動,它們只是內(nèi)存節(jié)點。

當(dāng)啟動Mnesia時,你可以給Erlang VM和解釋器一些命令行參數(shù)來告訴Mnesia master數(shù)據(jù)庫在哪里。
當(dāng)一個新的本地Mnesia節(jié)點與master Mnesia節(jié)點建立連接之后,新節(jié)點變成master節(jié)點集群的一部分。

假設(shè)master節(jié)點位于apple和orange節(jié)點上,添加一個新的網(wǎng)關(guān)、游戲服務(wù)器等等。OpenPoker集群簡單的如下所示:
Java代碼 復(fù)制代碼
  1. erl -mnesia extra_db_nodes \['db@apple','db@orange'\] -s mnesia start  

-s mnesia start相當(dāng)于這樣在erlang shell里啟動Mnedia:
Java代碼 復(fù)制代碼
  1. erl -mnesia extra_db_nodes \['db@apple','db@orange'\]   
  2. Erlang (BEAM) emulator version 5.4.8 [source] [hipe] [threads:0]   
  3.   
  4. Eshell V5.4.8 (abort with ^G)   
  5. 1> mnesia:start().   
  6. ok  

OpenPoker在Mnesia表里保存配置信息,并且這些信息在Mnesia啟動后立即自動被新的節(jié)點下載。零配置!

容錯
通過添加廉價的Linux機(jī)器到我的服務(wù)器集群,OpenPoker讓我隨心所欲的變大。
將幾架1U的服務(wù)器放在一起,這樣你就可以輕易的處理500,000甚至1,000,000的在線玩家。這對MMORPG也是一樣。

我讓一些機(jī)器運(yùn)行網(wǎng)關(guān)節(jié)點,另一些運(yùn)行數(shù)據(jù)庫master來寫數(shù)據(jù)庫事務(wù)到硬盤,讓其他的機(jī)器運(yùn)行游戲服務(wù)器。
我限制游戲服務(wù)器接受最多5000個并發(fā)的玩家,這樣當(dāng)游戲服務(wù)器崩潰時最多影響5000個玩家。

值得注意的是,當(dāng)游戲服務(wù)器崩潰時沒有任何信息丟失,因為所有的Mnesia數(shù)據(jù)庫事務(wù)都是實時備份到其他運(yùn)行Mnesia以及游戲服務(wù)器的節(jié)點上的。

為了預(yù)防出錯,游戲客戶端必須提供一些援助來平穩(wěn)的重連接OpenPoker集群。
一旦客戶端發(fā)現(xiàn)一個網(wǎng)絡(luò)錯誤,它應(yīng)該連接網(wǎng)關(guān),接受一個新的游戲服務(wù)器地址,然后重新連接新的游戲服務(wù)器。
下面發(fā)生的事情需要一定技巧,因為不同類型的重連接場景需要不同的處理。

OpenPoker會處理如下幾種重連接的場景:
1,游戲服務(wù)器崩潰
2,客戶端崩潰或者由于網(wǎng)絡(luò)原因超時
3,玩家在線并且在一個不同的連接上
4,玩家在線并且在一個不同的連接上并在一個游戲中

最常見的場景是一個客戶端由于網(wǎng)絡(luò)出錯而重新連接。
比較少見但仍然可能的場景是客戶端已經(jīng)在一臺機(jī)器上玩游戲,而此時從另一臺機(jī)器上重連接。

每個發(fā)送給玩家的OpenPoker游戲緩沖包和每個重連接的客戶端將首先接受所有的游戲包,因為游戲不是像通常那樣正常啟動然后接受包。
OpenPoker使用TCP連接,這樣我不需要擔(dān)心包的順序——包會按正確的順序到達(dá)。

每個客戶端連接由兩個OpenPoker進(jìn)程來表現(xiàn):socket進(jìn)程和真正的玩家進(jìn)程。
先使用一個功能受限的visitor進(jìn)程,直到玩家登錄。例如visitor不能參加游戲。
在客戶端斷開連接后,socket進(jìn)程死掉,而玩家進(jìn)程仍然活著。

當(dāng)玩家進(jìn)程嘗試發(fā)送一個游戲包時可以通知一個死掉的socket,并讓它自己進(jìn)入auto-play模式或者掛起。
在重新連接時登錄代碼將檢查死掉的socket和活著的玩家進(jìn)程的結(jié)合。代碼如下:
Java代碼 復(fù)制代碼
  1. login({atomic, [Player]}, [_Nick, Pass|_] = Args)   
  2.   when is_record(Player, player) ->   
  3.     Player1 = Player#player {   
  4.       socket = fix_pid(Player#player.socket),   
  5.       pid = fix_pid(Player#player.pid)   
  6.     },   
  7.     Condition = check_player(Player1, [Pass],   
  8.       [   
  9.         fun is_account_disabled/2,   
  10.         fun is_bad_password/2,   
  11.         fun is_player_busy/2,   
  12.         fun is_player_online/2,   
  13.         fun is_client_down/2,   
  14.         fun is_offline/2  
  15.       ]),   
  16.     ...  

condition本身由如下代碼決定:
Java代碼 復(fù)制代碼
  1. is_player_busy(Player, _) ->   
  2.   {Online, _} = is_player_online(Player, []),   
  3.   Playing = Player#player.game /= none,   
  4.   {Online and Playing, player_busy}.   
  5.   
  6. is_player_online(Player, _) ->   
  7.   SocketAlive = Player#player.socket /= none,   
  8.   PlayerAlive = Player#player.pid /= none,   
  9.   {SocketAlive and PlayerAlive, player_online}.   
  10.   
  11. is_client_down(Player, _) ->   
  12.   SocketDown = Player#player.socket == none,   
  13.   PlayerAlive = Player#player.pid /= none,   
  14.   {SocketDown and PlayerAlive, client_down}.   
  15.   
  16. is_offline(Player, _) ->   
  17.   SocketDown = Player#player.socket == none,   
  18.   PlayerDown = Player#player.pid == none,   
  19.   {SocketDown and PlayerDown, player_offline}.  

注意login方法的第一件事是修復(fù)死掉的進(jìn)程id:
Java代碼 復(fù)制代碼
  1. fix_pid(Pid)   
  2.   when is_pid(Pid) ->   
  3.     case util:is_process_alive(Pid) of   
  4.     true ->   
  5.       Pid;   
  6.     _->   
  7.       none   
  8.     end;   
  9.   
  10. fix_pid(Pid) ->   
  11.     Pid.  

以及:
Java代碼 復(fù)制代碼
  1. -module(util).   
  2.   
  3. -export([is_process_alive/1]).   
  4.   
  5. is_process_alive(Pid)   
  6.   when is_pid(Pid) ->   
  7.     rpc:call(node(Pid), erlang, is_process_alive, [Pid]).  

Erlang里一個進(jìn)程id包括正在運(yùn)行的進(jìn)程的節(jié)點的id。
is_pid(Pid)告訴我它的參數(shù)是否是一個進(jìn)程id(pid),但是不能告訴我進(jìn)程是活著還是死了。
Erlang自帶的erlang:is_process_alive(Pid)告訴我一個本地進(jìn)程(運(yùn)行在同一節(jié)點上)是活著還是死了,但沒有檢查遠(yuǎn)程節(jié)點是或者還是死了的is_process_alive變種。

還好,我可以使用Erlang rpc工具和node(pid)來在遠(yuǎn)程節(jié)點上調(diào)用is_process_alive()。
事實上,這跟在本地節(jié)點上一樣工作,這樣上面的代碼就可以作為全局分布式進(jìn)程檢查器。

剩下的唯一的事情是在不同的登錄條件上活動。
最簡單的情況是玩家離線,我期待一個玩家進(jìn)程,連接玩家到socket并更新player record。
Java代碼 復(fù)制代碼
  1. login(Player, player_offline, [Nick, _, Socket]) ->   
  2.   {ok, Pid} = player:start(Nick),   
  3.   OID = gen_server:call(Pid, 'ID'),   
  4.   gen_server:cast(Pid, {'SOCKET', Socket}),   
  5.   Player1 = Player#player {   
  6.     oid = OID,   
  7.     pid = Pid,   
  8.     socket = Socket   
  9.   },   
  10.   {Player1, {ok, Pid}}.  

假如玩家登陸信息不匹配,我可以返回一個錯誤并增加錯誤登錄次數(shù)。如果次數(shù)超過一個預(yù)定義的最大值,我就禁止該帳號:
Java代碼 復(fù)制代碼
  1. login(Player, bad_password, _) ->   
  2.   N = Player#player.login_errors + 1,   
  3.   {atomic, MaxLoginErrors} =   
  4.   db:get(cluster_config, 0, max_login_errors),   
  5.   if  
  6.   N > MaxLoginErrors ->   
  7.     Player1 = Player#player {   
  8.       disabled = true  
  9.     },   
  10.     {Player1, {error, ?ERR_ACCOUNT_DISABLED}};   
  11.   true ->   
  12.     Player1 = Player#player {   
  13.       login_errors =N   
  14.     },   
  15.     {Player1, {error, ?ERR_BAD_LOGIN}}   
  16.   end;   
  17.   
  18. login(Player, account_disabled, _) ->   
  19.     {Player, {error, ?ERR_ACCOUNT_DISABLED}};  

注銷玩家包括使用Object ID(只是一個數(shù)字)找到玩家進(jìn)程id,停止玩家進(jìn)程,然后在數(shù)據(jù)庫更新玩家record:
Java代碼 復(fù)制代碼
  1. logout(OID) ->   
  2.   case db:find(player, OID) of   
  3.   {atomic, [Player]} ->   
  4.     player:stop(Player#player.pid),   
  5.     {atomic, ok} = db:set(player, OID,   
  6.       [{pid, none},   
  7.       {socket, none}];   
  8.   _->   
  9.     oops   
  10.   end.  

這樣我就可以完成多種重連接condition,例如從不同的機(jī)器重連接,我只需先注銷再登錄:
Java代碼 復(fù)制代碼
  1. login(Player, player_online, Args) ->   
  2.   logout(Player#player.oid),   
  3.   login(Player, player_offline, Args);  

如果玩家空閑時客戶端重連接,我所需要做的只是在玩家record里替換socket進(jìn)程id然后告訴玩家進(jìn)程新的socket:
Java代碼 復(fù)制代碼
  1. login(Player, client_down, [_, _, SOcket]) ->   
  2.   gen_server:cast(Player#player.pid, {'SOCKET', Socket}),   
  3.   Player1 = Player#player {   
  4.     socket = Socket   
  5.   },   
  6.   {Player1, {ok, Player#player.pid}};  

如果玩家在游戲中,這是我們運(yùn)行上面的代碼,然后告訴游戲重新發(fā)送時間歷史:
Java代碼 復(fù)制代碼
  1. login(Player, player_busy, Args) ->   
  2.   Temp = login(Player, client_down, Args),   
  3.   cardgame:cast(Player#player.game,   
  4.     {'RESEND UPDATES', Player#player.pid}),   
  5.   Temp;  

總體來說,一個實時備份數(shù)據(jù)庫,一個知道重新建立連接到不同的游戲服務(wù)器的客戶端和一些有技巧的登錄代碼運(yùn)行我提供一個高級容錯系統(tǒng)并且對玩家透明。

負(fù)載均衡
我可以構(gòu)建自己的OpenPoker集群,游戲服務(wù)器數(shù)量大小隨心所欲。
我希望每臺游戲服務(wù)器分配5000個玩家,然后在集群的活動游戲服務(wù)器間分散負(fù)載。
我可以在任何時間添加一個新的游戲服務(wù)器,并且它們將自動賦予自己接受新玩家的能力。

網(wǎng)關(guān)節(jié)點分散玩家負(fù)載到OpenPoker集群里活動的游戲服務(wù)器。
網(wǎng)關(guān)節(jié)點的工作是選擇一個隨機(jī)的游戲服務(wù)器,詢問它所連接的玩家數(shù)量和它的地址、主機(jī)和端口號。
一旦網(wǎng)關(guān)找到一個游戲服務(wù)器并且連接的玩家數(shù)量少于最大值,它將返回該游戲服務(wù)器的地址到連接的客戶端,然后關(guān)閉連接。

網(wǎng)關(guān)上絕對沒有壓力,網(wǎng)關(guān)的連接都非常短。你可以使用非常廉價的機(jī)器來做網(wǎng)關(guān)節(jié)點。

節(jié)點一般都成雙成對出現(xiàn),這樣一個節(jié)點崩潰后還有另一個繼續(xù)工作。你可能需要一個類似于Round-robin DNS的機(jī)制來保證不只一個單獨(dú)的網(wǎng)關(guān)節(jié)點。

網(wǎng)關(guān)怎么知曉游戲服務(wù)器?

OpenPoker使用Erlang Distirbuted Named Process Groups工具來為游戲服務(wù)器分組。
該組自動對所有的節(jié)點全局可見。
新的游戲服務(wù)器進(jìn)入游戲服務(wù)器后,當(dāng)一個游戲服務(wù)器節(jié)點崩潰時它被自動刪除。

這是尋找容量最大為MaxPlayers的游戲服務(wù)器的代碼:
Java代碼 復(fù)制代碼
  1. find_server(MaxPlayers) ->   
  2.   case pg2:get_closest_pid(?GAME_SERVER) of   
  3.   Pid when is_pid(Pid) ->   
  4.     {Time, {Host, Port}} = timer:tc(gen_server, call, [Pid, 'WHERE']),   
  5.     Coutn = gen_server:call(Pid, 'USER COUNT'),   
  6.     if  
  7.       Count < MaxPlayers ->   
  8.         io:format("~s:~w ~w players~n", [Host, Port, Count]),   
  9.         {Host, Port};   
  10.       true ->   
  11.         io:format("~s:~w is full...~n", [Host, Port]),   
  12.         find_server(MaxPlayers)   
  13.     end;   
  14.   Any ->   
  15.     Any   
  16.   end.  

pg2:get_closest_pid()返回一個隨機(jī)的游戲服務(wù)器進(jìn)程id,因為網(wǎng)關(guān)節(jié)點上不允許跑任何游戲服務(wù)器。
如果一個游戲服務(wù)器進(jìn)程id返回,我詢問游戲服務(wù)器的地址(host和port)和連接的玩家數(shù)量。
只要連接的玩家數(shù)量少于最大值,我返回游戲服務(wù)器地址給調(diào)用者,否則繼續(xù)查找。

多出口電源插座中間件
OpenPoker是一個開源軟件,我最近在將它推銷給多個紙牌游戲廠商。
所有的廠商都有同樣的伸縮性和容錯的問題,即使做了多年開發(fā)。
有的最近剛剛完成服務(wù)器軟件重寫,而有的剛剛開始。
所有的廠商都嚴(yán)重依賴于它們的Java基礎(chǔ)架構(gòu),可以理解,它們不想換Erlang。

看來有一個需求必須滿足。我思考的越多,發(fā)現(xiàn)Erlang越適合提供高效的解決方案。
我把這個解決方案看作一個多出口電源插座。

你可以像寫一個使用數(shù)據(jù)庫后端的基于socket的服務(wù)器一樣來寫游戲服務(wù)器。
事實上,目前游戲服務(wù)器就是這樣寫的。
游戲服務(wù)器是標(biāo)準(zhǔn)的電源插頭,游戲服務(wù)器的多個實例插入到電源插座中,而玩家從另一端流過。

你提供游戲服務(wù)器,而我提供伸縮性、負(fù)載均衡和容錯。
我讓玩家連接到電源插座并監(jiān)控你的游戲服務(wù)器,必要時重啟它們。
當(dāng)一個游戲服務(wù)器崩潰時我將玩家切換到另一臺游戲服務(wù)器,你可以往插座里插入任意多的游戲服務(wù)器。

電源插座中間件是一個黑盒子,它位于你的玩家和你的服務(wù)器之間,很可能不需要你改動任何代碼。
你會得到伸縮性、負(fù)載均衡、容錯等諸多益處而只需改動極少的一部分現(xiàn)有架構(gòu)。

今天你就可以用Erlang寫這個中間件,然后運(yùn)行在一個內(nèi)核調(diào)優(yōu)過以支持大量TCP連接的Linux機(jī)器上,而將你的服務(wù)器放在一個防火墻后面。
即使你不這樣做,我建議你馬上仔細(xì)看看Erlang,想想如何使用它來簡化你的超強(qiáng)多玩家服務(wù)器架構(gòu)。而我會在這兒幫助你!

安徽新華電腦學(xué)校專業(yè)職業(yè)規(guī)劃師為你提供更多幫助【在線咨詢
欧美在线视频a| 日韩电影免费网址| 国产黄色免费在线观看| 东北一级毛片| 国产精品久久久久久久久久东京 | 91欧美一区二区| 久久这里只有精品首页| 国产日韩欧美制服另类| 1024成人网| 一本到一区二区三区| 欧美丰满少妇xxxxx高潮对白| 日韩精品一区国产麻豆| 亚洲天堂网站在线观看视频| 久久久av一区| 欧美丰满少妇xxxxx做受| 欧美制服第一页| 又黄又www的网站| 国产福利免费在线观看| av免费网站在线| 深夜视频一区二区| 校花撩起jk露出白色内裤国产精品 | 91在线看黄| 欧美激情网站| 亚洲日本一区二区三区在线| 欧美丰满日韩| 日本在线不卡一区| 99精品偷自拍| 亚洲一二三四久久| 7777精品伊人久久久大香线蕉超级流畅 | 91豆麻精品91久久久久久| 91精品国产综合久久精品性色| 亚洲精品xxx| 777777777亚洲妇女| 日本一二区视频| 亚洲精品传媒| 日韩城人网站| 中文字幕一区二区三区欧美日韩| 久久精品盗摄| 中文字幕av一区二区三区| 色国产综合视频| 国产亚洲欧美aaaa| 国产日产欧美精品| 性xxxxfjsxxxxx欧美| 亚洲精品三区| 亚洲黄色成人| 久久蜜臀中文字幕| 欧美视频日韩视频| 欧美丰满少妇xxxx| 狠狠色伊人亚洲综合网站l| 国产精品原创视频| 91久久综合| 国产精品欧美久久久久一区二区| 欧美日韩一级片在线观看| www.亚洲免费视频| 一个人免费观看视频www在线播放 一个人免费视频www在线观看 | 激情五月婷婷综合网| 中文字幕亚洲不卡| 日韩电影第一页| 国产精品久久久久久一区二区| 高清美女视频一区| 久久婷婷国产| 国产精品一二三四| 欧美日韩亚洲综合| 欧美中文字幕在线视频| 麻豆网站视频在线观看| 天天躁日日躁狠狠躁欧美巨大小说| 久久精品国产一区二区三| 午夜精品久久久久久久久久 | 超碰在线免费公开| 日韩欧美黄色| 成人美女在线观看| 欧美一区二区三区视频免费| 国产成人综合av| 桃花岛tv亚洲品质| 国产欧美在线| 欧美小视频在线| 97视频在线观看视频免费视频 | 精品福利樱桃av导航| 精品国产一区二区三区久久久 | 色女人在线视频| 天天精品视频| 18成人在线视频| 久久五月天综合| 在线播放蜜桃麻豆| 欧美日韩日本国产亚洲在线 | 日本韩国欧美国产| 日本亚洲精品在线观看| 黄色激情在线播放| 国产亚洲毛片在线| 日韩欧中文字幕| 国产精品成人国产乱一区| 一区二区视频免费完整版观看| 男人的天堂亚洲| 欧美亚洲一区二区三区四区| 国产精品免费网站| 2021年精品国产福利在线| 成人一区二区三区中文字幕| 亚洲黄页视频免费观看| 国产在线视频网| 欧美久久一级| 欧洲视频一区二区| 免费观看又污又黄在线观看国产| 久久av导航| 偷拍亚洲欧洲综合| 国产美女久久久| 免费毛片在线不卡| 亚洲最新在线观看| 国产精品久久久久久影视| 北条麻妃在线一区二区免费播放| 久久综合色天天久久综合图片| 伊人久久久久久久久久| 8x8ⅹ拨牐拨牐拨牐在线观看| 久久久久久黄| 亚洲成年人影院在线| 免费观看在线黄色网| 噜噜噜躁狠狠躁狠狠精品视频| 日韩三级视频在线看| 999在线视频| 日本美女一区二区三区| 日韩av中文字幕在线| 欧美xxxx性xxxxx高清| 国内精品第一页| 久久影院在线观看| 久久一级大片| 一区二区三区在线免费观看 | 久久久久久网址| 激情不卡一区二区三区视频在线| 91浏览器在线视频| 97福利一区二区| 女同另类激情重口| 色系网站成人免费| 免费毛片在线| 久久av资源站| 欧美乱妇高清无乱码| 综合激情五月婷婷| 欧美日韩国产限制| 成年人在线视频| 激情欧美一区二区| 欧美激情喷水视频| 亚洲国产最新| 777色狠狠一区二区三区| av人人综合网| 国产丝袜欧美中文另类| 国产精品一区二区三区免费视频 | 久久综合影视| 久久精品小视频| 电影一区二区在线观看| 五月激情综合色| 91在线直播| 99久久99久久精品免费看蜜桃 | 中文在线资源在线| 日韩不卡在线观看日韩不卡视频| 色偷偷噜噜噜亚洲男人的天堂| 99精品美女视频在线观看热舞| 亚洲电影第三页| 免费看a在线观看| 久久久欧美精品sm网站| 免费成年网站| 精品亚洲成a人在线观看| 51精品在线观看| 亚洲第一精品影视| 久久99精品视频一区97| 久久综合av| 色噜噜亚洲精品中文字幕| 亚洲v天堂v手机在线| 亚洲大胆美女视频| 97久久综合区小说区图片区| 欧美日韩视频在线观看一区二区三区| 免费毛片在线看片免费丝瓜视频 | 深夜视频一区二区| 精品久久久久久久久久久久| 高h视频在线观看| 中文字幕一区二区视频| 狠狠色伊人亚洲综合网站l| 久久影院午夜论| 在线观看国产麻豆| av电影天堂一区二区在线| 国产1区2区3区| 国产一区二区美女诱惑| 成年人福利视频| 国产成人丝袜美腿| 女人黄色片免费| jvid福利写真一区二区三区| 国产在线制服美女| 97久久精品人人爽人人爽蜜臀| 95影院理论片在线观看| 成人免费视频一区| 国产欧美亚洲视频| 国产精品伊人色| 三级视频在线| 亚洲人精品午夜| 中文在线аv在线| 欧美日韩在线直播| 2021年精品国产福利在线| 国产午夜精品理论片a级探花| 精品午夜久久| 91精品国产高清| 久久超碰97人人做人人爱| 日本中文字幕高清视频| 久久日韩精品一区二区五区|