91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Go預(yù)言實現(xiàn)的后段狀態(tài)推送設(shè)計與實踐

Linux愛好者 ? 來源:segmentfault ? 作者:hammermax ? 2021-05-10 17:46 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

【導(dǎo)讀】本文是一篇Go預(yù)言實現(xiàn)的后段狀態(tài)推送設(shè)計與實踐,寫的非常詳細,一起來學(xué)習(xí)吧!

狀態(tài)推送

前言:掃碼登錄功能自微信提出后,越來越多的被應(yīng)用于各個web與app。這兩天公司要做一個掃碼登錄功能,在leader的技術(shù)支持幫助下(基本都靠leader排坑),終于將服務(wù)搭建起來,并且支持上萬并發(fā)。

長連接選擇

決定做掃碼登錄功能之后,在網(wǎng)上查看了很多的相關(guān)資料。對于掃碼登錄的實現(xiàn)方式有很多,淘寶用的是輪詢,微信用長連接,QQ用輪詢……。方式雖多,但目前看來大體分為兩種,1:輪詢,2:長連接。(兩種方式各有利弊吧,我研究不深,優(yōu)缺點就不贅述了)

在和leader討論之后選擇了用長連接的方式。所以對長連接的實現(xiàn)方式調(diào)研了很多:

1.微信長連接:通過動態(tài)加載script的方式實現(xiàn)。

這種方式好在沒有跨域問題。

2.websocket長連接:在PC端與服務(wù)端搭起一條長連接后,服務(wù)端主動不斷地向PC端推送狀態(tài)。這應(yīng)該是最完美的做法了。

3.我使用的長連接:PC端向服務(wù)端發(fā)送請求,服務(wù)端并不立即響應(yīng),而是hold住,等到用戶掃碼之后再響應(yīng)這個請求,響應(yīng)后連接斷開。

為什么不采用websocket呢?因為當時比較急、而對于websocket的使用比較陌生,所以沒有使用。不過我現(xiàn)在這種做法在資源使用上比websocket低很多。

接口設(shè)計

(本來想把leader畫的一副架構(gòu)圖放上來,但涉及到公司,不敢)

自己畫的一副流程圖

96217642-b0bf-11eb-bf61-12bb97331649.png

稍微解釋一下:

第一條連接:打開PC界面的時候向服務(wù)端發(fā)送請求并建立長連接(1)。當APP成功掃碼后(2),響應(yīng)這次請求(3)。

第二條連接類似。

分析得出我們的服務(wù)只需要兩個接口即可

1.與PC建立長連接的接口

2.接收APP端數(shù)據(jù)并將數(shù)據(jù)發(fā)送給前端的接口

再細想可將這兩個接口抽象為:

1.PC獲取狀態(tài)接口:get

2.APP設(shè)置狀態(tài)接口:set

具體實現(xiàn)

用GO寫的(不多嗶嗶)

長連接的根本原理:連接請求后,服務(wù)端利用channel阻塞住。等到channel中有value后,將value響應(yīng)

Router

func Router(){

http.HandleFunc(“/status/get”, Get)

http.HandleFunc(“/status/set”, Set)

}

GET

每一條連接需要有一個KEY作標識,不然APP設(shè)置的狀態(tài)不知道該發(fā)給那臺PC。每一條連接即一個channel

var Status map[string](chan string) = make(map[string](chan string))

func Get(w http.ResponseWriter, r *http.Request){

//接收key的操作

key = //PC在請求接口時帶著的key

Status[key] = make(chan string) //不需要緩沖區(qū)

value := 《-Status[key]

ResponseJson(w, 0, “success”, value) //自己封的響應(yīng)JSON方法

}

SET

APP掃碼后可以得到二維碼中的KEY,同時將想給PC發(fā)送的VALUE一起發(fā)送給服務(wù)端

func Set(w http.ResponseWriter, r *http.Request){

key =

value = //向PC傳遞的值

Status[key] 《- value

}

這就是實現(xiàn)的最基本原理。

接下來我們一點點實現(xiàn)其他的功能。

