1. 程式人生 > >Caused by: java.io.FileNotFoundException: __app__.jar或WARN internal.EntityManagerFactoryRegistry

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也就被多次註冊。


  1. Spring with JPA ↩︎

  2. Spring ORM Data Access ↩︎

  3. StackOverflow about ‘Entity manager factory name (empsys) is already registered’ ↩︎

  4. Spring Boot Use Two EntityManagers ↩︎