JMicro微服務之超時&重試
JMicro是本人開發的基於Java實現的微服務框架,當前正式版本為0.0.3,並已發布到maven中央倉庫。項目源碼github:
//github.com/mynewworldyyl/jmicro,Demo源碼:
//github.com/mynewworldyyl/jmicro_demos/tree/0.0.3-release
Maven地址:
//mvnrepository.com/search?q=cn.jmicro
Demo://jmicro.cn 帳號:test00 密碼:1
超時&重試
客戶端調用服務時,由於網路抖動,服務故障等原因,不能及時響應給客戶端,往往需要重試一兩次才能成功,而不應該在發生超時時就直接給調用者失敗結果,這樣不友好,往往讓用戶懷疑我們服務的穩定性和可用性。
比如平時訪問某個網站,頁面出現長時間讀條載入的情況,我們往往按一下刷新按鈕就能很快成功打開頁面。在微服務調用中,往往由服務調用者在收到失敗響應後,根據業務場景發起合理的重試請求。JMicro在框架級別提供這種配置並且由平台自動做重試操作,提高服務調用的成功率。
JMicro超時重試配置
在前面JMicro微服務Helloworld基基礎上,通過配置SMethod註解屬性達到超時重試的目的。程式碼如下:
@Component @Service(version="0.0.1") public class TxShopServiceImpl implements ITxShopService { private final static Logger logger = LoggerFactory.getLogger(TxShopServiceImpl.class); private Random ran = new Random(System.currentTimeMillis()); @Override @SMethod(timeout=1000,retryCnt=3,retryInterval=100) public Resp<Boolean> buy(int goodId,int num) { Resp<Boolean> r = new Resp<>(Resp.CODE_SUCCESS,true); int st = ran.nextInt(6); if(st > 0) { logger.info("Sleep time: " + st+" Seconds idx: " +num); try { TimeUnit.SECONDS.sleep(30);//睡30秒,讓客戶端超時並重試,只要為了驗證超時重試邏輯 TimeUnit.SECONDS.sleep(ran.nextInt(5));//睡30秒,隨機睡0到5秒,測試並發表求時部份請求超時 } catch (InterruptedException e) { logger.error("",e); } } logger.info("Success return idx:" +num); return r; } }
在buy服務方法配置SMethod註解,並設置timeout,retryInterval,retryCnt三個屬性即可。
timeout:表示超時時間,單位毫秒,小於或等於0表示不啟用超時,大於0表示啟用超時;
retryInterval:表示超時後,重試間隔,單位毫秒,如果服務超時後,立即發起重試請求,很可能還是失敗,所以要稍微等一會,讓服務方快取一下,再發起重試,往往成功率會更高。
retryCnt:表示重試次數,不包括第一次,如設置為3,如果第一次超時,第2次成功,則總共請求了2次,同理,全部超時時,總共請求了4次,4次超時後,RPC失敗,拋出TimeoutException異常。
配置啟動參數及入口類
除了可以按照上一節用的命令方式運行測試 ,還可以將項目導入Eclipse或Idea中運行Debug測試。
比如Eclipse下,將源碼以Maven項目導入Eclipse中,在運行測試配置如下圖所示:
Eclipse入口類
參數:
參數及Agent配置
VM參數為Maven倉庫下的Agent類,需要改為你自己機器的路徑,此Jar包用於在我們啟動java虛擬機時,對我們的實體類(SO註解的類)做序列化增強,這樣我們RPC請求就可以飛起來。
-DsysLogLevel=4 日誌級別,用於日誌監控,需要日誌服務支援,後面專門章節說明;
-DclientId=25500 以什麼帳號啟動我們的服務實例,涉及帳號許可權及多租戶隔離;
-DadminClientId=0 超級帳號,用於申請超級帳號許可權
-Dlog4j.configuration=../../log4j.xml Log4j日誌配置文件
-Dpwd=1 clientId帳號對應的密碼
以上參數除了log4j.configuration外,其他都可以先不用管,後面用專門章節說明
通過以上配置後,就可以像平時啟動Java程式那樣啟動服務了。
配置並啟動客戶端
同樣方式配置並啟動客戶端,客戶端以固定頻率調用服務端buy方法,程式碼如下:
public void afterInit(IObjectFactory of) { AtomicInteger ai = new AtomicInteger(0); //為了不Block主執行緒,我們在此啟動一個執行緒每間隔3秒調用一次商店提供的購買方法 new Thread(()->{ for(;true;) { try { //調用商店服務 int cnt = ai.getAndIncrement(); Resp<Boolean> rst = shop.buy(1, cnt); if(rst == null || rst.getCode() != Resp.CODE_SUCCESS) { if(rst != null) { //系統組錯誤 logger.info(rst.getMsg()+"," + rst.getCode()+",idx:"+cnt); } else { logger.info("Timeout:" + cnt); } }else if(rst.getData()) { //業務購買失敗 logger.info("Success idx: " + cnt); }else { //成功 logger.info("Fulure"+rst.getMsg()+"," + rst.getCode()+",idx:"+cnt); } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { logger.error("",e); } }catch(Throwable e) { logger.error(e.getMessage()); } } }).start(); }
查看客戶端輸出日誌,重試3次後,最終報超時失敗,因為服務端buy方法睡了30秒,所以每個請求都肯定會超時!試著調整服務端的超時時間,讓一部份請求成功或重試成功。
重試3次,最終超時失敗
除了可以在終端看日誌外,還可以通過日誌文件看,日誌文件的位置配置在log4j配置文件中,默認為當前工作目錄下的logs/work.log文件。如下圖:
日誌文件 位置
超時重試存問題
必須要說明的是並非全部服務都需要重試或都能接受重試,只有冪等服務才能接受重試。所謂冪等是:以同一參數調用多次得到的效果相同,則稱服務是冪等服務。比如x++這個操作,每次調用的結果都不相同,則不是冪等,x=y+1則是冪等操作。結合業務場景,在技術實現中,往往通過去重的方式讓服務達到冪等的要求,這樣就可以讓服務接受重試,提高服務的可用性和一致性要求。
另一方面,在超時重試失敗率達到一定量後,比如30%的重試都失敗,則說明我們的服務很有可能出現了問題,此時如再做正常的重試,很有可能會壓垮整個服務系統(雪崩),這個又需要服務熔斷和降級來避免,下回分享JMicro熔斷和降級等內容。
如果你覺得JMicro對你有幫助,麻煩移步到Github上給個星!