CSDN博客

img TomHornson

我来改进EJB2.X的设计

发表于2004/12/29 17:55:00  1185人阅读

                    我来改进EJB2.X的设计
                                                                                                         TomHornson#(at)#hotmai.com 
                                                                                                                     
于珞珈山

      EJB
一度是J2EE技术中最闪亮的,然而,时过境迁,“EJB is dead!”、“EJB2.X已死”这样的论断此起彼伏。       

一门技术的好坏的评判,往往跟评判者本身的思维方式、技术偏向、切身项目经验等等相关。所以,往往,我们没有资格、也没有必要来强词别人的论断是错误的云云。

基于对EJB规范的理解、对目前一些 EJB Implements Source的阅读并结合一些切身经验和思考,笔者试图在本文中,分析EJB模型和EJB Implements的缺陷、提出一些改进的思路。

注:参看EJB Spec2.13.0

    参考implementsJfox-1.2Jboss-2.4.5Jboss-3.2.0OpenEJB-0.9.2

 

一、尽量降低客户端、服务器端通信次数和传输的数据量

1、对于以RMI为底层通信协议的服务,譬如Naming Service自身的locateNaming ServiceHome对象的Remote操作等,使用自定义连接管理。

2、客户端可以动态添加各种interceptor。如事务interceptor、安全interceptor、数据压缩interceptorLog interceptor等等。

 

二、 改进Protocol

 

1NIO来改进RMI

目前,RMI采用多线程来解决服务多客户时的阻塞问题。而且,从RMI Source中可以看到,RMI的线程技术的使用实在太过火。我们搭建一个场景:一个Remote对象可以指定在特定的port 导出。如果不指定,那么将会在默认的port 导出。每导出一个Remote对象,会产生一个新的thread,在特定port 监听,而每监听到一个connect,就会又产生一个处理thread。可以想象,即使少量的用户也会导致系统线程量剧增。而线程上下文切换导致的性能损失是众所周知的。

 

2、改进JNDI implement算法、数据结构等,来提高性能。
  
通过一系列的EJB Pattern改进Naming Service的使用。

这两点很重要,因为Naming Service是一个使用极度频繁的服务。

 

 

三、 改进Session Bean服务策略

目前Session Bean采用多线程、多Bean服务多客户的策略。注意,这里说的多Bean是针对Stateless Session Bean说的。而Stateful Session Bean,和Stateless Session Beanimplement是不同的,前端向后端的调用链为:客户->有状态Bean Instance Cache->无状态的Bean Instancepool。一个客户Session由一个特定的Stateful Bean来服务是天经地义的。

Tip:这里对于Bean开发者来说,设定恰当的Cache Size、恰当的Pool Size对于性能提高是至关重要的。调节Size主要为防止频繁的Bean Persistence,从而引起I/O瓶颈现象的出现。

 

首先,我们思考几个问题。

u 为什么多线程?

首先、因为目前的RMI采用多线程策略,这样可以说,Session Bean不可避免的需要使用多线程策略。

其次、Session Bean可以是阻塞的,这种情况下,Session Bean必须采用多线程策略。

v 为什么多Bean

为了将Bean开发者从Thread-Safe编程中解脱出来。

w  将把Bean开发者从Thread-Safe编程中解脱出来这个问题抛在一边,可不可以使用多线程、单Bean服务多客户?

答案是:不可以。因为Container-Managered Transaction是和线程绑定的。单Bean会使这个问题很难解决。

到这里,大家可以看到,最有效率的服务策略是单线程、单Bean服务多客户。但是,对于可能阻塞的Bean,必须使用多线程策略。

所以,我们能做的改进,就是用户可以根据Bean是否会阻塞,通过配置文件,指定服务策略。有两种情况:

1、 对于阻塞Bean,使用多线程、多Bean服务策略。

2、 对于非阻塞Bean,使用单线程、单Bean服务策略。

而,这一切的前提是,RMI已经使用NIO改进了,不然,如我们上面所说的,永远只能采用多线程、多Bean服务多客户策略。

 

x  对于,这个主题,最后,我还有一个问题,需要提出,目前,RMI采用多线程策略,那么Remote Home对象,包括Container等等,从EJB Implement者的角度来说,应该保证它的Thread-Safe。但是,我发现,JFOXJBoss中都没有相关处理代码,只有OpenEJB中有少量处理。

 

四、 改进Entity Bean

Entity Bean存在的问题就复杂多了。

1Entity Bean也是采用多线程、多Bean服务多客户的策略。

关于这个话题,我的想法跟Session Bean中对应问题的答案基本是一样的。

不过,Rd Roman认为多线程、多Bean是最好的服务策略。当然,他讨论的前提,是基于目前RMI多线程模型这样一个不可改变的事实,所以,需要讨论的就是需要多Bean还是单Bean来服务多客户。而多线程单Bean服务多客户,我已经证实了是不可以的。所以,总的来说,我的看法和Rd Roman看法是一致的,只是我假设使用NIO改进RMI,这样不一定非要多线程,对于非阻塞Bean可以使用单线程、单Bean服务多客户的策略而已。

