构建可扩展的Java EE应用(一)

WEB框架 专栏收录该内容
5 篇文章 0 订阅
 

【译】构建可扩展的Java EE应用(一)

发布时间:2008年07月08日 作者:BlueDavy

阅读次数:30次 类别:我的文章 永久链接 Trackback 
可扩展性作为系统的属性之一,是个很难定义的名词,经常会与性能混淆。当然,可扩展性和性能是有关系的,它的目的是为了达到高性能。但是衡量可扩展性和性能的方法是不一样的,在这篇文章中,我们采用wikipedia中的定义
原文地址: http://www.theserverside.com/tt/articles/article.tss?l=ScalingYourJavaEEApplications

对于一个具备使用价值的应用而言,其使用者有可能会在一段时间内疯狂的增长。随着越来越多的关键性质的应用在Java EE上运行,很多的Java开发者也开始关注可扩展性的问题了。但目前来说,大部分的web 2.0站点是基于script语言编写的,对于Java应用可扩展能力,很多人都抱着质疑的态度。在这篇文章中,Wang Yu基于他本身在实验室项目的经验来展示如何构建可扩展的java应用,同时,基于一些在可扩展性上做的比较失败的项目给读者带来构建可扩展java应用的实践、理论、算法、框架和经验。

我一直为一家互联网性质的实验室工作,这个实验室采用我们公司最新的大型服务器环境为合作伙伴的产品和解决方案免费做性能测试,我工作的部分就是帮助他们在强大的CMT和SMP服务器上进行性能调优。

这些年来,我已经为不同的解决方案测试了数十种java应用。许多的产品都是为了解决同样的领域问题,因此这些产品的功能基本都是类似的,但在可扩展性上表现的却非常不同,其中有些不能扩展到64 CPU的服务器上运行,但可以扩展到20台服务器做集群运行,有些则只能运行在不超过2 CPU的机器上。

造成这些差别的原因在于设计产品时的架构愿景,所有的具备良好扩展性的java应用从需求需求阶段、系统设计阶段以及实现阶段都为可扩展性做了考虑,所以,你所编写的java应用的可扩展能力完全取决于你的愿景。

可扩展性作为系统的属性之一,是个很难定义的名词,经常会与性能混淆。当然,可扩展性和性能是有关系的,它的目的是为了达到高性能。但是衡量可扩展性和性能的方法是不一样的,在这篇文章中,我们采用wikipedia中的定义:

可扩展性是系统、网络或进程的可选属性之一,它表达的含义是可以以一种优雅的方式来处理不断增长的工作,或者以一种很明白的方式进行扩充。例如:它可以用来表示系统具备随着资源(典型的有硬件)的增加提升吞吐量的能力。

垂直扩展的意思是给系统中的单节点增加资源,典型的是给机器增加CPU或内存,垂直扩展为操作系统和应用模块提供了更多可共用的资源,因此它使得虚拟化的技术(应该是指在一台机器上运行多个虚拟机)能够运行的更加有效。

水平扩展的意思是指给系统增加更多的节点,例如为一个分布式的软件系统增加新的机器,一个更清晰的例子是将一台web服务器增加为三台。随着计算机价格的不断降低以及性能的不断提升,以往需要依靠超级计算机来进行的高性能计算的应用(例如:地震分析、生物计算等)现在可以采用这种多个低成本的应用来完成。由上百台普通机器构成的集群可以达到传统的基于RISC处理器的科学计算机所具备的计算能力。

这篇文章的第一部分来讨论下垂直扩展Java应用。

如何让Java EE应用垂直扩展

很多的软件设计人员和开发人员都认为功能是产品中最重要的因素,而性能和可扩展性是附加的特性和功能完成后才做的工作。他们中大部分人认为可以借助昂贵的硬件来缩小性能问题。

但有时候他们是错的,上个月,我们实验室中有一个紧急的项目,合作伙伴提供的产品在他们客户提供的CPU的机器上测试未达到性能的要求,因此合作伙伴希望在更多CPU(8 CPU)的机器上测试他们的产品,但结果却是在8 CPU的机器上性能反而比4 CPU的机器更差。

为什么会这样呢?首先,如果你的系统是多进程或多线程的,并且已经用尽了CPU的资源,那么在这种情况下增加CPU通常能让应用很好的得到扩展。

基于java技术的应用可以很简单的使用线程,Java语言不仅可以用来支持编写多线程的应用,同时JVM本身在对java应用的执行管理和内存管理上采用的也是多线程的方式,因此通常来说Java应用在多CPU的机器上可以运行的更好,例如Bea weblogic、IBM Websphere、开源的Glassfish和Tomcat等应用服务器,运行在Java EE应用服务器中的应用可以立刻从CMT和SMP技术中获取到好处。

但在我的实验室中,我发现很多的产品并不能充分的使用CPU,有些应用在8 CPU的服务器上只能使用到不到20%的CPU,像这类应用即使增加CPU也提升不了多少的。

热锁(Hot Lock)是可扩展性的关键障碍

在Java程序中,用来协调线程的最重要的工具就是 synchronized这个关键字了。由于java所采用的规则,包括缓存刷新和失效,Java语言中的synchronized块通常都会其他平台提供的类似的机制更加的昂贵。即使程序只是一个运行在单处理器上的单线程程序,一个synchronized的方法调用也会比非同步的方法调用慢。

要检查问题是否为采用synchronized关键字造成的,只需要像JVM进程发送一个QUIT指令(译者注:在linux上也可以用kill -3 PID的方式)来获取线程堆栈信息。如果你看到类似下面线程堆栈的信息,那么就意味着你的系统出现了热锁的问题:

..
" Thread-0 "  prio = 10  tid = 0x08222eb0  nid = 0x9  waiting  for  monitor entry [ 0xf927b000 .. 0xf927bdb8 ]
at testthread.WaitThread.run(WaitThread.java:
39 )
-  waiting to lock  < 0xef63bf08 >  (a java.lang.Object)
-  locked  < 0xef63beb8 >  (a java.util.ArrayList)
at java.lang.Thread.run(Thread.java:
595 )

synchronized 关键字强制执行器串行的执行synchronized中的动作。如果很多线程竞争同样的同步对象,那么只有一个线程能够执行同步块,而其他的线程就只能进入blocked状态了,如果此时没有其他需要执行的线程,那么处理器就进入空闲状态了,在这种情况下,增加CPU也带来不了多少性能提升。

热锁可能会导致更多线程的切换和系统的调用。当多个线程竞争同一个monitor时,JVM必须维护一个竞争此monitor的线程队列(同样,这个队列也必须同步),这也就意味着更多的时间需要花费在JVM或OS的代码执行上,而更少的时间是用在你的程序上的。

要避免热锁现象,以下的建议能带来一些帮助:

尽可能的缩短同步块

当你将线程中持有锁的时间尽量缩短后,其他线程竞争锁的时间也就变得更短。因此当你需要采用同步块来操作共享的变量时,应该将线程安全的代码放在同步块的外面,来看以下代码的例子:

Code list 1:

public   boolean  updateSchema(HashMap nodeTree) {
synchronized  (schema) {
    String nodeName 
=  (String)nodeTree.get( " nodeName " );
    String nodeAttributes 
=  (List)nodeTree.get( " attributes " );
    
if  (nodeName  ==   null
        
return   false ;
    
else
        
return  schema.update(nodeName,nodeAttributes);
}
}

上面的代码片段是为了当更新"schema"变量时保护这个共享的变量。但获取attribute值部分的代码是线程安全的。因此我们可以将这部分移至同步块的外面,让同步块变得更短一些:

Code list 2:

public   boolean  updateSchema(HashMap nodeTree) {
    String nodeName 
=  (String)nodeTree.get( " nodeName " );
    String nodeAttributes 
=  (List)nodeTree.get( " attributes " );
    
synchronized  (schema) {
        
if  (nodeName  ==   null )
            
return   false ;
        
else
            
return  schema.update(nodeName,nodeAttributes);
    }
}
 

减小锁的粒度

当你使用"synchronized"时,有两种粒度可选择:"方法锁"或"块锁"。如果你将"synchronized"放在方法上,那么也就意味着锁定了"this"对象。

Code list 3:

public   class  SchemaManager {
     
private  HashMap schema;
     
private  HashMap treeNodes;
     .
     
public   boolean   synchronized  updateSchema(HashMap nodeTree) {
         String nodeName 
=  (String)nodeTree.get( " nodeName " );
         String nodeAttributes 
=  (List)nodeTree.get( " attributes " );
         
if  (nodeName  ==   null return   false ;
         
else   return  schema.update(nodeName,nodeAttributes);
     }

     
public   boolean   synchronized  updateTreeNodes() {
         
     }
}
对比Code list 2中的代码,这段代码就显得更糟糕些了,因为当调用"updateSchema"方法时,它锁定了整个
对象,为了获得更好的粒度控制,应该仅仅锁定"schema"变量来替代锁定整个对象,这样其他不同的方法就可
以保持并行执行了。

避免在static方法上加锁

最糟糕的状况是在static方法上加"synchronized",这样会造成锁定这个class的所有实例对象。
--------------------------------
at sun.awt.font.NativeFontWrapper.initializeFont(Native Method)
-  waiting to lock  < 0xeae43af0 >  (a java.lang.Class)
at java.awt.Font.initializeFont(Font.java:
316 )
at java.awt.Font.readObject(Font.java:
1185 )
at sun.reflect.GeneratedMethodAccessor147.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25 )
at java.lang.reflect.Method.invoke(Method.java:
324 )
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:
838 )
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:
1736 )
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:
1646 )
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:
1274 )
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:
1835 )
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:
1759 )
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:
1646 )
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:
1274 )
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:
1835 )
at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:
452 )
at com.fr.report.CellElement.readObject(Unknown Source)

当使用Java 2D来为报表生成字体对象时,开发人员放了一个native的static锁在"initialize"方法上,不过这是sun JDK 1.4中才会出现的,在JDK 5.0中,这个static lock就消失了。

在Java SE 5.0中使用lock free的数据结构

