日常開發過程當中,若是涉及到RPC調用,對於服務調用方和服務提供方,都是能夠設置接口超時時間的。以調用方爲例,調用方須要調用遠程的一個接口,爲了保證服務的質量,通常會設置調用接口的超時時間,好比將調用接口的超時時間設置爲1秒,當調用遠程接口後,通過1秒還未拿到結果,那麼就認爲是超時了,調用方就不會繼續等待服務提供方返回結果,而是直接拋出一個SocketTimeOutException。java
其實不單單是RPC接口調用須要設置超時時間,數據庫、緩存這些同樣的,通常都會設置超時時間,不能讓程序無休止的等待下去。數據庫
那麼問題來了!!應用或者說框架,是如何設置超時時間的呢?咱們設置超時時間爲1秒,那麼程序是怎麼保證1秒後就中止調用了呢?這也就是本文要介紹的,接口調用設置超時時間的原理。緩存
本文將以設置調用數據庫超時爲例,介紹設置超時時間是怎麼實現的,明白這個原理後,其餘好比RPC調用、緩存調用的超時原理也就明白。框架
下面是一個CallUtils,包含一個線程池和execute方法,execute方法中默認超時時間爲1秒。工具
package cn.ganlixin.util; import java.util.concurrent.*; /** * 帶有超時的任務執行器 */ public class CallUtils { /** * 用於執行任務的線程池 */ private static ExecutorService executorService = Executors.newFixedThreadPool(5); /** * 執行任務,而且超時時間爲1秒 * * @param callable 須要執行的任務 * @param <T> 任務執行完畢後返回值類型 * @return 任務結果 */ public static <T> T execute(Callable<T> callable) { // 提交任務 Future<T> future = executorService.submit(callable); try { return future.get(1, TimeUnit.SECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { e.printStackTrace(); } return null; } }
UserService使用CallUtils來執行數據庫操做,由於CallUtils設置有調用超時:測試
package cn.ganlixin.service; import cn.ganlixin.dao.UserDAO; import cn.ganlixin.model.User; import cn.ganlixin.util.CallUtils; import java.util.List; public class UserService { private UserDAO userDAO = new UserDAO(); public List<User> getAllUser() { return CallUtils.execute(() -> userDAO.getAllUserFromDB()); } }
userDAO中,做爲測試,只提供了一個getAllUserFromDB方法,休眠2秒來模擬數據庫操做,由於UserService使用CallUtils來調用該方法,CallUtils的超時時間爲1秒,因此該數據庫操做一定會超時:線程
package cn.ganlixin.dao; import cn.ganlixin.model.User; import java.util.List; import java.util.concurrent.TimeUnit; public class UserDAO { /** * 從DB獲取全量user列表 */ public List<User> getAllUserFromDB() { try { // 模擬數據庫操做,耗時2秒 TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } return null; } }
建立測試類,測試UserService的getAllUser方法,由於getAllUser方法中使用CallUtils來調用數據庫操做,CallUtils的超時時間爲1秒,而數據庫操做須要2秒,因此getAllUser方法必然會超時。blog
package cn.ganlixin.test; import cn.ganlixin.service.UserService; import org.junit.Test; public class TestUserService { public UserService userService = new UserService(); @Test public void testGetAllUser() { userService.getAllUser(); } }
執行測試,輸出以下:接口
本文演示了接口超時調用的原理,實現接口調用超時,無非是經過將任務提交到線程池後,使用future.get,設置超時時間便可。開發
上面的代碼不少細節都不太規範,好比涉及到數據庫的超時,應該是數據庫鏈接池的超時配置,而我在演示時是直接使用CallUtils來替代了,可是明白這個原理就OK。