測(cè)試一下Go語(yǔ)言goroutine的魅力

本段測(cè)試代碼的說(shuō)明

這里用go語(yǔ)言簡(jiǎn)單的寫了一個(gè)創(chuàng)建訂單的業(yè)務(wù)流程,目的是測(cè)試一下Go語(yǔ)言的goroutine的魅力,注意,只是測(cè)試目的,代碼并不存在任何實(shí)用性.

測(cè)試邏輯說(shuō)明

創(chuàng)建訂單的業(yè)務(wù)邏輯,一般情況下都需要下面的幾個(gè)步驟:
1.檢查訂單商品ID是否有效
2.檢查訂單商品ID的數(shù)量是否足夠本次創(chuàng)建訂單
3.檢查優(yōu)惠券是否可用
4.計(jì)算每個(gè)商品的訂單價(jià)格(這里可能是查詢商品的最新單價(jià)*數(shù)量)
5.計(jì)算訂單金額
6.創(chuàng)建訂單

測(cè)試代碼中,已經(jīng)把上面的6個(gè)步驟,全部封裝成了獨(dú)立的函數(shù),每個(gè)函數(shù)內(nèi)都啟用了延時(shí)1秒的操作來(lái)模擬真實(shí)
情況下的一些時(shí)間的耗費(fèi);

最終目的,是想要分別嘗試一下,用goroutine方式和不用goroutine方式的區(qū)別,和性能差異!

測(cè)試結(jié)果

這里是測(cè)試代碼的兩次運(yùn)行結(jié)果,從結(jié)果中可以看到,首先是程序的執(zhí)行過(guò)程的不同,再就是程序耗費(fèi)時(shí)間的不同,啟用goroutine的程序耗費(fèi)時(shí)間是3.0122762s,而沒啟用goroutine的程序耗費(fèi)時(shí)間是9.003462s,這性能差距兼職令人驚喜啊

D:\go\src\github.com\just5325\day01\04_channel>go run main.go
請(qǐng)選擇是否啟用子協(xié)程處理任務(wù)(1:使用 0:不使用 直接回車默認(rèn)為1):
1
正在計(jì)算訂單金額
正在檢查商品是否有效 ID: 1
正在計(jì)算每個(gè)商品的訂單價(jià)格 ID: 1
正在檢查商品數(shù)量是否有效 ID:1 數(shù)量:1
正在檢查優(yōu)惠券是否可用 ID:1
正在計(jì)算每個(gè)商品的訂單價(jià)格 ID: 2
正在檢查商品是否有效 ID: 2
正在檢查商品數(shù)量是否有效 ID:2 數(shù)量:1
正在創(chuàng)建訂單,訂單金額: 100
結(jié)束! [已啟用子協(xié)程],程序耗費(fèi)時(shí)間:3.0122762s

D:\go\src\github.com\just5325\day01\04_channel>go run main.go
請(qǐng)選擇是否啟用子協(xié)程處理任務(wù)(1:使用 0:不使用 直接回車默認(rèn)為1):
0
正在檢查商品是否有效 ID: 1
正在檢查商品是否有效 ID: 2
正在檢查商品數(shù)量是否有效 ID:1 數(shù)量:1
正在檢查商品數(shù)量是否有效 ID:2 數(shù)量:1
正在檢查優(yōu)惠券是否可用 ID:1
正在計(jì)算每個(gè)商品的訂單價(jià)格 ID: 1
正在計(jì)算每個(gè)商品的訂單價(jià)格 ID: 2
正在計(jì)算訂單金額
正在創(chuàng)建訂單,訂單金額: 100
結(jié)束! [未啟用子協(xié)程],程序耗費(fèi)時(shí)間:9.003462s

測(cè)試代碼

package main

import (
    "fmt"
    "time"
)

// 是否啟用子子協(xié)程 false:不啟用(就像我們寫PHP代碼一樣從上至下的順序運(yùn)行代碼) true:啟用(啟用多個(gè)協(xié)程同時(shí)處理不同的業(yè)務(wù)邏輯,相關(guān)聯(lián)的數(shù)據(jù)通過(guò)通道傳輸)
var subGoroutine bool = false