在Java中,"synchronized"关键字是一个较简单、并且相对来说比较好用的协作机制,不过同时对于管理一个简单的操作(例如增加统计值或更新一个值)来说就显得比较重量级了,就像以下的代码:

Code list 4:

public   class  OnlineNumber {
    
private   int  totalNumber;
    
public   synchronized   int  getTotalNumber() {  return  totalNumber; }
    
public   synchronized   int  increment() {  return   ++ totalNumber; }
    
public   synchronized   int  decrement() {  return   -- totalNumber; }
}

以上的代码只是用来锁定非常简单的操作,"synchronized"块也是非常的短。但是锁是非常重量级(当锁被其他线程持有时,线程会去频繁尝试获取锁)的,吞吐量会下降,并且同步锁的竞争也是很昂贵的。

幸运的是,在Java SE 5.0或以上版本,你可以在不使用native代码的情况下使用硬件级同步语义的wait-free、lock-free的算法。几乎所有现代的处理器都具有检测和防止其他处理器并发修改变量的基础设施。这些基础设施称为比较并交换,或CAS。

一个CAS操作包含三个参数 -- 一个内存地址,期待的旧的值以及新的值。 如果内存地址上的值和所期待的旧的值是同一个的话,处理器将此地址的值更新为新的值;否则它就什么都不做,同时它会返回CAS操作前内存地址上的值。一个使用CAS来实现同步的例子如下:

Code list 5:

public   int  increment() {
    
int  oldValue  =  value.getValue();
    
int  newValue  =  oldValue  +   1 ;
    
while  (value.compareAndSwap(oldValue, newValue)  !=  oldValue)
       oldValue 
=  value.getValue();
    
return  oldValue  +   1 ;
}

首先,我们从地址上读取一个值,然后执行几步操作来产生新的值(例子中只是做加1的操作),最后使用CAS方式来将地址中的旧值改变为新值。如果在时间片段内地址上的值未改变,那么CAS操作将成功。如果另外的线程同时修改了地址上的值,那么CAS操作将失败,但会检测到这个操作失败,并在while循环中进行重试。CAS最好的原因在于它是硬件级别的实现并且非常轻量级,如果100个线程同时执行这个increment()方法,最糟糕的情况是在 increment方法执行完毕前每个线程最多尝试99次。

在Java SE 5.0和以上版本的java.util.concurrent.atomic包中提供了在单个变量上lock-free和线程安全操作支持的类。这些原子变量的类都提供了比较和交换的原语,它基于各种平台上可用的最后的native的方式实现,这个包内提供了九种原子变量,包括:AtomicInteger;AtomicLong;AtomicReference;AtomicBoolean;array forms of atomic integer、long、reference;和atomic marked reference和stamped reference类。

使用atomic包非常容易,重写上面code list 5的代码片段:

Code list 6:

import  java.util.concurrent.atomic. * ;
.

private  AtomicInteger value  =   new  AtomicInteger( 0 );
public   int  increment() {
    
return  value.getAndIncrement();
}
.
几乎java.util.concurrent包中所有的类都直接或间接的采用了原子变量来替代synchronized。像
ConcurrentLinkedQueue采用了原子变量来直接实现wait-free算法,而像ConcurrentHashMap则采用
ReentrantLock来实现必要的锁,而ReentrantLock则是采用原子变量来维护所有等待锁的线程队列。

在我们实验室中一个最成功的关于lock free算法的案例发生在一个金融系统中,当将"Vector"数据结构替换为"ConcurrentHashMap"后,在我们的CMT机器(8核)性能提升了超过3倍。

竞争条件也会导致可扩展性出现问题

太多的"synchronized"关键字会导致可扩展性出现问题。但在某些场合,缺少"synchronized"也会导致系统无法垂直扩展。缺少"synchronized"会产生竞争场景,在这种场景下允许两个线程同时修改共享的资源,这有可能会造成破坏共享数据,为什么我说它会导致可扩展性出现问题呢?

来看一个实际的例子。这是一个制作业的ERP系统,当在我们最新的一台CMT服务器(2CPU、16核、128芯)上进行性能测试时,我们发现CPU的使用率超过90%,这非常让人惊讶,因为很少有应用能够在这款机器上扩展的这么好。但我们仅仅兴奋了5分钟,之后我们发现平均响应时间非常的慢,同时吞吐量也降到不可思议的低。那么这些CPU都在干嘛呢?它们不是在忙吗,那么它们到底在忙些什么呢?通过OS的跟踪工具,我们发现几乎所有的CPU都在干同一件事-- "HashMap.get()",看起来所有的CPU都进入了死循环,之后我们在不同数量的CPU的服务器上再测试了这个应用,结果表明,服务器拥有越多CPU,那么产生死循环的概率就会越高。

产生这个死循环的根源在于对一个未保护的共享变量 -- 一个"HashMap"数据结构的操作。当在所有操作的方法上加了"synchronized"后,一切恢复了正常。检查"HashMap"(Java SE 5.0)的源码,我们发现有潜在的破坏其内部结构最终造成死循环的可能。在下面的代码中,如果我们使得HashMap中的entries进入循环,那么"e.next()"永远都不会为null。

Code list 7:

