JMicro微服務之超時&重試

JMICRO發表於2021-06-01

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中,在執行測試配置如下圖所示:

JMicro微服務之超時&重試

Eclipse入口類

引數:

JMicro微服務之超時&重試

引數及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秒,所以每個請求都肯定會超時!試著調整服務端的超時時間,讓一部份請求成功或重試成功。

JMicro微服務之超時&重試

重試3次,最終超時失敗

除了可以在終端看日誌外,還可以通過日誌檔案看,日誌檔案的位置配置在log4j配置檔案中,預設為當前工作目錄下的logs/work.log檔案。如下圖:

JMicro微服務之超時&重試

日誌檔案 位置

超時重試存問題

必須要說明的是並非全部服務都需要重試或都能接受重試,只有冪等服務才能接受重試。所謂冪等是:以同一引數呼叫多次得到的效果相同,則稱服務是冪等服務。比如x++這個操作,每次呼叫的結果都不相同,則不是冪等,x=y+1則是冪等操作。結合業務場景,在技術實現中,往往通過去重的方式讓服務達到冪等的要求,這樣就可以讓服務接受重試,提高服務的可用性和一致性要求。

另一方面,在超時重試失敗率達到一定量後,比如30%的重試都失敗,則說明我們的服務很有可能出現了問題,此時如再做正常的重試,很有可能會壓垮整個服務系統(雪崩),這個又需要服務熔斷和降級來避免,下回分享JMicro熔斷和降級等內容。

如果你覺得JMicro對你有幫助,麻煩移步到Github上給個星!

相關文章