跨桌面端Web容器演进

简介: 随着客户端技术的逐渐发展,Web容器已经深入客户端,为客户端构建了丰富多彩的页面和交互体验,成为客户端的一个不可或缺的重要组成部分。在千牛客户端上,Web容器不仅承载了千牛的开放容器(经历了H5插件到千牛小程序的变革),而且一些核心功能,比如聊天窗口,消息中心,也都是由Web容器承载的。

一开始,Web容器在不同的操作系统有各自的选择,比如在Widnows上的IE内核,而在Macos用WKWebview。但是随着Chrome的发展,渐渐的Chromium开始一统江湖。大家也开始基于开源的Chromium定制自己的内核。自己定制的内核不仅有着跨端的特性,而且能很好的支持前端的业务诉求。千牛也在此时定制了自己的web容器AEF,全称Application Embedded Framework。


容器的架构演进


 Application Embedded Framework(AEF)


AEF是千牛自己是基于chromium源码定制的内核。

image.png

概括来看AEF框架主要封装了如下功能

  1. 控件接口,暴露接口给PC客户端,比如支持浏览网页,前进,后退,刷新,强制刷新,中止等功能
  2. 事件响应,AEF上面开放了很多事件出来,供应用来决定是否处理相应的事件,比如文档加载完成,页面加载失败,资源加载失败,页面发生崩溃,标题发生刷新,url发生重定向等。
  3. JS和本地C++的交互,这个是通过render delegate直接向WebKit注册native sdk的dll,再通过事件发送到PC客户端完成的。
  4. 丰富的扩展功能,比如定制自定义协议alires,完成本地包的加载和支持打印等功能。


 Chromium Embedded Framework (CEF)


早期AEF已经能很好的满足我们的需求。但随着开源技术的发展。CEF已经涵盖了AEF大部分的功能。同时为了解决安全漏洞,防止被灰产利用,为了更好跟进前端技术的发展,我们有了升级内核的需求。因为Chromium变化巨大,如果在AEF的基础上升级内核,我们不仅需要基于Chromium重新构建嵌入式框架,而且需要将原有功能重新实现一遍。而CEF是由 Marshall Greenblatt 于 2008 年创立的基于BSD 许可的开源项目,CEF 专注于促进第三方应用程序中嵌入浏览器框架,使用户免受底层 Chromium 和 Blink 代码复杂性的影响。所以如果我们选择CEF,可以免去一些基本的功能开发,而将更多的关注放在定制功能的开放上,从而提高我们的研发效率。

image.png


 容器框架切换


于是,我们决定将Web容器框架从AEF升级到CEF,并且在千牛上做了相应的改造。以其能平滑的迁移到CEF框架之上。Chromium本身是多进程架构的,Chromium为了保护应用程序不受某个页面的影响,将js引擎和渲染部分都独立到了Render进程。而且在新的内核中,网络,本地存储都隔离到Render进程。而Browser则对应应用程序的UI管理进程。在通常的CEF框架应用程序中,应用程序即担负起Browser进程的责任,应用程序整个UI消息泵由CEF托管。这也是合情合理的,因为Browser本身就是UI的管理进程,要响应窗口的创建销毁事件,所以,一开始,千牛是包含了Chromium的主进程的。但是,正如Chromium架构本身,Render的崩溃会影响应用程序的稳定性,Browser的异常一会影响到千牛的稳定性。为此,我们决定将Browser进程从千牛进程中剥离,从而保证千牛进程的独立性。在Browser崩溃时,我们还做了守护,保障Browser进程在崩溃后能自行恢复。

image.png

Browser进程隔离的前提是操作系统支持父子窗口能够跨进程,千牛中是由webview容器决定窗口的位置,并将句柄告知浏览器进程。并且Windows系统下,父子窗口可以在不同的进程中通信,但MacOS系统限制了这一点。为此我们需要一种灵活的方式,使得 Windows系统下Browser进程能独立于千牛进程,而MacOS下千牛和Browser则是一体的。为此,我们借鉴Chromium内核的mojo通信,设计了一套RPC通信框架,用于千牛和Browser之间的通信。又借助组件化编程的思维,设计了远程组件的概念。让工程可以想搭积木式的选择自己的架构。


 远程组件


