面试题

11 阅读20分钟

ci/cd相关

1.首先是环境嘛,所有开发、测试、生产环境统一使用 Docker 容器化,通过 Dockerfile 固化依赖,比如前端 Node.js 版本、系统库依赖等,让每个环境的运行时一致不然可能出现开发环境能运行但是生产环境报错的问题 2.第二个是git分支,我这边就用git flow模型去说吧 首先会分为三种分支 分别为开发分支,测试分支和生产分支,这边会控制只有测试和生产分支触发cicd流程,开发分支只做本地测试 3.私有仓库搭建,一般会使用GitLab自带的 Container Registry 作为私有镜像仓库,然后配置仓库权限,服务器只有拉取的权限没有推送权限,保证镜像安全

第二步 ci阶段 自动构建 + 测试 + 镜像打包 其实这也是我觉得最重要的一步 这一步的核心是 “把代码变成可部署的镜像”,由 GitLab CI 的 Pipeline 自动化触发,提交代码、合并分支时自动执行Pipeline 我们创建一个gitlab-ci.yml来配置 具体配置的话主要就是一个触发命令吧我觉得,然后我们分为三点去说,首先一个是构建前的校验 那么这边就用前端三剑客(ESLint+Prettier+styleLint)去做 然后使用 Vue Test Utils做单元测试 这边一个测试覆盖率需要达到一定程度才可以进行下一步,其次的话做一个应用构建,像我们是基于 Node.js 镜像,执行 npm install(这边要使用 package-lock.json 锁定依赖版本,避免依赖版本混乱)然后 npm run build 生成 dist 目录,然后将 dist 目录打包成 Nginx 基础镜像,最后是镜像标签不使用latest,不然的话版本可能造成混乱,然后采用「分支名 - commit 哈希 - 构建时间」的格式(比如 develop-7a3f2d-20240520)作为镜像标签,方便日后的一个回滚时候快速定位版本

第三步 cd阶段

这边主要是镜像推送 + 服务器部署(Docker + 蓝绿部署)首先是镜像推送CI 阶段打包完成后,GitLab CI 会通过配置好的私有仓库地址,将镜像推送到私有仓库,推送完成后会记录镜像标签到 GitLab 的部署记录中 然后是**服务器拉取镜像 + 蓝绿部署 **服务器端通过 GitLab Runner 或 SSH 脚本接收部署指令 采用蓝绿部署策略:服务器上同时运行两个相同配置的容器组(蓝组、绿组),当前提供服务的是蓝组;部署新镜像时,先启动绿组容器(拉取新镜像,执行 docker run -d --name app-green -p 8081:8080 镜像标签),等待绿组健康检查通过(docker inspect --format '{{.State.Health.Status}}' app-greenhealthy); 切换流量:确认绿组正常后,停止蓝组容器(docker stop app-blue && docker rm app-blue),并将绿组的端口映射切换到对外端口(比如从 8081 切换到 8080)

最后是回滚机制:如果部署后发现问题(比如监控告警、接口报错),直接启动之前的蓝组镜像(因为蓝组容器只是停止,镜像还在本地),切换流量回蓝组,再删除有问题的绿组容器,实现 “秒级回滚”。 (下面的可以随便说说不答)

