使用 Azul Zing 制作 Java 微服务
为了产生这些结果,我们在相同的硬件上运行相同的应用程序,具有相同的工作负载,只是在 JVM 之间切换。其次,在重新定位和重新映射阶段,对象的地址将是正确的,因此应用程序对对象状态所做的任何更改都不会丢失。使用从先前执行的服务中收集的配置文件。当服务的新实例启动时,配置文件可用于加载和初始化所需的所有可能的类,然后重用我们存储的编译代码(如果适用),或者使用 Falcon 编译必要的方法。正如你所看
微服务架构的优势
软件开发总是在变化,今天,我们看到了向使用微服务架构的明显转变。这种方法有许多明显的优点,但让我们看看其中最重要的两个:
简化云部署。使用像 Docker 这样的容器封装了服务的所有需求,从操作系统到运行时平台再到库和框架。确保在部署服务的地方安装正确版本的库甚至操作系统不再是问题。通过使用像 AuFS 这样的联合文件系统,没有必要复制服务的每个实例的所有文件(就像使用虚拟化时一样)。这样可以节省资源,从而节省云成本。
能够横向(在服务级别)和垂直(在应用程序级别)扩展应用程序。这种可伸缩性是动态的,这意味着服务实例可以在负载下降时关闭,也可以在负载增加时启动。对于云部署,这是节省成本的主要考虑因素。
Java 中的微服务
随着 JDK 9 中模块化的引入,通过使用 jlink 等工具构建针对特定服务定制的 Java 运行时,部署基于 Java 的微服务变得更加易于管理。通过仅包含服务所需的模块,消除手册页之类的内容,甚至剥离 JVM 的符号表,JDK 大小可以减少一个数量级。JDK 10 中的更改(向后移植到 JDK 8 u191)意味着 JVM 也具有 CGroup 感知功能,从而减少了 JVM 尝试向堆分配的内存多于其可以访问的内存时突然停止的潜在问题。
所有这些都使 Java 成为开发微服务的理想平台。大量的可用库和框架也有助于使开发更快、更直接。
然而,将 Java 用于微服务并非没有挑战。让我们看一下其中的两个:
最终用户请求的事务将需要编排许多服务才能提供所需的结果。在考虑完成事务所需的时间(延迟)的服务级别协议 (SLA) 时,这是所有相关服务的聚合延迟。Java 应用程序中延迟的最常见原因之一是垃圾回收 (GC) 的影响。所有生产就绪的算法(除了一个例外,我们将看到)都会在某个时候暂停应用程序线程,以便以安全的方式操作堆中的对象。使用多个 Java 服务时,由于 GC 导致的累积延迟可能会使满足用户级 SLA 变得具有挑战性。
随着负载的增加,通过启动新实例来扩展服务取决于新实例的响应能力是否从启动起就处于最佳水平。Java 代码被编译为字节码(Java 虚拟机的指令),而不是运行它的平台的本机指令。这为便携性提供了优势,并最终提高了性能,但确实需要预热时间才能达到全速。在非常动态的微服务架构中,预热时间可能会成为一个问题。
在 Azul,我们开发了 Zing JVM 来解决这两个问题,使 Java 成为开发所有应用程序的理想平台,无论是否使用微服务。
我们是怎么做到的?
首先,我们消除了GC暂停的问题。与CMS、G1等其他生产就绪算法不同,我们可以与应用程序线程同时执行GC的所有阶段,并压缩堆以消除碎片。这就是为什么我们将算法称为 C4,即连续并发压缩收集器。
其工作原理的关键是加载值屏障,它用作从应用程序代码访问所有对象的读取屏障。每当应用程序想要使用一个对象时,C4 都会执行测试并跳转;标头中的位用于指示 GC 的状态和阶段。通过这样做,C4 强制执行两个规则。首先,在GC的标记阶段,应用程序使用的所有对象都将被标记为实时数据;消除了意外收集活体物体的可能性。其次,在重新定位和重新映射阶段,对象的地址将是正确的,因此应用程序对对象状态所做的任何更改都不会丢失。
下图显示了它的效果如何。
结果显示了使用 1Gb 堆运行 Hazelcast(内存中数据网格)的 JVM 的延迟。为了产生这些结果,我们在相同的硬件上运行相同的应用程序,具有相同的工作负载,只是在 JVM 之间切换。jhiccup 工具用于记录 JVM 级别的延迟,低于此级别,应用程序延迟没有任何影响。在左侧,使用 Hotspot 运行,我们可以看到 GC 活动导致几乎连续 25-40 毫秒的暂停,这将大大减少响应时间和吞吐量。在右边,我们可以看到使用 Zing 没有明显的停顿。我们需要更改图形的比例才能查看 Zing 的延迟(其中大部分与 JVM 以下的事情有关,例如操作系统上下文切换)。
为微服务链部署 Zing 将消除所有服务的 GC 相关延迟,并使事务的 SLA 侧重于应用程序逻辑而不是运行时平台。
对于第二个挑战,即热身时间,Zing 包括 ReadyNow!为了最大限度地减少启动时间,ReadyNow!使用从先前执行的服务中收集的配置文件。配置文件是从以代表性负载运行的服务中收集的,直到使用 Falcon JIT 编译器编译所有热方法(代码完全预热)。Falcon 是基于 LLVM 开源项目的 Hotspot 中 C2 JIT 编译器的高性能替代品。The ReadyNow!配置文件记录了四组数据:
当前为应用程序加载的所有类。
所有已初始化的类。
在运行 C1 JIT 编译代码时收集的所有分析数据,这些数据被用作 Falcon 的输入。
发生的所有反优化。JIT 编译代码最重要的性能提升之一来自推理优化的使用。这些优化假定应用程序将继续像过去一样运行。当假设被证明是不正确的时,JIT 编译器必须丢弃编译后的代码,并根据新行为重新编译它。这是一种去优化,在性能方面非常昂贵。
由于 Falcon 是完全确定性的,我们知道相同的输入(方法字节码、分析数据等)将生成相同的本机代码。我们可以利用这一事实,因此配置文件现在包括 Zing 在收集配置文件时缓存的所有编译代码。这称为代码存储。
当服务的新实例启动时,配置文件可用于加载和初始化所需的所有可能的类,然后重用我们存储的编译代码(如果适用),或者使用 Falcon 编译必要的方法。所有这些都发生在服务的 main 方法开始执行之前。最终效果是,服务将以收集配置文件时性能的 98% 左右开始执行(100% 在处理了一两个事务后实现)。动态高效地扩展服务的任务变得更加简单。
正如你所看到的,将Zing用于部署到云中的基于微服务的应用程序将使开发团队的任务变得相当简单,并为用户提供更好的体验。
您可以免费试用 Zing 30 天,那么为什么不让您的微服务使用 Zing 呢?
更多推荐
所有评论(0)