JMicro是本人開發的基於Java實現的微服務框架,當前正式版本為0.0.3,並已釋出到maven中央倉庫。專案原始碼github:
https://github.com/mynewworldyyl/jmicro,Demo原始碼:
https://github.com/mynewworldyyl/jmicro_demos/tree/0.0.3-release
Maven地址:
https://mvnrepository.com/search?q=cn.jmicro
Demo:http://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中,在執行測試配置如下圖所示:
引數:
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秒,所以每個請求都肯定會超時!試著調整服務端的超時時間,讓一部份請求成功或重試成功。
除了可以在終端看日誌外,還可以通過日誌檔案看,日誌檔案的位置配置在log4j配置檔案中,預設為當前工作目錄下的logs/work.log檔案。如下圖:
超時重試存問題
必須要說明的是並非全部服務都需要重試或都能接受重試,只有冪等服務才能接受重試。所謂冪等是:以同一引數呼叫多次得到的效果相同,則稱服務是冪等服務。比如x++這個操作,每次呼叫的結果都不相同,則不是冪等,x=y+1則是冪等操作。結合業務場景,在技術實現中,往往通過去重的方式讓服務達到冪等的要求,這樣就可以讓服務接受重試,提高服務的可用性和一致性要求。
另一方面,在超時重試失敗率達到一定量後,比如30%的重試都失敗,則說明我們的服務很有可能出現了問題,此時如再做正常的重試,很有可能會壓垮整個服務系統(雪崩),這個又需要服務熔斷和降級來避免,下回分享JMicro熔斷和降級等內容。
如果你覺得JMicro對你有幫助,麻煩移步到Github上給個星!