众所周知,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可以同时为不同的调用使用相同的对象吗?
编辑我的功能是在基于事件的源上处理的,特别是它由API网关方法调用。
EDIT-2 当您想要将某种连接池实现到外部资源时,这类问题会上升,因此我希望将与外部资源的连接保持为对象变量。它实际上可以正常工作,但我害怕并发问题。
EDIT-3 更具体地说,我想知道: AWS lambda处理程序的实例是否可以共享公共堆(内存)?我必须指定这个额外的细节,以防止回答列出有关java线程安全对象的明显和常见的事情。
答案 0 :(得分:59)
可能AWS lambda同时使用同一个对象进行不同的调用吗?
AWS lambda处理程序的实例是否可以共享公共堆(内存)?
强烈的,明确的NO。 AWS Lambda处理程序的实例甚至无法共享文件(/tmp
)。
AWS Lambda容器可以不重用于Lambda函数的两个或多个并发存在的调用,因为这会破坏隔离要求:
Q: How does AWS Lambda isolate my code?
每个AWS Lambda函数在其自己的隔离环境中运行,具有自己的资源和文件系统视图。
&#34; AWS Lambda如何运行我的代码? how lambda functions work州官方说明中的容器模型&#34; :
执行Lambda函数后,AWS Lambda会维护 容器有一段时间以期待另一个Lambda函数 调用。实际上,服务冻结后的容器 如果AWS,Lambda函数完成并解冻容器以供重用 Lambda选择在Lambda函数时重用容器 再次调用。这种容器重用方法具有以下特征 影响:
Lambda函数代码中的任何声明都会保持初始化状态, 在再次调用函数时提供额外的优化。 例如,如果您的Lambda函数建立了数据库 连接,而不是重新建立连接,原始 连接用于后续调用。您可以添加逻辑 您的代码在创建连接之前检查连接是否已存在。
每个容器在/ tmp目录中提供一些磁盘空间。该 提供时,目录内容在容器冻结时保留 瞬态缓存,可用于多次调用。你可以加 额外的代码,用于检查缓存是否包含您存储的数据。
Lambda函数启动的后台进程或回调 如果AWS Lambda在函数结束时未完成 选择重用容器。你应该确定任何背景 代码中的进程或回调(如果是Node.js)已完成 在代码退出之前。
正如您所看到的,在尝试利用容器重用时,Lambda函数的多个并发调用之间绝对没有关于竞争条件的警告。唯一的注意事项是&#34;不要依赖它!&#34;。
答案 1 :(得分:0)
利用执行上下文重用绝对是与AWS Lambda一起使用时的一种做法(请参见AWS Lambda Best Practices)。但这不适用于并发执行,因为对于并发执行,将创建新的容器并因此创建新的上下文。简而言之,对于并发执行,如果一个处理程序更改了另一个处理程序的值,则不会获得新的值。
答案 2 :(得分:0)
据我所知,没有与 Lambda 相关的并发问题。只有一个调用“拥有”容器。第二次调用将获得另一个容器(或者可能必须等到第一个容器空闲)。
但我没有找到任何保证 Java 内存可见性 问题不会发生。在这种情况下,第一次调用所做的更改可能对第二次调用保持不可见。或者第一次调用的变化会在第二次调用完成后写入 RAM。
在大多数情况下,可见性问题的处理方式与并发问题的处理方式相同。因此,我建议开发 Lambda 函数线程安全(或同步)。至少只要 AWS 不向我们保证,他们会在每次调用后做一些事情来将 CPU 状态刷新到内存中。