9月7日-8日,相约 2023 腾讯全球数字生态大会!聚焦产业未来发展新趋势! 了解详情
写点什么

OpenSource | 在 AWS lambda 上运行用 Java 编写的 APLs

  • 2019-10-28
  • 本文字数:4766 字

    阅读完需:约 16 分钟

OpenSource | 在AWS lambda上运行用Java编写的APLs

从 Spring 和 Spring Boot 到 Jersey 到 Spark,Java 开发人员可以随心选择各种开放源来构建服务器端 API。这些框架通常都在编译包内嵌入了用来运行服务器的 Servlet 容器引擎,例如 Tomcat。AWS LambdaAmazon API Gateway 在无服务器环境中作为 HTTP 前端和计算平台。今天,我们发布了 aws-serverless-java-container 框架 1.0 版。使用无服务器 Java 容器,可以方便地在 Java 中使用 Spring、Spring Boot、Jersey 或 Spark 等框架编写应用程序,并且只需极少的代码修改即可在 AWS Lambda 内部运行。



无服务器 Java 容器库在 Lambda 运行时和您选择的框架之间扮演代理的角色,好比一个 Servlet 引擎,将传入的事件翻译为框架可以理解的请求对象,然后将来自应用程序的应答转换为 API Gateway 理解的格式。无服务器 Java 容器库现已在 Maven 上推出。我们针对不同的框架提供不同风格的库:SpringSpring BootJerseySpark。在本博文中,我们将构建一个 Jersey 应用程序。该库的其他实现也具有类似的结构,请参见 GitHub 上的快速入门指南

使用 Maven 原型

我们为所有支持的框架发布了基本的 Maven 原型。如要运行此教程,您需要在本地计算机上安装 Apache Maven。使用任何终端打开工作区目录,然后运行 Maven 命令以利用原型生成新的项目。请使用您需要的设置代替 groupId 和 artifactId 设置:


Bash


$ cd myworkspace$ mvn archetype:generate -DgroupId=my.service -DartifactId=jersey-sample -Dversion=1.0-SNAPSHOT \      -DarchetypeGroupId=com.amazonaws.serverless.archetypes \    -DarchetypeArtifactId=aws-serverless-jersey-archetype \    -DarchetypeVersion=1.0.1 -Dinteractive=false
复制代码


mvn 客户段将要求您确认参数,然后生成项目结构。在此例中,我们使用 aws-serverless-jersey-archetype – 我们为 spring、springboot 和 spark 准备了类似的工件。下面我们详细介绍生成的代码结构。如果您仅仅需要执行命令并测试您的基本应用程序,请直接跳转至本地测试部分。

Jersey 应用程序

使用您选择的 IDE 打开原型项目。Jersey 原型中包含的简单应用程序定义了一个 /ping 路径,将会返回一条 JSON hello world 消息。在代码包中,以我的例子为例,在 my.service下,有一个 resource 包,它采用 PingResource 类。Ping 类采用 JAX-RS’ @Path 注解,它定义了单一的 @GET 方法。


Java