四、访问层:Nginx 反向代理 + 性能优化 Nginx 主要做“流量入口” 和 “性能优化器”: 反向代理:前端静态资源直接由 Nginx 提供服务(因为前端镜像本身就是 Nginx 基础镜像,部署后直接监听 80 端口);后端接口请求通过 Nginx 反向代理到后端容器(比如 /api/* 转发到 http://localhost:8080),同时配置负载均衡(如果是多台服务器,通过 upstream 配置轮询策略); 缓存策略:对前端静态资源(JS、CSS、图片、字体)配置强缓存 + 协商缓存 —— 通过 expires 30d 给静态资源设置 30 天强缓存,同时给文件名添加 hash(比如 app.[hash].js),更新版本时 hash 变化,自动失效旧缓存;对接口请求配置协商缓存(ETagLast-Modified),减少重复请求; 压缩与安全优化:开启 gzip 压缩(压缩 JS、CSS、HTML,压缩级别 6),减少传输体积(通常能压缩 60% 以上);配置 gzip_types 包含常见的 MIME 类型,避免漏压缩;同时配置 CORS 跨域规则、HTTPS(通过 ssl_certificate 配置证书)、屏蔽不必要的响应头(比如 Server: Nginx),提升安全性。

监控与问题排查整个 CI/CD 流程不是 “部署完就结束”,而是有完整的监控闭环:

  1. Pipeline 监控:通过 GitLab CI 的可视化界面查看 Pipeline 运行状态,失败时会自动发送告警(邮件、企业微信 / 钉钉),并附带失败日志(比如测试失败的用例、镜像推送失败的原因);
  2. 应用监控:前端通过 Sentry 监控 JS 报错,通过百度统计或自研埋点监控页面加载速度;
  3. 部署日志:服务器上记录每次部署的脚本执行日志(比如镜像拉取时间、容器启动状态、健康检查结果),如果部署失败,可快速定位是镜像拉取失败、端口冲突还是健康检查不通过。

遇到的问题及解决方案

问题 1:前端构建时依赖下载慢,导致 Pipeline 超时 解决方案:在 GitLab Runner 上配置 npm 私服(比如 Nexus),缓存依赖包,同时在 .gitlab-ci.yml 中配置依赖缓存(cache: paths: - node_modules/),第二次构建时直接复用缓存,构建时间从 15 分钟压缩到 3 分钟; 问题 2:蓝绿部署时端口冲突(比如旧容器没停干净,新容器启动失败) 解决方案:部署脚本中先检查端口占用(netstat -tulpn | grep 8080),如果占用则强制停止对应容器,再启动新容器;同时给容器名称添加固定后缀(蓝组 app-blue,绿组 app-green),避免名称混乱 问题 3:镜像体积过大,拉取速度慢 解决方案:使用 Docker 多阶段构建(比如前端构建阶段用 Node.js 镜像,打包阶段用 Nginx 镜像,只复制 dist 目录,丢弃构建依赖),前端镜像体积从 1.2G 压缩到 50M 左右

灰度测试

物联网方向

首先我们应该采用的是EMS能源管理系统吧,那么EMS是什么 EMS就是通过软硬件系统,对能源的生产、存储、传输和消费进行监控、分析和优化,实现节能降耗和智能化管理。 那他是怎么做到的呢 一般来说 会通过各种传感器、通信网络,把设备、电表、储能、分布式能源互联,实现数据采集、远程控制和智能决策。

这边我主要讲一下物联网方向和云平台方面的吧 首先是物联网 这边设备端的协议我不是很熟悉 我了解到的是Modbus这种有线方式去进行设备端的传输拿到信息,至于前端方面我觉得主要还是用ws或者mqtt多一点 当然最有可能用的协议还是mqtt 其他比如4g/5g,基站传输我不太了解这里不做过多的说明 然后通过mqtt把数据通过网关上传至云平台,实现集中存储和统一管理。进而根据一些算法对能源使用进行一些优化,比如负荷均衡,避免高峰期用电超载等

为什么要做EMS管理系统云平台

物联网+云平台的优势

  • 实时性:任何异常或能耗波动都能快速发现和响应。
  • 可扩展性:新设备接入简单,适合分布式能源管理。
  • 智能化:通过数据分析和AI优化,实现自动调度和预测。
  • 远程化:管理者可以随时随地通过云端操作EMS系统,无需现场巡检。

网络协议相关

MQTT

首先他是轻量级发布/订阅消息协议,基于TCP/WS通信模式:客户端-服务端通过Broker(中间代理)进行发布/订阅

特点

轻量化,适合低带宽和不稳定网络

长连接,可保持持久会话

QoS(消息质量等级):0(最多一次)、1(至少一次)、2(正好一次)

支持离线消息,客户端掉线后可接收遗留消息

双向通信(客户端可以订阅/发布,服务端通过Broker转发)

优点

高可靠性,可保证重要消息不丢失

消息模式灵活,可大规模设备同时通信

网络消耗低,适合物联网设备

缺点 对实时性要求极高(毫秒级)场景略逊于WebSocket

那么iot方向最好还是用MQTT

WebSocket(WS)

类型:应用层双向实时通信协议,基于TCP,客户端与服务端建立长连接,双方可主动发送消息 特点:全双工:客户端和服务端都可以发送和接收消息 长连接:一次TCP握手建立连接之后不需要重复建立连接 延迟比mqtt还低 缺点

需要客户端保持在线,掉线后需要重连

消息可靠性需要应用层自己保证(不像MQTT有QoS)

SSE(Server-Sent Events)

  • 类型:单向实时通信协议,基于HTTP/HTTPS

  • 通信模式:服务端主动向客户端推送消息,客户端无法主动发送(单向)

    特点**:

    • 单向流:服务端可以不断推送事件到客户端
    • 长连接:保持HTTP连接不断开,实时更新数据
    • 自动重连:浏览器端内置重连机制
    • 轻量,无需额外协议,直接使用HTTP/HTTPS

    优点**:

    • 简单实现,浏览器原生支持
    • 无需维护复杂连接状态
    • 可通过HTTP/HTTPS穿透防火墙

    缺点**:

    • 单向通信:客户端无法发送数据,需要额外HTTP请求
    • 不能处理大规模双向消息
    • WebSocket在复杂交互和延迟要求高的场景更优

    应用场景

    EMS管理端实时能耗数据更新(只需服务端推送)

    告警信息更新、状态刷新

    浏览器端实时通知

    HTTPS长轮询(Long Polling)

    • 类型:基于HTTP/HTTPS的客户端轮询技术
    • 通信模式:客户端发起HTTP请求,服务器在有新数据前保持连接不返回响应,直到有数据或超时才返回,然后客户端立即重新发起请求
    • 特点
      • 模拟实时性:虽然基于HTTP,但通过延长响应等待时间实现“准实时”推送
      • 单向通信:客户端请求,服务器响应,客户端无法直接推送数据
      • 兼容性好:使用标准HTTP/HTTPS,无需额外协议或浏览器插件
    • 优点
      • 简单实现,适合传统HTTP架构
      • 可穿透防火墙和代理
      • 浏览器端支持好,无需WebSocket或SSE
    • 缺点
      • 每次响应后都需要客户端重新发请求,开销比长连接大
      • 延迟略高,不能毫秒级响应
      • 不适合大规模、高频率消息推送
    • 应用场景
      • EMS管理端轻量告警或状态刷新
      • 浏览器端需要实时数据,但网络环境不支持WebSocket或SSE
      • 小规模系统的“准实时”数据推送
特性MQTTWebSocketSSEHTTPS长轮询
通信模式发布/订阅(Broker)双向全双工单向服务器→客户端单向客户端请求→服务器响应
连接类型长连接(TCP)长连接(TCP)长连接(HTTP/HTTPS)短连接模拟长轮询
实时性高(延迟低)中高中等,延迟略高
双向通信是(通过Broker)
消息可靠性高,可配置QoS低,需要应用层保证低,依赖HTTP重发机制
网络开销高,频繁HTTP请求重连开销大
适用场景物联网设备通信、远程控制、告警实时监控、人机交互、可视化浏览器端实时刷新状态兼容老旧浏览器、网络环境不支持WebSocket/SSE

TCP 与 UDP 对比表

特性TCPUDP
连接方式面向连接,需要三次握手无连接,直接发送
可靠性高,保证顺序和完整性低,不保证顺序和重传
传输模式流式传输数据报传输
速度较慢快,延迟低
流量控制
拥塞控制
消息顺序保证不保证
协议开销
应用场景HTTP/HTTPS、FTP、SMTP、数据库同步、EMS关键命令视频直播、游戏、传感器数据上传、广播消息

TCP三次握手(建立连接)

目的:在通信前,确保客户端和服务端都能正常收发数据,并同步序列号。

过程

  1. SYN(同步)

    • 客户端发送一个带 SYN 标志的数据包给服务端,请求建立连接
    • 数据包中包含客户端初始序列号(ISN)
  2. SYN-ACK(确认)

    • 服务端收到客户端SYN,发送一个带 SYN 和 ACK 的数据包
    • ACK确认客户端的序列号,SYN包含服务端自己的初始序列号
  3. ACK(确认)

    • 客户端收到服务端SYN-ACK后,发送一个ACK确认
    • 双方连接建立完成,可以开始数据传输

    TCP四次挥手(关闭连接)

    目的:双方确认不再发送数据后安全断开连接。

    过程

    1. FIN(结束发送)
      • 客户端发送 FIN 给服务端,表示客户端数据发送完毕,但可以接收数据
    2. ACK(确认)
      • 服务端收到 FIN 后,发送 ACK 确认
      • 此时客户端进入 FIN-WAIT-2 状态,等待服务端关闭
    3. FIN(服务端结束发送)
      • 服务端数据发送完毕,发送 FIN 给客户端
      • 表示服务端也结束发送
    4. ACK(确认)
      • 客户端收到 FIN,发送 ACK 确认
      • 双方连接彻底关闭

握手挥手不成功

“如果三次握手过程中丢包,TCP会自动重传SYN或ACK包,重试若干次后仍失败,则客户端连接建立失败,这保证了可靠性和容错性。” “四次挥手过程中,如果ACK丢失,TCP会重传保证双方确认关闭。客户端会进入TIME-WAIT状态,确保最后的ACK被服务端收到,最终安全释放资源。若重传失败,操作系统也可通过RST强制关闭连接。”

用户输入url 浏览器做了什么

当用户在浏览器输入 URL 后,浏览器首先会判断这是搜索还是 URL,并对 URL 进行解析,然后依次检查本地的缓存(包括 Service Worker、memory cache、disk cache)是否命中。若未命中缓存,则开始通过 DNS 解析域名,获取目标服务器的 IP 地址。接着浏览器与服务器建立 TCP 连接,如果是 HTTPS,则还需额外完成 TLS/SSL 握手以建立安全通道。连接建立成功后,浏览器构建并发送 HTTP 请求报文。请求经过网络层、运营商、CDN 等中间节点后到达服务器,服务器根据路由解析请求,执行权限校验、参数解析、业务逻辑处理,并可能访问数据库、缓存或调用其他服务,然后生成响应结果并返回给浏览器。浏览器收到响应后解析响应头、处理缓存策略、设置 cookie,并根据内容类型解析 HTML、CSS、JS 或 JSON 数据,构建 DOM 树和 CSSOM,再经过渲染树构建、布局、绘制与图层合成,将页面最终呈现在用户面前;如果页面还包含 JS 异步请求,浏览器会继续发送请求并根据返回的数据更新页面视图。

dns劫持

如果 DNS 被劫持,浏览器可能将域名解析到错误 IP,用户访问可能被重定向到钓鱼网站或恶意服务器。HTTP 明文数据可能被窃取或篡改,而 HTTPS 会通过证书验证阻止中间人攻击。此外,DNS 劫持还会导致访问失败或延迟增加,影响缓存和 CDN 的正常使用。防护手段包括使用 HTTPS、DNSSEC、可信 DNS 以及浏览器安全机制如 HSTS

RBAC相关

首先RBAC是什么 RBAC是一种权限管理模型,用于控制系统中 用户对资源的访问权限。它的核心思想是:用户不直接赋予权限,而是通过角色间接获得权限。 先从表设计说吧 首先我会设计用户表 角色表 权限表 然后每个角色表里面的每个角色分别有什么权限 然后给用户去分配角色 比如说我的行政角色有管理员工账号权限 财务表可以查看员工工资权限 那我可以给用户一设置一个行政角色和财务角色去进行绑定 然后他就既有可以管理账号也有查看工资的权限
在前端实现上,主要通过 动态路由和权限拦截 来控制页面访问。前端根据用户所拥有的角色权限来渲染可访问的菜单、页面或按钮,从而实现细粒度的操作控制,同时避免未授权访问。

大文件分片上传

我们首先利用 Blob.prototype.slice 方法将大文件切割成一个个小的分片,然后为每个分片构造一个 FormData 对象,通过并发或可控的并发量将这些分片逐一上传到服务端。同时,前端需要记录每个分片的上传状态,并实现断点续传和错误重试机制。在所有分片成功上传后,最后再发送一个请求通知服务端进行分片合并,从而完成整个文件的上传

断点续传

前端使用文件的MD5哈希值作为唯一标识。在上传分片前,先请求接口服务端“哪些分片已经上传了?”前端只上传那些“未上传”的分片,实现断点续传

秒传

在开始整个上传流程前,前端将文件的MD5发送给服务端进行验证。看是否有完全相同的文件 如果有的话那就直接返回上传成功

并发控制:

维护一个“待上传队列”,使用类似Promise.allSettled或第三方库(如p-limit)来控制同时进行的上传请求数量(例如,一次只发3-5个)。

错误重试

为每个分片设置一个重试计数器(例如3次)。当某个分片上传失败时,将其重新放回队列进行重试,直到成功或重试次数用尽。

多租户设计

我能想到的就是数据隔离 有两种做法 第一种是分表隔离 第二个是分数据库隔离 然后的话就是子域名去确认请求属于那个租户

大前端

“Flutter、React Native 和 Uni-app 是三种主流的跨端开发方案,它们的核心区别在于渲染原理和技术栈。简单来说,RN 是基于 React 的原生组件封装,Flutter 是自绘引擎,而 Uni-app 是基于 WebView 的编译时转换。”

核心区别对比表

维度React Native (RN)FlutterUni-app
核心原理使用 JavaScript 编写逻辑,通过 Bridge 通信将虚拟DOM映射为原生组件进行渲染。使用 Dart 编写,直接通过 Skia 图形引擎绘制 UI,不依赖原生组件使用 Vue 语法,将代码编译到不同平台(小程序、Web、App),App端主要通过渲染到 WebView 或小程序引擎。
技术栈JavaScript / TypeScript + ReactDartVue.js + JavaScript
性能接近原生,但 Bridge 通信可能存在异步延迟和性能瓶颈。最高,编译为原生代码,自绘 UI,性能最接近原生,且流畅稳定。相对较低,App端受限于 WebView 性能,在复杂动画和交互上体验有差距。
一致性一般,UI 依赖原生组件,不同平台(iOS/Android)的组件外观和行为有差异。极高,UI 自己绘制,保证在不同平台上外观和行为完全一致。,一套代码多端运行,但各端(尤其是小程序)会有平台差异性需要处理。
开发体验热重载良好,依赖原生环境调试,有时需要懂原生知识来排查问题。热重载极佳,所有UI和逻辑一站式完成,对前端开发者更独立基于熟悉的 Vue 技术栈,学习成本低,一套代码多端发布,开发效率高。
动态化支持 CodePush 等热更新方案。官方不支持,发布需要走应用商店审核(但有一些非官方方案)。小程序端有天然的动态性,App端也有相应的热更新机制。
生态与社区社区成熟,生态丰富,有大量第三方库。社区增长迅猛,生态高质量,但相比 RN 稍显年轻。生态主要围绕小程序和 Vue,功能库丰富。

自我介绍

两位面试官你们好 首先感谢你们给我这次面试机会 我叫袁谷音 有差不多六年前端开发工作经验 我自己目前常用的技术栈有vue3 uniapp等 后端java也了解一些 在目前的这家公司的话我主要是担任一个前端组长的一个角色 主要是负责ZMR社交App Sams电务管理系统 还有一个protest是一个桌面端主要是给流水线工人做设备的蓝牙和Nfc测试用的 这也是我刚进入公司从0开始的项目 ZmrApp主要是打造一个通过社交驱动产品 智能电子烟的玩,社交和购买的一个生态闭环 主要的用户地区我们是在肯尼亚 中东和欧洲 今年的五月份我们的迪拜展会也非常成功 然后这个App主要的业务模块有设备交互 社交板块 商城板块等 但是商城板块的话因为谷歌和ios的上架政策被我们砍掉了 之后我们会设计一个H5的独立站方式去做 其中我觉得这个项目最复杂的就是设备交互以及社交 特别是社交 因为涉及到了聊天 我是直接学习微信的方式 通过MQTT和Sqllite本地数据库去做的 然后是设备 设备主要是靠低功耗蓝牙去做通讯 让我们app实时拿到我们烟的一个抽吸时间和次数数据 烟油等 然后我们app也可以控制我们的电子烟的功率 或者是寻找设备之类的一些交互功能 其实设备的嵌入式我们公司是给别的团队做 因为我们公司架构方面比较复杂 我们的技术总监带了特别特别多的团队 光是我们这栋楼都有好多家公司 然后因为这样的话其实跨团队的沟通会比较困难 然后我在准备面试的时候有去查看天邦达的官网 是专业从事储能锂电池管理系统和储能整体解决方案 那储能方案的话我也了解了一下基本是用EMS管理系统 而EMS管理系统的顶层设计上我觉得和我ZMR也有点类似 都是终端设备数据采集 状态监控和远程控制这一方面 我相信我的经验能够快速融入到贵公司业务。以上就是我的简单介绍,谢谢两位面试官