【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);
    }
} 
相关推荐
BillKu4 分钟前
Java + Spring Boot + Mybatis 插入数据后,获取自增 id 的方法
java·tomcat·mybatis
全栈凯哥5 分钟前
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
java·算法·leetcode·链表
chxii6 分钟前
12.7Swing控件6 JList
java
全栈凯哥8 分钟前
Java详解LeetCode 热题 100(27):LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)详解
java·算法·leetcode·链表
YuTaoShao8 分钟前
Java八股文——集合「List篇」
java·开发语言·list
PypYCCcccCc13 分钟前
支付系统架构图
java·网络·金融·系统架构
华科云商xiao徐34 分钟前
Java HttpClient实现简单网络爬虫
java·爬虫
扎瓦1 小时前
ThreadLocal 线程变量
java·后端
BillKu1 小时前
Java后端检查空条件查询
java·开发语言
jackson凌1 小时前
【Java学习笔记】String类(重点)
java·笔记·学习