获益于异步日志服务-使用Hibernate和JMS开发异步日志服务(翻译)

获益于异步日志服务

使用HibernateJMS开发异步日志服务

摘要

在软件开发中,日志是一个相当重要的组件,特别是对于软件的健壮性而言。通常,日志服务是同步的,会带来额外的性能开销(特别是在紧急的情况下)。这样的日志服务不能有效地作为分布式日志服务。在一个分布的计算环境中,比如J2EE,客户端期望日志能够被并发的记录到中央数据库,J2EE架构不提倡将日志记录到文件或者打印到控制台。做为JMS的一个应用,你可以使用异步的日志服务来代替通常的同步日志服务. 这样客户端便能在不阻塞的情况下继续执行. 在可靠模式(guaranteed mode)下,日志数据被提交(且仅仅被提交一次)到目的地(destination),接下来,日志信息将被Hibernate轻松的持久。这篇文章就是介绍如何使用HibernateJMS来开发一个异步的日志服务。

正文

开发一个通用的日志服务通常会引发争论。现在,已经有相当多的日志框架可用,这些日志服务是由专职人员或者开源社区开发的,特别应该提到Log4jJ2SE1.4的日志。由于许多日志框架都是在JMSJava消息服务-Java Message Service)还没有问世前开发的,并没有使用异步日志。在JMS问世后,框架被设计,以便能使用异步日志。

在传统的同步日志模型里,在日志服务调用没有成功返回前,调用者是不能往下执行的,所有的调用者都被阻塞,直到记录被持久或被日志服务所接收。显然,那样会导致额外的开销,特别是当一个应用被设计来记录大量的日志信息。想像一个日志文件有大量的(通常是数百)日志语句组成,每一条日志信息都应该在下一个日志被处理前记录完成,这是一个相当耗时处理过程。在分布计算环境中,客户端是并发运行的,可选择的方法是使用传统的日志服务创建并发的客户端,虽然性能是个问题。分布式计算框架会部署到很多服务器上(物理上相似的或者不相似的许多地方),在这种情况下,记录日志到中心数据库是相当烦琐的,而且也几乎不可能

本文将引导你开发一个简单的日志服务。创建一些日志信息,通过网络发送到JMS提供者,并且持久到数据库。为这个目的,我们使用JMS来实现异步、使用Hibernate来持久数据。你可以以多种方式持久数据,比如标准的JDBCJava Database Connectivity)连接、EJBEnterprise JavaBean),或者存储过程。我推荐使用工具从传统的POJOplain-old Java objects)创建实体域。在这篇文章里,我使用Hibernate,来替代原先使用EJB构建的实体模型

在开发日志服务前,让我们来了解下一些重要概念,这些概念关系到这次的应用开发。

企业级消息(Enterprise JavaBean

J2EE关于分布计算环境还在继续完善,我们能够补充它的许多特性,比如JMS,以开发异步服务。JMSSUN微系统的一个规范,并且适用于在标准的应用程序间通讯。参考资源部分,下载这个规范,Third-party vendors 实现了Sun定义的这个规范。JMS被设计用来处理不同的消息类型(消息无需知道自己持有的信息)

远程过程调用(RPC)模型,比如RMI(远程方法调用-remote method invocation)、SOAP(简单对象访问协议-Simple Object Access Protocol)、ORB(对象请求代理-object request broker)等是中央接口的,意味着发布者期望一个返回值。在使用JMS环境中,发送者并不了解接收者。接收和发送消息的应用依赖于特定的通道,但是他们并不通讯,实际上,他们只是被委托去传输消息给JMS提供者,应用程序发送消息到目的地后,他们的工作也就完成了。

消息类别

JMS 提供两种消模型,点对点(porint-to-porint)和发布/订阅(publish/subscribe)。点对点是基于队列的消息模型,每一个客户端都发送消息到一个目的队列,这些消息存在一个栈里。消息也可以被持久化,以保证服务器当机时,数据仍然可用。JMS提供者投递到达堆栈的消息,以供给消费者。在点对点模型里,一个消息只能被投递给一个消费者。
在发布/订阅是广播模型,在该模型中,所有对某一特定主题感兴趣的客户端,把自己注册为一个监听。发布者发布它的消息到目的地(主题),JMS提供者负责分发消息给这些监听。在这个模式中,所有的监听都会消费每一个消息

消息驱动Bean

EJB2.0介绍了一种新的Bean组件类型:消息驱动BeanMDB)。该Bean不响应客户端的调用。事实上,没有提供任何接口使得客户端可以调用MDB。当收到特定的JMS消息时,只有容器才激活MDBMDB仅仅是消息消费者,MDB的执行是简洁的,所有的JMS管理信息都在部署中被描述,在onMessage函数里,接收和处理消息的行为按照部署执行。查看资源部分,获取更多MDBs信息