public  V get(Object key) {
    
if  (key  ==   null return  getForNullKey();
    
int  hash  =  hash(key.hashCode());
    
for  (Entry < K,V >  e  =  table[indexFor(hash, table.length)];
         e 
!=   null ;
         e 
=  e.next) {
         Object k;
         
if  (e.hash  ==  hash  &&  ((k  =  e.key)  ==  key  ||  key.equals(k)))
            
return  e.value;
    }
    
return   null ;
}

不仅get()方法会这样,put()以及其他对外暴露的方法都会有这个风险,这算jvm的bug吗?应该说不是的,这个现象很早以前就报告出来了(详细见:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6423457)。Sun的工程师并不认为这是bug,而是建议在这样的场景下应采用"ConcurrentHashMap",在构建可扩展的系统时应将这点纳入规范中。

非阻塞 IO vs. 阻塞IO

Java 1.4中引入的java.nio包,允许开发人员在进行数据处理时获取更好的性能并提供更好的扩展性。NIO提供的非阻塞IO操作允许java应用像其他底层语言(例如c)一样操作IO。目前已经有很多NIO的框架(例如Apache的Mina、Sun的Grizzly)了被广泛的使用在很多的项目和产品中。

在最近的5个月内,我们实验室有两个Java EE项目测试对比了基于传统的阻塞I/O构建的服务器和非阻塞I/O构建的服务器上的性能。他们选择了Tomcat 5作为基于阻塞I/O的服务器,Glassfish作为基于非阻塞I/O的服务器。

首先,他们测试了一些简单的JSP页面和servlets,得到如下结果:(在一台4 CPU的服务器上)

Concurrent Users
Average Response Time (ms)
Tomcat
Glassfish
5
30
138
15
35
142
30
37
142
50
41
151
100
65
155


从测试结果来看,Glassfish的性能远低于Tomcat。客户对非阻塞I/O能够带来的提升表示怀疑,但为什么那么多的文章以及技术报告都告诉大家NIO具备更好的性能和可扩展性呢?

当在更多的场景进行测试后,随着NIO的能力逐步的展现出来,他们改变了观点,他们做了以下的测试:

1、比简单的JSP、servlet更为复杂的场景,包括EJB、数据库、文件IO、JMS和事务;

2、模拟更多的并发用户,从1000到10000;

3、在不同的硬件环境上进行测试,从2 CPU、4 CPU到16 CPU。

以下的图为在4 CPU服务器上的测试结果:

Figure 1: Throughput in a 4CPU server

传统的阻塞I/O为每个请求分配一个工作线程,这个工作线程负责请求的整个过程的处理,包括从网络读取请求数据、解析参数、计算或调用其他的业务逻辑、编码结果并将其返回给请求者,然后这个线程将返回到线程池中供其他线程复用。Tomcat 5采用的这种方式在应对完美的网络环境、简单的逻辑以及小量的并发用户时是非常高效的。

但如果请求包括了复杂的逻辑、或需要和外部的系统(例如文件系统、数据库或消息服务器)进行交互时,工作线程在其处理的大部分时间都会处于等待同步的调用或网络传输返回的状态中,这个阻塞的线程会被请求持有直到请求处理完毕,但操作系统需要暂停线程来保证CPU能够处理其他的请求,如果客户端和服务器端的网络状况不太好的话,网络的延时会导致线程被阻塞更长时间,在更糟的状况下,当需要keep-alive的话,当前的工作线程会在请求处理完毕后阻塞很长一段时间,在这样的情况下,为了更好的使用CPU,就必须增加更多的工作线程了。

Tomcat采用了一个线程池,每个请求都会被线程池中一个空闲的线程进行处理。"maxThreads"表示Tomcat 能创建的处理请求的最大线程数。如果我们把"maxThreads"设置的太小的话,就不能充分的使用CPU了,更为重要的是,随着并发用户的增长,会有很多请求被服务器抛弃和拒绝。在此次测试中,我们将"maxThreads"设置为了1000(这对于Tomcat来说有些太大了),在这样的设置下,当并发用户增长到较高数量时,Tomcat会创建很多的线程。大量的Java线程会导致JVM和OS忙于执行和维护这些线程,而不是执行业务逻辑处理,同时,太多的线程也会消耗更多的JVM heap内存(每个线程堆栈需要占用一些内存),并且会导致更为频繁的gc。

Glassfish不需要这么多的线程,在非阻塞IO中,一个工作线程并不会绑定到一个特定的请求上,如果请求被某些原因所阻塞,那么这个线程将被其他的请求复用。在这样的方式下,Glassfish可以用几十个工作线程来处理几千的并发用户。通过限制线程资源,非阻塞IO拥有了更好的可扩展性,这也是Tomcat 6采用非阻塞IO的原因了。

Figure 2: scalability test result


单线程任务问题

几个月前我们实验室测试了一个基于Java EE的ERP系统,它其中的一个测试场景是为了产生非常复杂的分析报告,我们在不同的服务器上测试了这个应用场景,发现竟然是在最便宜的AMD PC服务器上拥有最好的性能。这台AMD的服务器只有两个2.8HZ的CPU以及4G的内存,但它的性能竟然超过了昂贵的拥有8 CPU和32G内存的SPARC服务器。

原因就在于这个场景是个单线程的任务,它同时只能被一个用户运行(并发的多用户执行在这个案例中毫无意义),因此当运行时它只使用了一个CPU,这样的任务是没法扩展到多个处理器的,在大多数时候,这种场景下的性能仅取决于CPU的运行速度。

并行是解决这个问题的方案。为了让一个单线程的任务并行执行,你需要按顺序找出这个操作的过程中从某种程度上来讲不依赖的操作,然后采用多线程从而实现并行。在上面的案例中,客户重新定义了"分析报告产生"的任务,改为先生成月度报告,之后基于产生的这些12个月的月度报告来生成分析报告,由于最终用户并不需要“月度报告”,因此这些“月度报告”只是临时产生的结果,但"月度报告"是可以并行生成的,然后用于快速的产生最后的分析报告,在这样的方式下,这个应用场景可以很好的扩展到4 CPU的SPARC服务器上运行,并且在性能上比在AMD Server高80%多。

重新调整架构和重写代码的解决方案是一个耗时并且容易出现错误的工作。在我们实验室中的一个项目中采用了JOMP来为其单线程的任务获得并行性。JOMP是一个基于线程的SMP并行编程的Java API。就像OpenMP,JOMP也是根据编译指示来插入并行运行的代码片段到常规的程序中。在Java程序中,JOMP 通过//omp这样的指示方式来表示需要并行运行的部分。JOMP程序通过运行一个预编译器来处理这些//omp的指示并生成最终的java代码,这些 java代码再被正常的编译和执行。JOMP支持OpenMP的大部分特性,包括共享的并行循环和并行片段,共享变量,thread local变量以及reduction变量。以下的代码为JOMP程序的示例:

Code list 8:

Li n k e dLi s t c  =   new  Li n k e dLi s t ( ) ;
c . add ( 
"  t h i s  "  ) ;
c . add ( 
"  i s  "  ) ;
c . add ( 
"  a  "  ) ;
c . add ( 
" demo "  ) ;
/   /  #omp p a r a l l e l i t e r a t o r
f o r ( S t r i n g s : c )
    System . o u t . p r i n t l n ( 
"  s  "  ) ;

就像大部分的并行编译器,JOMP也是关注于loop-level和集合的并行运算,研究如何同时执行不同的迭代。为了并行化,两个迭代之间不能产生任何的数据依赖,这也就是说,不能依赖于其他任何一个执行后产生的计算结果。要编写一个JOMP程序并不是容易的事。首先,你必须熟练使用OpenMP的指示,同时还得熟悉JVM对于这些指示的内存模型映射,最后你需要知道在你的业务逻辑代码的正确的地方放置正确的指示。

另外一个选择是采用Parallel Java。Parallel Java,就像JOMP一样,也支持OpenMP的大部分特性;但又不同于JOMP,PJ的并行结构部分是通过在代码中调用PJ的类来实现,而不是通过插入预编译的指示,因此,"Parallel Java"不需要另外的预编译过程。Parallel Java不仅对于在多CPU上并行有效,对于多节点的扩展能力上也同样有效。以下的代码是"Parallel Java"程序的示例:

Code list 9:

static   double [][] d;
new  ParallelTeam().execute ( new  ParallelRegion()
    {
    
public   void  run()  throws  Exception
        {
        
for  ( int  ii  =   0 ; ii  <  n;  ++  ii)
            {
            
final   int  i  =  ii;
            execute (
0 , n - 1 new  IntegerForLoop()
                {
                    
public   void  run ( int  first,  int  last)
                        {
                        
for  ( int  r  =  first; r  <=  last;  ++  r)
                           {
                           
for  ( int  c  =   0 ; c  <  n;  ++  c)
                                {
                                d[r][c] 
=  Math.min (d[r][c],
                                d[r][i] 
+  d[i][c]);
                                }
                            }
                        }
                    });
                }
            }
        });

扩展使用更多的内存

内存是应用的重要资源。足够的内存对于任何应用而言都是关键的,尤其是数据库系统和其他I/O操作频繁的系统。更多的内存意味着更大的共享内存空间以及更大的数据缓冲,这也就使得应用能够更多的从内存中读取数据而不是缓慢的磁盘中读取。

Java gc将程序员从繁琐的内存分配和回收中解脱了出来,从而使得程序员能够更加高效的编写代码。但gc不好的地方在于当gc运行时,几乎所有工作的线程都会被挂起。另外,在gc环境下,程序员缺少调度CPU来回收那些不再使用的对象的控制能力。对于那些几乎实时的系统而言,例如电信系统和股票交易系统,这种延迟和缺少控制的现象是很大的风险。

回到Java应用在給予更多的内存时是否可以扩展的问题上,答案是有些时候是的。太小的内存会导致gc频繁的执行,足够的内存则保证JVM花费更多的时间来执行业务逻辑,而不是进行gc。

但它并不一定是这样的,在我们实验室中出现的真实例子是一个构建在64位JVM上的电信系统。使用64位JVM,应用可以突破32位JVM中4GB内存的限制,测试时使用的是一台4 CPU/16G内存的服务器,其中12GB的内存分配给了java应用使用,为了提高性能,他们在初始化时就缓存了超过3,000,000个的对象到内存中,以免在运行时创建如此多的对象。这个产品在第一个小时的测试中运行的非常快,但突然,系统差不多停止运行了30多分钟,经过检测,发现是因为gc导致了系统停止了半个小时。

gc是从那些不再被引用的对象回收内存的过程。不被引用的对象是指应用中不再使用的对象,因为所有对于这些对象的引用都已经不在应用的范围中了。如果一堆巨大的活动的对象存在在内存中(就像3,000,000个缓存的对象),gc需要花费很长的时间来检查这些对象,这就是为什么系统停止了如此长乃至不可接受的时间。

在我们实验室中测试过的以内存为中心的Java应用中,我们发现具备有如下特征:

1、每个请求的处理过程需要大量和复杂的对象;
2、在每个会话的HttpSession对象中保存了太多的对象;
3、HttpSession的timeout时间设置的太长,并且HttpSession没有显示的invalidated;
4、线程池、EJB池或其他对象池设置的太大;
5、对象的缓存设置的太大。

这样的应用是不好做扩展的,当并发的用户数增长时,这些应用所使用的内存也会大幅度的增长。如果大量的活动对象无法被及时的回收,JVM将会在gc上消耗很长的时间,另外,如果給予了太大的内存(在64位JVM上),在运行了相对较长的时间后,jvm会花费相当长的一段时间在 gc上,因此结论是如果给jvm分配了太多的内存的话,java应用将不可扩展。在大部分场合下,给jvm分配3G内存(通过"-Xmx"属性)是足够 (在windows和linux中,32位的系统最多只能分配2G的内存)的。如果你拥有更多的内存,请将这些内存分配给其他的应用,或者就将它留给OS 使用,许多OS都会使用空闲的内存来作为数据的缓冲和缓存来提升IO性能。实时JVM(JSR001)可以让开发人员来控制内存的回收,应用基于此特性可以告诉JVM:“这个巨大的内存空间是我的缓存,我将自己来管理它,请不要自动对它进行回收”,这个功能特性使得Java应用也能够扩展来支持大量的内存资源,希望JVM的提供者们能将这个特性在不久的将来带入到免费的JVM版本中。

为了扩展这些以内存为中心的java应用,你需要多个jvm实例或者多台机器节点。

其他垂直扩展的问题

有些Java EE应用的扩展性问题并不在于其本身,有些时候外部系统的限制会成为系统扩展能力的瓶颈,这些瓶颈可能包括:

  • 数据库系统:这在企业应用和web 2.0应用中是最常见的瓶颈,因为数据库通常是jvm线程中共享的资源。因此数据库执行的效率、数据库事务隔离的级别将会很明显的影响系统的扩展能力。我 们看到很多的项目将大部分的业务逻辑以存储过程的方式放在数据库中,而web层则非常的轻量,只是用来执行下数据的过滤等,这样的架构在随着请求数的增长 后会出现很多的扩展性问题。
  • 磁盘IO和网络IO。
  • 操作系统:有些时候系统扩展能力的瓶颈可能会出现在操作系统的限制上,例如,在同一个目录下放了太多的文件,导致文件系统在创建和查找文件时变得非常的慢;
  • 同步logging:这是一个可扩展性的常见问题。在有些案例中,可以通过采用Apache log4j来解决,或者采用jms消息来将同步的logging转为异步执行。

这些不仅仅是Java EE应用的问题,对于所有平台的所有系统而言同样如此。为了解决这些问题,需要从系统的各个层面来从数据库管理员、系统工程师和网络分析人员处得到帮助。

这篇文章的第二个部分将来探讨水平扩展的问题。


JSP的Request session的有效时间应合理设置

发布时间:2008年07月08日 作者:BlueDavy

阅读次数:29次 类别:我的文章 永久链接 Trackback 
之前写了个简单的jsp做压力测试,没想到出现的一个问题是当压力比较大的情况,运行比较久的话会出现一个现象,就是jvm的内存几乎被耗尽,用jprofiler查看会发现是有一个ConcurrentHashMap对象的内存一直在增长,而且没有释放的迹象,随后进入Debug模式,跟踪查找都有谁new了ConcurrentHashMap,因为测试场景中是个非常简单的jsp页面,发现只有jsp的Request session会创建这个ConcurrentHashMap,很久没写jsp了,猜测是request session的默认超时时间太长,所以导致高压力下(200并发,总共连续访问50万次,jvm内存1G)会出现内存一直没有回收的问题,后来打印了一下request session的默认超时(AS是jboss 4.2.2),是半小时,如果这样的话确实是会有造成上面内存一直被占用的现象。
这个jsp是这样的,非常简单:
<% @ page contentType = " text/html;charset=GBK "   %>
Just Test.
造成这个问题的具体原因需要结合session的机制来讲了,讲session机制的文章相当多,感兴趣的话可以参见以下两篇文章:
http://www.builder.com.cn/2007/1123/650439.shtml
http://bbs.chinaunix.net/viewthread.php?tid=1178466

这个问题怎么说呢,只能说session用的时候要非常注意吧,毕竟是消耗内存资源的,而且通常session都不会去配置成即时invalidate,都是有一定的超时时间的,在这样的情况下,如果在超时时间范围内出现高访问量的话,是很有可能会造成问题的,因此超时时间的合理设置非常重要。

ps: 好久没玩过jsp和session了,确实没太注意这种状况,对于熟悉jsp和session机制的人来说也许这是常识
了,^_^,见谅见谅。
如果页面上不需要session的话,可以这么做:
<%@ page session="false" %>

网侠大会上所做的OSGi简介的PPT

发布时间:2008年05月28日 作者:BlueDavy

阅读次数:220次 类别:OSGi、SCA 永久链接 Trackback 
网侠大会上所做的OSGi简介的PPT 由于Topic的时间有限,因此此篇PPT只是简单的对OSGi进行了介绍和演示,而没有做详细的OSGi使用的讲解,可能让参与这次Topic的同学们失望了,不过还是在此把PPT公开出来了,如感兴趣的话,可以从以下地址下载:
http://www.riawork.org/opentopic/Simple.Introduction.For.OSGi.ppt

此PPT的大纲如下:
OSGi概述
在OSGi概述中,首先介绍了下OSGi从成立之初到现在的一个发展轨迹;之后讲解了下OSGi到底是什么东西;最后介绍了下谁在用OSGi。
OSGi应用演示
启动后看到的是一个简单的留言板应用,只有留言板列表,其他什么功能都没有;
现在我们想给它添加一个新增留言的功能,做法是从目前的Bundle库中下载新增留言的Bundle,然后安装此Bundle并启动,刷新留言板列表页面,就看到新增留言功能被动态的添加上去了。
这个演示比较简单,但比较充分的反应了基于OSGi应用的即插即用的特性。
OSGi的未来
根据最近OSGi的发展,可以看出OSGi的未来是非常的光明,在这里我简单的对OSGi的未来发展做了一些畅想,到了明年的时候来验证下这些畅想是否都成为了现实,:)。
进一步学习
鉴于这次的Topic介绍的非常的粗浅,因此对于听过此次Topic后对OSGi产生兴趣的同学们,推荐的一些学习资料。

