問題
問題大概出在18年的雙十二期間替蛉,由于電商運營團隊在做活動的時候熊楼,在幾個活動頁面展示了好幾張?zhí)貏e大的圖片(大概有800k-1M大小的樣子)仅颇, 致使用戶在實際瀏覽過程中育叁,體驗并不是很好坞生。而如果只是普通用戶體驗不好片习,也就罷了懂鸵,偏偏那幾天大BOSS關(guān)注起來了app爬范,于是問起緣由來掸茅,一級一級問過來椅邓,問到了我這里。
我當(dāng)然很明白這里面的問題所在昧狮,說白了就是由于你網(wǎng)站上的媒體資源加載太慢了景馁,直接造成了用戶體驗差。而為什么你網(wǎng)站的媒體資源加載慢呢逗鸣?那是因為你選擇了在自有機房來托管這些媒體資源數(shù)據(jù)合住。而這些自有機房,在網(wǎng)絡(luò)連接速度撒璧、穩(wěn)定性等方面都與專業(yè)的云服務(wù)商沒法比透葛,這就間接導(dǎo)致了問題的產(chǎn)生。
選型
有了問題卿樱,就要有對應(yīng)的策略僚害。我于是首先負責(zé)了云存儲的選型,當(dāng)時面臨的選擇包括七牛云存儲繁调,騰訊云存儲萨蚕,阿里云儲存這幾家。當(dāng)時與老大商量過蹄胰,認為作為一款一多半流量來自于微信平臺的app岳遥,當(dāng)然還是應(yīng)該選擇一款騰訊旗下的云存儲服務(wù)了,他對此表示贊同烤送。但還是應(yīng)該考慮價格因素寒随,經(jīng)過簡單的計算,發(fā)現(xiàn)三家在價格上沒有差多少帮坚。于是選型并不費事,直接就選用了騰訊云存儲互艾。
然而试和,我還需要根據(jù)我的選型,去做一個使用這些新服務(wù)的費用評估纫普。這個費用評估報告阅悍,也會交給上級領(lǐng)導(dǎo)好渠,最后由他們來拍板,是否使用騰訊云儲存服務(wù)來作為媒體資源的提供者节视。
一切順利拳锚,我的選型報告給領(lǐng)導(dǎo)看后,認為這個方案是可行的寻行。一個預(yù)期的結(jié)果是霍掺,通過使用騰訊云存儲方案,能夠使app上的圖片加載速度快將近10倍拌蜘,這樣的好事杆烁,為什么不做?
技術(shù)方案
摸著石頭過河
我自己決定了技術(shù)方案简卧,這個技術(shù)方案的核心是摸著石頭過河兔魂。因為在當(dāng)前的技術(shù)棧中,還從來沒有使用過云存儲举娩,我們誰也不知道上云會有哪些風(fēng)險析校,會產(chǎn)生多少費用,即便是有一些評估铜涉,但畢竟難以保證準(zhǔn)確勺良。
既然是摸著石頭過河,我的第一步是先在app上做一個完全新的功能出來骄噪,這個新功能一定要用上云存儲尚困。這樣做有幾點好處,其一链蕊,數(shù)據(jù)是完全新的事甜,沒有任何歷史數(shù)據(jù),不用額外去做數(shù)據(jù)的遷移工作滔韵。其二逻谦,在做新功能的期間,也能夠熟悉了解云存儲接口和使用陪蜻。
我選中了訂單的評價功能的改進邦马,來作為我使用騰訊云的第一步。由于歷史的原因宴卖,以往的訂單評價功能滋将,只能給出文字評價以及好中差評,并不能帶圖片評價症昏。之前随闽,已經(jīng)有業(yè)務(wù)部門的同事要求,希望能夠?qū)崿F(xiàn)帶圖評價肝谭。于是正好借助這個功能掘宪,練練手蛾扇。事后來看,我這個思路非常好魏滚,這個功能做好后沒多久镀首,app所有的可用資源就都以騰訊云存儲的數(shù)據(jù)方式展現(xiàn)了。
在這期間鼠次,由于后端語言使用的是python
更哄,因此直接參考騰訊云存儲的python sdk文檔
是很快速高效的學(xué)習(xí)方法。通過簡單的測試须眷,可以很方便地對接口進行熟悉竖瘾。
技術(shù)細節(jié)
本地數(shù)據(jù)和遠程數(shù)據(jù)
從一開始我就不想完全放棄本地數(shù)據(jù),也就是說花颗,即便是我將所有的本地數(shù)據(jù)都遷移到了云上捕传。我還是要在本地進行一次備份。數(shù)據(jù)的存儲扩劝,刪除庸论,更新等操作,一定是先在本地完成棒呛,再同步到云上聂示。這個同步的過程,為了快速簇秒,并不是說再執(zhí)行一次遷移數(shù)據(jù)的腳本鱼喉,而是說,將原有的操作趋观,在云上再重新執(zhí)行一遍扛禽。而實際客戶端讀取資源文件時,還是直接拉取云上的資源皱坛,而無須去獲取本地資源编曼。這里要再多說兩句,最初我為了以防萬一剩辟,寫的讀取資源的方法掐场,原理是如果云上沒有這個資源,就去讀取本地資源
贩猎,但是經(jīng)過實際測試熊户,發(fā)現(xiàn)判斷云上是否含有某個資源的方法(get_qcloud_object
)執(zhí)行效率太低了,考慮到我當(dāng)時已經(jīng)將所有的數(shù)據(jù)完全進行了遷移融欧,并且保證本地所有的操作敏弃,也會同步到云上,因此我可以放心地拼接一個可用的云存儲鏈接噪馏,來作為我的圖片資源地址來使用麦到。這樣以來,整個讀取速度就上去了一大截欠肾。當(dāng)然這樣做還有一個好處瓶颠,也就是容災(zāi),我看到前不久刺桃,阿里云的北京機房還出了亂子粹淋,導(dǎo)致很多app處于不可用狀態(tài)。我這個方案瑟慈,當(dāng)然能夠很好地解決這個問題桃移,一旦騰訊云上的資源處于不可訪問的狀態(tài),我也可以快速地進行部署葛碧,然后將資源讀取從騰訊云切換到本地機房服務(wù)器借杰。
說到這里,其實已經(jīng)很明白了进泼。其實我的核心方法蔗衡,只有三個。一個是上傳數(shù)據(jù)的乳绕,用到的sdk里面的方法是client.put_object
這個方法绞惦,另外一個是刪除數(shù)據(jù),用到的sdk里面的方法是client.delete_object
,還有一個則是展示資源的方法洋措,這個方法是完全自己寫的济蝉,其原理也不過是將云上的資源url拼接好。
本地bucket與遠程bucket
在實現(xiàn)騰訊云存儲的時候菠发,有一個bucket
的概念王滤。其實按照我對于bucket
的理解,就是跟他的中文解釋一樣雷酪,是一個籃子
淑仆。這個籃子里面,什么東西都可以往里面裝哥力。在我最初的設(shè)計中蔗怠,我考慮到要創(chuàng)建一個bucket
的時候,想到了直接創(chuàng)建一個名叫comment
的bucket
來存放商品評價圖片吩跋。之所以這么做寞射,也是因為我在本地也是想要創(chuàng)建一個comment
的bucket
,但是后來發(fā)現(xiàn)不能簡單地將本地bucket
與遠程bucket
一一映射锌钮。
這主要也是從兩個方面來考慮桥温。第一是數(shù)據(jù)的遷移,數(shù)據(jù)遷移的時候梁丘,其中一個配置項就是bucket
的名字侵浸,這個時候如果我將本地20幾個bucket
一一遷移旺韭,就會顯得很麻煩,每次都要修改配置文件掏觉,也要上傳好多次区端。所以這個時候我開始考慮將原來的bucket
放到下一級去處理。這有點像是蘇聯(lián)的成立澳腹,原來的共和國本身也是國家织盼,但是我讓他成為蘇聯(lián)的加盟共和國,這樣就能夠方便管理酱塔。同樣的道理沥邻,我這里只要對象存儲上創(chuàng)建一個bucket
,由它來作為‘蘇聯(lián)’羊娃。這樣就能夠讓我的數(shù)據(jù)遷移工作更簡單唐全。
方便的還不只是數(shù)據(jù)遷移,由于在騰訊云的對象存儲中迁沫,每個bucket
需要單獨配置芦瘾。比如,跨域訪問CORS設(shè)置,防盜鏈設(shè)置,回源設(shè)置,是否開啟CDN設(shè)置等集畅,這些操作我們不能通過API來完成近弟,必須要通過工程師手動配置。而如果有多個bucket
的話挺智,那么配置起來就會很麻煩祷愉。
而我最后雖然建立了一個蘇聯(lián),但是也只不過是多寫了兩行代碼赦颇,將原來上傳資源二鳄,刪除資源,展示資源的方法又修改了一下媒怯,重新做了映射订讼。一起都非常簡單,就能夠解決我的難題扇苞。
當(dāng)然欺殿,我們也是需要考慮每個bucket
的最大存儲空間和存儲資源數(shù)量,我后來調(diào)查了一下鳖敷。發(fā)現(xiàn)我的數(shù)據(jù)量脖苏,完全可以將原來的多個bucket
合并成一個,而無需擔(dān)心這個問題定踱。
遷移數(shù)據(jù)
遷移數(shù)據(jù)乍一看是個麻煩事棍潘,我開始有幾點考慮。一是整個bucket
的數(shù)據(jù)量,達到了幾十GB亦歉。當(dāng)時考慮如果在網(wǎng)絡(luò)情況不好的情況下恤浪,一天的時間也未必能夠完成數(shù)據(jù)的全部遷移。有這個擔(dān)憂并不是毫無道理鳍徽,我最先想到的其實是當(dāng)前用迅雷下片的體驗资锰,幾個G的資源都要下載幾個小時敢课,更不要說是幾十GB的大量碎片文件了阶祭。但是后來發(fā)現(xiàn),我本地網(wǎng)速還不錯直秆,上傳的速度夠快濒募,至少是比我想象中要快。我的第二點考慮圾结,雖然數(shù)據(jù)遷移速度很快瑰剃,但是由于在遷移工作開展之前,并沒有對哪些數(shù)據(jù)需要遷移
進行分析筝野,致使有很長一段時間晌姚,我都看著terminal
窗口中在上傳一些日志文件、發(fā)票文件歇竟。而這些文件挥唠,用戶是很少有感知的,其實也并不是我此次工作想要解決的問題焕议。
這個時候宝磨,就看出騰訊云這個數(shù)據(jù)遷移工具的可配置性了。
在本地遷移中盅安,有兩個設(shè)置唤锉,一個是用來設(shè)置本地數(shù)據(jù)遷移的包含目錄,一個則是需要排除的目錄的别瞭。
localPath=/opt/demo/var/buckets
exeludes=/opt/demo/var/buckets/log;/opt/ljmall-public--2/var/buckets/invoice
通過上面的配置窿祥,我就可以將占用大量內(nèi)存空間的日志文件,發(fā)票(主要是pdf)文件排除出去蝙寨,節(jié)省了云上大量的存儲空間晒衩。
由于對配置文件進行了修改,也加上網(wǎng)絡(luò)情況良好籽慢。實際上浸遗,整個數(shù)據(jù)遷移的過程不僅快速而且也很完美。并沒有出現(xiàn)丟失數(shù)據(jù)的情況箱亿,這里需要補充一點跛锌,由于騰訊云存儲數(shù)據(jù)遷移工具是支持日志記錄的,因此所有已上傳成功的文件信息都已經(jīng)記錄在日志中,這樣也避免了重復(fù)上傳等問題髓帽。所以菠赚,雖然我在中途修改了兩次配置文件,但并不影響我最后將我需要上傳的資源快速高效地上傳上去郑藏。
由于在遷移數(shù)據(jù)之前衡查,幾乎所有的圖片資源都是由運營人員產(chǎn)生的,所以我特地選擇了一個運營不上班的周末來做這件事必盖。之后拌牲,只需要在遷移數(shù)據(jù)完成之后,將原來寫的上傳歌粥、刪除塌忽、展示資源的方法啟用,之后所有的數(shù)據(jù)就能夠?qū)崿F(xiàn)同步了失驶。當(dāng)然土居,為了做的更好一點,也是可以每隔一段時間嬉探,定期執(zhí)行遷移腳本擦耀,來保持數(shù)據(jù)的同步的。只是涩堤,從我目前的觀察來看眷蜓,無需定時遷移的腳本已經(jīng)能夠滿足業(yè)務(wù)的需要了。
前端資源的處理和上云
本來我做這個功能的時候定躏,最初其實只是想把圖片等媒體資源進行遷移账磺,這樣能夠確保用戶在訪問媒體資源的時候,不至于因為網(wǎng)絡(luò)情況差痊远,造成打開圖片卡頓垮抗,半天出不來一張完整的圖片。然而后來碧聪,我仔細想這個問題冒版。所謂的對象存儲,理論上來說逞姿,只要是靜態(tài)資源文件辞嗡,就都可以用上這個方案。因此滞造,后來续室,我也將微商城的前端資源放到了云上。
這些前端資源是由webpack生成的一些js,css等文件谒养,也包括由url-loader生成的一些圖片資源挺狰,字體資源等。我使用到了一個開源的前端工具庫來幫我自動完成build之后的上傳操作,這個工具庫是cos-webpack 丰泊。
這里的一切也很簡單薯定,不過這個工具庫,讓我覺得有一點不舒服的是瞳购,他只會負責(zé)將生成的文件上傳到云上话侄,但是卻不會負責(zé)將云上原有的資源刪除掉,這樣一旦新的前端資源上傳上去了学赛,部署了新的前端代碼年堆。那么那些老舊的前端資源就失去了意義,再也不會有他們的用武之地罢屈。我并沒有對這個前端工具庫進行修改嘀韧,直接使用了。當(dāng)然缠捌,這樣的設(shè)計也有好處,因為我們build新的前端資源的時機有可能與我們下一次部署服務(wù)的時機并不相同译蒂,這個時候如果說build之后曼月,就將原有的資源刪除掉,那么就會直接導(dǎo)致線上的某些頁面柔昼,甚至所有頁面處于不可訪問狀態(tài)哑芹。這顯然不是我們希望得到的結(jié)果。
也許有一天捕透,我會對之進行修改聪姿,做一個更好的工具,來處理前端資源乙嘀。
順便多說一句末购,我將前端資源單獨放到了一個叫做fe
的bucket
里面。這樣虎谢,實際上盟榴,我整個項目完成之后,一共是有兩個bucket
婴噩,也能夠很好地應(yīng)付業(yè)務(wù)擎场,邏輯清晰。
智能壓縮
在使用COS來作為前端資源存放地的時候几莽,還發(fā)現(xiàn)迅办,如果不應(yīng)用CDN的話,我沒有辦法做到資源的壓縮章蚣。我們知道站欺,包括js,css等資源,通過gzip壓縮之后,能夠節(jié)約一半以上的體積镊绪。對于chrome等瀏覽器來說匀伏,就意味著傳輸效率快了一倍以上。在沒有應(yīng)用COS之前蝴韭,我們通過本地服務(wù)器上的nginx
來實現(xiàn)gzip够颠。相關(guān)原理,點這里:探索HTTP傳輸中g(shù)zip壓縮的秘密榄鉴。但是如果直接使用上傳到COS上的資源鏈接履磨,正常加載的時候,是不能進行智能壓縮的庆尘。查看文檔才知道剃诅,只有配合了CDN之后,我才能夠?qū)崿F(xiàn)智能壓縮驶忌。但是僅僅啟用了CDN并沒有什么作用矛辕,后來我為此還專門發(fā)了工單提問,后來終于在騰訊云工程師的幫助下解決了付魔。原來聊品,我雖然啟用了CDN,但是我沒有相應(yīng)地將對外展示的資源url進行修改几苍,用戶訪問的其實還是對象存儲北京機房里面的數(shù)據(jù)翻屈。后來,當(dāng)我將url重新拼接妻坝,換成cdn的鏈接后伸眶,智能壓縮才應(yīng)用上。
CDN的設(shè)置
在使用過程中刽宪,由于沒有或?qū)DN設(shè)置得有問題厘贼,導(dǎo)致CDN上的資源加載也出了問題。
需要說明一下的是纠屋,當(dāng)以COS源的方式接入CDN的時候涂臣,在對象存儲上的bucket
是與CDN上的域名
一一對應(yīng)起來的。正式因為有這樣的對應(yīng)關(guān)系售担,我又要說了赁遗,我建立的那個蘇聯(lián)是多么的有必要。只需要一個蘇聯(lián)去跟美國單獨建立外交關(guān)系族铆,而無需各個加盟共和國單獨去建立外交關(guān)系岩四,省了好多事。
我最初設(shè)置了一個IP訪問限頻配置哥攘,當(dāng)時設(shè)置的時候也沒有在意剖煌。后來過了幾天材鹦,發(fā)現(xiàn)有時候,資源的訪問會出現(xiàn)514 錯誤耕姊,經(jīng)過查詢才發(fā)現(xiàn)原來是這個設(shè)置出的問題桶唐,又趕緊把這個設(shè)置放寬到了一個比較大的標(biāo)準(zhǔn)上。
另外茉兰,前面提到的fe
這個bucket
也出了問題尤泽,有跨域的問題,后來我把HTTP Header配置進行了設(shè)置规脸,問題才得以解決坯约。
當(dāng)然,前面提到的智能壓縮莫鸭,在默認情況下也是關(guān)閉的闹丐,需要手動打開。
效果和理解
開始的時候被因,我雖然考慮到了要使用騰訊云的對象存儲(COS)服務(wù)卿拴,也想按照大眾的做法用上CDN,但是坦白講氏身,那個時候我對CDN還沒有一個特別清晰的理解巍棱。我當(dāng)然知道他是內(nèi)容分發(fā)網(wǎng)絡(luò),能夠讓資源分配到各個節(jié)點蛋欣,讓全國各地,甚至全球各地的用戶在獲取資源時如贷,都能夠享受到一個比較快的速度陷虎。我有這樣一個理解,但是還是很難將他與對象存儲之間的關(guān)系說明白杠袱,講透徹尚猿。
后來真正開始使用對象存儲和CDN了。有那么幾天楣富,也通過騰訊云的后臺凿掂,觀測數(shù)據(jù)的變化,漸漸得也觀察出來點門道纹蝴。且聽我一一道來庄萎。
我最初只用了對象存儲,因此資源的訪問地址就是
bucketname-appid.cos.region-code.myqcloud.com/key
如果上面的參照不好理解塘安,可以舉個例子糠涛,比如:
test-125777777.cos.ap-beijing.myqcloud.com/test.jpg
由于我訪問資源的時候,已經(jīng)指定了資源是在ap-beijing這個機房兼犯,因此全國各地的用戶訪問app進而訪問到這個資源忍捡,都會從騰訊云北京機房獲取這個資源集漾。需要說明的是,我們的本地服務(wù)器的所在地也是在北京砸脊,但是由于我們本地服務(wù)器在網(wǎng)絡(luò)延遲具篇,響應(yīng)速度方面還是與騰訊云有較大差距。從實際體驗來看凌埂,只使用對象存儲而不使用CDN已經(jīng)對app中資源的加載速度有很大的改善了驱显。(實際經(jīng)過統(tǒng)計發(fā)現(xiàn),有60%的流量是來自于北京侨舆,外地的流量不足一半)
之所以秒紧,我們還要使用CDN,一方面當(dāng)然是希望能夠越快越好挨下,盡善盡美熔恢,這也是我作為一個開發(fā)者的追求。由于我身處北京臭笆,很難去測試外地用戶在直接訪問源站的時候的速度叙淌,但是我卻能夠通過CDN 的后臺看到,全國各地愁铺,訪問資源鹰霍,普遍的延時在200ms以內(nèi),這對我來說茵乱,是可以接受的茂洒。另外一方面,其實從整體費用上來看瓶竭,使用CDN并沒有增加多少費用督勺,沒有使用CDN之前,費用的大部分來自于COS外網(wǎng)下行流量費斤贰,應(yīng)用上CDN之后智哀,由于用戶可以訪問分布在全國各地的各個節(jié)點來獲取資源,因此對于COS來說荧恍,它的外網(wǎng)下行流量沒有了瓷叫。取而代之的是CDN流量,以及CDN回源流量送巡。這里有必要再解釋一下CDN回源流量摹菠,我最初還沒有應(yīng)用上騰訊云的時候,也不是很能理解CDN回源流量授艰,當(dāng)時在做費用評估的時候辨嗽,將外網(wǎng)下行流量=CDN回源流量來計算。
實際上淮腾,按照我的理解糟需,應(yīng)該有這樣的公式屉佳。在未使用CDN之前,
總流量≈扪骸= 外網(wǎng)下行流量
在使用CDN之后武花,
總流量 = CDN流量 + CDN回源流量
當(dāng)然我這里必須有一個前提杈帐,那就是COS(對象存儲)的外部讀取完全基于CDN体箕。
通過上面兩個公式,我們可以得出一個簡單的結(jié)論挑童,由于使用了CDN累铅,外網(wǎng)下行流量沒有了,由于業(yè)務(wù)的情況站叼,總流量約等于CDN流量娃兽。
既然總流量約等于CDN流量,那么這個性價比就可以說非常高了尽楔。
后話
完成這個工作之后投储,被運營人員夸獎,說是自己家的app已經(jīng)趕上天貓的速度了阔馋,聽到這個話玛荞,還是很開心的,也不枉我這些天的努力呕寝。