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

时间:2019-02-14 09:42:50

标签: java tomcat7

我已经在Tomcat 7上的新服务器上部署了成熟的Web应用程序。由于我们正处于配置使用的早期阶段,因此系统使用的数据库非常空。

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

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

  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;
  }
}

1 个答案:

答案 0 :(得分:0)

您的数据库是什么?它是云数据库吗?我对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