大話后端開發(fā)的奇淫技巧大集合

Hi,大家好韩容,很榮幸有這個機會可以通過寫博文的方式,把這些年在后端開發(fā)過程中總結(jié)沉淀下來的經(jīng)驗和設(shè)計思路分享出來

模塊化設(shè)計

根據(jù)業(yè)務(wù)場景唐瀑,將業(yè)務(wù)抽離成獨立模塊群凶,對外通過接口提供服務(wù),減少系統(tǒng)復(fù)雜度和耦合度哄辣,實現(xiàn)可復(fù)用请梢,易維護,易拓展

項目中實踐例子:

Before:

在返還購APP里有個【我的紅包】的功能力穗,用戶的紅包數(shù)據(jù)來自多個業(yè)務(wù)毅弧,如:邀請新用戶注冊領(lǐng)取100元紅包,大促活動雙倍紅包当窗,等各種活動紅包够坐,多個活動業(yè)務(wù)都實現(xiàn)了一套不同規(guī)則的紅包領(lǐng)取和紅包獎勵發(fā)放的機制,導(dǎo)致紅包不可管理崖面,不能復(fù)用元咙,難維護難拓展

After:

  • 重構(gòu)紅包業(yè)務(wù)
  • 紅包可后臺管理
    • 紅包信息管理,可添加嘶朱,可編輯蛾坯,可配置紅包使用的規(guī)則,可管理用戶紅包
  • 紅包獎勵發(fā)放統(tǒng)一處理
  • 應(yīng)用業(yè)務(wù)的接入只需要專注給用戶進行紅包發(fā)放即可

設(shè)計概要

紅包模塊

Before VS After

Before VS After

產(chǎn)品有時提出的業(yè)務(wù)需求沒有往這方面去考慮疏遏,結(jié)合場景和未來拓展需要脉课,在需求討論的時候提出模塊化設(shè)計方案,并可以協(xié)助產(chǎn)品進行設(shè)計


通用服務(wù)抽離

在項目開發(fā)中經(jīng)常會遇到些類似的功能财异,但是不同的開發(fā)人員都各自實現(xiàn)倘零,或者因為不能復(fù)用又重新開發(fā)一個,導(dǎo)致了類似功能的重復(fù)開發(fā)戳寸,所以我們需要對能夠抽離獨立服務(wù)的功能進行抽離呈驶,達到復(fù)用的效果,并且可以不斷拓展完善疫鹊,節(jié)約了后續(xù)開發(fā)成本袖瞻,提高開發(fā)效率司致,易于維護和拓展

項目中實踐例子:

Before

在業(yè)務(wù)中經(jīng)常需要對用戶進行信息通知,如:短信定時通知聋迎,APP消息推送脂矫,微信通知,等

開發(fā)人員在接到需求中有通知功能的時候沒有考慮后續(xù)拓展霉晕,就接入第三方信息通知平臺庭再,然后簡單封裝個信息通知方法,后續(xù)也有類似信息通知需求的時候牺堰,另一個開發(fā)人員發(fā)現(xiàn)當前這個通知方法無法滿足自己的需求拄轻,然后又自己去了解第三方平臺重新封裝了通知方法,或者后續(xù)需求加了定時通知的功能伟葫,開發(fā)人員針對業(yè)務(wù)去實現(xiàn)了個定時通知功能恨搓,但是只能自己業(yè)務(wù)上使用,其他業(yè)務(wù)無法接入扒俯,沒有人去做這塊功能的抽離奶卓,久而久之就演變成功能重復(fù)開發(fā)一疯,且不易于維護和拓展

After

接觸到這種可以抽離通用服務(wù)需求的時候撼玄,就會與產(chǎn)品確認這種需求是否后續(xù)會存在類似的需要,然后建議這把塊需求抽離成通用服務(wù)墩邀,方便后續(xù)維護和拓展

設(shè)計概要

通用服務(wù)

Before VS After

通用服務(wù)

架構(gòu)獨立服務(wù)

項目開發(fā)過程中有些需求是與所在項目業(yè)務(wù)無關(guān)掌猛,如:收集用戶行為習慣,收集商品曝光點擊眉睹,數(shù)據(jù)收集提供給BI進行統(tǒng)計報表輸出荔茬,公用拉新促活業(yè)務(wù)(柚子街和返還公用),類似這種需求竹海,我們結(jié)合應(yīng)用場景慕蔚,考慮服務(wù)的獨立性,以及未來的拓展需要斋配,架構(gòu)獨立項目進行維護孔飒,在服務(wù)器上獨立分布式部署不影響現(xiàn)有主業(yè)務(wù)服務(wù)器資源