1.超時

從網(wǎng)上找了很多資料,大部分都說這種方式

srv := &http.Server{

ReadTimeout: 5 * time.Second,

WriteTimeout: 10 * time.Second,

}

log.Println(srv.ListenAndServe())

這種方式確實是設(shè)置讀超時與寫超時。但(親測)這種超時方式并不友善,假如現(xiàn)在WriteTimeout是10s,PC端請求過來之后,長連接建立。PC處于pending狀態(tài),并且服務(wù)端被channel阻塞住。10s之后,由于超時連接失效(并沒有斷,我也不了解其中原理)。PC并不知道連接斷了,依然處于pending狀態(tài),服務(wù)端的這個goroutine依然被阻塞在這里。這個時候我調(diào)用set接口,第一次調(diào)用沒用反應(yīng),但第二次調(diào)用PC端就能成功接收value。

99289618-b0bf-11eb-bf61-12bb97331649.png

從圖可以看出,我設(shè)置的WriteTimeout為10s,但這條長連接即使15s依然能收到成功響應(yīng)。(ps:我調(diào)用了兩次set接口,第一次沒有反應(yīng))

研究后決定不使用這種方式設(shè)置超時,采用接口內(nèi)部定時的方式實現(xiàn)超時返回

select {

case 《-`Timer`:

utils.ResponseJson(w, -1, “timeout”, nil)

case value := 《-statusChan:

utils.ResponseJson(w, 0, “success”, value)

}

Timer即為定時器。剛開始Timer是這樣定義的

Timer := time.After(60 * time.Second)

60s后Timer會自動返回一個值,這時上面的通道就開了,響應(yīng)timeout

但這樣做有一個弊端,這個定時器一旦創(chuàng)建就必須等待60s,并且我沒想到辦法提前將定時器關(guān)了。如果這個長連接剛建立后5s就被響應(yīng),那么這個定時器就要多存在55s。這樣對資源是一種浪費,并不合理。

這里選用了context作為定時器

ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Timeout)*time.Second)

defer cancel()

select {

case 《-ctx.Done():

utils.ResponseJson(w, -1, “timeout”, nil)

case result := 《-Status[key]:

utils.ResponseJson(w, 0, “success”, result)

}

ctx在初始化的時候就設(shè)置了超時時間time.Duration(Timeout)*time.Second

超時之后ctx.Done()返回完成,起到定時作用。如果沒有cancel()則會有一樣的問題。原因如下

993413e4-b0bf-11eb-bf61-12bb97331649.png

context對比time包。提供了手動關(guān)閉定時器的方法cancel()

只要get請求結(jié)束,都會去關(guān)閉定時器,這樣可以避免資源浪費(一定程度避免內(nèi)存泄漏)。

注即使golang官方文檔中,也推薦defer cancel()這樣寫

993ef908-b0bf-11eb-bf61-12bb97331649.jpg

官方文檔也寫到:即使ctx會在到期時關(guān)閉,但在任何場景手動調(diào)用cancel都是很好的做法。

2.多機支持

服務(wù)如果只部署在一臺機器上,萬一機器跪了,那就全跪了。

所以我們的服務(wù)必須同時部署在多個機器上工作。即使其中一臺掛了,也不影響服務(wù)使用。

這個圖不會畫,只能用leader的圖了

99ae677a-b0bf-11eb-bf61-12bb97331649.jpg

在項目初期討論的時候leader給出了兩種方案。1.如圖使用redis做多機調(diào)度。2.使用zookeeper將消息發(fā)送給多機

因為現(xiàn)在是用redis做的,只講述下redis的實現(xiàn)。(但依賴redis并不是很好,多機的負載均衡還要依賴其他工具。zookeeper能夠解決這個問題,之后會將redis換成zookeeper)

首先我們要明確多機的難點在哪?

我們有兩個接口,get、set。get是給前端建立長連接用的。set是后端設(shè)置狀態(tài)用的。