/**
@title 檢查商品是否有效
@params productData []map[string]int 商品數(shù)據(jù)
@params out chan<- bool 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為是否有效 true:有效 false:無(wú)效
*/
func checkProductById(productData []map[string]int, out chan bool) {
    checkProductId := make(chan int, 10)
    funcRet := make(chan bool, 10)
    // 開啟子協(xié)程,把商品ID逐個(gè)發(fā)送到通道中
    go func() {
        for _, value := range productData {
            checkProductId <- value["id"]
        }
        // 關(guān)閉通道
        close(checkProductId)
    }()

    // 開啟子協(xié)程,遍歷商品ID,檢查通道中的每個(gè)商品是否有效
    go func() {
        // for range遍歷通道,值為商品ID,通道關(guān)閉后會(huì)自動(dòng)結(jié)束循環(huán),所以這里只管遍歷接收通道的數(shù)據(jù)并處理后發(fā)送數(shù)據(jù)到通道就OK了
        for value := range checkProductId {
            // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
            fmt.Println("正在檢查商品是否有效 ID:", value)
            time.Sleep(time.Second * 1)

            // 我們就當(dāng)所有商品ID檢查結(jié)果都為有效
            funcRet <- true
        }
        close(funcRet)
    }()

    // 遍歷通道,發(fā)送數(shù)據(jù)
    for v := range funcRet {
        out <- v
    }
    close(out)
}

/*
@title 檢查商品數(shù)量是否有效
@params productData []map[string]int 商品數(shù)據(jù)
@params out chan<- bool 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為是否有效 true:有效 false:無(wú)效
*/
func checkProductNumById(productData []map[string]int, out chan bool) {
    checkProductNum := make(chan map[string]int, 10)
    funcRet := make(chan bool, 10)
    // 開啟子協(xié)程,把商品ID逐個(gè)發(fā)送到通道中
    go func() {
        for _, value := range productData {
            checkProductNum <- value
        }
        // 關(guān)閉通道
        close(checkProductNum)
    }()

    // 開啟子協(xié)程,遍歷商品信息,檢查通道中的每個(gè)商品的數(shù)量是否有效
    go func() {
        // for range遍歷通道,值為商品信息,通道關(guān)閉后會(huì)自動(dòng)結(jié)束循環(huán),所以這里只管遍歷接收通道的數(shù)據(jù)并處理后發(fā)送數(shù)據(jù)到通道就OK了
        for value := range checkProductNum {
            // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
            fmt.Printf("正在檢查商品數(shù)量是否有效 ID:%d 數(shù)量:%d\n", value["id"], value["num"])
            time.Sleep(time.Second * 1)

            // 我們就當(dāng)所有商品ID檢查結(jié)果都為有效
            funcRet <- true
        }
        close(funcRet)
    }()

    // 遍歷通道,發(fā)送數(shù)據(jù)
    for v := range funcRet {
        out <- v
    }
    close(out)
}

/*
@title 檢查優(yōu)惠券是否可用
@params couponId int 優(yōu)惠券ID
@params out chan<- bool 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為是否有效 true:有效 false:無(wú)效
*/
func checkCoupon(couponId int, out chan int) {
    // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
    fmt.Printf("正在檢查優(yōu)惠券是否可用 ID:%d\n", couponId)
    time.Sleep(time.Second * 1)

    // 發(fā)送數(shù)據(jù)到通道
    out <- 100
    close(out)
}

/**
@title 計(jì)算每個(gè)商品的訂單價(jià)格(這里可能是查詢商品的最新單價(jià)*數(shù)量)
@params productData []map[string]int 商品數(shù)據(jù)
@params out chan<- int 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為是訂單中商品的金額
*/
func getOrderProductAmountById(productData []map[string]int, out chan int) {
    checkProductId := make(chan int, 10)
    funcRet := make(chan int, 10)
    // 開啟子協(xié)程,把商品ID逐個(gè)發(fā)送到通道中
    go func() {
        for _, value := range productData {
            checkProductId <- value["id"]
        }
        // 關(guān)閉通道
        close(checkProductId)
    }()

    // 開啟子協(xié)程,遍歷商品ID,檢查通道中的每個(gè)商品是否有效
    go func() {
        // for range遍歷通道,值為商品ID,通道關(guān)閉后會(huì)自動(dòng)結(jié)束循環(huán),所以這里只管遍歷接收通道的數(shù)據(jù)并處理后發(fā)送數(shù)據(jù)到通道就OK了
        for value := range checkProductId {
            // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
            fmt.Println("正在計(jì)算每個(gè)商品的訂單價(jià)格 ID:", value)
            time.Sleep(time.Second * 1)

            // 我們就當(dāng)所有商品ID檢查結(jié)果都為有效
            funcRet <- 100
        }
        close(funcRet)
    }()

    // 遍歷通道,發(fā)送數(shù)據(jù)
    for v := range funcRet {
        out <- v
    }
    close(out)
}