項目中實踐例子:

架構(gòu)用戶行為跟蹤獨立服務(wù),在開發(fā)前預(yù)估了下這個服務(wù)的請求量艰争,并會有相對大量的并發(fā)請求

架構(gòu)方案:

  • 項目搭建選擇用nodejs來做服務(wù)端
    • 單進程坏瞄,基于事件驅(qū)動和無阻塞I/O,所以非常適合處理并發(fā)請求
    • 負載均衡:cluster模塊/PM2
  • 架構(gòu)nodejs獨立服務(wù)
  • 提供服務(wù)接口給客戶端
  • 接口不直接DB操作甩卓,保證并發(fā)下的穩(wěn)定性
  • 數(shù)據(jù)異步入庫
    • 通過程序把數(shù)據(jù)從:消息隊列=>mysql
  • nodejs+express+redis(list)/mq+mysql

用戶行為跟蹤服務(wù)的服務(wù)架構(gòu)圖

獨立服務(wù)

高并發(fā)優(yōu)化

高并發(fā)除了需要對服務(wù)器進行垂直擴展和水平擴展之外鸠匀,作為后端開發(fā)可以通過高并發(fā)優(yōu)化,保證業(yè)務(wù)在高并發(fā)的時候能夠穩(wěn)定的運行逾柿,避免業(yè)務(wù)停滯帶來的損失缀棍,給用戶帶來不好的體驗

緩存:

  • 服務(wù)端緩存
    • 內(nèi)存數(shù)據(jù)庫
      • redis
      • memcache
    • 方式
      • 優(yōu)先緩存
        • 穿透DB問題
      • 只讀緩存
        • 更新/失效刪除
    • 注意
      • 內(nèi)存數(shù)據(jù)庫的分配的內(nèi)存容量有限宅此,合理規(guī)劃使用,濫用最終會導(dǎo)致內(nèi)存空間不足
      • 緩存數(shù)據(jù)需要設(shè)置過期時間爬范,無效/不使用的數(shù)據(jù)自動過期
      • 壓縮數(shù)據(jù)緩存數(shù)據(jù)诽凌,不使用字段不添加到緩存中
      • 根據(jù)業(yè)務(wù)拆分布式部署緩存服務(wù)器
  • 客戶端緩存
    • 方式
      • 客戶端請求數(shù)據(jù)接口,緩存數(shù)據(jù)和數(shù)據(jù)版本號坦敌,并且每次請求帶上緩存的數(shù)據(jù)版本號
      • 服務(wù)端根據(jù)上報的數(shù)據(jù)版本號與數(shù)據(jù)當前版本號對比
      • 版本號一樣不返回數(shù)據(jù)列表侣诵,版本號不一樣返回最新數(shù)據(jù)和最新版本號
    • 場景:
      • 更新頻率不高的數(shù)據(jù)

服務(wù)端緩存架構(gòu)圖

緩存

異步

異步編程

  • 方式:
    • 多線程編程
    • nodejs異步編程
  • 場景:
    • 參與活動成功后進行短信通知
    • 非主業(yè)務(wù)邏輯流程需要的操作,允許異步處理其他輔助業(yè)務(wù)狱窘,等

業(yè)務(wù)異步處理

  • 方式
    • 業(yè)務(wù)接口將客戶端上報的數(shù)據(jù)PUSH到消息隊列(MQ中間件)杜顺,然后就響應(yīng)結(jié)果給用戶
    • 編寫?yīng)毩⒊绦蛉ビ嗛喯㈥犃校惒教幚順I(yè)務(wù)
  • 場景:
    • 大促活動整點搶限量紅包
      • 參與成功后委婉提示:預(yù)計X天后進行紅包發(fā)放
    • 并發(fā)量比較大的業(yè)務(wù)蘸炸,且沒有其他更好的優(yōu)化方案躬络,業(yè)務(wù)允許異步處理
  • 注意:
    • 把控隊列消耗的進度
    • 保證冪等性和數(shù)據(jù)最終一致性
  • 缺陷:
    • 犧牲用戶體驗

【業(yè)務(wù)異步處理】架構(gòu)圖

通用服務(wù)

【業(yè)務(wù)異步處理】除了可以在高并發(fā)業(yè)務(wù)中使用,在上面通用服務(wù)的設(shè)計里也是用這種架構(gòu)方式