假設(shè)有兩臺機器A、B。若前端的請求發(fā)送到A機器上,即A機器與前端連接,此時后端調(diào)用set接口,如果調(diào)用的是A機器的set接口,那是最好,長連接就能成功響應(yīng)。但如果調(diào)用了B機器的set接口,B機器上又沒有這條連接,那么這條連接就無法響應(yīng)。

所以難點在于如何將同一個key的get、set分配到一臺機器。

有人給我提過一個意見:在做負載均衡的時候,就將連接分配到指定機器。剛開始我覺的很有道理,但細細想,如果這樣做,在以后如果要加機器或減機器的時候會很麻煩。對橫向的增減機器不友善。

最后我還是采用了leader給出的方案:用redis綁定key與機器的關(guān)系

即前端請求到一臺機器上,以key做鍵,以機器IP做值放在redis里面。后端請求set接口時先用key去redis里面拿到機器IP,再將value發(fā)送到這臺機器上。

此時就多了一個接口,用于機器內(nèi)部相互調(diào)用

ChanSet

func Router(){

http.HandleFunc(“/status/get”, Get)

http.HandleFunc(“/status/set”, Set)

http.HandleFunc(“/channel/set”, ChanSet)

}

func ChanSet(w http.ResponseWriter, r *http.Request){

key =

value =

Status[key] 《- value

}

GET

func Get(w http.ResponseWriter, r *http.Request){

IP = getLocalIp() //得到本機IP

RedisSet(key, IP) //以key做鍵,IP做值放入redis

Status[key] 《- value

}

SET

func Set(w http.ResponseWriter, r *http.Request){

IP = RedisGet(key) //用key去取對應(yīng)機器的IP

Post(IP, key, value) //將key與value都發(fā)送給這臺機器

}

注這里相當于用redis sentinel做多臺機器的通信。哨兵會幫我們將數(shù)據(jù)同步到所有機器上

這樣即可實現(xiàn)多機支持

3.跨域

剛部署到線上的時候,第一次嘗試就跪了。查看錯誤(Access-Control-Allow-Origin)

因為前端是通過AJAX請求的長連接服務(wù),所以存在跨域問題。

在服務(wù)端設(shè)置允許跨域

func Get(w http.ResponseWriter, r *http.Request){

w.Header().Set(“Access-Control-Allow-Origin”, “*”)

w.Header().Add(“Access-Control-Allow-Headers”, “Content-Type”)

}

若是像微信的做法,動態(tài)的加載script方式,則沒有跨域問題。

服務(wù)端直接允許跨域,可能會有安全問題,但我不是很了解,這里為了使用,就允許跨域了。

4.Map并發(fā)讀寫問題

跨域問題解決之后,線上可以正常使用了。緊接著請測試同學(xué)壓測了一下。

預(yù)期單機并發(fā)10000以上,測試同學(xué)直接壓了10000,服務(wù)掛了。

可能預(yù)期有點高,5000吧,于是壓了5000,服務(wù)掛了。

1000呢,服務(wù)掛了。

100,服務(wù)掛了。

……

這下豁然開朗,不可能是機器問題,絕對是有BUG

看了下報錯

9a396aaa-b0bf-11eb-bf61-12bb97331649.jpg

去看了下官方文檔

9a43e4da-b0bf-11eb-bf61-12bb97331649.png

Map是不能并發(fā)的寫操作,但可以并發(fā)的讀。

原來對Map操作是這樣寫的

func Get(w http.ResponseWriter, r *http.Request){

`Status[key] = make(chan string)`

`defer close(Status[key])`

select {

case 《-ctx.Done():

utils.ResponseJson(w, -1, “timeout”, nil)

case `result := 《-Status[key]`:

utils.ResponseJson(w, 0, “success”, result)

}

}

func ChanSet(w http.ResponseWriter, r *http.Request){

`Status[key] 《- value`

}

Status[key] = make(chan string)在Status(map)里面初始化一個通道,是map的寫操作

result := 《-Status[key]從Status[key]通道中讀取一個值,由于是通道,這個值取出來后,通道內(nèi)就沒有了,所以這一步也是對map的寫操作

