iNDIEVOX 是目前台灣最大的獨立音樂網路商店,主要商業模式是販售高音質數位音樂下載及演唱會售票,除了提供給獨立音樂藝人及音樂廠牌一個完整的數位銷售平台之外,也讓喜歡獨立音樂的大眾可以便利地購買到喜歡的音樂及門票。
我們將在這個議題分享 iNDIEVOX 如何在踏入演唱會售票領域之後開始快速蛻變,慢慢將一個無法 Scale 的 PHP 網站系統改造成如今可以 Scale Up / Scale Out 以承受線上同時上萬人搶票的 PHP 網站系統。
1. 感謝各位的參與,我將在這個議題分享 iNDIEVOX 如何在踏入演唱會售票領域之後開始快速蛻變,慢慢將一個無法 Scale 的 PHP 網站系統改造成如今可以很容易地 Scale Up 以及 Scale Out,目前我們已經是一個可以承受線上同時上萬人搶票的 PHP 網站系統。
2. 好,那跟各位介紹一下我自己,我是林志傑,網路上常用的名字是 Fukuball,所以各位可以用 Fukuball 這個關鍵字找到我。我目前在 iNDIEVOX 獨立音樂網工作,而今天的主題也就是我在 iNDIEVOX 工作的相關經驗分享,相信對同性質或是一些新創網站都會有些幫助。
3. 那麼 iNDIEVOX 究竟是什麼呢?
4. iNDIEVOX 這個網站主要專注於台灣獨立音樂相關的電子商務服務,獨立音樂人可以在這邊上傳他們的作品、或販售演唱會票券,樂迷就可以在這邊直接購買到創作者的數位音樂及演唱會票券,我們希望可以透過這個平台,讓台灣的獨立音樂生態可以像國外的獨立音樂那樣完善。所以大家有空也可以來 iNDIEVOX 上面逛逛、聽聽音樂~
5. 很多人會問,要讓台灣的獨立音樂跟歐美日一樣蓬勃,iNDIEVOX 只做數位音樂及演唱會門票販售這樣夠嗎?
6. 其實,當然不夠。所以我們公司對整個獨立音樂生態系統的完整藍圖是這樣的。iNDIEVOX 目前屬於中子文化集團的一員,我們集團底下還有 StreetVoice、Packer、Blow 等等服務。StreetVoice 主要專注於獨立音樂社群,還未成熟的獨立音樂人可以在這邊發表作品、互相交流,大家可以把 StreetVoice 想成是華人世界的 SoundCloud。而當獨立音樂人的作品更成熟了,我們有 Packer 這個服務來將音樂人的作品發佈到各種線上音樂服務比如 iTunes、Spotify、iNDIEVOX 以及 kkbox、甚至中國的蝦米、百度。而我們公司也會提供各種線下的資源,比如音樂節的演出機會,像是簡單生活節、音樂花房以及見證大團,讓這些獨立藝人可以用線下的方式與歌迷們互動。最後,我們還自創了音樂品牌媒體吹音樂,致力於宣傳獨立音樂相關新聞,而我們目前也是 Yahoo! 音樂及蘋果酷音樂的合作夥伴,在音樂線上媒體這個部分非常有影響力,也因此可以將好的獨立音樂推銷出去。
7. 所以 iNDIEVOX 只是完整獨立音樂生態系統中的一小塊,大家可以把我們網站想成是一個獨立音樂的電子商務平台,所以其他部分我們需要跟其他服務一起合作,這樣我們才可以專注於做好獨立音樂的電子商務。
8. 當然,我們今天只談售票這一塊
9. 給大家看一下 iNDIEVOX 售票系統的現況,我們是從 2011 年開始售票服務的,左邊我列出了這幾年幾個比較重大的熱門活動,紅色標起來的活動是 iNDIEVOX 系統架構有大改變的事件,等一下我就會詳細說明我們做了什麼改變。
10. 在進入正題之前,再次跟大家提醒一下,這個講題並沒有什麼「新的」技術,所以如果各位期待聽到什麼高科技的玩意,可能沒有,這只是我的開發血淚史,以及一些可供參考的經驗。
11. 好,那故事開始了。很久很久以前,那時 iNDIEVOX 還在老闆家的頂樓,我們接到了 iNDIEVOX 史上第一個熱門售票活動
12. 這個事件,iNDIEVOX 內部稱之為「2011 年蘇打粉之亂」!
13. 讓我們看一下當時 iNDIEVOX 售票系統的網站架構。在硬體上,我們只有一台 Server 硬幹,上面裝了 web server 以及 database server。在軟體上,我們沒有使用 Framework,所以原始碼非常亂,很類似大家在第一本 PHP 書上看到的那樣,架構沒有使用 MVC,也沒有 ORM,所以很難維護。
14. 所謂的到處都是 DB Connection 就是這樣,在 Script A 及 Script B 都有可能會有人自己開一個連線來 access database,這在新手剛學 PHP 或沒有使用 Framework 時會常常看到。
15. 所以,當然,我們一開賣就被蘇打粉攻破了.....
16. 經過這次事件之後,我們開始因應蘇打粉之亂來調整網站架構。我們添購了兩台 Server,加上原本的機器,我們就可以有一台機器當 Web Server、一台當 DB Server,然後一台當 Storage Server。所以在硬體上我們可以做到某種程度的分散負載量。在軟體上,由於當時還沒有一個像 Laravel 這樣好用的 Framework,所以我當時就參考了 Rails 來自行研發適合 iNDIEVOX 使用的 Framework,終於把我們的系統改成 MVC 的架構並支援 ORM。在 DB 的連線上也改成使用 Singleton Pattern,不僅增加效能,原始碼的維護也變得容易許多。
17. 使用 Singleton Pattern 來做資料庫連線,大致上的程式架構像這樣。當我們要使用資料庫連線的時候,都是透過 getDBInstance 這個 static method 來取得,這個 method 就會檢查目前是不是已經有資料庫連線存在,如果存在就回傳,如果不存在就進行連線再回傳。這樣大家就可以用統一的方法連進行資料庫連線,在程式上就會比較好維護,然後也不會開太多連線,可以節省資源增加效能。
18. 經過這次的調整之後,我們很辛苦的支撐了一陣子,直到這個事件的發生
19. iNDIEVOX 內部稱之為「2013 年伍粉條之亂」!
20. 其實在經過「2011 年蘇打粉之亂」的調整之後,我心中一直對未來的系統架構有這樣的藍圖,在硬體上,我希望 Web Server 及 DB Server 都可以很容易擴展,只要買了新機器,裝好環境,就可以很容易的加進去整個系統中,增加負載量,然後 Storage Server 的空間可以無上限。在軟體上,Web Appliction 可以很容易複製到各個 Web Server 裡,並且程式碼可以支援資料庫的讀寫分離。整個系統的藍圖就像我右邊畫的這個圖。(稍微說明一下圖)但其實小公司無法一次到位,我們沒辦法一次花個幾百萬買機器來組出這樣的架構,只能一年一年來增加設備,所以這個藍圖一直是個夢。
21. 後來,我看到了 AWS,而且在 2013 年時 AWS 已經相對算是非常成熟的雲端服務,在我自己研究了一陣子之後,就開始說服老闆將所有的系統都搬上 AWS,這樣才有可能可以成功度過伍佰的演唱會搶票。
22. 但要搬上 AWS 還要經過一些前置作業,基本上一定會遇到這三個問題。首先是檔案儲存的問題。原本檔案儲存我們可能都是用 Storage Server 來存放檔案,但如果想要建制一個分散式的系統,在 AWS 上面還是要使用 S3 會是比較好的解決方案。
23. 這段時間我大量修改程式碼,然後將 1T 的檔案搬到了 S3,由於實體機器也還在運作中,所以當時的資料庫是分開的,要特別注意資料庫的同步以及檔案的同步。做完這樣的調整之後,我發現整個系統已經很接近我夢想中的樣子(稍微說明一下圖)。
24. 搬上 AWS 之後,其實就是讓我們從原本使用實體機器時要時時注意的 Performance 改換成注意 Scalability。Performance 就像是有一個很好的小便斗,Scalability 就像是有很多個小便斗一樣!在 AWS 上面可以很容易地做到 Scalability,而 Scalability 又包含 Scale Up 以及 Scale Out。
25. 首先我們來看看 AWS 如何做到 Scale Up。Scale Up 的意思就像右邊的這兩個圖,小便斗由小變大,就是一種 Scale Up。所以 Server 可以很容易的由小變大,就是 Scale Up。
其中 EC2 可以透過 AMI 的方式來做到 Scale Up。AMI 有點像是映像檔,我們可以把運作中的系統包成 AMI,然後就可以用這個 AMI 來開比較大的 EC2 Instance,這樣就做到了 EC2 的 Scale Up。
RDS 則是可以很容易的使用 Modify 的方式,做到 Scale Up,即使不會寫程式,也可以用滑鼠點幾下就完成!
ElastiCache 可以用另開一個比較大的 Instance Node 來做到 Scale Up。
26. 接下來看一下 AWS 如何做到 Scale Out,Scale Out 的意思就像右邊的這兩個圖,小便斗由一個變成多個,就是一種 Scale Out。所以 Server 可以很容易的由一個變成多個,就是 Scale Out。
在 EC2 這邊可以透過 AMI 多開了 EC2 Instance 之後,把這些 Instance 加入 Load Balance 就可以做到 Scale Out 了。
RDS 可以透過 Replication 的方式做到 Scale Out,同樣只要點點滑鼠就可以做到。但是這需要原本撰寫的 PHP Code 也有支援資料庫讀寫分離,才可以真的發揮作用。
ElastiCache 要做到 Scale Out 也很簡單,是就多開一個 Node 就可以了。
27. 我原本以為這樣的調整就可以萬無一失了
28. 沒想到,居然還是被伍粉條攻破了........
29. 檢討這次被伍粉條攻破的原因,其中一點就是我們使用了 AWS 的 Auto Scaling。Auto Scaling 的機制可以設定條件來自動增加 EC2,但是這個增加 EC2 的動作趕不及人潮聚集的速度。EC2 掛到之後,資源無法釋放,拖累 RDS,所以後來資料庫也掛掉。我那時還來不及撰寫資料庫讀寫分離的 PHP Code,所以 RDS 也沒有使用 Replication Scale Out。所以改善的方式就是,將 iNDIEVOX 網站的程式改寫成可以支援讀寫分離,然後熱門活動售票前要事先做好 Scale Up 及 Scale Out,至於要 Scale 到什麼程度,就需要事先人工評估。
30. 這是支援讀寫分離資料庫的程式架構,基本上所有資料庫的存取最終都會來到 DBAccess 這個 class,然後這邊會知道哪些資料庫指令是讀、哪寫指令是寫,它會自動幫我們切換到可以讀或寫的資料庫。大致上會有這幾個重要的 method(稍微說明一下程式碼)
31. 因應伍粉條之亂做完網站架構的調整,主要就是多了資料庫讀寫分離的支援(稍微說明一下圖)。那時 Laravel 還沒有支援資料庫的讀寫分離,所以覺得自己還蠻屌的!
32. 一個月過去,我們馬上又有了一個新的挑戰
33. 這個事件 iNDIEVOX 內部稱之為「2013 年田馥甄的洗禮」!
34. 在田馥甄售票開賣之前,我大量的將資料加上快取。比如單一項目或不會變動的資料像是歌曲、活動、專輯等等資料一律永久快取。然後只要有資料上的更新,也會同時更新快取。所以快取上永遠會是最新的資料,雖然寫入的速度可能會變慢,但讀取的速度因為都在快取上,不會再到資料庫那一層,所以讀取速度變很快。然後會變動的列表資料像是排行榜、新上架列表,就快取 60 秒,60 秒之後就會失效從資料庫讀取最新的列表資料。總之就是可以快取的就快取。
35. 這個是 iNDIEVOX 快取機制的程式範例,所有的 Model Class 可能都會繼承這個抽象類別,基本上這個快取機制的邏輯就跟我上一張投影片所說的那樣,項目資料會永久快取,然後有任何更新就同時更新資料庫及快取。然後列表資料就快取 60 秒。(稍微說明一下程式碼)
36. 經過這個調整之後,整個架構就是多了很多快取,資料庫的 loading 變小,因此效能也增加了。目前整個架構就是 Web、Database 及 Cache 都可以 Scale Up 及 Scale Out,所以只要售票前事先評估好 Scale 量,基本上應該就可以順利應付搶票人潮。
37. 這次完全極致的調整應該就可以萬無一失了吧!
38. 田馥甄演唱會票券開賣之後,人潮達到 EC2 機器的使用上線,所以又被攻破了。我們目前對外公開的 Google Analytics 活躍人數是線上同時 11650 人,但實際的數字更高,我這邊不能透露。
39. 所以經過這次田馥甄的洗禮之後,我們又開始檢討這次售票遇到的問題。首先是 AWS 一個 Region 預設上限只有 20 台 EC2,田馥甄那時用了 20 台 4x.large 的 EC2,但還是超過這些機器的負載量。第二點是第三方金流服務瞬間承載量不夠,許多交易無法完成,造成使用者一直回來重新購票。第三點是讀寫分離在搶票時表現不如預期,因為交易時有太多寫入的操作,所以讀寫分離幾乎無法分散負載量,反而可能在大量切換到 Master 及 Slave 資料庫時增加 loading。
所以改善的方式就是向 AWS 申請使用更多的 EC2,如果購買 AWS Business Support,AWS 可以很快速的處理,讓你可以使用更多的 EC2,有時最快十分鐘之內就會完成。在金流這方面,由於無法指望金流公司可以有更多的負載量,我們可以將付費從購票流程中抽離出來,但 iNDIEVOX 上的主辦單位很堅持訂票時要同時付費,為了應付這種情況,我們研發了一個演算法來實作排隊機制,讓使用者可以分批進入購票流程,減輕金流公司的負載量。然後資料庫方面打到讀寫分離的架構,改用 DynamoDB 來支援售票功能。
40. 在這邊說明一下 iNDIEVOX 如何做到排隊機制。我們會做這個排隊機制主要還是因為主辦單位堅持訂票同時要付費,然後第三方金流又無法承受這個搶票的負載量,所以才使用排隊機制,如果有類似的情境,也可以直接應用這個方法。使用排隊機制,假設每秒可以產生出 500 張訂單,其實 60 秒也可以完成 30000 張訂單,所以沒有什麼缺點。在排隊機制這邊我參考了 Bakery Algorithm 來實做,這個演算法在讀作業系統時應該都看過,我將它稍微修改了一下就完成了我們的排隊機制演算法。最後,如果等待排隊的執行緒太多,有時反而會拖累我們自己的系統,所以到達一個臨界值,我會將使用者導到一個 S3 上面的靜態網頁,讓使用者等待進入購票流程,減少使用者耗掉 EC2 的資源。
41. 這是 iNDIEVOX 的排隊機制等待頁面,使用者會以為還在購票流程中,但其實有更多使用者排在他前面正在付費,有時搶票真的要看速度還有運氣。
42. 然後由於資料庫讀寫分離的架構在搶票時幾乎無用武之地,我拿掉了資料庫讀寫分離的架構,改嘗試使用 DynamoDB 來支援搶票的功能。DynamoDB 是一個很容易擴展且效能很好的 NoSQL Database。它的 Throughput 可以任意調整,比如 Read、Write 可以依據預估每秒 I/O 量來設定,每秒想要完成 10000 張訂單也不是問題,但就是要付很多錢,不過如果是短期的搶票,就很適合使用 DynamoDB。我們可以使用 DynamoDB 來存取售票訂單以及活動的座位表。使用這個方法,比較大的缺點就是讓程式碼變得複雜。不過為了搶票,就還是需要使用到 DynamoDB。
43. 所以在經過了田馥甄的洗禮之後,iNDIEVOX 整個網站的系統架構圖大概就是這樣(稍微說明一下圖)。
44. 這是我們近期一些熱門活動的節錄,像是最近比較熱門的有 08 月的徐懷鈺、07 月的伍佰、06 月的楊丞琳及 SHE 的 Ella,我們都順利的應付了搶票人潮,再也沒有因為搶票而掛站了~
45. 以上就是 iNDIEVOX 踏入售票領域時在系統架構上的快速蛻變,這些架構上的改變,其實現在的 Laravel 5 大部份都可以做得到,所以奉勸大家沒事還是不要自幹框架,否則每次改架構時都會很痛苦!
46. 接下來想跟大家分享一下,如前面所說的,售票前我們必須先預估好 Scale 的量,所以熱門活動售票前我們都需要做一些售票前的準備,接下來就來分享一下這個部份。
47. 在熱門活動售票前,我們會依據以往搶票人數預估此場搶票人數,iNDIEVOX 已累積上萬場售票活動,所以總是可以預估出很接近的數字。這樣的方法很主觀,也許未來累積更多資料,我們也可以嘗試使用 Machine Learning 的方法來幫忙預估搶票的人數。
得出預估的搶票人數之後,我們會計算每個購票步驟的 Request 及 Response 量、每個步驟會使用多少 CPU 及 Memory,還有每個步驟的 Database I/O 量。這牽涉到 EC2 要開幾台,RDS 及 DynamoDB 的 IOPS 要開到多少。然後用這個數字開好機器後,使用 multi thread 的方式模擬搶票,也許跟實際搶票還是會有所不同,但至少可以稍微了解一下到時搶票時的情況。
48. 然後剛剛 iNDIEVOX 最終的系統架構還有一個瓶頸,那就是 EC2 前面的 ELB 以及後面的金流服務。金流服務我們可以用剛剛的排隊機制解決,而 ELB 我們無法直接調整,但是我們可以透過購買 AWS Support 進行 pre-warm 的服務,以增加負載量,這通常需要幾天的作業時間,所以最好可以一個禮拜前就開始準備。要進行 ELB pre-warm 時,會收到一份問券,這時剛剛預估的數字就可以再派上用場。
49. 以上就是今天講題最主要想跟大家分享的部分了,接下來我個人還有一些想法想跟大家分享一下。
50. 有關台灣的售票系統,如果現在問大家覺得做得好不好,目前大家可能還是會說還有許多可以改善的空間,所以我們正在做這件事情。台灣的售票系統有很長期一段時間沒有什麼太大的改變,像是展弈系統的年代、寬宏、華娛等等,宏碁系統的小宇宙、大市集、江蕙賣票等等,還有大家常使用的超商機台,我們一直會在這些系統聽到搶票當機的問題,而我們其實也很想解決這樣的問題,所以 iNDIEVOX、KKTIX 跟拓元雖然表面上競爭關係,但我們私底下也組成了 AWS 賣票同好會,互相交流相關的資訊。
51. 其實售票當機這個問題不是今天才發生,一直以來都有很多因為搶票而當機的新聞,經過這麼多年都還是沒有解決。我們希望這個問題可以在 AWS 賣票同好會得到一個很好的解決方案。
52. 其實 iNDIEVOX 在經歷了田馥甄的洗禮之後就沒再遇過因為搶票而掛站的情形,但是我發現了一個售票定律,那就是「即使沒有當機,報紙還是會寫當機」,因為當機現在已經變成了一個行銷的手段。比如這次徐懷鈺售票,我們大概 30 秒就順利完售,但是新聞就寫「1000多張門票在開賣30秒內秒殺,還造成售票系統大當機」,大家不會覺得這句話很有邏輯問題嗎?如果當機掛站了,到底要怎麼 30 秒內秒殺?
53. 最後跟大家提一下,目前售票服務還是不夠完善,有一些是 iNDIEVOX 無法改變的現況。比如之前提的第三方金流服務瞬間承載量不夠,但主辦單位堅持訂票時同時要付費,如果主辦單位不堅持訂票時同時要付費,我們還可以有更大的承載量。
然後目前 iNDIEVOX 還是有跟 ibon 系統串接,這也造成一些售票瓶頸,實體端點的售票速度絕對比不上網路售票,但有些主辦單位還是會有實體端點的迷思,他會認為實體端點才是銷售的主力,即使目前看到的銷售數字絕大部分都是網路售票。
跟 ibon 系統串接的另一個原因,就是歌迷對實體票券的迷思,有些比較老派的歌迷還是會希望拿到實體票券,而不習慣使用電子票,否則電子票絕對是未來的一個方向。
然後國外對於這種搶票活動,幾乎都是用抽票的方式來解決,但這種方式在台灣並不普及,主辦單位及歌迷對這樣的改變都有疑慮,甚至常常覺得售票系統會有黑箱作業的情況,所以從不考慮這個技術上非常可行的方案。
使用電子票可以解決實體端點瓶頸的很多問題,但目前仍然不夠普及,許多主辦單位不太願意嘗試全電子票的售票方案。
這些問題,其實是因為目前環境的原因,所以我們並無法用自己的技術來解決這些問題,當然未來我們還是會依照主辦單位跟歌迷的習慣慢慢去做調整,讓台灣的售票系統越來越好。
54. 這就是我今天這個講題所有的內容,希望大家都有得到收獲,如果有問題可以現在發問,或者用上面這些方式與我聯繫,目前 iNDIEVOX 也在招募工程師,有興趣的開發者可以加入我們!謝謝!