组件,大家都知道,它是模块化编程的一种方式,有着高内聚低耦合的特点。利用它可以实现搭积木式的编程。我们的组件也是学习了Microsoft的COM技术来实现的。那么远程组件是什么概念呢?远程组件也是组件,但它不提供真正的服务,一般情况下需要通过RPC连接到真正的组件上,通过RPC调用进而提供服务。使用者不关心自己调用的组件是远程组件还是真正提供服务的组件。而工程化时只需要通过搭积木的拼接部署,就可以实现产品的架构调整。我们这里在千牛中,就是通过这种方式实现的。

image.png

熟悉COM的人都知道,组件中有一个ClassID的概念,可以叫他类ID,意思一套组件化接口可以有多种实现。我们这里WebViewRemote和WebViewLocal实际上都是WebView,只是一个ClassID是Remote,另一个是Local。Remote即远程组件,而Local则是真正提供服务的组件。后续加入我们能提供多种进程间通信方式的话,我们的ClassID还可以根据通信方式来扩展,比如zmq,mojo等。目前我们只实现了一套基于Chromium IPC的进程间通信方式,在IPC通信的基础上,我们加上了rapid_json的序列化方式。封装到了RPCFramework中,为我们的远程组件提供通信支撑。


 全新JS注入方式


不管是AEF和CEF,JS的注入和本地C++的通信都是业务的一个重要基点。但从结构中我们看到js的注入和c++的交互代码通常会封装在一个dll插入Render进程中,而业务通常在Aliworkbench千牛进程中。中间需要Browser进程进行事件的转发作为通信的桥梁。为此增加一个js注入节点需要3个不同地方实现相应代码,开发十分不方便。在新的WebView内部,我们提供了一个灵活的接口,在接口内,通过远程组件对通信进行了封装,并且自动创建的js节点注入的代码。使调用者更多的关注业务的本身,而无需关心内部注入流程、js和c++交互流程和序列化相关等这些繁琐的步骤。

image.png

容器的功能增强和稳定性保障


 Render分组优化


在Browser进程隔离完成后,我们对Render进程也做了分组优化。在之前的Chromiun的多进程图中,我们知道Chromium会根据页面的不同,创建多个Render进程。Chromium的Render进程分组策略是由站点的Origin决定的,Origin是url的scheme和host的组合,不同的Origin对应Siteinstance不同,进而对应的Render进程就会不同。但这种策略在千牛中却显的不是很合适。因为多个Render进程势必会照成一些不必要的开销和内存的浪费。为此我们设计了一种新的策略,为Render分组,在创建WebView的时候即指定RenderID, 在此Webview上的的页面和子iframe都会在RenderID对应的Render进程中运行。基于这个特性,我们将千牛聊天记录页面放到一个专属的Render进程中,千牛的二方页面放在一个独立的Render进程,还有三方页面,插件页面,小程序页面等都进行了分组。这样做既节省内存开销,同时保证了核心页面的稳定性,还大大额方便了问题定位和调试。


image.png

 网络收口,打造安全容器


近年来,黑灰产日益猖獗,对我们的容器安全性带来了一定的挑战。因为H5过于开放的模式,导致我们的数据被泄露。为此我们决定管控我们端侧的容器的所有网络请求,包括WebSocket和XMLhttpReqest。而原生的Chromium内核并未开放拦截的卡口,所以CEF也并未支持WebSocket的拦截,仅支持网络资源的请求的拦截。为此我们打算通过定制内核,提供统一的网络收口,并联合白名单机制,打造一个安全的web跨端容器。并且为了支持二次开放等多样化的拦截需求,我们还支持ifame级别的拦截,使用方可以根据iframe的id来做区分。