Status[key] 《- value向Status[key]內(nèi)放入一個值,map的寫操作

由于這三處操作的是一個map,所以要加同一把鎖

var Mutex sync.Mutex

func Get(w http.ResponseWriter, r *http.Request){

//這里是同組大佬教我的寫法,通道之間的拷貝傳遞的是指針,即statusChan與Status[key]指向的是同一個通道

statusChan := make(chan string)

Mutex.Lock()

Status[key] = statusChan

Mutex.Unlock()

//在連接結(jié)束后將這些資源都釋放

defer func(){

Mutex.Lock()

delete(Status, key)

Mutex.Unlock()

close(statusChan)

RedisDel(key)

}()

select {

case 《-ctx.Done():

utils.ResponseJson(w, -1, “timeout”, nil)

case result := 《-statusChan:

utils.ResponseJson(w, 0, “success”, result)

}

}

func ChanSet(w http.ResponseWriter, r *http.Request){

Mutex.Lock()

Status[key] 《- value

Mutex.Unlock()

}

到現(xiàn)在,服務(wù)就可以正常使用了,并且支持上萬并發(fā)。

5.Redis過期時間

服務(wù)正常使用之后,leader review代碼,提出redis的數(shù)據(jù)為什么不設(shè)置過期時間,反而要自己手動刪除。我一想,對啊。

于是設(shè)置了過期時間并且將RedisDel(key)刪了。

設(shè)置完之后不出意外的服務(wù)跪了。

究其原因

我用一個key=1請求get,會在redis內(nèi)存儲一條數(shù)據(jù)記錄(1 =》 Ip)。如果我set了這條連接,按之前的邏輯會將redis里的這條數(shù)據(jù)刪掉,而現(xiàn)在是等待它過期。若是在過期時間內(nèi),再次以這個key=1,調(diào)用set接口。set接口依然會從redis中拿到IP,Post數(shù)據(jù)到ChanSet接口。而ChanSet中Status[key] 《- value由于Status[key]是關(guān)閉的,會阻塞在這里,阻塞不要緊,但之前這里加了鎖,導(dǎo)致整個程序都阻塞在這里。

這里和leader討論過,仍使用redis過期時間但需要修復(fù)這個Bug

func ChanSet(w http.ResponseWriter, r *http.Request){

Mutex.Lock()

ch := Status[key]

Mutex.Unlock()

if ch != nil {

ch 《- value

}

}

不過這樣有一個問題,就是同一個key,在過期時間內(nèi)是無法多次使用的。不過這與業(yè)務(wù)要求并不沖突。

6.Linux文件最大句柄數(shù)

在給測試同學(xué)測試之前,自己也壓測了一下。不過剛上來就瘋狂報錯,“%¥#@¥……%……%%..too many fail open.。.”

搜索結(jié)果是linux默認最大句柄數(shù)1024.

開了下自己的機器 ulimit -a 果然1024。修改(修改方法不多BB)

7.同時監(jiān)聽兩個端口

服務(wù)有兩個API,get是給前端使用的,對外開放。set是給后端使用的,內(nèi)部接口。所以這兩個接口需要放在兩個端口上。

由于http.ListenAndServe()本身有阻塞,故第一個監(jiān)聽需要一個goroutine

go http.ListenAndServe(“:11000”, FrontendMux) //對外開放的端口

http.ListenAndServe(“:11001”, BackendMux) //內(nèi)部使用的端口

原文標題:Golang-長連接-狀態(tài)推送

文章出處:【微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • PC
    PC
    +關(guān)注

    關(guān)注

    9

    文章

    2167

    瀏覽量

    159378
  • 連接
    +關(guān)注

    關(guān)注

    2

    文章

    101

    瀏覽量

    21829