/**
@title 計(jì)算訂單金額
@params productAmount chan int 商品金額
@params couponAmount chan int 優(yōu)惠券金額
@params out chan<- int 發(fā)送數(shù)據(jù)的通道,通道內(nèi)傳遞的值為訂單金額
*/
func getOrderAmount(productAmount chan int, couponAmount chan int, out chan int) {
    // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
    fmt.Println("正在計(jì)算訂單金額")
    time.Sleep(time.Second * 1)

    // 訂單總價(jià)
    var orderAmount int
    for v := range productAmount {
        orderAmount += v
    }
    // 不考慮滿減,能用的優(yōu)惠券就減
    for v := range couponAmount {
        orderAmount -= v
    }
    // 發(fā)送數(shù)據(jù)到通道
    out <- orderAmount
    close(out)
}

/**
@title 創(chuàng)建訂單
@params orderAmountChannel chan int 訂單金額
*/
func create(orderAmountChannel chan int) {
    // 訂單總價(jià)
    var orderAmount int
    for v := range orderAmountChannel {
        orderAmount = v
    }

    // 延時(shí)1秒,代表這里執(zhí)行的各種耗費(fèi)時(shí)間的任務(wù)
    fmt.Println("正在創(chuàng)建訂單,訂單金額:", orderAmount)
    time.Sleep(time.Second * 1)
}

/**
@title 獲取用戶輸入信息,配置是否使用子協(xié)程
@params isSubGoroutine int 請(qǐng)選擇是否啟用子協(xié)程處理任務(wù)(1:使用 0:不使用 直接回車默認(rèn)為1)
*/
func inputIsSubGoroutine() bool {
    // 聲明一個(gè)int類型的變量,接收用戶輸入的信息
    var isSubGoroutine int
    // 打印輸入提示信息
    fmt.Println("請(qǐng)選擇是否啟用子協(xié)程處理任務(wù)(1:使用 0:不使用 直接回車默認(rèn)為1):")
    // 預(yù)先聲明一個(gè)error類型的變量,用作接收錯(cuò)誤信息
    var err error
    // fmt.Scanln從標(biāo)準(zhǔn)輸入os.Stdin讀取文本,會(huì)在讀取到換行時(shí)停止.
    _, err = fmt.Scanln(&isSubGoroutine)
    // 使用if語(yǔ)句,初始化定義一個(gè)變量為空,如果有錯(cuò)誤信息,提示錯(cuò)誤并退出程序
    if errorMsg := ""; err != nil {
        // 這里我們正好也練習(xí)一下switch的寫法,實(shí)際上和php也是一樣的
        switch err.Error() {
        case "unexpected newline":
            // 未輸入?yún)?shù)時(shí),不報(bào)錯(cuò),默認(rèn)為1,使用子協(xié)程
            isSubGoroutine = 1
        case "expected newline", "expected integer":
            errorMsg = "參數(shù)錯(cuò)誤,只允許輸入整型數(shù)字的 0 或者 1 "
        default:
            errorMsg = fmt.Sprintf("參數(shù)錯(cuò)誤,Error: [%s]", err.Error())
        }
        fmt.Println(errorMsg)
    }
    if isSubGoroutine == 1 {
        subGoroutine = true
    } else {
        subGoroutine = false
    }
    return subGoroutine
}

