共计 4510 个字符,预计需要花费 12 分钟才能阅读完成。
一、背景
腾讯 KonaJDK 团队最近对外开源了 KonaJDK11, 该版本 JDK 是通过外部超大规模生产环境验证的定制 JDK,该版本在启动性能、峰值性能以及事物解决能力方面,绝对于前一版本 Kona JDK8 都有了综合性晋升,积淀了腾讯云与大数据团队在大数据 / 机器学习、云原生场景下的深度优化,并且通过了 JCK 验证,确保充沛的 Java SE 规范兼容。通过工业规范 Benchmark 表明,Kona JDK11 比照 Kona JDK8 大多数场景在峰值性能上具备非常明显的晋升,个别性能晋升靠近 50%。
KonaJDK11 如此优良,咱们能不能把它引入到 Serverless 呢?另外,最近笔者也在思考怎么样让 Java spring 框架在 SCF 中顺滑的跑起来,所以借着这个机会,索性来一把 KonaJDK + Spring 在 SCF 上的实际总结。
多说无用,Show you my code!
二、SCF 应用 JDK11
腾讯云 Serverless 云函数 SCF 产品中内置 Java8 反对,然而并没有高版本 JDK 的环境反对,那么如何实现 SCF 的 Java11 云函数呢?
实际上,SCF 云函数提供的 CustomRuntime 性能曾经解锁了用户应用编程语言的限度,目前曾经有 webassembly,swift,rust 等胜利例子。咱们能够同样借助这个性能来将 KonaJDK11 引入 SCF,从而实现高版本 Java 的反对。
过程如下:
- 下载 KonaJDK11,https://github.com/Tencent/Te…
- 因为 KonaJDK11 的二进制包比拟大,须要应用 SCF 层的概念来上传 KonaJDK11 程序包
首先须要创立层,因为 KonaJDK11 程序包超过 50MB,所以能够抉择 COS 形式,现将 KonaJDK11 安装包上传到腾讯云 COS,之后在创立层时指定门路即可,具体应用能够参考产品阐明 https://cloud.tencent.com/doc…
- 创立云函数,留神这里须要应用 CustomRuntime,咱们抉择 Shell 函数示例,再次根底上拓展咱们的 KonaJDK11 的反对.
进入【高级配置】->【层配置】->【增加层】
依照下图所示配置好【层】【超时工夫】与【内存】点击【实现】
- 依据 SCF CustomRuntime 的应用阐明,须要编写 CustomRuntime 的启动文件 Bootstrap,SCF CustomRuntime 会在函数启动时第一步找到并执行这个名为 bootstrap 的可执行文件。
- 咱们的 bootstrap 中须要配置环境变量,并启动 Java 程序. 咱们先假如我写了一个名为 Hello 的 class,外面只打印 hello SCF 字符串。之后将 bootstrap 文件和 Hello.class 文件一起打包成一个 zip 文件,上传到 SCF 部署,这时 bootstrap 的内容如下:
能够看到就是简略的环境变量配置和执行 java -version 与 Hello 程序。
之后点击【测试】触发执行,之后咱们能够看到函数执行日志如下:
咱们曾经能够从日志里看到 openjdk version “11.0.9.1-ga” 的 Java 版本,并且看到了 Hello 程序失常输入。至此,KonaJDK 11 曾经顺利跑在了云函数环境中。
留神此处显示【测试失败】是失常的,因为咱们还没有编写解决【函数事件】的逻辑,也就是还没有实现具体的云函数。
三、实现 spring 云函数
当初让咱们来用 spring 框架实现一个能跑在 KonaJDK11 上的云函数。为了清晰,咱们写一个最简略的 springboot Demo, 它的 controller 长这样:
入口函数长这样:
OK, 目前这个 Demo 能够承受 http Get localhost:8080/hello
申请并返回 hello, this is a springboot demo!
字符串。那么如何将它改编成云函数呢?
从 SCF CustomRuntime 文档以及一些公开的材料,能够看到编写 CustomRuntime 的函数,只须要两个关键步骤:
- 编写可执行启动程序 bootstrap,在 bootstrap 外面启动咱们的 spring 云函数
- 编写云函数。这一步首先须要理解 CustomRuntime 工作的流程,从这篇文章能够看到,次要流程如下:
具体说来,就是在 bootstrap 启动云函数当前,sping 云函数在本身初始化时须要先 POST 一个 Ready 的 httpRequest 给 SCF 服务端,目标是告诉 SCF 函数初始化结束,能够取得下发的事件了。
之后,spring 须要一个循环,循环外部通过向 SCF 服务端发送 HTTP GET 申请,取得待处理事件,再调用外部逻辑,解决完事件之后通过 POST 申请发送给 SCF 服务端,循环期待下一次事件下发。
针对 Springboot,咱们的云函数次要有以下几个须要解决的中央:
- 事件下发:Springboot 云函数次要是启动并监听云函数外部的一个自定义 http 端口,通过 http 申请实现解决工作。SCF 云函数目前 http 申请次要通过 API Gateway 事件下发,也就是说,spring 云函数的逻辑外面,须要将 API Gateway 事件转换成 http 事件之后再发给函数外部的 springboot 监听的端口。好在整个这一套逻辑的转换 SCF 其实曾经提供给了咱们,就在 SCF java event 的代码中,能够从 https://github.com/tencentyun… 这个代码间接抽取复用。
- 初始化:也就是在第一次启动云函数的时候,咱们须要启动 springboot,另其建设 httpserver 并监听端口。之后每次事件下发,只须要发送 httprequest 即可。
- 监听事件:这里就是依照 SCF CustomRutime 的要求,写一个循环,应用 http GET 申请获取 event,并发送给外部 springboot 监听的端口。
通过下面的梳理,逻辑曾经基本上清晰了:首先,须要在 cold launch 阶段启动 springboot 入口函数,告诉 SCF 服务端,springboot 云函数初始化结束,期待接管音讯。之后就是一个大循环,循环外面工作如下:
- 通过
Http GET
申请从 SCF 服务端取得 ApiGateway 下发的 event
- Api GW event 转换成 http request 并发送到 springboot 监听的端口,期待返回处理结果
- springboot 返回的 event 转换为 ApiGateway Response,通过 POST 申请返回给 SCF 服务端
- 进入下一次循环,期待下一次事件下发.
解决流程代码也很简略:
至此,咱们曾经实现了云函数的编写,之后咱们能够测试一下,将 bootstrap 和编译后的 springboot-application.jar
打包到一个 zip 文件,而后上传到 SCF 云函数进行部署。
之后依照如下配置 apiGW 的 event,留神这里配置 Get,“/hello”是因为咱们的 springboot 云函数的 controller 配置成了接管 Get,“/hello”申请并打印和返回字符串,实际上用户须要依据本人的业务,批改 apiGW 这里 event 相应的内容。
而后点击 [测试]: 稍等一下就能够看到如下 log:
springboot 曾经启动,而后咱们还能够看到:
函数曾经失常响应了 GET /hello 的申请。
四、利用 appCDS 个性提速降存
在下面的 springboot 云函数中,咱们能够看到一次冷启动耗时和内存如下:
同时 log 中也蕴含了 springboot 的启动工夫
总体来说就是耗时 6 秒多,应用了 168MB 的内存。
那么,如何进步启动速度缩小内存应用呢?
JDK11 外面自带 appCDS 性能,具 openJDK 官网说法,该性能能够缩小 java 类加载工夫同时缩小内存占用量,进步启动速度。这不正是咱们想要的么,咱们当初曾经有了 KonaJDK+springboot 的云函数,那么怎么在 KonaJDK 中应用起来这个性能呢?
- appCDS 性能应用步骤:
依照 JDK 官网文档,appCDS 应用形式次要是以下几个步骤:
- 生成待
dump
的类文件列表,应用-Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=classes.lst
JVM 选项运行程序,会生成 classes.lst 文件 - 应用
-Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=classes.lst \ -XX:SharedArchiveFile=dump.jsa
生成 dump.jsa 文件 - 应用 appCDS 失常启动 java 程序,应用 JVM 选项
-Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=dump.jsa
- 云函数中 enable appCDS
针对云函数 SCF 的场景,次要须要以下适配工作
- 因为在云函数中,目前只有 /tmp 目录是可写目录,所以 1 中的步骤咱们须要将所有波及到的文件门路变更为
/tmp/classes.list
和/tmp/dump.jsa
- 因为咱们冀望最终生成的 dump.jsa 能够在多个云函数实例中应用,咱们须要失去 /tmp/dump.jsa 文件,而后将其和云函数一起打包,这样在应用时候,咱们只须要指定 Jvm 参数
-XX:SharedArchiveFile=dump.jsa
即可复用dump.jsa
文件。所以咱们须要取得生成的/tmp/dump.jsa
文件,因为 SCF 不能间接下载/tmp
目录的文件,所以咱们依据 COS 的文档写了一小段程序,帮忙咱们在生成/tmp/dump.jsa
文件后上传到指定的 COS 中,具体能够参考 COS java 的 sdk - 在失去
dump.jsa
之后,咱们就能够对整个云函数从新打包,最终打包的文件中蕴含 3 个子文件,云函数 CustomRuntime 的启动脚本 bootstrap, springboot 云函数的实现SCF-springboot-web-1.0-SNAPSHOT.jar
, 以及 appCDS 的 archive 文件 dump.jsa,咱们将这 3 个文件打包重新部署。 - 再部署之后,咱们须要增加
JAVA_TOOL_OPTIONS
环境变量JAVA_TOOL_OPTIONS=-Xshare:on -XX:SharedArchiveFile=dump.jsa
这样就能够在启动云函数时应用这些 jvm 选项了。
- 成果
在应用 AppCDS 之后登程云函数的冷启动,能够看到如下成果:
- 内存应用 从原来的 169MB 升高到了 100MB
- springboot 启动工夫从原来的 6.137s 进步到了 4.772s
总结
至此,咱们在腾讯 Serverless 云函数上借助 CustomRuntime 实现了 KonaJDK11 + SpringBoot 云函数的应用,并利用 KonaJDK11 中 AppCDS 个性优化了云函数冷启动的速度与内存损耗。文中利用 CustomeRuntime 引入 KonaJDK11 的办法能够作为腾讯云 Faas 上解锁多语言或高版本 Java 语言 runtime 的一种通用形式。
在将来腾讯 KonaJDK 团队会进一步针对腾讯云业务 Faas 场景的特点提供更多的性能与性能晋升,敬请关注。