<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Jack Jiang

    我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
    posts - 494, comments - 13, trackbacks - 0, articles - 1

    本文由探探服務端高級技術專家張凱宏分享,原題“探探長鏈接項目的Go語言實踐”,因原文內容有較多錯誤,有修訂和改動。

    1、引言

    即時通信長連接服務處于網絡接入層,這個領域非常適合用Go語言發揮其多協程并行、異步IO的特點。

    探探自長連接項目上線以后,對服務進行了多次優化:GC從5ms降到100微秒(Go版本均為1.9以上),主要gRPC接口調用延時p999從300ms下降到5ms。在業內大多把目光聚焦于單機連接數的時候,我們則更聚焦于服務的SLA(服務可用性)。

    本文將要分享的是陌生人社交應用探探的IM長連接模塊從技術選型到架構設計,再到性能優化的整個技術實踐過程和經驗總結。

    學習交流:

    - 即時通訊/推送技術開發交流5群:215477170 [推薦]

    - 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

    - 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK 

    本文已同步發布于:http://www.52im.net/thread-3780-1-1.html

    2、關于作者

     

    張凱宏:擔任探探服務端高級技術專家。

    6年Go語言開發經驗,曾用Go語言構建多個大型Web項目,其中涉及網絡庫、存儲服務、長連接服務等。專注于Go語言實踐、存儲服務研發及大數據場景下的Go語言深度優化。

    3、項目緣起

    我們這個項目是2018年下半年開始,據今天大概1年半時間。

    當時探探遇到一些技術痛點,最嚴重的就是嚴重依賴第三方Push,比如說第三方有一些故障的話,對實時IM聊天的KPS有比較大的影響。

    當時通過push推送消息,應用內的push延時比較高,平均延時五六百毫秒,這個時間我們不能接受。   

    而且也沒有一個 Ping Pland 機制(心跳檢查機制?),無法知道用戶是否在線。

    當時產品和技術同學都覺得是機會搞一個長連接了。

    4、一個小插曲

    項目大概持續了一個季度時間,首先是拿IM業務落地,我們覺得長連接跟IM綁定比較緊密一些。

    IM落地之后,后續長連接上線之后,各個業務比較依賴于長連接服務。

    這中間有一個小插曲,主要是取名字那一塊。

    項目之初給項目起名字叫Socket,看到socket比較親切,覺得它就是一個長連接,這個感覺比較莫名,不知道為什么。但運維提出了異議,覺得UDP也是Socket,我覺得UDP其實也可以做長連接。

    運維提議叫Keepcom,這個是出自于Keep Alive實現的,這個提議還是挺不錯的,最后我們也是用了這個名字。

    客戶端給的建議是Longlink,另外一個是Longconn,一個是IOS端技術同事取的、一個是安卓端技術同事取的。

    最后我們都敗了,運維同學勝了,運維同學覺得,如果名字定不下來就別上線的,最后我們妥協了。

    5、為什么要做長連接?

    為什么做長連接?

     

    如上圖所示:看一下對比挺明顯,左邊是長連接,右邊是短長連接。

    對于長連接來說,不需要重新進入連接,或者是釋放連接,一個X包只需要一個RTT就完事。右邊對于一個短連接需要三次握手發送一個push包,最后做揮手。

    結論:如果發送N條消息的數據包,對于長連接是2+N次的RTT,對于短連接是3N次RTT,最后開啟Keep Alive,N是連接的個數。

    6、長連接技術優勢

    我們決結了一下,長連接有以下四大優勢:

    • 1)實時性:長連接是雙向的通道,對消息的推送也是比較實時;
    • 2)有狀態:長連接本身維護用戶的狀態,通過KeepAlive方式,確定用戶是否在線;
    • 3)省流程:長連接比較省流量,可以做一些用戶自定義的數據壓縮,本身也可以省不少的歸屬包和連接包,所以說比較省流量;
    • 4)更省電:減少網絡流量之后,能夠進一步降低移動客戶端的耗電。

    7、TCP在移動端能勝任嗎?

    在項目開始之前,我們做了比較多的考量。

    首先我們看一下對于移動端的長連接來說,TCP協議是不是能夠Work?

    對于傳統的長連接來說,Web端的長連接TCP可以勝任,在移動端來說TCP能否勝任?這取決于TCP的幾個特性。

    首先TCP有慢啟動和滑動窗口的特性,TCP通過這種方式控制PU包,避免網絡阻塞。

    TCP連接之后走一個慢啟動流程,這個流程從初始窗大小做2個N次方的擴張,最后到一定的域值,比如域值是16包,從16包開始逐步往上遞增,最后到24個數據包,這樣達到窗口最大值。

    一旦遇到丟包的情況,當然兩種情況。一種是快速重傳,窗口簡單了,相當于是12個包的窗口。如果啟動一個RTO類似于狀態連接,窗口一下跌到初始的窗口大小。

    如果啟動RTO重傳的話,對于后續包的阻塞蠻嚴重,一個包阻塞其他包的發送。

     

    ▲ 上圖引用自《邁向高階:優秀Android程序員必知必會的網絡基礎

    有關TCP協議的基礎知識,可以讀讀以下資料:

    1. TCP/IP詳解 - 第17章·TCP:傳輸控制協議
    2. TCP/IP詳解 - 第18章·TCP連接的建立與終止
    3. TCP/IP詳解 - 第21章·TCP的超時與重傳
    4. 通俗易懂-深入理解TCP協議(上):理論基礎
    5. 通俗易懂-深入理解TCP協議(下):RTT、滑動窗口、擁塞處理
    6. 網絡編程懶人入門(一):快速理解網絡通信協議(上篇)
    7. 網絡編程懶人入門(二):快速理解網絡通信協議(下篇)
    8. 網絡編程懶人入門(三):快速理解TCP協議一篇就夠
    9. 腦殘式網絡編程入門(一):跟著動畫來學TCP三次握手和四次揮手
    10. 網絡編程入門從未如此簡單(二):假如你來設計TCP協議,會怎么做?

    8、TCP還是UDP?

     

    ▲ 上圖引用自《移動端IM/推送系統的協議選型:UDP還是TCP?

    TCP實現長連接的四個問題:

    • 1)移動端的消息量還是比較稀疏,用戶每次拿到手機之后,發的消息總數比較少,每條消息的間隔比較長。這種情況下TCP的間連和保持長鏈接的優勢比較明顯一些;
    • 2)弱網條件下丟包率比較高,丟包后Block后續數據發送容易阻塞;
    • 3)TCP連接超時時間過長,默認1秒鐘,這個由于TCP誕生的年代比較早,那會兒網絡狀態沒有現在好,當時定是1s的超時,現在可以設的更短一點;
    • 4)在沒有快速重傳的情況下,RTO重傳等待時間較長,默認15分鐘,每次是N次方的遞減。

    為何最終還是選擇TCP呢?因為我們覺得UDP更嚴重一點。

    首先UDP沒有滑動窗口,無流量控制,也沒有慢啟動的過程,很容易導致丟包,也很容易導致在網絡中間狀態下丟包和超時。

    UDP一旦丟包之后沒有重傳機制的,所以我們需要在應用層去實現一個重傳機制,這個開發量不是那么大,但是我覺得因為比較偏底層,容易出故障,所以最終選擇了TCP。

    TCP還是UDP?這一直是個比較有爭議的話題:

    1. 網絡編程懶人入門(四):快速理解TCP和UDP的差異
    2. 網絡編程懶人入門(五):快速理解為什么說UDP有時比TCP更有優勢
    3. 5G時代已經到來,TCP/IP老矣,尚能飯否?
    4. Android程序員必知必會的網絡通信傳輸層協議——UDP和TCP
    5. 不為人知的網絡編程(六):深入地理解UDP協議并用好它
    6. 不為人知的網絡編程(七):如何讓不可靠的UDP變的可靠?

    如果你對UDP協議還不了解,可以讀讀這篇:《TCP/IP詳解 - 第11章·UDP:用戶數據報協議》。

    9、選擇TCP的更多理由

    我們羅列一下,主要有這3點:

    • 1)目前在移動端、安卓、IOS來說,初始窗口大小比較大默認是10,綜合TCP慢啟動的劣勢來看;
    • 2)在普通的文本傳輸情況下,對于丟包的嚴重不是很敏感(并不是說傳多媒體的數據流,只是傳一些文本數據,這一塊對于丟包的副作用TCP不是特別嚴重);
    • 3)我們覺得TCP在應用層用的比較多。

    關于第“3)”點,這里有以下三個考量點。

    第一個考量點:

    基本現在應用程序走HTP協議或者是push方式基本都是TCP,我們覺得TCP一般不會出大的問題。

    一旦拋棄TCP用UDP或者是QUIC協議的話,保不齊會出現比較大的問題,短時間解決不了,所以最終用了TCP。

    第二個考量點:

    我們的服務在基礎層上用哪種方式做LB,當時有兩種選擇,一種是傳統的LVS,另一種是HttpDNS(關于HttpDNS請見《全面了解移動端DNS域名劫持等雜癥:原理、根源、HttpDNS解決方案等》)。

    最后我們選擇了HttpDNS,首先我們還是需要跨機房的LB支持,這一點HttpDNS完全勝出。其次,如果需要跨網端的話,LVS做不到,需要其他的部署方式。再者,在擴容方面,LVS算是略勝一籌。最后,對于一般的LB算法,LVS支持并不好,需要根據用戶ID的LB算法,另外需要一致性哈希的LB算法,還需要根據地理位置的定位信息,在這些方面HttpDNS都能夠完美的勝出,但是LVS都做不到。

    第三個考量點:

    我們在做TCP的飽和機制時通過什么樣的方式?Ping包的方式,間隔時間怎么確定,Ping包的時間細節怎么樣確定?

    當時比較糾結是客戶端主動發ping還是服務端主動發Ping?

    對于客戶端保活的機制支持更好一些,因為客戶端可能會被喚醒,但是客戶端進入后臺之后可能發不了包。

    其次:APP前后臺對于不同的Ping包間隔來?;睿驗樵诤笈_本身處于一種弱在線的狀態,并不需要去頻繁的發Ping包確定在線狀態。

    所以:在后臺的Ping包的時間間隔可以長一些,前端可以短一些。

    再者:需要Ping指數增長的間隔支持,在故障的時候還是比較救命的。

    比如說:服務端一旦故障之后,客戶端如果拼命Ping的話,可能把服務端徹底搞癱瘓了。如果有一個指數級增長的Ping包間隔,基本服務端還能緩一緩,這個在故障時比較重要。

    最后:Ping包重試是否需要Backoff,Ping包重新發Ping,如果沒有收到Bang包的話,需要等到Backoff發Ping。

    10、動態Ping包時間間隔算法

    PS:在IM里這其實有個更專業的叫法——智能心跳算法”。

    我們還設計了一個動態的Ping包時間間隔算法。

    因為國內的網絡運營商對于NIT設備有一個?;顧C制,目前基本在5分鐘以上,5分鐘如果不發包的話,會把你的緩存給刪掉?;旧细鬟\營商都在5分鐘以上,只不過移動4G阻礙了?;究梢栽?到10分鐘之內發一個Ping包就行,可以維持網絡運營商設備里的緩存,一直保持著,這樣就沒有問題,使長連接一直?;钪?/p>

    增加Ping包間隔可以減少網絡流量,能夠進一步降低客戶端的耗電,這一塊的受益還是比較大的。

    在低端安卓設備的情況下,有一些DHCP租期的問題。這個問題集中在安卓端的低版本上,安卓不會去續租過期的IP。

    解決問題也比較簡單,在DHCP租期到一半的時候,去及時向DHCP服務器續租一下就能解決了。

    限于篇幅,我就不在這里展開了,有興趣可以讀這些資料:

    1. 為何基于TCP協議的移動端IM仍然需要心跳?;顧C制?
    2. 一文讀懂即時通訊應用中的網絡心跳包機制:作用、原理、實現思路等
    3. 微信團隊原創分享:Android版微信后臺?;顚崙鸱窒?網絡?;钇?
    4. 移動端IM實踐:實現Android版微信的智能心跳機制
    5. 移動端IM實踐:WhatsApp、Line、微信的心跳策略分析
    6. 一種Android端IM智能心跳算法的設計與實現探討(含樣例代碼)
    7. 手把手教你用Netty實現網絡通信程序的心跳機制、斷線重連機制

    11、服務架構

    11.1 基本介紹

    服務架構比較簡單,大概是四個模塊:

    • 1)首先是HttpDNS;
    • 2)另一個是Connector接入層,接入層提供IP,
    • 3)然后是Router,類似于代理轉發消息,根據IP選擇接入層的服務器,最后推到用戶;
    • 4)最后還有認證的模塊Account,我們目前只是探探APP,這個在用戶中心實現。

    11.2 部署

    部署上相當于三個模塊:

    • 1)一個是Dispatcher;
    • 2)一個是Redis;
    • 3)一個是Cluser。

    如下圖所示:客戶端在連接的時候:

    • 1)需要拿到一個協議;
    • 2)第二步通過HttpDNS拿到ConnectorIP;
    • 3)通過IP連長連接,下一步發送Auth消息認證;
    • 4)連接成功,后面發送Ping包保活;
    • 5)之后斷開連接。

    11.3 消息轉發流程

    消息轉發的流程分為兩個部分。

    首先是消息上行:服務端發起一個消息包,通過Connector接入服務,客戶端通過Connector發送消息,再通過Connector把消息發到微服務上,如果不需要微服務的話直接去轉發到Vetor就行的,這種情況下Connector更像一個Gateway。

    對于下行:業務方都需要請求Router,找到具體的Connector,根據Connector部署消息。

    各個公司都是微服務的架構,長連接跟微服務的交互基本兩塊。一塊是消息上行時,更像是Gateway,下行通過Router接入,通過Connector發送消息。

    11.4 一些實現細節

    下面是一些是細節,我們用了GO語言1.13.4,內部消息傳輸上是gRPC,傳輸協議是Http2,我們在內部通過ETCD做LB的方式,提供服務注冊和發現的服務。

    如下圖所示:Connector就是狀態,它從用戶ID到連接的一個狀態信息。

    我們看下圖的右邊:它其實是存在一個比較大的MAP,為了防止MAP的鎖競爭過于嚴重,把MAP拆到2到56個子MAP,通過這種方式去實現高讀寫的MAP。對于每一個MAP從一個ID到連接狀態的映射關系,每一個連接是一個Go Ping,實現細節讀寫是4KB,這個沒改過。

    我們看一下Router:它是一個無狀態的CommonGRPC服務,它比較容易擴容,現在狀態信息都存在Redis里面,Redis大概一組一層,目前峰值是3000。

    我們有兩個狀態:一個是Connector,一個是Router。

    首先以Connector狀態為主,Router是狀態一致的保證。

    這個里面分為兩種情況:如果連接在同一個Connector上的話,Connector需要保證向Router復制的順序是正確的,如果順序不一致,會導致Router和Connector狀態不一致。通過統一Connector的窗口實現消息一致性,如果跨Connector的話,通過在Redis Lua腳本實現Compare And Update方式,去保證只有自己Connector寫的狀態才能被自己更新,如果是別的Connector的話,更新不了其他人的信心。我們保證跨Connector和同一Connector都能夠去按照順序通過一致的方式更新Router里面連接的狀態。

    Dispatche比較簡單:是一個純粹的Common Http API服務,它提供Http API,目前延時比較低大概20微秒,4個CPU就可以支撐10萬個并發。

    目前通過無單點的結構實現一個高可用:首先是Http DNS和Router,這兩個是無障礙的服務,只需要通過LB保證。對于Connector來說,通過Http DNS的客戶端主動漂移實現連接層的Ordfrev,通過這種方式保證一旦一個Connector出問題了,客戶端可以立馬漂到下一個Connector,去實現自動的工作轉移,目前是沒有單點的。        

    12、性能優化

    12.1 基本情況

    后續有優化主要有以下幾個方面:

    • 1)網絡優化:這一塊拉著客戶端一起做,首先客戶端需要重傳包的時候發三個嗅探包,通過這種方式做一個快速重傳的機制,通過這種機制提高快速重傳的比例;
    • 2)心跳優化:通過動態的Ping包間隔時間,減少Ping包的數量,這個還在開發中;
    • 3)防止劫持:是通過客戶端使用IP直連方式,回避域名劫持的操作;
    • 4)DNS優化:是通過HttpDNS每次返回多個IP的方式,來請求客戶端的HttpDNS。

    12.2 網絡優化

    對于接入層來說,其實Connector的連接數比較多,并且Connector的負載也是比較高。

    我們對于Connector做了比較大的優化,首先看Connector最早的GC時間到了4、5毫秒,慘不忍睹的。

    我們看一下下面這張圖(圖上)是優化后的結果,大概平均100微秒,這算是比較好。第二張圖(圖下)是第二次優化的結果,大概是29微秒,第三張圖大概是20幾微秒。

    12.3 消息延遲

    看一下消息延遲,探探對im消息的延遲要求比較高,特別注重用戶的體驗。

    這一塊剛開始大概到200ms,如果對于一個操作的話,200ms還是比較嚴重的。

    第一次優化之后(下圖-上)的狀態大概1點幾毫秒,第二次優化之后(下圖-下)現在降到最低點差不多100微秒,跟一般的Net操作時間維度上比較接近。

    12.4 Connector優化過程

    優化過程是這樣的:

    • 1)首先需要關鍵路徑上的Info日志,通過采樣實現Access Log,info日志是接入層比較重的操作;
    • 2)第二通過Sync.Poll緩存對象;
    • 3)第三通過Escape Analysis對象盡可能在線上分配。

    后面還實現了Connector的無損發版:這一塊比較有價值。長連接剛上線發版比較多,每次發版對于用戶來說都有感,通過這種方式讓用戶盡量無感。

    實現了Connector的Graceful Shutdown的方式,通過這種方式優化連接。

    首先:在HttpDNS上下線該機器,下線之后緩慢斷開用戶連接,直到連接數小于一定閾值。后面是重啟服務,發版二進制。

    最后:是HttpDNS上線該機器,通過這種方式實現用戶發版,時間比較長,當時測了挺長時間,去衡量每秒鐘斷開多少個連接,最后閾值是多少。

    后面是一些數據:剛才GC也是一部分,目前連接數都屬于比較關鍵的數據。首先看連接數單機連接數比較少,不敢放太開,最多是15萬的單機連接數,大約100微秒。

    Goroutine數量跟連接數一樣,差不多15萬個:

    看一下內存使用狀態,下圖(上)是GO的內存總量,大概是2:3,剩下五分之一是屬于未占用,內存總量是7.3個G。

    下圖是GC狀態,GC比較健康,紅線是GC每次活躍內存數,紅線遠遠高于綠線。

    看到GC目前的狀況大概是20幾微秒,感覺目前跟GO的官方時間比較能對得上,我們感覺GC目前都已經優化到位了。

    12.5 后續要做的優化

    最后是規劃后續還要做優化。

    首先:對系統上還是需要更多優化Connector層,更多去減少內存的分配,盡量把內存分配到堆上而不是站上,通過這種方式減少GC壓力,我們看到GO是非Generational Collection GE,堆的內存越多的話,掃的內存也會越多,這樣它不是一個線性的增長。

    第二:在內部更多去用Sync Pool做短暫的內存分配,比如說Context或者是臨時的Dbyle。

    協議也要做優化:目前用的是WebSocket協議,后面會加一些功能標志,把一些重要信息傳給服務端。比如說一些重傳標志,如果客戶端加入重傳標志的話,我們可以先校驗這個包是不是重傳包,如果是重傳包的話會去判斷這個包是不是重復,是不是之前發過,如果發過的話就不需要去解包,這樣可以少做很多的服務端操作。

    另外:可以去把Websocket目前的Mask機制去掉,因為Mask機制防止Web端的改包操作,但是基本是客戶端的傳包,所以并不需要Mask機制。

    業務上:目前規劃后面需要做比較多的事情。我們覺得長連接因為是一個接入層,是一個非常好的地方去統計一些客戶端的分布。比如說客戶端的安卓、IOS的分布狀況。

    進一步:可以做用戶畫像的統計,男的女的,年齡是多少,地理位置是多少。大概是這些,謝謝!

    13、熱門問題回復

    * 提問:剛才說連接層對話重啟,間接的過程中那些斷掉的用戶就飄到其他的,是這樣做的嗎?

    張凱宏:目前是這樣的,客戶端做自動飄移。

    * 提問:現在是1千萬日活,如果服務端往客戶端一下推100萬,這種場景怎么做的?

    張凱宏:目前我們沒有那么大的消息推送量,有時候會發一些業務相關的推送,目前做了一個限流,通過客戶端限流實現的,大概三四千。

    * 提問:如果做到后端,意味著會存在安全隱患,攻擊者會不停的建立連接,導致很難去做防御,會有這個問題嗎?因為惡意的攻擊,如果攻擊的話建立連接就可以了,不需要認證的機制。

    張凱宏:明白你的意思,這一塊不只是長連接,短連接也有這個問題??蛻舳艘恢痹趥卧煸L問結果,流量還是比較大的,這一塊靠防火墻和IP層防火墻實現。

    * 提問:長連接服務器是掛在最外方,中間有沒有一層?

    張凱宏:目前接著如下層直接暴露在外網層,前面過一層IP的防DNSFre的防火墻。除此之外沒有別的網絡設備了。

    * 提問:基于什么樣的考慮中間沒有加一層,因為前面還加了一層的情況。

    張凱宏:目前沒有這個計劃,后面會在Websofte接入層前面加個LS層可以方便擴容,這個收益不是特別大,所以現在沒有去計劃。

    * 提問:剛剛說的斷開重傳的三次嗅探那個是什么意思?

    張凱宏:我們想更多的去觸發快速重傳,這樣對于TCP的重傳間隔更短一些,服務端根據三個循環包判斷是否快速重傳,我們會發三個循環包避免一個RTO重傳的開啟。

    * 提問:探探最開始安卓服務器是使用第三方的嗎?

    張凱宏:對的,剛開始是極光推送的。

    * 提問:從第三方的安卓服務器到自研。

    張凱宏:如果極光有一些故障的話,對我們影響還是蠻大。之前極光的故障頻率挺高,我們想是不是自己能把服務做起來。第二點,極光本身能提供一個用戶是否在線的判斷,但是它那個判斷要走通道,延時比較高,本身判斷是連接把延時降低一些。

    * 提問:比如說一個新用戶上線連接過來,有一些用戶發給他消息,他是怎么把一線消息拿到的?

    張凱宏:我們通過業務端保證的,未發出來的消息會存一個ID號,當用戶重新連的時候,業務端再拉一下。

    14、參考資料

    [1] 移動端IM/推送系統的協議選型:UDP還是TCP?

    [2] 5G時代已經到來,TCP/IP老矣,尚能飯否?

    [3] 為何基于TCP協議的移動端IM仍然需要心跳?;顧C制?

    [4] 一文讀懂即時通訊應用中的網絡心跳包機制:作用、原理、實現思路等

    [5] 微信團隊原創分享:Android版微信后臺?;顚崙鸱窒?網絡?;钇?

    [6] 移動端IM實踐:實現Android版微信的智能心跳機制

    [7] 邁向高階:優秀Android程序員必知必會的網絡基礎

    [8] 全面了解移動端DNS域名劫持等雜癥:原理、根源、HttpDNS解決方案等

    [9] 技術掃盲:新一代基于UDP的低延時網絡傳輸層協議——QUIC詳解

    [10] 新手入門一篇就夠:從零開發移動端IM

    [11] 長連接網關技術專題(二):知乎千萬級并發的高性能長連接網關技術實踐

    [12] 長連接網關技術專題(三):手淘億級移動端接入層網關的技術演進之路

    [13] 長連接網關技術專題(五):喜馬拉雅自研億級API網關技術實踐

    [14] 一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等

    [15] 一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等

    [16] 從新手到專家:如何設計一套億級消息量的分布式IM系統

    本文已同步發布于“即時通訊技術圈”公眾號。

    同步發布鏈接是:http://www.52im.net/thread-3780-1-1.html



    作者:Jack Jiang (點擊作者姓名進入Github)
    出處:http://www.52im.net/space-uid-1.html
    交流:歡迎加入即時通訊開發交流群 215891622
    討論:http://www.52im.net/
    Jack Jiang同時是【原創Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
    本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
    主站蜘蛛池模板: 亚洲精品亚洲人成在线观看麻豆| 性短视频在线观看免费不卡流畅| 伊伊人成亚洲综合人网7777| 免费中文字幕视频| 亚洲成AⅤ人影院在线观看| 日韩精品免费一线在线观看| 亚洲国产综合人成综合网站| 男人扒开添女人下部免费视频| 国产区卡一卡二卡三乱码免费| 国产在亚洲线视频观看| 亚洲成AV人在线观看网址| 亚洲一区二区三区免费| 亚洲永久无码3D动漫一区| 久久国产精品国产自线拍免费| 久久亚洲精品AB无码播放| 最近2019免费中文字幕6| 亚洲一区二区三区高清视频| 啦啦啦高清视频在线观看免费| 亚洲色成人网站WWW永久四虎 | av无码东京热亚洲男人的天堂 | 9420免费高清在线视频| 亚洲精品视频在线观看免费| 免费大片黄在线观看yw| 亚洲av永久中文无码精品综合| 免费v片视频在线观看视频| 一级做a爰性色毛片免费| 国产亚洲一区二区在线观看| 99视频在线精品免费| 亚洲人成免费电影| 日本特黄特黄刺激大片免费| 免费播放国产性色生活片| 伊人久久亚洲综合| 69av免费观看| 亚洲av中文无码乱人伦在线观看 | 亚洲中文精品久久久久久不卡| 日本免费中文字幕在线看| 久久99久久成人免费播放| 久久亚洲精品人成综合网| 成人免费一区二区三区在线观看| 精品视频免费在线| 亚洲av午夜成人片精品网站|