项目优化(异步化)

news2024/11/26 23:42:35

项目优化(异步化)

1. 认识异步化

1.1 同步与异步

  • 同步:一件事情做完,再做另外一件事情,不能同时进行其他的任务。
  • 异步:不用等一件事故完,就可以做另外一件事情。等第一件事完成时,可以收到一个通知,通知你这件事做好了,你可以再进行后续处理。

1.2 标准异步化的业务流程 ⭐

  1. 当用户要进行耗时很长的操作时,如点击提交后,不需要在界面长时间的等待,而是应该把这个任务保存到数据库中记录下来

  2. 用户要执行新任务时:

    1. 任务提交成功:

      • 程序还有多余的空闲线程时,可以立刻去执行这个任务。

      • 程序的线程都在繁忙时,无法继续处理,那就放到等待队列里。

    2. 任务提交失败:比如程序所有线程都在忙,任务队列满了

      • 拒绝掉这个任务,再也不去执行。

      • 通过保存到数据库中的记录来看到提交失败的任务,并且在程序空闲的时候,可以把任务从数据库中回调到程序里,再次去执行此任务。3

  3. 程序(线程)从任务队列中取出任务依次执行,每完成一件事情要修改一下的任务的状态。

  4. 用户可以查询任务的执行状态,或者在任务执行成功或失败时能得到通知(发邮件、系统消息提示、短信),从而优化体验。

  5. 如果我们要执行的任务非常复杂,包含很多环节,在每一个小任务完成时,要在程序(数据库中)记录一下任务的执行状态(进度)。

