语雀桌面端技术架构实践

简介: 语雀桌面端作为语雀为用户提供的生产力工具,上线两年多来一直保持高频的迭代和健康的业务增长。本次主要介绍我们在做桌面端时的一些技术架构思考和实践,同时也将分享我们沉淀的一些通用桌面应用解决方案和经验。

image.png

文章会分为四部分,首先会简单介绍语雀桌面端,然后介绍当前语雀桌面端的应用架构以及关键点,之后介绍架构中的几个架构重点项,最后在进行总结。

语雀桌面端介绍

语雀是孵化自蚂蚁体验技术部的一款笔记与文档知识库工具。我们在两年前,针对语雀用户特点,以及后续发展策略,旨在为创作者提供更好的创作体验,推出语雀桌面客户端。

image.png

相较于现有浏览器提供的产品服务而言,我们提供的桌面端产品主要考虑以下几点:

  • 无干扰 :给用户一个沉浸式的创作体验,而不像浏览器有其他窗口、tab 进行干扰,以及用完即走的用户心智。
  • 系统级常驻 :打开速度更快,可以一键启动或者利用各类快捷工具唤起。
  • 集成更多操作系统能力 :提升创作效率的多窗口、系统菜单和快捷键、对文件读写、与系统软件集成等。
  • 离线 :期望能在离线或弱网的情况下,无障碍的进行创作。

image.png

image.png

研发测主要分为左边三层,最底层是语雀的基础设施,依赖了语雀后台提供或封装的大量云服务,以及底层依赖的安全能力和存储模块。

中间一层是比较偏应用架构的一些能力封装,上面是代码层面用的的辅助能力,还有主进程的模块,然后有给应用提供的一些管理能力和一些跟 UI 相关的功能模块,最上层就是基于底层架构搭建的业务应用。包括桌面端应用比较核心的几个模块,以及一些由子应用方式承接的业务模块(后面也会详细介绍子应用这个概念)

同时最右侧也有很多辅助研发的依赖能力,来完成语雀桌面端的发布、质量和稳定性管理。

架构概览 - 关键点

相比较普通 web 应用来说,我们觉得桌面端有以下几个能力比较重要:包括安全、软件升级、以及桌面端通用的的基础能力:

image.png

架构概览 - 安全

安全是一个生产力工具软件的生命线,特别是语雀作为一款知识管理工具,对于安全是非常看重。

