ES101系列09 | 运维、监控与性能优化
本篇文章主要讲解 ElasticSearch 中 DevOps 与性能优化的内容,包括集群部署最佳实践、容量规划、读写性能优化和缓存、熔断器等。
集群部署最佳实践
在生产环境中建议设置单一角色的节点。
- Dedicated master eligible nodes:负责集群状态的管理。使用低配置的 CPU,RAM 和磁盘。
- Dedicated data nodes:负责数据存储及处理客户端请求使用高配置的 CPU,RAM 和磁盘。
- Dedicated ingest nodes:负责数据处理使用高配置 CPU,中等配置的 RAM,低配置的磁盘。
从高可用 & 避免脑裂的角度出发,一般生产环境需要配置 3 台 Delicate Master Node。
拓展方式
- 当磁盘容量无法满足需求时,可以增加数据节点;磁盘读写压力大时,增加数据节点。
- 当系统中有大量的复杂查询及聚合时候,增加 Coordinating 节点,增加查询的性能。
Hot & Warm Architecture
- Hot & Warm Architecture
- 数据通常不会有 Update 操作;适用于 Time based 索引数据(生命周期管理),同时数据量比较大的场景。
- 引入 Warm 节点,低配置大容量的机器存放老数据,以降低部署成本。
- 两类数据节点,不同的硬件配置
- Hot 节点(通常使用 SSD):索引有不断有新文档写入。通常使用 SSD。
- Warm 节点(通常使用 HDD):索引不存在新数据的写入;同时也不存在大量的数据查询。
分片设计
单个分片
- 7.0 开始,新创建一个索引时,默认只有一个主分片。
- 单个分片,查询算分,聚合不准的问题都可以得以避免。
- 单个索引,单个分片时候,集群无法实现水平扩展。
- 即使增加新的节点,无法实现水平扩展。
两个分片
新增节点后,ElasticSearch 会自动进行分片的移动(Shard Rebalancing)。也就实现了水平拓展。
如何设计分片数
- 当分片数>节点数时
- 一旦集群中有新的数据节点加入,分片就可以自动进行分配。
- 分片在重新分配时,系统不会有 downtime。
- 多分片的好处:一索引如果分布在不同的节点,多个节点可以并行执行
- 查询可以并行执行。
- 数据写入可以分散到多个机器。
如何确定主分片数
- 从存储的物理角度看
- 日志类应用,单个分片不要大于 50GB
- 搜索类应用,单个分片不要超过 20GB
- 为什么要控制分片存储大小
- 提高 Update 的性能
- Merge 时,减少所需的资源
- 丢失节点后,具备更快的恢复速度/便于分片在集群内 Rebalancing
如何确定副本分片数
- 副本是主分片的拷贝
- 提高系统可用性:相应查询请求,防止数据丢失。
- 需要占用和主分片一样的资源。
- 对性能的影响
- 副本会降低数据的索引速度:有几份副本就会有几倍的 CPU 资源消耗在索引上。
- 会减缓对主分片的查询压力,但是会消耗同样的内存资源。
- 如果机器资源充分,提高副本数,可以提高整体的查询 QPS。
集群容量规划
- 一个集群总共需要多少个节点?一个索引需要设置几个分片?
- 规划上需要保持一定的余量,当负载出现波动,节点出现丢失时,还能正常运行。
- 做容量规划时,一些需要考虑的因素
- 机器的软硬件配置。
- 单条文档的尺寸 / 文档的总数据量 / 索引的总数据量(Time base 数据保留的时间)/ 副本分片数。
- 文档是如何写入的(Bulk 的尺寸)。
- 文档的复杂度,文档是如何进行读取的(怎么样的查询和聚合)。
业务性能评估
- 数据呑吐及性能需求
- 数据写入的吞吐量,每秒要求写入多少数据?
- 查询的吞吐量?
- 单条查询可接受的最大返回时间?
- 了解你的数据
- 数据的格式和数据的 Mapping。
- 实际的查询和聚合长的是什么样的。
硬件配置
- 数据节点尽量用 SSD。
- 日志类或并发查询低的场景可以用机械硬盘。
- 单节点数据控制在 2TB 以内,最高不超过 5TB。
- JVM 配置机器内存的一半,不建议超过 32G。
集群扩容
- 增加 Coordinating / Ingest Node
- 解决 CPU 和内存开销的问题。
- 增加数据节点
- 解决存储的容量的问题。
- 为避免分片分布不均的问题,要提前监控磁盘空间,提前清理数据或增加节点(70%)。
监控 & 排查 API
类别 | API 路径 | 核心用途 | 关键返回字段说明 | 生产环境关注点 |
---|---|---|---|---|
集群健康 | GET /_cluster/health | 集群整体状态概览 | status (green/yellow/red), unassigned_shards , active_shards_percent | 红色状态立即处理;黄色状态检查未分配分片 |
节点资源 | GET /_nodes/stats | 节点级资源监控 | heap_used_percent , cpu.percent , disk_free_percent | Heap >75% 需扩容;磁盘<20% 需清理或扩容 |
索引性能 | GET /_cat/indices?v | 索引存储与文档统计 | docs.count , store.size , pri.store.size | 分片数合理性;定期清理无用索引 |
慢查询定位 | GET /_search?pretty&q=slow | 定位高延迟请求 | took 时间(需预设慢日志阈值) | 优化 took >1s 的查询/索引设计 |
线程池积压 | GET /_cat/thread_pool?v | 线程队列监控 | search.queue , write.queue | 队列>0 表示资源瓶颈,需扩容或限流 |
分片分布 | GET /_cat/shards?v | 分片负载均衡检测 | node , state , store | 均衡分片分布;避免单节点负载过高 |
任务监控 | GET /_tasks?detailed | 长任务跟踪 | running_time_in_nanos , description | 监控耗时任务(如 reindex),避免阻塞集群 |
磁盘水位 | GET /_cat/allocation?v | 节点磁盘使用率 | disk.avail , disk.used | 预警 disk.avail <30% 的节点 |
Segment 状态 | GET /_cat/segments?v | 索引碎片监控 | segment.count , size | 过多小 segment 增加 Heap 压力;触发 force_merge 需谨慎 |
JVM 状态 | GET /_nodes/stats/jvm | 垃圾回收监控 | young.collection_count , old.collection_time_in_millis | Young GC >2 次/秒或 Old GC >10 秒/次需调优堆大小 |
关键监控指标 API 详解
-
集群健康 (
/_cluster/health
)status: red
表示主分片缺失(数据丢失风险)unassigned_shards
常见原因:节点离线或分片分配规则冲突 。
-
节点资源 (
/_nodes/stats
)-
JVM Heap 示例:
"jvm": { "mem": { "heap_used_percent": 65 } }
-
持续>75% 需扩容或优化内存(如减少 fielddata/cache)。
-
线程池积压 (
/_cat/thread_pool
)node2 search 8 0 8 0 # search.queue=8 表示队列积压
queue>0
需扩容或优化查询并发度 。
-
慢查询阈值配置
在
elasticsearch.yml
中设置:index.search.slowlog.threshold.query.warn: 10s index.search.slowlog.threshold.query.info: 5s
生产环境排查流程建议
- 集群异常 → 用
GET /_cluster/allocation/explain
定位未分配分片原因。 - 节点负载高 → 执行
GET /_nodes/hot_threads
抓取热点线程栈。 - 查询延迟 → 使用
GET /_search?profile=true
分析执行计划。 - 磁盘告警 → 优先清理大型临时索引或扩容冷节点 。
分片没有被分配的一些原因
- INDEX_CREATE:创建索引导致。在索引的全部分片分配完成之前,会有短暂的 Red,不一定代表有问题。
- CLUSTER_RECOVER:集群重启阶段会有这个问题。
- INDEX_REOPEN:Open 一个之前 Close 的索引。
- DANGLING_INDEX_IMPORTED:一个节点离开集群期间,有索引被删除。这个节点重新返回时,会导致问题。
集群变红的常见问题与解决方法
- 集群变红,需要检查是否有节点离线。如果有,通常通过重启离线的节点可以解决问题。
- 由于配置导致的问题,需要修复相关的配置(例如错误的 box_type,错误的副本数)。如果是测试的索引,可以直接删除。
- 因为磁盘空间限制,分片规则(ShardFiltering)引发的,需要调整规则或者增加节点。
读写性能优化
写性能优化
目标增大写吞吐量,越高越好。
- 客户端:多线程批量写,通过性能测试确定最佳并发度。
- 服务端:
- 使用更好的硬件,观察 CPU / IO Block。
- 降低 IO 操作,使用 ES 自动生成文档 id。
- 降低 CPU 和存储开销,减少不必要的分词。
- 尽可能做到写入和分片的均衡负载,实现水平扩展。
一切优化都基于高质量的数据建模。
Bulk、线程池和队列大小
- 客户端
- 单个 bulk 请求体的数据量不要太大,官方建议大约 5-15mb。
- 写入端的 bulk 请求超时需要足够长,建议 60s 以上。
- 写入端尽量将数据轮询打到不同节点。
- 服务器端
- 索引创建属于计算密集型任务,应该使用固定大小的线程池来配置。来不及处理的放入队列,线程数应该配置成 CPU 核心数 +1,避免过多的上下文切换。
- 队列大小可以适当增加,不要过大,否则占用的内存会成为 GC 的负担。
读性能优化
尽可能 Denormalize 数据,从而获取最佳的性能。
- 避免查询的时候使用脚本。
- 避免使用通配符开头的正则。
- 控制聚合的数量。
优化分片
- 避免 Over Sharding。
- 控制单个分片的大小,查询场景尽量 20GB 以内。
- 将只读的索引进行 force merge。
段合并优化
ES 和 Lucene 会自动进行 Merge 操作,该操作较重,需要优化降低对系统的影响。
- 降低分段产生的数量/频率
- 可以将 Refresh Interval 调整到分钟级别。
- 尽量避免文档的更新操作。
- 降低最大分段大小,避免较大的分段继续参与 Merge,节省系统资源。
Index.merge.policy.max_merged_segment
默认 5GB,操作此大小以后就不再参与后续的合并操作。
Force Merge
当 Index 不再有写入操作的时候,建议对其进行 force merge。能够提升查询速度/减少内存开销。
最终分成几个 segments 比较合适?
- 越少越好,最好可以 merge 成 1 个,但是 Force Merge 会占用大量的网络、IO 和 CPU。
- 如果不能在业务高峰期之前做完,就需要考虑增大最终的分段数。包括 Shard 的大小、
Index.merge.policy.max_merged_segment
的大小。
缓存与内存
缓存类型 | 作用域 | 主要作用 | 触发条件 | 失效条件 |
---|---|---|---|---|
Node Query Cache (Filter Cache) | 节点级 | 缓存过滤查询(Filter)的结果(文档 ID 位图) | filter 上下文查询 | 索引数据变更、手动清除、段合并、LRU 淘汰 |
Shard Request Cache | 分片级 | 缓存整个搜索请求的完整结果(如聚合查询) | size:0 的请求或显式启用 | 索引数据变更、手动清除、分片 Refresh、LRU 淘汰 |
Fielddata Cache | 分片级 | 缓存字段值到文档 ID 的映射(用于排序、聚合) | 对 text 字段进行聚合/排序 | 手动清除、段合并、LRU 淘汰 |
管理内存的重要性
ElasticSearch 高效运维依赖于内存的合理分配。可用内存一半分配给 JVM,一半留给操作系统缓存索引文件。
内存问题可能会导致 GC 和 OOM 问题。
一些常见的内存问题
- Segments 个数过多,导致 full GC。集群响应慢但没有特别多是读写,节点在持续 full GC。查看内存发现
segments.memory
占用很大空间。通过 force merge 段合并。 - Field data cache 过大,导致 full GC。集群响应慢但没有特别多是读写,节点在持续 full GC。查看内存发现
fielddata.memory.size
占用很大空间。将indices.fielddata.cache.size
设小,重启节点,堆内存恢复正常。
Circuit Breaker
熔断器名称 | 主要功能 | 触发条件(默认阈值) |
---|---|---|
Parent Circuit Breaker | 总内存保护,防止所有子熔断器总和超过节点内存 | 所有子熔断器申请内存总和 > 95% JVM 堆 |
Fielddata Circuit Breaker | 防止加载字段数据(如 text 字段聚合)耗尽堆内存 | 字段数据内存 > 40% JVM 堆 |
Request Circuit Breaker | 限制单个请求(查询、聚合等)的内存使用,防止大请求压垮节点 | 单个请求内存 > 60% 父熔断器剩余内存 |
In Flight Requests Circuit Breaker | 限制传输中请求(未完成)的总内存,保护网络和内存资源 | 传输中请求总内存 > 100% JVM 堆 |
Accounting Circuit Breaker | 跟踪分片级资源(如 Lucene 段内存),确保资源释放后及时回收 | 未释放资源 > 100% JVM 堆 |
Script Compilation Circuit Breaker | 限制一定时间窗口内脚本编译次数,防止频繁编译消耗 CPU | 15 分钟内编译次数 > 75 (默认) |
Disk Usage Circuit Breaker | 防止节点磁盘写满导致集群故障(触发只读保护) | 磁盘空间 > 低水位线 (默认 85%) |
💡 注:阈值可通过配置调整(如
indices.breaker.fielddata.limit
),触发后返回429 (Too Many Requests)
错误。
写在最后
这是该系列的第九篇,主要讲解 ElasticSearch 中 DevOps 与性能优化的内容,包括集群部署最佳实践、容量规划、读写性能优化和缓存、熔断器等。可以自己去到 Kibana 的 Dev Tool 实战操作,未来会持续更新该系列,欢迎关注👏🏻。
同时欢迎关注小红书:LanLance。不定时分享职场思考、大厂方法论和后端经验❤️
参考
- https://github.com/onebirdrocks/geektime-ELK/
- https://www.elastic.co/elasticsearch/
相关文章:
ES101系列09 | 运维、监控与性能优化
本篇文章主要讲解 ElasticSearch 中 DevOps 与性能优化的内容,包括集群部署最佳实践、容量规划、读写性能优化和缓存、熔断器等。 集群部署最佳实践 在生产环境中建议设置单一角色的节点。 Dedicated master eligible nodes:负责集群状态的管理。使用…...
Java常用的判空方法
文章目录 Java常用的判空方法JDK 自带的判空方法1. 使用 或 ! 运算符2. 使用 equals 方法3. Objects.isNull / Objects.nonNull4. Objects.equals4. JDK8 中的 Optional 第三方工具包1. Apache Commons Lang32. Google Guava3. Lombok 注解4. Vavr(函数式风格&…...

Excel处理控件Aspose.Cells教程:使用 C# 在 Excel 中创建组合图表
可视化项目时间线对于有效规划和跟踪至关重要。在本篇教程中,您将学习如何使用 C# 在 Excel 中创建组合图。只需几行代码,即可自动生成动态、美观的组合图。无论您是在构建项目管理工具还是处理内部报告,本指南都将向您展示如何将任务数据转换…...

【多线程初阶】阻塞队列 生产者消费者模型
文章目录 一、阻塞队列二、生产者消费者模型(一)概念(二)生产者消费者的两个重要优势(阻塞队列的运用)1) 解耦合(不一定是两个线程之间,也可以是两个服务器之间)2) 削峰填谷 (三)生产者消费者模型付出的代价 三、标准库中的阻塞队列(一)观察模型的运行效果(二)观察阻塞效果1) 队…...