image.png

 DNS解析拦截,支持AMDC


DNS在互联网中的一个很重要的服务,dns解析出现异常,任务服务再强大也无法访问。历史上也出现了很多因为DNS异常导致的故障。为了重点保障我们的核心页面,我们希望能够在DNS异常的情况下,仍然能够证确的访问目标。为此我们拦截的内核的DNS解析流程,通过AMDC的动态配置,保障我们的核心域名稳定。通过引入AMDC HTTPDNS方案 整体千牛mtop网关成功率从99.6%提升到99.8%。

image.png

 HTML Node节点打印功能


千牛打单可谓是千牛商家非常常用的功能了,因为发货单或者快递单的特殊性,客户往往不需要打印整个页面。开发者需要打印当前页面的一个html node节点,比如某个iframe,或者某个div。但原生的chromium支持整页打印的。我们需要为小程序提供node节点打印,为此我们深入定制了内核打印接口。

image.png

 iframe增加appid自定义属性


PC小程序中的LocalWebview是利用iframe实现的,为了能够让不同的localWebView能证确的路由到小程序包,我们在iframe的标签中增加了自定义属性appid的解析。千牛端在识别appid后可以把资源获取路由到对应的小程序包中。小程序利用这一点支持了插件webview功能,从而实现了丰富的二次开放。


 UserAgent增强


原生的UserAgent是跟随固定名称如Chrome/98.0.4758.102,一个应用程序中,UA是固定的,不允许随意更改。但我们提供WebView容器级别的UA定制。即同在千牛中,不同的WebView的UserAgent 可以增加自定义字段,从而实现在H5插件的WebView和小程序的WebView可以展现不同的UA。利用者一点,ISV可以实现一套代码同时应用在插件和小程序上,完成自己特定的功能。


 清理浏览器缓存


浏览器的缓存大大提升了浏览器加载速度,但是偶儿因为自身或服务端的异常,会导致浏览器缓存异常。我们通过在CEF中增加接口,沿袭Chromium内部清理缓存流程,实现了可以在不退出应用程序的情况完成缓存的清理。使缓存出现异常的情况下能够恢复。


未来的演进


 更好的内存回收策略


CEF的框架下,我们沿用了AEF时期的内存回收策略,更多的是在业务测进行。比如在判断用户空闲时对于不活动的WebControl进行回收。在判断单Render进程内存超过一定阈值时重新启动相应Render进程。但这些都会损失一定的用户体验。所以我们打算借鉴钉钉之前研究过的Tab休眠策略,让不活动的页面仅保留缩略图,从而在不损失用户体验的情况下,实现内存的优化。


 白屏检测


之前我们做过相应的白屏检测的研究。有区于依赖前端技术的白屏检测,客户端做的检测会更贴近用户真实体验。因为前端只能通过检测DOM树的变化来感知,但是否在真的渲染以及真实呈现在用户视觉上的东西是无法感知的。我们通过检测Render最后生成的渲染帧的绘制指令,以及观察最后窗口的贴图动作能最接近用户测的感知到是否白屏。但在之前预研中,我用统计出来的白屏却高于正常值,需要进一步调研,也欢迎有兴趣的同学一起探索。

