設計BookMyShow預訂車票 –系統設計架構師面試問題
在劇院中搜索您喜歡的電影,檢查座位是否可用以及在5-10分鐘內無需花費太多精力即可在BookMyShow應用上預訂車票確實很容易……
我們都知道BookMyShow的服務(畢竟我們都喜歡看電影……哈哈茲),以及它是如何工作的,但是您能想像到在這個巨大的網站後面,工程師是如何運用大腦來構建該系統的複雜體系結構的。
而且,如果我們要求您在短短的45分鐘(或更短的時間內)內設計此系統該怎麼辦(這是個玩笑嗎?我們並不是在開玩笑,但是如果您正準備進入頂尖的技術巨頭公司,您可能會在面試中面對系統設計(特別是高級工程師的角色),而像BookMyShow這樣的系統的設計相當這輪的一個常見問題。
在此博客中,我們將討論如何設計諸如BookMyShow之類的在線票務預訂系統,但在繼續之前,我們希望您閱讀文章“如何在採訪中進行系統設計?”。它會給你一個想法,這輪比賽看起來像什麼,應該做什麼,以及在面試官面前應該避免的錯誤。
1.定義目標和要求
告訴您的面試官您將支持以下功能。如果訪問者想添加更多功能,他/她將提及。
- 該門戶網站應列出劇院所在的不同城市。(RDBMS)
- 用戶選擇城市後,應向該用戶顯示在該特定城市發行的電影。
- 用戶選擇電影后,門戶網站應顯示運行該電影的電影院和可用的節目。
- 用戶應該能夠選擇特定劇院的演出並預訂門票(第三方付款支持)。
- 通過短信通知或電子郵件發送票務副本。(工人和GCM)
- 登錄時的電影建議(Hadoop和ML火花流以獲取推薦引擎),向用戶實時通知有關新電影發行和其他內容的信息。
- 門戶網站應向用戶顯示電影院的座位安排。
- 用戶應能夠根據自己的選擇選擇多個座位。
- 用戶在完成付款之前應該能夠保持座位5-10分鐘。
- 門戶網站應以先進先出的方式提供門票
- 評論和評分(Cassandra)
- 該系統應高度並發,因為同一時間將有多個預訂請求。
- 門戶的核心是機票預訂,這意味著金融交易。因此系統應該是安全的並且符合ACID。
- 響應式設計(ReactJS和Bootstrap)可在各種尺寸的設備(例如手機,平板電腦,台式機等)上運行。
- 電影信息。
2. Bookmyshow如何與劇院對話?
當您使用移動應用程序或網站訪問任何第三方應用程序/電影票務匯總器時,您會在該劇院看到電影節目的可用座位和已佔用座位。現在的問題是這些第三方聚合器如何與劇院對話,獲取可用的座位信息並將其顯示給用戶。肯定地,該應用需要與劇院的服務器配合使用以獲取座位分配並將其分配給用戶。主要有兩種策略可將席位分配給這些聚合器。
- 每個聚合器都將使用特定數量的席位,然後將這些席位提供給用戶。在此策略中,已經為這些聚合器預留了一些席位,因此無需不斷更新所有劇院的席位信息。
- 在第二種策略中,該應用程序可以與劇院和其他聚合器一起使用,以不斷更新座位可用性信息。然後,門票將提供給用戶。
3.如何獲取座位可用性信息?
獲取信息的方法主要有兩種:
- 聚合器可以直接連接到劇院的DB,並從數據庫表中獲取信息。然後,可以將該信息緩存並顯示給用戶。
- 使用劇院的服務器API獲取可用的座位信息並預訂門票。
如果多個用戶嘗試使用不同的平台預訂同一張票,將會發生什麼? 如何解決這個問題呢?
劇院的服務器需要遵循超時鎖定機制策略,該策略將在特定時間段(例如5-10分鐘)內臨時鎖定用戶的座位。如果用戶無法在該時間段內預訂座位,請為另一個用戶釋放座位。這應該以先到先得的原則進行。
如果您使用的是Theaters服務器API,那麼您將發出大量請求或IO阻止從服務器到劇院服務器的調用。為了獲得更好的性能,我們應該在python或Erlangs輕量級線程中使用async或在Go中使用Go Coroutines。
高層架構
BookMyShow建立在微服務架構之上。讓我們分別看一下組件。
負載均衡器
當我們水平擴展應用程序服務器時,負載均衡器用於分配服務器上的負載並保持系統高度並發。負載平衡器可以使用多種技術來平衡負載,這些是…
- 一致的散列
- 循環賽
- 加權輪循
- 最少連接
前端緩存和CDN
我們使用Varnish進行前端緩存,以減少後端基礎結構的負載。我們還可以使用CDN Cloudflare來緩存頁面,API,視頻,圖像和其他內容。
應用服務器
將有多個應用程序服務器,BookMyShow使用Java,Spring Boot,Swagger,Hibernate作為應用程序服務器。我們還可以使用基於Python的服務器或NodeJS服務器(取決於要求)。我們還需要水平擴展這些應用服務器,以承擔繁重的工作並並行處理大量請求。
彈性搜尋
彈性搜索用於支持Bookmyshow上的搜索API(以搜索電影或節目)。彈性搜索是分佈式的,並且在系統中具有RESTful搜索API。它也可以用作分析引擎,用作應用程序級搜索引擎來回答來自前端的所有搜索查詢。
緩存
要保存與電影,座位訂購,劇院等有關的信息,我們需要使用緩存。我們可以使用Memcache或Redis進行緩存,以將所有這些信息保存在Bookmyshow中。Redis是開源的,它也可以用於鎖定機制,暫時為用戶阻止票證。這意味著當用戶嘗試預訂票證時,Redis將使用特定的TTL阻止票證。
數據庫
我們需要將RDBMS和NoSQL數據庫用於不同的目的。讓我們分析一下我們系統中需要什麼,以及哪種數據庫適合哪種數據……
- RDBMS:我們已經提到在系統中需要ACID屬性。同樣,我們有國家,城市,城市中的劇院,這些劇院中的多個屏幕,每個屏幕上有多排座位。因此,很顯然,我們需要適當的關係表示形式。另外,我們需要處理交易。 RDBMS適合這些情況。該門戶網站將是繁重的讀取操作,因此我們需要按Geo分片數據,或者需要使用master-master slave體系結構。從站可用於讀取,而主站可用於寫入。
- NoSQL:我們還有大量數據,例如電影信息,演員,劇組,評論和評論。RDBMS無法處理大量數據,因此我們需要使用可以分發的NoSQL數據庫。Cassandra是處理大量信息的不錯選擇。我們可以將數據的多個副本保存在部署在多個區域中的多個節點中。這樣可以確保數據的高可用性和持久性(如果某個節點發生故障,我們將在其他節點中擁有可用數據)。
- 使用HDFS運行查詢以進行分析。
異步隊列
異步工作器的主要任務是執行任務,例如生成預訂票證圖像的pdf或png並將通知發送給用戶。對於推送通知,SMS通知或電子郵件,我們需要調用第三方API。這些是網絡IO,它增加了很多延遲。而且,這些耗時的任務無法同步執行。為了解決此問題,應用服務器一旦確認票務預訂,便會將消息發送到消息隊列,一名免費的工作人員將接任務,異步執行該任務並提供SMS通知,其他通知或電子郵件給用戶。RabbitMQ或Kafka可以用於消息隊列系統和Python celery可用於工人。對於瀏覽器通知或電話通知,請使用GCM / APN。
商業智能和機器學習
為了對業務信息進行數據分析,我們需要一個Hadoop平台。所有日誌,用戶活動和信息都可以轉儲到Hadoop中,最重要的是,我們可以運行PIG / Hive查詢來提取信息,例如用戶行為或用戶圖。ML用於了解用戶的行為並生成電影推薦等。對於實時分析,我們可以使用Spark流。我們還可以使用Spark或Storm流處理引擎找出欺詐檢測和緩解策略。
日誌管理
ELK(ElasticSearch,Logstash,Kibana)堆棧用於日誌記錄系統。所有日誌都被推送到Logstash中。Logstash通過Files / Syslog / socket / AMQP等從所有服務器收集數據,並根據一組不同的過濾器將日誌重定向到Queue / File / Hipchat / Whatsapp / JIRA等。
分步工作
- 客戶訪問門戶並過濾位置。要找到位置,我們可以使用GPS(如果是手機)或ISP(如果是筆記本電腦)。然後,將向用戶建議在這些劇院中放映的劇院和電影。數據將從DB和ELK推薦引擎提供。
- 用戶選擇電影並檢查附近所有劇院中電影的不同定時。
- 用戶以他/她自己的劇院選擇來選擇電影的特定日期和時間。可用的座位信息將從數據庫中獲取,並顯示給用戶。
- 一旦用戶選擇了可用的座位,BMS將在接下來的10分鐘內暫時鎖定該座位。BMS與影院DB交互並為用戶鎖定座位。該票將暫時為當前用戶預訂,對於使用其他聚合器或應用的所有其他用戶,該席位在接下來的10分鐘內將不可用。如果用戶未能在該時間段內預訂機票,則該座位將被釋放給其他聚合器。
- 如果用戶繼續預訂,他/她將檢查帶有付款選項的發票,並在通過付款網關付款後,將通知App Server成功付款。
- 付款成功後,劇院將生成一個唯一的ID,並將其提供給應用服務器。
- 使用該唯一ID,將使用QR碼生成票證,並向用戶顯示該票證的副本。同樣,將添加到隊列的消息,以通過SMS通知或電子郵件將票證的發票副本發送給用戶。門票將包含所有詳細信息,例如電影,劇院地址,時間,劇院編號等。
- 在電影放映時,當顧客參觀劇院時,如果顧客門票和劇院門票上的ID均匹配,則QR碼將被掃描,並且允許顧客進入劇院。
所需的API
- GetListOfCities()
- GetListOfEventsByCity(CityId)
- GetLocationsByCity(CityId)
- GetLocationsByEventandCity(cityid,eventid)
- GetEventsByLocationandCity(CityId,LocationId)
- GetShowTiming(eventid,locationid)
- GetAvailableSeats(eventid,locationid,showtimeid)
- VarifyUserSelectedSeatsAvailable(eventid,locationid,showtimeid,seats)
- BlockUserSelectedSeats()
- BookUserSelectedSeat()
- GetTimeoutForUserSelectedSeats()
RDBMS表
- 放置(保存任何給定劇院的層級數據,如國家,州,城市和街道)
- 劇院
- 屏幕
- 層(座位數)
- 座位數
- 電影
- 優惠
- 票
- 用戶
RDBMS表之間的關係
- 一對多:地點和劇院。
- 一對多:劇院和銀幕
- 一對多:屏幕和層
- 一對多:等級和席位
- 一對一: 屏幕和電影
- 一對多:用戶和票證
- 一對多:門票和座位
NoSQL表
這些表之間將沒有關係。
- 評論
- 等級
- 電影資訊
- 拖車或畫廊
- 藝人
- 演職人員
- 評論
- 分析數據
Bookmyshow使用的技術
- 用戶界面: ReactJS和BootStrapJS
- 服務器語言和框架: Java,Spring Boot,Swagger,Hibernate
- 安全性:春季安全性
- 數據庫: MySQL
- 服務器: Tomcat
- 緩存:在內存緩存中進行Hazelcast。
- 通知: RabbitMQ。用於推送通知的分佈式消息隊列。
- 付款API:熱門的是Paypal,Stripe,Square
- 部署: Docker和Ansible
- 代碼庫: Git
- 記錄: Log4J
- 日誌管理: Logstash,ELK堆棧
- 負載均衡器: Nginx
很多候選人比編碼回合更害怕系統設計回合。原因是……他們不知道在有限的時間內應該涵蓋哪些主題和權衡取捨。他們需要記住,系統設計回合是極端開放的,沒有標準答案之類的東西。對於相同的問題,與不同訪調員的對話可能會有所不同。您的實踐經驗,知識,對現代軟件系統的理解以及在面試中如何清楚地表達自己的意見,對於成功設計系統至關重要。