JavaScript系列(48)-- 3D渲染引擎实现详解

news2025/1/30 13:29:31

JavaScript 3D渲染引擎实现详解 🎮

今天,让我们深入探讨JavaScript的3D渲染引擎实现。通过WebGL和现代JavaScript技术,我们可以构建一个功能完整的3D渲染系统。

3D渲染基础概念 🌟

💡 小知识:3D渲染引擎的核心是将三维场景转换为二维图像的过程。这涉及到场景图管理、几何变换、光照计算、材质渲染等多个方面。通过WebGL,我们可以直接访问GPU,实现高性能的3D渲染。

基本实现 📊

// 1. 渲染引擎核心
class Engine3D {
    constructor(canvas) {
        this.canvas = canvas;
        this.gl = canvas.getContext('webgl2');
        
        if (!this.gl) {
            throw new Error('WebGL2 not supported');
        }
        
        this.scene = new Scene();
        this.camera = new Camera();
        this.renderer = new Renderer(this.gl);
        
        this.initGL();
    }
    
    // 初始化WebGL上下文
    initGL() {
        this.gl.enable(this.gl.DEPTH_TEST);
        this.gl.enable(this.gl.CULL_FACE);
        this.gl.cullFace(this.gl.BACK);
    }
    
    // 设置视口大小
    setViewport(width, height) {
        this.canvas.width = width;
        this.canvas.height = height;
        this.gl.viewport(0, 0, width, height);
        this.camera.updateAspect(width / height);
    }
    
    // 渲染循环
    render() {
        this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
        this.renderer.render(this.scene, this.camera);
    }
}

// 2. 场景管理
class Scene {
    constructor() {
        this.objects = [];
        this.lights = [];
    }
    
    // 添加对象
    add(object) {
        if (object.isLight) {
            this.lights.push(object);
        } else {
            this.objects.push(object);
        }
        object.parent = this;
    }
    
    // 移除对象
    remove(object) {
        const array = object.isLight ? this.lights : this.objects;
        const index = array.indexOf(object);
        if (index !== -1) {
            array.splice(index, 1);
            object.parent = null;
        }
    }
    
    // 遍历场景
    traverse(callback) {
        this.objects.forEach(object => {
            callback(object);
            if (object.children) {
                object.traverse(callback);
            }
        });
    }
}

// 3. 相机系统
class Camera {
    constructor() {
        this.position = new Vector3(0, 0, 5);
        this.target = new Vector3(0, 0, 0);
        this.up = new Vector3(0, 1, 0);
        
        this.fov = 45;
        this.aspect = 1;
        this.near = 0.1;
        this.far = 1000;
        
        this.updateProjectionMatrix();
        this.updateViewMatrix();
    }
    
    // 更新投影矩阵
    updateProjectionMatrix() {
        this.projectionMatrix = Matrix4.perspective(
            this.fov,
            this.aspect,
            this.near,
            this.far
        );
    }
    
    // 更新视图矩阵
    updateViewMatrix() {
        this.viewMatrix = Matrix4.lookAt(
            this.position,
            this.target,
            this.up
        );
    }
    
    // 更新相机位置
    lookAt(target) {
        this.target.copy(target);
        this.updateViewMatrix();
    }
}

高级功能实现 🚀

// 1. 几何体系统
class Geometry {
    constructor() {
        this.vertices = [];
        this.indices = [];
        this.normals = [];
        this.uvs = [];
        
        this.vertexBuffer = null;
        this.indexBuffer = null;
        this.normalBuffer = null;
        this.uvBuffer = null;
    }
    
    // 设置顶点数据
    setVertices(vertices) {
        this.vertices = vertices;
        this.updateVertexBuffer();
    }
    
    // 设置索引数据
    setIndices(indices) {
        this.indices = indices;
        this.updateIndexBuffer();
    }
    
