本文介紹了AWS Lambda使用來自多版本JAR的不正確類文件?的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!
問題描述
我有一個lambda在Java 8下運行了幾年,我剛剛把它更新到Java 11。它立即壞了,給我這樣的錯誤:
Caused by: java.lang.ExceptionInInitializerError
at com.mycompany.rest.providers.JsonProvider.writeTo(JsonProvider.java:80)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:242)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:227)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:139)
at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1116)
at org.glassfish.jersey.client.ClientRequest.doWriteEntity(ClientRequest.java:461)
at org.glassfish.jersey.client.ClientRequest.writeEntity(ClientRequest.java:443)
at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:367)
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:265)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:297)
... 15 more
Caused by: java.lang.UnsupportedOperationException: No class provided, and an appropriate one cannot be found.
at org.apache.logging.log4j.LogManager.callerClass(LogManager.java:571)
at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:596)
at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:583)
at com.mycompany.rest.util.NonClosingOutputStream.<clinit>(NonClosingOutputStream.java:11)
... 25 more
這個類并不特別令人興奮,它有一個簡單的靜態初始化,這在我的類中很常見:
public class NonClosingOutputStream extends ProxyOutputStream {
private static final Logger log = LogManager.getLogger(); // Line 11
public NonClosingOutputStream(final OutputStream proxy) {
super(proxy);
}
...
我以前遇到過這樣的問題,當我將我的(非Lambda)Java服務器從8切換到11時;我需要將JAR的清單標記為Multi-Release: true
,因為我所依賴的ApacheLog4j構件為Java 8和9+中的org.apache.logging.log4j.util.StackLocator
類提供了替代實現。然而,我有點希望JVM只選擇類的適當版本。是否有我必須在某個地方設置的配置?是否可能在某處將我的Lambda從Java 8->;Java 11切換為混淆了?
jar/META-INF/versions:
versions/
├── 11
│?? └── org
│?? └── glassfish
│?? └── jersey
│?? └── internal
│?? └── jsr166
│?? ├── JerseyFlowSubscriber$1.class
│?? ├── JerseyFlowSubscriber.class
│?? ├── SubmissionPublisher$1.class
│?? ├── SubmissionPublisher$2.class
│?? ├── SubmissionPublisher$3.class
│?? ├── SubmissionPublisher$4.class
│?? ├── SubmissionPublisher$5.class
│?? ├── SubmissionPublisher$6.class
│?? ├── SubmissionPublisher.class
│?? └── SubmissionPublisherFactory.class
└── 9
├── module-info.class
└── org
└── apache
└── logging
└── log4j
├── core
│?? └── util
│?? └── SystemClock.class
└── util
├── Base64Util.class
├── ProcessIdUtil.class
├── StackLocator.class
└── internal
└── DefaultObjectInputFilter.class
編輯:我發現some references表明,當AWS Lambda提取JAR時,他們不會提取META-INF目錄,該目錄包含MANIFEST.MF文件,該文件告訴JVM該JAR是多版本JAR。Lambda是否支持多版本JAR?
推薦答案
不完全是您問題的答案,但我希望這可能會有幫助。
您的分析是正確的-AWS lambda提取整個JAR文件。然后,運行lambda函數的JVM不再將代碼識別為JAR文件,并且實際上會忽略整個META-INF目錄。
在我的例子中,我使用maven-shade-plugin
創建了一個包含lambda函數的所有依賴項的&uber";-jar。official AWS documentation中推薦使用這種方法。
現在–這一點很重要–maven-shade-plugin
提取所有JAR文件依賴項,并將它們重新打包到單個平面JAR文件中。如果您的一個依賴項是多版本JAR(就像log4j2一樣),那么您可以配置maven-shade-plugin
以重新構建適當的META-INF目錄,如果您將JAR作為JAR文件運行,那么一切仍然正常。
但是,因為AWS Lambda提取JAR,所以JVM不再看到META-INF目錄,并且META-INF/版本中的任何內容都將被忽略。
要解決這個問題,我切換到maven-assembly-plugin
。它允許使用lambda的代碼創建ZIP文件,并將依賴項添加為JAR文件。現在,當AWS Lambda解壓縮此ZIP文件時,JAR保持完好,一切工作正常。
要進行配置,請創建一個文件assembly.xml
,如下所示:
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>zip</id>
<!-- Make sure the ZIP contents are not nested in a subdirectory -->
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}/conf</directory>
</fileSet>
<!-- Include the compiled classes as-is and put them in the root of the ZIP -->
<fileSet>
<directory>${project.build.directory}/classes</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
<dependencySets>
<!-- Include all dependencies in the lib/ directory -->
<dependencySet>
<outputDirectory>lib</outputDirectory>
<excludes>
<exclude>${project.groupId}:${project.artifactId}:jar:*</exclude>
</excludes>
</dependencySet>
</dependencySets>
</assembly>
然后您需要在pom.xml
中配置maven-assembly-plugin
:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
<finalName>${project.artifactId}</finalName>
</configuration>
</execution>
</executions>
</plugin>
現在,只需像往常一樣將生成的壓縮文件部署到AWS Lambda,就可以了!
作為備注-陰影JAR文件包含數千個單獨的.class
文件,而匯編的ZIP文件只包含少數幾個JAR文件。即使總體大小(以字節為單位)更大,文件的數量也會少得多,從而減少冷啟動時間。我還沒有在AWS Cloud上測試過這一點,但在我的LocalStack上,冷啟動時間從大約1分鐘降到了6秒–這絕對是一個很好的開發助推器。
這篇關于AWS Lambda使用來自多版本JAR的不正確類文件?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,