《100天精通Python——基础篇 2025 第5天:巩固核心知识,选择题实战演练基础语法》
目录 一、踏上Python之旅二、Python输入与输出三、变量与基本数据类型四、运算符五、流程控制 一、踏上Python之旅 1.想要输出 I Love Python,应该使用()函数。 A.printf() B.print() C.println() D.Print() 在Python中想要在屏幕中输出内容,应该使用print()函数…...

机器人夹爪的选型与ROS通讯——机器人抓取系统基础系列(六)
文章目录 前言一、夹爪的选型1.1 任务需求分析1.2 软体夹爪的选型 二、夹爪的ROS通讯2.1 夹爪的通信方式介绍2.2 串口助手测试2.3 ROS通讯节点实现 总结Reference: 前言 本文将介绍夹爪的选型方法和通讯方式。以鞋子这类操作对象为例,将详细阐述了对应的夹爪选型过…...

第二十八章 RTC——实时时钟
第二十八章 RTC——实时时钟 目录 第二十八章 RTC——实时时钟 1 RTC实时时钟简介 2 RTC外设框图剖析 3 UNIX时间戳 4 与RTC控制相关的库函数 4.1 等待时钟同步和操作完成 4.2 使能备份域涉及RTC配置 4.3 设置RTC时钟分频 4.4 设置、获取RTC计数器及闹钟 5 实时时…...