    // 计算法线
    computeNormals() {
        this.normals = new Array(this.vertices.length);
        
        for (let i = 0; i < this.indices.length; i += 3) {
            const i1 = this.indices[i];
            const i2 = this.indices[i + 1];
            const i3 = this.indices[i + 2];
            
            const v1 = new Vector3().fromArray(this.vertices, i1 * 3);
            const v2 = new Vector3().fromArray(this.vertices, i2 * 3);
            const v3 = new Vector3().fromArray(this.vertices, i3 * 3);
            
            const normal = Vector3.cross(
                Vector3.subtract(v2, v1),
                Vector3.subtract(v3, v1)
            ).normalize();
            
            this.normals[i1] = normal;
            this.normals[i2] = normal;
            this.normals[i3] = normal;
        }
        
        this.updateNormalBuffer();
    }
}

// 2. 材质系统
class Material {
    constructor() {
        this.uniforms = {
            diffuseColor: new Vector3(1, 1, 1),
            specularColor: new Vector3(1, 1, 1),
            shininess: 32.0,
            opacity: 1.0
        };
        
        this.vertexShader = null;
        this.fragmentShader = null;
        this.program = null;
    }
    
    // 编译着色器
    compile(gl) {
        const vertexShader = this.compileShader(
            gl, gl.VERTEX_SHADER, this.vertexShader
        );
        const fragmentShader = this.compileShader(
            gl, gl.FRAGMENT_SHADER, this.fragmentShader
        );
        
        this.program = gl.createProgram();
        gl.attachShader(this.program, vertexShader);
        gl.attachShader(this.program, fragmentShader);
        gl.linkProgram(this.program);
        
        if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
            throw new Error('Program linking failed: ' + 
                          gl.getProgramInfoLog(this.program));
        }
    }
    
    // 编译单个着色器
    compileShader(gl, type, source) {
        const shader = gl.createShader(type);
        gl.shaderSource(shader, source);
        gl.compileShader(shader);
        
        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            throw new Error('Shader compilation failed: ' + 
                          gl.getShaderInfoLog(shader));
        }
        
        return shader;
    }
}

// 3. 光照系统
class Light {
    constructor() {
        this.isLight = true;
        this.color = new Vector3(1, 1, 1);
        this.intensity = 1.0;
    }
}

class DirectionalLight extends Light {
    constructor() {
        super();
        this.direction = new Vector3(0, -1, 0);
    }
}

class PointLight extends Light {
    constructor() {
        super();
        this.position = new Vector3();
        this.distance = 0;
        this.decay = 1;
    }
}

实际应用场景 💼

// 1. 3D模型加载器
class ModelLoader {
    constructor(engine) {
        this.engine = engine;
    }
    
    // 加载OBJ格式模型
    async loadOBJ(url) {
        const response = await fetch(url);
        const text = await response.text();
        
        const vertices = [];
        const normals = [];
        const uvs = [];
        const indices = [];
        
        const lines = text.split('\n');
        
        for (const line of lines) {
            const parts = line.trim().split(/\s+/);
            
            switch (parts[0]) {
                case 'v':  // 顶点
                    vertices.push(
                        parseFloat(parts[1]),
                        parseFloat(parts[2]),
                        parseFloat(parts[3])
                    );
                    break;
                    
                case 'vn':  // 法线
                    normals.push(
                        parseFloat(parts[1]),
                        parseFloat(parts[2]),
                        parseFloat(parts[3])
                    );
                    break;
                    
                case 'vt':  // 纹理坐标
                    uvs.push(
                        parseFloat(parts[1]),
                        parseFloat(parts[2])
                    );
                    break;
                    
                case 'f':  // 面
                    for (let i = 1; i <= 3; i++) {
                        const vertexData = parts[i].split('/');
                        indices.push(parseInt(vertexData[0]) - 1);
                    }
                    break;
            }
        }
        
        const geometry = new Geometry();
        geometry.setVertices(vertices);
        geometry.setIndices(indices);
        
        if (normals.length > 0) {
            geometry.normals = normals;
        } else {
            geometry.computeNormals();
        }
        
        if (uvs.length > 0) {
            geometry.uvs = uvs;
        }
        
        return geometry;
    }
}

// 2. 动画系统
class AnimationSystem {
    constructor() {
        this.animations = new Map();
        this.currentTime = 0;
    }
    
    // 添加关键帧动画
    addKeyframeAnimation(name, keyframes) {
        this.animations.set(name, {
            keyframes,
            duration: keyframes[keyframes.length - 1].time
        });
    }
    
