问题描述
我已经将成熟的Web应用程序部署到了tomcat 7上的新服务器上。该系统使用的数据库是空的,因为我们处于对其进行配置的早期阶段。
转到该应用程序,您将获得一个登录页面。 登录,通常会带您到应用程序的主页。
但是在第二天早上来之后,我们总是遇到同样的问题:
- 我们弹出登录屏幕-没问题
- 输入我们的用户名和密码-系统挂起
- 我们转到tomcat并使用系统托盘,停止服务。
- 停止服务进度条出现,然后消失,但是tomcat属性对话框上的状态仍然显示为“已启动”,并且“开始”和“停止”按钮均被禁用。
- 我们检查了tomcat日志,没有错误
- 我们重新启动服务器,它再次正常运行
我们看不到明显的东西。 一个tomcat的“查找泄漏”请求什么都没有显示,并且在VisualVM上查看堆大小显示了一致的堆占用模式,随后进行了垃圾回收,将其降低到相同的低水平(因此没有明显的泄漏)
我以为可能是mysql连接超时,但是不应该这样,因为如果我使用错误的密码登录,系统将进入数据库以检查密码并按预期返回“错误密码”。 唯一失败的地方是输入正确的密码。
我们唯一的线索是登录时出现错误,系统使用一些自定义代码来找出用户的主机名:
2019-02-14 08:10:14,277 08:10:14.277 [http-bio-8080-exec-9] ERROR com.sw.app.ui.UserAuthenticatedWebSession - Unknown host!
java.net.UnknownHostException: null
at java.net.Inet6AddressImpl.getHostByAddr(Native Method) ~[na:1.8.0_201]
at java.net.InetAddress$2.getHostByAddr(Unknown Source) ~[na:1.8.0_201]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_201]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_201]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_201]
at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_201]
at com.sw.app.data.utils.NetUtilities.getHostName(NetUtilities.java:114) ~[app-data-4.0.15.1-SNAPSHOT.jar:na]
仅当用户成功登录以存储他们从中登录的位置时,才调用此方法,但是该异常会被捕获到代码中,然后才被记录而不是向上传播,然后我们使用默认的“未知”主机名。 这是代码:
public static String getHostName( InetAddress inaHost ) throws UnknownHostException
{
try {
Class<? extends InetAddress> clazz = Class.forName( "java.net.InetAddress" ).asSubclass( InetAddress.class );
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
constructors[0].setAccessible( true );
InetAddress ina = (InetAddress)constructors[0].newInstance();
Field[] fields = ina.getClass().getDeclaredFields();
for( Field field : fields ) {
// Depends on the version of java we are dealing with:
// Older version - single nameservice
if( field.getName().equals( "nameService" ) ) {
return getHostName( field.get( null ), inaHost );
} else if( field.getName().equals( "nameServices" ) ) {
// newer version - multiple name services possible
StringBuilder builder = new StringBuilder();
field.setAccessible( true );
// A list of nameservice objects
@SuppressWarnings( "unchecked" )
List<Object> nameServices = (List<Object>)field.get( null );
for( Object nameService : nameServices ) {
String hostName = getHostName( nameService, inaHost );
if( builder.length() > 0 ) {
builder.append( ", " );
}
builder.append( hostName );
}
return builder.toString();
}
}
} catch( ClassNotFoundException cnfe ) {
throw new InvalidOperationException( "Class not found when looking up host name", cnfe );
} catch( IllegalAccessException iae ) {
throw new InvalidOperationException( "Cannot access method/field", iae );
} catch( InstantiationException ie ) {
throw new InvalidOperationException( "Cannot instantiate class", ie );
} catch( InvocationTargetException ite ) {
throw (UnknownHostException)ite.getCause();
}
return null;
}
/**
* Get the host name using reflection on the hidden class implementation of the InetAddress details.
* @param p_nameService
* @param p_address
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
private static String getHostName( Object nameService, InetAddress address ) throws IllegalAccessException, InvocationTargetException {
Method[] methods = nameService.getClass().getDeclaredMethods();
for( Method method : methods ) {
// The nameService is assumed to have a method, getHostByAddr, which takes the byte[] inet address
if( method.getName().equals( "getHostByAddr" ) ) {
method.setAccessible( true );
return (String)method.invoke( nameService, address.getAddress() );
}
}
return "";
}
有人有类似的问题吗?
-编辑-
这是数据库配置bean类。
@Configuration
public class AppPersistence {
private static final Logger LOGGER = LoggerFactory.getLogger( AppPersistence.class );
protected static final String INTERNAL_IP_DOMAIN = "*******";
protected static final String JDBC_PROTOCOL = "jdbc:mysql://";
protected static final String DEFAULT_DATABASE_NAME = "*******";
/** The path for context-based property lookups */
protected static final String CONTEXT_LOOKUP_PATH = "java:comp/env";
/** This is the default location for the database - on the same machine as the deployment */
protected static final String DB_LOCAL = JDBC_PROTOCOL + "localhost:3306/" + DEFAULT_DATABASE_NAME;
@Bean
public DataSource createDataSource() throws Exception {
BasicDataSource source = new BasicDataSource();
// allow for parameterised config
source.setDriverClassName( Driver.class.getName() );
source.setUrl( getProperty( "app.database.url", DB_LOCAL ) );
source.setUsername( getProperty( "app.database.username", "*****" ) );
source.setPassword( getProperty( "app.database.password", "****" ) );
LOGGER.warn( "Connecting to: " + source.getUrl() );
return source;
}
protected String getProperty( String name, String default ) {
// first check system properties
String val = System.getProperty( name );
if( val != null ) {
logLookup( "System Properties", name, val );
return val;
}
// check environment variables
val = System.getenv( name );
if( val != null ) {
logLookup( "System Environment Variables", name, val );
return val;
}
// if we are deployed to a container, check the environment variables in that.
try {
Context context = InitialContext.doLookup( "java:comp/env" );
if( context != null ) {
Object valObj = context.lookup( name );
if( valObj != null ) {
logLookup( "Context", name, valObj.toString() );
return valObj.toString();
}
}
} catch( NamingException e ) {
// if running on a dev machine this will probably happen
LOGGER.warn( "Could not find context for lookup of " + p_name + " - assuming running in dev mode with defaults. Error was: " + e.toString( true ) );
LOGGER.info( "Error received on lookup of " + name + ":", e );
}
return p_default;
}
protected void logLookup( String source, String lookup, String value ) {
if( value.contains( "password" ) ) {
// avoid displaying any password info
LOGGER.warn( "Successfully looked up sensitive value from " + source + " for name '" + lookup + "': [******]" );
} else {
LOGGER.warn( "Successfully looked up value from " + source + " for name '" + lookup + "': '" + value + "'" );
}
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory( DataSource dataSource ) {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPersistenceUnitName( "com.sw.app.data.persistence" );
entityManagerFactory.setDataSource( dataSource );
entityManagerFactory.setJpaVendorAdapter( new HibernateJpaVendorAdapter() );
entityManagerFactory.setLoadTimeWeaver( new InstrumentationLoadTimeWeaver() );
entityManagerFactory.setJpaDialect( new HibernateJpaDialect() );
entityManagerFactory.setPackagesToScan( "com.sw.app.data", "com.sw.app.rawimport",
"com.sw.app.view", "com.sw.app.warranty" );
entityManagerFactory.setJpaPropertyMap( hibernateJpaProperties( dataSource ) );
return entityManagerFactory;
}
private Map<String, ?> hibernateJpaProperties( DataSource dataSource ) {
HashMap<String, String> properties = new HashMap<>();
// Need to copy these values over, otherwise c3p0 can't see them.
if( dataSource instanceof BasicDataSource ) {
BasicDataSource source = (BasicDataSource)p_dataSource;
properties.put( "hibernate.connection.driver_class", source.getDriverClassName() );
properties.put( "hibernate.connection.url", source.getUrl() );
properties.put( "hibernate.connection.username", source.getUsername() );
properties.put( "hibernate.connection.password", source.getPassword() );
}
// Added to avoid some merge problems when updating entities (eg contact to custimport)
properties.put( "hibernate.event.merge.entity_copy_observer", "allow" );
// Second level cache
properties.put( "hibernate.cache.use_second_level_cache", "true" );
properties.put( "hibernate.cache.use_query_cache", "true" );
properties.put( "hibernate.cache.provider_class", "org.hibernate.cache.EhCacheProvider" );
properties.put( "hibernate.cache.region.factory_class", EhCacheRegionFactory.class.getName() );
properties.put( "hibernate.generate_statistics", "false" );
properties.put( "hibernate.show_sql", "false" );
properties.put( "hibernate.format_sql", "false" );
// validate | update | create | create-drop -->
properties.put( "hibernate.hbm2ddl.auto", "update" );
properties.put( "hibernate.dialect", MySQL5Dialect.class.getName() );
// [main] WARN org.hibernate.cfg.AnnotationBinder - HHH000457: Joined inheritance hierarchy [com.sw.system4.data.collateral.AbstractCollateral] defined explicit @DiscriminatorColumn. Legacy Hibernate behavior was to ignore the @DiscriminatorColumn. However, as part of issue HHH-6911 we now apply the explicit @DiscriminatorColumn. If you would prefer the legacy behavior, enable the `hibernate.discriminator.ignore_explicit_for_joined` setting (hibernate.discriminator.ignore_explicit_for_joined=true) -->
properties.put( "hibernate.discriminator.ignore_explicit_for_joined", "true" );
//properties.put("hibernate.hbm2ddl.import_files", "insert-data.sql");
//properties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
// This wasnt required in persistence.xml, but for some reason is here.
properties.put( "hibernate.connection.provider_class", C3P0ConnectionProvider.class.getName() );
// just adding c3p0 props was enough in persistence.xml, but not here.
properties.put( "hibernate.c3p0.min_size", "5" );
properties.put( "hibernate.c3p0.max_size", "20" );
properties.put( "hibernate.c3p0.timeout", "300" ); // 5mins
properties.put( "hibernate.c3p0.max_statements", "50" );
properties.put( "hibernate.c3p0.idle_test_period", "100" );
properties.put( "hibernate.c3p0.preferredTestQuery", "select 1" );
properties.put( "hibernate.c3p0.testConnectionOnCheckout", "true" );
properties.put( "hibernate.c3p0.numHelperThreads", "12" );
properties.put( "hibernate.c3p0.maxStatementsPerConnection", "25" );
properties.put( "hibernate.c3p0.statementCacheNumDeferredCloseThreads", "1" );
return l_properties;
}
@Bean
public JpaTransactionManager transactionManager( EntityManagerFactory emf ) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory( emf );
return jpaTransactionManager;
}
}
1楼
你的数据库是什么? 它是云数据库吗? 我对CloudSQL也有类似的问题。 发生的事情是,当某些活动的连接对数据库没有任何作用时,从数据库侧来看,它在几个小时后拒绝了连接。 但是在应用程序方面,您将其视为活动连接。 我使用了Apache DBCP池,并且能够在数据库配置中使用它来解决问题。
dataSource.setValidationQuery("SELECT 1");
dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(true);
因为您正在使用C3P0 ,所以以下命令应该对您有用 。
hibernate.dbcp.testOnBorrow=true
hibernate.dbcp.testOnReturn=true
hibernate.dbcp.validationQuery=SELECT 1