埋头干活,抬头看路
JAVA隔离机制漫谈

JAVA隔离机制漫谈

最近遇到一个事情,一个JAVA业务系统进程频繁崩溃,测试环境几乎每天一次,多的时候一天多次,非常让人恼火,而且这种崩溃,不是报一个异常业务流程终止,而是JVM进程崩溃掉了。分析过程就不谈了,挺曲折,最终使用gdb和jstat联合分析程序崩溃时的现场,发现是企业微信提供的SDK导致的问题。

至此,现象首先明确了。企业微信的JAVA版的SDK实际上是使用了JNI技术调用了C编写的动态库。而底层C动态库在执行时遇到了问题(比如内存分配问题)出现异常,这种异常导致了整个JVM进程的崩溃。

详细的避免方案以及解决方案还在分析中。不过也让我想到另外一个问题,多年之前也遇到过一个JNI的问题,因为参数没有校验好,导致JNI调用时出现空指针,也是导致了JVM进程崩溃,进而导致了大面积生产故障。

JAVA程序在处理业务逻辑的时候,如果是纯纯的自己写的JAVA代码还好。一旦引入了其他三方包,就可能出现包冲突问题,包冲突问题,有的可以通过依赖管理给解除掉,有的搞不定的话,可能就需要用隔离机制,比如使用不同的类加载器机制。

说到类加载器这种隔离方案,就不得不提TOMCAT的经典的WEB类加载器实现的轻量级容器方案。通过这种类加载器容器,可以实现不同的WEB应用其自身的关联jar包之间的完全无影响。

回到我们这次遇到的JNI异常导致的进程崩溃情况,虽然也是第三方SDK导致的,但是以上类隔离机制,对于此种情况完全没有效果。要想杜绝出现异常时进程崩溃的方式,最好不用JNI,如果非得用JNI,可能需要考虑微进程隔离机制。

什么意思呢?

设计一个专用的隔离组件交互机制,每个需要进行隔离的第三方处理过程,实现此隔离组件接口(或者其他方式)。主应用进程,在调用隔离组件的实现时,会创建外部进程,然后由外部进程来执行第三方的处理过程。这样,即使第三方处理过程出现问题,最坏的情况导致进程崩溃,也会最大程度降低对主进程的影响,大部分情况下主进程都可以高枕无忧。

而外部进程与主进程之间的关系,以及进程职责的设计,就会影响整个的隔离颗粒度以及资源消耗。

如果子进程可以执行完整的业务流程,则相当于子进程与主进程是基本等价的,则子进程应该有自己的服务暴露方式,比如HTTP监听或者其他入口。

如果子进程只处理需要隔离的最小内容,比如仅仅用于处理第三方SDK的调用,则子进程启动时,其内存占用,classpath,性能都会非常可控。

而因为子进程没有独立的服务暴露接口,则需要有主进程来承接对外的服务暴露,接收服务访问和流程处理,在需要访问第三方SDK时,与子进程进行交互,完成任务转交分配和任务结果回传。这其中涉及到进程间交互。简单粗暴点的进程交互,可以在主进程和子进程之间使用RPC接口调用的方式来实现。

或者我们可以选择一种更为古老的技术,IPC,一字之差。JAVA能够使用的IPC技术,只有内存映射文件了,通过内存映射文件,可以在一个服务器的多个本地进程之间,以非常高的效率进行信息交互。可以近似认为是共享内存了。

如此,三方SDK的风险可控,且代价较低。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注