Hibernate (с поддержкой Spring)
Предоставить статистику Hibernate с помощью JMX довольно просто , однако при использовании JPA API для получения базового SessionFactory
требуются некоторые неприятные обходные пути.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
class JmxLocalContainerEntityManagerFactoryBean() extends LocalContainerEntityManagerFactoryBean { override def createNativeEntityManagerFactory() = { val managerFactory = super .createNativeEntityManagerFactory() registerStatisticsMBean(managerFactory) managerFactory } def registerStatisticsMBean(managerFactory : EntityManagerFactory) { managerFactory match { case impl : EntityManagerFactoryImpl = > val mBean = new StatisticsService(); mBean.setStatisticsEnabled( true ) mBean.setSessionFactory(impl.getSessionFactory); val name = new ObjectName( "org.hibernate:type=Statistics,application=spring-pitfalls" ) ManagementFactory.getPlatformMBeanServer.registerMBean(mBean, name); case _ = > } } } |
Обратите внимание, что я создал подкласс встроенного в Springs LocalContainerEntityManagerFactoryBean
. Переопределив createNativeEntityManagerFactory()
я могу получить доступ к EntityManagerFactory
и попытаться org.hibernate.ejb.EntityManagerFactoryImpl
его до org.hibernate.ejb.EntityManagerFactoryImpl
мы смогли зарегистрировать Hibernate Mbean.
Еще одна вещь ушла. Очевидно, что мы должны использовать наш собственный подкласс вместо org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
. Кроме того, чтобы собирать фактическую статистику вместо того, чтобы просто видеть нули до конца, мы должны установить флаг hibernate.generate_statistics
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@ Bean def entityManagerFactoryBean() = { val entityManagerFactoryBean = new JmxLocalContainerEntityManagerFactoryBean() entityManagerFactoryBean.setDataSource(dataSource()) entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter()) entityManagerFactoryBean.setPackagesToScan( "com.blogspot.nurkiewicz" ) entityManagerFactoryBean.setJpaPropertyMap( Map( "hibernate.hbm2ddl.auto" -> "create" , "hibernate.format_sql" -> "true" , "hibernate.ejb.naming_strategy" -> classOf[ImprovedNamingStrategy].getName, "hibernate.generate_statistics" -> true .toString ).asJava ) entityManagerFactoryBean } |
Вот пример того, что мы можем увидеть в JvisualVM (не забудьте установить все плагины!):
Кроме того, мы получаем хорошую запись в Hibernate:
1
|
HQL: select generatedAlias0 from Book as generatedAlias0, time: 10ms, rows: 20 |
Ehcache
Мониторинг кэшей очень важен, особенно в приложениях, где вы ожидаете, что значения обычно присутствуют там. Я склонен запрашивать базу данных так часто, как это необходимо, чтобы избежать ненужных аргументов метода или локального кэширования . Все, чтобы сделать код максимально простым. Однако этот подход работает только тогда, когда кэширование на уровне базы данных работает правильно. Как и в Hibernate, включение мониторинга JMX в EhCache является двухэтапным процессом. Сначала вам нужно выставить предоставленный MBean
в MBeanServer
:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@ Bean(initMethod = "init" , destroyMethod = "dispose" ) def managementService = new ManagementService(ehCacheManager(), platformMBeanServer(), true , true , true , true , true ) @ Bean def platformMBeanServer() = ManagementFactory.getPlatformMBeanServer def ehCacheManager() = ehCacheManagerFactoryBean.getObject @ Bean def ehCacheManagerFactoryBean = { val ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean ehCacheManagerFactoryBean.setShared( true ) ehCacheManagerFactoryBean.setCacheManagerName( "spring-pitfalls" ) ehCacheManagerFactoryBean } |
Обратите внимание, что я явно установил имя CacheManager
. Это не обязательно, но это имя используется как часть имени Mbean, а имя по умолчанию содержит значение hashCode
, что не очень приятно. Последний штрих — включить статистику на основе кеша:
1
2
3
4
5
6
7
8
9
|
< cache name = "org.hibernate.cache.StandardQueryCache" maxElementsInMemory = "10000" eternal = "false" timeToIdleSeconds = "3600" timeToLiveSeconds = "600" overflowToDisk = "false" memoryStoreEvictionPolicy = "LRU" statistics = "true" /> |
Теперь мы можем с радостью отслеживать различные характеристики кэширования каждого кэша в отдельности:
Как мы видим, процент пропусков кэша увеличивается. Никогда хорошая вещь. Если мы не включаем статистику кэширования, включение JMX все еще является хорошей идеей, поскольку мы бесплатно получаем множество операций управления, включая очистку и очистку кэшей (полезно при отладке и тестировании).
Кварцевый планировщик
По моему скромному мнению, планировщик Quartz — очень недооцененная библиотека, но я напишу статью об этом самостоятельно. На этот раз мы только научимся контролировать его через JMX. К счастью, это так же просто, как добавить:
1
|
org.quartz.scheduler.jmx.export= true |
В файл quartz.properties
. Поддержка JMX в Quartz могла бы быть немного шире, но все же можно запросить, например, какие задания выполняются в данный момент. Кстати, новая основная версия Quartz (2.x) предлагает очень хорошую DSL-подобную поддержку планирования:
1
2
3
4
5
6
7
8
9
|
val job = newJob(classOf[MyJob]) val trigger = newTrigger(). withSchedule( repeatSecondlyForever() ). startAt( futureDate( 30 , SECOND) ) scheduler.scheduleJob(job.build(), trigger.build()) |
Apache Commons DBCP
Apache Commons DBCP — самая разумная библиотека пула JDBC, с которой я когда-либо сталкивался. Существует также c3p0 , но, похоже, он уже не активно развивается. Tomcat JDBC Connection Pool выглядел многообещающе, но, поскольку он входит в состав Tomcat, ваши драйверы JDBC больше не могут быть упакованы в WAR.
Единственная проблема с DBCP заключается в том, что он не поддерживает JMX. На всех (см. Этот двухлетний выпуск). К счастью, это легко обойти. Кроме того, мы узнаем, как использовать встроенную поддержку Spring JMX.
Похоже, что стандартный BasicDataSource
имеет все, что нам нужно, все, что нам нужно сделать, это представить существующие метрики через JMX. В Spring это очень просто — просто создайте подкласс BasicDataSource и добавьте аннотацию @ManagedAttribute
к нужным атрибутам:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
@ ManagedResource class ManagedBasicDataSource extends BasicDataSource { @ ManagedAttribute override def getNumActive = super .getNumActive @ ManagedAttribute override def getNumIdle = super .getNumIdle @ ManagedAttribute def getNumOpen = getNumActive + getNumIdle @ ManagedAttribute override def getMaxActive : Int = super .getMaxActive @ ManagedAttribute override def setMaxActive(maxActive : Int) { super .setMaxActive(maxActive) } @ ManagedAttribute override def getMaxIdle = super .getMaxIdle @ ManagedAttribute override def setMaxIdle(maxIdle : Int) { super .setMaxIdle(maxIdle) } @ ManagedAttribute override def getMinIdle = super .getMinIdle @ ManagedAttribute override def setMinIdle(minIdle : Int) { super .setMinIdle(minIdle) } @ ManagedAttribute override def getMaxWait = super .getMaxWait @ ManagedAttribute override def setMaxWait(maxWait : Long) { super .setMaxWait(maxWait) } @ ManagedAttribute override def getUrl = super .getUrl @ ManagedAttribute override def getUsername = super .getUsername } |
Вот несколько метрик источника данных, сходящих с ума во время нагрузочного теста:
Поддержка JMX в самой среде Spring довольно проста. Как вы видели выше, предоставление произвольного атрибута или операции — это просто добавление аннотации. Вам нужно только помнить о включении поддержки JMX с использованием XML или Java (также см .: SPR-8943: аннотация, эквивалентная <context: mbean-export /> с @Configuration ):
1
|
< context:mbean-export /> |
или же:
1
|
@ Bean def annotationMBeanExporter() = new AnnotationMBeanExporter() |
Эта статья не была особенно захватывающей. Однако знание метрик JMX позволит нам в кратчайшие сроки написать простые, но модные информационные панели. Будьте на связи!
Ссылка: Включение JMX в Hibernate, EhCache, Quartz, DBPC и Spring от нашего партнера по JCG Томаша Нуркевича в блоге Java и соседей .
Статьи по Теме :