@Path("/ping")public class PingResource {
@GET @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.WILDCARD) public Response createPet() { // return a basic map. This will be marshalled automatically into a // json object with a single property Map<String, String> pong = new HashMap<>(); pong.put("pong", "Hello, World!"); return Response.status(200).entity(pong).build(); }}
复制代码

Lambda 句柄

在我们应用程序的主包中,原型还会生成一个 StreamLambdaHandler 类。下面我们介绍该类中的代码:


Java


public class StreamLambdaHandler implements RequestStreamHandler
复制代码


我们的类会实现 Lambda 的 RequestStreamHandler 接口。该类是 AWS Lambda 在我们应用程序中的主要入口:Lambda 行话里的“句柄”。我们使用流句柄而不是基于 POJO 的句柄,因为我们的事件模型需要利用注解来编组和解组,但 Lambda 的内嵌序列化器部支持注解。


Java


private static final ResourceConfig jerseyApplication = new ResourceConfig()                                                            .packages("com.sapessi.jersey.resource")                                                            .register(JacksonFeature.class);
复制代码


首先,我们会声明一个静态的 ResourceConfig对象,这是 Jersey Application 实现对象。我们配置此对象以扫描有注解的类的 resource 包,然后加载 JacksonFeature 类以处理 JSON 内容类型。


Java


private static final JerseyLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler            = JerseyLambdaContainerHandler.getAwsProxyHandler(jerseyApplication);
复制代码


然后,我们声明 JerseyLambdaContainerHandler 对象的第二个静态实例。我们将使用 getAwsProxyHandler 静态方法和 ResourceConfig 对象,将此对象初始化。 getAwsProxyHandler 方法会自动创建一个配置处理 API Gateway 代理集成事件的库实例。您可以创建 RequestReader 以及 ResponseWriter 对象的自定义实现,以支持自定义事件类型。您将注意到这两个变量都被声明为静态类成员。它们之所以是类成员,是因为我们只需要这些对象的单一实例。AWS Lambda 尝试在不同的调用之间重复使用容器。我们句柄类由运行时作为单例模式持有,每次都会调用 handleRequest 方法。我们可以重复使用 ResourceConfigJerseyLambdaContainerHandler。静态变量将在 Lambda 启动它时由 Java 运行时实例化;从而提高了繁重内省操作的性能。


Java


public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)            throws IOException {    handler.proxyStream(inputStream, outputStream, context);
复制代码


句柄方法的实现时 Lambda 的主要切入点。在句柄方法内部,我们会单次调用容器句柄的 proxyStream 方法。 proxyStream 方法将负责读取我们的输入流并利用其数据创建 HttpServletRequest 。应用程序生成的 HttpServletResponse 将按照 Amazon API Gateway 要求的格式自动写入输入流。

项目根

在项目根中,原型会生成三个文件: pom.xmlsam.yamlREADME.md。pom 文件声明项目并定义 Maven 依存关系。它包含 serverless-java-container 库。


Xml


<dependency>    <groupId>com.amazonaws.serverless</groupId>   <artifactId>aws-serverless-java-container-jersey</artifactId>   <version>1.0</version></dependency>
复制代码


pom 文件还使用 Maven Shade 插件来生成一个可以加载到 AWS Lambda 的“uber-jar”。请参阅 <build> 部分。 sam.yaml 文件是一种无服务器应用程序模型 (SAM) 模板,我们可以用它将应用程序部署到 AWS 或在本地使用 SAM Local 进行测试。SAM 是对 CloudFormation 的进一步抽象,从而可以更方便地定义代码中的无服务器堆栈。SAM 文件定义了我们单一资源 AWS::Serverless::Function。该函数配置使用编译进程生成的“uber-jar”并指向我们的句柄类。API 前端在函数资源中的 Events 部分定义。在部署模板时将会隐含创建 API Gateway RestApiStageDeploymentREADME.md 文件包含了所生成的应用程序构建、部署和测试指令集。

在本地测试应用程序

您可以使用 AWS SAM Local 在本地计算机上启动服务。如要启动 SAM Local,您需要安装并运行 Docker (社区版或企业版)。首先安装 SAM Local (如果尚未安装):


Bash


$ npm install -g aws-sam-local
复制代码


然后使用一台终端,打开项目根文件夹并构建 jar 文件。


Bash


$ cd myworkspace/jersey-sample$ mvn clean package
复制代码


仍在 sam.yaml 文件所在的项目根文件夹中 — 使用 SAM Local CLI 启动 API。


Bash


$ sam local start-api --template sam.yaml
...Mounting com.sapessi.jersey.StreamLambdaHandler::handleRequest (java8) at http://127.0.0.1:3000/{proxy+} [OPTIONS GET HEAD POST PUT DELETE PATCH]...
复制代码


现在我们已经启动并运行了 API Gateway 和 Lambda 的本地模拟器。使用新的壳,您可以向 API 发送测试 Ping 请求:


Bash


$ curl -s http://127.0.0.1:3000/ping | python -m json.tool
{ "pong": "Hello, World!"}
复制代码

部署到 AWS

您可以使用 AWS CLI 快速将应用程序部署到 AWS Lambda 和 Amazon API Gateway。您将需要 S3 存储桶来存储需要部署的工件。创建了 S3 存储桶后,从文件 sam.yaml 所在的项目根文件夹运行如下命令:


Bash


$ aws cloudformation package --template-file sam.yaml --output-template-file output-sam.yaml --s3-bucket <YOUR S3 BUCKET NAME>Uploading to xxxxxxxxxxxxxxxxxxxxxxxxxx  6464692 / 6464692.0  (100.00%)Successfully packaged artifacts and wrote output template to file output-sam.yaml.Execute the following command to deploy the packaged templateaws cloudformation deploy --template-file /your/path/output-sam.yaml --stack-name <YOUR STACK NAME>
复制代码


如命令的输出所显示,您现在可以使用 CLI 来部署应用程序。选择堆栈名称并从包命令的输出中运行 aws cloudformation deploy 命令。


Bash


$ aws cloudformation deploy --template-file output-sam.yaml --stack-name ServerlessJerseyApi --capabilities CAPABILITY_IAM
复制代码


应用程序部署完成后,您可以描述堆栈以显示 API 终端节点已经创建。终端节点应当是 OutputsServerlessJerseyApikey 属性:


Bash


$ aws cloudformation describe-stacks --stack-name ServerlessJerseyApi --query 'Stacks[0].Outputs[*].{Service:OutputKey,Endpoint:OutputValue}'[    {    "Service": "JerseySampleApi",    "Endpoint": "https://xxxxxxx.execute-api.us-west-2.amazonaws.com/Prod/ping"    }]
复制代码


OutputValue 复制到浏览器中,或者使用 curl 来测试您的第一个请求:


Bash


$ curl -s https://xxxxxxx.execute-api.us-west-2.amazonaws.com/Prod/ping | python -m json.tool
{ "pong": "Hello, World!"}
复制代码

冷启动说明

Java 是一种大型运行时;因此有必要在此包含有关冷启动的内容。冷启动是在 AWS Lambda 需要启动基础设施、启动运行时以及启动代码时第一次调用您的 Lambda 函数。多个因素可能会影响函数首次启动的速度:


  • 内存和 CPU 分配:AWS Lambda 将一定比例的 CPU 周期分配给您分配到函数的内存。生成的 SAM 模板默认使用 512MB 内存。如果您的代码为 CPU 密集型代码,请增加此参数以提高性能。

  • 代码包大小:每当 Lambda 函数首次启动时,它需要下载并解压代码包。“uber-jar”的大小十分重要。在导入依存关系时要特别注意,请使用 Maven Shade 插件来剔除不必要的临时性依存关系。例如,在此库的 Spark 实现中,我们删除了嵌入的 Jetty 容器

  • 代码生命周期:在第一次启动函数时,AWS Lambda 会创建一个句柄对象实例,并且将在未来调用时以单例模式重复使用,直接针对您的 handleRequest 方法。这意味着您可以使用类成员来缓存您希望在不同的调用中重复使用的元数据、对象以及连接。不要缓存保密数据:无法保证 Lambda 实例将在有一天被任何人使用,并且有人在某一天会不可避免地忘记清除调用之间缓存的数据。

  • 框架有不同的功能和性能特征。Spring 和 Spring Boot 在注入依存关系以及自动连接应用程序方面的功能极其强大。但这会让您丧失冷启动时间的灵活性 — 反射十分缓慢。Jersey 仅执行少量的反射以查找其提供商和资源。Spark 中的一切都是“静态链接”的,是目前启动最快的框架。


由于所有这些参数的原因,我们要如何选择正确的框架?如果您没有严格的延迟要求,请选择自己最满意的框架。在实践中利用生产流量,您将发现冷启动仅影响 1% (如果不是 0.1%) 的指标。

结论

利用无服务器 Java 容器,可以方便地使用任意 Java 框架创建可扩展的 API。在本博中,我使用 Jersey,该库还提供 SpringSpring BootSpark 原型。使用原型创建的项目预封装了一个运行的 Lambda 句柄、一个示例 /ping 资源以及一个便于您快速在本地测试应用程序并将其部署到 AWS 的 SAM 模板。如果您遇到有关无服务器 Java 容器库的问题,请在我们的 GitHub 存储库报告。有关 AWS LambdaAmazon API Gateway 的更多信息,请使用 AWS 论坛。


本文转载自 AWS 技术博客。


原文链接:


https://amazonaws-china.com/cn/blogs/china/java-apis-aws-lambda/


活动推荐:

2023年9月3-5日,「QCon全球软件开发大会·北京站」 将在北京•富力万丽酒店举办。此次大会以「启航·AIGC软件工程变革」为主题,策划了大前端融合提效、大模型应用落地、面向 AI 的存储、AIGC 浪潮下的研发效能提升、LLMOps、异构算力、微服务架构治理、业务安全技术、构建未来软件的编程语言、FinOps 等近30个精彩专题。咨询购票可联系票务经理 18514549229(微信同手机号)。

2019-10-28 08:00540

评论

发布
暂无评论
发现更多内容

项目管理如何有效进行

PingCode

架构实战营:模块五作业

Geek_93ffb0

「架构实战营」

模块 5 作业

miliving

阿里云刘伟光:3.5万字拆解核心系统转型,核心从业者如何寻得“出路”

OceanBase 数据库

阿里 数字化转型 OceanBase 社区版 核心系统

VuePress 博客优化之拓展 Markdown 语法

冴羽

JavaScript Vue markdown vuepress 博客搭建

2021年小总结暨2022年打脸计划

秦怀杂货店

总结 程序人生、

架构实战营 4 期第五模块作业

jialuooooo

架构实战营

从零开发区块链应用(四)--自定义业务错误信息

杰哥的技术杂货铺

(1-15/15) 预训练模型+微调范式下如何做到文本数据安全

mtfelix

300天创作 2022Y300P

金融云原生漫谈(六)|安全平稳高于一切的金融行业,如何构建云原生安全防线

York

容器 云原生 安全 金融科技

蚂蚁大规模 Sigma 集群 Etcd 拆分实践

SOFAStack

etcd #k8s SIGMA

Spring都在用的技术,你确定不过来看看?1️⃣

XiaoLin_Java

1月日更

从零开发区块链应用(一)--golang配置文件管理工具viper

杰哥的技术杂货铺

golang 区块链

HarmonyOS工程【鸿蒙开发02】

坚果

鸿蒙开发 1月月更

网关流控利器:结合 AHAS 实现 Ingress/Nginx 流量控制

阿里巴巴云原生

nginx 阿里云 高可用 云原生 ingress

基于 Prometheus 的边缘计算监控实践

火山引擎边缘云

云原生 监控 边缘计算

逐鹿万亿赛道:智能重卡规模量产的困境与进化

脑极体

创新推出 | Serverless 场景排查问题利器:函数实例命令行操作

阿里巴巴云原生

阿里云 Serverless 云原生 函数计算

鸿蒙开发初体验【鸿蒙01】

坚果

鸿蒙 1月月更

Go 语言快速入门指南:Go 结构体

宇宙之一粟

Go 语言 结构体 1月月更

瀑布式开发与敏捷开发的区别是什么

PingCode

从零开发区块链应用(三)--mysql初始化及gorm框架使用

杰哥的技术杂货铺

一个cpp协程库的前世今生(二十)外部调度

SkyFire

c++ cocpp

使用 google_breakpad 分析 Electron 崩溃日志文件

编程三昧

Electron 1月月更 google_breakpad

从零开发区块链应用(二)--mysql安装及数据库表的安装创建

杰哥的技术杂货铺

实时云渲染,汽车产业数字化转型新动能

3DCAT实时渲染

云计算 数字化 汽车 云渲染

【组件攻击链】一文看懂Spring全家桶各类RCE漏洞

H

网络安全 漏洞

谈A股投资策略--《香帅中国财富报告》摘录(5/100)

hackstoic

投资

Discord模式等十大场景,环信带你玩转泛娱乐行业

环信

即时通讯 IM 泛娱乐 Discord

如何快速调度 PTS 的百万并发能力

阿里巴巴云原生

阿里云 云原生 Jmeter 压测 PTS

混合云应用双活容灾最佳实践

阿里巴巴云原生

阿里云 运维 云原生 混合云 多活容灾

  • 扫码添加小助手
    领取最新资料包
OpenSource | 在AWS lambda上运行用Java编写的APLs_语言 & 开发_亚马逊云科技 (Amazon Web Services)_InfoQ精选文章