Play Framework: пул подключений к БД отключен

У меня есть приложение Play, работающее с использованием пула соединений HikariCP jdbc.

Приложение какое-то время будет работать нормально, но через короткий промежуток времени оно отключит пул соединений, что означает, что я больше не могу использовать приложение.

Сборка СБТ:

name := "virtual-betting"
version := "1.0"

lazy val root = (project in file(".")).enablePlugins(PlayJava).settings(javacOptions ++= Seq("-source", "1.8", "-target", "1.8"))

scalaVersion := "2.11.8"

libraryDependencies ++= Seq(
  javaJdbc,
  javaJpa,
  javaJpa.exclude("org.hibernate.javax.persistence", "hibernate-jpa-2.1-api"),
  "org.hibernate" % "hibernate-core" % "5.1.0.Final",
  "org.hibernate" % "hibernate-entitymanager" % "5.1.0.Final",
  "com.typesafe.play" % "play-java-jpa_2.11" % "2.5.3",
  "org.springframework" % "spring-context" % "4.2.4.RELEASE",
  "org.webjars" % "bootstrap" % "3.3.4",
  "mysql" % "mysql-connector-java" % "5.1.42",
  cache,
  javaWs,
  "org.jsoup" % "jsoup" % "1.7.2",
  "org.apache.commons" % "commons-email" % "1.4",
  "org.apache.cxf" % "cxf-rt-rs-client" % "3.1.6",
  "com.google.code.gson" % "gson" % "2.7",
  "com.squareup.okhttp3" % "okhttp" % "3.4.1"
)

fork in run := false


fork in run := true

приложение.conf:

## Database Connection Pool
# https://www.playframework.com/documentation/latest/SettingsJDBC
# ~~~~~
# Play doesn't require a JDBC database to run, but you can easily enable one.
#
# libraryDependencies += jdbc
#

play.db {
  # The combination of these two settings results in "db.default" as the
  # default JDBC pool:
  config = "db"
  default = "default"

  pool = "hikaricp"
  # Play uses HikariCP as the default connection pool.  You can override
  # settings by changing the prototype:

  prototype {
    pool = "hikaricp"

    # Sets a fixed JDBC connection pool size of 50
    hikaricp.minimumIdle = 0
    hikaricp.maximumPoolSize = 30
    hikaricp.connectionTimeout = 30000
    hikaricp.idleTimeout = 600000
    hikaricp.maxLifetime = 1800000
    hikaricp.leakDetectionThreshold = 5000
    hikaricp.connectionTestQuery = "SELECT 1"
    hikaricp.readOnly = false

    hikari.dataSourceClassName = com.mysql.jdbc.jdbc2.optional.MysqlDataSource
    # The database url
    #url = "jdbc:mysql://localhost:3306/madduxsp_sportsbook?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8"

    registerMBeans = true
    poolName = "sportsbook_pool"
  }
}

db.default.driver=com.mysql.jdbc.Driver
db.default.url="jdbc:mysql://localhost:3306/madduxsp_sportsbook_new?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8"
db.default.username=root
db.default.password=mypassword
db.default.jndiName=DefaultDS
jpa.default=defaultPersistenceUnit

application.secret="mysecret"

spring.context.location=application-context.xml

Я написал некоторый код, чтобы поддерживать соединение с БД, которое работает, если оно отключено из-за бездействия.

Мой DbKeepAliveService:

package services;

import play.Logger;
import play.db.jpa.JPAApi;
import util.DbKeepAliveTask;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Timer;

@Singleton
public class DbKeepAliveService {

    private final JPAApi jpa;

    @Inject
    public DbKeepAliveService(JPAApi jpa) {
        this.jpa = jpa;

        DbKeepAliveTask dbKeepAliveTask = new DbKeepAliveTask(jpa);
        Logger.info("Application has started");

        Timer timer = new Timer("dbKeepAlive", true);
        timer.schedule(dbKeepAliveTask, 0, 1200000);
    }
}

Вот только это не работает. Вот мои журналы:

2017-05-06 06:12:15,010 [INFO] from application in dbKeepAlive - Keeping database connection alive...
2017-05-06 06:32:15,010 [INFO] from application in dbKeepAlive - Keeping database connection alive...
2017-05-06 06:52:15,010 [INFO] from application in dbKeepAlive - Keeping database connection alive...
2017-05-06 06:58:35,894 [INFO] from application in Thread-8 - Application shutdown...
2017-05-06 06:58:36,054 [INFO] from application in Thread-8 - Shutting down connection pool.

