页面查询多项数据组合的线程池设计

这篇具有很好参考价值的文章主要介绍了页面查询多项数据组合的线程池设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

我们应对并发场景时一般会采用下面方式去预估线程池的线程数量,比如QPS需求是1000,平均每个任务需要执行的时间是t秒,那么我们需要的线程数是t * 1000。

但是在一些情况下,这个t是不好估算的,即便是估算出来了,在实际的线程环境上也需要进行验证和微调。比如在本文所阐述分页查询的数据项组合场景中。

1、数据组合依赖不同的上游接接口, 它们的响应时间参差不齐,甚至差距还非常大。有些接口支持批量查询而另一些则不支持批量查询。有些接口因为性能问题还需要考虑降级和平滑方案。

2、为了提升用户体验,这里的查询设计了动态列,因此每一次访问所需要组合的数据项和数量也是不同的。

因此这里如果需要估算出一个合理的t是不太现实的。

方案

一种可动态调节的策略,根据监控的反馈对线程池进行微调。整体设计分为装配逻辑线程池封装设计。

1、装配逻辑

查询结果,拆分分片(水平拆分),并行装配(垂直拆分),获得装配项列表(动态列), 并行装配每一项。

2、线程池封装

可调节的核心线程数、最大线程数、线程保持时间,队列大小,提交任务重试等待时间,提交任务重试次数。 固定异常拒绝策略。

调节参数:

字段 名称 说明
corePoolSize 核心线程数 参考线程池定义
maximumPoolSize 最大线程数 参考线程池定义
keepAliveTime 线程存活时间 参考线程池定义
queueSize 队列长度 参考线程池定义
resubmitSleepMillis 提交任务重试等待时间 添加任务被拒绝后重试时的等待时间
resubmitTimes 提交任务重试次数 添加任务被拒绝后重试添加的最大次数
    @Data
	private static class PoolPolicy {

		/** 核心线程数 */
		private Integer corePoolSize;

		/** 最大线程数 */
		private Integer maximumPoolSize;

		/** 线程存活时间 */
		private Integer keepAliveTime;

		/** 队列容量 */
		private Integer queueSize;

		/** 重试等待时间 */
		private Long resubmitSleepMillis;

		/** 重试次数 */
		private Integer resubmitTimes;
	}

创建线程池:

线程池的创建考虑了动态的需求,满足根据压测结果进行微调的要求。首先缓存旧的线程池后再创建新的线程,当新的线程池创建成功后再去关闭旧的线程池。保证在这个替换过程中不影响正在执行的业务。线程池使用了中断策略,用户可以及时感知到系统繁忙并保证了系统资源占用的安全。

public void reloadThreadPool(PoolPolicy poolPolicy) {
    if (poolPolicy == null) {
        throw new RuntimeException("The thread pool policy cannot be empty.");
    }
    if (poolPolicy.getCorePoolSize() == null) {
        poolPolicy.setCorePoolSize(0);
    }
    if (poolPolicy.getMaximumPoolSize() == null) {
        poolPolicy.setMaximumPoolSize(Runtime.getRuntime().availableProcessors() + 1);
    }
    if (poolPolicy.getKeepAliveTime() == null) {
        poolPolicy.setKeepAliveTime(60);
    }
    if (poolPolicy.getQueueSize() == null) {
        poolPolicy.setQueueSize(Runtime.getRuntime().availableProcessors() + 1);
    }
    if (poolPolicy.getResubmitSleepMillis() == null) {
        poolPolicy.setResubmitSleepMillis(200L);
    }
    if (poolPolicy.getResubmitTimes() == null) {
        poolPolicy.setResubmitTimes(5);
    }
    // - 线程池策略没有变化直接返回已有线程池。
    ExecutorService original = this.executorService;
    this.executorService = new ThreadPoolExecutor(
            poolPolicy.getCorePoolSize(),
            poolPolicy.getMaximumPoolSize(),
            poolPolicy.getKeepAliveTime(), TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(poolPolicy.getQueueSize()),
            new ThreadFactoryBuilder().setNameFormat(threadNamePrefix + "-%d").setDaemon(true).build(),
            new ThreadPoolExecutor.AbortPolicy());
    this.poolPolicy = poolPolicy;
    if (original != null) {
        original.shutdownNow();
    }
}