ps:从最近的整个业界的发展来看,OSGi现在已经逐渐由以前的是否应该采用OSGi的讨论步入到了毫无疑问的采用OSGi的时代了,而相信在不久的将来,也许连OSGi都不需要讨论了,因为它成为了所有java开发人员的必备技能,:)

JavaOne第二天重大新闻:Java 7 will support OSGi

发布时间:2008年05月18日 作者:BlueDavy

阅读次数:126次 类别:OSGi、SCA 永久链接 Trackback 
JavaOne第二天重大新闻:Java 7 will support OSGi
JavaOne的第二天Sun正式官方宣布在Java 7中将支持OSGi:This will allow developers who create applications that use OSGi bundles will be able to run them unmodified in JDK 7.这消息对于知悉OSGi Vs JSR 277的一系列历史战争的人而言绝对是非常的振奋人心,尽管不是说Java 7直接纳用OSGi来实现模块化这一块(这个呢,其实如果JDK做的话,确实可以做的更好,至少可以更高效什么的),但就支持这一点也可看出Sun已经看到了OSGi是事实性的模块化标准,这对于OSGi来说也是里程碑的一天。
Sun在第一天中除了重点推广它的JavaFX外,重点也讲到了Java EE、SE的后续发展目标,我们可以看到其中模块化和可扩展性两点成了新版本的SE和EE希望达到的目标,而在这两点上OSGi提供了很好的范例,可见OSGi中传达的思想的重要性。
其实Sun对OSGi的认可或者说妥协呢,从之前Glassfish V3移植至OSGi框架上运行就差不多能猜出来了。
对于OSGi的熟练使用者而言,这更是一个非常好的消息了,毕竟看到了OSGi得到了又一个认可者,而且应该说也是比较重要的认可者,同时也基本上意味着掌握了一个将来需要学习的技术。
相信到了Java 7流行后,OSGi已经不会成为什么专门的话题来讨论了,因为它已经成了所有Java开发者的日常使用的东西,必备的技能,就像现在在java中直接使用并发包一样。

具体内容可参见:
http://blogs.sun.com/javaone2008/entry/sun_general_technical_session_java

分析JBoss Remoting

发布时间:2008年05月08日 作者:BlueDavy

阅读次数:396次 类别:Java 永久链接 Trackback 
分析JBoss Remoting
Java领域中的分布式框架比较的多,分析一个已有的远程调用框架无论是对于打算采用已有成果还是自己做分布式框架,都是很必要的事情,JBoss Remoting是其中很好很强大的一个框架,在此来对JBoss Remoting进行深入的分析,看看JBoss Remoting是如何基于java.net提供的包去解决这些问题的,本文所分析的JBoss Remoting源码的版本为2.2.2_SP2,本来以为会是篇不怎么长的文档,没想到还没写的详细和深入的时候就已经有三十多页了,也不好在这里直接贴出来,就把文档目录和最后的总结部分贴在这了,感兴趣的同学们可以从这个地址下载PDF版本的文档: http://www.riawork.org/opendoc/JBoss.Remoting.Opendoc.pdf
目录:
1 分布式应用概述
2 分析JBoss Remoting
   2.1 Socket方式远程调用
   2.2 多种调用方式的支持
   2.3 远程加载class的支持
   2.4 高并发下的稳定性
   2.5 异常处理
   2.6 提高性能
   2.7 集群的支持
3 学到了什么
4 总结

总结

从对JBoss Remoting的分析中,分布式应用带来的需要深入学习的知识体系较之集中式的应用多了很多,最基础也最明显的涉及到的知识体系有:网络通讯(涉及到的有协议、网络I/O等)、java网络编程(java.net包、NIO等)、序列化机制、并发编程、池技术等,就这些知识点每个拓展开来讲都可以讲成大篇甚至一本书的范畴,对于大型分布式应用而言,涉及的知识体系就更多了,例如还需要掌握cluster环境下的处理(很多东西到了集群环境下复杂程度绝对是需要以翻倍来计算的,而且很多现在的处理方式都会变得不可用)、load balance策略等,本文也只是对JBoss Remoting的一些基本以及关键的特点进行了分析(最开始的时候打算叫深入分析JBoss Remoting的,不过写到最后发现其实还有很多细节和深入的部分并没有写,因此还是改名叫分析JBoss Remoting了),其中其实还有很多细节是值得研究和学习的,希望有研究的同仁们贡献出其他方面的研究或指出本文错误的地方,非常感谢。

大型的分布式应用中不可能要求每个开发人员都去掌握这些知识体系,因此对于大型分布式应用而言,提供一个分布式的框架是非常有必要的,做到将分布式应用涉及的相关知识点尽量的剥离,就像Erlang,做到将并发的基础知识分离。

JBoss Remoting解决了很多分布式应用所需面对的问题,是目前可选的开源分布式框架中一个很不错的选择,并且其在保证高并发场景下的稳定性和性能提升上也做了很多的工作,但还是有很多可提升的空间,例如统一的远程调用的API上(还可以进一步加强透明化的oneway、异步的调用)、NIO的支持、异步调用的提升(或者可以考虑提供结合MQ实现的异步调用)等等,而如果要成为大型分布式应用的支撑平台,无论是性能上还是功能上,JBoss Remoting还有不少需要改进和提升的地方。

分布式应用较之集中式应用在对象的接口的设计和使用上也有了更高的要求,例如不要出现依靠参数引用传递来隐性的填充一些值、远程对象应是线程安全的、尽量不要出现频繁调用远程对象的现象、尽量减少往返传输大对象的现象等等细节。

分布式应用对比集中式应用而言,无论是开发还是支撑框架上都复杂了很多,因此尽管分布式应用相对集中式应用而言,能够带来机器配置要求降低、系统结构更加清晰和松耦合、降低维护的复杂度等等优点,但还是应该做到能不分布式就尽量不要分布式。

解读OSGi DevCon2008 Topic

发布时间:2008年04月09日 作者:BlueDavy

阅读次数:304次 类别:OSGi、SCA 永久链接 Trackback 
OSGi DevCon2008已经闭幕,迫不及待、非常迫不及待的希望能了解更多此次大会的盛况,不过目前相关的新闻报道等还是比较少的,除了osgi.org/blog上有三四篇报道,根据日程找到目前公开的OSGi DevCon 2008中Topic的PPT,共11个,在此根据自己看这些PPT的情况做个简单的介绍和评价。 1、Android and OSGi: Can they work togther?
      推荐指数  ★★★
      下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=276
      主讲人 BJ Hargrave (CTO of OSGi Alliance)   Neil Bartlett
      这个Topic关注的人好像挺多的,不过我对android不怎么了解,只能在这里简单的说下这个PPT了。
      首先,这个PPT背景和字对比太强了,看起来挺累的,:(
      不过PPT的内容还是不错的,至少前几页就已经让我初步的知道了android是个怎么样的东西。
      这个PPT属于一个实践性很强的PPT,清楚的介绍了如何改造Equinox这样的OSGi实现,使得它能够应用在android中,PPT中详细的介绍了改造的方法以及原因,所以还是很值得一看的。
      由于对android不够了解,我不好评价这两者的合并各会得到什么优点,等有机会了解了android后再对这个话题来做分析。
2、BUG: A Customizable Hardware and Software Platform Using Linux,Java and OSGi
      推荐指数 ★★
      下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=349
      主讲人 Ken Gilmer(Bug Labs)    Angel Roman(Bug Labs)
      这个Topic并不属于自己很关注的,不过既然有PPT,还是简单看了下。
      简单来讲,是个基于OSGi的硬件平台,看PPT的意思应该是基于此平台方式开发的硬件是可以直接热插拔的,这个热插拔的概念是类似连PCI卡那些都是可以的,而且Bundle中的OSGi service还可以以webservice的方式对外提供调用,这样说来,也就可以在外部调ws来操作硬件了。
3、Everything can be a bundle
      推荐指数 ★★★★★
      下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=145
      主讲人  Erik Wistrand
      这是个非常有意思的PPT,在这个PPT中Topicer介绍了一个可以自动将现有的jar在完全不做任何改动的情况下打包为OSGi Bundle的方式,如果没做过OSGi应用而只是简单玩过OSGi Framework的话,也许你不会觉得这有什么难的,这个过程中会有些什么问题在PPT中讲的还是比较详细的,主要是classloader方面的问题,一个很典型的,在以前正常的情况下,用当前thread的contextClassloader能取到的class到了OSGi后就不一定能取到了,还有像Class.forName这种就更了,在以往要将使用了这些方式加载class的jar改造为OSGi bundle,还是需要花费一番精力,而这篇PPT中给出的一个方法呢,看着让我差点惊讶的不行,它是直接采用asm动态的分析bytecode,然后替换从而做到的,例如它发现有使用Class.forName的,就会将这里改为使用此工具提供的一个classloader来加载,PPT中最后举了个例子,来把一个传统的jedit jar直接就做成了OSGi Bundle,相当的帅,:)