У кого-нибудь есть идеи, почему он может продолжать отключать пул соединений БД? Я подозреваю, что это как-то связано с моей конфигурацией, но я, честно говоря, не уверен.

Я также читал, что это может быть связано с тем, что приложение не запущено в производственном режиме, но я установил секрет.

Я запускаю приложение с помощью следующей команды:

./bin/virtual-betting -Dconfig.file=conf/production.conf -Dplay.crypto.secret="mysecret" &

Со свойством play.crypto.secret, установленным в файле conf/production.conf.

Смотрел на это в течение долгого времени, поэтому помощь очень ценится!


person James Murphy    schedule 06.05.2017    source источник


Ответы (3)


Рассмотрение предоставленных вами журналов показывает, что причиной отключения пула соединений с базой данных является завершение работы приложения (воспроизведения).

Как указано в документации Play:

Когда вы запускаете команду запуска, Play разветвляет новую JVM и запускает HTTP-сервер Netty по умолчанию. Стандартный поток вывода перенаправляется на консоль Play, поэтому вы можете следить за его состоянием.

Идентификатор процесса сервера отображается при загрузке и записывается в файл RUNNING_PID. Чтобы убить работающий сервер Play, достаточно отправить SIGTERM в процесс, чтобы правильно завершить работу приложения.

Если вы нажмете Ctrl+D, консоль Play закроется, но созданный серверный процесс продолжит работу в фоновом режиме. Затем стандартный поток вывода разветвленной JVM закрывается, и журнал можно прочитать из файла logs/application.log.

Следовательно, возможно, либо процесс получил сигнал SIGTERM, либо приложение Play было запущено с play run вместо play start, и кто-то использовал Ctrl+D на консоли, либо, возможно, сервер убивает процесс, потому что он недостаточно памяти, что может указывать или не указывать на утечку памяти в вашем приложении, см. OOM killer:

В условиях отчаянно нехватки памяти срабатывает убийца нехватки памяти (OOM) и выбирает процесс для уничтожения, используя набор эвристик, который развивался с течением времени.

Я бы порекомендовал вам профилировать ваше приложение/контролировать память сервера (вы также можете проверить журналы сервера, чтобы увидеть, не убило ли ядро ​​какие-либо процессы).

Если вы можете расширить HikariCPModule и переопределить метод закрытия, вы можете добавить вызов регистратора для печати трассировки стека текущего потока и проверки того, что на самом деле вызывает закрытие, например. хук выключения или что-то еще.

  override def close(dataSource: DataSource) = {
    Logger.info("Shutting down connection pool.")
    Thread.dumpStack()
    dataSource match {
      case ds: HikariDataSource => ds.close()
      case _ => sys.error("Unable to close data source: not a HikariDataSource")
    }

Включение ведения журнала DEBUG для com.zaxxer.hikari и Play может помочь в дальнейшей диагностике того, что не так с настройкой базы данных/приложением.

Удачи!

person Johno Crawford    schedule 19.05.2017

Итак, я узнал некоторое время назад, в чем была моя проблема. Это было действительно очень глупо.

Я не запускал свое приложение с помощью команды nohup. Как следствие, приложение было завершено ОС с сообщением SIGTERM.

Это вообще не имело никакого отношения к моему приложению...

В общем, nohup с передачей вывода в dev > null

Живи и учись.

person James Murphy    schedule 17.07.2017

Вы можете удалить конфигурацию MinimumIdle, так как HikariCP рекомендует установить значение по умолчанию.

hikaricp.minimumIdle = 0

справочник по HikariCP

Также проверьте, достаточно ли хорош размер вашего пула соединений, равный 30.

person jayant    schedule 06.05.2017
comment
Спасибо @jayant, я должен сказать, что до сих пор это был очень разочаровывающий процесс. Оба посмотрю - спасибо! - person James Murphy; 06.05.2017
comment
К сожалению, @jayant не в этом проблема. Я пришел посмотреть на это сегодня утром, и это снова не удалось. - person James Murphy; 07.05.2017