Hibernate

Hibernate是用于免除JDBC编码的开源产品。这是一个在java环境实现OR映射的工具,它在Java 对象和数据库实体间建立映射。在企业开发中,对象关系映射是一个关键需求,产生的或者修改的数据必须被持久。数据持久是开发人员的一个恶梦,尤其是需求的多边,而导致数据模型的修改。Hiberante可以减轻数据持久的复杂化。查看资源部分以了解Hibernate的更多信息,本文介绍开发的日志服务,将使用Hibernate作为OR持久的媒介。

异步日志服务

在上面的几节,我简单的介绍了开发这个日志服务所需的主要技术。现在让我们使用JMSHibernate来开发一个简单的框架。

概要的讲一下,异步日志服务是如下工作:客户端创建一个日志消息LogMessageValue Object),要求辅助类(JMSLogger)发布这个消息到队列。辅助类(JMSLoger)使用日志消息来创建一个JMSObject Message,然后提交到队列。一旦消息到达,容器就调用监听这个队列的MDB,访问它的onMessage回调方法。MDB读取消息(LogMessage),然后使用Hibernate持久消息到数据库

日志服务使用JMS的点对点消息方式。一旦一个日志消息被送出,客户端不需要担心日志消息被送到了哪里,或者它是否被发送。客户端信任JMS的消息机制,保证消息被投递。

下面的图例说明了日志服务的主要组成

功能框架:日志服务的主要组件

框架组件

在分析实现细节前,让我们来仔细看一下这些组件,日志程序是这个应用的入口点,作为非J2EE的客户端使用,这与J2EE客户端只有较小的差异。日志程序使用JMSLogger发送消息到队列,它创建一个日志消息(LogMessage),然后由JMSLogger发送到LogMessageQueue目的地

JMSLogger是一个JMS类,一旦被实例化,它就关联自己到消息目的地。因此,当JMSLogger被日志程序创建,它就通过JNIDIJava命名目录访问接口-Java Naming and Directory Interface)命名空间查找连接工厂(Connection Factory)和队列(Queue),然后开启和JMS提供者(JMS Provider)的会话(Session),接下来,JMSLogger就发送消息到目的地。应用客户端通过简单的实例化日志程序和调用它的方法来访问日志服务。在J2EE环境下,日志服务只在应用服务启动的时候被实例化一次,当应用服务启动时,使用Java管理扩展(JMXJava Management Extensions)来管理Beans,这样客户端便能通过JNDI查找到这个服务,然后象往常一样,调用它的方法来持久日志消息

LogMessageQueue是日志消息发送的目的地,一个MDB监听这个队列。一旦消息到达队列,EJB容器就调用MDB,并且委派它作更多的处理。MDB取出日志消息(LogMessage),通过Hiberante的持久机制,持久日志消息到数据库。

LogMessage是一个被持久的含有日志信息的Java对象。它被设计成用来持有日志信息,比如简洁描述、详细描述、日志时间和日志级别。

实现

现在,我将详细描述日志服务的实现,在资源部分,你能下载所有的实现代码,然后解压到本地目录。

创建日志表格

首先让我们来创建一个存储日志信息的表,在表中我们只需要保存日志信息的如下部分:简单描述、详细描述、日志级别、日志时间。Listing 1的脚本语句创建一个名为LOG_DATA的日志表,它有5个数据列。除了MESSAGE_ID,其它的都不需要加以说明了,使用在源源中提供的脚本创建这个表格

MESSAGE_ID是这个表格的主键,它和应用相关。在下面的“Hibernate映射文件”一节,我会详细讲述。

Listing 1: 创建日志表格的脚本

create table LOG_DATA
(
  MESSAGE_ID VARCHAR2(30) not null,
  SHORT_DESC VARCHAR2(20),
  LONG_DESC  VARCHAR2(200),
  LOG_DATE   DATE,
  LOG_LEVEL  VARCHAR2(10)
)

LogMessage

LogMessage是一个被持久到日志数据库的Value Object,日志程序用合适的日志信息创建这个对象,正如前面提到,我将用Hiberante持久这个LogMessage对象

根据前面表的描述创建一个java类,我创建了一个含有5个属性的Java类,它的每一个属性都对应日志表的一个数据列,同时还要为每个属性创建settersgetters方法,因为Hibernate需要这些方法。

Hibernate OR 映射文件

