腾讯 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!
腾讯云Serverless云函数SCF产品中内置Java8支持,但是并没有高版本JDK的环境支持,那么如何实现SCF的Java11云函数呢?
实际上,SCF云函数提供的CustomRuntime功能已经解锁了用户使用编程语言的限制,目前已经有webassembly,swift,rust等成功例子。我们可以同样借助这个功能来将KonaJDK11引入SCF,从而实现高版本Java的支持。
过程如下:
首先需要创建层,由于KonaJDK11程序包超过50MB,所以可以选择COS方式,现将KonaJDK11安装包上传到腾讯云COS,之后在创建层时指定路径即可, 具体使用可以参考产品说明/document/product/583/45760
进入【高级配置】->【层配置】->【添加层】
按照下图所示配置好【层】【超时时间】与【内存】点击【完成】
/document/product/583/47274
,需要编写CustomRuntime的启动文件 Bootstrap,SCF CustomRuntime会在函数启动时第一步找到并执行这个名为bootstrap的可执行文件。可以看到就是简单的环境变量配置和执行java -version 与 Hello程序。
之后点击【测试】触发执行,之后我们可以看到函数执行日志如下:
我们已经可以从日志里看到 openjdk version "11.0.9.1-ga"的 Java版本,并且看到了Hello程序正常输出。至此,KonaJDK 11 已经顺利跑在了云函数环境中。
注意此处显示【测试失败】是正常的,因为我们还没有编写处理【函数事件】的逻辑,也就是还没有实现具体的云函数。
现在让我们来用spring框架实现一个能跑在KonaJDK11上的云函数。为了清晰,我们写一个最简单的springboot Demo, 它的controller长这样:
入口函数长这样:
OK,目前这个Demo可以接受 http Get localhost:8080/hello
请求并返回 hello, this is a springboot demo!
字符串。那么如何将它改编成云函数呢?
从 SCF CustomRuntime 文档以及一些公开的资料,可以看到编写CustomRuntime的函数,只需要两个关键步骤:
/developer/article/1690709
文章可以看到,主要流程如下:具体说来,就是在bootstrap启动云函数以后,sping云函数在自身初始化时需要先POST一个Ready的httpRequest给 SCF服务端, 目的是通知SCF函数初始化完毕,可以获得下发的事件了。
之后,spring需要一个循环,循环内部通过向SCF服务端发送HTTP GET请求,获得待处理事件,再调用内部逻辑,处理完事件之后通过POST请求发送给SCF服务端,循环等待下一次事件下发。
针对Springboot, 我们的云函数主要有以下几个需要处理的地方:
经过上面的梳理,逻辑已经基本上清晰了:首先,需要在 cold launch阶段启动springboot入口函数, 通知SCF服务端,springboot云函数初始化完毕,等待接收消息。之后就是一个大循环,循环里面工作如下:
Http GET
请求从SCF服务端获得 ApiGateway 下发的event处理流程代码也很简单:
至此,我们已经完成了云函数的编写,之后我们可以测试一下,将bootstrap和编译后的 springboot-application.jar
打包到一个zip文件,然后上传到SCF云函数进行部署。
之后按照如下配置 apiGW 的 event,注意这里配置 Get,“/hello” 是由于我们的springboot 云函数的controller配置成了接收Get, “/hello” 请求并打印和返回字符串,实际上用户需要根据自己的业务,修改apiGW这里event相应的内容。
然后点击[测试]: 稍等一下就可以看到如下log:
springboot已经启动, 然后我们还可以看到:
函数已经正常响应了GET /hello的请求。
在上面的springboot云函数中,我们可以看到一次冷启动耗时和内存如下:
同时log中也包含了springboot的启动时间
总体来说就是耗时6秒多,使用了168MB的内存。
那么,如何提高启动速度减少内存使用呢?
JDK11里面自带appCDS功能,具openJDK官方说法,该功能可以减少java类加载时间同时减少内存占用量,提高启动速度。这不正是我们想要的么,我们现在已经有了KonaJDK+springboot的云函数,那么怎么在KonaJDK中使用起来这个功能呢?
按照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文件-Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=dump.jsa
针对云函数SCF的场景,主要需要以下适配工作
/tmp/classes.list
和 /tmp/dump.jsa
-XX:SharedArchiveFile=dump.jsa
即可复用 dump.jsa
文件。所以我们需要获得生成的 /tmp/dump.jsa
文件,由于SCF不能直接下载 /tmp
目录的文件,所以我们根据COS的文档写了一小段程序,帮助我们在生成 /tmp/dump.jsa
文件后上传到指定的COS中,具体可以参考COS java 的sdkdump.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之后出发云函数的冷启动,可以看到如下效果:
至此,我们在腾讯 Serverless 云函数上借助 CustomRuntime 完成了KonaJDK11 + SpringBoot云函数的使用,并利用KonaJDK11中AppCDS特性优化了云函数冷启动的速度与内存损耗。文中利用CustomeRuntimely引入KonaJDK11的方法可以作为作为腾讯云Faas上解锁多语言或高版本Java语言runtime的这种方法。
在未来腾讯KonaJDK团队会进一步针对腾讯云业务Faas场景的特点提供更多的功能与性能提升,敬请关注。
立即体验腾讯云 Serverless Demo,获取 Serverless 新用户礼包,请在 PC 端访问: serverless.cloud.tencent.com/start?c=wx
欢迎进入千人 QQ 群 (871445853) 交流!
点击「阅读原文」进入 Serverless 中文网,体验更多 Serverless 应用的最佳实践!
本文分享自 ServerlessCloudNative 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与?腾讯云自媒体同步曝光计划? ,欢迎热爱写作的你一起参与!