堅持是一種態度,優秀是一種習慣!
最近一直在倒騰oauth2,oauth2是一種開放授權的協議。具體的原理不在本篇文章敘述,如同學們有興趣,可以下次詳細介紹。本篇主要講述這兩天遇到的獲取token的併發問題。
出現的問題
廢話少說,上乾貨。oauth2的一個主要作用是獲取token(有四種方式),併發出現的現象是當有同一個client_id,secret併發訪問/oauth/token時報
Incorrect result size: expected 1, actual 2 using jdbctokenstore錯誤。通過debug發現是oauth中的DefaultTokenService中的createAccessToken方法拋出來的異常
![spring cloud oauth2 中的併發問題](http://p2.ttnews.xyz/loading.gif)
上圖中的函數跟進去,發現在獲取token之前首先會根據client_id去數據庫中查詢是否token,當數據庫中有>1條記錄的時候,拋異常。
此時,數據庫中卻出現了2條記錄,why???
原因
createAccessToken方法的大概的邏輯是先刪除後插入,數據庫中有2條記錄,有時候會更多,懷疑是createAccessToken沒有做併發控制,當多個線程同時進入createAccessToken方法時會出現多條記錄的現象。於是寫了個腳本驗證自己的推斷:
![spring cloud oauth2 中的併發問題](http://p2.ttnews.xyz/loading.gif)
結果發現,如果同時啟動10個線程,同一個client_id會出現10天記錄(有時候低於10,純屬看運氣),驗證了我的推斷。
解決方案:
問題根源找到了,就得想辦法解決。在spring-security-oauth git的issue上發現有好幾個人也遇到同樣的問題,具體產生多條記錄的,有個老外總結如下:
看不懂英文的,可以去Google翻譯,但是作為一個程序猿,還是要有閱讀技術類文檔的能力。
按照老外的說法是,oauth2目前只支持單線程,認為現實的應用場景也只有單線程用戶,wtf!!!殊不知還有腳本這玩意。
目前總體的解決方案有如下2種:
1、繼承DefaultTokenService 在createAccessToken方法加上sychronized關鍵字。但是這種方案有個弊端在於,oauth服務器如果是分佈式部署還是會出現併發問題
2、繼承DefaultTokenService 將createAccessToken事務級別調SERIALIZABLE。這種方案的弊端在於使用的底層數據庫得支持事務
歡迎關注我的公眾號Porce2018哈。之後繼續分享工作中遇到的各種技術問題。
閱讀更多 互聯網那些愛恨情仇 的文章