平常開發過程中,如果涉及到RPC調用,對於服務調用方和服務提供方,都是可以設置接口超時時間的。以調用方為例,調用方需要調用遠程的一個接口,為了保證服務的質量,一般會設置調用接口的超時時間,比如將調用接口的超時時間設置為1秒,當調用遠程接口后,經過1秒還未拿到結果,那么就認為是超時了,調用方就不會繼續等待服務提供方返回結果,而是直接拋出一個SocketTimeOutException。
其實不僅僅是RPC接口調用需要設置超時時間,數據庫、緩存這些一樣的,一般都會設置超時時間,不能讓程序無休止的等待下去。
那么問題來了!!應用或者說框架,是如何設置超時時間的呢?我們設置超時時間為1秒,那么程序是怎么保證1秒后就停止調用了呢?這也就是本文要介紹的,接口調用設置超時時間的原理。
本文將以設置調用數據庫超時為例,介紹設置超時時間是怎么實現的,明白這個原理后,其他比如RPC調用、緩存調用的超時原理也就明白。
CallUtils-帶有超時的執行工具類
下面是一個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-將調用數據庫
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-耗時的數據庫操作
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方法必然會超時。
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。
原文鏈接:https://www.cnblogs.com/-beyond/p/13204252.html