原文標題:Golang-長連接-狀態(tài)推送

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    Go 語言高并發(fā)服務(wù)設(shè)計與性能調(diào)優(yōu)實戰(zhàn):從萬級到百萬級并發(fā)的演進之路

    ()) // 獲取 CPU 核心數(shù) fmt.Printf(\"NumCPU: %d\\\\n\", runtime.NumCPU()) } 1.2 Goroutine 最佳實踐 go
    發(fā)表于 02-18 19:19

    蔚來世界模型NWM全新版本正式推送

    2026年1月28日,「蔚來世界模型 NWM」全新版本正式開啟推送,首批將為超過46萬輛「Banyan 榕」車型推送?!窩edar 雪松」車型及「Cedar S 雪松」車型,也將于近期開啟推送。
    的頭像 發(fā)表于 01-28 15:38 ?436次閱讀

    低成本TLI4971/TLE4971電流傳感器評估套件——MS2Go與S2Go

    低成本TLI4971/TLE4971電流傳感器評估套件——MS2Go與S2Go 在電子工程師的日常工作中,電流傳感器的評估和應(yīng)用是一個重要的環(huán)節(jié)。今天我們要介紹的是英飛凌(Infineon
    的頭像 發(fā)表于 12-19 16:50 ?862次閱讀

    探索TLE493D-P3XX-MS2GO 3D 2Go套件:開啟3D磁傳感器評估之旅

    探索TLE493D-P3XX-MS2GO 3D 2Go套件:開啟3D磁傳感器評估之旅 在電子工程師的日常工作中,評估和開發(fā)磁傳感器是一項常見且重要的任務(wù)。英飛凌(Infineon
    的頭像 發(fā)表于 12-18 17:15 ?888次閱讀

    深度解析 | 低抖動高精度EtherCAT多軸控制的實現(xiàn)實踐案例

    深度解析 | 低抖動高精度EtherCAT多軸控制的實現(xiàn)實踐案例 在工業(yè)自動化領(lǐng)域,運動控制的精度和穩(wěn)定性直接決定了生產(chǎn)效率和產(chǎn)品質(zhì)量。其中EtherCAT多軸控制技術(shù)尤為引人注目。今天,我們
    發(fā)表于 12-09 17:17

    電能質(zhì)量在線監(jiān)測裝置支持的數(shù)據(jù)推送頻率是多少?

    1 秒~24 小時 的自定義周期,部分高端設(shè)備可實現(xiàn) 毫秒級實時推送 。 一、按數(shù)據(jù)類型劃分的推送頻率 數(shù)據(jù)類型 典型推送頻率 應(yīng)用場景 標準 / 行業(yè)建議 實時基礎(chǔ)參數(shù) (電壓 /
    的頭像 發(fā)表于 12-05 15:07 ?425次閱讀
    電能質(zhì)量在線監(jiān)測裝置支持的數(shù)據(jù)<b class='flag-5'>推送</b>頻率是多少?

    房產(chǎn)數(shù)據(jù)平臺安家go獲取地區(qū)列表數(shù)據(jù)的API接口

    如何使用安家go提供的API接口來獲取地區(qū)列表數(shù)據(jù),包括API端點、請求參數(shù)、響應(yīng)格式以及代碼實現(xiàn)。我們將逐步引導(dǎo)您完成整個過程,確保您能輕松集成到自己的項目中。 1. API概述 安家go的“獲取地區(qū)列表”API是一個基于RE
    的頭像 發(fā)表于 11-21 14:38 ?386次閱讀
    房產(chǎn)數(shù)據(jù)平臺安家<b class='flag-5'>go</b>獲取地區(qū)列表數(shù)據(jù)的API接口

    電能質(zhì)量在線監(jiān)測裝置的數(shù)據(jù)推送頻率調(diào)整會影響數(shù)據(jù)的準確性嗎?

    電能質(zhì)量在線監(jiān)測裝置的數(shù)據(jù)推送頻率調(diào)整對數(shù)據(jù)準確性的影響需從 采樣、處理、傳輸 三個核心環(huán)節(jié)綜合評估,其關(guān)鍵取決于 調(diào)整策略與裝置設(shè)計的匹配度 。以下是基于技術(shù)原理與工程實踐的詳細分析: 一、核心
    的頭像 發(fā)表于 11-07 11:08 ?681次閱讀

    設(shè)備的狀態(tài)監(jiān)測可以通過哪些方式實現(xiàn)?

    設(shè)備狀態(tài)監(jiān)測的核心是 通過 “硬件傳感 + 軟件自檢 + 通信反饋 + 遠程聯(lián)動”,實現(xiàn)對設(shè)備 “健康狀態(tài)、運行狀態(tài)、安全狀態(tài)” 的全維度感
    的頭像 發(fā)表于 11-07 09:44 ?922次閱讀

    資源狀態(tài)感知是如何實現(xiàn)對網(wǎng)絡(luò)鏈路狀態(tài)的實時感知的?

    資源狀態(tài)感知對網(wǎng)絡(luò)鏈路狀態(tài)的實時監(jiān)測是通過硬件底層檢測、協(xié)議層交互、算法模型分析的多層協(xié)同實現(xiàn)的,具體技術(shù)路徑如下: 一、硬件層:物理信號的實時捕獲 PHY 芯片的直接感知以太網(wǎng) PHY 芯片(如
    的頭像 發(fā)表于 11-06 14:49 ?700次閱讀

    訂單實時狀態(tài)查詢接口技術(shù)實現(xiàn)

    ? ?在電子商務(wù)系統(tǒng)中,訂單實時狀態(tài)查詢是核心功能之一。用戶需要即時獲取訂單的最新狀態(tài)(如“已支付”、“發(fā)貨中”或“已完成”),這對用戶體驗和業(yè)務(wù)運營至關(guān)重要。本文將一步步介紹如何設(shè)計并實現(xiàn)一個高效
    的頭像 發(fā)表于 10-21 17:58 ?747次閱讀
    訂單實時<b class='flag-5'>狀態(tài)</b>查詢接口技術(shù)<b class='flag-5'>實現(xiàn)</b>

    淘寶/天貓:使用訂單查詢API實時追蹤包裹狀態(tài),自動推送物流通知至用戶

    實現(xiàn)包裹狀態(tài)的實時監(jiān)控,并自動推送物流更新通知給用戶,從而優(yōu)化服務(wù)流程。本文將逐步介紹如何利用淘寶/天貓的開放平臺API實現(xiàn)這一功能,確保高效、可靠。 1. 理解訂單查詢API的基本原
    的頭像 發(fā)表于 09-10 16:55 ?1324次閱讀
    淘寶/天貓:使用訂單查詢API實時追蹤包裹<b class='flag-5'>狀態(tài)</b>,自動<b class='flag-5'>推送</b>物流通知至用戶

    鋰電池制造:電芯后段處理中的除氣工藝

    在鋰離子電池的規(guī)?;圃熘校娦?b class='flag-5'>后段處理是將電極組件轉(zhuǎn)化為合格成品的關(guān)鍵環(huán)節(jié),直接決定電池的能量密度、循環(huán)壽命與安全性能。其中,除氣工藝作為后段處理的核心工序,專門針對電芯在化成過程中產(chǎn)生的反應(yīng)氣體
    的頭像 發(fā)表于 08-11 14:52 ?1593次閱讀
    鋰電池制造:電芯<b class='flag-5'>后段</b>處理中的除氣工藝

    蔚來世界模型NWM首個版本正式推送

    近日,「蔚來世界模型 NWM」首個版本正式開啟推送。首批推送車型為超過40萬臺的「Banyan 榕」車型。「Cedar 雪松」車型,包括ET9、新ES6、新EC6、新ET5、新ET5T在內(nèi),將會于6月底開啟推送
    的頭像 發(fā)表于 06-04 15:13 ?937次閱讀

    單片機C語言實例(350+例)

    350+單片機C語言實例! 純分享帖,需要者可點擊附件免費獲取完整資料~~~【免責(zé)聲明】本文系網(wǎng)絡(luò)轉(zhuǎn)載,版權(quán)歸原作者所有。本文所用視頻、圖片、文字如涉及作品版權(quán)問題,請第一時間告知,刪除內(nèi)容!
    發(fā)表于 05-22 21:47