func main() {
    // 獲取用戶輸入信息,配置是否使用子協(xié)程
    inputIsSubGoroutine()

    // 程序開始運(yùn)行時(shí)間
    startDate := time.Now()
    // 練習(xí)一下channel通道的使用,簡(jiǎn)單的寫一下創(chuàng)建訂單的業(yè)務(wù)流程吧

    // 聲明一個(gè)變量productData,類型為切片,值為map類型,存儲(chǔ)創(chuàng)建訂單的商品信息
    var productData []map[string]int
    productData = append(productData, map[string]int{
        // id的表示商品id
        "id": 1,
        // num的表示購(gòu)買數(shù)量
        "num": 1,
    })
    productData = append(productData, map[string]int{
        "id":  2,
        "num": 1,
    })

    // 聲明一個(gè)變量couponId,表示訂單所使用的優(yōu)惠券ID
    couponId := 1
    // OK,到這里進(jìn)入主業(yè)務(wù)流程了
    // 一般情況下都需要的幾個(gè)步驟:
    // 1.檢查訂單商品ID是否有效
    // 2.檢查訂單商品ID的數(shù)量是否足夠本次創(chuàng)建訂單
    // 3.檢查優(yōu)惠券是否可用
    // 4.計(jì)算每個(gè)商品的訂單價(jià)格(這里可能是查詢商品的最新單價(jià)*數(shù)量)
    // 5.計(jì)算訂單金額
    // 6.創(chuàng)建訂單

    // 創(chuàng)建一個(gè)切片,存儲(chǔ)所有的商品ID
    productIds := make([]int, 0)
    for _, value := range productData {
        productIds = append(productIds, value["id"])
    }

    // 1.檢查訂單商品ID是否有效 +++++++++++++++++++++++++++++++++++++++++ Start
    // 創(chuàng)建一個(gè)通道,傳輸檢查錯(cuò)誤信息
    checkProductByIdFuncRet := make(chan bool, 10)
    // 判斷配置是否啟用子協(xié)程
    if subGoroutine {
        go checkProductById(productData, checkProductByIdFuncRet)
    } else {
        checkProductById(productData, checkProductByIdFuncRet)
    }
    // 1.檢查訂單商品ID是否有效 +++++++++++++++++++++++++++++++++++++++++ End

    // 2.檢查訂單商品ID的數(shù)量是否足夠本次創(chuàng)建訂單 ++++++++++++++++++++++++++ Start
    // 創(chuàng)建一個(gè)通道,傳輸檢查錯(cuò)誤信息
    checkProductNumByIdFuncRet := make(chan bool, 10)
    // 判斷配置是否啟用子協(xié)程
    if subGoroutine {
        go checkProductNumById(productData, checkProductNumByIdFuncRet)
    } else {
        checkProductNumById(productData, checkProductNumByIdFuncRet)
    }
    // 2.檢查訂單商品ID的數(shù)量是否足夠本次創(chuàng)建訂單 ++++++++++++++++++++++++++ End

    // 3.檢查優(yōu)惠券是否可用 ++++++++++++++++++++++++++++++++++++++++++++ Start
    // 創(chuàng)建一個(gè)通道,傳輸檢查錯(cuò)誤信息
    checkCouponFuncRet := make(chan int, 10)
    // 判斷配置是否啟用子協(xié)程
    if subGoroutine {
        go checkCoupon(couponId, checkCouponFuncRet)
    } else {
        checkCoupon(couponId, checkCouponFuncRet)
    }
    // 3.檢查優(yōu)惠券是否可用 ++++++++++++++++++++++++++++++++++++++++++++ End

    // 計(jì)算每個(gè)商品的訂單價(jià)格(這里可能是查詢商品的最新單價(jià)*數(shù)量) ++++++++++++++ Start
    // 創(chuàng)建一個(gè)通道,傳輸每個(gè)商品的訂單價(jià)格
    getOrderProductAmountByIdFuncRet := make(chan int, 10)
    // 判斷配置是否啟用子協(xié)程
    if subGoroutine {
        go getOrderProductAmountById(productData, getOrderProductAmountByIdFuncRet)
    } else {
        getOrderProductAmountById(productData, getOrderProductAmountByIdFuncRet)
    }
    // 計(jì)算每個(gè)商品的訂單價(jià)格(這里可能是查詢商品的最新單價(jià)*數(shù)量) ++++++++++++++ End

    // 計(jì)算訂單金額 +++++++++++++++++++++++++++++++++++++++++++++++++++ Start
    // 創(chuàng)建一個(gè)通道,傳輸訂單金額
    getOrderAmountFuncRet := make(chan int, 10)
    // 判斷配置是否啟用子協(xié)程
    if subGoroutine {
        go getOrderAmount(getOrderProductAmountByIdFuncRet, checkCouponFuncRet, getOrderAmountFuncRet)
    } else {
        getOrderAmount(getOrderProductAmountByIdFuncRet, checkCouponFuncRet, getOrderAmountFuncRet)
    }
    // 計(jì)算訂單金額 +++++++++++++++++++++++++++++++++++++++++++++++++++ Start

    // 遍歷通道數(shù)據(jù),有false就輸出錯(cuò)誤信息
    for v := range checkProductByIdFuncRet {
        if !v {
            fmt.Println("有無(wú)效的商品,請(qǐng)重新下單")
            // 發(fā)送錯(cuò)誤信息后,結(jié)束主goroutine,子goroutine也會(huì)自動(dòng)結(jié)束
            return
        }
    }
    for v := range checkProductNumByIdFuncRet {
        if !v {
            fmt.Println("商品數(shù)量不足,請(qǐng)重新下單")
            // 發(fā)送錯(cuò)誤信息后,結(jié)束主goroutine,子goroutine也會(huì)自動(dòng)結(jié)束
            return
        }
    }

    // 代碼執(zhí)行到這里,說(shuō)明錯(cuò)誤檢查都通過(guò)了,所有需要計(jì)算的業(yè)務(wù)邏輯也都計(jì)算完成了,
    // 該拿著計(jì)算好的數(shù)據(jù)直接寫入數(shù)據(jù)庫(kù)完成訂單創(chuàng)建了,這一步就不需要啟動(dòng)子協(xié)程了
    create(getOrderAmountFuncRet)

    // 程序開始運(yùn)行時(shí)間
    subtract := time.Now().Sub(startDate)
    var subGoroutineMsg string
    if subGoroutine {
        subGoroutineMsg = "已啟用子協(xié)程"
    } else {
        subGoroutineMsg = "未啟用子協(xié)程"
    }
    fmt.Printf("結(jié)束! [%v],程序耗費(fèi)時(shí)間:%v\n", subGoroutineMsg, subtract)
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末算芯,一起剝皮案震驚了整個(gè)濱河市禽捆,隨后出現(xiàn)的幾起案子椭懊,更是在濱河造成了極大的恐慌诗茎,老刑警劉巖泥耀,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妓布,死亡現(xiàn)場(chǎng)離奇詭異叛拷,居然都是意外死亡腾窝,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門盈匾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)腾务,“玉大人,你說(shuō)我怎么就攤上這事削饵⊙沂荩” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵葵孤,是天一觀的道長(zhǎng)担钮。 經(jīng)常有香客問(wèn)我,道長(zhǎng)尤仍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任狭姨,我火速辦了婚禮宰啦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饼拍。我一直安慰自己赡模,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布师抄。 她就那樣靜靜地躺著漓柑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叨吮。 梳的紋絲不亂的頭發(fā)上辆布,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音茶鉴,去河邊找鬼锋玲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涵叮,可吹牛的內(nèi)容都是我干的惭蹂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼割粮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盾碗!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起舀瓢,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤廷雅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榜轿,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幽歼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谬盐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甸私。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖飞傀,靈堂內(nèi)的尸體忽然破棺而出皇型,到底是詐尸還是另有隱情,我是刑警寧澤砸烦,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布弃鸦,位于F島的核電站,受9級(jí)特大地震影響幢痘,放射性物質(zhì)發(fā)生泄漏唬格。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一颜说、第九天 我趴在偏房一處隱蔽的房頂上張望购岗。 院中可真熱鬧,春花似錦门粪、人聲如沸喊积。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)乾吻。三九已至,卻和暖如春拟蜻,著一層夾襖步出監(jiān)牢的瞬間绎签,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工瞭郑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辜御,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓屈张,卻偏偏與公主長(zhǎng)得像擒权,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阁谆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容