当前位置:   article > 正文

基于AIGC的BI系统项目知识点挖掘与回顾

基于AIGC的BI系统项目知识点挖掘与回顾

项目后端项目技术栈:SSM+Spring Boot、Redis、RabbitMQ、MySQL、MyBatis-Plus、Hutool工具库。

  • Spring Boot是基于Spring框架的Java开发框架,简化了Spring的搭建和配置。该框架可用于快速构建基础的后端项目,无需进行各种复杂的配置操作,只需要修改配置文件,就能轻松整合SSM、Redis、RabbitMQ等依赖。
  • SSM是Spring+Sping MVC+MyBatis的整合。
  • MyBatis-Plus 和 MyBatis X。MyBatis-Plus是MyBatis的增强版,依然是Java操作数据库的框架,但简化了CRUD操作,提供了通用Mapper接口,直接继承该接口,无需手写。MyBatis X是基于MyBatis和Plus的代码生成工具,支持根据数据库表结构生成实体类、Mapper接口、XML文件、Service接口等代码,同时支持自定义模板和插件,实现开发者的定制化需求。
  • 如何使用AIGC生成指定形式的json代码?采用了国内的某款AI助手,底层与ChatGPT相似。首先创建自己的AI助手并且编写系统预设prompt,全局指定助手的职责、输入内容和回复格式。然后通过不断的调整prompt,使其达到理想的输出效果。可以根据用户输入的分析目标和上传的原始数据生成Apache Echarts可视化库接受的图表配置JSON。之后通过调用AI助手官方的SDK调用该助手。
  • 我赋予AI助手的Prompt预设大概是:

几个优化Prompt的点是:

  1. 使用系统预设全局改变AI助手的行为,并赋予它一个角色,如你是一名数据分析师和前端开发专家。
  2. 控制输入格式,使得AI更容易理解用户需求,如接下来我会按照以下固定格式给你提供内容。
  3. 控制输出格式,便于AI处理返回的内容,如不要输出任何多余的开头、结尾、注释。
  4. 给AI提供一组示例回答。
  • 关于如何让AI读取到用户上传的原始数据。无论是ChatGPT还是本项目中使用的鱼聪明AI,它们读取的都是数据而非文件(当然现在的ChatGPT4.0已经可以读取文件了),因此需要让AI读取Excel文件,所以需要编写一个工具类把Excel文件转换为csv文件(CSV文件是文本文件,只存储数据,没有格式公式等内容,平面文件,方便读取),这里就用到EasyExcel开源库,使用它的读监听器。在读取的时候要尽量压缩数据,过滤掉空数据(return“ ”),之后再把读取到的数据进行拼接(.append),再通过前端接受一下上传的文件就可以了。
  • EasyExcel。它是基于Java开发的简单易用的Excel操作框架,功能丰富,开发者能够轻松的读取、写入和操作Excel文件。
  1. 简单易用:提供了简介API接口,使得开发者能够快速上手,无需复杂的配置和学习成本。
  2. 支持读取和写入:它支持各种常用格式的Excel文件的读写操作,如xls,xlsx。
  3. 高性能:它是基于流的读写模式,对于大数据量的excel文件具有优秀的性能和低内存消耗。
  4. 数据转换和处理:它支持将excel文件转换为Java对象,并且支持一些复杂的数据处理操作,如筛选、合并等。
  • AI的调用是通过鱼聪明官方SDK。引入sdk后,初始化AI对象,然后在manager中创建新类对接第三方接口。大概流程是调用AI,得到响应结果,从结果中取出信息,保存图表到数据库。
  • 关于校验用户上传的文件。在本项目中主要用到了两种,第一种是验证文件的类型,通过检验文件扩展名实现,比如后缀名必须是xlsx。第二种是限制文件的大小,从先前上传文件的对象函数中获取文件大小信息,限制其不能超过10MB。还有其它一些更加严格的校验方式,比如文件格式的校验,防止用户通过修改文件后缀名来上传非法文件,可以通过匹配特定格式的开头或结尾来校验内容。文件内容的校验,防止用户上传敏感违规的内容,需要用到第三方服务,比如腾讯云数据万象。
  • 限流与限流算法。限流是控制网络接口发送与接收请求的速率,在系统面临高并发或者大流量请求的情况下,限制新的请求对系统的访问,从而保证系统的稳定性。一般需要在系统稳定与用户体验之间做出平衡。常见的限流算法有:

       1. 固定窗口限流算法:首先维护一个计数器,将单位时间段当做一个窗口,计数器记录这个窗口接收请求的次数。当次数少于限流阀值,就允许访问,并且计数器+1;当次数大于限流阀值,就拒绝访问;当前的时间窗口过去之后,计数器清零。但有明显的临界问题,如果阈值为5,单位时间窗口为1s,分别在0.8-1s,1-1.2s两个时间段内并发5个请求,理论上是没有超过阈值,但如果按照0.8-1.2s时间段计算,则是并发了10个请求,已经超过阈值。

        2. 滑动窗口限流算法:解决了固定窗口限流算法的临界值问题,将单位时间周期分为n个小周期,分别记录每个小周期内接口的访问次数,并根据时间滑动删除过期的小周期。假设单位时间为1s,滑动窗口将其分为5个小周期,则每个小周期0.2s,每过0.2s,滑动窗口向右滑动一格,如果在0.83s时来了一个请求,那么0.8-1s这个小周期的计数器就会加1。