Several Entity Bean Instances May Represent the Same Underlying Data

Lets consider the scenario in which many threads of execution want to access the same database data simultaneously. In banking, interest might be applied to a bank account, while at the same time a company directly deposits a check into that same account. In e-commerce, many different client browsers may be simultaneously interacting with a catalog of products.

To facilitate many clients accessing the same data, we need to design a highperformance

access system to our entity beans. One possibility is to allow many clients to share the same entity bean instance; that way, an entity bean could service many client requests simultaneously. While this is an interesting idea, it is not very appropriate for EJB, for two reasons. First, if wed like an

entity bean instance to service many concurrent clients, wed need to make that instance thread-safe. Writing thread-safe code is difficult and error-prone. Remember that the EJB value proposition is rapid application development. Mandating that component vendors produce stable thread-safe code does not encourage this. Second, having multiple threads of execution makes transactions almost impossible to control by the underlying transaction system. For these reasons, EJB dictates that only a single thread can ever be running within a bean instance. With session beans and message-driven beans, as well as entity beans, all bean instances are single-threaded.

Mandating that each bean can service only one client at a time could result in performance bottlenecks. Because each instance is single-threaded, clients need to effectively run in lockstep, each waiting their turn to use a bean. This could easily grind performance to a halt in any large enterprise deployment.

To boost performance, we could allow containers to instantiate multiple instances of the same entity bean class. This would allow many clients to concurrently interact with separate instances, each representing the same underlying entity data. Indeed, this is exactly what EJB allows containers to do. Thus, client requests do not necessarily need to be processed sequentially, but

rather concurrently.Having multiple bean instances represent the same data now raises a new

problem: data corruption. If many bean instances are representing the same underlying data via caching (see Chapter 14), were dealing with multiple inmemory cached replicas. Some of these replicas could become stale, representing data that is not current.

To achieve entity bean instance cache consistency, each entity bean instance needs to be routinely synchronized with the underlying storage. The container synchronizes the bean with the underlying storage by calling the beans ejbLoad() and ejbStore() callbacks, as described in the previous section.

The frequency with which beans are synchronized with an underlying storage is dictated by transactions, a topic we cover in Chapter 10. Transactions allow each client request to be isolated from every other request. They enable clients to believe they are dealing with a single in-memory bean instance, when in fact many instances are behind the scenes. Transactions give clients the illusion that they have exclusive access to data when in fact many clients are touching the same data.

 

 

2、  改进Entity Bean工作模型
目前,Entity Bean工作模型,我觉的性能问题太显著。

我们搭建一个场景:客户想更改PrimaryKey为特定值的Person EntityBeanname成员变量,那么EJB Implements引发的操作序列为:(假设为BMP)
u Home调用findByPrimaryKeyHome从无状态Bean Instance Pool中任意取出一个Bean Instance,执行Bean方法ejbFindByPrimaryKey,访问数据库,查看数据库是否有PrimaryKey为指定值的记录,如果有,则返回PrimaryKey

v Home调用ejbLoad来从数据库中加载数据,并且为无状态Entity Bean Instance 赋值,使之成为Entity Bean,Pool进入Cache。最后,给用户返回一个EJBObject

w 客户调用这个EJBObject HandlesetName()

setName()方法,在对应Bean Instance上作用之前,会调用ejbLoad()来同步数据库,当setName()方法后,还会调用ejbStore(),再次来同步数据库。

x到这里,一个简单的更改一个记录一个字段的操作,才算全部完成。一共进行了四次和数据库的交互、n多次的对Bean Instance的操作。你说,这种模型,没有性能问题,我才惊讶。

Tip1:如果你使用CMP EnityBean而不是BMP EntityBean,很多情况下,性能会有很大的提升。具体高多少看具体EJB Implement了。当然,越专业的Implement,优化程度越高。

Tip2: 这里的同步行为(ejbLoad/ejbStore)频率和事务是有关的。而具体EJB Implement实现的策略也有所不同。同步问题,很棘手。它不但很难处理好,而且对服务性能的影响非常之大。

 

所以,我觉的,EJB SpecExpert Group应该考虑,重新设计EJB工作模型。

 

3、 增强EntityBean功能

EJBQL太弱、表-对象映射支持太弱等等不能满足需求的情况需要改善。

 

4EntityBean Engine

在特定EJB 模型下,各EJB Implement厂商可以充分施展技术实力的地方就在于开发高性能的CMP EntityBean Engine了。JFOX没有实现Entity Bean,而JBossJAWS3.X之前,是相当的简陋的。

 

5、很好的定位Entity Bean

很久之前,写这篇文章时的看法

设计出不偏不倚的Persistence EngineEntity Bean不需要向Hibernate靠近,它们本质上根本不是一回事,Hibernate在我看来,就是一种特殊的(轻量级、动态执行)的翻译中间件,它的上层是对象视图、下层是面向数据源的SQL视图,而Entity Bean定位应该是静态的服务器端服务组件。它只需要一个能够支持固定功能的Persistence Engine即可。改进的主要的工作应该放在:从性能出发重新设计EntityBean Work Model、并且增强CMP EntityBean的功能。

 

