接口调用超时的实现原理

发表于:2020-7-01 08:49

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:寻觅beyond    来源:博客园

分享:
  平常开发过程中,如果涉及到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。

     本文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号