網站首頁 編程語言 正文
背景
在實現圖片轉碼的需求時,需要支持最大 500 個圖片下載后轉換格式;
如果是一個一個下載后轉碼,耗時太長,需要使用 goroutine 實現 500 個圖片并發下載后,并發轉碼;
但自測過程中發現,會偶現下載后只轉換了 499 個圖片或更少的情況(全部下載、轉碼成功的條件下);
然后就開始了打印日志找 bug 的過程。
排查問題
因為并發時使用到了 sync 等待全部協程結束,起初以為是 sync 異步等待出了問題;
打印日志發現,正常執行了 500 次下載,執行完成下載之后,繼續執行的轉碼操作,排除 sync 異步等待有問題;
代碼如下:
import ( "github.com/satori/go.uuid" "sync" ) func downloadFiles(nWait *sync.WaitGroup, urls []interface{}, successFiles *[]string, failedFiles *[]string) { // 遍歷 urls 進行下載 for _, value := range urls { go func(value interface{}) { defer nWait.Done() // 執行結束,協程減 1 fullname := config.TranscodeDownloadPath + "/" + uuid.NewV4().String() // 需要確保文件名的唯一性 (防止不同用戶同一時間操作了同一文件,導致轉碼失敗) err := utils.DownloadCeph(value.(string), fullname) // 下載文件 // 下載文件狀態記錄 if err != nil { *failedFiles = append(*failedFiles, fullname) } else { *successFiles = append(*successFiles, fullname) } }(value) } } // 前端傳入的圖片 url strUrlList := req["strUrlList"] // 初始化變量 nWait := sync.WaitGroup{} // 多協程異步等待 var successFiles []string // 下載成功文件 var failedFiles []string // 下載失敗文件 // 遍歷 strUrlList 進行下載 log.Error("開始下載!長度:", len(strUrlList)) nWait.Add(len(strUrlList)) // 等待協程數 downloadFiles(&nWait, strUrlList, &successFiles, &failedFiles) nWait.Wait() // 阻塞,等待完成 log.Error("下載結束!長度:", len(successFiles)) //... log.Error("下載轉碼!") //...
日志如下:
2022-10-29 21:28:51.996 ERROR ? services/tools.go:149 ? 開始下載!長度:500
2022-10-29 21:28:52.486 ERROR ? services/tools.go:153 ? 下載結束!長度:499
2022-10-29 21:28:52.486 ERROR ? services/tools.go:155 ? 開始轉碼!
打印更詳細的日志,對 for range 循環內的邏輯進行排查;
在單個 for 循環結束時增加日志:
log.Error("下載協程結束: ", len(*successFiles))
發現一處特殊的日志:
2022-10-29 21:40:38.407 ERROR ? services/tools.go:35 ? ?下載協程結束: 63
2022-10-29 21:40:38.407 ERROR ? services/tools.go:35 ? ?下載協程結束: 64
2022-10-29 21:40:38.407 ERROR ? services/tools.go:35 ? ?下載協程結束: 65
2022-10-29 21:40:38.407 ERROR ? services/tools.go:35 ? ?下載協程結束: 65
2022-10-29 21:40:38.408 ERROR ? services/tools.go:35 ? ?下載協程結束: 66
2022-10-29 21:40:38.408 ERROR ? services/tools.go:35 ? ?下載協程結束: 67
兩次長度都是 65,切片長度沒有發生變化,同一時間點執行兩次切片 append 方法,會偶現一次失效,問題原因找到;
解決問題
使用切片索引進行賦值,不再使用 append ;
修復代碼如下:
import ( "github.com/satori/go.uuid" "sync" ) func downloadFiles(nWait *sync.WaitGroup, urls []interface{}, successFiles *[]string, failedFiles *[]string) { // 遍歷 urls 進行下載 for index, value := range urls { go func(index int, value interface{}) { defer nWait.Done() // 執行結束,協程減 1 fullname := config.TranscodeDownloadPath + "/" + uuid.NewV4().String() // 需要確保文件名的唯一性 (防止不同用戶同一時間操作了同一文件,導致轉碼失敗) err := utils.DownloadCeph(value.(string), fullname) // 下載文件 // 下載文件狀態記錄 if err != nil { (*failedFiles)[index] = fullname } else { (*successFiles)[index] = fullname } }(index, value) } } // 前端傳入的圖片 url strUrlList := req["strUrlList"] // 初始化變量 nWait := sync.WaitGroup{} // 多協程異步等待 successFiles := make([]string, len(strUrlList), len(strUrlList)) // 下載成功文件 failedFiles := make([]string, len(strUrlList), len(strUrlList)) // 下載失敗文件 // 遍歷 strUrlList 進行下載 nWait.Add(len(strUrlList)) // 等待協程數 downloadFiles(&nWait, strUrlList, &successFiles, &failedFiles) nWait.Wait() // 阻塞,等待完成
原文鏈接:https://juejin.cn/post/7159967055217688590
相關推薦
- 2022-08-27 C#使用HttpHelper框架重啟路由器_C#教程
- 2021-12-18 C++?STL容器詳解之紅黑樹部分模擬實現_C 語言
- 2022-11-09 Python有序容器的?sort?方法詳解_python
- 2022-07-21 提高新手寫代碼效率的Emmet插件怎么使用
- 2022-10-02 SQL堆疊注入簡介_MsSql
- 2022-05-18 C++?qt實現打開關閉狀態按鈕的代碼_C 語言
- 2022-09-09 python?獲取星期字符串的實例_python
- 2022-10-16 Python?numpy中np.random.seed()的詳細用法實例_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支