Java tomcat应用程序挂起,停止后无法重启tomcat

问题描述 投票:0回答:1

我已将成熟的Web应用程序部署到tomcat 7上的新服务器上。系统使用的数据库非常空,因为我们处于配置使用的早期阶段。

转到应用程序,您将获得一个登录页面。登录后,它通常会将您带到应用程序的主页面。

但是在第二天早上来之后,我们总是遇到同样的问题:

  1. 我们打开登录界面 - 没问题
  2. 输入您的用户名和密码 - 系统挂起
  3. 我们去tomcat并使用系统托盘停止服务。
  4. 停止服务进度条出现然后消失,但tomcat属性对话框上的状态仍显示“已启动”,并且“开始”和“停止”按钮均被禁用。
  5. 我们检查tomcat日志,没有错误
  6. 我们重新启动服务器,它再次正常工作

我们看不到任何明显的东西。 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;
  }
}
java tomcat7
1个回答
0
投票

你的数据库是什么?它是云数据库吗?我遇到了与CloudSQL类似的问题。发生了什么事情是当一些活动连接没有对数据库做任何事情时,从数据库端,它在几个小时后拒绝连接。但在应用程序方面,您会将其视为活动连接。我已经使用了apache dbcp pool,我能够在数据库配置中使用它解决问题。

    dataSource.setValidationQuery("SELECT 1");
    dataSource.setTestOnBorrow(true);
    dataSource.setTestWhileIdle(true);

因为您使用的是C3P0,所以以下命令应该适合您。

    hibernate.dbcp.testOnBorrow=true
    hibernate.dbcp.testOnReturn=true
    hibernate.dbcp.validationQuery=SELECT 1
© www.soinside.com 2019 - 2024. All rights reserved.