基础安全

  • 下载安装包时,需要有安全管理机制,避免下载过程中被恶意替换;
  • 升级到最新的 Electron 版本(语雀目前紧跟官方大版本,同时也会参考微软的头部应用 (vscode),避免有没考虑到的场景;
  • 同时用户离线下载到本地的文件,包括图片,附件等,也需要经过加密。

启动安全

  • 杀毒软件:启动安全主要是在启动软件时的一些安全问题,例如二进制模块是否有加签名,避免被杀毒软件查杀导致无法启动,也可以联系安全厂商加白名单,同时还能提升启动速度。
  • 禁止调试:因为软件代码都会下载到客户端,可以禁止软件在客户端浏览器进行调试。
  • 数据库秘钥管理:本地数据库文件需要保证即使被恶意拿到,也要保证无法查看到里面的内容。我们通过生成内存安全级别的方案,确保其他非当前电脑的语雀软件即使拿到数据库文件后也无法打开。

应用安全

  • 渲染容器设置白名单来控制不被引入恶意页面;
  • 渲染进程关闭 Node 功能以及开启隔离模式,避免渲染进程权限过高;
  • Electron 本身是 web 开发模式,所以 web 中遇到的安全问题,在 Electron 同样会遇到,可以统一处理。

架构概览 - 软件升级

客户端软件相比较于 web 来说,还有一个非常大的区别就是有功能更新时,有一个升级过程,不像 web 直接刷新页面即可。语雀桌面端作为迭代迅速的产品,对于升级这块也是踩了不少坑。

image.png

语雀桌面端由两大部分组成:包括 Electron 和 Node.js 等基础模块的软件包 + 以及自己的业务代码。

Mac OS:Mac 下的升级流程比较简单,软件下载完成后,利用 hdiutil来模拟用户手动安装流程,用户重启即可完成安装。

Windows:Windows 下因为环境特殊性,需要下载安装包后,通过主进程自动打开安装界面,引导用户进行手动一步一步安装。

其实这种方案很好的满足的我们早期的功能迭代,但是随着用户量上涨,也遇到了很多问题。

image.png

比如:

  • 每次升级带宽消耗巨大:对于每次安装每个 UV 都有近 100M 的下载,每次推送版本时,都会遇到 OSS 流量告警,这背后都对应着成本;
  • 安装体验差:Windows 下因为每次升级几乎都是一次新的安装流程,所以体验也是比较差的,经常收到用户吐槽。

所以我们就调研了一种增量更新的方案,一个 Electron 程序包括 Electron 核心包以及业务代码,其实每次变更的仅仅是业务代码,所以理论上每次更新只需要增量更新业务代码即可。

image.png

Mac 下增量直接下载到增量代码后,替换掉即可。

Windows 下比较复杂,我们主要遇到两个问题:

  1. 文件占用问题:由于 Windows 系统特性,如果某个文件在使用,会无法删除。所以说如果要替换,肯定关闭程序,然后进行删除操作后再启动。所以我们写了一个  .exe 可执行文件来做关闭程序、更新文件、启动语雀。
  2. UAB 权限控制:文件写入另一个问题是 C 盘文件一般是需要授权才可以操作的。我们软件启动没办法拿到这么高权限。不过还在 Windows 7 及以后新增了一个 PowerShell 功能,通过这个功能执行,能引导用户授权,拿到更高级的权限。

当然过程中还碰到不少细节问题,比如替换过程中路径中英文问题、用户自定义过环境变量位置问题等等

架构概览 - 基础能力沉淀

另外我们在做软件的过程中,也沉淀了一些与业务无耦合的组件:

image.png

  • 多窗口管理:当给用户提供提供更方便的多窗口编辑能力时,如何去管理这些窗口打开,关闭,性能监控等;
  • Webview:不同编辑器以及子应用都是通过 Webview 来承载的,需要有一个通用的模块来维护系统中用到的各个 webview 的生命周期等;
  • 离线在线:电脑离线和在线状态获取,虽然浏览器有提供这个状态的获取和事件监听,但是 Windows 下不太准确。我们封装了一个比较通用的模块。

桌面端架构重点

从架构上来说,相较于用了多酷炫的技术,更重要的是研发交付效率高不高,性能怎么样,稳定性高不高。我们认为以下三点是架构好坏评判的重要标准。


image.png

image.png

同构过程中的一些经验:

  • 通用代码移动到 Common 目录:语雀代码仓库是 monorepo模式,如果没有很清晰的目录划分,很容易造成跨端兼容问题。有了这个约定以后,业务研发同学就会注意到这个会用在移动端或者桌面端
  • 通过 webpack alias 适配多端:这个方式比较常见,比如各端有不同的网络请求库,在组件层面使用 adapter/request。webpack 在构建时,adapter 映射到不同的端实现。
  • 定义多端代码研发规范:梳理出不同端的一些差异点,在研发时避免采坑。

交付效率(同构问题)

虽然同构模式可以解决我们当时遇到的一些问题,但是随着业务规模和功能增加,陆续有些问题暴露出来:

  • 迁移到 Common 目录,各种依赖问题:很多功能在桌面端之前已经上线,有些复杂组件迁移到 Common 下本身也需要耗费不少时间。
  • 缺少动态化能力,迭代滞后(web 与桌面端功能不一致):组件在 web 发布上线后,桌面端需要发布才支持,所以经常会碰到 web 和桌面端不一致的吐槽。
  • 容易出现多端兼容问题(环境依赖等):虽然我们定义了一些规范,但很难彻底避免出现环境依赖问题。
  • 缺少独立沙箱,容易影响主应用(内存泄露、样式) :web 可能用完即走,可以刷新等,不太容易遇到问题,但是桌面端因为是常驻的,如果有些内存泄露或者样式污染问题,就直接影响到整体可用性。

交付效率(子应用)

考虑到上述原因,我们将代码复用架构升级成子应用模式,利用桌面端容器,嵌套一个 html 在线或者离线页面。

简单来说,子应用模式可以理解为支付宝九宫格进去的各个小程序模块。

image.png

  • 快速迭代:提供独立的发布迭代能力,所以无需跟随桌面端整体发布。而且直接由业务同学跟进整个交付流程,无需桌面端同学参与,提升交付效率。
  • 具备端相关能力:每个子应用默认就能使用桌面端提供的 JSBridge 能力,天然能做到跟桌面端模块一样的能力。
  • 独立沙箱:独立于桌面端主窗口,利用桌面端的容器来完成渲染,所以能完全做到进程级别隔离,相互之间的内存开销一目了然,更好的做到管控,保证整体应用稳定性。
  • 加载初始化:除了上述的一些优势,也会到来一些问题,比如加载速度慢等,我们通过 webview 预热、缓存等方式,一定程度上解决了这个问题。

架构重点 - 性能

性能是一个桌面端软件必须要面对的问题,是需要持续在不同角度进行优化的

image.png

主要包括这几个方面:

  • 启动速度优化:启动速度是用户第一映像,我们主要是将主进程代码进行缓存,尽早展示 loading 避免白屏,主窗口和渲染进程的部分任务同时执行,达到并发效果;
  • 主进程优化:主进程和渲染进程执行是同步的,如果主进程做了太多任务,会导致用户使用起来卡顿,甚至闪退。所以尽量减少主进程所做的事情;
  • Web 优化:同时,之前很多在 web 上做的优化,一样可以拿过来使用。例如懒加载,合并模块等(组合多个模块本身也有开销);
  • Webview 优化:例如预热 webview、并进行一些性能管控措施,避免失控。

性能(持续性任务)

性能优化其实不是说做完哪些事情就可以彻底解决,而是一个长期过程。可能我们后续新增功能时,代码里有某一个内存泄露问题,就很容易导致性能拉胯下来。所以我们也建立了一些持续性的机制:

image.png

主要包括:

  • 日常观测:在开发模式下,建立观测性能指标能力,做到心中有数;
  • 自动化任务:日常也会有自动化任务,模拟真实用户长时间使用,及时发现内存泄露等问题;
  • 性能大盘:对于线上性能水位,能有一个全盘的感知能力,灰度发布过程中可重点关注。

架构重点 - 稳定性

相比较于 web 而言,桌面端的稳定性也是要求更高的。

image.png

image.png

注:UIA 是语雀工程师自研的自动化方案,详见 :Macaca MacOS:https://github.com/macacajs/macaca-macos

image.png

另外建立了稳定性大盘和实时告警,来感知到线上性能情况。

为了保证每次发布的稳定性,减少回归成本,我们利用每周一次预览版发布的敏捷研发模式,来分解大版本发布带来的集成风险。

总结

image.png

  • 针对当前的业务体量和团队经验,选择合适的技术架构;
  • 现在肯定有比 Electron 更新的桌面端架构,比如 flutter、tauri 等,要综合看比如团队积累以及稳定性,是否有成熟的商业化产品等;
  • 性能和稳定性优化是持续性的过程,先建立度量和感知;
  • 交付效率和交付质量最容易被忽视,但却是架构方案的重要考量;
  • 架构好坏的评判标准一定是由业务效果决定的,交付效率和交付质量是衡量业务效果的手段之一。


相关文章
|
4天前
|
Cloud Native 安全 API
云原生架构下的微服务治理策略与实践####
—透过云原生的棱镜,探索微服务架构下的挑战与应对之道 本文旨在探讨云原生环境下,微服务架构所面临的关键挑战及有效的治理策略。随着云计算技术的深入发展,越来越多的企业选择采用云原生架构来构建和部署其应用程序,以期获得更高的灵活性、可扩展性和效率。然而,微服务架构的复杂性也带来了服务发现、负载均衡、故障恢复等一系列治理难题。本文将深入分析这些问题,并提出一套基于云原生技术栈的微服务治理框架,包括服务网格的应用、API网关的集成、以及动态配置管理等关键方面,旨在为企业实现高效、稳定的微服务架构提供参考路径。 ####
24 5
|
8天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
6天前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型浪潮中,企业纷纷拥抱云计算,而云原生架构作为其核心技术支撑,正引领着一场深刻的技术变革。本文聚焦于云原生环境下微服务架构的治理策略与实践,探讨如何通过精细化的服务管理、动态的流量调度、高效的故障恢复机制以及持续的监控优化,构建弹性、可靠且易于维护的分布式系统。我们将深入剖析微服务治理的核心要素,结合具体案例,揭示其在提升系统稳定性、扩展性和敏捷性方面的关键作用,为读者提供一套切实可行的云原生微服务治理指南。 ####
|
6天前
|
消息中间件 缓存 Cloud Native
云原生架构下的性能优化实践与挑战####
随着企业数字化转型的加速,云原生架构以其高度解耦、弹性伸缩和快速迭代的特性,成为现代软件开发的首选模式。本文深入探讨了云原生环境下性能优化的关键策略与面临的主要挑战,通过案例分析,揭示了如何有效利用容器化、微服务、动态调度等技术手段提升应用性能,同时指出了在复杂云环境中确保系统稳定性和高效性的难题,为开发者和架构师提供了实战指南。 ####
19 3
|
6天前
|
运维 Kubernetes Cloud Native
深入理解云原生架构:从理论到实践
【10月更文挑战第38天】本文将引导读者深入探索云原生技术的核心概念,以及如何将这些概念应用于实际的软件开发和运维中。我们将从云原生的基本定义出发,逐步展开其背后的设计哲学、关键技术组件,并以一个具体的代码示例来演示云原生应用的构建过程。无论你是云原生技术的初学者,还是希望深化理解的开发者,这篇文章都将为你提供有价值的见解和实操指南。
|
6天前
|
Kubernetes Cloud Native 持续交付
云原生技术在现代应用架构中的实践与思考
【10月更文挑战第38天】随着云计算的不断成熟和演进,云原生(Cloud-Native)已成为推动企业数字化转型的重要力量。本文从云原生的基本概念出发,深入探讨了其在现代应用架构中的实际应用,并结合代码示例,展示了云原生技术如何优化资源管理、提升系统弹性和加速开发流程。通过分析云原生的优势与面临的挑战,本文旨在为读者提供一份云原生转型的指南和启示。
19 3
|
6天前
|
运维 Kubernetes Cloud Native
云原生技术在现代应用架构中的实践与挑战####
本文深入探讨了云原生技术的核心概念、关键技术组件及其在实际项目中的应用案例,分析了企业在向云原生转型过程中面临的主要挑战及应对策略。不同于传统摘要的概述性质,本摘要强调通过具体实例揭示云原生技术如何促进应用的灵活性、可扩展性和高效运维,同时指出实践中需注意的技术债务、安全合规等问题,为读者提供一幅云原生技术实践的全景视图。 ####
|
9天前
|
监控 API 持续交付
后端开发中的微服务架构实践与挑战####
本文深入探讨了微服务架构在后端开发中的应用,分析了其优势、面临的挑战以及最佳实践策略。不同于传统的单体应用,微服务通过细粒度的服务划分促进了系统的可维护性、可扩展性和敏捷性。文章首先概述了微服务的核心概念及其与传统架构的区别,随后详细阐述了构建微服务时需考虑的关键技术要素,如服务发现、API网关、容器化部署及持续集成/持续部署(CI/CD)流程。此外,还讨论了微服务实施过程中常见的问题,如服务间通信复杂度增加、数据一致性保障等,并提供了相应的解决方案和优化建议。总之,本文旨在为开发者提供一份关于如何在现代后端系统中有效采用和优化微服务架构的实用指南。 ####
|
11天前
|
消息中间件 设计模式 运维
后端开发中的微服务架构实践与挑战####
本文深入探讨了微服务架构在现代后端开发中的应用,通过实际案例分析,揭示了其在提升系统灵活性、可扩展性及促进技术创新方面的显著优势。同时,文章也未回避微服务实施过程中面临的挑战,如服务间通信复杂性、数据一致性保障及部署运维难度增加等问题,并基于实践经验提出了一系列应对策略,为开发者在构建高效、稳定的微服务平台时提供有价值的参考。 ####
|
11天前
|
Cloud Native API 云计算
云原生架构的深度探索与实践####
本文深入探讨了云原生架构的核心概念、技术特点及其在现代软件开发中的应用实践。通过分析云原生架构如何促进企业数字化转型,提升业务敏捷性与可扩展性,本文旨在为读者提供一个全面而深入的理解框架。我们将从云原生的定义出发,逐步深入到其关键技术组件、最佳实践案例及面临的挑战与解决方案,为开发者和企业决策者提供宝贵的参考与启示。 ####