如何解决临近值问题呢?假设单位时间窗口为1s,阈值为5,如果在0.9s时并发了5个请求,落在黄色小周期内,但在过了1s之后,又来了5个请求,就会落在紫色小周期内,但此时窗口已经向又滑动了一格,也就是此时的单位周期为0.2-1.2s,因此紫色小周期内的5个请求会被拒绝。

理论上来说,单位时间内小周期划分的越多,滑动窗口的滚动就会越平滑,对限流的统计就会越精确。该算法虽然解决了临界值问题,但会直接暴力拒绝超额的请求,使得损失部分请求,对产品并不友好。

      3. 漏桶算法:该算法处理限流会更加柔性,不存在暴力拒绝。可以理解为注水与漏水的过程,水滴(请求)以某种速率向桶(系统)内流入,桶会以恒定的速率流出水滴(处理请求),桶的容量固定(系统能处理的请求数),如果桶满了,则会溢出水(请求拒绝)。

但该算法不适合面对突发流量请求,因为流出的速率固定,也就系统处理请求的速率不变,面对突发流量肯定希望系统处理的越快越好。

       4. 令牌桶算法:该算法可有效应对处理突发流量。有一个令牌管理员,根据限流大小,定速往令牌桶里放令牌。如果令牌数量满了,超过令牌桶容量的限制,那就丢弃。系统在接受到一个用户请求时,都会先去令牌桶要一个令牌。如果拿到令牌,那么就处理这个请求的业务逻辑;如果拿不到令牌,就直接拒绝这个请求。

 在本项目中采用了Redisson的RateLimiter,底层为令牌桶限流算法。

  • 什么是Redisson?Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。提供了基于Redis的各种分布式数据结构和便捷的操作接口,如分布式集合、分布式锁、分布式队列等,让开发者能够更轻松的使用Redis进行数据存储和中间件处理。在本项目中,我使用了Redisson自带的RateLimiter实现了对单个用户生成图表操作的限流。
  • 什么是Redisson的RateLimiter?RateLimiter是Redisson自带的基于Redis实现的分布式限流组件。可以控制某个操作或服务在一定时间内的请求频率,保护系统不被过多的请求压垮。由于本项目中使用了AI生成图表,这项操作是耗时耗资源的,因此给AI生成图表接口增加限流,限制单个用户每秒内最多执行2次生成图表操作。具体实现方式为:
  1. 集中管理限流器:创建一个RateLimiterManager类用来集中管理项目中的所有限流器,并提供创建限流器的接口。
  2. 创建限流器:通过向redissonClient传入指定的key来创建限流器,每个用户对应的key不同,因此对应的限流器也不同,实现不同用户的独立限流。
  3. 设置限流规则:通过rateLimiter中的trySetRate方法指定限流规则,每秒最多获取2个令牌。
  4. 请求令牌:用户执行操作时,会执行rateLimiter的tryAcquire方法获取令牌,如果能够得到令牌,则继续执行后续操作,如果无法得到,则抛出TOO_MANY_REQUEST异常。
  • 如何将限流器实际应用到项目中呢?需要给每个用户分配一个限流器,也就是做一个限流判断。在ChartController中做限流判断。
  • 基于Redis的分布式缓存和分布式锁。
  1. 分布式缓存是将数据存储在应用程序与数据存储系统之间的多个缓存节点中,这些节点可以位于不同的服务器或计算机中。当应用程序需要访问数据时,它会首先查询缓存,如果缓存中存在数据,则无需访问数据存储系统,提高了系统性能与响应速度。同时减轻了数据库的压力,降低数据库负载。
  2. 但需要注意一些问题。数据一致性:需要确保缓存与数据库中的数据保持同步。缓存雪崩:当缓存同一时间失效时,会将大量数据同时写入数据库中,使得数据库压力增大。缓存击穿:某个缓存数据失效时,会使后续请求直接访问数据库,使得数据库压力增大。
  3. 分布式锁是分布式系统中实现资源互斥访问的机制,确保同一时间只有一个进程或线程可以访问共享资源,从而避免数据竞争和并发问题。但应注意避免死锁,通过设置锁的过期时间,让线程不会长期占有某项资源。
  • 什么是分库分表,为什么要对用户的原始数据进行分表存储?分库分表是数据库性能优化的方式之一,它是将单个数据库分成多个子数据库以及多个表,将数据进行分散存储,以提升查询性能、扩展性与负载均衡,解决大规模数据存储与查询性能问题。传统情况下是将用户生成的图表存储在一个数据库中的一个表中,但随着用户数量与生成图表量的增多,单表数据量过大会对Chart表的查询性能造成影响;此外分表存储还能够方便用户灵活管理自己上传的数据,比如根据某个字段对表内的数据进行过滤查询,能够提升每份数据的查询检索性能。
  • 常见的分库分表方式有水平分表、垂直分库。水平分表是将单个表分成多个小表,每个小表存储部分数据,减少单个表的数据量,以提升查询性能。常用的划分规则有基于哈希值、范围、时间等。垂直分库是将一个大型数据库按照业务模块或者数据类型进行划分,每个数据库包含特定的表,将相关的数据集中在一起,提升系统性能。但也存在一些缺点,比如水平分表会导致事务并发处理复杂度增加,可能需要分布式事务的管理,并且跨节点查询困难;垂直分库可能会导致不同数据库之间的同步与维护困难,并且性能会相互影响。
  • 应根据具体的业务场景与技术架构,综合考虑分库分表的方式。可能会存在SQL注入风险,什么是SQL注入:select * from chart_123 where id=1 or 1=1, 因为1=1永远成立,所以这样可以直接将chart_123表中的所有数据查询出来。SQL注入就是通过构造一些特殊的sql语句,来达到非法获取数据或篡改数据的目的。
  • 创建索引是提升数据库查询性能的方式之一。通常有B树索引(常用)、哈希索引、全文索引等。B树索引以平衡树的形式存储索引数据,它通常支持范围查询、模糊查询、精确查询等。哈希索引是将索引列的值通过哈希算法计算为哈希值,然后将哈希值与索引值映射,它只支持精确匹配的等值查询。全文索引用于在文本数据中进行全文搜索,适用于包含大量文本数据并需要执行全文搜索的表。
  • 索引创建:首先需要确定在哪个表上创建索引,并了解该表上哪些列需要索引。                       ALTER TABLE table_name ADD(UNIQUE)INDEX index_name(column_name);
  • 什么是同步和异步?阻塞和非阻塞?
  1. 同步和异步是关注消息的通知机制。同步是指完成一个任务并得到返回结果后,才进行下一个任务。异步是指下一个任务不需要等待上一个任务的完成和返回结果,等完成后会通过回调机制通知。比如烧水,同步是一直等待水烧开,这期间什么也不做,只是等待烧水。异步则是把水烧上之后就去做别的事情,等水烧开后会有蜂鸣器通知。
  2. 阻塞和非阻塞是关注线程等待消息通知时的状态。阻塞是指当前任务的结果返回之前,线程会一直处于等待状态,不能执行其它任务。非阻塞是指当前任务的结果返回之前,线程不用等待返回结果,可以执行其它任务。比如烧水,阻塞是指在水烧开之前,无论是等着水烧开,还是等着蜂鸣器通知,这期间什么也不做。非阻塞是指在水烧开之前,不需要一直等待,去做别的事情即可,等蜂鸣器通知再去查看。
  • 什么是线程池?线程池是一种用于管理和复用线程的机制,它允许在应用程序中创建一组可用线程,并在需要执行任务时将这些任务分配给线程。线程池优化了线程的创建、销毁和管理过程,可以提高多线程应用程序的性能和效率。
  • 使用线程池的好处。
  1. 降低创建销毁线程的开销,线程池使得线程可以被重复利用,减少了这部分的资源消耗。
  2. 控制并发度,通过设置线程池参数,可以限制程序的并发执行数量,使得系统不会过载。
  3. 统一管理和监控,线程池提供了对线程的统一管理与监控接口,便于对线程的执行状态进行查看与调整。
  4. 避免资源竞争,在多线程环境中执行任务,可能会发生资源抢占与线程冲突,线程池可以帮助避免这类情况发生。
  • 线程池的核心参数。在本项目中,我是用ThreadPoolExecutor实现自定义线程池,通过ArrayBlockingQueue存放任务。
  1. 核心线程数(corePoolSize):线程池中一直处于活动状态的线程。
  2. 最大线程数(maximumPoolSize):线程池中允许存在的最多线程数。
  3. 空闲线程存活时间(keepAliveTime):临时线程在没有任务执行时,被销毁前可存在的时间。
  4. 空闲线程存活时间单位(TimeUnit):分钟、秒。
  5. 工作队列(workQueue):用于存放等待执行的任务的阻塞队列。可根据任务特性选择不同类型的队列,LinkedBlockingQueue为默认的无界阻塞队列,也可设置为有界阻塞队列ArrayBlockingQueue。
  6. 线程工厂(threadFactory): 用于创建、管理线程的工厂。
  7. 拒绝策略(rejectedExecutionHandler):当线程池无法接受新任务时会根据拒绝策略进行异常处理。
  • 根据具体的业务场景与任务类型,设置线程池为IO密集型或是计算密集型。计算密集型主要消耗CPU资源,如图像处理、视频处理、数学计算等,通常将核心线程数设置为CPU核数+1,保证CPU的每个核都得到充分利用。IO密集型主要消耗内存、硬盘的读写资源,核心线程数通常会设置的大一些。
  • 本项目采用IO密集型线程池。任务主要为AI生成内容,这期间需要等待第三方AI接口返回结果,耗费较长时间,但涉及的高强度CPU计算较少。
  • 什么是解耦。解耦是指将系统中各个组件和模块之间的依赖关系降低到最低程度,使得它们可以独立地进行开发、测试、维护和扩展。解耦的目的是降低代码模块之间的耦合度,提升系统灵活性、可维护性和可扩展性。
  • 什么是中间件。中间件是指在开发系统或多个应用时,用于连接多个系统或使多个系统紧密协作的工具。比如Redis、消息队列等。
  • 什么是消息队列。消息队列就是用来存储信息的队列。三个核心关键词是存储、消息和队列。存储就是对数据的储存能力。消息指任何形式的数据结构,如字符串、二进制数据、对象、JSON等。队列是一种先进先出的数据结构。消息队列不仅可以实现对数据的存储,而且可以在不同的系统应用之间实现消息的传输,并且无需关心应用的编程语言或系统架构(应用解耦)。消息队列由消息生产者、消息消费者、消息、队列组成。
  • 消息队列的优势。异步处理,消息生产者发送完消息后便可以立即转向其它任务,而消费者可以在任何时候开始处理消息,避免了阻塞。削峰填谷,消息队列允许先将用户请求存储起来,然后消费者(实际执行任务的应用)根据自己的处理能力和需求,逐步取出请求并处理。比如之前可能需要在短时间内进行大量的请求处理,但现在只需要从队列中逐步取出请求按照恒定的速率处理,保护了系统的稳定性。
  • 分布式消息队列的优势:
  1. 数据持久化。将消息集中存储到硬盘中,即使服务器重启后,数据也不会丢失。
  2. 可扩展性。这是分布式与单机的最大区别。可以根据需求随时增加或减少节点,保证服务的稳定。
  3. 应用解耦。允许在不同的多个系统之间实现消息的传输,无需考虑系统的架构、编程语言等。关于这点,举个例子来说明。比如在一个大型项目中有订单系统、发货系统和库存系统,传统情况下是这三个系统互相关联,比如订单系统需要调用发货系统与库存系统。但如果其中一个系统出现问题,会导致其它系统也会收到影响。但使用消息队列后,订单系统无需调用其它两个系统,只需要将消息发送至队列中,其它两个系统分别从队列中获取消息即可,这样即使其中某个系统出现了问题,其它系统也不会受到影响。并且整体性能更高,响应更快。
  4. 订阅模式。一个大的核心系统(QQ)每次向消息队列中发布消息,其它各子系统(王者荣耀、吃鸡等)去订阅这个队列,每次有消息发布时从队列中获取消息即可。
  5. 提高系统可靠性:分布式消息队列可以保证消息的可靠传输,确保消息不会丢失,未及时处理的任务可以在消费者准备好时再次处理。
  6. 任务重试:分布式消息队列通常支持消息的重试机制,如果某个任务由于某种原因未能成功处理,消息队列会将其重新推送给消费者,直至成功。
  • 消息队列的应用场景:
  1. 耗时的场景(异步)。
  2. 高并发的场景(异步、削峰填谷)。
  3. 分布式系统协作(尤其是跨团队、跨业务,应用解耦)。
  4. 强稳定场景(比如金融服务业,持久化、可靠性、削峰填谷)。
  • 分布式消息队列的应用场景(与上一个问题没太大区别,但更完善):
  1. 应用解耦:解耦系统内不同的模块,使它们能够相互独立开发、部署和扩展。比如一个模块将消息发送到消息队列,其它模块可以异步处理这些消息。
  2. 异步处理:将耗时的任务作为消息放入消息队列中,然后由单个或多个线程处理,提高了系统的性能与响应速度。
  3. 流量削峰:系统面对流量突刺或高峰流量时,分布式消息队列可以通过请求排队的方式平滑处理流量,避免系统过载。
  4. 日志和监控:分布式消息队列可以高效地收集和传输数据、监控数据。
  5. 跨语言和跨平台通信:分布式消息队列通常提供多语言和多平台的客户端,因此允许在不同架构和编程语言的系统之间通信,本质上也是应用解耦。
  6. 分布式事务:有些分布式消息队列支持分布式事务,用于确保消息的可靠传递与处理,保证数据的一致性。
  • 消息队列的缺点:
  1. 使用消息队列就意味着需要学习并掌握一个新的工具,并且需要在系统中额外引入一个中间件,使得系统构造变得复杂,并且需要额外的维护工作。若在企业中实施,则需要使用第三方大公司提供的稳定中间件,但这又需要额外的成本投入。
  2. 承担消息队列可能产生的问题。使用消息队列并非保证信息不会丢失,可能在发送信息的过程中某些原因导致信息发送失败。
  3. 需要保证消息的顺序性,防止消息被重复消费,保证数据的一致性等。
  • 为什么选择RabbitMQ?目前我了解到一些主流消息队列有RabbitMQ、Apache Kafka、RocketMQ等。Kafka吞吐量极高,能够处理大量的实时数据,适用于构建实时流处理系统;RocketMQ可靠性可用性都很强,适合大规模消息处理,适用于金融、电商交易等对可靠性要求较高的场景。而RabbitMQ简单易用,通过阅读官方文档即可上手开发,并且它时效性极高,延迟很低,智能BI系统用户量不多,使用它完全能满足需求。而且RabbitMQ生态好,支持多语言的客户端,便于后续的学习与项目扩展。
  • 什么是交换机?交换机是消息队列中的重要组件,它负责接收生产者的消息并将消息发送到指定的消息队列,可以通过设置路由规则来限制其发送的队列情况。
  • 绑定,是指将交换机和队列关联起来,并指定交换机将哪种类型的消息发送给哪个队列。routingKey(路由键),控制消息将要转发给哪个队列。
  • RabbitMQ有哪几种交换机?
  1. Fanout Exchange(广播交换机):将消息发送到所有与之绑定的队列中,忽略路由键。适用于广播消息给多个消费者。
  2. Direct Exchange(直连交换机):根据消息的路由键(Routing Key)将消息直接发送到与之完全匹配的队列中。适用于点对点通信。
  3. Topic Exchange(主题交换机):根据消息的路由键和绑定队列的通配符模式进行匹配,将消息发送到符合匹配规则的队列上。适用于灵活的消息路由与相对复杂的业务场景。
  4. Headers Exchange(头交换机):根据消息的头部信息进行匹配,将消息发送到符合匹配规则的队列中。这种方式应用不多,适合于基于头部信息进行匹配的路由和场景。
  • 在本项目中使用了直连交换机,因为项目中只有一种生产者(AI服务),一种消费者(用户),一种消息(分析结论和图表),因此使用点对点通信即可。
  • 消息队列中的两个核心特性:消息确认机制和消息过期机制。
  1. 消息确认机制:消费者在接收到一条消息后向消息队列发送确认信息,告知消息队列消息已经被成功处理(ack:消费成功;nack:消费失败;reject:拒绝)。分为两种模式:自动确认(autoack)和手动确认。自动确认模式指消费者接收到消息后,系统自动认为消息被成功处理,无需手动确认。但如果消费者在处理消息期间发生故障,消息将被丢失。手动确认模式则需要消费者显式的发送确认信息,之后系统才会认为消息被成功处理。如果处理期间发生故障,消息不会被确认,会被发送给其它消费者。但对于实际系统来说,我们更希望是确保下游消费者在处理完消息并执行完业务逻辑后再确认,这样即使出现异常情况,至少还有一个备份来保证消息的可靠性。因此通常将autoack设置为false。
  2. 消息过期机制:消息在队列中存储了一定时间,如果没有被消费者及时处理,消息会被标记为过期并丢弃。它适用于清理过期数据、模拟延迟队列的实现。而延迟队列是指将消息延迟一段时间后再进行处理,比如区分普通用户和会员用户,对于会员用户是立即处理请求,而普通用户会让其排队等待一段时间,再进行处理,以鼓励其购买会员。消息过期机制通常有两种,一种是对队列中所有消息指定一个统一的过期时间,另一种是针对某条具体的消息指定过期时间。根据实际的业务场景进行选择。
  • 死信队列。
  1. 死信:过期的消息、被拒收的消息、消息队列已满或者处理失败的消息的统称。
  2. 死信队列:专门用于处理死信的队列。(本质上就是一个普通的队列,只是用来接收并处理死信消息)
  3. 死信交换机:用于将死信消息转发到死信队列中的交换机,也可以设置路由键来确定消息的路由规则。(本质上是一个普通交换机,只是用来将死信消息转发到死信队列)
  • 线程池与消息队列的区别:线程池适用于处理需要多个线程并发执行的任务,消息队列适用于分布式场景下的信息传输、应用解耦、负载均衡以及消息可靠性等。应该根据实际情况来权衡使用线程池或是消息队列,并设计适当的机制确保任务的顺序性。
  • 什么是AMQP协议。Advanced Message Queue Protocol 高级消息队列协议,RabbitMQ就是基于这个协议开发的。它包括生产者(发送消息到交换机)、消费者(从队列中取消息)、交换机(负责将消息从生产者发送至对应的队列)、队列(存储消息的地方)、路由(转发)。
  • 在Spring Boot中如何使用RabbitMQ?
  1. 首先引入spring-boot-starter-amqp依赖,并且在application.yml中编写rabbitmq连接配置。
  2. 编写初始化队列类,创建Direct交换机、队列、交换机和队列的绑定。
  3. 创建生产者类:通过Spring Boot整合的RabbitTemplate类来操作MQ。
  4. 创建消费者类:通过@RabbitListener注解标识处理消息的方法,并在方法内编写对应的业务逻辑。
  • 做项目时遇到的瓶颈以及解决优化方案?
  1. 由于调用第三方AI服务需要耗费一定时间,AI的响应时间有时也会根据数据量、分析诉求、网络环境等情况产生波动,导致用户可能需要等待较长时间。因此将AI服务的同步等待优化为异步执行,在本项目中基于线程池实现。可以让用户在提交某次分析后立即进行下一次的分析,提升用户体验与系统效率。
  2. AI服务单次处理数据的能力有限,因此通过数据格式压缩,来让系统处理更多行列的数据。或者通过限制用户上传文件的大小来实现。
  3. 第三方AI服务的调用是有次数限制的,有时会产生调用超限的报错,因此通过限流的方式来限制用户的频繁调用,保证系统的稳定性。限流通过Redisson的RateLimiter实现。

