简体   繁体   English

在Tomcat中通过带有Spring的JMX公开Hibernate(缓存)统计信息

[英]Exposing Hibernate (cache) statistics through JMX with Spring in Tomcat

Collecting Hibernate/Ehcache statistics and exposing them through JMX in Spring-based setups seems easy. 在基于Spring的设置中收集Hibernate / Ehcache统计信息并通过JMX公开它们似乎很容易。 The Internet has lots of resources that help eg http://snippets.dzone.com/posts/show/11159 互联网上有很多资源可以提供帮助,例如http://snippets.dzone.com/posts/show/11159

However, all those articles assume one is working with a Hibernate session factory of some sort. 但是,所有这些文章都假设有人正在使用某种Hibernate会话工厂。 I'm not - my entities are JPA annotated and I use a javax.persistence.EntityManager . 我不是-我的实体带有JPA批注,并且使用javax.persistence.EntityManager If I were deploying to a Java EE container I might have been able to obtain a Hibernate session factory through JNDI as described here http://internna.blogspot.com/2007/08/hibernate-statistics-in-enterprise-5.html but I'm on Tomcat... 如果我要部署到Java EE容器,则可以通过JNDI获得Hibernate会话工厂,如此处所述http://internna.blogspot.com/2007/08/hibernate-statistics-in-enterprise-5.html但我在使用Tomcat ...

How to go about this? 怎么去这个? I haven't come up with a solution yet. 我还没有想出解决方案。

If I had a reference to the Ehcache CacheManager I could try something like: 如果我引用了Ehcache CacheManager ,则可以尝试以下操作:

<context:mbean-server />
<bean class="net.sf.ehcache.management.ManagementService" init-method="init">
  <constructor-arg ref="..myCacheManager.."/>
  <constructor-arg ref="mbeanServer"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
</bean>

