--- tags: notes, bash, tool, cli, curl --- # 使用 `curl` 的 `--parallel` > :memo: `curl` 指令就像是電腦系統上的瑞士刀,可靠、好用且無所不在。除了 Linux 和 macOS 系統內有提供,[Win10 也在 2017 年後內建 `curl.exe`](https://curl.se/windows/microsoft.html)。時至今日,你可以說在任何平台的系統都能見到他。隨著 RESTful API 標準興起,很有可能你呼叫第一個 API,就是透過 `curl` 指令完成 > > `curl` 在 [2019 的 7.66 版](https://daniel.haxx.se/blog/2019/07/22/curl-goez-parallel) 迎來新功能 `--parallel`,遊戲規則產生變化,我們不需再藉由外部工具如 `xargs` 或 `GNU Parallel`,即可達成並行化 (concurrency) 工作。curl 開發者持續新增和修正相關功能,建議若要使用此功能直接[選用最新版`curl`](https://curl.se/download.html) ::: warning 本篇撰文者為系統管理員,測試環境為 Bash 搭配 curl `7.84.0` 透過 server 的 BMC 提供的 redfish api (類似 restful api),演示 curl 的 `--parallel` 功能 本文連結: https://hackmd.io/@kmo/curl_parallel 任何回饋歡迎留言在此篇 hackmd :) ::: ## 實例演示 ::: info :scroll: 情境說明 - 管理的 server 分 3 個群組,每群有 10 台電腦,共 30 台電腦 ```bash= # 群組 1 的電腦 IP 範圍 10.50.1.[1-10] (也就是 10.50.1.1, 10.50.1.2, 10.50.1.3, ..., 10.50.1.9, 10.50.1.10) # 群組 2 的電腦 IP 範圍 10.50.2.[1-10] (也就是 10.50.2.1, 10.50.2.2, 10.50.2.3, ..., 10.50.2.9, 10.50.2.10) # 群組 3 的電腦 IP 範圍 10.50.3.[1-10] (也就是 10.50.3.1, 10.50.3.2, 10.50.3.3, ..., 10.50.3.9, 10.50.3.10) ``` - 現代 server 的 BMC(Baseboard Management Controller) 服務,幾乎都有提供 redfish api (類似 restful api) - 使用 curl 指令,透過 api 取得單 1 台電腦 BIOS 設定,並存成檔案 以 IP 為 `10.50.1.1` 的 server 為例 ```bash= # 不同硬體廠牌的 api 都略有差異 # 實際應用務必查詢自己機器的 redfish 說明文件,或是詢問硬體 vendor curl -sku usr:pw 'https://10.50.1.1/redfish/v1/Systems/1/bios' -o 10.50.1.1.bios.conf ``` ::: ::: warning :dart: 問題 - 請使用上述 curl 指令範例,備份 30 台 server 的 BIOS 設定檔 ::: ### 1. Bash 的 nested loop - 此題直觀解法,就是透過 bash 腳本的 nested loop 完成 ```bash= #!/bin/bash for ii in {1..3}; do for jj in {1..30};do curl -sku usr:pw "https://10.50.$ii.$jj/redfish/v1/Systems/1/bios" -o "10.50.$ii.$jj.bios.conf" done done ``` - 但此方法效率不夠好,實際測試約花 `47` 秒完成 😴 ### 2. 匹配多個 URL - 透過 curl 的 [匹配 URL 語法](https://everything.curl.dev/cmdline/globbing)(URL globbing),可以一次匹配多個 URL 並執行 ,簡化上述 bash 腳本成為一行指令 ```bash= curl -sku usr:pw 'https://10.50.[1-3].[1-10]/redfish/v1/Systems/1/bios' ``` - 題目希望把 bios 設定檔備份下來,但 URL 結尾名稱都是 `bios`,若使用 `-O, --remote-name` 直接存檔,檔名相同(都叫 `bios`)會彼此覆蓋,最後只存到 1 個 `bios` 檔案 - 因此 curl 開發者也考慮到此情況,所以 URL 匹配語法支援輸出變數,可改寫為 ```bash= # 檔名的 `#1` 對應第 1 個匹配語法 `[1-3]` # 檔名的 `#2` 對應第 2 個匹配語法 `[1-10]` curl -sku usr:pw 'https://10.50.[1-3].[1-10]/redfish/v1/Systems/1/bios' -o '10.50.#1.#2.bios.conf' ``` - 上述指令尚未使用並行化處理,但執行效率已經大幅改善,實際測試約花 `15` 秒完成 👌 ### 3. 使用 `--parallel` ::: success - 當 request 的 URL 是不同來源,curl 開發者[建議可以使用 `--parallel` 並行化處理](https://everything.curl.dev/cmdline/urls/parallel) - 除此之外還有 2 個 option 可搭配使用 - `--parallel-immediate`: curl 預設會儘可能重複使用已建立的連線(multiplex),可加速處理需求並減少負載,不過當 URL 是不同來源,那 multiplex 效果就不大。使用此選項會把考慮 multiplex 的因子權重降低,儘可能開啟新連線。本文的實例就很適合使用 - `--parallel-max`: 預設是 50 個並行工作,可以調整自己適用的情境 ::: - 採用 `--parallel` 一次進行 30 個並行連線,此時指令為 ```bash= curl --parallel --parallel-immediate --parallel-max 30 \ -sku usr:pw \ 'https://10.50.[1-3].[1-10]/redfish/v1/Systems/1/bios' -o '10.50.#1.#2.bios.conf' ``` - 並行化處理後,實際測試僅花 `0.5` 秒完成 👏 ### 上述測試時間整理 | 問題解法 | 實際測試 | | --------------------- | ------------ | | Bash 的 nested loop | 47 秒 | | 匹配多個 URL (curl globbing) | 15 秒 | | 使用 curl 的 `--parallel` | 0.5 秒 | ### 取得回應資訊 - 當處理的 URL request 數量多,難免會遇到對方 server 忙碌或當機,此時需要取得回應資訊判斷。可以透過 `--write-out` 取得 http 的 `response_code`,並且搭配印出 `url` 對照。此時指令改為 ```bash= curl --write-out 'code %{response_code} url %{url}\n' \ --parallel --parallel-immediate --parallel-max 30 \ -sku usr:pw \ 'https://10.50.[1-3].[1-10]/redfish/v1/Systems/1/bios' -o '10.50.#1.#2.bios.conf' ``` - 執行時可以看到如下資訊印出在螢幕。由於是並行處理,所以並不會照數字順序執行。仔細看會注意最後一行 `10.50.3.3` 的回應是 `000`,代表`10.50.3.3`有不預期狀況發生 ```bash= code 200 url https://10.50.1.1/redfish/v1/Systems/1/bios code 200 url https://10.50.1.4/redfish/v1/Systems/1/bios code 200 url https://10.50.1.9/redfish/v1/Systems/1/bios ... 略 ... code 200 url https://10.50.3.10/redfish/v1/Systems/1/bios code 200 url https://10.50.3.9/redfish/v1/Systems/1/bios code 000 url https://10.50.3.3/redfish/v1/Systems/1/bios ``` ### 讓 curl 更可靠 - 安全性: 上述例子透過 `-u` 後面加 `usr:pw` 指定帳號密碼,這種方式在 linux 系統上是非常不安全的行為,若你使用的電腦為多人登入使用,此時其他用戶下 `ps -ef | grep curl` 之類指令,即可看到你使用 curl 指定的參數(當然包含密碼)。此時務必參閱 curl [`--config`](https://everything.curl.dev/cmdline/configfile) 方式,至少把密碼包在特定檔案 - 可靠性: 若要透過 curl 提供穩定的服務,那勢必得考慮錯誤處理,當 timeout 或 fail 等不符合預期的回傳值,要怎麼處理。此時可以參考 [manpage](https://curl.se/docs/manpage.html) 關於 `retry` or `fail` 等關鍵字的 option。當然最快方式參考 github or stackoverflow 網友們的腳本,並依據自己情境調整 ## 後記 - curl 是系統管理老朋友,近年以驚人速度持續更新,開發者在 7.66 release 的 [ blog 畫這張圖](https://daniel.haxx.se/blog/2019/09/11/curl-7-66-0-the-parallel-http-3-future-is-here/),來表明 curl 更新次數以指數成長。期待不久將來,還有來自這位老朋友的新消息 😀 ![](https://i.imgur.com/KTFofnW.png =x300) ## 參考連結 [Everything curl](https://everything.curl.dev): curl 開發者寫的手冊,讚 [curl 的 manpage](https://curl.se/docs/manpage.html): 網頁上查詢 option 好用 [curl 的 globbing 範例](https://unix.stackexchange.com/a/91574): stackoverflow 網友的範例 [curl 的 parallel 範例](https://stackoverflow.com/a/71967814): stackoverflow 網友的範例 --- {%hackmd @kmo/widget_license %}