4、Modular web applications based on OSGi
      推荐指数 ★★★
      下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=244
      主讲人  Jochen Hiller
      这个Topic是列入了关注的Topic系列的,不过发现PPT中其实没讲什么,我开始以为会讲如何做一个modular web application,看PPT才发现原来只是讲OSGi的HttpService不够强(因为是servlet 2.1 spec的),所以自己做了个WebapplicationService来支持web.xml这样的方式,当然,这个在实际使用中还是很有帮助的,不过后面还会提到另外一个,貌似这东西和现在OSGi RFC 66的东西有重叠了。
5、Spring-DM Tutorial
      推荐指数 ★★★★★
      下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=495
      主讲人 Adrian Colyer (CTO of SpringSource)   Martin Lippert     BJ Hargrave
      这个Topic也是列入了关注的Topic系列的,不过主要是因为这是Spring-DM的第一次公开宣讲,PPT写的相当的厚,99页,my god,由于我之前使用过了Spring-DM,因此也就只是粗略的浏览了下这个PPT,只能说这个PPT很适合Spring-DM的初学者看,如果已经用过的人的话估计从这个PPT中学习不到多少东西。
      本来是想从这个PPT中看看有没有考虑采用Spring-DM后打算支持原有Spring Bean的动态加载的,如果在现场的话,我估计我会问这个话题。
6、Using OSGi Metadata with a standard classloader
      推荐指数 ★★
      下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=383
      主讲人   David Kemper (Principal Architect of TIBCO)
      这篇PPT没讲的很详细,不过意思还是表达出来了,就是在不使用OSGi框架的情况下去使用OSGi的一些metadata,主要是为了实现像package import/export的控制、version的控制,这两个需求在实际的项目中好像确实挺多的,看来Tibco之前考虑OSGi就是这两个原因了。
7、EEG Status Report
      推荐指数 ★★★★★
      下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=131
      主讲人 Eric Newcomer (CTO of IONA)   Tim Diekmann (Siemens)
      这个Topic是列入了关注的Topic系列的,终于是看到了EEG成立一年来都干了什么,咋一看,忒帅了,没想到EEG这一年还真干了不少活,EEG这一年来讨论了很多新的规范,估计这些规范是要列入OSGi将来版本的,其中像OSGi Managment Object Model、Remote Management Architecture,这两个都是我最近也考虑到的东西,还有像OSGi Intergration of  SCA(Part of RFC 119),Distributed OSGi - External Services and Service Discovery(RFC 119),这个我就更关心了,不过目前只有OSGi members才可以看到这些规范,真希望这些规范能尽早的颁布,当然,更希望equinox紧跟这些规范,在颁布时就同时提供实现,:),其他研究中的规范也都是很不错的一些,相当的帅,看来EEG可真不是盖的,虽然进度貌似是有点慢,RFC 119、124、122今年会正式的release,期待着吧...
8、Introducing OSGi technology to existing projects
      推荐指数 ★★
      下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=353
      主讲人 Nils Hofseth Anderson
      这个Topic简单的讲到了为什么要用OSGi,然后就是把现有的projects移植到OSGi会面临哪些挑战,这里面推荐的方法是用maven/ant+bnd来进行改造,当然,这也是一种好的方法,不过还是有很多事情要做的,可以参看下OSGi进阶Opendoc里写的。
9、The OSGi Complete
      推荐指数 ★★★★
      下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=149
      主讲人  Pavlin Dobrev     Stoyan Boshev
      对这个Topic不是非常感兴趣,这个Topic重点在于讲OSGi Services Spec中的几个由Prosyst捐献出来的Service实现:Wire Admin Service、Declarative Services、IO Connector Service、Initial Provisioning,由于这里面只对DS有点兴趣,其他几个service也没怎么去看,如果感兴趣的话可以看看这个PPT,不过这篇PPT主要是讲怎么用,如果是讲怎么实现这些service的话就好了,:)
10、What"s Next for OSGi
        推荐指数 ★★★★★
        下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=371
        主讲人 BJ Hargrave
        这个Topic对于所有OSGi fans来说都是非常关心的,不算失望,至少从PPT中知道大概的进度,在今年年底会颁布OSGi新版规范的草稿,在这个新版规范中,重点会涵盖:DS的提升、Manifest的重写、Service Registry Hooks、Class Loading增强、ServiceException、BundleTracker、并发,当然,对于企业版的规范而言,则会加上Distributed OSGi,这些话题都是现在使用OSGi的人很关心的内容,看来这个新版规范是非常值得期待的。
11、The Virtual OSGi Framework
        推荐指数 ★★★
        下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=86
        主讲人  Jan S. Rellermeyer
        这个Topic是关注的Topic系列中的,翻译PPT才知道原来R-OSGi的service discovery是基于SLP实现的,不过这个Topic需要涵盖的内容实在太多了,PPT上写的都不怎么深入,因此也没学到多少东西,看下来的感觉和基于OSGi实现SCA是非常的像,只是它还具备了SOA Govermance的特性,而且还考虑了load balance情况下的支持,好像真的不错,估计现场听的话可能会好很多。 

总体而言,有次这样的DevCon还是很爽的,实战的、理论的Topic都有,大家各取所好,:),是个很好的交流机会,希望后续Peter Kriens会写更多的关于此次大会的blog。       
还有不少值得关注的Topic没有放上PPT,像Build Secure OSGi Applications、Remotely deploying Equinox with Maynstall等等,无法对其做出评价了,真希望能拿到这次所有Topic的视频,有录音也行,:)。

ps:在这个部分说下自己看到的另外一个感兴趣的Topic
SwordFish
下载地址:http://www.eclipsecon.org/2008/?page=sub/&id=86
SwordFish是一个基于Equinox、JBI和SCASOA实现平台,挺有意思,不过它要等到2008 Q2左右才能看到初步成果,值得关注,因为目前像IBM尽管有了SCA实现,不过也没明确说SCA Component Model那块是不是就是基于Equinox扩展来实现的,当然,Newton是开源的基于OSGi实现的SCA,也是可以研究的。

畅想OSGi DevCon2008

发布时间:2008年03月14日 作者:BlueDavy

阅读次数:194次 类别:OSGi、SCA 永久链接 Trackback 
期待已久的OSGi DevCon 2008将会在下周(3月17日---3月20日)和EclipseCon 2008共同召开,今年OSGi的Topic比去年更多,也占据了更重要的位置,来看看本次大会即将开讲的Topic,畅想畅想,看看哪些Topic将会成为热题。
本届Topic仍然和往年一年,分为Long Talks、Tutorials、Short Talks、Panel和Additional OSGi Talks,本届OSGi DevCon可谓是众星云集,世界级的OSGi大师们共聚一堂,毫无疑问将给我们这些OSGi Fans们贡献一顿大餐。
Long Talks方面Topic的选择可谓是用心良苦,几乎涵盖了整个软件领域:企业应用、手机应用和嵌入式应用,对于我个人而言,其中最值得关注的Topic是:
1、How To Build Large Scale Enterprise Applications Using OSGi
      这个Topic从名字就会吸引很多人了,毕竟以目前OSGi的规范而言,在企业应用领域的支持方面还是有很多的不足,但从这篇实战性质的Topic中应该能学习到不少东西。
2、The Virtual OSGi Framework
      这个Topic由去年主讲R-OSGi的人来讲,无疑去年的R-OSGi已经吸引了众多人的眼球,尽管一年来还是没去好好研究R-OSGi,不过还是非常关注OSGi在分布式领域的应用的,毕竟这和我现在所从事的也非常相关,而今年的这篇Topic无疑更加的吸引人,如果说R-OSGi完成了一个基于OSGi的分布式应用框架的话,那么今年这个Topic就是要讲解基于R-OSGi实现的一个可管理、可监控的分布式应用框架了,又流行的词说就是具备了govermance能力,这也是SOA中最难实现的一个目标,同时如何在分布式应用中也仍然保持OSGi的那些特性,这也是很值得学习的,来我们来期望这个Topic能给我们带来点实际的东西。
Tutorials则基本就回到了企业应用领域了,不过还是会挺精彩的,值得期待的有:
1、Building Secure OSGi Applications
      安全性这方面是插件式应用中很关心的一点,尤其是在OSGi中,由于OSGi的插件动态化使得很有可能会直接运行一个不安全的bundle,因此如何起用OSGi本身的security来实现安全控制是很重要的,还有如何在OSGi中使用数字签名bundle。
2、Remotely deploying Equinox with Maynstall
      这个Topic上比较值得期待的是关于如何实现远程部署的部分。