    // 更新动画
    update(deltaTime) {
        this.currentTime += deltaTime;
        
        for (const [name, animation] of this.animations) {
            const time = this.currentTime % animation.duration;
            
            // 查找当前关键帧
            let frame1, frame2;
            for (let i = 0; i < animation.keyframes.length - 1; i++) {
                if (time >= animation.keyframes[i].time && 
                    time < animation.keyframes[i + 1].time) {
                    frame1 = animation.keyframes[i];
                    frame2 = animation.keyframes[i + 1];
                    break;
                }
            }
            
            if (frame1 && frame2) {
                const t = (time - frame1.time) / 
                         (frame2.time - frame1.time);
                this.interpolate(frame1, frame2, t);
            }
        }
    }
    
    // 插值计算
    interpolate(frame1, frame2, t) {
        // 实现关键帧插值
        return {
            position: Vector3.lerp(frame1.position, frame2.position, t),
            rotation: Quaternion.slerp(frame1.rotation, frame2.rotation, t),
            scale: Vector3.lerp(frame1.scale, frame2.scale, t)
        };
    }
}

// 3. 物理系统
class PhysicsSystem {
    constructor() {
        this.objects = [];
        this.gravity = new Vector3(0, -9.81, 0);
    }
    
    // 添加物理对象
    addObject(object, mass = 1) {
        this.objects.push({
            object,
            mass,
            velocity: new Vector3(),
            acceleration: new Vector3()
        });
    }
    
    // 更新物理
    update(deltaTime) {
        for (const obj of this.objects) {
            // 应用重力
            obj.acceleration.add(this.gravity);
            
            // 更新速度
            obj.velocity.add(
                Vector3.multiply(obj.acceleration, deltaTime)
            );
            
            // 更新位置
            obj.object.position.add(
                Vector3.multiply(obj.velocity, deltaTime)
            );
            
            // 重置加速度
            obj.acceleration.set(0, 0, 0);
        }
        
        // 碰撞检测
        this.detectCollisions();
    }
    
    // 碰撞检测
    detectCollisions() {
        for (let i = 0; i < this.objects.length; i++) {
            for (let j = i + 1; j < this.objects.length; j++) {
                const obj1 = this.objects[i];
                const obj2 = this.objects[j];
                
                if (this.checkCollision(obj1, obj2)) {
                    this.resolveCollision(obj1, obj2);
                }
            }
        }
    }
}

性能优化技巧 ⚡

// 1. 渲染优化
class RenderOptimizer {
    constructor(engine) {
        this.engine = engine;
        this.frustumCuller = new FrustumCuller();
        this.occlusionCuller = new OcclusionCuller();
    }
    
    // 视锥体剔除
    cullFrustum(scene, camera) {
        this.frustumCuller.updateFrustum(camera);
        
        return scene.objects.filter(object => 
            this.frustumCuller.isVisible(object)
        );
    }
    
    // 遮挡剔除
    cullOcclusion(objects) {
        return this.occlusionCuller.getVisibleObjects(objects);
    }
    
    // LOD管理
    updateLOD(objects, camera) {
        for (const object of objects) {
            if (object.lod) {
                const distance = Vector3.distance(
                    object.position,
                    camera.position
                );
                object.updateLOD(distance);
            }
        }
    }
}

// 2. 内存管理
class ResourceManager {
    constructor() {
        this.geometries = new Map();
        this.textures = new Map();
        this.materials = new Map();
    }
    
    // 加载几何体
    async loadGeometry(url) {
        if (this.geometries.has(url)) {
            return this.geometries.get(url);
        }
        
        const loader = new ModelLoader();
        const geometry = await loader.loadOBJ(url);
        this.geometries.set(url, geometry);
        
        return geometry;
    }
    
    // 加载纹理
    async loadTexture(url) {
        if (this.textures.has(url)) {
            return this.textures.get(url);
        }
        
        return new Promise((resolve, reject) => {
            const image = new Image();
            image.onload = () => {
                const texture = new Texture(image);
                this.textures.set(url, texture);
                resolve(texture);
            };
            image.onerror = reject;
            image.src = url;
        });
    }
    
