<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 訂閱與發布 [TOC=2,3] Redis 通過 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 、 [SUBSCRIBE](http://redis.readthedocs.org/en/latest/pub_sub/subscribe.html#subscribe "(in Redis 命令參考 v2.8)") 等命令實現了訂閱與發布模式,這個功能提供兩種信息機制,分別是訂閱/發布到頻道和訂閱/發布到模式,下文先討論訂閱/發布到頻道的實現,再討論訂閱/發布到模式的實現。 ### 頻道的訂閱與信息發送 Redis 的 [SUBSCRIBE](http://redis.readthedocs.org/en/latest/pub_sub/subscribe.html#subscribe "(in Redis 命令參考 v2.8)") 命令可以讓客戶端訂閱任意數量的頻道,每當有新信息發送到被訂閱的頻道時,信息就會被發送給所有訂閱指定頻道的客戶端。 作為例子,下圖展示了頻道 `channel1` ,以及訂閱這個頻道的三個客戶端 —— `client2` 、 `client5` 和 `client1` 之間的關系: ![digraph pubsub_relation { rankdir = BT; node [style = filled]; edge [style = bold]; channel1 [label = "channel1", fillcolor = "#A8E270"]; node [shape = box, fillcolor = "#95BBE3"]; client2 [label = "client2"]; client5 [label = "client5"]; client1 [label = "client1"]; client2 -> channel1 [label = "subscribe"]; client5 -> channel1 [label = "subscribe"]; client1 -> channel1 [label = "subscribe"];}](https://box.kancloud.cn/2015-09-13_55f4effd60979.svg) 當有新消息通過 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 命令發送給頻道 `channel1` 時,這個消息就會被發送給訂閱它的三個客戶端: ![digraph send_message_to_subscriber { node [style = filled]; edge [style = "dashed, bold"]; message [label = "PUBLISH channel1 message", shape = plaintext, fillcolor = "#FADCAD"]; message -> channel1 [color = "#B22222]"]; channel1 [label = "channel1", fillcolor = "#A8E270"]; node [shape = box]; client2 [label = "client2", fillcolor = "#95BBE3"]; client5 [label = "client5", fillcolor = "#95BBE3"]; client1 [label = "client1", fillcolor = "#95BBE3"]; /* client2 -> channel1 [label = "subscribe"]; client5 -> channel1 [label = "subscribe"]; client1 -> channel1 [label = "subscribe"]; */ channel1 -> client2 [label = "message", color = "#B22222"]; channel1 -> client5 [label = "message", color = "#B22222"]; channel1 -> client1 [label = "message", color = "#B22222"];}](https://box.kancloud.cn/2015-09-13_55f4effd69833.svg) 在后面的內容中,我們將探討 [SUBSCRIBE](http://redis.readthedocs.org/en/latest/pub_sub/subscribe.html#subscribe "(in Redis 命令參考 v2.8)") 和 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 命令的實現,以及這套訂閱與發布機制的運作原理。 ### 訂閱頻道 每個 Redis 服務器進程都維持著一個表示服務器狀態的 `redis.h/redisServer` 結構,結構的 `pubsub_channels` 屬性是一個字典,這個字典就用于保存訂閱頻道的信息: ~~~ struct redisServer { // ... dict *pubsub_channels; // ... }; ~~~ 其中,字典的鍵為正在被訂閱的頻道,而字典的值則是一個鏈表,鏈表中保存了所有訂閱這個頻道的客戶端。 比如說,在下圖展示的這個 `pubsub_channels` 示例中, `client2` 、 `client5` 和 `client1` 就訂閱了 `channel1` ,而其他頻道也分別被別的客戶端所訂閱: ![digraph pubsub { rankdir = LR; node [shape = record, style = filled]; edge [style = bold]; // keys pubsub [label = "pubsub_channels |<channel1> channel1 |<channel2> channel2 |<channel3> channel3 | ... |<channelN> channelN", fillcolor = "#A8E270"]; // clients blocking for channel1 client1 [label = "client1", fillcolor = "#95BBE3"]; client5 [label = "client5", fillcolor = "#95BBE3"]; client2 [label = "client2", fillcolor = "#95BBE3"]; null_1 [label = "NULL", shape = plaintext]; pubsub:channel1 -> client2; client2 -> client5; client5 -> client1; client1 -> null_1; // clients blocking for channel2 client7 [label = "client7", fillcolor = "#95BBE3"]; null_2 [label = "NULL", shape = plaintext]; pubsub:channel2 -> client7; client7 -> null_2; // channel client3 [label = "client3", fillcolor = "#95BBE3"]; client4 [label = "client4", fillcolor = "#95BBE3"]; client6 [label = "client6", fillcolor = "#95BBE3"]; null_3 [label = "NULL", shape = plaintext]; pubsub:channel3 -> client3; client3 -> client4; client4 -> client6; client6 -> null_3;}](https://box.kancloud.cn/2015-09-13_55f4effd71e2f.svg) 當客戶端調用 [SUBSCRIBE](http://redis.readthedocs.org/en/latest/pub_sub/subscribe.html#subscribe "(in Redis 命令參考 v2.8)") 命令時,程序就將客戶端和要訂閱的頻道在 `pubsub_channels` 字典中關聯起來。 舉個例子,如果客戶端 `client10086` 執行命令 `SUBSCRIBE channel1 channel2 channel3` ,那么前面展示的 `pubsub_channels` 將變成下面這個樣子: ![digraph new_subscribe { rankdir = LR; node [shape = record, style = filled]; edge [style = bold]; // keys pubsub [label = "pubsub_channels |<channel1> channel1 |<channel2> channel2 |<channel3> channel3 | ... |<channelN> channelN", fillcolor = "#A8E270"]; // clients blocking for channel1 client1 [label = "client1", fillcolor = "#95BBE3"]; client5 [label = "client5", fillcolor = "#95BBE3"]; client2 [label = "client2", fillcolor = "#95BBE3"]; client10086 [label = "client10086", fillcolor = "#FFC1C1"]; client10086_1 [label = "client10086", fillcolor = "#FFC1C1"]; client10086_2 [label = "client10086", fillcolor = "#FFC1C1"]; null_1 [label = "NULL", shape = plaintext]; null_2 [label = "NULL", shape = plaintext]; null_3 [label = "NULL", shape = plaintext]; pubsub:channel1 -> client2; client2 -> client5; client5 -> client1; client1 -> client10086; client10086 -> null_1; // clients blocking for channel2 client7 [label = "client7", fillcolor = "#95BBE3"]; pubsub:channel2 -> client7; client7 -> client10086_1; client10086_1 -> null_2; // channel client3 [label = "client3", fillcolor = "#95BBE3"]; client4 [label = "client4", fillcolor = "#95BBE3"]; client6 [label = "client6", fillcolor = "#95BBE3"]; pubsub:channel3 -> client3; client3 -> client4; client4 -> client6; client6 -> client10086_2; client10086_2 -> null_3;}](https://box.kancloud.cn/2015-09-13_55f4effd7add8.svg) [SUBSCRIBE](http://redis.readthedocs.org/en/latest/pub_sub/subscribe.html#subscribe "(in Redis 命令參考 v2.8)") 命令的行為可以用偽代碼表示如下: ~~~ def SUBSCRIBE(client, channels): # 遍歷所有輸入頻道 for channel in channels: # 將客戶端添加到鏈表的末尾 redisServer.pubsub_channels[channel].append(client) ~~~ 通過 `pubsub_channels` 字典,程序只要檢查某個頻道是否為字典的鍵,就可以知道該頻道是否正在被客戶端訂閱;只要取出某個鍵的值,就可以得到所有訂閱該頻道的客戶端的信息。 ### 發送信息到頻道 了解了 `pubsub_channels` 字典的結構之后,解釋 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 命令的實現就非常簡單了:當調用 `PUBLISH channel message` 命令,程序首先根據 `channel` 定位到字典的鍵,然后將信息發送給字典值鏈表中的所有客戶端。 比如說,對于以下這個 `pubsub_channels` 實例,如果某個客戶端執行命令 `PUBLISH channel1 "hello moto"` ,那么 `client2` 、 `client5` 和 `client1` 三個客戶端都將接收到 `"hello moto"` 信息: ![digraph pubsub { rankdir = LR; node [shape = record, style = filled]; edge [style = bold]; // keys pubsub [label = "pubsub_channels |<channel1> channel1 |<channel2> channel2 |<channel3> channel3 | ... |<channelN> channelN", fillcolor = "#A8E270"]; // clients blocking for channel1 client1 [label = "client1", fillcolor = "#95BBE3"]; client5 [label = "client5", fillcolor = "#95BBE3"]; client2 [label = "client2", fillcolor = "#95BBE3"]; null_1 [label = "NULL", shape = plaintext]; pubsub:channel1 -> client2; client2 -> client5; client5 -> client1; client1 -> null_1; // clients blocking for channel2 client7 [label = "client7", fillcolor = "#95BBE3"]; null_2 [label = "NULL", shape = plaintext]; pubsub:channel2 -> client7; client7 -> null_2; // channel client3 [label = "client3", fillcolor = "#95BBE3"]; client4 [label = "client4", fillcolor = "#95BBE3"]; client6 [label = "client6", fillcolor = "#95BBE3"]; null_3 [label = "NULL", shape = plaintext]; pubsub:channel3 -> client3; client3 -> client4; client4 -> client6; client6 -> null_3;}](https://box.kancloud.cn/2015-09-13_55f4effd84159.svg) [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 命令的實現可以用以下偽代碼來描述: ~~~ def PUBLISH(channel, message): # 遍歷所有訂閱頻道 channel 的客戶端 for client in server.pubsub_channels[channel]: # 將信息發送給它們 send_message(client, message) ~~~ ### 退訂頻道 使用 [UNSUBSCRIBE](http://redis.readthedocs.org/en/latest/pub_sub/unsubscribe.html#unsubscribe "(in Redis 命令參考 v2.8)") 命令可以退訂指定的頻道,這個命令執行的是訂閱的反操作:它從 `pubsub_channels` 字典的給定頻道(鍵)中,刪除關于當前客戶端的信息,這樣被退訂頻道的信息就不會再發送給這個客戶端。 ### 模式的訂閱與信息發送 當使用 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 命令發送信息到某個頻道時,不僅所有訂閱該頻道的客戶端會收到信息,如果有某個/某些模式和這個頻道匹配的話,那么所有訂閱這個/這些頻道的客戶端也同樣會收到信息。 下圖展示了一個帶有頻道和模式的例子,其中 `tweet.shop.*` 模式匹配了 `tweet.shop.kindle` 頻道和 `tweet.shop.ipad` 頻道,并且有不同的客戶端分別訂閱它們三個: ![digraph pattern_relation { rankdir = BT; node [style = filled]; edge [style = bold]; kindle [label = "tweet.shop.kindle", fillcolor = "#A8E270"]; ipad [label = "tweet.shop.ipad", fillcolor = "#A8E270"]; node [shape = octagon]; pattern [label = "tweet.shop.*"]; pattern -> kindle [label = "match"]; pattern -> ipad [label = "match"]; node [shape = box]; client123 [fillcolor = "#95BBE3"]; client256 [fillcolor = "#95BBE3"]; clientX [fillcolor = "#95BBE3"]; clientY [fillcolor = "#95BBE3"]; client3333 [fillcolor = "#95BBE3"]; client4444 [fillcolor = "#95BBE3"]; client5555 [fillcolor = "#95BBE3"]; client123 -> pattern [label = "subscribe"]; client256 -> pattern [label = "subscribe"]; clientX -> kindle [label = "subscribe"]; clientY -> kindle [label = "subscribe"]; client3333 -> ipad [label = "subscribe"]; client4444 -> ipad [label = "subscribe"]; client5555 -> ipad [label = "subscribe"];}](https://box.kancloud.cn/2015-09-13_55f4effd8d5d6.svg) 當有信息發送到 `tweet.shop.kindle` 頻道時,信息除了發送給 `clientX` 和 `clientY` 之外,還會發送給訂閱 `tweet.shop.*` 模式的 `client123` 和 `client256` : ![digraph send_message_to_pattern { node [style = filled]; edge [style = bold]; // tweet.shop.ipad ipad [label = "tweet.shop.ipad", fillcolor = "#A8E270"]; ipad -> pattern [label = "match", dir = back]; node [shape = box]; ipad -> client3333 [label = "subscribe", dir = back]; ipad -> client4444 [label = "subscribe", dir = back]; ipad -> client5555 [label = "subscribe", dir = back]; node [shape = plaintext]; message [label = "PUBLISH tweet.shop.kindle message", fillcolor = "#FADCAD"]; kindle [label = "tweet.shop.kindle", shape = ellipse, fillcolor = "#A8E270"]; pattern [label = "tweet.shop.*", shape = octagon]; message -> kindle [style = "bold, dashed", color = "#B22222"]; kindle -> pattern [style = "bold, dashed", color = "#B22222"]; node [shape = box]; kindle -> clientX [style = "bold, dashed", color = "#B22222", label = "message"]; kindle -> clientY [style = "bold, dashed", color = "#B22222", label = "message"]; pattern -> client123 [label = "message", style = "bold, dashed", color = "#B22222"]; pattern -> client256 [label = "message", style = "bold, dashed", color = "#B22222"]; // client color client123 [fillcolor = "#95BBE3"]; client256 [fillcolor = "#95BBE3"]; clientX [fillcolor = "#95BBE3"]; clientY [fillcolor = "#95BBE3"]; client3333 [fillcolor = "#95BBE3"]; client4444 [fillcolor = "#95BBE3"]; client5555 [fillcolor = "#95BBE3"];}](https://box.kancloud.cn/2015-09-13_55f4effd98208.svg) 另一方面,如果接收到信息的是頻道 `tweet.shop.ipad` ,那么 `client123` 和 `client256` 同樣會收到信息: ![digraph pattern_relation { rankdir = BT; node [style = filled]; edge [style = bold]; kindle [label = "tweet.shop.kindle", fillcolor = "#A8E270"]; ipad [label = "tweet.shop.ipad", fillcolor = "#A8E270"]; node [shape = octagon]; pattern [label = "tweet.shop.*"]; pattern -> kindle [label = "match"]; pattern -> ipad [style = "bold, dashed", color = "#B22222", dir = back]; node [shape = box]; client123 -> pattern [label = "message", dir = back, style= "bold, dashed", color = "#B22222"]; client256 -> pattern [label = "message", dir = back, style= "bold, dashed", color = "#B22222"]; clientX -> kindle [label = "subscribe"]; clientY -> kindle [label = "subscribe"]; client3333 -> ipad [label = "message", style = "bold, dashed", color = "#B22222", dir = back]; client4444 -> ipad [label = "message", style = "bold, dashed", color = "#B22222", dir = back]; client5555 -> ipad [label = "message", style = "bold, dashed", color = "#B22222", dir = back]; // new publish [label = "PUBLISH tweet.shop.ipad message", shape = plaintext, fillcolor = "#FADCAD"]; ipad -> publish [style = "bold, dashed", color = "#B22222", dir = back]; // client color client123 [fillcolor = "#95BBE3"]; client256 [fillcolor = "#95BBE3"]; clientX [fillcolor = "#95BBE3"]; clientY [fillcolor = "#95BBE3"]; client3333 [fillcolor = "#95BBE3"]; client4444 [fillcolor = "#95BBE3"]; client5555 [fillcolor = "#95BBE3"];}](https://box.kancloud.cn/2015-09-13_55f4effda0456.svg) ### 訂閱模式 `redisServer.pubsub_patterns` 屬性是一個鏈表,鏈表中保存著所有和模式相關的信息: ~~~ struct redisServer { // ... list *pubsub_patterns; // ... }; ~~~ 鏈表中的每個節點都包含一個 `redis.h/pubsubPattern` 結構: ~~~ typedef struct pubsubPattern { redisClient *client; robj *pattern; } pubsubPattern; ~~~ `client` 屬性保存著訂閱模式的客戶端,而 `pattern` 屬性則保存著被訂閱的模式。 每當調用 `PSUBSCRIBE` 命令訂閱一個模式時,程序就創建一個包含客戶端信息和被訂閱模式的 `pubsubPattern` 結構,并將該結構添加到 `redisServer.pubsub_patterns` 鏈表中。 作為例子,下圖展示了一個包含兩個模式的 `pubsub_patterns` 鏈表,其中 `client123` 和 `client256` 都正在訂閱 `tweet.shop.*` 模式: ![digraph publish_pattern { rankdir = LR; node [shape = record, style = filled]; edge [style = bold]; redisServer [label = "redisServer| ... |<pubsub_patterns> pubsub_patterns | ...", fillcolor = "#A8E270"]; pubsubPattern_1 [label = "pubsubPattern | client \n client123 | pattern \n tweet.shop.*", fillcolor = "#95BBE3"]; pubsubPattern_2 [label = "pubsubPattern | client \n client256 | pattern \n tweet.shop.*", fillcolor = "#95BBE3"]; redisServer:pubsub_patterns -> pubsubPattern_1; pubsubPattern_1 -> pubsubPattern_2;}](https://box.kancloud.cn/2015-09-13_55f4effda9d15.svg) 如果這時客戶端 `client10086` 執行 `PSUBSCRIBE broadcast.list.*` ,那么 `pubsub_patterns` 鏈表將被更新成這樣: ![digraph pubsub_pattern { rankdir = LR; node [shape = record, style = filled]; edge [style = bold]; redisServer [label = "redisServer| ... |<pubsub_patterns> pubsub_patterns | ...", fillcolor = "#A8E270"]; pubsubPattern_1 [label = "pubsubPattern | client \n client123 | pattern \n tweet.shop.*", fillcolor = "#95BBE3"]; pubsubPattern_2 [label = "pubsubPattern | client \n client256 | pattern \n tweet.shop.*", fillcolor = "#95BBE3"]; pubsubPattern_3 [label = "pubsubPattern | client \n client10086 | pattern \n broadcast.live.*", fillcolor = "#FFC1C1"]; redisServer:pubsub_patterns -> pubsubPattern_1; pubsubPattern_1 -> pubsubPattern_2; pubsubPattern_2 -> pubsubPattern_3;}](https://box.kancloud.cn/2015-09-13_55f4effdb1cd0.svg) 通過遍歷整個 `pubsub_patterns` 鏈表,程序可以檢查所有正在被訂閱的模式,以及訂閱這些模式的客戶端。 ### 發送信息到模式 發送信息到模式的工作也是由 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 命令進行的,在前面講解頻道的時候,我們給出了這樣一段偽代碼,說它定義了 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 命令的行為: ~~~ def PUBLISH(channel, message): # 遍歷所有訂閱頻道 channel 的客戶端 for client in server.pubsub_channels[channel]: # 將信息發送給它們 send_message(client, message) ~~~ 但是,這段偽代碼并沒有完整描述 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 命令的行為,因為 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 除了將 `message` 發送到所有訂閱 `channel` 的客戶端之外,它還會將 `channel` 和 `pubsub_patterns` 中的模式進行對比,如果 `channel` 和某個模式匹配的話,那么也將 `message` 發送到訂閱那個模式的客戶端。 完整描述 [PUBLISH](http://redis.readthedocs.org/en/latest/pub_sub/publish.html#publish "(in Redis 命令參考 v2.8)") 功能的偽代碼定于如下: ~~~ def PUBLISH(channel, message): # 遍歷所有訂閱頻道 channel 的客戶端 for client in server.pubsub_channels[channel]: # 將信息發送給它們 send_message(client, message) # 取出所有模式,以及訂閱模式的客戶端 for pattern, client in server.pubsub_patterns: # 如果 channel 和模式匹配 if match(channel, pattern): # 那么也將信息發給訂閱這個模式的客戶端 send_message(client, message) ~~~ 舉個例子,如果 Redis 服務器的 `pubsub_patterns` 狀態如下: ![digraph pubsub_pattern { rankdir = LR; node [shape = record, style = filled]; edge [style = bold]; redisServer [label = "redisServer| ... |<pubsub_patterns> pubsub_patterns | ...", fillcolor = "#A8E270"]; pubsubPattern_1 [label = "pubsubPattern | client \n client123 | pattern \n tweet.shop.*", fillcolor = "#95BBE3"]; pubsubPattern_2 [label = "pubsubPattern | client \n client256 | pattern \n tweet.shop.*", fillcolor = "#95BBE3"]; pubsubPattern_3 [label = "pubsubPattern | client \n client10086 | pattern \n broadcast.live.*", fillcolor = "#FFC1C1"]; redisServer:pubsub_patterns -> pubsubPattern_1; pubsubPattern_1 -> pubsubPattern_2; pubsubPattern_2 -> pubsubPattern_3;}](https://box.kancloud.cn/2015-09-13_55f4effdbab5a.svg) 那么當某個客戶端發送信息 `"Amazon Kindle, $69."` 到 `tweet.shop.kindle` 頻道時,除了所有訂閱了 `tweet.shop.kindle` 頻道的客戶端會收到信息之外,客戶端 `client123` 和 `client256` 也同樣會收到信息,因為這兩個客戶端訂閱的 `tweet.shop.*` 模式和 `tweet.shop.kindle` 頻道匹配。 ### 退訂模式 使用 [PUNSUBSCRIBE](http://redis.readthedocs.org/en/latest/pub_sub/punsubscribe.html#punsubscribe "(in Redis 命令參考 v2.8)") 命令可以退訂指定的模式,這個命令執行的是訂閱模式的反操作:程序會刪除 `redisServer.pubsub_patterns` 鏈表中,所有和被退訂模式相關聯的 `pubsubPattern` 結構,這樣客戶端就不會再收到和模式相匹配的頻道發來的信息。 ### 小結 - 訂閱信息由服務器進程維持的 `redisServer.pubsub_channels` 字典保存,字典的鍵為被訂閱的頻道,字典的值為訂閱頻道的所有客戶端。 - 當有新消息發送到頻道時,程序遍歷頻道(鍵)所對應的(值)所有客戶端,然后將消息發送到所有訂閱頻道的客戶端上。 - 訂閱模式的信息由服務器進程維持的 `redisServer.pubsub_patterns` 鏈表保存,鏈表的每個節點都保存著一個 `pubsubPattern` 結構,結構中保存著被訂閱的模式,以及訂閱該模式的客戶端。程序通過遍歷鏈表來查找某個頻道是否和某個模式匹配。 - 當有新消息發送到頻道時,除了訂閱頻道的客戶端會收到消息之外,所有訂閱了匹配頻道的模式的客戶端,也同樣會收到消息。 - 退訂頻道和退訂模式分別是訂閱頻道和訂閱模式的反操作。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看