使用 DuckLake 和 DuckDB 构建 S3 数据湖实战指南
本文介绍了由 DuckDB 和 DuckLake 组成的轻量级数据湖方案,旨在解决传统数据湖(如HadoopHive)元数据管理复杂、查询性能低及厂商锁定等问题。该方案为中小规模数据湖场景提供了简单、高性能且无厂商锁定的替代选择。 1. 什么是 DuckLake 和 D…...

大语言模型提示词(LLM Prompt)工程系统性学习指南:从理论基础到实战应用的完整体系
文章目录 前言:为什么提示词工程成为AI时代的核心技能一、提示词的本质探源:认知科学与逻辑学的理论基础1.1 认知科学视角下的提示词本质信息处理理论的深层机制图式理论的实际应用认知负荷理论的优化策略 1.2 逻辑学框架下的提示词架构形式逻辑的三段论…...

如何基于Mihomo Party http端口配置git与bash命令行代理
如何基于Mihomo Party http端口配置git与bash命令行代理 1. 确定Mihomo Party http端口配置 点击内核设置后即可查看 默认7892端口,开启允许局域网连接 2. 配置git代理 配置本机代理可以使用 127.0.0.1 配置局域网内其它机代理需要使用本机的非回环地址 IP&am…...
CMake 为 Debug 版本的库或可执行文件添加 d 后缀
在使用 CMake 构建项目时,我们经常需要区分 Debug 和 Release 构建版本。一个常见的做法是为 Debug 版本的库或可执行文件添加后缀(如 d),例如 libmylibd.so 或 myappd.exe。 本文将介绍几种在 CMake 中实现为 Debug 版本自动添加 d 后缀的方法。 方法一:使用 CMAKE_DEBU…...
Linux 特殊权限位详解:SetUID, SetGID, Sticky Bit
Linux 特殊权限位详解:SetUID, SetGID, Sticky Bit 在Linux权限系统中,除了基本的读、写(w)、执行(x)权限外,还有三个特殊权限位:SetUID、SetGID和Sticky Bit。这些权限位提供了更精细的权限控制机制,尤其在需要临时提升权限或管理共享资源时非常有用。 一、SetUID (s位…...

埃文科技智能数据引擎产品入选《中国网络安全细分领域产品名录》
嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》,埃文科技智能数据引擎产品成功入选数据分级分类产品名录。 在数字化转型加速的今天,网络安全已成为企业生存与发展的核心基石,为了解这一蓬勃发展的产业格局,嘶吼安全产业…...
使用VTK还是OpenGL集成到qt程序里哪个好?
在Qt程序中集成VTK与OpenGL:选择哪个更好? 在Qt程序中实现三维可视化时,开发者常常面临一个选择:是使用VTK(Visualization Toolkit)还是OpenGL(Open Graphics Library)。这两种技术…...
Java-IO流之打印流详解
Java-IO流之打印流详解 一、打印流概述1.1 什么是打印流1.2 打印流的特点1.3 打印流的应用场景 二、PrintStream详解2.1 基本概念2.2 构造函数2.3 核心方法2.4 使用示例 三、PrintWriter详解3.1 基本概念3.2 构造函数3.3 核心方法3.4 使用示例 四、PrintStream与PrintWriter的比…...
高效图像处理:使用 Pillow 进行格式转换与优化
高效图像处理:使用 Pillow 进行格式转换与优化 1. 背景引入 在图像处理应用中,格式转换、裁剪、压缩等操作是常见需求。Python 的 Pillow 库基于 PIL(Python Imaging Library),提供 轻量、强大 的图像处理能力,广泛用于 Web 开发、数据分析、机器学习 等领域。 本文将…...
Github 2025-06-06 Java开源项目日报Top10
根据Github Trendings的统计,今日(2025-06-06统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目10TypeScript项目1Java实现的算法集合:使用Gitpod.io进行编辑和贡献 创建周期:2883 天开发语言:Java协议类型:MIT LicenseStar数量…...
使用 Ansible 在 Windows 服务器上安装 SSL 证书
在本教程中,我将向您展示如何使用 Ansible 在 Windows 服务器上安装 SSL 证书。使用 Ansible 自动化 SSL 证书安装过程可以提高 IT 运营的效率、一致性和协作性。我将介绍以下步骤: 将 SSL 证书文件复制到服务器将 PFX 证书导入指定的存储区获取导入的证…...
厂区能源监控系统:网关赋能下的高效能源管理与环保监测
在现代工业生产领域,能源的有效利用与环境保护是企业实现可持续发展的两大关键要素。厂区能源监控系统借助先进的信息技术与自动化控制手段,对厂区内能源消耗及污水处理等核心环节展开实时监控与精细化管理。其中,御控网关作为系统关键枢纽&a…...
CentOS 7 如何安装llvm-project-10.0.0?
CentOS 7 如何安装llvm-project-10.0.0? 需要先升级gcc至7.5版本,详见CentOS 7如何编译安装升级gcc版本?一文 # 备份之前的yum .repo文件至 /tmp/repo_bak 目录 mkdir -p /tmp/repo_bak && cd /etc/yum.repo.d && /bin/mv ./*.repo …...
Cursor 1.0 的核心功能亮点及技术价值分析
Cursor 1.0 的核心功能亮点及技术价值分析 结合官方更新和开发者实测整理: 🛠️ 一、BugBot:智能自动化代码审查 功能亮点:深度集成 GitHub,自动扫描 Pull Request(PR)中的潜在 Bug(…...
软考 系统架构设计师系列知识点之杂项集萃(83)
接前一篇文章:软考 系统架构设计师系列知识点之杂项集萃(82) 第150题 体系结构权衡分析方法(Architecture Tradeoff Analysis Method,ATAM)是一种常见的系统架构评估框架,该框架主要关注系统的…...

NLP学习路线图(二十六):自注意力机制
一、为何需要你?序列建模的困境 在你出现之前,循环神经网络(RNN)及其变种LSTM、GRU是处理序列数据(如文本、语音、时间序列)的主流工具。它们按顺序逐个处理输入元素,将历史信息压缩在一个隐藏…...

Unity3D仿星露谷物语开发60之定制角色其他部位
1、目标 上一篇中定制了角色的衬衫、手臂。 本篇中将定制角色其他部位的图形,包括:裤子、发型、皮肤、帽子等。 2、定制裤子 (1)修改ApplyCharacterCustomisation.cs脚本 我们需要设置一个输入框选择裤子的颜色。 // Select …...
C++动态链接库封装,供C#/C++ 等编程语言使用——C++动态链接库概述(总)
目录: 一、前言及背景1.1需求描述1.2常见编程语言对比1.3应用背景 二、C对外接口2.1C对外封装2.2基于目标平台封装接口形式 三、系列文章汇总 一、前言及背景 1.1需求描述 不同的编程语言,具有不同的编程生态环境,对于项目应用来说ÿ…...

Google机器学习实践指南(机器学习模型泛化能力)
🔥 Google机器学习(14)-机器学习模型泛化能力解析 Google机器学习(14)-机器学习模型泛化原理与优化(约10分钟) 一、泛化问题引入 ▲ 模型表现对比: 假设森林中树木健康状况预测模型: 图1:初始模型表现 …...

MySQL性能调优:Mysql8高频面试题汇总
1,主键和唯一键有什么区别? 主键不能重复,不能为空,唯一键不能重复,可以为空。 建立主键的目的是让外键来引用。 一个表最多只有一个主键,但可以有很多唯一键 2,MySQL常用的存储引擎有哪些&…...
Neo4j 数据建模:原理、技术与实践指南
Neo4j 作为领先的图数据库,其核心优势在于利用图结构直观地表达和高效地查询复杂关系。其数据建模理念与传统关系型数据库截然不同,专注于实体(节点)及其连接(关系)。以下基于官方文档,系统阐述其建模原理、关键技术、实用技巧及最佳实践: 一、 核心原理:以关系为中心…...
【数据结构知识分享】顺序表详解
一、存储结构 物理相邻性: 若元素 a 和 b 逻辑相邻,则它们在内存中的地址也连续(如 &a[i1] &a[i] sizeof(ElemType))。 内存布局x: 基地址 索引 元素大小,通过首地址直接计算任意位置地址。 …...

vue+elementUI+springboot实现文件合并前端展示文件类型
项目场景: element的table上传文件并渲染出文件名称点击所属行可以查看文件,并且可以导出合并文件,此文章是记录合并文档前端展示的帖子 解决方案: 后端定义三个工具类 分别是pdf,doc和word的excle的目前我没整 word的工具类 package com.sc.modules…...