任务提交:

线程池封装对象中使用的线程池拒绝策略是AbortPolicy,因此在线程数和阻塞队列到达上限后会触发异常。另外在这里为了保证提交的成功率利用重试策略实现了一定程度的延迟处理,具体场景中可以结合业务特点进行适当的调节和配置。

public <T> Future<T> submit(Callable<T> task) {
    RejectedExecutionException exception = null;
    Future<T> future = null;
    for (int i = 0; i < this.poolPolicy.getResubmitTimes(); i++) {
        try {
            // - 添加任务
            future = this.executorService.submit(task);
            exception = null;
            break;
        } catch (RejectedExecutionException e) {
            exception = e;
            this.theadSleep(this.poolPolicy.getResubmitSleepMillis());
        }
    }
    if (exception != null) {
        throw exception;
    }
    return future;
}

监控:

1、submit提交的监控

见代码中的「监控点①」,在submit方法中添加监控点,监控key的需要添线程池封装对象的线程名称前缀,用于区分具体的线程池对象。

「监控点①」用于监控添加任务的动作是否正常,以便对线程池对象及策略参数进行微调。

public <T> Future<T> submit(Callable<T> task) {
    // - 监控点①
    CallerInfo callerInfo = Profiler.registerInfo(UmpConstant.THREAD_POOL_WAP + threadNamePrefix,
                UmpConstant.APP_NAME,
                UmpConstant.UMP_DISABLE_HEART,
                UmpConstant.UMP_ENABLE_TP);
    RejectedExecutionException exception = null;
    Future<T> future = null;
    for (int i = 0; i < this.poolPolicy.getResubmitTimes(); i++) {
        try {
            // - 添加任务
            future = this.executorService.submit(task);
            exception = null;
            break;
        } catch (RejectedExecutionException e) {
            exception = e;
            this.theadSleep(this.poolPolicy.getResubmitSleepMillis());
        }
    }
    if (exception != null) {
        // - 监控点①
        Profiler.functionError(callerInfo);
        throw exception;
    }
    // - 监控点①
    Profiler.registerInfoEnd(callerInfo);
    return future;
}

2、线程池并行任务

见代码的「监控点②」,分别在添加任务和任务完成后。

「监控点②」实时统计在线程中执行的总任务数量,用于评估线程池的任务的数量的满载水平。

/** 任务并行数量统计 */
private AtomicInteger parallelTaskCount = new AtomicInteger(0);

public <T> Future<T> submit(Callable<T> task) {
    RejectedExecutionException exception = null;
    Future<T> future = null;
    for (int i = 0; i < this.poolPolicy.getResubmitTimes(); i++) {
        try {
            // - 添加任务
            future = this.executorService.submit(()-> {
                T rst = task.call();
                // - 监控点②
                log.info("{} - Parallel task count {}", this.threadNamePrefix,  this.parallelTaskCount.decrementAndGet());
                return rst;
            });
            // - 监控点②
            log.info("{} + Parallel task count {}", this.threadNamePrefix,  this.parallelTaskCount.incrementAndGet());
            exception = null;
            break;
        } catch (RejectedExecutionException e) {
            exception = e;
            this.theadSleep(this.poolPolicy.getResubmitSleepMillis());
        }
    }
    if (exception != null) {
        throw exception;
    }
    return future;
}

3、调节

线程池封装对象策略的调节时机

1)上线前基于流量预估的压测阶段;

2)上线后跟进监控数据和线程池中任务的满载水平进行人工微调,也可以通过JOB在指定的时间自动调整;

3)大促前依据往期大促峰值来调高相关参数。

线程池封装对象策略的调节经验

1)访问时长要求较低时,我们可以考虑调小线程数和阻塞队列,适当调大提交任务重试等待时间和次数,以便降低资源占用。

2)访问时长要求较高时,就需要调大线程数并保证相对较小的阻塞队列,调小提交任务的重试等待时间和次数甚至分别调成0和1(即关闭重试提交逻辑)。

作者:京东零售 王文明

来源:京东云开发者社区 转载请注明来源文章来源地址https://www.toymoban.com/news/detail-711518.html

