# 後端工程師技術問卷 (20210114) 1. 請問您目前主要的技術學習管道為何?(例:某網站,特定書籍等) Ans : * 購買相關書籍(EX: PHP 學習手冊、Laravel 新手道場、Laravel 啟動與運行) * 網站(EX: iT邦幫忙、stackoverflow) * 網路教學影片(Youtube、B站、laracasts) * 實際演練side project,當遇到問題時,自行google查詢相關問題並嘗試解決 2. 請舉例在你過去的工作經驗中,覺得面臨過最大的技術挑戰或最令你印象深刻的技術性問題,並分享最後如何解決他? Ans : 曾經有個案子是機上盒的App,App端是請其他工程師處理,我這是負責用PHP開立API輸出內容,及控制前台操作功能,當時有需求是要開發websocket,可以建立持續監聽實現讓使用者能操作掃QR Code進行登入,但當下開發時遇到在本地端測試正常,但是一上到正式環境就會有問題,最後嘗試了很多不同的方式或套件,都還是一樣,最後這部分功能是改用Node.js去進行開發,來實現需求,最後完成的當下,感受到很大的成就感 3. 當 Client 端存取 https://foo.com/api/v1/bar 這個 HTTP API 的位置,請從 DNS 解析到 PHP 解析到最後回傳 Response 內容盡可能完整描述中間所有詳細發生的流程。(以 Nginx + PHP-FPM 的架構為例) Ans : **流程步驟** Client端存取 -> DNS解析 -> 伺服器進行數據處理(Server) -> Response **說明** * Client端存取 => EX : 存取到 https://foo.com/api/v1/bar 網址 * DNS解析 => DNS (Domain Name Server) 將網址(https://foo.com/api/v1/bar)轉為一組 IP Address(EX : 192.168.0.1),接著再由 IP Address 向 Server 發送 Request * 伺服器進行數據處理(Server) => 接收到Request(EX : HTTP Method),Nginx 會作為代理伺服器將Request傳送給後端的伺服器(PHP-FPM),讓PHP-FPM 可以正確將請求對應到正確的PHP檔案,並進行數據處理 * Response => 數據處理完後,伺服器會回傳HTTP狀態碼跟Response給Client端 4. 如下有一個票券購買的範例程式,程式會先檢查票券的剩餘數量,若數量足夠則進行票券購買流程並且扣除票券剩餘數量,若票券數量不足則會拋出例外錯誤。請問以下的程式可能存在什麼問題,如果是你會怎麼修改? ```php= public function buyTicket(Request $request, Ticket $ticket) { // 檢查票券是否還有足夠數量 if ($ticket->available_amount <= 0) { throw new TicketUnavailableException('Ticket amount is not available'); } // 中間購買邏輯省略 // 扣除票券剩餘數量 $ticket->update([ 'available_number' => $ticket->available_amount - 1; ]); return [ 'success' => true ]; } ``` Ans : **可能存在的問題 :** 當情境是一筆一筆進行此扣庫存程式,扣庫存數量會是正常,但當同一時間內湧入大量執行此扣庫存的程式時同一個資源被互搶的問題就產生了,因此有可能導致扣庫存數量異常的情形發生,又或者可能因大量執行導致庫存數最後變成負數 EX : 當A和B同時執行扣庫存程序時,A和B都是取到相同庫存數量50,結果相互執行-1的動作後,雖說有可能sql update時間不一樣,但2筆update都是49的異常情況就產生了,應該是一筆49, 另一筆48才是正確的 **解決方式 :** * 先將資料表欄位available_amount設定成不為負數,以防多倒扣變成負數 * 使用Transaction進行處理,當A進行扣庫存程式時,會產生事務A,此資源會處於被鎖定狀態,當事務A完成update資料,釋放資源後,B進行扣庫存程式時產生的事務B,才能接著執行,這樣就解決因大量時可能造成扣除庫存異常的問題 ```php= public function buyTicket(Request $request, Ticket $ticket) { //開始交易 DB::beginTransaction(); try { //查詢時進行鎖表 $ticket = Ticket::lockForUpdate()->find($ticket->id); // 檢查票券是否還有足夠數量 if ($ticket->available_amount <= 0) { throw new TicketUnavailableException('Ticket amount is not available'); } // 中間購買邏輯省略 // 扣除票券剩餘數量 $ticket->update([ 'available_amount' => $ticket->available_amount - 1, ]); } catch (\Exception $e) { DB::rollback(); return [ 'success' => false, $e->getMessage() ]; } //提交結果 DB::commit(); return [ 'success' => true ]; } ```