【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);
    }
} 
相关推荐
纠结哥_Shrek26 分钟前
Java 有很多常用的库
java·开发语言
爱是小小的癌1 小时前
Java-数据结构-优先级队列(堆)
java·前端·数据结构
天乐敲代码1 小时前
JAVASE入门十五脚-网络TCP,UDP,,Lambda
java
2501_903238652 小时前
自定义登录页面的Spring Security实践
java·后端·spring·个人开发
飞翔的佩奇3 小时前
Java项目: 基于SpringBoot+mybatis+maven+mysql实现的图书管理系统(含源码+数据库+答辩PPT+毕业论文)
java·数据库·spring boot·mysql·spring·毕业设计·图书管理
一 乐4 小时前
基于vue船运物流管理系统设计与实现(源码+数据库+文档)
前端·javascript·数据库·vue.js·spring boot·后端·船运系统
jerry6095 小时前
注解(Annotation)
java·数据库·sql
Future_yzx5 小时前
Java Web的发展史与SpringMVC入门学习(SpringMVC框架入门案例)
java·前端·学习
辞半夏丶北笙6 小时前
最近最少使用算法(LRU最近最少使用)缓存替换算法
java·算法·缓存
lwprain6 小时前
springboot 2.7.6 security mysql redis jwt配置例子
spring boot·redis·mysql