知识卡片
沙盒隔离与"提交源码而非编译产物"的设计考量
内容
当一个平台允许第三方(插件开发者)提交代码在自己的进程内运行时,最大的风险是这段代码出错或恶意行为波及到平台自身乃至其他插件——U系统的应对方式是为每个插件代码提供独立沙盒,沙盒之间互不感知彼此的存在,沙盒内的代码也无法修改沙盒之外(引擎)的状态,具体实现上可以用基于JVM的classloader隔离,需要更强隔离时也可以引入Docker把沙盒做成本机虚拟环境。与这个隔离机制配套的另一个不那么直觉的决策是:要求插件方提交的是源代码而不是编译好的jar包,原因有三层:一是如果要支持脚本语言(Python等),提交源代码本身就是更通用的设计,不需要为每种语言单独设计编译产物格式;二是提交源代码可以在插件更新时做热编译,依赖问题能在编译这一步就被发现并反馈,而jar包的问题只能等到实际运行时才抛异常,反馈时机被大幅推迟;三是通过本机缓存编译结果,可以避免重复编译带来的额外开销,运行效率并不会因为提交源码而打折扣。这个设计权衡的核心启发在于:当系统需要在”给第三方最大自由度”和”保证自身稳定性”之间取舍时,答案往往不是在两者间简单择一,而是像这里一样,用运行时隔离机制解决稳定性问题,同时通过改变交付物形态(源码而非产物)把原本要等到线上才暴露的问题尽量提前到开发期。
参考来源
- 位置:《高可用架构(第1卷)》第2章《高可用架构原理与分布式实践》"2.3 解耦的艺术——大型互联网业务系统的插件化改造"节,"2.3.1 插件化"(源文件:_epub-src/OEBPS/Text/Chapter2_3_2.xhtml)
- 结论依据:原文说明沙盒需保证"不同的沙盒不能互相感知彼此的存在""插件中的代码无法修改沙盒以外(引擎)中的状态",并给出提交源代码而非jar包的三点理由(支持脚本语言、热编译提前反馈依赖问题、本机缓存避免重复编译),共同支撑本卡片结论。
- 原始内容:沙盒可以保证:不同的沙盒不能互相感知彼此的存在;插件中的代码无法修改沙盒以外(引擎)中的状态……提交代码可以在更新插件时热编译,发现依赖问题,及时反馈;jar包只能在运行期抛异常,反馈置后。