项目前端技术栈:React、Ant Design Pro脚手架、Umi4前端框架、Ant Design组件库、Echarts可视化库、OpenAPI前端代码生成。

  • React是JavaScript的一个库,可以简化可视化界面的开发,是主流的前端开发框架。
  • React的优势(简单来说就是用的人多、性能优越、生态成熟):
  1. 组件化开发:React的核心概念,将UI拆分为独立的可重用组件,使得代码更易于理解、维护和测试。
  2. 虚拟DOM:在页面中定义若干的DOM元素,这些元素承载着外观表现和数据变化,传统的HTML网页中定义的都是实际DOM,但如果页面UI复杂,频繁操作大量实际DOM,使得访问性能严重下降,用户体验也会变差。React通过引入虚拟DOM树,可以最小化实际DOM操作,提高性能和响应速度。
  3. 单向数据流:单向数据流模型使得数据流动可控,减少了重复代码,易于跟踪调试。
  4. 良好的生态系统:React生态系统丰富,包括大量的第三方库和组件,可以加速开发并提供各种解决方案。
  5. 跨平台性:React可以用于构建Web应用、移动应用、桌面应用等,实现不同平台上的代码共享。
  6. 社区支持:React拥有庞大的开发者社区和活跃的维护团队,可以获得大量的文档、教程支持。
  • React的应用场景(适合开发企业级、高性能应用):
  1. 单页面应用:适合构建单页面应用,所有的交互都在同一个页面中完成,无需每次加载新页面。
  2. 大规模应用:React的性能和可维护性使其成为大规模应用的理想选择,特别是需要多人协作的项目。
  3. 跨平台开发:如果需要同时支持ios、web和android,React可实现共享代码和技术。
  4. 服务端渲染:React可用于服务器端渲染,提高应用的性能和搜索引擎优化。
  • Ant Design Pro脚手架的使用可以提升开发效率,它是一个开箱即用的企业级前端应用开发脚手架,基于React和Ant Design构建,能够通过命令行选项的方式,快速构建一个包含示例页面、全局通用布局、权限管理、路由管理、国际化、前端工程化的默认项目。因此我只需要在此基础上编写和业务相关的页面,能够大幅节省时间。
  • Ant Design Pro的缺点是对新手不友好,功能太多太杂,理解项目中的代码文件、系统阅读官方文档就要耗费很多时间。其次就是框架封装的代码较多,如果现有功能无法满足要求,自己去自定义开发功能比较困难,成本较高。而且这款脚手架更新迭代很快,通常需要指定版本,否则很容易出现依赖冲突、代码不兼容等问题。
  • 根据业务定制前端项目模版,之后每次开发新项目的时候,只需要复制或克隆下项目模版,全局替换页面的标题和logo等基础信息就可以了,大幅提升开发效率。
  1. 首先需要对项目进行瘦身操作。即移除一些相对不必要的功能代码,比如国际化、mock、测试数据等,精简项目。
  2. 全局异常处理,是指修改app.tsx入口文件的请求对象,自定义全局请求响应拦截器,这样能够自动根据后端业务返回的错误代码进行响应处理。比如后端返回了code=40100未登录,全局异常处理就会自动返回给前端用户并弹出message提示,重定向到登录页面。
  3. 开发通用业务,比如登录注册页,并且将用户登录态托管到Ant Design Pro提供的initialState全局状态中进行管理,通过修改access.ts和路由配置实现自动化权限管理。
  4. 请求代码生成,在config配置文件中添加了后端Swagger接口文档自动生成前端请求后端代码的配置,执行一行OpenAPI插件命令就能自动生成代码。
  • 通过一系列工具和技术保证项目编码规范,包括TypeScript、ESLint、Prettier、Husky。如果是团队项目,还可执行代码审查机制,让其他人帮助检查代码,以减少潜在的bug和不规范。
  1. TypeScript支持静态类型检查功能,用于增强代码的类型安全性,减少类型相关的错误。
  2. ESLint是一个代码检查工具,用于检查JavaScript的代码质量、风格等问题,强制开发者遵守编码规范。
  3. Prettier是一个代码格式化工具,能够根据配置自动格式化代码,确保代码的可读性和一致性。
  4. Husky是用于管理Git钩子的工具,在Git提交操作前自动运行代码检查脚本,确实提交的代码质量和规范。
  • Umi OpenAPI插件是一个用于UmiJS的插件,它的主要作用是根据OpenAPI规范生成前端代码,包括API请求函数和数据模型定义。大大简化前后端API集成的过程,提升开发效率。并且Ant Design Pro中默认整合了这个插件,因此无需手动引入依赖。只需要在config配置文件中指定Swagger接口文档的地址以及生成的规则,然后执行package.json中定义的openapi命令即可自动生成请求代码。
  • 为什么使用ECharts可视化库。ECharts是一个开源的数据可视化库,开发者只需要导入图表配置和数据,就能得到精美的交互式可视化图表。它生态广、兼容性强、图表丰富,并且官方还提供了Playground供开发者在线调试。
  • ECharts兼容性好主要体现在以下几方面:
  1. 版本更新稳定。2020年12月发布的v5版本至今仍是主流应用版本。
  2. 跨平台。ECharts支持目前市面上大部分Web浏览器,Chrome、Safari、Edge等。
  3. 响应式设计。ECharts能够自适应屏幕的尺寸与设备类型,支持从桌面端到移动端的转换。
  4. 开源社区支持。拥有庞大的社区支持,各种bug问题都能得到及时有效的解决。
  • 如何使用ECharts接收后端生成的动态JSON自动渲染可视化图表?首先通过阅读官方文档和Playground调试,明确后端要返回的JSON数据格式,包括图表名称、横纵坐标、数据内容等。接收到后端返回的JSON之后,先进行返回值校验,然后使用JSON.parse将JSON字符串转换为JS对象,最后将JS对象作为options参数传给ECharts组件,然后组件自动进行渲染。
  • 什么是React组件的生命周期,以及生命周期函数的执行顺序。
  1. React组件的生命周期包括挂载阶段(Mounting)、更新阶段(Updating)、卸载阶段(Unmounting)。生命周期函数就是组件在不同阶段中执行的特定操作,比如初始化组件、处理数据、更新UI等。
  2. 挂载阶段是组件被创建并插入到DOM中的过程。主要有:constructor()构造函数,用于初始化组件的状态和绑定事件处理程序,只会被调用一次。render()渲染函数,用于生成虚拟DOM,在组件挂载之前和每次更新时都会被调用。componentDidMount(),组件挂载后立即调用,通常执行DOM操作、网络请求、订阅事件等初始化操作。
  3. 更新阶段是组件被重新渲染并更新到DOM中的过程。主要有:shouldComponentUpdate(nextProps, nextState)用于决定组件是否更新,默认返回true。可以将组件中的props和state与当前的进行比较来决定是否更新。componentDidUpdate(prevProps, prevState)组件更新后立即调用,通常执行DOM操作、网络请求、订阅事件等操作。
  4. 卸载阶段是将组件从DOM中移除。主要有componentWillUnmount(),组件即将被卸载时调用,用于清理工作,如取消订阅等。
  • 在React中,什么是Virtual DOM?有什么作用?与真实DOM相比,优势是什么?
  1. Virtual DOM是React中的重要概念,它是一种轻量、快速的表示真实DOM树的内存数据结构,也可以理解为网页的编程接口。其作用是优化页面渲染性能和提高开发效率。
  2. 性能优化:当组件的状态发生变化时,React会创建一个虚拟DOM树,然后将新旧DOM树进行差异化比较,找出需要更新的部分,并将这些更新一次性批量操作到真实DOM树中,减少了真实DOM树的操作次数,提升了性能。
  3. 跨平台兼容:Virtual DOM不仅可以用于Web开发,同时也可以用于构建移动端应用和桌面端应用,因为虚拟DOM与平台无关。
  4. 提高开发效率:Virtual DOM使得开发人员更容易管理和维护UI组件,因为它将UI的状态和视图分离,并提供了声明式UI编程模型,使得UI开发更加直观。
  • 在前端开发中,如何处理跨域请求?描述常见的跨域解决方案及其优缺点。
  1. JSONP:利用<script>标签可以跨域访问的特性,使用回调函数获取远程数据。JSONP简单易用,但仅支持GET请求,并且目标服务器支持JSONP接口,容易受到跨站脚本攻击(XSS)。
  2. 代理服务器:在自己服务器中设置一个代理,将请求发送到目标域,然后将响应返回给前端,绕过浏览器的同源策略限制。但代理服务器容易引起额外的网络延迟。
  3. CROS:跨域资源共享。一种基于HTTP头部的机制,允许服务器授权浏览器访问跨域资源。需要在服务端设置合适的CROS头部。
  • 解释一下React Hooks。React16.8版本引入的功能,它允许函数组件具有类组件的能力,比如使用状态管理和其它React特性,它是让组件的状态逻辑更容易复用、理解和测试。常用的两个React Hooks组件是useState和useEffect。
  1. useState用于在函数组件中添加状态管理,它返回一个状态变量和更新该变量的数组,用于在组件中管理和更新状态。
  2. useEffect用于在函数组件中执行副作用操作,如订阅事件、手动操作DOM等,它接受两个参数,一个是函数,用于执行副作用操作,一个是数组,用于指定依赖项。
  • 在前端开发中如何优化网页的加载性能和渲染性能,提出一些常用的优化策略。
  1. 优化图片:使用适当的图片格式以减小文件大小;使用压缩工具来减小图片文件的大小;为不同屏幕提供不同尺寸的图片,避免加载过大图片。
  2. 使用CDN:内容分发网络,将静态资源,如CSS、图片等托管到CDN上,加速资源的加载;由于CDN将内容缓存到全球各地的服务器上,因此可以减少距离造成的延迟。
  3. 使用懒加载和预加载:懒加载是延迟加载图片和部分资源,优先加载用户滚动到的当前区域,预加载是提前加载可能需要的资源,使用<link rel = “preload”>标签。
  4. 前端框架和库的优化:使用一些轻量级前端框架和库,比如Vue、jQuery等。Ant Design Pro其实是一个相对重的开发框架,适合中后台管理系统。
  5. 监控和分析性能:使用一些性能检测工具来追踪和检测网站性能指标,以便及时优化,比如Google PageSpeed Insights等。
  6. 性能优化是一个持续的过程,需要不断的监控、调试。

通用问题

  • 什么是AIGC。Artificial Intelligence Generated Content,人工智能生成内容。利用AI自动生成文章、视频、图片等,大幅提升生产效率。ChatGPT就是其中一种,它是典型的自然语言处理模型。
  • 本项目的完整使用流程。区别于传统的BI系统,本项目只需要用户导入原始数据并输入分析诉求,即可完成对数据的分析与可视化。它利用AI自动生成图表与分析结论,使得没有数据分析经验的人群也能够进行相应操作,实现数据分析的降本增效。

内容较多,会持续更新优化。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号