到了这里,关于页面查询多项数据组合的线程池设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • mongo——or查询包含多项模糊匹配

    MongoDB OR 条件语句使用了  $or MongoDB 模糊查询使用了  $regex 两者整合在一起,语法格式如下:

    2024年02月12日
    浏览(29)
  • JDK 21预告:虚拟线程正式发布及十多项新特性

    Java 21进入发布候选阶段,其中包括15个最终特性,包括虚拟线程、分代Z垃圾收集器和密钥封装机制API。 JDK21计划于9月19日作为Oracle标准Java实现的下一个LTS版本发布,已进入发布候选(RC)阶段。Java 21将具有15个新特性,之前提议的第16个特性实验性Shenandoah垃圾收集器已在6月被舍

    2024年02月10日
    浏览(46)
  • java查询数据库百万条数据,优化之:多线程+数据库

    今天去面试时hr问了个关于大量数据查询的问题。 面试官:“我们公司是做数据分析的,每次需要从数据库中查询100万条数据进行分析,该接口不能用分页(不限制具体怎么实现),请问怎么优化sql或者java代码呢??” 如果用普通查询需要5分多分钟才查询完毕,所以我们用

    2024年02月15日
    浏览(53)
  • 编写jsp页面实现对数据库表的查询

    在本次作业中,我将eclipse与sql server连接起来,并在SSMS中进行插入数据,从而编写jsp页面实现对数据库表的查询。 一、连接数据库 按照(4条消息) eclipse连接SQL server数据库_eclipse如何连接数据库_shexianyu的博客-CSDN博客 教程中的步骤,最初只得到了加载驱动成功,连接数据库失

    2024年02月07日
    浏览(40)
  • Elasticsearch 中 bool组合查询(must 和 should 组合)

    需求 查询考试成绩是 60分 或 90分 的 女生 。 所以查询条件应该这么写 sex == \\\'女\\\' ( score == 60 || score ==90 );  先看下bool的语法 在ES中的实现 方式一: 在bool查询的must 中再嵌套一层bool来做should过滤。 方式二: 使用 minimum_should_match 选项 ,至少匹配一项should子句。 GET stu/_search

    2023年04月09日
    浏览(36)
  • vue设置页面背景及背景图片

    本地静态图片  cdn图片 设置背景色 实战-PC web登录页 实战-小程序登录页

    2024年02月12日
    浏览(58)
  • Elasticsearch 组合查询的使用

    这里介绍Elasticsearch 中的组合查询的使用细节,Elasticsearch支持类似于在SQL中使用AND、OR以及NOT的运算符,可以通过组合嵌套这些条件进行复杂的数据筛选。 数据准备 复用上篇文章的mapping和数据供这里的demo使用。 mapping: 数据: filter的用法 在使用filter查询的时候,会使用一

    2024年02月08日
    浏览(30)
  • 【MySQL】组合查询

    目录 一、组合查询 1.创建组合查询 2.union规则 3.包含或取消重复的行 4.对组合查询结果排序 一、组合查询         多数SQL查询都只包含从一个或多个表中返回数据的单条SELECT语句。MySQL也允许执行多个查询(多条SELECT语句),并将结果作为单个查询结果返回。 这些组合查询

    2024年02月11日
    浏览(26)
  • ES组合查询-Boolean Query

    BooleanQuery可以将多个查询语句组合在一起。下面是一个基础的模板: 上面是从官网搬过来的官网地址 must 中的语句是必须满足的,会影响最终文档的得分 filter 中的语句是必须满足的,但是不会影响最终的得分 must_not 中的语句是必须不满足的, 不影响最终的得分 should 中的语

    2023年04月20日
    浏览(36)
  • ElasticSearch系列 - SpringBoot整合ES:组合多个查询条件 bool 查询

    01. ElasticSearch 布尔查询是什么? 在实际应用中,我们很有可能会查询多个值或字段。 一个 bool 查询由三部分组成: must:所有的语句都必须(must) 匹配,与 AND 等价。 must_not:所有的语句都不能(must not)匹配,与 NOT 等价。 should:至少有一个语句要匹配,与 OR 等价。 02.

    2023年04月08日
    浏览(74)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包