    // 释放资源
    unload(url) {
        if (this.geometries.has(url)) {
            const geometry = this.geometries.get(url);
            geometry.dispose();
            this.geometries.delete(url);
        }
        
        if (this.textures.has(url)) {
            const texture = this.textures.get(url);
            texture.dispose();
            this.textures.delete(url);
        }
    }
}

// 3. 渲染批处理
class BatchRenderer {
    constructor(gl) {
        this.gl = gl;
        this.batches = new Map();
    }
    
    // 添加到批次
    addToBatch(object) {
        const key = this.getBatchKey(object);
        
        if (!this.batches.has(key)) {
            this.batches.set(key, {
                objects: [],
                vertices: [],
                indices: []
            });
        }
        
        const batch = this.batches.get(key);
        batch.objects.push(object);
        
        // 合并几何数据
        this.mergeGeometry(batch, object);
    }
    
    // 获取批次键
    getBatchKey(object) {
        return `${object.material.id}_${object.geometry.id}`;
    }
    
    // 合并几何数据
    mergeGeometry(batch, object) {
        const geometry = object.geometry;
        const baseVertex = batch.vertices.length / 3;
        
        // 添加顶点
        batch.vertices.push(...geometry.vertices);
        
        // 添加索引
        for (const index of geometry.indices) {
            batch.indices.push(index + baseVertex);
        }
    }
    
    // 渲染批次
    render() {
        for (const batch of this.batches.values()) {
            if (batch.objects.length === 0) continue;
            
            // 使用第一个对象的材质
            const material = batch.objects[0].material;
            material.use(this.gl);
            
            // 渲染合并后的几何体
            this.renderBatch(batch);
        }
    }
}

最佳实践建议 💡

  1. 性能优化策略
// 1. 渲染状态管理
class RenderStateManager {
    constructor(gl) {
        this.gl = gl;
        this.currentState = {
            program: null,
            texture: null,
            blending: false
        };
    }
    
    // 设置着色器程序
    useProgram(program) {
        if (this.currentState.program !== program) {
            this.gl.useProgram(program);
            this.currentState.program = program;
        }
    }
    
    // 设置纹理
    bindTexture(texture) {
        if (this.currentState.texture !== texture) {
            this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
            this.currentState.texture = texture;
        }
    }
    
    // 设置混合
    setBlending(enable) {
        if (this.currentState.blending !== enable) {
            if (enable) {
                this.gl.enable(this.gl.BLEND);
            } else {
                this.gl.disable(this.gl.BLEND);
            }
            this.currentState.blending = enable;
        }
    }
}

// 2. 着色器管理
class ShaderManager {
    constructor() {
        this.shaders = new Map();
    }
    
    // 注册着色器
    register(name, vertexSource, fragmentSource) {
        this.shaders.set(name, {
            vertex: vertexSource,
            fragment: fragmentSource
        });
    }
    
    // 获取着色器
    get(name) {
        return this.shaders.get(name);
    }
    
    // 编译着色器
    compile(gl, name) {
        const shader = this.get(name);
        if (!shader) return null;
        
        const program = new ShaderProgram(gl);
        program.compile(shader.vertex, shader.fragment);
        
        return program;
    }
}

// 3. 调试工具
class DebugTools {
    constructor(engine) {
        this.engine = engine;
        this.stats = {
            drawCalls: 0,
            vertices: 0,
            triangles: 0
        };
    }
    
    // 开始性能分析
    beginProfile() {
        this.stats.drawCalls = 0;
        this.stats.vertices = 0;
        this.stats.triangles = 0;
    }
    
    // 记录绘制调用
    recordDrawCall(vertices, triangles) {
        this.stats.drawCalls++;
        this.stats.vertices += vertices;
        this.stats.triangles += triangles;
    }
    
    // 获取性能报告
    getStats() {
        return {
            ...this.stats,
            fps: this.calculateFPS()
        };
    }
    
    // 显示调试信息
    showDebugInfo() {
        const stats = this.getStats();
        console.log(`
            FPS: ${stats.fps}
            Draw Calls: ${stats.drawCalls}
            Vertices: ${stats.vertices}
            Triangles: ${stats.triangles}
        `);
    }
}