限流

在類秒殺的活動中通過限制請求量搭儒,可以避免超賣穷当,超領(lǐng)等問題
高并發(fā)的活動業(yè)務(wù),通過前端控流淹禾,分散請求馁菜,減少并發(fā)量

  • 服務(wù)端限流
    • redis 計數(shù)器
    • 如:類秒殺活動
  • 客戶端控流
    • 通過參與活動游戲的方式
    • 紅包雨/小游戲,等方式

服務(wù)降級

當服務(wù)器資源消耗已經(jīng)達到一定的級別的時候铃岔,為了保證核心業(yè)務(wù)正常運行汪疮,需要丟卒保車,棄車保帥毁习,服務(wù)降級是最后的手段智嚷,避免服務(wù)器宕機導(dǎo)致業(yè)務(wù)停滯帶來的損失,以及給用戶帶來不好的體驗

  • 業(yè)務(wù)降級
    • 從復(fù)雜服務(wù)纺且,變成簡單服務(wù)
    • 從動態(tài)交互盏道,變成靜態(tài)頁面
  • 分流到CDN
    • 從CDN拉取提前備好的JSON數(shù)據(jù)
    • 引導(dǎo)到CDN靜態(tài)頁面
  • 停止服務(wù)
    • 停止非核心業(yè)務(wù),并進行委婉提示

高并發(fā)優(yōu)化概要圖

高并發(fā)

防刷/防羊毛黨

大多數(shù)公司的產(chǎn)品設(shè)計和程序猿對于推廣活動業(yè)務(wù)的防刷意識不強载碌,在活動業(yè)務(wù)設(shè)計和開發(fā)的過程中沒有把防刷的功能加入業(yè)務(wù)中猜嘱,給那些喜歡刷活動的人創(chuàng)造了很多的空子
等到你發(fā)現(xiàn)自己被刷的時候,已經(jīng)產(chǎn)生了不小的損失恐仑,少則幾百幾千泉坐,多則幾萬

隨著利益的誘惑,現(xiàn)在已經(jīng)浮現(xiàn)了一個新的職業(yè)“刷客”裳仆,專業(yè)刷互聯(lián)網(wǎng)活動為生腕让,養(yǎng)了N臺手機+N個手機號碼+N個微信賬號,刷到的獎勵金進行提現(xiàn),刷到活動商品進行低價轉(zhuǎn)手處理纯丸,開辟了一條新的灰色產(chǎn)業(yè)鏈

我們要拿起武器(代碼)進行自我的防御偏形,風控,加高門檻觉鼻,通過校驗和限制減少風險發(fā)生的各種可能性俊扭,減少風險發(fā)生時造成的損失

這里列出常用套路(具體應(yīng)用結(jié)合業(yè)務(wù)場景):

校驗請求合法性

  • 請求參數(shù)合法性判斷
  • 請求頭校驗
    • user-agent
    • referer
    • ... ...
  • 簽名校驗
    • 對請求參數(shù)進行簽名
  • 設(shè)備限制
  • IP限制
  • 微信unionid/openid合法性判斷
  • 驗證碼/手機短信驗證碼
    • 犧牲體驗
  • 自建黑名單系統(tǒng)過濾

業(yè)務(wù)風控

  • 限制設(shè)備/微信參與次數(shù)
  • 限制最多獎勵次數(shù)
  • 獎池限制
  • 根據(jù)具體業(yè)務(wù)場景設(shè)計... ...

應(yīng)對角色

  • 普通用戶
  • 技術(shù)用戶
  • 專業(yè)刷客
    • 目前還沒有很好的限制方式

防刷/防羊毛黨套路概要圖

防刷套路

附加

  • APP/H5中簽名規(guī)則應(yīng)該由客戶端童鞋開發(fā),然后拓展API給前端JS調(diào)用坠陈,在H5發(fā)起接口請求的時候調(diào)用客戶端拓展的簽名萨惑,這樣可以避免前端JS里構(gòu)造簽名規(guī)則而被發(fā)現(xiàn)破解

并發(fā)問題

多操作

  • 場景:

當==同用戶==多次觸發(fā)點擊,或者通過模擬并發(fā)請求仇矾,就會出現(xiàn)多操作的問題庸蔼,比如:簽到功能,一天只能簽到一次贮匕,可以獲得1積分姐仅,但是并發(fā)的情況下會出現(xiàn)用戶可以獲得多積分的問題

  • 剖析:

