【CompletableFuture java8 使用场景一】

需求

有一个业务场景如下:对外提供一个查询接口,该接口接收一个指定的超时时间参数 timeout,接口并发执行两个方法,方法一为查询缓存,方法二为http接口查询,http接口的超时时间为timeout。如果在timeout时间内方法二有返回结果,则接口返回方法二的查询结果。如果方法二在指定的超时内没有返回结果,则查询接口返回方法一的执行结果。同时如果方法二如果超时了,需要异步等待300ms,尽可能获取方法二的执行结果,如果有结果就写一条日志,否则丢弃中断http请求。

需求分析

  1. 接口接收超时参数: 接口定义一个timeout参数。
  2. 并发执行查询: 同时执行查询缓存(cacheQuery)和HTTP接口查询(httpQuery)。
  3. 优先返回HTTP查询结果: 如果在timeout时间内httpQuery有结果,返回该结果。
  4. 超时返回缓存结果: 如果httpQuery超时,返回cacheQuery结果。
  5. 记录日志: 如果httpQuery超时,异步等待300ms,记录结果或中断请求。

技术实现

Future 实现方式

Controller

java 复制代码
@RestController
public class QueryController {

    @Autowired
    private QueryService queryService;

    @RequestMapping(value = "/query", method = RequestMethod.GET)
    public ResponseEntity<?> query(@RequestParam("timeout") long timeout) {
        try {
            String result = queryService.queryWithTimeout(timeout);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
        }
    }
}

Service

java 复制代码
@Service
public class QueryService {

    @Autowired
    private CacheQueryComponent cacheQueryComponent;

    @Autowired
    private HttpQueryComponent httpQueryComponent;

    public String queryWithTimeout(long timeout) throws InterruptedException, ExecutionException, TimeoutException {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        try {
            // 提交缓存查询任务
            Future<String> cacheFuture = executor.submit(() -> cacheQueryComponent.query());

            // 提交HTTP查询任务,带有超时设置
            Future<String> httpFuture = executor.submit(() -> httpQueryComponent.query(timeout));

            try {
                // 优先获取HTTP查询结果
                return httpFuture.get(timeout, TimeUnit.MILLISECONDS);
            } catch (TimeoutException e) {
                // HTTP查询超时,返回缓存查询结果
                return cacheFuture.get();
            } finally {
                // 如果HTTP查询超时,异步等待300ms
                if (!httpFuture.isDone()) {
                    executor.submit(() -> {
                        try {
                            String result = httpFuture.get(300, TimeUnit.MILLISECONDS);
                            // 记录日志
                            logResult(result);
                        } catch (Exception ex) {
                            httpFuture.cancel(true); // 中断HTTP请求
                        }
                    });
                }
            }
        } finally {
            executor.shutdown();
        }
    }

    private void logResult(String result) {
        // 日志记录逻辑
        System.out.println("Delayed HTTP query result: " + result);
    }
} 

缓存查询组件

java 复制代码
@Component
public class CacheQueryComponent {
    public String query() {
        try {
	      Thread.sleep(20);
	    } catch (InterruptedException e) {
	      throw new RuntimeException(e);
	    }
	    printLog("cache result");
	    return "cache result";
    }
} 

HTTP查询组件

java 复制代码
@Component
public class HttpQueryComponent {
    public String query() {
        try {
	      Thread.sleep(400);
	    } catch (InterruptedException e) {
	      throw new RuntimeException(e);
	    }
	    printLog("cache result");
	    return "cache result";
    }
} 

CompletableFuture 实现

Service

java 复制代码
@Service
public class QueryService {

    @Autowired
    private CacheQueryComponent cacheQueryComponent;

    @Autowired
    private HttpQueryComponent httpQueryComponent;

    public String queryWithTimeout(long timeout) throws InterruptedException, ExecutionException, TimeoutException {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        try {
            // 创建一个异步任务,从缓存中查询数据
		    CompletableFuture<String> cacheTask = CompletableFuture.supplyAsync(() -> {
		      printLog("queryFromCache");
		      return cacheQueryComponent.query();
		    });
		
		    // 创建一个异步任务,从HTTP中查询数据
		    CompletableFuture<String> httpTask = CompletableFuture.supplyAsync(() -> {
		      printLog("queryFromHttp");
		      return httpQueryComponent.query();
		    });
		
		    // 如果HTTP任务在200ms内没有完成,就取消任务
		    executor.schedule(() -> {
		      if (!httpTask.isDone()) {
		        try {
		          String httpResult = httpTask.get(300, TimeUnit.MILLISECONDS);
		          printLog("HTTP task completed after 200ms: " + httpResult);
		        } catch (Exception e) {
		          printLog("HTTP task cancel");
		          httpTask.cancel(true);
		        }
		      }
		    }, 200, TimeUnit.MILLISECONDS);
		
		    // 创建一个延迟200ms的任务
		    CompletableFuture<Void> delay = CompletableFuture.runAsync(() -> {
		      try {
		        Thread.sleep(200);
		      } catch (InterruptedException e) {
		        throw new RuntimeException(e);
		      }
		    });
		
		    // 如果HTTP任务完成,就返回HTTP任务的结果,否则返回缓存任务的结果
		    return delay.thenApply(v -> {
		      if (httpTask.isDone()) {
		        return httpTask.join();
		      } else {
		        return cacheTask.join();
		      }
		    }).join();
        } finally {
            executor.shutdown();
        }
    }

    private void logResult(String result) {
        // 日志记录逻辑
        System.out.println("Delayed HTTP query result: " + result);
    }
} 
相关推荐
麦兜*1 小时前
Spring Boot 企业级动态权限全栈深度解决方案,设计思路,代码分析
java·spring boot·后端·spring·spring cloud·性能优化·springcloud
ruan1145142 小时前
MySQL4种隔离级别
java·开发语言·mysql
Hellyc6 小时前
基于模板设计模式开发优惠券推送功能以及对过期优惠卷进行定时清理
java·数据库·设计模式·rocketmq
lifallen6 小时前
Paimon LSM Tree Compaction 策略
java·大数据·数据结构·数据库·算法·lsm-tree
hdsoft_huge6 小时前
SpringBoot 与 JPA 整合全解析:架构优势、应用场景、集成指南与最佳实践
java·spring boot·架构
百锦再7 小时前
详细解析 .NET 依赖注入的三种生命周期模式
java·开发语言·.net·di·注入·模式·依赖
程序员的世界你不懂7 小时前
基于Java+Maven+Testng+Selenium+Log4j+Allure+Jenkins搭建一个WebUI自动化框架(2)对框架加入业务逻辑层
java·selenium·maven
有没有没有重复的名字8 小时前
线程安全的单例模式与读者写者问题
java·开发语言·单例模式
张先shen9 小时前
Elasticsearch RESTful API入门:基础搜索与查询DSL
大数据·spring boot·elasticsearch·搜索引擎·全文检索·restful
慕木兮人可9 小时前
Docker部署MySQL镜像
spring boot·后端·mysql·docker·ecs服务器