2. 线程池

  1. 线程池是什么:线程池是一种并发编程技术,用于优化多线程应用程序的性能和稳定性。它可以在应用程序启动时创建一组可重用的线程,并将工作任务分配给这些线程,以避免重复地创建和销毁线程,从而提高应用程序的吞吐量、响应时间和资源利用率。

  2. 线程池优点:

    1. 减少了线程的创建和销毁开销,提高了性能和效率。

    2. 避免了线程数量过多而导致的系统资源耗尽和线程调度开销增大的问题。

    3. 允许调整线程池大小,以满足不同应用程序的需求。

    4. 可以提高代码的可维护性和可重用性,避免了线程相关的错误,使得代码更加健壮和可靠。

  3. 线程池的作用 :帮助你轻松管理线程、协调任务的执行过程

  4. 线程池的实现:(不用自己写)

    1. 如果是在Spring中,可以用ThreadPoolTaskExecutor配合@Async注解来实现。(不太建议:进行了封装)

    2. JUC线程池的实现方式(JUC并发编程包,中的ThreadPoolExecutor来实现非常灵活地自定义线程池。)

      1. 创建配置线程池类

        /**
         * 配置线程池类
         * 可以在yml文件中写配置,实现自动注入
         */
        @Configuration
        public class ThreadPoolExecutorConfig {
        
            /**
             * 线程池的实现类
             * @return
             */
            @Bean
            public ThreadPoolExecutor threadPoolExecutor(){
                ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor();
                return threadPoolExecutor;
            }
        }
        
      2. 参数解释

        参数根据实际场景,测试调整,不断优化

        根据BI系统来说,线程数量要配置AI的能力(AI能力是瓶颈),ai支持4个线程,允许20个任务排队(参数根据条件调整)

        资源隔离策略:不同的程度的任务,分为不同的队列,比如VIP一个队列,普通用户一个队列。

        public ThreadPoolExecutor(int corePoolSize,//核心线程数:正常情况下,系统能同时工作的数量,处于随时就绪的状态
                                      int maximumPoolSize,//最大线程数,极限情况下,线程池有多少个线程
                                      long keepAliveTime,//空闲线程存活时间,非核心线程在没有任务的情况下多久删除,从而释放线程资源
                                      TimeUnit unit,//空闲线程存活时间单位
                                      BlockingQueue<Runnable> 															workQueue, //工作队列,用于存放给线程执行的任务,存在队列最大长度(一定要设置不可以为无限)
                                ThreadFactory threadFactory,//线程工厂,控制每个线程的生产
                                      RejectedExecutionHandler handler//(拒绝策略)线程池拒绝策略,==任务队列满的时候,我们采取什么措施,比如抛异常、不抛异常、自定义策略==。
                                 ) {
            
        }
        
        1. 线程池的工作机制

          1. 开始时:没有任务的线程,也没有任何的任务。
          1. 刚开始的核心线程数、最大线程数、任务队列中分别存在的数量为:

          corePoolSize = 0 ;maximumPoolSize = 0 ,workQueue.size = 0

          在这里插入图片描述

          1. 来了第一个任务,发现我们的员工没有到达正式的员工人数,来了一个员工直接处理这个任务。
          1. 第一个任务到来时的核心线程数、最大线程数、任务队列中分别存在的数量为:

          corePoolSize = 1 ;maximumPoolSize = 1 ,workQueue.size = 0

          在这里插入图片描述

          1. 又来了一个任务,发现我们的员工还没有达到正式员工数,再来一个员工直接处理这个任务。

          3、第二个任务到来时的核心线程数、最大线程数、任务队列中分别存在的数量为:

          corePoolSize = 2 ;maximumPoolSize = 2 ,workQueue.size = 0

          (一个人处理一个任务,一线程一任务)

          在这里插入图片描述

          1. 又来了一个任务,但是我们正式员工数已经满了(当前线程数 = corePoolSize = 2),将最新来的任务放到任务队列(最大长度 workQueue.size 是 2) 里等待,而不是再加新员工。

          4、第三、四个任务到来时的核心线程数、最大线程数、任务队列中分别存在的数量为:

          corePoolSize = 2 ;maximumPoolSize = 2 ,workQueue.size = 2

          (一个人处理一个任务,一线程一任务)

          在这里插入图片描述

          1. 又来了一个任务,但是我们的任务队列已经满了(当前线程数 大于了 corePoolSize=2,己有任务数 = 最大长度 workQueue.size = 2),新增线程(maximumPoolSize = 4)来处理任务,而不是丢弃任务。
          1. 第五个任务到来时的核心线程数、最大线程数、任务队列中分别存在的数量为:

          corePoolSize = 2 ;maximumPoolSize = 3 ,workQueue.size = 2

          (找了一个临时工处理这个队新来的这个任务)

          在这里插入图片描述

          1. 当到了任务7,但是我们的任务队列已经满了、临时工也招满了(当前线程数 = maximumPoolSize = 4,已有任务数 = 最大长度 workQueue.size = 2) 调用RejectedExecutionHandler 拒绝策略来处理多余的任务。
          1. 等到第六个任务到来时的核心线程数、最大线程数、任务队列中分别存在的数量为:

          corePoolSize = 2 ;maximumPoolSize = 4 ,workQueue.size = 2

          (再找了一个临时工处理这个队列任务中最前面的任务4,然后这个第六个新来的线程就进入到任务队列中等待)

          1. 等到第七个任务到来时的核心线程数、最大线程数、任务队列中分别存在的数量为:

          corePoolSize = 2 ;maximumPoolSize = 4 ,workQueue.size = 2

          (此时在核心线程数、最大线程数以及任务队列中都占满了,以及无法接收新的任务了,所以说只能拒绝任务7)

          7、最后,如果当前线程数超过 corePoolSize (正式员工数),又没有新的任务给他,那么等 keepAliveTime 时间达到后,就可以把这个线程释放。

        2. 确定线程池的参数

          1. corePoolSize(核心线程数=>正式员工数):正常情况下,可以设置为 2 - 4
          2. maximumPoolSize(最大线程数):设置为极限情况,设置为 <= 4
          3. keepAliveTime(空闲线程存活时间):一般设置为秒级或者分钟级
          4. TimeUnit unit(空闲线程存活时间的单位):分钟、秒
          5. workQueue(工作队列):结合实际请况去设置,可以设置为20 (2n+1)
          6. threadFactory(线程工厂):控制每个线程的生成、线程的属性(比如线程名)
          7. RejectedExecutionHandler(拒绝策略):抛异常,标记数据库的任务状态为 “任务满了已拒绝”
        3. 线程设置种类

          • IO密集型:耗费带宽/内存/硬盘的读写资源,corePoolSize可以设置大一点,一般经验值是 2n 左右,但是建议以 IO 的能力为主。
          • 计算密集型:耗费cpu资源,比如音视频资源,图像处理、数学计算等一般是设置corePoolSize为cpu的核数+1(空余线程池)

          考虑导入百万数据到数据库,属于IO密集型任务、还是计算密集型任务?

          答:考虑导入百万数据到数据库是一个IO密集型任务。导入数据需要从外部读取大量数据,然后将其写入数据库,这个过程中计算的工作量不是很高,相对来说需要更多的磁盘IO和网络IO。因此,这个任务的性能瓶颈通常是在IO操作上,而非计算上。

        4. 自定义线程池

          /**
           * 配置线程池类
           * 可以在yml文件中写配置,实现自动注入
           */
          @Configuration
          public class ThreadPoolExecutorConfig {
          
              @Bean
              public ThreadPoolExecutor threadPoolExecutor() {
                  ThreadFactory threadFactory = new ThreadFactory() {
                      private int count = 1;
          
                      @Override
                      public Thread newThread(@NotNull Runnable r) {
                          // 一定要将这个 r 放入到线程当中
                          Thread thread = new Thread(r);
                          thread.setName("线程:" + count);
                          // 任务++
                          count++;
                          return thread;
                      }
                  };
                  ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 100, TimeUnit.SECONDS,
                          new ArrayBlockingQueue<>(100), threadFactory);
                  return threadPoolExecutor;
              }
          }
          
        5. 提交任务到自定义线程池

          @RestController
          @RequestMapping("/queue")
          @Slf4j
          @Profile({ "dev", "local" })
          @Api(tags = "QueueController")
          @CrossOrigin(origins = "http://localhost:8000", allowCredentials = "true")
          public class QueueController {
          
              @Resource
              private ThreadPoolExecutor threadPoolExecutor;
          
              @GetMapping("/add")
              public void add(String name) {
                  CompletableFuture.runAsync(() -> {
                      log.info("任务执行中:" + name + ",执行人:" + Thread.currentThread().getName());
                      try {
                          Thread.sleep(60000);
                      } catch (InterruptedException e) {
                          throw new RuntimeException(e);
                      }
                  },threadPoolExecutor);
              }
          
              @GetMapping("/get")
              public String get() {
                  Map<String, Object> map = new HashMap<>();
                  int size = threadPoolExecutor.getQueue().size();
                  map.put("队列长度:", size);
                  long taskCount = threadPoolExecutor.getTaskCount();
                  map.put("任务总数:", taskCount);
                  long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();
                  map.put("已完成任务数:", completedTaskCount);
                  int activeCount = threadPoolExecutor.getActiveCount();
                  map.put("正在工作的线程数:", activeCount);
                  return JSONUtil.toJsonStr(map);
              }
          }
          