簡化簽到邏輯一般是這樣的:

查詢是否有簽到記錄 --> 否 --> 添加今日簽到記錄 --> 累加用戶積分 --> 簽到成功
查詢是否有簽到記錄 --> 是 --> 今日已經(jīng)簽到過

假設(shè)這個時候用戶A并發(fā)兩個簽到請求,這時會同時進入到 【查詢是否有簽到記錄】刻盐,然后同時返回否掏膏,就會添加兩條的簽到記錄,并且多累加積分

  • 解決方案:

最理想簡單的方案敦锌,只需要在簽到記錄表添加【簽到日期】+【用戶ID】的組合唯一索引馒疹,當并發(fā)的時候只有會一條可以添加成功,其他添加操作會因為唯一約束而失敗

庫存負數(shù)

  • 場景:

當==多用戶==并發(fā)點擊參與活動供屉,如:抽獎活動行冰,這個時候獎品只有一個庫存了,理論上只有一個用戶可以獲得伶丐,但是并發(fā)的時候往往會出現(xiàn)他們都成功獲得獎品,導(dǎo)致獎品多支出疯特,加大了活動成本

  • 剖析:

有問題的邏輯流程一般是這樣的:

中獎 --> 查詢獎品庫存 --> 有 --> 更新獎品庫存 --> 添加中獎紀錄 --> 告知中獎
中獎 --> 查詢獎品庫存 --> 無 --> 告知無中獎

假設(shè)抽獎活動哗魂,當前獎品A只有最后一個庫存,然后用戶A漓雅、B录别、C,同時參與活動同時中獎獎品都是A邻吞,這個時候查詢商品庫存是存在1個组题,就會進行更新庫存,添加中獎紀錄抱冷,然后就同時中獎了

  • 解決方案:

最理想根本就不需要用多做一個庫存的SELECT獎品庫存操作崔列,只需要UPDATE 獎品庫存-1 WHERE 獎品庫存>=1,UPDATE成功后就說明是有庫存的,然后再做后續(xù)操作赵讯,并發(fā)的時候只會有一個用戶UPDATE成功

總結(jié):

在開發(fā)業(yè)務(wù)接口的時候需要把==同用戶==和==多用戶==并發(fā)的場景考慮進去盈咳,這樣就可以避免在并發(fā)的時候產(chǎn)生數(shù)據(jù)異常問題,導(dǎo)致成本多支出
可以使用下面的工具進行模擬并發(fā)測試:

  • Apache JMeter
  • Charles Advanced Repeat
  • Visual Studio 性能負載

數(shù)據(jù)采集技巧(番外)

普遍方案

  • 獲取平臺數(shù)據(jù)接口
  • 模擬接口請求
  • 數(shù)據(jù)解析過濾
  • 數(shù)據(jù)構(gòu)造入庫

使用selenium+Headless自動化測試框架

  • 開發(fā)
    • 推薦用python開發(fā)
      • python+selenium+headless
    • 控制請求頻率边翼,避免被平臺限制請求
    • 使用代理IP鱼响,繞過請求IP限制
  • 優(yōu)點
    • 無需模擬接口請求
      • 無法攻克數(shù)據(jù)接口模擬請求(加密簽名等)
      • 接口版本頻繁變化(需要重新調(diào)研)
    • 平臺接口/頁面版本變化,可以快速調(diào)整
      • 只需要調(diào)整采集數(shù)據(jù)所在的HTML元素的位置(class/id)
    • 可以用戶操作/選中/點擊/模擬登陸组底,等
      • 登陸失效后可以模擬登陸
      • 可以發(fā)送登陸二維碼到釘釘進行掃碼登錄
  • 應(yīng)用場景:
    • 競品數(shù)據(jù)采集
    • 淘寶商品價格和自建商品庫后臺價格監(jiān)控
    • 淘寶領(lǐng)券金額和自建商品庫后臺券金額監(jiān)控
    • ... ...

反反爬蟲

