AWS lambda 和 Java 并发

Posted

技术标签:

【中文标题】AWS lambda 和 Java 并发【英文标题】:AWS lambda and Java concurrency 【发布时间】:2016-10-27 06:24:51 【问题描述】:

众所周知,AWS lambda 可能重用早期创建的处理程序对象,并且确实做到了(参见FAQ):

问:AWS Lambda 会复用函数实例吗?

为了提高性能,AWS Lambda 可能会选择保留 您的函数并重用它来服务后续请求,而不是 创建一个新副本。您的代码不应假定这将始终 发生。


问题与Java 并发有关。如果我有一个处理程序的类,请说:

public class MyHandler 
    private Foo foo;
    public void handler(Map<String,String> request, Context context) 
       ...
    

那么,在这里访问和使用对象变量foo 是否是线程安全的?

换句话说:AWS lambda 是否可以同时为不同的调用使用同一个对象?

EDIT 我的函数在基于事件的源上进行处理,特别是由 API Gateway 方法调用。

EDIT-2 当您想实现某种与外部资源的连接池时,就会出现此类问题,因此我想将与外部资源的连接保持为对象变量。它实际上可以按预期工作,但我担心并发问题。

EDIT-3 更具体地说,我想知道:AWS lambda 处理程序的实例是否可以共享公共堆(内存)?我必须指定这个额外的细节,以防止答案列出关于 java 线程安全对象的明显和众所周知的事情。

【问题讨论】:

IMO“保留函数的实例并重用它”与“使用相同的对象”完全相同。而且很可能同时发生。 @zapl 当然,但同样,无状态风格并不意味着它们不能是线程安全的。 @Shibashis 我在每秒数百个请求的情况下使用它。如果我的上下文是单线程的,我自然不会问这样的问题。 你的问题不清楚。如果在您的函数中,请确保您的函数以线程安全的方式访问 foo 变量。由于 lambda 函数的重用,您不应该担心线程安全。它仅在函数不处理另一个请求时才重用。有点像对象池。该函数的每个实例都在底层容器中运行并且是独立的。 @Shibashis 我的问题实际上不清楚什么?我问了一个精确的问题:关于重用处理程序实例的可能性,它是否是线程安全的使用对象变量?什么不清楚?关于评论的第二部分 - 如果您有指向描述此功能的确切文档的链接,您可以提供它作为答案,它将被接受。 【参考方案1】:

AWS lambda 可以同时为不同的调用使用同一个对象吗?

AWS lambda 的处理程序实例是否可以共享公共堆(内存)?

一个强有力的,明确的NO。 AWS Lambda 的处理程序实例甚至无法共享文件(/tmp)。

AWS Lambda 容器可能被重复用于两个或多个同时存在的 Lambda 函数调用,因为这会破坏隔离要求:

Q: How does AWS Lambda isolate my code?

每个 AWS Lambda 函数在其自己的隔离环境中运行,具有自己的资源和文件系统视图。

how lambda functions work 的官方描述中的“AWS Lambda 如何运行我的代码?容器模型”部分指出:

执行 Lambda 函数后,AWS Lambda 会维护 容器一段时间以期待另一个 Lambda 函数 调用。实际上,服务在一个 Lambda 函数完成并解冻容器以供重用,如果 AWS Lambda 选择在 Lambda 函数运行时重用容器 再次调用。这种容器重用方法有以下几点 含义:

您的 Lambda 函数代码中的任何声明都保持初始化状态, 再次调用函数时提供额外的优化。 例如,如果您的 Lambda 函数建立一个数据库 连接,而不是重新建立连接,原来的 连接用于后续调用。您可以在其中添加逻辑 在创建连接之前检查您的代码是否已经存在。

每个容器在 /tmp 目录中提供一些磁盘空间。这 当容器被冻结时,目录内容仍然存在,提供 可用于多次调用的瞬态缓存。你可以加 额外的代码来检查缓存是否有你存储的数据。

由您的 Lambda 函数发起的后台进程或回调 如果 AWS Lambda 在函数结束时未完成恢复 选择重用容器。你应该确保任何背景 代码中的进程或回调(如果是 Node.js)是完整的 在代码退出之前。

如您所见,在尝试利用容器重用时,绝对没有关于 Lambda 函数的多个并发调用之间的竞争条件的警告。唯一的注意是“不要依赖它!”。

【讨论】:

赏金将尽快发放,因此允许(还剩 22 小时)【参考方案2】:

在使用 AWS Lambda 时,利用执行上下文重用绝对是一种做法(请参阅 AWS Lambda Best Practices)。但这不适用于并发执行,因为并发执行会创建一个新容器并因此创建新上下文。简而言之,对于并发执行,如果一个处理程序更改了值,其他处理程序将不会获得新值。

【讨论】:

【参考方案3】:

据我所知,没有与 Lambda 相关的并发问题。只有一个调用“拥有”容器。第二次调用将获得另一个容器(或者可能必须等到第一个容器空闲)。

但我没有找到任何保证 Java 内存可见性 问题不会发生的保证。在这种情况下,第一次调用所做的更改对于第二次调用可能保持不可见。或者第一次调用的更改将在第二次调用完成更改后写入 RAM。

在大多数情况下,可见性问题的处理方式与并发问题相同。因此,我建议开发 Lambda 函数线程安全(或同步)。至少只要 AWS 不向我们保证,他们会在每次调用后将 CPU 状态刷新到内存中。

【讨论】:

以上是关于AWS lambda 和 Java 并发的主要内容,如果未能解决你的问题,请参考以下文章

aws lambda 上的保留并发不会阻止 lambda 进行更多扩展?

AWS Lambda 异步并发限制

在 AWS Lambda 函数中从 S3 获取对象并发送到 Api Gateway

并发.futures.ThreadPoolExecutor的使用总是在aws lambda中抛出超时异常

FUNCTION_ERROR_INIT_FAILURE AWS lambda

读Java8函数式编程笔记06_Lambda表达式编写并发程序