3. 实操项目使用异步化优化

  1. 系统问题分析:

    1. 用户等待时间较长
    2. 业务服务器可能会有很多请求在处理,导致系统资源紧张,严重时导致服务器宕机或者无法处理新的请求(很多用户在系统中进行同一个请求导致系统使用体验降低。)
    3. 调用的第三方服务(如AI能力)的处理能力是有限的,比如每3秒只处理1个请求,当多个请求时会导致 AI 处理不过来,严重时AI可能会对后台系统拒绝服务。
  2. 解决方法=>异步化

    • 异步化使用场景:调用的服务处理能力有限,或者接口的处理(返回)时间较长,考虑异步化
  3. 异步化优化前后对比

    • 优化前架构图

      在这里插入图片描述

    • 优化后架构图

      在这里插入图片描述

  4. 异步化(new Rhread)实现

    1. 任务队列的最大容量应该设置多少合适?
    2. 程序怎么从任务队列中取出任务去执行,这个任务队列的流程怎么实现的,怎么保证程序最多同时执行多少个任务?
      • 阻塞队列
      • 线程池
      • 增加更多的人手?
    1. 流程梳理:

      1. 给chart表新增任务状态字段(比如排队中、执行中、已完成、失败),任务执行信息字段(用于记录任务执行中、或者失败的一些信息)

        -- 图表信息表
        create table if not exists chart
        (
            id          bigint auto_increment comment 'id' primary key,
            goal        text                                   null comment '分析目标',
            chartName   varchar(256)                           null comment '图表名称',
            chartData   text                                   null comment '图表数据',
            chartType   varchar(256)                           null comment '图表类型',
            genChart    text                                   null comment '生成的图表信息',
            genResult   text                                   nul l comment '生成的分析结论',
            chartStatus varchar(128) default 'wait'            not null comment 'wait-等待,running-生成中,succeed-成功生成,failed-生成失败',
            execMessage text                                   null comment '执行信息',
            userId      bigint                                 null comment '创建图标用户 id',
            createTime  datetime     default CURRENT_TIMESTAMP not null comment '创建时间',
            updateTime  datetime     default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
            isDelete    tinyint      default 0                 not null comment '是否删除'
        ) comment '图表信息表' collate = utf8mb4_unicode_ci;
        
      2. 用户点击智能分析页面的提交按钮时,先把图表立刻保存到数据库中(作为一个任务)

      3. 任务:先修改图表任务状态为"执行中"。等执行成功后,修改为"已完成"、保存执行结果;执行失败后,状态修改为"失败",记录任务失败信息。

      4. 用户可以在图表管理界面插查看所有的图表的信息和状态

        • 已生成的
        • 生成中的
        • 生成失败的
      5. 用户可以修改生成失败的图表信息,点击重新生成图表

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1296304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

