Caused by: java.io.FileNotFoundException: __app__.jar或WARN internal.EntityManagerFactoryRegistry
2018.11.18
文章目錄
前言
專案背景:有一個基於Spring Boot的排程器,負責排程並向Yarn提交Spark作業。在測試時發現,有個Spark作業一直報__app__.jar找不到的錯誤。
ERROR yarn.ApplicationMaster: User class threw exception: org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [applicationContext.xml]; nested exception is java.lang.IllegalStateException: Unable to load schema mappings from location [META-INF/spring.schemas] org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [applicationContext.xml]; nested exception is java.lang.IllegalStateException: Unable to load schema mappings from location [META-INF/spring.schemas] at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:414) ... at org.apache.spark.deploy.yarn.ApplicationMaster$$anon$2.run(ApplicationMaster.scala:542) Caused by: java.lang.IllegalStateException: Unable to load schema mappings from location [META-INF/spring.schemas] at org.springframework.beans.factory.xml.PluggableSchemaResolver.getSchemaMappings(PluggableSchemaResolver.java:154) ... at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:391) ... 27 more Caused by: java.io.FileNotFoundException: /path/application_1540964852368_1198/container_1540964852368_1198_01_000001/__app__.jar (No such file or directory) at java.util.zip.ZipFile.open(Native Method) ... at org.springframework.beans.factory.xml.PluggableSchemaResolver.getSchemaMappings(PluggableSchemaResolver.java:145) ... 48 more
看到root caused by之後,我一度認為是叢集的問題,比如Yarn某個節點不穩定,導致jar包在分發時失敗,進而執行失敗。然而在經過多次重啟後,除了這個作業其它作業都能正常執行,這種說法是站不住腳的。
方法
在向上翻看log時,發現一個跟Spring Boot有關的Warning:
WARN internal.EntityManagerFactoryRegistry: HHH000436: Entity manager factory name (default) is already registered. If entity manager will be clustered or passivated, specify a unique value for property 'hibernate.ejb.entitymanager_factory_name'
從Warning可以看出,報錯的作業至少試圖註冊兩個同名的EntityManagerFactory1。專案使用了Hibernate作為ORM,Hibernate中,每個EntityManagerFactory有一個對應的Persistence Unit,Persistence Unit(PU)要求擁有唯一的名字2。再看這個Warning就可以得知,當前JPA配置裡的EntityManagerFactory已經註冊,並且有另一個EntityManagerFactory也試圖註冊在同一個PU名下3。
錯誤的嘗試
在沒有了解PU和EMF關係之前,我根據Warning的直接理解,嘗試著在JPA的配置裡對EntityManagerFactory的Bean進行唯一命名(比如用當前時間生成Bean的名字)。這種想法期望的結果是:驗證是不是因為多次註冊同一個EntityManagerFactory而導致報錯,如果唯一命名之後不再報錯,再去定位多次註冊的原因是什麼。
個人認為出發點是好的,希望能逐步地逼近真相。但是由於缺乏對PU和EMF的瞭解,所以結果就導致報錯一直在。實際上,應該為EMF的PU設定唯一命名4:
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
dataSource.setUrl("<URL>");
dataSource.setUsername("<USER>");
dataSource.setPassword("<PWD>");
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPersistenceUnitName("<UNIQUE_PU_NAME>");
}
正確的解法
由於只有一個作業會報錯,應該直接檢查作業相關的程式碼。後來發現原因是:在某段程式碼裡,多次地建立ClassPathXmlApplicationContext
並用ClassPathXmlApplicationContext#getBean
方法去獲取操作資料庫的Bean,結果導致JPA配置被多次載入,EMF也就被多次註冊。