Hibernate需要映射文件,才可以持久化LogMessage。正如本节名字所写的,映射文件定义了对象-关系映射。在我们的需求里,它定义了LogMessage的属性和日志表的映射。这个XML映射文件的语法是简单易懂的。

Listing 2: Hibernate OR 映射文件

<hibernate-mapping>
<!-Provide the name of the class and a mapping table here à
   <class name="com.madhusudhan.articles.logger.LogMessage"
table="LOG_DATA">
      <!-id represents the primary key. In our case, MESSAGE_ID is the
primary key.
      The key is generated by the application  à
         <id name="messageId" unsaved-value="id_value">
         <column name="MESSAGE_ID" sql-type="VARCHAR2(30)"
not-null="true"/>
          <generator class="assigned"/>
      </id>
      <!- map the Attributes of the class with the table columns à
      <property name="shortDesc">
         <column name="SHORT_DESC" sql-type="VARCHAR2(20)"
not-null="false"/>
      </property>
      <property name="longDesc">
         <column name="LONG_DESC" sql-type="VARCHAR2(200)"
not-null="false"/>
      </property>
      <property name="logDate">
         <column name="LOG_DATE" sql-type="DATE"  not-null="false"/>
      </property>
      <property name="logLevel">
         <column name="LOG_LEVEL" sql-type="VARCHAR2(10)"
not-null="false"/>
      </property>
   </class>
</hibernate-mapping>

我简单的介绍一下这个文件:

文件的第一行(<class name="com.madhusudhan.articles.logger.LogMessage" table="LOG_DATA">)建立了LogMessage类和数据库表间的映射,提供了类的全名,包括包名。Id属性描述主键值。类的属性messageId通过一个generator标签映射到表的MESSAGE_ID列,generator这个类根据你的需求,产生表的主键。在我们的例子里,我设置让Hibernate来分配(assignid,当然,你也可以使用Hibernat提供的generator类来产生(generatorid。可以查看Hibernate的文档获取详细的信息。

记得修改这个文件名为LogMessage.hbm.xml

JMS日志Bean -JMSLoggerBean (MDB)

JMSLoggerBean监听消息到达的队列,持久消息到数据库。一旦消息到达,容器调用JMSLoggerBean。在onMessage( )方法里,MDB请求Hibernate持久消息对象。因为Hibernate简化的持久数据,我不需要传递消息给其它的组件,比如会话BeanSession Bean)做一步的处理。MDB能做自己所有的事物处理了。

onMessage() 方法看起来象是这样:

public void onMessage(javax.jms.Message message) {
   try {

   // Cast the JMS message to ObjectMessage type
   ObjectMessage objMsg = (ObjectMessage)message;

   // And extract the log message from it
   LogMessage logMsg = (LogMessage)objMsg.getObject();

   // Persist the message
   persistMessage(logMsg);

   }catch (Exception t) {
      t.printStackTrace();
   }
}

一旦消息到达队列,容器便调用MDBonMessage方法,在这个方法里,从JMSObject MessageObject Message是消息的一个类别)里得到LogMessage,并且调用如下的方法持久。