3、Spring DM, formerly called Spring-OSGi
      听听关于Spring DM的正式宣讲,好像改名以来这是第一次这么大场合的公开宣讲,希望能听到些什么,毕竟Spring DM是个非常好的结合。
      也许能让我们看到如何让Spring支持hot deploy,:),那就是件很好的事了,而且如果能在不怎么改动现有project based on spring就实现hot deploy的话,我想这就会是个非常成功的Topic。
4、An introduction to Pax tools for OSGi
      这个Topic中值得关注的无疑就是听听Pax team是怎么讲解把传统project移植到OSGi的(可以看看和我的Opendoc中的做法的不同点),以及基于pax tools支持的传统war的部署。
Short Talks反倒是我认为这次大会中非常精彩的部分,可惜short talk时间太短,only 10 min,如果去参加这次大会的short talk的话,我估计都会有意犹未尽的感觉,来看看short talks的Topic:
1、Enterprise Expert Group Status Report
      这个期待已久了,EEG成立也这么久了,没看到什么动静,真希望这个short talk中能展现出些什么。
2、Modular web applications based on OSGi
      这个话题无疑是个很值得探讨的话题,因为OSGi对于web app的影响就会是web app modular的真正贯彻实施了,它里面有个很有意思的地方就是可以把众多的OSGi bundle组织为一个URI的范畴,尽管从技术上来讲不是太难,不过可以看看他们会采用什么方案来实现这个。
3、How to distribute Spring Dynamic Modules for OSGi with the Newton Project
      这个有点意思,其实这话题也是值得深入探讨探讨的,希望在EclipseCon之后会有这话题的深入探讨的article或blog。
4、What"s next for OSGi?
       这个无疑也是非常值得关注的,看看OSGi接下来能继续带给我们哪些惊喜吧。
Panel是个精彩的论战,几位大师共同论说eclipse extensions Vs DS,这很有意思,我在我以前的blog中也对这个进行过评述,现在的观点仍然是两者适用于不同的应用场景。
在Additional OSGi Talks中,Transactions in Eclipse Based SOA这个Topic很值得关注,从这个Topic也许我们能观摩观摩Topicer会展示一个怎么样的分布式事务的实现方案。

总体看下来,本次大会仍然将会非常的精彩,而必然也会再次让OSGi成为世界的焦点,让我们共同的期待这场盛宴吧。

ps:如果OSGi DevCon所有的Topic都能有录像、录音以及PPT提供就好了。

Java远程通讯可选技术及原理

发布时间:2008年03月06日 作者:BlueDavy

阅读次数:490次 类别:OSGi、SCA 永久链接 Trackback 
在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,这些名词之间到底是些什么关系呢,它们背后到底是基于什么原理实现的呢,了解这些是实现分布式服务框架的基础知识,而如果在性能上有高的要求的话,那深入了解这些技术背后的机制就是必须的了,在这篇blog中我们将来一探究竟,抛砖引玉,欢迎大家提供更多的实现远程通讯的技术和原理的介绍。
基本原理
要实现网络机器间的通讯,首先得来看看计算机系统网络通信的基本原理,在底层层面去看,网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO来实现,其中传输协议比较出名的有http、tcp、udp等等,http、tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输协议,网络IO,主要有bio、nio、aio三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。

应用级协议

远程服务通讯,需要达到的目标是在一台计算机发起请求,另外一台机器在接收到请求后进行相应的处理并将结果返回给请求端,这其中又会有诸如one way request、同步请求、异步请求等等请求方式,按照网络通信原理,需要实现这个需要做的就是将请求转换成流,通过传输协议传输至远端,远端计算机在接收到请求的流后进行处理,处理完毕后将结果转化为流,并通过传输协议返回给调用端。
原理是这样的,但为了应用的方便,业界推出了很多基于此原理之上的应用级的协议,使得大家可以不用去直接操作这么底层的东西,通常应用级的远程通信协议会提供:
1、为了避免直接做流操作这么麻烦,提供一种更加易用或贴合语言的标准传输格式;
2、网络通信机制的实现,就是替你完成了将传输格式转化为流,通过某种传输协议传输至远端计算机,远端计算机在接收到流后转化为传输格式,并进行存储或以某种方式通知远端计算机。
所以在学习应用级的远程通信协议时,我们可以带着这几个问题进行学习:
1、传输的标准格式是什么?
2、怎么样将请求转化为传输的流?
3、怎么接收和处理流?
4、传输协议是?
不过应用级的远程通信协议并不会在传输协议上做什么多大的改进,主要是在流操作方面,让应用层生成流和处理流的这个过程更加的贴合所使用的语言或标准,至于传输协议则通常都是可选的,在java领域中知名的有:RMI、XML-RPC、Binary-RPC、SOAP、CORBA、JMS,来具体的看看这些远程通信的应用级协议:
--------------------------------------------------------------------------------------------------------------------------------------------------
RMI

RMI是个典型的为java定制的远程通信协议,我们都知道,在single vm中,我们可以通过直接调用java object instance来实现通信,那么在远程通信时,如果也能按照这种方式当然是最好了,这种远程通信的机制成为RPC(Remote Procedure Call),RMI正是朝着这个目标而诞生的。
来看下基于RMI的一次完整的远程通信过程的原理:
1、客户端发起请求,请求转交至RMI客户端的stub类;
2、stub类将请求的接口、方法、参数等信息进行序列化;
3、基于socket将序列化后的流传输至服务器端;
4、服务器端接收到流后转发至相应的skelton类;
5、skelton类将请求的信息反序列化后调用实际的处理类;
6、处理类处理完毕后将结果返回给skelton类;
7、Skelton类将结果序列化,通过socket将流传送给客户端的stub;
8、stub在接收到流后反序列化,将反序列化后的Java Object返回给调用者。
来看jboss-remoting对于此过程的一个更好的图示:

根据原理来回答下之前学习应用级协议带着的几个问题:
1、传输的标准格式是什么?
      是Java ObjectStream。
2、怎么样将请求转化为传输的流?
      基于Java串行化机制将请求的java object信息转化为流。
3、怎么接收和处理流?
      根据采用的协议启动相应的监听端口,当有流进入后基于Java串行化机制将流进行反序列化,并根据RMI协议获取到相应的处理对象信息,进行调用并处理,处理完毕后的结果同样基于java串行化机制进行返回。
4、传输协议是?
      Socket。
--------------------------------------------------------------------------------------------------------------------------------------------------
XML-RPC
XML-RPC也是一种和RMI类似的远程调用的协议,它和RMI的不同之处在于它以标准的xml格式来定义请求的信息(请求的对象、方法、参数等),这样的好处是什么呢,就是在跨语言通讯的时候也可以使用。
来看下XML-RPC协议的一次远程通信过程:
1、客户端发起请求,按照XML-RPC协议将请求信息进行填充;
2、填充完毕后将xml转化为流,通过传输协议进行传输;
3、接收到在接收到流后转换为xml,按照XML-RPC协议获取请求的信息并进行处理;
4、处理完毕后将结果按照XML-RPC协议写入xml中并返回。
图示以上过程:

同样来回答问题:
1、传输的标准格式是?
      标准格式的XML。
2、怎么样将请求转化为传输的流?
      将XML转化为流。
3、怎么接收和处理流?
      通过监听的端口获取到请求的流,转化为XML,并根据协议获取请求的信息,进行处理并将结果写入XML中返回。
4、传输协议是?
      Http。
--------------------------------------------------------------------------------------------------------------------------------------------------
Binary-RPC
Binary-RPC看名字就知道和XML-RPC是差不多的了,不同之处仅在于传输的标准格式由XML转为了二进制的格式。
同样来回答问题:
1、传输的标准格式是?
      标准格式的二进制文件。
2、怎么样将请求转化为传输的流?
      将二进制格式文件转化为流。
3、怎么接收和处理流?
      通过监听的端口获取到请求的流,转化为二进制文件,根据协议获取请求的信息,进行处理并将结果写入XML中返回。
4、传输协议是?
      Http。
--------------------------------------------------------------------------------------------------------------------------------------------------
SOAP
SOAP原意为Simple Object Access Protocol,是一个用于分布式环境的、轻量级的、基于XML进行信息交换的通信协议,可以认为SOAP是XML RPC的高级版,两者的原理完全相同,都是http+XML,不同的仅在于两者定义的XML规范不同,SOAP也是Webservice采用的服务调用协议标准,因此在此就不多加阐述了。
--------------------------------------------------------------------------------------------------------------------------------------------------
CORBA
Common Object Request Broker Architecture(公用对象请求代理[调度]程序体系结构),是一组用来定义“分布式对象系统”的标准,由OMG(Object Menagement Group)作为发起和标准制定单位。CORBA的目的是定义一套协议,符合这个协议的对象可以互相交互,不论它们是用什么样的语言写的,不论它们运行于什么样的机器和操作系统。
CORBA在我看来是个类似于SOA的体系架构,涵盖可选的远程通信协议,但其本身不能列入通信协议这里来讲,而且CORBA基本淘汰,再加上对CORBA也不怎么懂,在此就不进行阐述了。
--------------------------------------------------------------------------------------------------------------------------------------------------
JMS
JMS呢,是实现java领域远程通信的一种手段和方法,基于JMS实现远程通信时和RPC是不同的,虽然可以做到RPC的效果,但因为不是从协议级别定义的,因此我们不认为JMS是个RPC协议,但它确实是个远程通信协议,在其他的语言体系中也存在着类似JMS的东西,可以统一的将这类机制称为消息机制,而消息机制呢,通常是高并发、分布式领域推荐的一种通信机制,这里的主要一个问题是容错(详细见ErLang论文)。
来看JMS中的一次远程通信的过程:
1、客户端将请求转化为符合JMS规定的Message;
2、通过JMS API将Message放入JMS Queue或Topic中;
3、如为JMS Queue,则发送中相应的目标Queue中,如为Topic,则发送给订阅了此Topic的JMS Queue。
4、处理端则通过轮训JMS Queue,来获取消息,接收到消息后根据JMS协议来解析Message并处理。
回答问题:
1、传输的标准格式是?
      JMS规定的Message。