最近,写《EJBHibernateSpring:剖析、批判和展望》时的思考

我们没必要说EJB2.X本身有多少的缺陷,毕竟,它是前一个J2EE时代的产物,只能说EJB2.X已经不能反映大多数J2EE应用的实际需要。过时了。那么EJB3.0打算带我们走向何方?

  EJB3.0 Spec除了针对简化开发、方便测试、方便部署等目标做了不少的修改,更重要的是EJB3.0SessionBean,特别是EntityBean模型做了一个全面的整容手术。这种修改是革命性的。

在我的《如果我来改进EJB2.X模型》中,我谈到,如果,让我对EJB2.XEntityBean模型做修改,那么首先需要为新的模型定好位。就拿EntityBean来说好了。 

第一条路:继续EntityBean设计的初试理念:Remote Domain Model(包括BMP EntityBean代表的Domain ModelCMP EntityBean代表的AnaemicDomainObject),并且保留Local接口,力图改经持久模型的设计,提高性能(即使CMP EntityBean的性能也是难以令人接受的,这种情况,我个人认为,主要是因为EntityBean模型设计的不好,在我的另一篇《如果我来改进EJB2.X模型》中有深入的分析)、增强功能(EJBQL实在太弱),让那些连SessionBeanEntityBean都需要部署在不同Server上的应用来为EJB2.XEntityBean留口气。

但是,显然,EJB Server提供商是不可能甘心这一点羹的,因为那样的应用实在太少了。事实已经证明,如果EntityBeanRemote不是必须的,那么RemoteEntityBean性能上是不可行的,它只能工作在SessionBean后端,然而,即使EntityBean工作在SessionBean后端,但是EntityBean本身的局限性也太多,粒度要么太粗要么太细,性能、功能太弱,等等,开发数据应用非常地蹩脚,那么如果,在Remote EntityBean不是必须的情况下,我为什么不完全放弃EntityBean,在SessionBean后端使用其它的O/R Mapping Tool来开发数据应用,譬如Hibernate。这就是,EntityBean可以走第二条路。当然,从某种意义上来说,也是它必须走的路。

第二条路:完全抛弃EntityBean,采用Hibernate这样的O/R Mapping Engine作为Session BeanMessage-Driven Bean的后端数据持久化工具。而从EJB3.0可以看出,EJB3.0的确完全抛弃了传统的EntityBean模型。个人意见:可以这样说吧,EntityBean已经不复存在,Expert GroupSessionBean下给你换上了一个非常sharpPersistence engine,你拿着engine,想干什么就干什么好了(上面讲过,EntityBean中,PersitenceEngineclient是通明的,这是由这两种引擎的本质作用决定的。有人说,EntityBean Application中不可以使用Dynamic Query,只能在配置文件中申明EJBQL,这些都是两种Persistence Engine的本质所决定的)。蹩脚的、强制模型的EntityBean不复存在!另外,EntityBean Remote特性在EJB3.0中根本没有提到,或许只是作为一个可选特性了吧(我还没有想到,EJB3.0中,如何来支持Remote PO,这个问题很诡异)。看来,Expert Group已经彻底否定了EntityBean的设计,或者说EntityBean的确是不符合实际需求的,Remote EntityBeanRemote Domain Object在绝大多数情况下是不切实际的。

 

话外题:HibernateJDO的关系,很微妙。EJB3.0JDO的合并、Gavin进入EJB3.0 ExpertGroup令人很迷惑。EJB3.0的持久化模型采用JDO,应该是理所当然的。但是,目前,EJB3.0Persitence Engine部分似乎被Hibernate左右,那么JDO的位置应该在哪里?

 

 

五、 提高EJB附加值功能的可扩展性、使用简易性,

从而提高EJB的竞争能力。
EJB
SecurityTransactionTimer Service、基于RMI/IIOPCORBA交互等等特

性于一体,应该是非常值得关注的、应该有很广泛的应用范围。但是为什么,这些附加值功能,我们却往往不会用,这里,我就是着重说一下Security
1
EJBSecurity开发是非常复杂的。

2、  EJB本身集成太紧密,没有扩展性

3、  安全模型太固定。

 

六、 将种类繁多的EJB Patterns集成进EJB Server

这样,可以大幅度简化EJB开发、提高项目质量。

譬如,在客户端增加一个Layer(Serializable),来缓冲Home Handle,并且当服务器重新启动时,通知客户端缓存更新。当然,如果Home Handle ID不是随即算法产生的,那么客户端,也不需要更新。说到这里,我觉的,JFox中一个小小可以改进的地方就是:在ClientContainerInvoker中,使用Map,缓存通过Naming Service查询的各种ContainerInvokerService

 

七、        Dynamic ProxyReflect这两种技术,严重影响性能,

设计替代技术。

好像,不少的EJB Server没有采用这两种技术,譬如Apusic,不过,我还不知道,他们是怎样做的。

修订记录:
第一次:6/10/2004
第二此:10/28/2004

0 0

相关博文

我的热门文章

img
取 消
img即使是一小步
也想与你分享
打开
img