private void persistMessage(LogMessage message) throws
HibernateException {
   net.sf.hibernate.Session session = null;
   SessionFactory sessionFactory = null;
   Transaction tx = null;
   try {
      // Create a session factory from the configuration
(hibernate.properties
      // File should be present in the class path)
      sessionFactory = new Configuration(). addClass(LogMessage.class).
        buildSessionFactory();

      // Create a session
      session = sessionFactory.openSession();

      // Begin a transaction
      tx = session.beginTransaction ();
   }catch(..){
   .....
}
try{

   // Assign the message id
   message.setMessageId (""+System.currentTimeMillis ());

   // Save the object to the database and commit the transaction
   session.save(message);
   tx.commit ();
}catch(Exception ex){
....
}finally {
   // Close the session
   session.close();
}
}

在第一个trycatch块里, SessionFactory 对象被创建, 它开启了一个活动的会话到数据库,我从这个会话创建事务

   sessionFactory = new Configuration().
   addClass(LogMessage.class).
   buildSessionFactory();
   session = sessionFactory.openSession();
   tx = session.beginTransaction ();

在下一个trycatch, id被分配给LogMessage对象,然后LogMessage对象被持久(保存)到数据库,如下代码:

   message.setMessageId (""+System.currentTimeMillis ());
   // Save the object to the database.
   session.save(message);
   tx.commit ();

 

正如你所看到的,一旦你创建了对象-关系映射文件,持久日志信息到数据将变得非常容易,Hiberante在后台完成了所有的处理。

部署描述(Deployment descriptor

部署描述如下,Listing 3 是一个 ejb-jar.xml 文件, Listings 4 and 5JbossJrun的特定部署。

Listing 3: ejb-jar.xml 部署描述

   ....
   <message-driven>
      ....
      <ejb-name>JMSLoggerBean</ejb-name>
      
<ejb-class>com.madhusudhan.articles.logger.JMSLoggerBean</ejb-class>
      <transaction-type>Container</transaction-type>
      <acknowledge-mode>Auto-acknowledge</acknowledge-mode>

      <message-driven-destination>
         <destination-type>javax.jms.Queue</destination-type>
        
<subscription-durability>NonDurable</subscription-durability>
      </message-driven-destination>

      <resource-env-ref>
         <description>Asynchronous Log Queue</description>
        
<reso, urce-env-ref-name>AsynchronousLogQueue</resource-env-ref-name>
        
<resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
      </resource-env-ref>

   </message-driven>
   ....

Listing 4: JBoss 部署描述

   <message-driven>
      <ejb-name>JMSLoggerBean</ejb-name>
      <configuration-name>Standard Message Driven
Bean</configuration-name>

<destination-jndi-name>queue/AsynchronousLogQueue</destination-jndi-name>
    </message-driven>

Listing 5: JRun 部署描述

....
<message-driven>
      ....
   <resource-ref>
      <res-ref-name>ConnectionFactory</res-ref-name>
      <jndi-name>jms/ConnectionFactory</jndi-name>
   </resource-ref>

   <resource-env-ref>
      <resource-env-ref-name>AsynchronousLogQueue
</resource-env-ref-name>
      <jndi-name>jms/queue/AsynchronousLogQueue </jndi-name>
      <mdb-destination>jms/queue/AsynchronousLogQueue
</mdb-destination>
   </resource-env-ref>
</message-driven>

你可以从资源部分下载所有的这些部署描述

客户端代码

接下来开始编码开发客户端程序:JMSLoggerLogServiceLogger,这些类用于客户端交互

JMSLogger是一个发送消息的程序,LogService调用它的构造方法来实例化,构造函数需要连接工厂和目的地的名称作为参数

     Logger logger = new JMSLogger("ConnectionFactory",
                              "queue/CyrusLogQueue");

一旦JMSLogger被实例化,LogService只需要调用sendMessage方法,代码如下:

   private void sendMessage(LogMessage logMessage){
   JMSLogger logger = null;
   ....
   logger = new JMSLogger("ConnectionFactory",
     "queue/CyrusLogQueue");
   ...
   logger.sendMessage(logMessage);
      System.out.println("[ LogService::sendMessage: The message sent ]");
   ....
}

Logger是围绕LogService的一个封装好的类,被客户端调用。客户端创建如下创建Logger  Logger logger = new Logger(),然后调用它的方法。

部署Bean

在访问日志服务前,部署MDBJAR到应用服务器,创建队列和连接工厂。在JBoss里,你可以通过两种方式创建这些受JMS管理的对象,通过使用控制台或者写你自己的目的服务到XML文件(创建<some-name>-destinations-service.xml 文件,然后部署到如下目录 server/<servername>/deploy/jms/

如果是使用Jrun,通过编辑jrun-resources.xml或者使用控制台创建这些对象。

请在资源部分了解JbossJrun的部署过程

使用日志服务

使用日志服务的一个简单方法是在客户端初始化Logger,一旦被初始化,日志方法就可以被调用了,如下代码

   try{
      Logger logger = new Logger();
      logger.info("MSG000111",
        "CONFIG SEVICE is not initialised. Please check the settings.");
      logger.severe("ERR101010",
        "APPSERVER CRASHED. Notification to the Admin sent.")
   }catch(LogException lex){
   ....
}

一些Junit测试可以测试这个服务,解压资源里的LogServiceTestCase.java LogServiceTestSuite.java 测试文件。

总结

这篇文章介绍录入如何开发一个异步的日志服务。JMSJ2EE一个功能强大的特性。JMS的异步行为和Hiberante的对象-关系映射提供了一个可靠灵活的框架。很多的Java应用依赖的协议,比如RMISOAPCORBA,都可以通过使用JMS来扩展。在EJB2.0后,自JMS被整合到应用服务的框架,EJB可以利用消息模型。一个调用Session Bean的消息代理程序成功的被消息驱动Bean所替代。

异步消息服务原理可以被应用到许多的服务。比如属性服务、配置服务和其它的数据服务。通过使用JMX来管理Bean,这片文章介绍的异步日志服务能够被配置。当然,你也可以创建一个日志浏览器来浏览持久到数据库的日志信息。

资源:

Download the asynchronous log service:
http://www.javaworld.com/javaworld/jw-05-2004/logging/jw-0510-logging.zip

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: FileZilla服务器是一种免费的开源FTP服务器软件,它提供了强大的资源管理功能。首先,FileZilla服务器支持多种操作系统,包括Windows、Linux和macOS,使得用户可以在不同平台上运行服务器。这为用户提供了更大的灵活性和选择性。 FileZilla服务器提供了高效的资源管理功能,使得用户可以方便地管理和控制文件的上传和下载。它支持多个连接,意味着多个用户可以同时访问服务器,并进行文件传输。用户可以通过简单的拖放操作来上传和下载文件,大大简化了文件传输的过程。此外,FileZilla服务器还提供了断点续传功能,允许在传输过程断,并从断处继续,避免了传输失败后重新传输的麻烦。 FileZilla服务器还提供了一些安全功能,以保护用户的文件和数据。它支持SSL/TLS加密,可以通过加密传输来确保传输的安全性。用户可以设置访问控制,指定哪些用户可以访问服务器和执行哪些操作。这可以有效地保护文件和数据的机密性和完整性。 另外,FileZilla服务器还提供了一些高级功能,如文件的远程编辑和文件夹同步。用户可以直接在服务器上编辑文件,无需先将其下载到本地。此外,用户还可以设置文件夹同步,将服务器上的文件夹与本地文件夹进行同步,确保文件的一致性。 总之,FileZilla服务器是一个强大而又易于使用的工具,提供了丰富的资源管理功能,使用户能够轻松地进行文件传输和管理。无论是个人用户还是企业用户,都可以从FileZilla服务器的资源获益。 ### 回答2: FileZilla服务器是一个免费且开源的FTP服务器软件,它提供了高度可靠和安全的文件传输服务。它是一个跨平台的软件,可在Windows、Linux和Mac等操作系统上运行。 FileZilla服务器资源包括以下几个方面: 1. 文件传输功能:FileZilla服务器支持FTP、FTPS和SFTP协议,可以实现简单、高效地文件上传和下载。用户可以通过可视化的界面或命令行界面进行文件操作,同时还可以设置文件的权限和访问控制等。 2. 安全性:FileZilla服务使用TLS/SSL加密技术来保护文件传输的安全性。它支持客户端证书和服务器证书验证,有效地防止非法访问和数据泄露。 3. 管理功能:FileZilla服务器提供了一系列管理功能,方便管理员管理和监控服务器。管理员可以配置用户权限、限制访问IP范围、设置带宽限制等。此外,服务器还提供了日志记录功能,以便管理员分析和跟踪用户的操作。 4. 扩展性:FileZilla服务器支持插件化架构,可以根据需要添加和定制不同的插件。这使得服务器具有很好的扩展性,可以满足不同用户的特定需求。 总之,FileZilla服务器是一个功能强大且易于使用的文件传输服务器软件。它提供了安全可靠的文件传输功能,并具备灵活的管理和扩展性。无论是个人用户还是企业用户,都可以通过使用FileZilla服务器资源来满足其文件传输和管理的需求。 ### 回答3: FileZilla是一种免费的开源FTP(文件传输协议)服务器软件。作为一个FTP服务器资源,FileZilla提供了许多有用的功能和优点。 首先,FileZilla服务器资源具有强大的性能和稳定性。它可以同时处理多个用户和文件传输请求,提供高效的文件传输速度和可靠的连接。这对于需要频繁进行文件传输的组织或个人来说,非常重要。 其次,FileZilla服务器资源易于配置和管理。它提供了直观的用户界面,使用户可以轻松地创建和管理不同的FTP用户账户,设置访问权限和文件夹。此外,它还支持加密传输,以确保数据传输的安全性。 另外,FileZilla服务器资源具有高度的可扩展性。它允许用户通过插件来增加额外的功能和扩展服务器的性能。用户可以根据实际需求选择安装和使用不同的插件,以满足其特定的FTP服务器需求。 此外,FileZilla服务器资源还提供了一些额外的功能。例如,它可以用作网站服务器,支持多个网站的同时访问。它还提供了日志记录功能,以跟踪用户活动和传输记录。此外,它还支持断点续传,允许用户在文件传输断后恢复,而不必重新开始。 总的来说,FileZilla服务器资源是一种功能丰富、易于配置和管理、高性能和可靠的FTP服务器软件。无论是个人用户还是组织机构,都可以从受益,并实现高效、安全的文件传输。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值