2、怎么样将请求转化为传输的流?
      将参数信息放入Message中即可。
3、怎么接收和处理流?
      轮训JMS Queue来接收Message,接收到后进行处理,处理完毕后仍然是以Message的方式放入Queue中发送或Multicast。
4、传输协议是?
      不限。
基于JMS也是常用的实现远程异步调用的方法之一。

可选实现技术
当然,在上面的原理中并没有介绍到所有的java领域可选的远程通信协议了,例如还有EJB采用的ORMI、Spring自己定义的一个简单的Http Invoker等等。
看完原理后我们再来看看目前java领域可用于实现远程通讯的框架或library,知名的有:JBoss-Remoting、Spring-Remoting、Hessian、Burlap、XFire(Axis)、ActiveMQ、Mina、Mule、EJB3等等,来对每种做个简单的介绍和评价,其实呢,要做分布式服务框架,这些东西都是要有非常深刻的了解的,因为分布式服务框架其实是包含了解决分布式领域以及应用层面领域两方面问题的。
当然,你也可以自己根据远程网络通信原理(transport protocol+Net IO)去实现自己的通讯框架或library。
那么在了解这些远程通讯的框架或library时,会带着什么问题去学习呢?
1、是基于什么协议实现的?
2、怎么发起请求?
3、怎么将请求转化为符合协议的格式的?
4、使用什么传输协议传输?
5、响应端基于什么机制来接收请求?
6、怎么将流还原为传输格式的?
7、处理完毕后怎么回应?
--------------------------------------------------------------------------------------------------------------------------------------------------
JBoss-Remoting
Jboss-remoting是由jboss编写的一个java领域的远程通讯框架,基于此框架,可以很简单的实现基于多种传输协议的java对象的RPC。
直接来回答问题:
1、是基于什么协议实现的?
      JBoss-Remoting是个通讯框架,因此它支持多种协议方式的通信,例如纯粹的socket+io方式、rmi方式、http+io方式等。
2、怎么发起请求?
      在JBoss-Remoting中,只需将需要发起的请求参数对象传入jboss-remoting的InvocationRequest对象即可,也可根据协议基于InvocationRequest封装符合需求的InvocationRequest对象。
3、怎么将请求转化为符合协议的格式的?
      JBoss-Remoting基于Java串行化机制或JBoss自己的串行化实现来将请求转化为对象字节流。
4、使用什么传输协议传输?
      支持多种传输协议,例如socket、http等。
5、响应端基于什么机制来接收请求?
      响应端只需将自己的处理对象注册到JBoss-Remoting提供的server端的Connector对象中即可。
6、怎么将流还原为传输格式的?
      JBoss-Remoting基于java串行化机制或jboss自己的串行化实现来将请求信息还原为java对象。
7、处理完毕后怎么回应?
      处理完毕后将结果对象直接返回即可,jboss-remoting会将此对象按照协议进行序列化,返回至调用端。
另外,jboss-remoting支持多种通信方式,例如同步/异步/单向通信等。

--------------------------------------------------------------------------------------------------------------------------------------------------
Spring-Remoting
Spring-remoting是Spring提供java领域的远程通讯框架,基于此框架,同样也可以很简单的将普通的spring bean以某种远程协议的方式来发布,同样也可以配置spring bean为远程调用的bean。
1、是基于什么协议实现的?
      和JBoss-Remoting一样,作为一个远程通讯的框架,Spring通过集成多种远程通讯的library,从而实现了对多种协议的支持,例如rmi、http+io、xml-rpc、binary-rpc等。
2、怎么发起请求?
      在Spring中,由于其对于远程调用的bean采用的是proxy实现,发起请求完全是通过服务接口调用的方式。
3、怎么将请求转化为符合协议的格式的?
      Spring按照协议方式将请求的对象信息转化为流,例如Spring Http Invoker是基于Spring自己定义的一个协议来实现的,传输协议上采用的为http,请求信息是基于java串行化机制转化为流进行传输。
4、使用什么传输协议传输?
      支持多种传输协议,例如rmi、http等等。
5、响应端基于什么机制来接收请求?
      响应端遵循协议方式来接收请求,对于使用者而言,则只需通过spring的配置方式将普通的spring bean配置为响应端或者说提供服务端。
6、怎么将流还原为传输格式的?
      按照协议方式来进行还原。
7、处理完毕后怎么回应?
      处理完毕后直接返回即可,spring-remoting将根据协议方式来做相应的序列化。

--------------------------------------------------------------------------------------------------------------------------------------------------
Hessian
Hessian是由caucho提供的一个基于binary-RPC实现的远程通讯library。
1、是基于什么协议实现的?
      基于Binary-RPC协议实现。
2、怎么发起请求?
      需通过Hessian本身提供的API来发起请求。
3、怎么将请求转化为符合协议的格式的?
      Hessian通过其自定义的串行化机制将请求信息进行序列化,产生二进制流。
4、使用什么传输协议传输?
      Hessian基于Http协议进行传输。
5、响应端基于什么机制来接收请求?
      响应端根据Hessian提供的API来接收请求。
6、怎么将流还原为传输格式的?
      Hessian根据其私有的串行化机制来将请求信息进行反序列化,传递给使用者时已是相应的请求信息对象了。
7、处理完毕后怎么回应?
      处理完毕后直接返回,hessian将结果对象进行序列化,传输至调用端。

--------------------------------------------------------------------------------------------------------------------------------------------------
Burlap
Burlap也是有caucho提供,它和hessian的不同在于,它是基于XML-RPC协议的。
1、是基于什么协议实现的?
      基于XML-RPC协议实现。
2、怎么发起请求?
      根据Burlap提供的API。
3、怎么将请求转化为符合协议的格式的?
      将请求信息转化为符合协议的XML格式,转化为流进行传输。
4、使用什么传输协议传输?
      Http协议。
5、响应端基于什么机制来接收请求?
      监听Http请求。
6、怎么将流还原为传输格式的?
      根据XML-RPC协议进行还原。
7、处理完毕后怎么回应?
      返回结果写入XML中,由Burlap返回至调用端。

--------------------------------------------------------------------------------------------------------------------------------------------------
XFire、Axis
XFire、Axis是Webservice的实现框架,WebService可算是一个完整的SOA架构实现标准了,因此采用XFire、Axis这些也就意味着是采用webservice方式了。
1、是基于什么协议实现的?
      基于SOAP协议。
2、怎么发起请求?
      获取到远端service的proxy后直接调用。
3、怎么将请求转化为符合协议的格式的?
      将请求信息转化为遵循SOAP协议的XML格式,由框架转化为流进行传输。
4、使用什么传输协议传输?
      Http协议。
5、响应端基于什么机制来接收请求?
      监听Http请求。
6、怎么将流还原为传输格式的?
      根据SOAP协议进行还原。
7、处理完毕后怎么回应?
      返回结果写入XML中,由框架返回至调用端。

--------------------------------------------------------------------------------------------------------------------------------------------------
ActiveMQ
ActiveMQ是JMS的实现,基于JMS这类消息机制实现远程通讯是一种不错的选择,毕竟消息机制本身的功能使得基于它可以很容易的去实现同步/异步/单向调用等,而且消息机制从容错角度上来说也是个不错的选择,这是Erlang能够做到容错的重要基础。
1、是基于什么协议实现的?
      基于JMS协议。
2、怎么发起请求?
      遵循JMS API发起请求。
3、怎么将请求转化为符合协议的格式的?
      不太清楚,猜想应该是二进制流。
4、使用什么传输协议传输?
      支持多种传输协议,例如socket、http等等。
5、响应端基于什么机制来接收请求?
      监听符合协议的端口。
6、怎么将流还原为传输格式的?
      同问题3。
7、处理完毕后怎么回应?
      遵循JMS API生成消息,并写入JMS Queue中。
基于JMS此类机制实现远程通讯的例子有Spring-Intergration、Mule、Lingo等等。

--------------------------------------------------------------------------------------------------------------------------------------------------
Mina
Mina是Apache提供的通讯框架,在之前一直没有提到网络IO这块,之前提及的框架或library基本都是基于BIO的,而Mina是采用NIO的,NIO在并发量增长时对比BIO而言会有明显的性能提升,而java性能的提升,与其NIO这块与OS的紧密结合是有不小的关系的。
1、是基于什么协议实现的?
      基于纯粹的Socket+NIO。
2、怎么发起请求?
      通过Mina提供的Client API。
3、怎么将请求转化为符合协议的格式的?
      Mina遵循java串行化机制对请求对象进行序列化。
4、使用什么传输协议传输?
      支持多种传输协议,例如socket、http等等。
5、响应端基于什么机制来接收请求?
      以NIO的方式监听协议端口。
6、怎么将流还原为传输格式的?
      遵循java串行化机制对请求对象进行反序列化。
7、处理完毕后怎么回应?
      遵循Mina API进行返回。
MINA是NIO方式的,因此支持异步调用是毫无悬念的。

--------------------------------------------------------------------------------------------------------------------------------------------------
EJB
EJB最突出的在于其分布式,EJB采用的是ORMI协议,和RMI协议是差不多的,但EJB在分布式通讯的安全控制、transport pool、smart proxy等方面的突出使得其在分布式领域是不可忽视的力量。
1、是基于什么协议实现的?
      基于ORMI协议。
2、怎么发起请求?
      EJB调用。
3、怎么将请求转化为符合协议的格式的?
      遵循java串行化机制对请求对象进行序列化。
4、使用什么传输协议传输?
      Socket。
5、响应端基于什么机制来接收请求?
      监听协议端口。
6、怎么将流还原为传输格式的?
      遵循java串行化机制对请求对象进行反序列化。
7、处理完毕后怎么回应?
      直接返回处理对象即可。