在做數(shù)據(jù)采集的過程中丈积,有些平臺會對重要數(shù)據(jù)的請求設(shè)置反爬蟲策略,避免數(shù)據(jù)被競品挖掘和利用债鸡,以及消耗大量資源拖垮服務(wù)器桶癣,
反爬蟲和反反爬蟲是技術(shù)之間的較量,這場沒有硝煙的戰(zhàn)爭永不停息娘锁。(程序員何必為難程序員)

  • 反爬蟲可以分為以下兩種
    • 服務(wù)端限制
      • 服務(wù)器端行請求限制牙寞,防止爬蟲進行數(shù)據(jù)請求
    • 前端限制
      • 前端通過CSS和HTML標簽進行干擾混淆關(guān)鍵數(shù)據(jù),防止爬蟲輕易獲取數(shù)據(jù)
  • 破解服務(wù)端限制:
    • 模擬設(shè)置請求頭
      • Referer
      • User-Agent
      • Authorization
      • .....
    • 破解簽名
      • 簽名規(guī)則
        • 在JS中找到簽名規(guī)則
    • 控制請求平率
      • 調(diào)整請求時間莫秆,延遲請求
    • 代理IP
      • 切換請求的代理IP间雀,自建/第三方
    • 登錄限制
      • 帶上登錄成功后的Cookie/Authorization
    • 驗證碼限制
      • 識圖,基于庫/第三方
    • 投毒破解
      • 為了防止被投毒镊屎,需要對數(shù)據(jù)進行抽樣校驗
  • 破解前端限制:
    • font-face惹挟,自定義字體干擾
      • 找到ttf字體文件地址,然后下載下來缝驳,使用font解析模塊包對ttf文件進行解析连锯,與文字編碼進行映射出中文
    • 偽元素隱藏式
      • 在CSS里找到xxxx::before {content: "中文";}對應(yīng)的中文
    • backgroud-image移量
      • 通過背景圖片的position位置偏移量和圖片中的內(nèi)容進行映射
    • html標簽干擾
      • 過濾掉干擾混淆的HTML標簽,或者只讀取有效數(shù)據(jù)的HTML標簽的內(nèi)容

總結(jié)

作為后端開發(fā)者用狱,不僅是完成需求功能開發(fā)运怖,要結(jié)合業(yè)務(wù)場景進行合理設(shè)計,架構(gòu)未來夏伊,對核心業(yè)務(wù)進行壓測優(yōu)化摇展,以保證業(yè)務(wù)在并發(fā)下能夠正常運行,同時要考慮安全問題以及防刷溺忧,防羊毛黨咏连,在編碼上避免壞代碼味道,面相抽象開發(fā)鲁森,適當使用設(shè)計模式祟滴,避免技術(shù)債

開發(fā)應(yīng)該銘記于心的精句

  • 技術(shù)的存在價值,是讓技術(shù)推動業(yè)務(wù)增長歌溉,實現(xiàn)公司盈利增長
  • 沒有最好的架構(gòu)只有最適合的架構(gòu)
  • 開發(fā)語言只是工具垄懂,在適合的場景中使用適合的工具
  • 抽象思維是從具體存在的各不相同的問題當中洞察問題的本質(zhì),理解產(chǎn)品需求的深層次模型,治本而不是治標
  • 知識很重要埠偿,她雖然不能直接給你財富透罢,但是可以給你很多機會,活到老學(xué)到老

aixin

歡迎star:《大話WEB開發(fā)》 Github

閱讀原文地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末冠蒋,一起剝皮案震驚了整個濱河市羽圃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抖剿,老刑警劉巖朽寞,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異斩郎,居然都是意外死亡脑融,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颓帝,你說我怎么就攤上這事〖瞬迹” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵宋梧,是天一觀的道長匣沼。 經(jīng)常有香客問我,道長捂龄,這世上最難降的妖魔是什么释涛? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮倦沧,結(jié)果婚禮上唇撬,老公的妹妹穿的比我還像新娘。我一直安慰自己刀脏,他們只是感情好局荚,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愈污,像睡著了一般。 火紅的嫁衣襯著肌膚如雪轮傍。 梳的紋絲不亂的頭發(fā)上暂雹,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音创夜,去河邊找鬼杭跪。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的涧尿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼姑廉,長吁一口氣:“原來是場噩夢啊……” “哼缺亮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起桥言,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤萌踱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后号阿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體并鸵,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年扔涧,在試婚紗的時候發(fā)現(xiàn)自己被綠了园担。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡枯夜,死狀恐怖弯汰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卤档,我是刑警寧澤蝙泼,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站劝枣,受9級特大地震影響汤踏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舔腾,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一溪胶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧稳诚,春花似錦哗脖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至氨距,卻和暖如春桑逝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背俏让。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工楞遏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留茬暇,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓寡喝,卻偏偏與公主長得像糙俗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子预鬓,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345