结语 📝

JavaScript的3D渲染引擎实现是一个复杂但有趣的主题。通过本文,我们学习了:

  1. 3D渲染引擎的基本架构和实现
  2. 场景管理和相机系统
  3. 几何体、材质和光照系统
  4. 动画和物理系统
  5. 性能优化技巧和最佳实践

💡 学习建议:在实现3D渲染引擎时,要注意性能优化和内存管理。合理使用批处理、剔除和LOD等技术可以显著提升渲染性能。同时,要充分利用WebGL的特性,避免不必要的状态切换。


如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻

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

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

相关文章

jmeter中对接口进行循环请求后获取相应数据

1、工作中遇到一个场景就是对某个单一接口进行循环请求&#xff0c;并需要获取每次请求后返回的相应数据&#xff1b; 2、首先就在jmeter对接口相关组件进行配置&#xff0c;需要组件有&#xff1a;循环控制器、CSV数据文件设置、计数器、访问接口、HTTP信息头管理器、正则表达…

网络工程师 (4)存储系统

一、多级存储结构 &#xff08;一&#xff09;组成 寄存器&#xff1a; 寄存器是与CPU直接协调工作的高速存储器&#xff0c;用于加速存储器的访问速度。它通常用于存放操作数或作为地址寄存器&#xff0c;以加快地址转换速度。寄存器的数量有限&#xff0c;一般在几个到几百个…

oracle比较一下统计信息差异吧

统计信息发生了哪些变化&#xff1f; 从上次收集到最近一次收集有什么不同&#xff1f; set long 999999 longc 99999 line 100 select report, maxdiffpct from table(dbms_stats.diff_table_stats_in_history(SYS,T1,to_timestamp(2025-01-22 09:01:46,YYYY-MM-DD hh24:mi:s…

Hive:内部表和外部表,内外转换

内部表和外部表 内部表示例 给表添加数据 外部表示例 给表添加数据 外部表示例 用location指定表目录位置,那么表的位置在实际指定的位置,但是可以被映射 外部表和内部表的区别 删除表后使用show tables in shao; 已经没有被删除的表,说明元数据已经被删除(mysql里面存放),但是…

P1030 [NOIP2001 普及组] 求先序排列(c++)详解

题目链接&#xff1a;P1030 [NOIP2001 普及组] 求先序排列 - 洛谷 | 计算机科学教育新生态 思路&#xff1a; 1.先确定跟节点 2.根据根节点&#xff0c;划分出左右子树 中&#xff1a;BADC 后&#xff1a;BDCA 分析&#xff1a; 根据后序遍历&#xff0…

Mac cursor设置jdk、Maven版本

基本配置 – Cursor 使用文档 首先是系统用户级别的设置参数&#xff0c;运行cursor&#xff0c;按下ctrlshiftp&#xff0c;输入Open User Settings(JSON)&#xff0c;在弹出的下拉菜单中选中下面这样的&#xff1a; 在打开的json编辑器中追加下面的内容&#xff1a; {"…

提升企业内部协作的在线知识库架构与实施策略

内容概要 在当前快速变化的商业环境中&#xff0c;企业对于提升内部协作效率的需求愈显迫切。在线知识库作为信息存储与共享的平台&#xff0c;成为了推动企业数字化转型的重要工具。本文将深入探讨如何有效打造与实施在线知识库&#xff0c;强调架构设计、知识资产分类管理及…

Vue3.5 企业级管理系统实战(三):页面布局及样式处理 (Scss UnoCSS )

本章主要是关于整体页面布局及样式处理&#xff0c;在进行这一章代码前&#xff0c;先将前两章中的示例代码部分删除&#xff08;如Home.vue、About.vue、counter.ts、App.vue中引用等&#xff09; 1 整体页面布局 页面整体布局构成了产品的框架基础&#xff0c;通常涵盖主导…

Excel中LOOKUP函数的使用

文章目录 VLOOKUP&#xff08;垂直查找&#xff09;&#xff1a;HLOOKUP&#xff08;水平查找&#xff09;&#xff1a;LOOKUP&#xff08;基础查找&#xff09;&#xff1a;XLOOKUP&#xff08;高级查找&#xff0c;较新版本Excel提供&#xff09;&#xff1a; 在Excel中&…