在之前的分布式服务框架系列的文章中对于jndi有误导的嫌疑,在这篇blog中也顺带的提下jndi的机制,由于JNDI取决于具体的实现,在这里只能是讲解下jboss的jndi的实现了。
在将对象实例绑定到jboss jnp server后,当远程端采用context.lookup()方式获取远程对象实例并开始调用时,jboss jndi的实现方法是从jnp server上获取对象实例,将其序列化回本地,然后在本地进行反序列化,之后在本地进行类调用。
通过这个机制,就可以知道了,本地其实是必须有绑定到jboss上的对象实例的class的,否则反序列化的时候肯定就失败了,而远程通讯需要做到的是在远程执行某动作,并获取到相应的结果,可见纯粹基于JNDI是无法实现远程通讯的。
但JNDI也是实现分布式服务框架一个很关键的技术点,因为可以通过它来实现透明化的远端和本地调用,就像ejb,另外它也是个很好的隐藏实际部署机制(就像datasource)等的方案。

总结
由上一系列的分析可知,在远程通讯领域中,涉及的知识点还是相当的多的,例如有:通信协议(Socket/tcp/http/udp/rmi/xml-rpc etc.)、消息机制、网络IO(BIO/NIO/AIO)、MultiThread、本地调用与远程调用的透明化方案(涉及java classloader、Dynamic Proxy、Unit Test etc.)、异步与同步调用、网络通信处理机制(自动重连、广播、异常、池处理等等)、Java Serialization (各种协议的私有序列化机制等)、各种框架的实现原理(传输格式、如何将传输格式转化为流的、如何将请求信息转化为传输格式的、如何接收流的、如何将流还原为传输格式的等等),要精通其中的哪些东西,得根据实际需求来决定了,只有在了解了原理的情况下才能很容易的做出选择,甚至可以根据需求做私有的远程通讯协议,对于从事分布式服务平台或开发较大型的分布式应用的人而言,我觉得至少上面提及的知识点是需要比较了解的。

参考文档(感谢这些文章)
RMI原理及实现:http://www.yesky.com/274/1625274.shtml
Java NIO原理和使用:http://www.jdon.com/concurrent/nio%D4%AD%C0%ED%D3%A6%D3%C3.htm
XML RPC协议:http://hedong.3322.org/archives/000470.html
                             http://www.mengyan.org/blog/archives/2005/07/12/30.html
Spring技术应用中的远程服务详解:http://www.builder.com.cn/2007/1027/583384.shtml
JAVA RPC通信机制之SOAP:http://www.java114.com/content16/content3826.html
Java Remoting:Protocol BenchMarks:http://q.sohu.com/forum/5/topic/1148909
Evalution of RMI Alternative:https://www.jfire.org/modules/phpwiki/index.php/Evaluation%20of%20RMI%20Alternative
Metaprotocol Taxonomy:http://hessian.caucho.com/doc/metaprotocol-taxonomy.xtp
什么是Webservice:http://www.vchome.net/dotnet/webservice/webservice15.htm

服务接口+版本+属性注册/查找机制

发布时间:2008年02月18日 作者:BlueDavy

阅读次数:212次 类别:OSGi、SCA 永久链接 Trackback 
在使用Spring的时候,我们习惯于用bean的名称作为注册、查找的条件,这也就意味着bean的引用是唯一的了,而不能来查找、注入一系列具备相同功能但不同实现的bean,这种应用的场景其实还是很多的,尤其在扩展的场景中,在这篇blog中以一个应用场景来说明下这种需求,顺便也宣传下OSGi的服务接口+版本+属性的注册和查找机制。


以将Spring bean发布为DSF服务的bean来讲,这个bean需要做到根据发布DSF服务的方式,调用相应的具体发布DSF服务的实现bean,同时要做到的自然是在以后增加了新的发布方式后,不需要修改这个bean的代码。
要实现这个需求,首先想到的是这样的解决方案:
1、在这个bean中直接注入所有的发布DSF服务实现的bean,在调用的时候可以根据规则寻找到相应的bean,这个在Spring中能够实现的方式也许是这样:
      <bean id="DSFServiceExporterFactory" class="将Spring bean发布为DSF服务的bean">
              <property name="exporterClassMap">
                     <map>
                                   <entry key="jndi"><ref bean="以JNDI方式发布DSF服务端的bean"></entry>
                                   <entry key="webservice"><ref bean="以Webservice方式发布DSF服务端的bean"></entry>
                     </map>
              </property>
      </bean>

      这是一种实现方式了,这样当以后增加了新的发布方式后,通过增加bean的定义以及修改这里map里面的东西就可以了。
2、第二种实现方法就很常见了,就是写个properties文件,配置各种发布方式具体对应的实现类。
但这两种方法都不够的优雅,它们都有个共同的特点,就是需要去维护一个共同的配置的地方,想象中最好的解决方法是类似这样的配置:
<bean id="DSFServiceExporterFactory" class="将Spring bean发布为DSF服务的bean">
              <property name="exporterClasses">
                      <ref bean="cn.bluedavy.dsf.exporter.*">                     
              </property>
</bean>
这样就把所有的cn.bluedavy.dsf.exporter.开头的bean都注入到exporterClasses里了,当然,在根据具体的协议查找实现类时,又得提供一种支持方法了,好,不再去自己瞎琢磨了,来看看如果是OSGi的服务模型的话,会怎么样去实现这样的东西:
在OSGi中每个对外提供的service都以接口来定义,在上面的场景中,很明显,自然会出现的一个现象就是如果多个服务实现同样的接口的话怎么去找到自己要的那个服务呢,OSGi中多数采用的方法是为这个服务加上一个属性标识,在查找服务时就可以通过接口,再加上属性标识来查找到想要的服务了,又或者可以只查找接口,获取到所有实现这类接口的服务。
按照这样的模型的话上面的场景就很容易实现了,写个模拟的配置文件:
<osgi:reference id="exporterClasses" interface="cn.bluedavy.dsf.exporter.DSFExporter"/>
<bean id="DSFServiceExporterFactory" class="将Spring bean发布为DSF服务的bean">
              <property name="exporterClasses">
                      <ref bean="exporterClasses">                     
              </property>
</bean>
这个和真实的会有些不同,但意思是差不多的,:),而OSGi的服务呢,在注入后可以直接通过ServiceReference这个对象来获取到这个服务的相关属性信息,这样其实也就可以做到类似之前第一个解决方案的效果,但同时又解决了不需要维护统一文件的问题。
服务模型的这种语义性质的机制使得服务的概念表达得更为清楚,使用得也更为方便了,以接口来表达服务的功能是一种非常合理的方式,同时辅以属性来描述实现此功能的方式(有点像元数据里的keyword)无疑更加全面的表达了此服务, 也使得使用者能够更合理的选择需要的服务实现。
版本机制在这篇blog中没有提及,这个我想在实际的过程中大家应该都深受版本混淆的痛苦了,:),在这里不多描述了,服务模型目的无疑就是为了更加清晰的表达一个功能,并且让使用者更加方便的查找、使用所需要的功能。

基于Spring-DM实现分布式服务框架(DSF)(二)

发布时间:2008年02月13日 作者:BlueDavy

阅读次数:301次 类别:OSGi、SCA 永久链接 Trackback 1条评论
在上篇分析完了在V 0.7需要干的活后,开始细化其中的实现细节,由于技术细节和之前想的有点不同,在细化的同时也稍做了调整,系统的架构仍然保持不变,在这篇blog中来看看实现每项任务的技术细节,之后就可以进入编码实现阶段了。 1、服务模型
      采用OSGi的服务模型,在Spring中使用此服务模型时和Spring-DM中的osgi:service、osgi:reference基本一致,示例如下:
      发布服务(将bulletinListAction以jndi的方式发布为dsf服务): 
< dsf:service  id ="BulletinListCommandService"  version ="1.0"  ref ="bulletinListAction"  
  interface
="cn.org.osgi.xwork.action.IAction" >
   
< dsf:service-properties >
    
< prop  key ="command" > LIST </ prop >
   
</ dsf:service-properties >
      
<!-- 以jndi的方式对外发布 -->  
      
< dsf:jndi />
  
</ dsf:service >

     引用服务(引用dsf服务):

< dsf:reference  id ="extensionRegistry"  interface ="org.eclipse.core.runtime.IExtensionRegistry"  version ="[1.0,2,0)"  cardinality ="1..x"  retries ="3"  timeout ="5000" >
        
< dsf:service-properties >
         
< prop  key ="command" > LIST </ prop >
       
</ dsf:service-properties >
           <!--以jndi的方式调用远程服务-->
           <dsf:jndi/>
     
</ dsf:reference >

    关于服务模型所支持的所有spring的配置在之后我会公布相应的xsd文件。
2、服务中心
      在查询了memcachedb的相关资料后,感觉目前它的java接口好像还不太好用,决定直接采用memcached,自己来实现存储,由于服务模型信息的数据其实非常的小,而且维护改动的频率并不是那么的高,暂时采用文件方式直接存储,服务中心在注册时将文件存储在共享的空间中,之后将文件解析为服务模型对象,放入memcached,当有更新、删除动作时同样做相应的处理,文件存储以及memcached交互都是比较容易的事,将文件解析为服务模型对象采用xstream完成。
      服务中心基于Spring-DM、Webwork-OSGi简单实现。  
3、发布服务
      使用方法已经在上面示例了,具体实现步骤则为:
      提供扩展的spring xml namespace的支持;
      编写DSFJNDIExporter class,基于Spring的JNDITemplate实现将spring bean注册到JNDI的过程(要求为在本地启动jndi server,默认采用jboss jnp)。

4、调用服务
      这个部分之前分析错误,之前忽略了服务应用端是不知道目标服务的地址的,需要通过分布式缓存查询才可得知,因此不是直接采用Spring的JNDIObjectFactoryBean就可以实现的,需要编写自己的DSFObjectFactoryBean,具体实现步骤为:
      提供扩展的spring xml namespace的支持;
      编写DSFObjectFactoryBean class,由这个bean来负责调用分布式缓存,查询目标服务地址,由于目前只有JNDI方式,在获取到目标服务地址后仍然通过Spring的JNDIObjectFactoryBean完成剩余工作。
经过上面四步技术实现细节的分析,可以来编码完成V 0.7的实现了。
  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值