iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
Software Development

一套真實運作中的 Laravel 系統,拆解它的原生機制系列 第 19

Day 19:SOAP 逾時防護——從「先能動」到「補上超時控制」的真實演進

  • 分享至 

  • xImage
  •  

前言:「先讓它動起來」跟「一開始就設計完善」,真實專案通常是哪一種?

「跟外部系統對接的程式碼,逾時控制不是一開始就該設計好嗎?」

理論上是這樣沒錯。但今天要老實講一個真實發生過的演進過程:這個系統跟外部系統對接的 SOAP Client,最初的版本完全沒有逾時控制——它能動、能拿到資料,但如果外部系統那端卡住不回應,這邊的請求會一直等下去。逾時防護是後來才補上的。這篇想講的不是「應該怎麼設計」,是「真實系統常常是這樣一步一步補齊的」。

今日目標

  • 看一次真實的程式碼演進:從「只接受 WSDL 位址」到「加入可控的逾時傳輸層」
  • 理解為什麼「先能動」不代表設計錯了,而是真實開發節奏的一部分
  • 認識沒有逾時控制的外部呼叫,實際上會帶來什麼風險
  • 建立「先求能動、再補防護」跟「先求完善」之間的取捨意識

本文主體

最初的版本:只管拿到資料,沒管會不會卡住

這個系統跟外部系統對接用的 SOAP Client Factory,最早的版本簽章大致是這樣:

public static function factory(string $wsdl): DataGatewayClient
{
    $engine = DefaultEngineFactory::create(
        ExtSoapOptions::defaults($wsdl, [])
            ->withClassMap(DataGatewayClassmap::getCollection())
    );
    // ...
}

只吃一個 WSDL 位址,內部組出一個沒有特別設定逾時的 SOAP engine。這個版本能正常運作——多數時候,外部系統回應得夠快,這支 Client 完全感覺不出問題。

後來補上的版本:加入可控的傳輸層跟逾時設定

實際的 commit 訊息只留下簡短的一句「soap timeout throw error」,但改動內容清楚說明了發生了什麼事——這支 Factory 的簽章加了一個新參數:

public static function factory(Psr18Transport $transport, string $wsdl): DataGatewayClient
{
    $extSoapOptions = ExtSoapOptions::defaults($wsdl, [
        'connection_timeout' => 5,
    ])->withClassMap(DataGatewayClassmap::getCollection());

    $engine = DefaultEngineFactory::create($extSoapOptions, $transport);
    // ...
}

多了一個 Psr18Transport $transport 參數,讓呼叫端可以控制底層的傳輸層設定(就是 Day 17 看到的、Guzzle Client 帶著 CONNECT_TIMEOUT/TIMEOUT 設定的那個傳輸層),同時 SOAP 層自己也加上了 connection_timeout => 5

為什麼「先沒有逾時」不等於「設計錯了」

沒有逾時控制的版本,在多數情況下運作正常——這正是這類問題最容易被拖延處理的原因:外部系統平常回應夠快,逾時風險不會在日常開發、日常使用時顯現出來。只有在外部系統真的卡住、網路異常、或者對方系統本身出問題時,這個缺口才會變成真正的痛。

這是一個很真實的開發節奏:先讓功能能動起來、能拿到資料、能通過驗收,逾時防護這種「平常不會出事、一旦出事影響很大」的邊角情況,往往要等到真的踩到(或者有意識地回頭補強)才會補上。commit 訊息裡的「throw error」暗示著,這次改動很可能就是因為真的遇到了外部系統卡住、導致請求無限期等待的情況,才回頭補上這一層防護。

這不是說「一開始就該設計完善」是錯的建議——如果條件允許,當然應該一開始就把逾時考慮進去。但真實專案的節奏經常是:先解決「能不能動」,再回頭處理「會不會出事」,這篇想誠實記錄的正是這個過程,而不是假裝這個系統從第一天就設計得完美無缺。

沒有逾時控制的真實風險

如果一支負責跟外部系統溝通的程式碼完全沒有逾時控制,最壞的情況是:外部系統卡住不回應,這邊的請求會一直掛著等待,佔用一個執行緒/連線資源不釋放。如果這種呼叫發生在使用者的請求週期裡(不是背景 Job),使用者會看到頁面一直轉圈圈,甚至觸發伺服器層級更嚴重的資源耗盡問題。如果是背景 Job 裡呼叫,佇列裡的這個工作會一直卡著不結束,後面排隊的其他工作全部被拖住。

今日思考題

你的專案裡有沒有呼叫外部系統的程式碼,逾時設定是明確控制的,還是依賴框架/套件的預設值(甚至完全沒設)?如果外部系統今天卡住不回應,你的系統會怎麼反應——是優雅地在幾秒內失敗,還是無限期等下去?

今日重點回顧

  • 這個系統對接外部系統的 SOAP Client,最初版本完全沒有逾時控制,後來才補上
  • 「先能動、沒有逾時控制」在多數時候運作正常,正是這類問題容易被拖延的原因
  • 真實的改動加入了 Psr18Transport 參數 + connection_timeout 設定,讓逾時變得可控
  • 「先求能動、再補防護」是真實開發常見的節奏,不代表設計者一開始就疏忽了

明日預告

明天要看這個系統怎麼處理外部系統回傳的不乾淨資料——一段用來清理非法字元的防禦函式,作者自己誠實標註「這不是我自己想出來的,是抄一篇公開文章的解法」。


上一篇
Day 18:跟外部系統對接——SOAP 協議在 2026 年怎麼用 PSR-18 包起來用
下一篇
Day 20:XML 容錯——抄一篇公開文章的解法,也要懂它在防什麼
系列文
一套真實運作中的 Laravel 系統,拆解它的原生機制20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言