Since the cache manager is created by Hibernate (ie it's not a Spring bean) it won't work. 由于缓存管理器是由Hibernate创建的(即它不是Spring Bean),因此将无法工作。 I tried replacing that ref with 我尝试用

<constructor-arg><bean id="cacheManager" class="net.sf.ehcache.CacheManager" factory-method="getInstance"/></constructor-arg>

hoping I'd somehow catch the right instance. 希望我能以某种方式抓住合适的实例。 Won't work either as this would in fact create a new cache manager instance. 也不起作用,因为这实际上会创建一个新的缓存管理器实例。

I've recently built a sample Spring based webapp that very cleanly enables JMX for latest versions of Spring, Hibernate and Ehcache. 我最近构建了一个基于Spring的示例Web应用程序,该应用程序非常干净地为JMX启用了Spring,Hibernate和Ehcache的最新版本。

It has examples for both EntityManager based access and DAO access (including transactions!). 它具有基于EntityManager和DAO访问(包括事务!)的示例。 It also shows how to do annotation based injection in order to negate having to use Spring's xml config for beans. 它还显示了如何进行基于注解的注入,以便不必为豆使用Spring的xml配置。 There is even a SpringMVC based example servlet using annotations. 甚至还有一个使用注释的基于SpringMVC的示例servlet。 Basically, this is a Spring based version of a fairly powerful application server running on top of any servlet engine. 基本上,这是运行在任何servlet引擎之上的功能强大的应用程序服务器的基于Spring的版本。

It isn't documented yet, but I'll get to that soon. 它尚未记录,但我会尽快解决。 Take a look at the configuration files and source code and it should be pretty clear. 看一下配置文件和源代码,它应该很清楚。

The motivation behind this is that I got tired of all of the crazy blog posts with 50 different ways to set things up and finally made a single simple source that people can work from. 其背后的动机是,我厌倦了用50种不同方式进行设置的所有疯狂博客文章,并最终使人们可以使用一个简单的来源。 It is up on github so feel free to fork the project and do whatever you want with it. 它位于github上,因此可以随意分叉该项目并对其进行任何操作。

https://github.com/lookfirst/fallback https://github.com/lookfirst/fallback

You can actually expose any CacheManager using Spring Expression Language . 实际上,您可以使用Spring Expression Language公开任何CacheManager。

<bean id="hibernateCacheManagement" class="net.sf.ehcache.management.ManagementService" init-method="init" >
    <constructor-arg value="#{T(net.sf.ehcache.CacheManager).getCacheManager('CACHE_NAME')}"/>
    <constructor-arg><ref bean="mbeanServer"/></constructor-arg>
    <constructor-arg value="true"/>
    <constructor-arg value="true"/>
    <constructor-arg value="true"/>
    <constructor-arg value="true"/>
</bean>

Where CACHE_NAME is the name of the cache configured in the ehcache.xml file. 其中CACHE_NAME是在ehcache.xml文件中配置的缓存的名称。

<ehcache name="CACHE_NAME">
    ...
</ehcache>

The JPA EntityManager exposes the underlying Hibernate session, so you can get at its Factory: JPA EntityManager公开了基础的Hibernate会话,因此您可以使用其Factory:

   public static Session getHibernateSession(EntityManager entityManager) {
        Session session;
        if (entityManager.getDelegate() instanceof EntityManagerImpl) {
            EntityManagerImpl entityManagerImpl = (EntityManagerImpl) entityManager.getDelegate();
            session = entityManagerImpl.getSession();
        } else {
            session = (Session) entityManager.getDelegate();
        }

        return session;
    }

You can then use this Session to enable the statistics like we have done it in RHQ project 然后,您可以像使用RHQ项目一样使用此Session 启用统计信息

I ended up writing the following class 我最终写了下面的课

HibernateStatisticsJmxRegistration HibernateStatisticsJmxRegistration

import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.persistence.EntityManagerFactory;

import org.hibernate.SessionFactory;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.jmx.StatisticsService;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Provides code to register Hibernate's statistics bean with a JMX MBean server. Assumes that both
 * the MBeanServer and the EntityManagerFactory are available as Spring-managed beans. Note that
 * while registering this class enables the collection of statistics even if that was previously
 * disabled.
 * <p>
 * May become obsolete once <a href="https://hibernate.onjira.com/browse/HHH-6034">HHH-6034</a> is
 * implemented. Even if not the confusing situation abround the meanwhile deprecated
 * {@link StatisticsService} should be clear then.
 */
@SuppressWarnings({"deprecation", "javadoc" })
public class HibernateStatisticsJmxRegistration {

  @Autowired
  private EntityManagerFactory entityManagerFactory;

  @Autowired
  private MBeanServer mbeanServer;

  private ObjectName objectName;

  private String jmxObjectName = "org.hibernate:name=HibernateStatistics";

  /**
   * Registers the statistics MBean that wraps a Hibernate session factory. The bean is registered
   * under the name provided by {@link HibernateStatisticsJmxRegistration#getJmxObjectName()}.
   *
   * @throws JMException if anything fails..
   * @see HibernateStatisticsJmxRegistration#unregister()
   */
  public void register() throws JMException {
    final SessionFactory sessionFactory = ((HibernateEntityManagerFactory) entityManagerFactory).getSessionFactory();

    objectName = new ObjectName(jmxObjectName);

    final StatisticsService statsMBean = new StatisticsService();
    statsMBean.setSessionFactory(sessionFactory);
    statsMBean.setStatisticsEnabled(true);
    mbeanServer.registerMBean(statsMBean, objectName);
  }

  /**
   * Unregisters the MBean that was registered.
   *
   * @throws JMException if the de-registration fails
   * @see HibernateStatisticsJmxRegistration#register()
   */
  public void unregister() throws JMException {
    mbeanServer.unregisterMBean(objectName);
  }

  /**
   * Override the default JMX object name. Obviously you need to call this method before
   * registration for it to have any effect. The string must comply to the rules described in
   * {@link ObjectName}. Suggested is {@code <domain>:name=<name>}.
   *
   * @param jmxObjectName the name to use during registration
   */
  public void setJmxObjectName(String jmxObjectName) {
    this.jmxObjectName = jmxObjectName;
  }
}

Spring configuration 弹簧配置

<!-- Setting up Ehcache manager for various caches (offer facade, images). -->
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
  <property name="configLocation" value="classpath:ehcache.xml" />
</bean>  
<ehcache:annotation-driven cache-manager="ehCacheManager" />

<!-- Exposing cache statistics through JMX. -->
<context:mbean-server />
<bean class="net.sf.ehcache.management.ManagementService" init-method="init">
  <constructor-arg ref="ehCacheManager"/>
  <constructor-arg ref="mbeanServer"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
  <constructor-arg value="true"/>
</bean>    
<bean class="HibernateStatisticsJmxRegistration"
      init-method="register" destroy-method="unregister" />

Very ouch, but this works, too, where (guess what) mbeanServer is the bean name of the MBean Server and entityManagerFactory is the bean name of the EntityManagerFactory: 非常有帮助,但这也行得通,其中(猜测什么)mbeanServer是MBean Server的bean名称,而entityManagerFactory是EntityManagerFactory的bean名称:

<bean id="managementService-ehcache-hibernate"
    class="net.sf.ehcache.management.ManagementService"
    init-method="init"
    destroy-method="dispose">

    <constructor-arg value="#{T(org.springframework.security.util.FieldUtils).getFieldValue(@entityManagerFactory.cache.unwrap(T(org.hibernate.cache.spi.RegionFactory)),'manager')}"/>
    <constructor-arg ref="mbeanServer"/>
    <constructor-arg index="2" value="true"/>
    <constructor-arg index="3" value="true"/>
    <constructor-arg index="4" value="true"/>
    <constructor-arg index="5" value="true"/>
</bean>

Spring仅在JMX上公开他的bean,但是您可以通过MBeanExporter以编程方式导出资源。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM