一、背景
腾讯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,github.com/Tencent/Ten…
- 由于KonaJDK11的二进制包比较大,需要使用SCF层的概念来上传KonaJDK11程序包
首先需要创建层,由于KonaJDK11程序包超过50MB,所以可以选择COS方式,现将KonaJDK11安装包上传到腾讯云COS,之后在创建层时指定路径即可, 具体使用可以参考产品说明cloud.tencent.com/document/pr…
- 创建云函数, 注意这里需要使用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的代码中,可以从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环境变量
这样就可以在启动云函数时使用这些jvm选项了。
- 效果
在使用AppCDS之后出发云函数的冷启动,可以看到如下效果:
- 内存使用 从原来的169MB降低到了100MB
- springboot启动时间从原来的6.137s提高到了4.772s
总结
至此,我们在腾讯Serverless云函数上借助CustomRuntime完成了KonaJDK11 + SpringBoot云函数的使用,并利用KonaJDK11中AppCDS特性优化了云函数冷启动的速度与内存损耗。 文中利用CustomeRuntimely引入KonaJDK11的方法可以作为作为腾讯云Faas上解锁多语言或高版本Java语言runtime的这种方法。
在未来腾讯KonaJDK团队会进一步针对腾讯云业务Faas场景的特点提供更多的功能与性能提升,敬请关注。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!