2009年8月28日
1.使用DNS轮询.
2.使用Apache R-proxy方式。
3.使用Apache mod_jk方式.
DNS轮询的缺点是,当集群中某台服务器停止之后,用户由于dns缓存的缘故,便无法访问服务,
必须等到dns解析更新,或者这台服务器重新启动。
还有就是必须把集群中的所有服务端口暴露给外界,没有用apache做前置代理的方式安全,
并且占用大量公网IP地址,而且tomcat还要负责处理静态网页资源,影响效率。
优点是集群配置最简单,dns设置也非常简单。
R-proxy的缺点是,当其中一台tomcat停止运行的时候,apache仍然会转发请求过去,导致502网关错误。
但是只要服务器再启动就不存在这个问题。
mod_jk方式的优点是,Apache 会自动检测到停止掉的tomcat,然后不再发请求过去。
缺点就是,当停止掉的tomcat服务器再次启动的时候,Apache检测不到,仍然不会转发请求过去。
R-proxy和mod_jk的共同优点是.可以只将Apache置于公网,节省公网IP地址资源。
可以通过设置来实现Apache专门负责处理静态网页,让Tomcat专门负责处理jsp和servlet等动态请求。
共同缺点是:如果前置Apache代理服务器停止运行,所有集群服务将无法对外提供。
R-proxy和mod_jk对静态页面请求的处理,都可以通设置来选取一个尽可能优化的效果。
这三种方式对实现最佳负载均衡都有一定不足,mod_jk相对好些,可以通过设置lbfactor参数来分配请求任务,但又因为mod_jk2方式不被推荐,mod_jk2已经不再被更新了。郁闷中……
哈哈,发现apache2.2以后与tomcat做负载均衡不需要用mod_jk2,在配置文件中稍做修改就OK
posted @
2009-08-28 15:08 kit_lo 阅读(2707) |
评论 (5) |
编辑 收藏
Java EE应用的性能问题对严肃的项目和产品来说是一个非常重要的问题。特别是企业级的应用,并发用户多,数据传输量大,业务逻辑复杂,占用系统资源多,因此性能问题在企业级应用变得至关重要,它和系统的稳定性有着直接的联系。更加重要的是,性能好的应用在完成相同任务的条件下,能够占用更少的资源,获得更好的用户体验,换句话说,就是能够节省费用和消耗,获得更高的利润。
要获得更好的性能,就需要对原来的系统进行性能调优。对运行在Glassfish上的JavaEE应用,调优是一件相对复杂的事情。在调优以前必须要认识到:对JavaEE的系统,调优是多层次的。一个JavaEE的应用其实是整个系统中很少的一部分。开发人员所开发的JavaEE程序,无论是JSP还是 EJB,都是运行在JavaEE应用服务器(Glassfish)之上。而应用服务器本身也是Java语言编写的,需要运行在Java虚拟机之上。 Java虚拟机也只不过是操作系统的一个应用而已,和其他的应用(如Apache)对于操作系统来说没有本质的区别。而操作系统却运行在一定的硬件环境中,包括CPU,内存,网卡和硬盘等等。在这么多的层次中,每一个层次的因素都会影响整个系统的性能。因此,对一个系统的调优,事实上需要同时对每个层次都要调优。JavaEE应用性能调优不仅仅和Glassfish有关,Java语言有关,还要和操作系统以及硬件都有关系,需要调优者有综合的知识和技能。这些不同层面的方法需要综合纵效,结合在一起灵活使用,才能快速有效的定位性能瓶颈。下面是一些具体的案例分析:
内存泄漏问题
某个JavaEE应用运行在8颗CPU的服务器上。上线运行发现性能不稳定。性能随着时间的增加而越来越慢。通过操作系统的工具(mpstat),发现在系统很慢的时候,只有一颗CPU很忙,其他的CPU都很空闲。因此怀疑是Java虚拟机经常进行内存回收,因为虚拟机在内存回收的时候,有的回收算法通常只能运行在一个CPU上。通过Java虚拟机的工具“jstat”可以清楚的看到,Java虚拟机进行内存回收的频率非常高,几乎每5秒中就有一次,每次回收的时间为2秒钟。另外,通过“jstat”的输出还发现每次回收释放的内存非常有限,大多数对象都无法回收。这种现象很大程度上暗示着内存泄漏。使用 Java虚拟机的工具“jmap”来获得当前的一个内存映象。发现有很多(超过10000)个的session对象。这是不正常的一个现象。一般来说, session对应于一个用户的多次访问,当用户退出的时候,session就应该失效,对象应该被回收。当我们和这个系统的开发工程师了解有关 session的设置,发现当他们部署应用的时候,竟然将session的timeout时间设置为50分钟,并且没有提供logout的接口。这样的设置下,每个session的数据都会保存50分钟才会被回收。根据我们的建议,系统提供了logout的链接,并且告诉用户如果退出应用,应该点击这个 logout的链接;并且将session的timeout时间修改为5分钟。通过几天的测试,证明泄漏的问题得到解决。
数据库连接池问题
某财务应用运行在JavaEE服务器上,后台连接Oracle数据库。并发用户数量超过100人左右的时候系统停止响应。通过操作系统层面的进程监控工具发现进程并没有被杀死或挂起,而CPU使用率几乎为零。那么是什么原因导致系统停止响应用户请求呢?我们利用Java虚拟机的工具(kill -3 pid)将当前的所有线程状态DUMP出来,发现JavaEE服务器的大部分处理线程都在等待数据库连接池的连接,而那些已经获得数据库连接的线程却处于阻塞状态。数据库管理员应要求检查了数据库的状态,发现所有的连接的session都处于死锁状态。显然,这是因为数据库端出现了死锁的操作,阻塞了那些有数据库操作的请求,占用了所有数据库连接池中的连接。后续的请求如果还要从连接池中获取连接,就会阻塞在连接池上。当解决数据库死锁的问题之后,性能问题迎刃而解。
大对象缓存问题
电信应用运行在64位Java虚拟机上,系统运行得很不稳定,系统经常停止响应。使用进程工具查看,发现进程并没有被杀死或挂起。利用Java虚拟机的工具发现系统在长时间的进行内存回收,内存回收的时间长达15分钟,整个系统在内存回收的时候就像挂起一样。另外还观察到系统使用了12G的内存(因为是 64位虚拟机所以突破了4G内存的限制)。从开发人员那里了解到,这个应用为了提高性能,大量使用了对象缓存,但是事与愿违,在Java中使用过多的内存,虽然在正常运行的时候能够获得很好的性能,但是会大大增加内存回收的时间。特别是对象缓存,本系统使用了8G的缓存空间,共缓存了6000多万个对象,对这些对象的遍历导致了长时间的内存回收。根据我们的建议,将缓存空间减少到1G,并调整回收算法(使用增量回收的算法),使得系统由于内存回收而造成的最大停顿时间减少到4秒,基本满足用户的需求。
外部命令问题
数字校园应用运行在4CPU的Solaris10服务器上,中间件为JavaEE服务器。系统在做大并发压力测试的时候,请求响应时间比较慢,通过操作系统的工具(mpstat)发现CPU使用率比较高。并且系统占用绝大多数的CPU资源而不是应用本身。这是个不正常的现象,通常情况下用户应用的CPU占用率应该占主要地位,才能说明系统是正常工作。通过Solaris 10的Dtrace脚本,我们查看当前情况下哪些系统调用花费了最多的CPU资源,竟然发现最花费CPU的系统调用是“fork”。众所周知, “fork”系统调用是用来产生新的进程,在Java虚拟机中只有线程的概念,绝不会有进程的产生。这是个非常异常的现象。通过本系统的开发人员,我们找到了答案:每个用户请求的处理都包含执行一个外部shell脚本,来获得系统的一些信息。这是通过Java的“Runtime.getRuntime ().exec”来完成的,但是这种方法在Java中非常消耗资源。Java虚拟机执行这个命令的方式是:首先克隆一个和当前虚拟机一样的进程,再用这个新的进程去执行外部命令,最后再退出这个进程。如果频繁执行这个操作,系统的消耗会很大,不仅在CPU,内存操作也很重。用户根据建议去掉这个shell 脚本执行的语句,系统立刻回复了正常。
文件操作问题
内容管理(CMS)系统运行在JavaEE服务器上,当系统长时间运行以后,性能非常差,用户请求的延时比系统刚上线的时候要大很多,并且用户的并发量很小,甚至是单个用户也很慢。通过操作系统的工具观察,一切都很正常,CPU利用率不高,IO也不是很大,内存很富余,网络几乎没有压力(因为并发用户少)。先不考虑线程互锁的问题,因为单个用户性能也不好。通过Java虚拟机观察也没有发现什么问题(内存回收很少发生)。这使得我们不得不使用代码跟踪器来全程跟踪代码。我们采用了Netbeans的Profiler,跟踪的结果非常意外,用户请求的90%的时间在创建新文件。从系统设计人员了解到,此系统使用了一个目录用于保存所有上传和共享的文件,文件用其命名方式来唯一区别于其他文件。我们查看了那个文件目录,发现该目录下已经拥有80万个文件了。这时候我们才定位到问题了:在同个目录下放置太多的文件,在创建新文件的时候,系统的开销是比较大的,例如为了防止重名,文件系统会遍历当前目录下所有的文件名等等。根据我们的建议,将文件分类保存在不同的目录下,性能有了大幅度的提高。
高速缓存命中率问题
运行在JavaEE服务器上的ERP系统,在CPU充分利用的情况下性能仍然不太好。从操作系统层面上观察不到什么大问题,而且ERP系统过于复杂,代码跟踪比较困难。于是进行了CPU状态的进一步检查,发现CPU的TLB命中率不是很高,于是对Java虚拟机的启动参数进行了修改,强迫虚拟机使用大尺寸的内存页面,提高TLB的命中率。下面的参数是在Sun的HOTSPOT中调整大尺寸(4M)页面的设置:
-XX:+AggressiveHeap
-XX:LargePageSizeInBytes=256m
通过调整,TLB命中明显提高,性能也得到近40%的提升。
转载之:http://developers.sun.com.cn/blog/yutoujava/entry/8
posted @
2009-08-28 15:07 kit_lo 阅读(1959) |
评论 (1) |
编辑 收藏
负载均衡器通常称为四层交换机或七层交换机。四层交换机主要分析IP层及TCP/UDP层,实现四层流量负载均衡。七层交换机除了支持四层负载均衡以外,还有分析应用层的信息,如HTTP协议URI或Cookie信息。
一、F5配置步骤:
1、F5组网规划
(1)组网拓朴图(具体到网络设备物理端口的分配和连接,服务器网卡的分配与连接)
(2)IP地址的分配(具体到网络设备和服务器网卡的IP地址的分配)
(3)F5上业务的VIP、成员池、节点、负载均衡算法、策略保持方法的确定
2、F5配置前的准备工作
(1)版本检查
f5-portal-1:~# b version
Kernel:
BIG-IP Kernel 4.5PTF-07 Build18
(2)时间检查--如不正确,请到单用户模式下进行修改
f5-portal-1:~# date
Thu May 20 15:05:10 CST 2004
(3)申请license--现场用的F5都需要自己到F5网站上申请license
3、F5 的通用配置
(1)在安全要求允许的情况下,在setup菜单中可以打开telnet及ftp功能,便于以后方便维护
(2)配置vlan unique_mac选项,此选项是保证F5上不同的vlan 的MAC地址不一样。在缺省情况下,F5的各个vlan的MAC地址是一样的,建议在配置时,把此项统一选择上。可用命令ifconfig –a来较验
具体是system/Advanced Properties/vlan unique_mac
(3)配置snat any_ip选项选项,此选项为了保证内网的机器做了snat后,可以对ping的数据流作转换。Ping是第三层的数据包,缺省情况下F5是不对ping的数据包作转换,也就是internal vlan的主机无法ping external vlan的机器。(注意:还可以采用telnet来验证。)
具体是system/Advanced Properties/snat any_ip
4、F5 的初始化配置
建议在对F5进行初始时都用命令行方式来进行初始化(用Web页面初始化的方式有时会有问题)。登录到命令行上,运行config或setup命令可以进行初始化配置。初次运行时会提示一些license的信息。
default:~# config
5、F5双机切换监控配置(有F5双机时需要)
(1)在web页面中选择相应的vlan,在arm failsafe选择则可。Timeout为从F5收不到包的时间起,经过多长时间就发生切换。此配置不能同步,需要在F5的主备机上同时配置。每个vlan都可以配置vlan arm failsafe。
具体在Network下
(2)在web页面中选择system,在redundant properties中把gateway failsafe选择则可。Router是需要监控的地址。此配置不能同步,需要在F5的主备机上同时配置。一套F5上只能配置一个gateway failsafe
具体在system/redundant properties/gateway failsafe
6、F5 MAC masquerade配置
Mac Masquerading是F5的Shared IP Address (Floating)的MAC地址,F5如果不配置此项,则shared IP Address的MAC地址与每台F5的vlan self IP Address的MAC地址是一样的。
一般服务器是以shared IP Address为网关,在两台F5上都配置了Mac Masquerade(相同的MAC地址),这样当F5发生切换后,服务器上shared IP address的MAC不变,保证了业务的不中断
具体在Network下
7、F5的pool配置
(1)在配置工具Web页面的导航面板中选择“Pools”中的“Pools”标签,点击“ADD”按钮添加服务器池(Pool)。
(2)在池属性(Pool Properties)中的“Load Balancing Method”表格中选择负载均衡策略,通常采用默认策略:“Round Robin”
(3)在“Resouces”表格中的“Member Address”文本框输入成员IP地址,在“Service”文本框中输入服务端口,点击“>>”添加到“Current Members”当前成员列表中。
(4)添加所有组成员,点击“Done”完成配置。
(5)在“Pools”中的“Pool Name”列选中特定池,然后池属性页面中选择“Persistence”标签。
(6)在“Persistence Type”表格中选定会话保持类型。点击“Apply”应用配置。
8、F5的virtual server配置
(1)在配置工具Web页面的导航面板中选择“Virtual Servers”中的“Virtual Servers”标签,点击“ADD”按钮添加虚拟服务器。
(2)在“Add Virtual Server”窗口的“Address”文本框中输入虚拟服务器IP地址,并在“Service”文本框中输入服务端口号或在下拉框中选择现有的服务名称,点击“Next”执行下一步。
(3)在“Add Virtual Server”窗口的“Configure Basic Properties”页面中点击“Next”执行下一步。 在“Add Virtual Server”窗口的“Select Physical Resources”页面中点击单选按钮“Pool”,并在下拉框中选择虚拟服务器对应的负载均衡池。
(4)按“Done”完成创建虚拟服务器。
9、F5的monitor的配置
(1)在配置工具Web页面的导航面板中选择“Monitor”中的“Monitors”标签,点击“ADD”按钮添加监控
(2)根据需要选择相关关联类型:“Node Associations”标签、Node Address Associations”标签、Service Associations”标签。
(3)被选关联标签中,在“Choose Monitor”表格中选择监控名称,点击“>>”按钮添加到“Monitor Rule”监控规格文本框中。监控规则可以为一条或多条。
(4)选择监控规则后,在对应节点的“Associate Current Monitor Rule”复选框中选中。如果欲删除监控关联,则选中对应节点的“Delete Existing Assocation”复选框。
(5)点击“Apply”关联监控
10、F5的SNAT配置
(1)在配置工具Web页面的导航面板中选择“NATs”中的“SNATs”标签,点击“ADD”按钮添加SNAT地址。
(2)在“Add SNAT”窗口中“Translation Address”的“IP”文本框中输入SNAT IP地址,并在“Origin List”的“Origin Address”文本框中输入节点IP地址或在“Origin VLAN”下拉框中选择VLAN名称,点击“>>”加入“Current List”列表。
(3)按“Done”完成添加SNAT IP地址。
11、F5主备机同步及切换校验
具体在system/Redundant Properties/synchonize Config...
12、业务的校验
F5主备机切换的校验
F5主备机业务运行的校验
其中1~6是基本配置,7~10业务配置,11~12校验
二、F5负载均衡器的维护
1、F5节点及应用的检查
通过“System -> Network Map”页面查看节点及应用状态
绿色:节点或虚拟服务器为“UP”
红色:节点或虚拟服务器状态为“Down”
灰色:节点或虚拟服务器被禁用
2、日志的检查
(1)当天日志:从web上查看logs中的system log、bigip log、monitor log,看日志中是否有异常。
(2)7天内的日志
系统日志文件 - /var/log/messages消息, 系统消息
BIG-IP 日志文件 - /var/log/bigip
“External” BIG-IP events
Monitor 日志文件 - /var/log/bigd
“Internal” BIG-IP Events
3DNS 日志文件 - /var/log/3dns
3DNS Information
用gzcat、more、vi命令打开
3、F5流量的检查
(1)业务上的基本维护主要是在F5上查看F5分发到各节点的connect是否负载均衡,一般不应有数量级的差别
(2)通过WEB->pool-> pool statistics中查看connection项中的total和current项,不应有明显的数量级的差别
(3)F5 qkview命令
执行qkview,执行完成后将输出信息保存在文件“/var/tmp/-tech.out”中,供高级技术支持用
(4)F5 tcpdump命令
TCPDUMP是Unix系统常用的报文分析工具,TCPDUMP经常用于故障定位,如会话保持失效、SNAT通信问题等
tcpdump [ -adeflnNOpqRStvxX ] [ -c count ] [ -F file ]
[ -i interface ] [ -m module ] [ -r file ]
[ -s snaplen ] [ -T type ] [ -w file ]
[ -E algo:secret ] [ expression ]
posted @
2009-08-28 15:06 kit_lo 阅读(3611) |
评论 (0) |
编辑 收藏
1. Tomcat是Apache鼎力支持的Java Web应用服务器,由于它优秀的稳定性以及丰富的文档资料,广泛的使用人群,从而在开源领域受到最广泛的青睐。
2. Jboss作为Java EE应用服务器,它不但是Servlet容器,而且是EJB容器,从而受到企业级开发人员的欢迎,从而弥补了Tomcat只是一个Servlet容器的缺憾。
3. Resin也仅仅是一个Servlet容器,然而由于它优秀的运行速度,使得它在轻量级Java Web领域备受喜爱,特别是在互联网Web服务领域,众多知名公司都采用其作为他们的Java Web应用服务器,譬如163、ku6等。
在商用应用服务器里主要有:Weblogic、Websphere,其中Weblogic我也使用过很长一段时间,当时也只用其当Servlet容器,然而就在同等条件下,在性能及易用性等方面,要比Tomcat优秀很多。
4.glassfish是Sun公司推出的Java EE服务器,一个比较活跃的开源社区,不断的通过社区的反馈来提高其的可用性,经过glassfish v1 glassfish v2 到今天的glassfish v3 ,它已经走向成熟.Glassfish是一个免费、开放源代码的应用服务,它实现了Java EE 5,Java EE 5 平台包括了以下最新技术:EJB 3.0、JSF 1.2、Servlet 2.5、JSP 2.1、JAX-WS 2.0、JAXB 2.0、 Java Persistence 1.0、Common Annonations 1.0、StAX 1.0等.
支持集群,通过内存中会话状态复制,增强了部署体系结构的可用性与可伸缩性,它对集群有着很好的支持,可以简单到通过添加机器,就可轻松的提高网站的带负载能力,在解析能力方面,它对html的吞吐能力与apache服务器不分上下,就是tomcat所不能比的,支持目录部署,热部署,解决了tomcat对热部署能力的缺陷.在版本方面做的更加人性化,有开发时用的简化版,专门用于部署web项目的版本,还要完全符合j2ee标准的版本.
posted @
2009-08-28 15:05 kit_lo 阅读(5547) |
评论 (0) |
编辑 收藏
为应用程序添加搜索能力经常是一个常见的需求。本文介绍了一个框架,开发者可以使用它以最小的付出实现搜索引擎功能,理想情况下只需要一个配置文件。该框架基于若干开源的库和工具,如 Apache Lucene,Spring 框架,cpdetector 等。它支持多种资源。其中两个典型的例子是数据库资源和文件系统资源。Indexer 对配置的资源进行索引并传输到中央服务器,之后这些索引可以通过 API 进行搜索。Spring 风格的配置文件允许清晰灵活的自定义和调整。核心 API 也提供了可扩展的接口。
引言
为应用程序添加搜索能力经常是一个常见的需求。尽管已经有若干程序库提供了对搜索基础设施的支持,然而对于很多人而言,使用它们从头开始建立一个搜索引擎将是一个付出不小而且可能乏味的过程。另一方面,很多的小型应用对于搜索功能的需求和应用场景具有很大的相似性。本文试图以对多数小型应用的适用性为出发点,用 Java 语言构建一个灵活的搜索引擎框架。使用这个框架,多数情形下可以以最小的付出建立起一个搜索引擎。最理想的情况下,甚至只需要一个配置文件。特殊的情形下,可以通过灵活地对框架进行扩展满足需求。当然,如题所述,这都是借助开源工具的力量。
基础知识
Apache Lucene 是开发搜索类应用程序时最常用的 Java 类库,我们的框架也将基于它。为了下文更好的描述,我们需要先了解一些有关 Lucene 和搜索的基础知识。注意,本文不关注索引的文件格式、分词技术等话题。
什么是搜索和索引
从用户的角度来看,搜索的过程是通过关键字在某种资源中寻找特定的内容的过程。而从计算机的角度来看,实现这个过程可以有两种办法。一是对所有资源逐个与关键字匹配,返回所有满足匹配的内容;二是如同字典一样事先建立一个对应表,把关键字与资源的内容对应起来,搜索时直接查找这个表即可。显而易见,第二个办法效率要高得多。建立这个对应表事实上就是建立逆向索引(inverted index)的过程。
Lucene 基本概念
Lucene 是 Doug Cutting 用 Java 开发的用于全文搜索的工具库。在这里,我假设读者对其已有基本的了解,我们只对一些重要的概念简要介绍。要深入了解可以参考 参考资源 中列出的相关文章和图书。下面这些是 Lucene 里比较重要的类。
Document:索引包含多个 Document。而每个 Document 则包含多个 Field 对象。Document 可以是从数据库表里取出的一堆数据,可以是一个文件,也可以是一个网页等。注意,它不等同于文件系统中的文件。
Field:一个 Field 有一个名称,它对应 Document的一部分数据,表示文档的内容或者文档的元数据(与下文中提到的资源元数据不是一个概念)。一个 Field 对象有两个重要属性:Store ( 可以有 YES, NO, COMPACT 三种取值 ) 和 Index ( 可以有 TOKENIZED, UN_TOKENIZED, NO, NO_NORMS 四种取值 )
Query:抽象了搜索时使用的语句。
IndexSearcher:提供Query对象给它,它利用已有的索引进行搜索并返回搜索结果。
Hits:一个容器,包含了指向一部分搜索结果的指针。
使用 Lucene 来进行编制索引的过程大致为:将输入的数据源统一为字符串或者文本流的形式,然后从数据源提取数据,创建合适的 Field 添加到对应该数据源的 Document 对象之中。
系统概览
要建立一个通用的框架,必须对不同情况的共性进行抽象。反映到设计需要注意两点。一是要提供扩展接口;二是要尽量降低模块之间的耦合程度。我们的框架很简单地分为两个模块:索引模块和搜索模块。索引模块在不同的机器上各自进行对资源的索引,并把索引文件(事实上,下面我们会说到,还有元数据)统一传输到同一个地方(可以是在远程服务器上,也可以是在本地)。搜索模块则利用这些从多个索引模块收集到的数据完成用户的搜索请求。
图 1 展现了整体的框架。可以看到,两个模块之间相对是独立的,它们之间的关联不是通过代码,而是通过索引和元数据。在下文中,我们将会详细介绍如何基于开源工具设计和实现这两个模块。
图 1. 系统架构图
建立索引
可以进行索引的对象有很多,如文件、网页、RSS Feed 等。在我们的框架中,我们定义可以进行索引的一类对象为资源。从实现细节上来说,从一个资源中可以提取出多个 Document 对象。文件系统资源和数据库结果集资源都是资源的代表性例子。
前面提到,从资源中收集到的索引被统一传送到同一个地方,以被搜索模块所用。显然除了索引之外,搜索模块需要有对资源更多的了解,如资源的名称、搜索该资源后搜索结果的呈现格式等。这些额外的附加信息称为资源的元数据。元数据和索引数据一同被收集起来,放置到某个特定的位置。
简要地介绍过资源的概念之后,我们首先为其定义一个 Resource 接口。这个接口的声明如下。
清单 1. Resource 接口
public interface Resource {
// RequestProcessor 对象被动地从资源中提取 Document,并返回提取的数量
public int extractDocuments(ResourceProcessor processor);
// 添加的 DocumentListener 将在每一个 Document 对象被提取出时被调用
public void addDocumentListener(DocumentListener l);
// 返回资源的元数据
public ResourceMetaData getMetaData();
}
其中元数据包含的字段见下表。在下文中,我们还会对元数据的用途做更多的介绍。
表 1. 资源元数据包含的字段
属性 类型 含义
resourceName String 资源的唯一名称
resourceDescription String 资源的介绍性文字
hitTextPattern String 当文档被搜索到时,这个 pattern 规定了结果显示的格式
searchableFields String[] 可以被搜索的字段名称
而 DocumentListener 的代码如下。
清单 2. DocumentListener 接口
public interface DocumentListener extends EventListener {
public void documentExtracted(Document doc);
}
为了让索引模块能够知道所有需要被索引的资源,我们在这里使用 Spring 风格的 XML 文件配置索引模块中的所有组件,尤其是所有资源。您可以在 下载部分 查看一个示例配置文件。
为什么选择使用 Spring 风格的配置文件?
这主要有两个好处:
仅依赖于 Spring Core 和 Spring Beans 便免去了定义配置机制和解析配置文件的负担;
Spring 的 IoC 机制降低了框架的耦合性,并使扩展框架变得简单;
基于以上内容,我们可以大致描述出索引模块工作的过程:
首先在 XML 配置的 bean 中找出所有 Resource 对象;
对每一个调用其 extractDocuments() 方法,这一步除了完成对资源的索引外,还会在每次提取出一个 Document 对象之后,通知注册在该资源上的所有 DocumentListener;
接着处理资源的元数据(getMetaData() 的返回值);
将缓存里的数据写入到本地磁盘或者传送给远程服务器;
在这个过程中,有两个地方值得注意。
第一,对资源可以注册 DocumentListener 使得我们可以在运行时刻对索引过程有更为动态的控制。举一个简单例子,对某个文章发布站点的文章进行索引时,一个很正常的要求便是发布时间更靠近当前时间的文章需要在搜索结果中排在靠前的位置。每篇文章显然对应一个 Document 对象,在 Lucene 中我们可以通过设置 Document 的 boost 值来对其进行加权。假设其中文章发布时间的 Field 的名称为 PUB_TIME,那么我们可以为资源注册一个 DocumentListener,当它被通知时,则检测 PUB_TIME 的值,根据距离当前时间的远近进行加权。
第二点很显然,在这个过程中,extractDocuments() 方法的实现依不同类型的资源而各异。下面我们主要讨论两种类型的资源:文件系统资源和数据库结果集资源。这两个类都实现了上面的 接口。
文件系统资源
对文件系统资源的索引通常从一个基目录开始,递归处理每个需要进行索引的文件。该资源有一个字符串数组类型的 excludedFiles 属性,表示在处理文件时需要排除的文件绝对路径的正则表达式。在递归遍历文件系统树的同时,绝对路径匹配 excludedFiles 中任意一项的文件将不会被处理。这主要是考虑到一般我们只需要对一部分文件夹(比如排除可能存在的备份目录)中的一部分文件(如 doc, ppt 文件等)进行索引。
除了所有文件共有的文件名、文件路径、文件大小和修改时间等 Field,不同类型的文件需要有不同的处理方法。为了保留灵活性,我们使用 Strategy 模式封装对不同类型文件的处理方式。为此我们抽象出一个 DocumentBuilder 的接口,该接口仅定义了一个方法如下:
清单 3. DocumentBuilder 接口
public interface DocumentBuilder {
Document buildDocument(InputStream is);
}
什么是 Strategy 模式?
根据 Design patterns: Elements of reusable object orientated software 一书:Strategy 模式“定义一系列的算法,把它们分别封装起来,并且使它们相互可以替换。这个模式使得算法可以独立于使用它的客户而变化。”
不同的 DocumentBuilder(Strategy) 用于从一个输入流中读取数据,处理不同类型的文件。对于常见的文件格式来说,都有合适的开源工具帮助进行解析。在下表中我们列举一些常见文件类型的解析办法。
文件类型 常用扩展名 可以使用的解析办法
纯文本文档 txt 无需类库解析
RTF 文档 rtf 使用 javax.swing.text.rtf.RTFEditorKit 类
Word 文档(非 OOXML 格式) doc Apache POI (可配合使用 POI Scratchpad)
PowerPoint 演示文稿(非 OOXML 格式) xls Apache POI (可配合使用 POI Scratchpad)
PDF 文档 pdf PDFBox(可能中文支持欠佳)
HTML 文档 htm, html JTidy, Cobra
这里以 Word 文件为例,给出一个简单的参考实现。
清单 4. 解析纯文本内容的实现
// WordDocument 是 Apache POI Scratchpad 中的一个类
Document buildDocument(InputStream is) {
String bodyText = null;
try {
WordDocument wordDoc = new WordDocument(is);
StringWriter sw = new StringWriter();
wordDoc.writeAllText(sw);
sw.close();
bodyText = sw.toString();
} catch (Exception e) {
throw new DocumentHandlerException("Cannot extract text from a Word document", e);
}
if ((bodyText != null) && (bodyText.trim().length() > 0)) {
Document doc = new Document();
doc.add(new Field("body", bodyText, Field.Store.YES, Field.Index.TOKENIZED));
return doc;
}
return null;
}
那么如何选择合适的 Strategy 来处理文件呢?UNIX 系统下的 file(1) 工具提供了从 magicnumber 获取文件类型的功能,我们可以使用 Runtime.exec() 方法调用这一命令。但这需要在有 file(1) 命令的情况下,而且并不能识别出所有文件类型。在一般的情况下我们可以简单地根据扩展名来使用合适的类处理文件。扩展名和类的映射关系写在 properties 文件中。当需要添加对新的文件类型的支持时,我们只需添加一个新的实现 DocumentBuilder 接口的类,并在映射文件中添加一个映射关系即可。
数据库结果集资源
大多数应用使用数据库作为永久存储,对数据库查询结果集索引是一个常见需求。
生成一个数据库结果集资源的实例需要先提供一个查询语句,然后执行查询,得到一个结果集。这个结果集中的内容便是我们需要进行索引的对象。extractDocuments 的实现便是为结果集中的每一行创建一个 Document 对象。和文件系统资源不同的是,数据库资源需要放入 Document 中的 Field 一般都存在在查询结果集之中。比如一个简单的文章发布站点,对其后台数据库执行查询 SELECT ID, TITLE, CONTENT FROM ARTICLE 返回一个有三列的结果集。对结果集的每一行都会被提取出一个 Document 对象,其中包含三个 Field,分别对应这三列。
然而不同 Field 的类型是不同的。比如 ID 字段一般对应 Store.YES 和 Index.NO 的 Field;而 TITLE 字段则一般对应 Store.YES 和 Index.TOKENIZED 的 Field。为了解决这个问题,我们在数据库结果集资源的实现中提供一个类型为 Properties 的 fieldTypeMappings 属性,用于设置数据库字段所对应的 Field 的类型。对于前面的情况来说,这个属性可能会被配置成类似这样的形式:
ID = YES, NO
TITLE = YES, TOKENIZED
CONTENT = NO, TOKENIZED
配合这个映射,我们便可以生成合适类型的 Field,完成对结果集索引的工作。
收集索引
完成对资源的索引之后,还需要让索引为搜索模块所用。前面我们已经说过这里介绍的框架主要用于小型应用,考虑到复杂性,我们采取简单地将分布在各个机器上的索引汇总到一个地方的策略。
汇总索引的传输方式可以有很多方案,比如使用 FTP、HTTP、rsync 等。甚至索引模块和搜索模块可以位于同一台机器上,这种情况下只需要将索引进行本地拷贝即可。同前面类似,我们定义一个 Transporter 接口。
清单 5. Transporter 接口
public interface Transporter {
public void transport(File file);
}
以 FTP 方式传输为例,我们使用 Commons Net 完成传输的操作。
public void transport(File file) throws TransportException {
FTPClient client = new FTPClient();
client.connect(host);
client.login(username, password);
client.changeWorkingDirectory(remotePath);
transportRecursive(client, file);
client.disconnect();
}
public void transportRecursive(FTPClient client, File file) {
if (file.isFile() && file.canRead()) {
client.storeFile(file.getName(), new FileInputStream(file));
} else if (file.isDirectory()) {
client.makeDirectory(file.getName());
client.changeWorkingDirectory(file.getName());
File[] fileList = file.listFiles();
for (File f : fileList) {
transportRecursive(client, f);
}
}
}
对其他传输方案也有各自的方案进行处理,具体使用哪个 Transporter 的实现被配置在 Spring 风格的索引模块配置文件中。传输的方式是灵活的。比如当需要强调安全性时,我们可以换用基于 SSL 的 FTP 进行传输。所需要做的只是开发一个使用 FTP over SSL 的 Transporter 实现,并在配置文件中更改 Transporter 的实现即可。
进行搜索
在做了这么多之后,我们开始接触和用户关联最为紧密的搜索模块。注意,我们的框架不包括一个基于已经收集好的索引进行搜索是个很简单的过程。Lucene 已经提供了功能强大的 IndexSearcher 及其子类。在这个部分,我们不会再介绍如何使用这些类,而是关注在前文提到过的资源元数据上。元数据从各个资源所在的文件夹中读取得到,它在搜索模块中扮演重要的角色。
构建一个查询
对不同资源进行搜索的查询方法并不一样。例如搜索一个论坛里的所有留言时,我们关注的一般是留言的标题、作者和内容;而当搜索一个 FTP 站点时,我们更多关注的是文件名和文件内容。另一方面,我们有时可能会使用一个查询去搜索多个资源的结果。这正是之前我们在前面所提到的元数据中 searchableFields 和 resourceName 属性的作用。前者指出一个资源中哪些字段是参与搜索的;后者则用于在搜索时确定使用哪个或者哪些索引。从技术细节来说,只有有了这些信息,我们才可以构造出可用的 Query 对象。
呈现搜索结果
当从 IndexSearcher 对象得到搜索结果(Hits)之后,当然我们可以直接从中获取需要的值,再格式化予以输出。但一来格式化输出搜索结果(尤其在 Web 应用中)是个很常见的需求,可能会经常变更;二来结果的呈现格式应该是由分散的资源各自定义,而不是交由搜索模块来定义。基于上面两个原因,我们的框架将使用在资源收集端配置结果输出格式的方式。这个格式由资源元数据中的 hitTextPattern 属性定义。该属性是一个字符串类型的值,支持两种语法
形如 ${field_name} 的子字符串都会被动态替换成查询结果中各个 Document 内 Field 的值。
形如 $function(...) 的被解释为函数,括号内以逗号隔开的符号都被解释成参数,函数可以嵌套。
例如搜索“具体”返回的搜索结果中包含一个 Document 对象,其 Field 如下表:
Field 名称 Field 内容
url http://example.org/article/1.html
title 示例标题
content 这里是具体的内容。
那么如果 hitTextPatten 被设置为“${title}
$highlight(${content}, 5, "", "")”,返回的结果经浏览器解释后可能的显示结果如下(这只是个演示链接,请不要点击):
示例标题
这里是具体...
上面提到的 $highlight() 函数用于在搜索结果中取得最匹配的一段文本,并高亮显示搜索时使用的短语,其第一个参数是高亮显示的文本,第二个参数是显示的文本长度,第三和第四个参数是高亮文本时使用的前缀和后缀。
可以使用正则表达式和文本解析来实现前面所提到的语法。我们也可以使用 JavaCC 定义 hitTextPattern 的文法,进而生成词法分析器和语法解析器。这是更为系统并且相对而言不易出错的方法。对 JavaCC 的介绍不是本文的重点,您可以在下面的 阅读资源 中找到学习资料。
下面列出的是一些与我们所提出的框架所相关或者类似的产品,您可以在 学习资料 中更多地了解他们。
IBM?OmniFind?Family
OmniFind 是 IBM 公司推出的企业级搜索解决方案。基于 UIMA (Unstructured Information Management Architecture) 技术,它提供了强大的索引和获取信息功能,支持巨大数量、多种类型的文档资源(无论是结构化还是非结构化),并为 Lotus?Domino?和 WebSphere?Portal 专门进行了优化。
Apache Solr
Solr 是 Apache 的一个企业级的全文检索项目,实现了一个基于 HTTP 的搜索服务器,支持多种资源和 Web 界面管理,它同样建立在 Lucene 之上,并对 Lucene 做了很多扩展,例如支持动态字段及唯一键,对查询结果进行动态分组和过滤等。
Google SiteSearch
使用 Google 的站点搜索功能可以方便而快捷地建立一个站内搜索引擎。但是 Google 的站点搜索基于 Google 的网络爬虫,所以无法访问受保护的站点内容或者 Intranet 上的资源。另外,Google 所支持的资源类型也是有限的,我们无法对其进行扩展。
SearchBlox?
SearchBlox 是一个商业的搜索引擎构建框架。它本身是一个 J2EE 组件,和我们的框架类似,也支持对网页和文件系统等资源进行索引,进而进行搜索。
还需考虑的问题
本文介绍的思想试图利用开源的工具解决中小型应用中的常见问题。当然,作为一个框架,它还有很多不足,下面列举出一些可以进行改进的地方。
性能考虑
当需要进行索引的资源数目不多时,隔一定的时间进行一次完全索引不会占用很长时间。使用一台 2G 内存,Xeon 2.66G 处理器的服务器进行实际测试,发现对数据库资源的索引占用的时间很少,一千多条记录花费的时间在 1 秒到 2 秒之内。而对 1400 多个文件进行索引耗时大约十几秒。但在大型应用中,资源的容量是巨大的,如果每次都进行完整的索引,耗费的时间会很惊人。我们可以通过跳过已经索引的资源内容,删除已不存在的资源内容的索引,并进行增量索引来解决这个问题。这可能会涉及文件校验和索引删除等。
另一方面,框架可以提供查询缓存来提高查询效率。框架可以在内存中建立一级缓存,并使用如 OSCache 或 EHCache 实现磁盘上的二级缓存。当索引的内容变化不频繁时,使用查询缓存更会明显地提高查询速度、降低资源消耗。
分布式索引
我们的框架可以将索引分布在多台机器上。搜索资源时,查询被 flood 到各个机器上从而获得搜索结果。这样可以免去传输索引到某一台中央服务器的过程。当然也可以在非结构化的 P2P 网络上实现分布式哈希表 (DHT),配合索引复制 (Replication),使得应用程序更为安全,可靠,有伸缩性。在阅读资料中给出了 一篇关于构建分布式环境下全文搜索的可行性的论文。
安全性
目前我们的框架并没有涉及到安全性。除了依赖资源本身的访问控制(如受保护的网页和文件系统等)之外,我们还可以从两方面增强框架本身的安全性:
考虑到一个组织的搜索功能对不同用户的权限设置不一定一样,可以支持对用户角色的定义,实行对搜索模块的访问控制。
在资源索引模块中实现一种机制,让资源可以限制自己暴露的内容,从而缩小索引模块的索引范围。这可以类比 robots 文件可以规定搜索引擎爬虫的行为。
通过上文的介绍,我们认识了一个可扩展的框架,由索引模块和搜索模块两部分组成。它可以灵活地适应不同的应用场景。如果需要更独特的需求,框架本身预留了可以扩展的接口,我们可以通过实现这些接口完成功能的定制。更重要的是这一切都是建立在开源软件的基础之上。希望本文能为您揭示开源的力量,体验用开源工具组装您自己的解决方案所带来的莫大快乐。
posted @
2009-08-28 15:03 kit_lo 阅读(1550) |
评论 (0) |
编辑 收藏
第一步:加入log4j-1.2.8.jar到lib下。
第二步:在CLASSPATH下建立log4j.properties。内容如下:
1 log4j.rootCategory=INFO, stdout , R
2
3 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
4 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
5 log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n
6
7 log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
8 log4j.appender.R.File=D:\Tomcat 5.5\logs\qc.log
9 log4j.appender.R.layout=org.apache.log4j.PatternLayout
10 log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n
11
12 log4j.logger.com.neusoft=DEBUG
13 log4j.logger.com.opensymphony.oscache=ERROR
14 log4j.logger.net.sf.navigator=ERROR
15 log4j.logger.org.apache.commons=ERROR
16 log4j.logger.org.apache.struts=WARN
17 log4j.logger.org.displaytag=ERROR
18 log4j.logger.org.springframework=DEBUG
19 log4j.logger.com.ibatis.db=WARN
20 log4j.logger.org.apache.velocity=FATAL
21
22 log4j.logger.com.canoo.webtest=WARN
23
24 log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
25 log4j.logger.org.hibernate=DEBUG
26 log4j.logger.org.logicalcobwebs=WARN
第三步:相应的修改其中属性,修改之前就必须知道这些都是干什么的,在第二部分讲解。
第四步:在要输出日志的类中加入相关语句:
定义属性:protected final Log log = LogFactory.getLog(getClass());
在相应的方法中:
if (log.isDebugEnabled())
{
log.debug(“System …..”);
}
二、Log4j说明
1 log4j.rootCategory=INFO, stdout , R
此句为将等级为INFO的日志信息输出到stdout和R这两个目的地,stdout和R的定义在下面的代码,可以任意起名。等级可分为OFF、 FATAL、ERROR、WARN、INFO、DEBUG、ALL,如果配置OFF则不打出任何信息,如果配置为INFO这样只显示INFO, WARN, ERROR的log信息,而DEBUG信息不会被显示,具体讲解可参照第三部分定义配置文件中的logger。
3 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
此句为定义名为stdout的输出端是哪种类型,可以是
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
具体讲解可参照第三部分定义配置文件中的Appender。
4 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
此句为定义名为stdout的输出端的layout是哪种类型,可以是
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
具体讲解可参照第三部分定义配置文件中的Layout。
5 log4j.appender.stdout.layout.ConversionPattern= [QC] %p [%t] %C.%M(%L) | %m%n
如果使用pattern布局就要指定的打印信息的具体格式ConversionPattern,打印参数如下:
%m 输出代码中指定的消息
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。
[QC]是log信息的开头,可以为任意字符,一般为项目简称。
输出的信息
[TS] DEBUG [main] AbstractBeanFactory.getBean(189) | Returning cached instance of singleton bean 'MyAutoProxy'
具体讲解可参照第三部分定义配置文件中的格式化日志信息。
7 log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
此句与第3行一样。定义名为R的输出端的类型为每天产生一个日志文件。
8 log4j.appender.R.File=D:\Tomcat 5.5\logs\qc.log
此句为定义名为R的输出端的文件名为D:\Tomcat 5.5\logs\qc.log
可以自行修改。
9 log4j.appender.R.layout=org.apache.log4j.PatternLayout
与第4行相同。
10 log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n
与第5行相同。
12 log4j.logger.com. neusoft =DEBUG
指定com.neusoft包下的所有类的等级为DEBUG。
可以把com.neusoft改为自己项目所用的包名。
13 log4j.logger.com.opensymphony.oscache=ERROR
14 log4j.logger.net.sf.navigator=ERROR
这两句是把这两个包下出现的错误的等级设为ERROR,如果项目中没有配置EHCache,则不需要这两句。
15 log4j.logger.org.apache.commons=ERROR
16 log4j.logger.org.apache.struts=WARN
这两句是struts的包。
17 log4j.logger.org.displaytag=ERROR
这句是displaytag的包。(QC问题列表页面所用)
18 log4j.logger.org.springframework=DEBUG
此句为Spring的包。
24 log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
25 log4j.logger.org.hibernate=DEBUG
此两句是hibernate的包。
以上这些包的设置可根据项目的实际情况而自行定制。
三、log4j详解
1、定义配置文件
Log4j支持两种配置文件格式,一种是XML格式的文件,一种是Java特性文件log4j.properties(键=值)。下面将介绍使用log4j.properties文件作为配置文件的方法:
①、配置根Logger
Logger 负责处理日志记录的大部分操作。
其语法为:
log4j.rootLogger = [ level ] , appenderName, appenderName, …
其中,level 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者自定义的级别。Log4j建议只使用四个级别,优 先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定 义了INFO级别,只有等于及高于这个级别的才进行处理,则应用程序中所有DEBUG级别的日志信息将不被打印出来。ALL:打印所有的日志,OFF:关 闭所有的日志输出。 appenderName就是指定日志信息输出到哪个地方。可同时指定多个输出目的地。
②、配置日志信息输出目的地 Appender
Appender 负责控制日志记录操作的输出。
其语法为:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.optionN = valueN
这里的appenderName为在①里定义的,可任意起名。
其中,Log4j提供的appender有以下几种:
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),可通过 log4j.appender.R.MaxFileSize=100KB设置文件大小,还可通过 log4j.appender.R.MaxBackupIndex=1设置为保存一个备份文件。
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
例如:log4j.appender.stdout=org.apache.log4j.ConsoleAppender
定义一个名为stdout的输出目的地,ConsoleAppender为控制台。
③、配置日志信息的格式(布局)Layout
Layout 负责格式化Appender的输出。
其语法为:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.optionN = valueN
其中,Log4j提供的layout有以下几种:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
2、格式化日志信息
Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下:
%m 输出代码中指定的消息
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。
3、在代码中使用Log4j
我们在需要输出日志信息的类中做如下的三个工作:
1、导入所有需的commongs-logging类:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
2、在自己的类中定义一个org.apache.commons.logging.Log类的私有静态类成员:
private final Log log = LogFactory.getLog(getClass());
LogFactory.getLog()方法的参数使用的是当前类的class。
3、使用org.apache.commons.logging.Log类的成员方法输出日志信息:
if (log.isDebugEnabled())
{
log.debug("111");
}
if (log.isInfoEnabled())
{
log.info("222");
}
if (log.isWarnEnabled())
{
log.warn("333");
}
if (log.isErrorEnabled())
{
log.error("444");
}
if (log.isFatalEnabled())
{
log.fatal("555")
}
posted @
2009-08-28 15:00 kit_lo 阅读(194479) |
评论 (29) |
编辑 收藏
目标驱动,系统思维,风险意识,数据量化
凡事预则立,不预则废。如果你不知道要到哪里?给你一张地图也没有用。目标驱动首先要有最基本的计划管理和时间管理能力。对于一个项目,我们过程中做的所有工作都是为了要达到项目目标,因此在项目各个阶段所有活动都需要考虑对达成目标的影响,当发现偏差后及时纠正。目标驱动让我们从无目的的事后应急变成了有计划有目的的事前预测。目标驱动不是要抛弃过程,项目的成功涉及到过程,人和方法工具技术。为了达到项目目标,我们要根据项目的实际情况采取一系列项目原来已经总结的最佳实践形成一套过程,高效的过程和积极心态的人是保证项目目标达成的关键。因此作为项目经理要时刻问自己,项目的目标是什么?项目当前状态和我达成目标的差距是什么?我如何解决和应对。
项目的成功受到多方面的因素的影响,而且各个因素之间还存在正反作用力。系统思维就是要让我们能够清楚的认识到影响项目目标和成功的各个要素,以及它们之间存在的关系。形成一种适合项目的动态系统模型,通过这个动态模型去平衡项目各方干系人的利益,平衡项目四要素之间的关系,平衡项目的短期和长期的利益。项目经理的一个重要能力就是平衡,没有最优解,只有满意解,懂得了平衡就知道当项目出现变更和调整的时候如何更好的应对。从单要素最优的单向思维过渡到关注整个系统的全局思维模式上。
风险意识简单来讲就是项目在执行过程中可能发生的各种问题我都事先预见到了而采取了适当的缓解措施,这样才能够真正的让项目能够按照预先制定的计划和目标进行。再简单点就是如果风险管理做得好,项目是不应该失败的。君子安而不忘危,存而不忘亡,治而不忘乱。风险管理的重点正是在于要形成风险意识,要能够通过历史经验的积累,能够把项目的关键风险识别出来,使项目能够从事后的救火转变到事前的防备,使项目能够在前面紧张后面轻松。
要谈及量化管理首先应该要培训用数据说话的分析思维。在软件项目管理中我们做度量的目的,就是要收集和分析各种历史数据,通过对数据的分析来知道项目真正的效率,同时为后续项目提供各种估算参数数据。以数据说话让我们从全凭主观经验臆断转变到对事物的客观数据分析上。只有能够收集数据,分析数据我们才可能持续改进。有了数据意识后就是要有统计和量化管理方面的意识,利用统计学的思维和量化管理手段不仅仅是让我们的过程稳定和受控制,能够去发现项目执行过程中特殊原因引起的波动,针对特殊波动进行根源分析并采取纠正行动;还能够让我们能够根据预测模型更加准确的预测项目能够达成目标的程度和概率。
----------------------------------------
努力使自己成为工作的终结者
posted @
2009-08-28 14:53 kit_lo 阅读(449) |
评论 (1) |
编辑 收藏
大家喝的是啤酒。这时你入座了。
你给自己倒了杯可乐,这叫低配置。
你给自己倒了杯啤酒,这叫标准配置。
你给自己倒了杯茶水,这茶的颜色还跟啤酒一样,这叫木马。
你给自己倒了杯可乐,还滴了几滴醋,不仅颜色跟啤酒一样,而且不冒热气还有泡泡,这叫超级木马。
你的同事给你倒了杯白酒,这叫推荐配置。
人到齐了,酒席开始了。
你先一个人喝了一小口,这叫单元测试。
你跟旁边的人说哥们咱们随意,这叫交叉测试。
但是他说不行,这杯要干了,这叫压力测试。
于是你说那就大家一起来吧,这叫内部测试。
这个时候boss向全场举杯了,这叫集成测试。
菜过三巡,你就不跟他们客气了。
你向对面的人敬酒,这叫p2p.
你向对面的人敬酒,他回敬你,你又再敬他……,这叫tcp.
你向一桌人挨个敬酒,这叫令牌环。
你说只要是兄弟就干了这杯,这叫广播。
可是你的女上司听了不高兴了:只有兄弟么,罚酒三杯。这叫炸弹。
可是你的女下属听了不高兴了:我喝一口,你喝一杯,这叫恶意攻击。
有一个人过来向这桌敬酒,你说不行你先过了我这关,这叫防火墙。
你的小弟们过来敬你酒,这叫一对多。
你是boss,所有人过来敬你酒,这叫服务器。
酒是一样的,可是喝法是不同的。
你喝了一杯,boss喝了一口,这叫c#。
你喝了一杯,mm喝了一口,这叫vb。
你喝了一杯,你大哥喝了半杯,这叫c++。
你喝了半杯,你小弟喝了一杯,这叫汇编。
你喝了一杯,你的搭档也喝了一杯,这叫c。
酒是一样的,可是喝酒的人是不同的。
你越喝脸越红,这叫资源释放。
你越喝脸越白,这叫资源独占。
你已经醉了,却说我还能喝,叫做虚拟内存。
你明明能喝,却说我已经醉了,叫做资源保留。
你喝一段时间就上厕所,这叫cache。
酒过三巡,你也该活动活动了。
你一桌一桌的走,这叫轮巡。
你突然看到某一桌的漂亮mm,走了过去,这叫激活事件。
你去了坐下来就不打算走了,这叫死循环。
你的老大举杯邀你过去,你只好过去,这叫优先级。
你向一桌敬酒,他们说不行不行我们都喝白的,于是你也喝白的,这叫本地化。
你向boss敬酒,可是boss被围了起来,你只能站在外圈,这叫队列。
你终于到了内圈,小心翼翼的向前一步,这叫访问临界区。
你拍着boss的肩膀说哥们咱们喝一杯,这叫越界。
你不知喝了几圈了,只会说两个字,干了,这叫udp。
可是还有人拿着酒瓶跑过来说,刚才都没跟你喝,这叫丢包。
喝酒喝到最后的结果都一样
你突然跑向厕所,这叫捕获异常错误。
你在厕所吐了,反而觉得状态不错,这叫释放内存。
你在台面上吐了,觉得很惭愧,这叫时实错误。
你在boss面前吐了,觉得很害怕,这叫灾难性错误。
你吐到了boss身上,只能索性晕倒了,这叫Shut Down。
posted @
2009-08-28 14:52 kit_lo 阅读(453) |
评论 (1) |
编辑 收藏