美创科技获浙江省网络空间安全协会年度表彰

近日&#xff0c;浙江省网络空间安全协会第二届理事会第三次会议在杭州隆重召开&#xff0c;会议总结部署工作、表彰先进、分享创新实践成果。 会上&#xff0c;省委网信办副主任马晓军出席会议并致辞、宋皆荣理事长向第二届理事会报告2024年协会工作、常务副理事长单位浙江联通…

游戏引擎介绍:Game Engine

简介 定义&#xff1a;软件框架&#xff0c;一系列为开发游戏的工具的集合 可协作创意生产工具&#xff0c;复杂性艺术&#xff0c;注重realtime实时 目的 为艺术家&#xff0c;设计师&#xff0c;程序员设计工具链 游戏引擎开发参考书 推荐&#xff1a;Game Engine Archite…

[A-29]ARMv8/v9-GIC-中断子系统的安全架构设计(Security/FIQ/IRQ)

ver0.1 前言 打开这篇文章的时候,我们已经为每一个中断信号规划一条路径,在外设和PE-Core之间建立了消息通道,外设有紧急的情况下可以给SOC中的大哥打报告了。下面就把接力棒就交到了CPU手里了,但是PE-Core要交给那个Exception Level以及Security下运行的软件处理呢?本文…

能够对设备的历史数据进行学习与分析,通过与设备当前状态的比对,识别潜在故障并做出预判的名厨亮灶开源了。

明厨亮灶视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。AI技术可以24小时…

Linux进程调度与等待:背后的机制与实现

个人主页&#xff1a;chian-ocean 文章专栏-Linux 前言&#xff1a; 当一个进程发起某种操作&#xff08;如I/O请求、信号、锁的获取等&#xff09;&#xff0c;但该操作需要的资源暂时不可用时&#xff0c;进程会被操作系统挂起&#xff0c;进入“等待队列”或“阻塞状态”。…

寒假1.25

题解 web:[极客大挑战 2019]Upload 打开环境 上传一个一句话木马试试 只能上传图片那就再上传一次&#xff0c;bp抓包修改type-content为image/jpeg试试 不行 看来是文件后缀被绕过了&#xff0c;上传一个.html然后抓包改类型试试 上传成功了&#xff0c;但是提示‘<&…

C++/stack_queue

目录 1.stack 1.1stack的介绍 1.2stack的使用 练习题&#xff1a; 1.3stack的模拟实现 2.queue的介绍和使用 2.1queue的介绍 2.2queue的使用 2.3queue的模拟实现 3.priority_queue的介绍和使用 3.1priority_queue的介绍 3.2priority_queue的使用 欢迎 1.stack 1.1stack…

【Java】微服务找不到问题记录can not find user-service

一、问题描述 运行网关微服务与用户微服务后&#xff0c;nacos服务成功注册 但是测试接口的时候网关没有找到相关服务 二、解决方案 我先检查了pom文件确定没问题后查看配置文件 最后发现是配置里spring.application.namexxx-user里面服务的名字后面多了一个空格 三、总结…

QT:图像上绘制图形

需求描述 1、展示一张图像 2、在图像上可以使用数据绘制图像&#xff1a;矩形、不规则图形、线条 3、有按键可以选择 概要设计 规划布局如下 1、左边是Qlabel 用于展示图片 2、右边是三个按钮 具体实现 1、 首先设计 UI 界面&#xff0c;对控件进行布局 在 mainwindow.u…

基于java线程池和EasyExcel实现数据异步导入

基于java线程池和EasyExcel实现数据异步导入 2.代码实现 2.1 controller层 PostMapping("import")public void importExcel(MultipartFile file) throws IOException {importService.importExcelAsync(file);}2.2 service层 Resource private SalariesListener sa…

日志收集Day007

1.配置ES集群TLS认证: (1)elk101节点生成证书文件 cd /usr/share/elasticsearch ./bin/elasticsearch-certutil cert -out config/elastic-certificates.p12 -pass "" --days 3650 (2)elk101节点为证书文件修改属主和属组 chown elasticsearch:elasticsearch con…