进击的 Java ,云原生时代的蜕变
点击蓝色“程序猿DD”关注我
回复“资源”获取独家整理的学习资料!
作者 | 易立
来源 | 公众号「阿里巴巴云原生」
导读:云原生时代的来临,与Java 开发者到底有什么联系?有人说,云原生压根不是为了 Java 存在的。然而,本文的作者却认为云原生时代,Java 依然可以胜任“巨人”的角色。作者希望通过一系列实验,开拓同学视野,提供有益思考。
在企业软件领域,Java 依然是绝对王者,但它让开发者既爱又恨。一方面因为其丰富的生态和完善的工具支持,可以极大提升了应用开发效率;但在运行时效率方面,Java 也背负着”内存吞噬者“,“CPU 撕裂者“的恶名,持续受到 NodeJS、Python、Golang 等新老语言的挑战。
- 体积更小:对于微服务分布式架构而言,更小的体积意味着更少的下载带宽,更快的分发下载速度。
- 启动速度更快:对于传统单体应用,启动速度与运行效率相比不是一个关键的指标。原因是,这些应用重启和发布频率相对较低。然而对于需要快速迭代、水平扩展的微服务应用而言,更快的的启动速度就意味着更高的交付效率,和更加快速的回滚。尤其当你需要发布一个有数百个副本的应用时,缓慢的启动速度就是时间杀手。对于Serverless 应用而言,端到端的冷启动速度则更为关键,即使底层容器技术可以实现百毫秒资源就绪,如果应用无法在 500ms 内完成启动,用户就会感知到访问延迟。
- 占用资源更少:运行时更低的资源占用,意味着更高的部署密度和更低的计算成本。同时,在 JVM 启动时需要消耗大量 CPU资源对字节码进行编译,降低启动时资源消耗,可以减少资源争抢,更好保障其他应用 SLA。
- 支持水平扩展:JVM 的内存管理方式导致其对大内存管理的相对低效,一般应用无法通过配置更大的 heap size 实现性能提升,很少有 Java 应用能够有效使用 16G 内存或者更高。另一方面,随着内存成本的下降和虚拟化的流行,大内存配比已经成为趋势。所以我们一般是采用水平扩展的方式,同时部署多个应用副本,在一个计算节点中可能运行一个应用的多个副本来提升资源利用率。
热身准备
熟悉 Spring 框架的开发者大多对 不会陌生。本文将借助这个著名示例应用来演示如何让我们的 Java 应用变得更小、更快、更轻、更强大!
我们 fork 了 IBM 的 Michael Thompson 的示例,并做了一些调整。
$ git clone https://github.com/denverdino/adopt-openj9-spring-boot
$ cd adopt-openj9-spring-boot
$ cat Dockerfile.openjdk
FROM adoptopenjdk/openjdk8
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/' /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y \git \maven
WORKDIR /tmp
RUN git clone https://github.com/spring-projects/spring-petclinic.git
WORKDIR /tmp/spring-petclinic
RUN mvn install
WORKDIR /tmp/spring-petclinic/target
CMD ["java","-jar","spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar"]
$ docker build -t petclinic-openjdk-hotspot -f Dockerfile.openjdk .
$ docker run --name hotspot -p 8080:8080 --rm petclinic-openjdk-hotspot|\ _,,,--,,_/,`.-'`' ._ \-;;,________ __|,4- ) )_ .;.(__`'-'__ ___ __ _ ___ _______| | '---''(_/._)-'(_\_) | | | | | | | | || _ | ___|_ _| | | | | |_| | | | __ _ _| |_| | |___ | | | | | | | | | | \ \ \ \| ___| ___| | | | _| |___| | _ | | _| \ \ \ \| | | |___ | | | |_| | | | | | | |_ ) ) ) )|___| |_______| |___| |_______|_______|___|_| |__|___|_______| / / / /==================================================================/_/_/_/
...
2019-09-11 01:58:23.156 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-09-11 01:58:23.158 INFO 1 --- [ main] o.s.s.petclinic.PetClinicApplication : Started PetClinicApplication in 7.458 seconds (JVM running for 8.187)
$ docker images petclinic-openjdk-hotspot
REPOSITORY TAG IMAGE ID CREATED SIZE
petclinic-openjdk-hotspot latest 469f73967d03 26 hours ago 871MB
镜像瘦身
我们将镜像构建分成两个阶段:
- 在 ”build“ 阶段依然采用 JDK 作为基础镜像,并利用 Maven 进行应用构建;
- 在最终发布的镜像中,我们会采用 JRE 版本作为基础镜像,并从”build“ 镜像中直接拷贝出生成的 jar 文件。这意味着在最终发布的镜像中,只包含运行时所需必要内容,不包含任何编译时依赖,大大减少了镜像体积。
$ cat Dockerfile.openjdk-slim
FROM adoptopenjdk/openjdk8 AS build
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/' /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y \git \maven
WORKDIR /tmp
RUN git clone https://github.com/spring-projects/spring-petclinic.git
WORKDIR /tmp/spring-petclinic
RUN mvn install
FROM adoptopenjdk/openjdk8:jre8u222-b10-alpine-jre
COPY --from=build /tmp/spring-petclinic/target/spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar
CMD ["java","-jar","spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar"]
查看一下新镜像大小,从 871MB 减少到 167MB!
$ docker build -t petclinic-openjdk-hotspot-slim -f Dockerfile.openjdk-slim .
...
$ docker images petclinic-openjdk-hotspot-slim
REPOSITORY TAG IMAGE ID CREATED SIZE
petclinic-openjdk-hotspot-slim latest d1f1ca316ec0 26 hours ago 167MB
从 JIT 到 AOT —启动提速
$cat Dockerfile.openj9.warmed
FROM adoptopenjdk/openjdk8-openj9 AS build
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/' /etc/apt/sources.list
RUN apt-get update
RUN apt-get install -y \git \maven
WORKDIR /tmp
RUN git clone https://github.com/spring-projects/spring-petclinic.git
WORKDIR /tmp/spring-petclinic
RUN mvn install
FROM adoptopenjdk/openjdk8-openj9:jre8u222-b10_openj9-0.15.1-alpine
COPY --from=build /tmp/spring-petclinic/target/spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar
# Start and stop the JVM to pre-warm the class cache
RUN /bin/sh -c 'java -Xscmx50M -Xshareclasses -Xquickstart -jar spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar &' ; sleep 20 ; ps aux | grep java | grep petclinic | awk '{print $1}' | xargs kill -1
CMD ["java","-Xscmx50M","-Xshareclasses","-Xquickstart", "-jar","spring-petclinic-2.1.0.BUILD-SNAPSHOT.jar"]
-Xshareclasses
开启SCC,-Xquickstart
开启AOT。
$ docker build -t petclinic-openjdk-openj9-warmed-slim -f Dockerfile.openj9.warmed-slim .
$ docker run --name hotspot -p 8080:8080 --rm petclinic-openjdk-openj9-warmed-slim
...
2019-09-11 03:35:20.192 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-09-11 03:35:20.193 INFO 1 --- [ main] o.s.s.petclinic.PetClinicApplication : Started PetClinicApplication in 3.691 seconds (JVM running for 3.952)
...
可以看到,启动时间已经从之前的 8.2s 减少到 4s,提升近50%。
在这个方案中,我们一方面将耗时耗能的编译优化过程转移到构建时完成,一方面采用以空间换时间的方法,将预编译的 SCC 缓存保存到 Docker 镜像中。在容器启动时,JVM 可以直接使用内存映射文件来加载 SCC,优化了启动速度和资源占用。
这个方法另外一个优势是:由于 Docker 镜像采用分层存储,同一个宿主机上的多个 Docker 应用实例会共享同一份 SCC 内存映射,可以大大减少在单机高密度部署时的内存消耗。
下面我们做一下资源消耗的比较,我们首先利用基于 HotSpot VM 的镜像,同时启动 4 个 Docker 应用实例,30s 后利用docker stats
查看资源消耗。
$ ./run-hotspot-4.sh
...
Wait a while ...
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
0fa58df1a291 instance4 0.15% 597.1MiB / 5.811GiB 10.03% 726B / 0B 0B / 0B 33
48f021d728bb instance3 0.13% 648.6MiB / 5.811GiB 10.90% 726B / 0B 0B / 0B 33
a3abb10078ef instance2 0.26% 549MiB / 5.811GiB 9.23% 726B / 0B 0B / 0B 33
6a65cb1e0fe5 instance1 0.15% 641.6MiB / 5.811GiB 10.78% 906B / 0B 0B / 0B 33
...
$ ./run-openj9-warmed-4.sh
...
Wait a while ...
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
3a0ba6103425 instance4 0.09% 119.5MiB / 5.811GiB 2.01% 1.19kB / 0B 0B / 446MB 39
c07ca769c3e7 instance3 0.19% 119.7MiB / 5.811GiB 2.01% 1.19kB / 0B 16.4kB / 120MB 39
0c19b0cf9fc2 instance2 0.15% 112.1MiB / 5.811GiB 1.88% 1.2kB / 0B 22.8MB / 23.8MB 39
95a9c4dec3d6 instance1 0.15% 108.6MiB / 5.811GiB 1.83% 1.45kB / 0B 102MB / 414MB 39
...
- https://www.ibm.com/developerworks/cn/java/j-class...
- https://www.ibm.com/developerworks/cn/java/j-optim...
- HotSpot 在 Class Data Sharing (CDS) 和 AOT 方面也有了很大进展,但是 IBM J9 在这方面更加成熟。期待阿里的
原生代码编译
$ git clone https://github.com/denverdino/micronaut-petclinic
$ cd micronaut-petclinic
$ cat Dockerfile
FROM maven:3.6.1-jdk-8 as build
COPY ./ /micronaut-petclinic/
WORKDIR /micronaut-petclinic
RUN mvn package
FROM oracle/graalvm-ce:19.2.0 as graalvm
RUN gu install native-image
WORKDIR /work
COPY --from=build /micronaut-petclinic/target/micronaut-petclinic-*.jar .
RUN native-image --no-server -cp micronaut-petclinic-*.jar
FROM frolvlad/alpine-glibc
EXPOSE 8080
WORKDIR /app
COPY --from=graalvm /work/petclinic .
CMD ["/app/petclinic"]
- 在 "build" 阶段,利用Maven构建 Micronaut 版本的 PetClinic 应用,
- 在 "graalvm" 阶段,我们通过
native-image
将 PetClinic jar 文件转化成可执行文件。 - 在最终阶段,将本地可执行文件加入一个 Alpine Linux 基础镜像
$ docker-compose build
$ docker-compose up db
$ docker-compose up app
micronaut-petclinic_db_1 is up-to-date
Starting micronaut-petclinic_app_1 ... done
Attaching to micronaut-petclinic_app_1
app_1 | 04:57:47.571 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL95Dialect
app_1 | 04:57:47.649 [main] INFO org.hibernate.type.BasicTypeRegistry - HHH000270: Type registration [java.util.UUID] overrides previous : org.hibernate.type.UUIDBinaryType@5f4e0f0
app_1 | 04:57:47.653 [main] INFO o.h.tuple.entity.EntityMetamodel - HHH000157: Lazy property fetching available for: com.example.micronaut.petclinic.owner.Owner
app_1 | 04:57:47.656 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
app_1 | 04:57:47.672 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 159ms. Server Running: http://1285c42bfcd5:8080
https://docs.micronaut.io/latest/guide/index.html#...
https://www.exoscale.com/syslog/how-to-integrate-s...
总结与后记
本文通过OpenWrite的免费Markdown转换工具发布
-END-
留言交流不过瘾
关注我,回复“加群”加入各种主题讨论群
这里有点“券”点击领取!
进击的 Java ,云原生时代的蜕变相关推荐
- 进击的.NET 在云原生时代的蜕变
你一定看过这篇文章 <进击的 Java ,云原生时代的蜕变>, 本篇文章的灵感来自于这篇文章.北京时间9.24 就将正式发布.NET Core 3.0, 所以写下这篇文章让大家全面认识. ...
- 阿里技术专家:进击的 Java ,云原生时代的蜕变
作者| 易立 阿里云资深技术专家 导读:云原生时代的来临,与Java 开发者到底有什么联系?有人说,云原生压根不是为了 Java 存在的.然而,本文的作者却认为云原生时代,Java 依然可以胜任&qu ...
- 云原生时代 给予.NET的机会
.NET诞生于与Java的竞争,微软当年被罚款20亿美元.Java绝不仅仅是一种语言,它是COM的替代者!而COM恰恰是Windows的编程模型.而Java编程很多时候比C++编程要容易的多,更致命的 ...
- 带你深入探究云原生时代的分布式操作系统 Kubernetes
过去几年,以 docker.kubernetes 为代表的容器技术已发展为一项通用技术,BAT.滴滴.京东.头条等大厂,都争相把容器和 k8s 项目作为技术重心,试图"放长线钓大鱼" ...
- 云原生时代,Java还有优势么?
1.最近,很多后端工程师都在讨论,Rust.Go和Java这三门语言的对比.虽然我们经常拿PHP是世界上最好的语言来开玩笑.但具体到工作里,确实语言的选择非常重要,这一定程度上会直接决定一个工程师薪资 ...
- 云原生时代,Java 的危与机
今天,25 岁的 Java 仍然是最具有统治力的编程语言,长期占据编程语言排行榜的首位,拥有一千二百万的庞大开发者群体,全世界有四百五十亿部物理设备使用着 Java 技术,同时,在云端数据中心的虚拟化 ...
- 云原生时代的 Java 虚拟机
GraalVM 背景 新.旧编程语言的兴起躁动,说明必然有其需求动力所在,譬如互联网之于JavaScript.人工智能之于Python,微服务风潮之于 Golang 等等.大家都清楚不太可能有哪门语言 ...
- GraalVM - 云原生时代的 Java 笔记
GraalVM - 云原生时代的 Java 笔记 前言 GraalVM 诞生的背景 GraalVM Graal Compiler Benefits of JIT Creating a Native I ...
- 【Java核心技术大会 PPT分享】张家驹:云原生时代的Java — Quarkus及其最新进展...
导读:云原生时代的Java - Quarkus及其最新进展 Java核心技术大会2022 云原生时代的Java - Quarkus及其最新进展 张家驹:红帽大中华区首席架构师 (Chief Archi ...
最新文章
- 洛谷P3252 [JLOI2012]树
- C语言 fread()与fwrite()函数说明与示例
- vue.js将一个对象的所有属性作为prop进行传递
- 内存中Android,什么是Android内存转储中的EGL和GL mtrack?
- Spring Boot返回的数据格式是XML 而不是JSON之原因探求的和解决
- 手机通讯录c语言导入手机软件,有什么软件能快速将手机通讯录的联系人传到另一个手机上...
- On September 8, the “PDF first share“ Foxit Software
- mysql 字段值保留2位小数
- python编程可以自学么-python编程还能自学?怎么能学好? - 【大连东软睿道】
- mysql ip 访问_MySql通过ip地址进行访问的方法
- 在组策略中 计算机策略仅对,在组策略中,计算机策略仅对()生效。 - 问答库...
- windbg分析C++ EH exception
- 蓝牙遥控器连接流程分析
- 手机邮箱看不到已发送邮件_iPhone用户请注意:你的邮件App得禁用,刚曝光的安全漏洞,iOS 6以上设备全中招...
- android代码查找快捷键是什么,Android Studio搜索功能(查找功能)及快捷键图文详解(示例代码)...
- 开放鸿蒙,未来可循:一文读懂华为开发者大会2021主题演讲
- 远程连接Linux服务器并实现文件的上传下载
- 公务员考试题集错解汇集之判断推理
- 【每日1刷系列】软件测试常见面试题—深层概念
- 点微西瓜同城分类信息系列打包整套discuz插件 - 含下载地址
热门文章
- python 代理使用方法简介
- python 类实例化理解
- 反射型 DDoS 攻击的原理和防范措施
- windows平台下 c++获取 系统版本 网卡 内存 CPU 硬盘 显卡信息
- linux 线程 pthread_create 源码 剖析
- 大数据教父Micheal Stonebraker告诉你大数据的秘密
- 新浪博客服务器维护,服务器安全维护
- 测试自己像什么动物软件叫什么,【测试】你最像哪种动物?
- echarts饼图解析html标签,解决echarts中饼图标签重叠的问题
- java出现404的原因是_为什么使用eclipse总是出现404