相关文章
|
8月前
|
运维 数据可视化 C++
2025 热门的 Web 化容器部署工具对比:Portainer VS Websoft9
2025年热门Web化容器部署工具对比:Portainer与Websoft9。Portainer以轻量可视化管理见长,适合技术团队运维;Websoft9则提供一站式应用部署与容器管理,内置丰富开源模板,降低中小企业部署门槛。两者各有优势,助力企业提升容器化效率。
511 1
2025 热门的 Web 化容器部署工具对比:Portainer VS Websoft9
|
前端开发 安全 数据库
Web架构&前后端分离站&Docker容器站&集成软件站&建站分配
Web架构&前后端分离站&Docker容器站&集成软件站&建站分配
382 1
|
资源调度 JavaScript 前端开发
IM跨平台技术学习(十一):环信基于Electron打包Web IM桌面端的技术实践
这次借着论证 Web IM端 SDK 是否可以在 Electron 生成的桌面端正常稳定使用,我决定把官方新推出的 webim-vue3-demo,打包到桌面端,并记录了这次验证的过程以及所遇到的问题和解决方法。
456 2
|
开发者 Docker Python
从零开始:使用Docker容器化你的Python Web应用
从零开始:使用Docker容器化你的Python Web应用
689 4
|
机器学习/深度学习 数据采集 Docker
Docker容器化实战:构建并部署一个简单的Web应用
Docker容器化实战:构建并部署一个简单的Web应用
|
Web App开发 前端开发 JavaScript
Web前端项目的跨平台桌面客户端打包方案之——CEF框架
Chromium Embedded Framework (CEF) 是一个基于 Google Chromium 项目的开源 Web 浏览器控件,旨在为第三方应用提供嵌入式浏览器支持。CEF 隔离了底层 Chromium 和 Blink 的复杂性,提供了稳定的产品级 API。它支持 Windows、Linux 和 Mac 平台,不仅限于 C/C++ 接口,还支持多种语言。CEF 功能强大,性能优异,广泛应用于桌面端开发,如 QQ、微信、网易云音乐等。CEF 开源且采用 BSD 授权,商业友好,装机量已超 1 亿。此外,GitHub 项目 CefDetector 可帮助检测电脑中使用 CEF
3665 3
|
开发者 容器 Docker
JSF与Docker,引领容器化浪潮!让你的Web应用如虎添翼,轻松应对高并发!
【8月更文挑战第31天】在现代Web应用开发中,JSF框架因其实用性和灵活性被广泛应用。随着云计算及微服务架构的兴起,容器化技术变得日益重要,Docker作为该领域的佼佼者,为JSF应用提供了便捷的部署和管理方案。本文通过基础概念讲解及示例代码展示了如何利用Docker容器化JSF应用,帮助开发者实现高效、便携的应用部署。同时也提醒开发者注意JSF与Docker结合使用时可能遇到的限制,并根据实际情况做出合理选择。
252 0
|
Kubernetes 应用服务中间件 nginx
基于容器化的Web服务器管理
【8月更文第28天】随着云原生技术的发展,容器化已经成为部署和管理应用程序的标准方式之一。Docker 和 Kubernetes 等工具提供了强大的容器管理和编排能力,使得开发者能够轻松地部署、扩展和维护 Web 服务器。本文将详细介绍如何使用 Docker 和 Kubernetes 实现 Web 服务器的容器化部署,并提供详细的步骤和代码示例。
592 1
|
Kubernetes 安全 JavaScript
Docker 与 Podman:探索用于现代 Web 开发的容器技术
在软件开发中,Docker 和 Podman 作为主流容器化技术,各具特色。本文深入对比两者优缺点,探讨其实用场景与部署Web应用的最佳实践。Docker 凭借成熟生态和跨平台能力引领潮流;Podman 以无守护进程架构提升安全与效率。通过具体示例展示如何构建和运行Node.js与Angular应用,帮助读者理解核心命令与配置要点。无论是在开发环境还是生产部署,选择合适的工具能显著增强应用的安全性和稳定性。
911 1
|
Java 应用服务中间件 Apache
浅谈Tomcat和其他WEB容器的区别
Tomcat是一款轻量级的免费开源Web应用服务器,常用于中小型系统及并发访问量适中的场景,尤其适合开发和调试JSP程序。它不仅能处理HTML页面,还充当Servlet和JSP容器。相比之下,物理服务器是指具备处理器、硬盘等硬件设施的服务器,如云服务器,其设计目标是在处理能力、稳定性和安全性等方面提供高标准服务。简言之,Tomcat专注于运行Java应用,而物理服务器则提供基础计算资源。