拦截 open调用 (进程白名单,文件白名单)

拦截 open 文章目录 拦截 open第一个需求文件结构进程白名单文件白名单 测试代码第一个版本版本二代码演示 增加一个日志记录代码解释 gcc -shared -fPIC -o libintercept.so intercept.c -ldlLD_PRELOAD./libintercept.so ./processA在Linux中&#xff0c;我们可以使用LD_PREL…

Navicat 技术指引 | 适用于 GaussDB 分布式的用户/权限功能

Navicat Premium&#xff08;16.3.3 Windows 版或以上&#xff09;正式支持 GaussDB 分布式数据库。GaussDB 分布式模式更适合对系统可用性和数据处理能力要求较高的场景。Navicat 工具不仅提供可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结…

如何提高大模型在超长上下文的表现?Claude实验表明加一句prompt立即提升效果~

本文来自DataLearnerAI官方网站&#xff1a;如何提高大模型在超长上下文的表现&#xff1f;Claude实验表明加一句prompt立即提升效果~ | 数据学习者官方网站(Datalearner)https://www.datalearner.com/blog/1051701947131881 Claude 2.1版本的模型上下文长度最高拓展到200K&am…

UG NX二次开发(C#)-求曲线在某一点处的法矢和切矢

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、在UG NX中创建一个曲线3、直接放代码4、测试案例1、前言 最近确实有点忙了,好久没更新博客了。今天恰好有时间,就更新下,还请家人们见谅。 今天我们讲一下如何获取一条曲线上某一条曲…

自然语言处理22-基于本地知识库的快速问答系统,利用大模型的中文训练集为知识库

大家好,我是微学AI,今天给大家介绍一下自然语言处理22-基于本地知识库的快速问答系统,利用大模型的中文训练集为知识库。我们的快速问答系统是基于本地知识库和大模型的最新技术,它利用了经过训练的中文大模型,该模型使用了包括alpaca_gpt4_data的开源数据集。 一、本地…

分布式数据库 GaiaDB-X 金融应用实践

1 银行新一代核心系统建设背景及架构 在银行的 IT 建设历程中&#xff0c;尤其是中大行&#xff0c;大多都基于大型机和小型机来构建核心系统。随着银行业务的快速发展&#xff0c;这样的系统对业务的支持越来越举步维艰&#xff0c;主要体现在以下四个方面&#xff1a; 首先…

RHEL8_Linux计划任务

本章主要介绍如何创建计划任务 使用 at 创建计划任务使用 crontab 创建计划任务 有时需要在某个指定的时间执行一个操作&#xff0c;此时就要使用计划任务了。计划任务有两种&#xff1a;一个是at计划任务&#xff0c;另一个是 crontab计划任务。 下面我们分别来看这两种计划任…

AI聊天专题报告:ChatGPT全景图聊聊技术产品和未来

今天分享的AI系列深度研究报告&#xff1a;《AI聊天专题报告&#xff1a;ChatGPT全景图聊聊技术产品和未来》。 &#xff08;报告出品方&#xff1a;LanguageX&#xff09; 报告共计&#xff1a;22页 争论&#xff1a;ChatGPT算不算技术革命 回应吴军老师“ChatGPT不算新技术…

基于jsonrpc4j实现JSON-RPC over HTTP(服务端集成Spring Boot)

1.JSON-RPC说明 JSON-RPC是一个无状态且轻量级的远程过程调用(RPC)协议。 它主要定义了一些数据结构及其相关的处理规则。 它运行时可以基于tcp(socket),http等不同的消息传输方式&#xff0c; 即它不关心底层传输方式的细节。 它使用JSON&#xff08;RFC 4627&#xff09;作为…

空间运算设备-Apple Vision Pro

苹果以其在科技领域的创新而闻名&#xff0c;他们致力于推动技术的边界&#xff0c;这在他们的产品中表现得非常明显。他们尝试开发一项的新型突破性显示技术。在 2023 年 6 月 5 日官网宣布将发布 Apple Vision Pro 头戴空间设备&#xff0c;我们一起来了解一下 Apple Vision …

《每天一个Linux命令》 -- (5)通过sshkey密钥登录服务器

欢迎阅读《每天一个Linux命令》系列&#xff01;在本篇文章中&#xff0c;将介绍通过密钥生成&#xff0c;使用公钥连接管理服务器。 概念 SSH 密钥是用于安全地访问远程服务器的一种方法。SSH 密钥由一对密钥组成&#xff1a;公钥和私钥。公钥存储在远程服务器上&#xff0c;…

PandoraFMS 监控软件 SQL注入漏洞复现

0x01 产品简介 Pandora FMS是西班牙Artica公司的一套监控系统。该系统通过可视化的方式监控网络、服务器、虚拟基础架构和应用程序等。 0x02 漏洞概述 Pandora FMS监控软件存在SQL注入漏洞,攻击者通过chart_generator.php 来执行恶意语句,获取数据库敏感信息。 0x03 复现…

湖南电网启用输电线路无人机巡视,提升电力运维水平

国网湖南电网近期成功引入输电线路无人机巡视技术&#xff0c;实现了电力运维的全新突破。此创新模式通过全自主飞行、自主巡检&#xff0c;极大提高了电力线路运维效率&#xff0c;确保了电网的安全稳定。 一、全自动巡检&#xff0c;无人值守 在输电线路无人机巡视作业现场&…

【Cisco Packet Tracer】路由器实验 静态路由/RIP/OSPF/BGP

本教程讲解路由器的静态IP配置、RIP、OSPF、BGP等实验内容。 一、基本设置 绘制以下拓扑结构&#xff1a; PC0设置&#xff1a; PC1设置&#xff1a; Router0端口0设置&#xff1a; Router0端口1设置&#xff1a; Router1端口0设置&#xff1a; Router1端口1设置&#xff1a…

【Vue3从入门到项目实现】RuoYi-Vue3若依框架前端学习——动态路由与菜单栏

菜单栏 若依框架的侧边栏组件通常由菜单项和子菜单组成。 登录后&#xff0c;会获取用户拥有的路由菜单 {"msg": "操作成功","code": 200,"data": [{"name": "System","path": "/system",…

unity 2d 入门 飞翔小鸟 场景延续(八)

1、新建c#脚本如下 代码&#xff0c;在前方生成生成自身图片并3s后销毁自身&#xff0c;在碰撞物体后小鸟死亡后不删除自身 using System.Collections; using System.Collections.Generic; using UnityEngine;public class CopyScene : MonoBehaviour { //要复制的对象public…

硕士毕业论文格式修改要点_word

目录 0、最开始要做的事情1、更改样式&#xff08;先善器&#xff09;2、多级标题&#xff08;解决自动更新问题必要的基础设置&#xff09;2、插入图片&#xff08;1&#xff09;设置一个图片样式——“无间隔”&#xff08;2&#xff09;插入题注&#xff08;3&#xff09;修…

【K8S】微服务不香了?单体化改造悄然兴起!!

微服务一直以来是服务治理的基本盘之一,落地到云原生上,往往是每个 K8s pods 部署一个服务,独立迭代、独立运维。 但是在快速部署的时候,有时候,我们可能需要一些宏服务的优势。有没有一种方法,能够 “既要又要” 呢?本文基于 tRPC-Go 服务,提出并最终实践了一种经验证…

AI 训练框架:Pytorch TensorFLow MXNet Caffe ONNX PaddlePaddle

https://medium.com/jit-team/bridge-tools-for-machine-learning-frameworks-3eb68d6c6558

12.Mysql 多表数据横向合并和纵向合并

Mysql 函数参考和扩展&#xff1a;Mysql 常用函数和基础查询、 Mysql 官网 Mysql 语法执行顺序如下&#xff0c;一定要清楚&#xff01;&#xff01;&#xff01;运算符相关&#xff0c;可前往 Mysql 基础语法和执行顺序扩展。 (8) select (9) distinct (11)<columns_name…