Java代码优化实战 您所在的位置:网站首页 合并码安全吗 Java代码优化实战

Java代码优化实战

2023-03-27 01:31| 来源: 网络整理| 查看: 265

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

在进行系统设计的时候,不知道大家是否也遇到过这个问题,大量请求造成数据库压力过大。本文将介绍在高并环境下,上面这个问题的一种简单应对思路。

首先思考一个场景,在高并发的系统中,在每秒内有大量的请求要访问数据库,如果不考虑缓存,怎么才能够处理降低数据库压力。有的同学可能会说这多简单啊,增加带宽,加内存提升服务器性能。

如果不用这些方法呢?那么就可以用到请求合并的方法,将一段时间内的请求进行合并,然后统一提交查询数据库,能够做到将几十个甚至上百个查询进行批量处理。

当然,这么做也有一个前提,就是这些请求对实时性的要求不能太高。在这个条件下,牺牲一定的处理时间,来减少网络连接数,这么处理是一种性价比非常高的方法。

首先我们模拟一个场景,在不进行合并请求的情况下进行1000次请求,使用Postman进行请求测试,并使用Druid连接池进行数据库的监控:

可以看出,实际进行了1000次数据库的访问。在超高流量的情况下,这种访问方式是十分危险的,所以减少数据库的访问就成了当务之急。

再看看之前提到的请求合并,实现起来有这么几个问题需要解决:

1、以什么粒度作为合并请求的规则:

这里推荐按照时间粒度去合并请求,不推荐按照请求数量达到一定值再进行合并是因为有可能一段时间内请求数量比较少,达不到阈值则无法执行,造成早到达的请求等待非常长的时间。

Java中的ScheduledExecutorService提供了定时调度机制,且本身实现了ExecutorService接口,所以本身也支持线程池的所有功能。

2、如何存放一段时间的请求:

存放请求的方式就比较多了,我们知道,在高并发系统的设计中,消息队列被普遍应用于解耦,使用消息队列存放请求是非常合适的做法。由于我们这里是单机环境,能够保证线程安全的阻塞队列LinkedBlockingQueue就能简单实现我们的需求。

3、如何将请求的结果返回给请求

自从JAVA 1.5以后引入了Future接口,用于处理异步调用和并发事务。Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作。 简单的说,我们可以用它来接收线程的执行结果。

好了,请求的合并、执行、返回三大步骤都梳理清楚了,让我们看看具体怎么实现。

@Service public class BatchQueryService { //队列用来存放请求 private LinkedBlockingQueue queue = new LinkedBlockingQueue(); @Autowired ItemService queryItemService; //封装请求 class Request { String code; CompletableFuture future; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public CompletableFuture getFuture() { return future; } public void setFuture(CompletableFuture future) { this.future = future; } } @PostConstruct public void init() { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); scheduledExecutorService.scheduleAtFixedRate(() -> { int size = queue.size(); if (size == 0) return; List requests = new ArrayList(size); for (int i = 0; i < size; i++) { Request request = queue.poll(); requests.add(request); } System.out.println("批量处理了" + size + "条请求"); List codes = new ArrayList(); for (Request request : requests) { codes.add(request.getCode()); } List responses = queryItemService.queryByCodes(codes); //结果集完成--> 把请求分发给每一个具体的Request Map responseMap = new HashMap(); for (Map response : responses) { String code = response.get("code").toString(); responseMap.put(code, response); } //返回请求 for (Request request : requests) { Map result = responseMap.get(request.getCode()); request.future.complete(result); } }, 0, 200, TimeUnit.MILLISECONDS); } //根据code进行单个查询 public Map queryItem(String code) { Request request = new Request(); request.setCode(code); CompletableFuture future = new CompletableFuture(); request.setFuture(future); queue.add(request); try { return future.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } return null; } } 复制代码

使用1000个线程对请求合并方法进行测试:

@ResponseBody @RequestMapping("/batchQuery") public String batchQuery(){ Thread thread[]=new Thread[1000]; for (int i = 0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有