在 Tomcat 中使用 Spring 通过 JMX 公开 Hibernate(缓存)统计信息

发布于 2024-11-05 11:52:18 字数 1356 浏览 1 评论 0原文

收集 Hibernate/Ehcache 统计信息并在基于 Spring 的设置中通过 JMX 公开它们似乎很容易。互联网上有很多资源可以提供帮助,例如 http://snippets.dzone.com/posts/show/ 11159

然而,所有这些文章都假设人们正在使用某种 Hibernate 会话工厂。我不是 - 我的实体是 JPA 注释的,并且我使用 javax.persistence.EntityManager。 如果我部署到 Java EE 容器,我可能能够通过 JNDI 获取 Hibernate 会话工厂,如下所述 http://internna.blogspot.com/2007/08/hibernate-statistics-in-enterprise-5.html 但我在 Tomcat 上...

该怎么办?我还没有想出解决办法。

如果我有对 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>

由于缓存管理器是由 Hibernate 创建的(即它不是 Spring bean),因此它不会工作。我尝试替换该引用,

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

希望我能以某种方式捕获正确的实例。也不起作用,因为这实际上会创建一个新的缓存管理器实例。

Collecting Hibernate/Ehcache statistics and exposing them through JMX in Spring-based setups seems easy. The Internet has lots of resources that help e.g. http://snippets.dzone.com/posts/show/11159

However, all those articles assume one is working with a Hibernate session factory of some sort. I'm not - my entities are JPA annotated and I use a 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...

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:

<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 (i.e. it's not a Spring bean) it won't work. 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

人│生佛魔见 2024-11-12 11:52:18

我最近构建了一个基于 Spring 的 Web 应用程序示例,它非常干净地为最新版本的 Spring、Hibernate 和 Ehcache 启用了 JMX。

它具有基于 EntityManager 的访问和 DAO 访问(包括事务!)的示例。它还展示了如何进行基于注释的注入,以避免必须使用 Spring 的 bean xml 配置。甚至还有一个使用注释的基于 SpringMVC 的示例 servlet。基本上,这是一个基于 Spring 的版本,是一个相当强大的应用程序服务器,运行在任何 servlet 引擎之上。

它还没有记录下来,但我很快就会记录下来。看一下配置文件和源码应该就很清楚了。

这背后的动机是,我厌倦了所有疯狂的博客文章以及 50 种不同的设置方式,并最终制作了一个人们可以使用的简单来源。它位于 github 上,因此您可以随意分叉该项目并用它做任何您想做的事情。

https://github.com/lookfirst/fallback

I've recently built a sample Spring based webapp that very cleanly enables JMX for latest versions of Spring, Hibernate and Ehcache.

It has examples for both EntityManager based access and DAO access (including transactions!). It also shows how to do annotation based injection in order to negate having to use Spring's xml config for beans. There is even a SpringMVC based example servlet using annotations. Basically, this is a Spring based version of a fairly powerful application server running on top of any servlet engine.

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. It is up on github so feel free to fork the project and do whatever you want with it.

https://github.com/lookfirst/fallback

琴流音 2024-11-12 11:52:18

JPA EntityManager 公开了底层 Hibernate 会话,因此您可以获取其工厂:

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

然后您可以使用此会话来 启用统计,就像我们在RHQ 项目中所做的那样

The JPA EntityManager exposes the underlying Hibernate session, so you can get at its 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

伊面 2024-11-12 11:52:18

您实际上可以使用 公开任何 CacheManager Spring 表达式语言

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

其中 CACHE_NAME 是 ehcache.xml 文件中配置的缓存的名称。

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

You can actually expose any CacheManager using Spring Expression Language.

<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.

<ehcache name="CACHE_NAME">
    ...
</ehcache>
暖风昔人 2024-11-12 11:52:18

我最终编写了以下类

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 配置

<!-- 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" />

I ended up writing the following class

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" />
心舞飞扬 2024-11-12 11:52:18

非常糟糕,但这也有效,其中(猜猜是什么)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>

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:

<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>
蘑菇王子 2024-11-12 11:52:18

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

Spring exposes on JMX only his beans but you can programaticaly export a resource via the MBeanExporter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文