WebX基于OSGi改造的可行性报告-WebX基于OSGi的改造demo
From Tuscany中文社区
目录 |
[编辑] 思路
Spring framework提供了osgi方案,叫做dynamic module,这个方案提供了一个基于osgi内核、osgi bundle模式的ioc模式的应用。spring的强大的IoC特性通过dynamic module(以下称spring-osgi)在osgi之上得到了很好的应用。spring-osgi的构成分为两个关键部分:一是osgi bundle的manifest.mf内容,增加Spring-Context:标记,其后的值指向一个spring的xml文件;另外,spring的xml文件中增加了支持osgi的扩展标记;通过扩展标记将bean发布为osgi服务;通过扩展标记引用来自不同的bundle的服务。与Spring原来的IoC相比,OSGi的服务的加载被分割到每个bundle中,并且其加载顺序并不由spring决定,所以spring- osgi通过listener克服了这种无序加载所带来的困难,也就是说,当开发者声明引用一个服务的时候,spring-osgi实际上仅仅是为引用方增加了一个listener,当被引用服务“出现”的时候,它才被真正引用近来。这个listener会“检查”每个服务,并用指定的java接口类型进行匹配,匹配成功的,就是目标/被引用服务。
在传统的struts1和Spring集成中,包含了一个plugin标签和一个controller标签,这两个标签实现了Struts1和Spring的集成。plugin标签使用的类是由spring提供的(org.springframework.web.struts.ContextLoaderPlugIn),这个类负责加载指定的spring xml(资源)文件;controller标签使用的类也是由Spring提供的(org.springframework.web.struts.DelegatingRequestProcessor),这个类负责将原来对Action的引用转为对spring bean的引用。这个controller是struts1集成spring的核心。在osgi模式下struts1与spring集成中,与传统集成的最重要区别是:没有了servlet context里面的那个ApplicationContext,所以我们面临的问题是:创建我们自己的controller,由它来负责获得ApplicationContext,并从中“定位”所需的Action对象。spring-osgi不但兼容传统spring的所有特性,同时还提供了一个BundleContextAware接口,它与ApplictionContextAware接口的用途相近,使开发者可以在spring-osgi模式下,获得OSGi的BundleContext指针。我们将原来的服务层设计为OSGi服务,并不需要对原来的类作任何修改,并且,这种“服务化”将由spring-osgi替我们做。Action仍然作为普通的spring bean存在。我们就可以通过spring-osgi的IoC将OSGi服务“注入”Action对象中。
默认情况下Hibernate通过hibernate.cfg.xml中的mapping resource来加载归入SessionFactory中管理的PO(Persistent Object),在加载时Hibernate通过Hibernate类所在的 classloader加载Mapping resource中配置的hbm映射文件,并通过cglib将hbm映射文件中指定的class生成proxy。改为基于OSGi后产生冲突的地方就在于PO分散到不同的工程中去了,同时要保持模块化的封装性,就不能所有人都来修改封装 Hibernate模块里的hibernate.cfg.xml;另外一个冲突点在于封装Hibernate的模块的classloader无法加载到位于其他模块的PO对象,只要解决了这两个冲突,基于OSGi使用Hibernate的问题就解决了。要解决这两个冲突,需要解决的就是如何将其他模块的PO注册到封装Hibernate的模块中,以及Hibernate的Configuration如何加载其他模块的PO和映射文件。基于OSGi的可扩展性,要将其他模块的PO注册到封装Hibernate的模块中,通过扩展点方式即可实现,我们注意到Hibernate的Configuration.addClass加载class的方式可达到和mapping resource完全一样的效果,扩展点中需要的仅为po的classname。
[编辑] 改造点
[编辑] Struts
[编辑] 标签定义文件
由于使用的所有的其它的类库都是通过bundle的方式访问,并且访问不到其它bundle的META-INF下的tld文件,所以需要把用到的下列文件中的tld文件拷贝到webroot\WEB-INF下面:standard-1.1.2.jar、struts-1.2.9.jar、extremecomponents-1.0.1.jar。
[编辑] 创建controller
创建我们自己的controller,由它来负责获得ApplicationContext,并从中“定位”所需的Action对象。
并且在struts-conf.xml文件中(不需要plugin部分),把controller的改为我们增加的controller类。
[编辑] Spring
[编辑] 创建MyContextAware
我们创建类MyContextAware来加载各种资源。它实现接口:BundleContextAware和ApplicationContextAware。由spring-osgi来调用方法setBundleContext,在这个方法中,初始化并调用我们创建的内嵌类HttpServiceTracker,它继承类ServiceTracker,并且在方法addingService中来加载各种资源:struts配置文件、参数配置、jsp资源等等。
[编辑] Bean配置
和传统的配置一样,spring-osgi也支持bean的配置,把action、service、dao等都可以配置成bean,除此之外,也可以把bean对外暴露成service,也可以把引用其它bundle提供的service引入成bean,供本bundle使用。注意配置文件的xmlns一定要包括osgi的schema,如下所示:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-1.0-rc2.xsd">
spring-osgi提供了两种加载bean配置的方法:
- 在目录META-INF下面创建spring目录,它会加载这个目录里面的所有项目了文件;
- 在META-INF\MANIFEST.MF里增加一行配置,后面为bean的配置文件,如下:
Spring-Context: config/springBeans.xml
[编辑] Hibernate
[编辑] 用于数据库连接的bundle
需要专门增加这样一个bundle,它的主要作用是提供整个应用的数据库连接,提供其它bundle增加hibernate支持的方法,并且向外暴露service:sessionFactory,其它bundle只要引用此service就可以进行数据库的相关操作了。
[编辑] 增加扩展点
在此bundle中增加一个扩展点,这个扩展点里只有一个属性:类名。系统要通过这个类名找到此类的hbm文件并加载到数据库连接工厂中。
[编辑] 实现我们的SessionFactoryBean
扩展点增加后,我们需要在运行期获取到此扩展点的所有扩展,并且把扩展的类加载到SessionFactory中,因此我们继承了Spring的LocalSessionFactoryBean类,为了监听bundle注册的改变,我们实现IRegistryChangeListener接口。
获取到扩展以后,需要把class加入到Hibernate的Configuration中,OSGi中每个Bundle都是独立的ClassLoader,那也就是说Hibernate所在的这个Bundle是无法加载到扩展点中 className对应的类的,所幸的是IExtension的IConfigurationElement提供了初始化扩展中class的方法,也就是说可以实现在扩展点的模块中直接创建提供扩展的模块中的类。那么什么时候应用这些扩展点呢?我们通过重载LocalSessionFactoryBean的postProcessConfiguration方法,这个时候我们把扩展点中的类的实例加入SessionFactory的管理范围内。
上面的实现方式解决了静态时Hibernate加载其他模块中的PO初始化SessionFactory的问题,但记住要保持好基于OSGi的系统的动态性的特征,要保持动态化,就得监听Hibernate模块这个扩展点的扩展实现的变化情况,当系统中增加了新的需要归入SessionFactory管理的PO时,需要动态的加入到目前的SessionFactory中,当已有的PO从系统中删除时,也需要动态的将其从目前的SessionFactory中删除,不过Hibernate的SessionFactory是不支持动态的删除和增加其中管理的PO的,当发生变化时,只能重新初始化SessionFactory了。所以我们实现了IRegistryChangeListener中的registryChanged 方法。
按照上面的步骤完成后,Hibernate会根据映射文件中的classname去实例化该Class,而不是直接使用我们通过 Configuration.addClass时传递给Hibernate的Class,直接使用Class.forName的方法实例化扩展出的PO会出错,因为Hibernate类所在的ClassLoader和扩展的类所在的ClassLoader并不同,碰到这样的情况,就得使用OSGi提供给Bundle的DynamicImport-Package了,使用此属性OSGi就可以在运行期动态地为这个Bundle获取其他Bundle Export的Package,使用这种方法的话要求PO所在的Bundle将PO的Package Export。
[编辑] 需要数据库连接的bundle
扩展上面定义的扩展点,把类加入到sessionFactory的管理范围中。注意:hbm映射定义文件名字必须是:类名.hbm.xml,否则系统找不到hbm映射文件。
[编辑] Acegi
[编辑] Acegi安全系统介绍
Acegi是Spring Framework 下的安全系统,它提供了强大灵活的企业级安全服务,如完善的认证和授权机制,Http资源访问控制,Method 调用访问控制,Access Control List (ACL) 基于对象实例的访问控制,Yale Central Authentication Service (CAS) 耶鲁单点登陆,X509 认证,当前所有流行容器的认证适配器,Channel Security频道安全管理等功能。
- 基于Servlet Filter和Spring aop, 使商业逻辑和安全逻辑分开,结构更清晰
- 使用Spring 来代理对象,能方便地保护方法调用
[编辑] Acegi可以进行多方面的安全控制粒度
- URL 资源访问控制
http://apps:8080/index.htm -> for public http://apps:8080/user.htm -> for authorized user
- 方法调用访问控制
public void getData() -> all user public void modifyData() -> supervisor only
- 对象实例保护
order.getValue() < $100 -> all user order.getValue() > $100 -> supervisor only
[编辑] Acegi的配置
[编辑] 在Web.xml中的配置
- FilterToBeanProxy
Acegi通过实现了Filter接口的FilterToBeanProxy提供一种特殊的使用Servlet Filter的方式,它委托Spring中的Bean -- FilterChainProxy来完成过滤功能,这好处是简化了web.xml的配置,并且充分利用了Spring IOC的优势。FilterChainProxy包含了处理认证过程的filter列表,每个filter都有各自的功能。
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
- filter-mapping
<filter-mapping>限定了FilterToBeanProxy的URL匹配模式,只有*.do和*.jsp 和/j_acegi_security_check 的请求才会受到权限控制,对javascript,css等不限制。
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/j_acegi_security_check</url-pattern>
</filter-mapping>
- HttpSessionEventPublisher
<listener>的HttpSessionEventPublisher用于发布HttpSessionApplicationEvents和HttpSessionDestroyedEvent事件给spring的applicationcontext。
<listener>
<listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>
</listener>
[编辑] 在applicationContext-acegi-security.xml中的配置
[编辑] FILTER CHAIN
FilterChainProxy会按顺序来调用这些filter,使这些filter能享用Spring ioc的功能, CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON定义了url比较前先转为小写, PATTERN_TYPE_APACHE_ANT定义了使用Apache ant的匹配模式。
[编辑] 基础认证
[编辑] authenticationManager
起到认证管理的作用,它将验证的功能委托给多个Provider,并通过遍历Providers, 以保证获取不同来源的身份认证,若某个Provider能成功确认当前用户的身份,authenticate()方法会返回一个完整的包含用户授权信息的Authentication对象,否则会抛出一个AuthenticationException。
Acegi提供了不同的AuthenticationProvider的实现,如:
- DaoAuthenticationProvider 从数据库中读取用户信息验证身份
- AnonymousAuthenticationProvider 匿名用户身份认证
- RememberMeAuthenticationProvider 已存cookie中的用户信息身份认证
- AuthByAdapterProvider 使用容器的适配器验证身份
- CasAuthenticationProvider 根据Yale中心认证服务验证身份,用于实现单点登陆
- JaasAuthenticationProvider 从JASS登陆配置中获取用户信息验证身份
- RemoteAuthenticationProvider 根据远程服务验证用户身份
- RunAsImplAuthenticationProvider 对身份已被管理器替换的用户进行验证
- X509AuthenticationProvider 从X509认证中获取用户信息验证身份
- TestingAuthenticationProvider 单元测试时使用
每个认证者会对自己指定的证明信息进行认证,如DaoAuthenticationProvider仅对UsernamePasswordAuthenticationToken这个证明信息进行认证。
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<ref local="anonymousAuthenticationProvider"/>
<ref local="rememberMeAuthenticationProvider"/>
</list>
</property>
</bean>
[编辑] daoAuthenticationProvider
进行简单的基于数据库的身份验证。DaoAuthenticationProvider获取数据库中的账号密码并进行匹配,若成功则在通过用户身份的同时返回一个包含授权信息的Authentication对象,否则身份验证失败,抛出一个AuthenticatiionException。
<bean id="daoAuthenticationProvider"
class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="jdbcDaoImpl"/>
<property name="userCache" ref="userCache"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>
[编辑] passwordEncoder
使用加密器对用户输入的明文进行加密。Acegi提供了三种加密器:
- PlaintextPasswordEncoder—默认,不加密,返回明文.
- ShaPasswordEncoder—哈希算法(SHA)加密
- Md5PasswordEncoder—消息摘要(MD5)加密
<bean id="passwordEncoder"
class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>
[编辑] jdbcDaoImpl
用于在数据中获取用户信息。 acegi提供了用户及授权的表结构,但是您也可以自己来实现。通过usersByUsernameQuery这个SQL得到你的(用户ID,密码,状态信息);通过authoritiesByUsernameQuery这个SQL得到你的(用户ID,授权信息)
<bean id="jdbcDaoImpl"
class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
<property name="usersByUsernameQuery">
<value>select loginid,passwd,1 from users where loginid = ?</value>
</property>
<property name="authoritiesByUsernameQuery">
<value>select u.loginid,p.name
from users u,roles r,permissions p,user_role ur,role_permis rp
where u.id=ur.user_id and r.id=ur.role_id
and p.id=rp.permis_id
and r.id=rp.role_id and p.status='1'
and u.loginid=?</value>
</property>
</bean>
[编辑] userCache & resourceCache
缓存用户和资源相对应的权限信息。每当请求一个受保护资源时,daoAuthenticationProvider就会被调用以获取用户授权信息。如果每次都从数据库获取的话,那代价很高,对于不常改变的用户和资源信息来说,最好是把相关授权信息缓存起来。
userCache提供了两种实现: NullUserCache和EhCacheBasedUserCache, NullUserCache实际上就是不进行任何缓存,EhCacheBasedUserCache是使用Ehcache来实现缓功能。
<bean id="userCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager"/>
<property name="cacheName" value="userCache"/>
</bean>
<bean id="userCache"
class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"
autowire="byName">
<property name="cache" ref="userCacheBackend"/>
</bean>
<bean id="resourceCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager"/>
<property name="cacheName" value="resourceCache"/>
</bean>
<bean id="resourceCache"
class="org.springside.modules.security.service.acegi.cache.ResourceCache"
autowire="byName">
<property name="cache" ref="resourceCacheBackend"/>
</bean>
[编辑] basicProcessingFilter
用于处理HTTP头的认证信息,如从Spring远程协议(如Hessian和Burlap)或普通的浏览器如IE,Navigator的HTTP头中获取用户信息,将他们转交给通过authenticationManager属性装配的认证管理器。如果认证成功,会将一个Authentication对象放到会话中,否则,如果认证失败,会将控制转交给认证入口点(通过authenticationEntryPoint属性装配)
<bean id="basicProcessingFilter"
class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint"/>
</bean>
[编辑] basicProcessingFilterEntryPoint
通过向浏览器发送一个HTTP401(未授权)消息,提示用户登录。处理基于HTTP的授权过程, 在当验证过程出现异常后的"去向",通常实现转向、在response里加入error信息等功能。
<bean id="basicProcessingFilterEntryPoint"
class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
<property name="realmName" value="SpringSide Realm"/>
</bean>
[编辑] authenticationProcessingFilterEntryPoint
当抛出AccessDeniedException时,将用户重定向到登录界面。属性loginFormUrl配置了一个登录表单的URL,当需要用户登录时,authenticationProcessingFilterEntryPoint会将用户重定向到该URL。
<bean id="authenticationProcessingFilterEntryPoint"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl">
<value>/security/login.jsp</value>
</property>
<property name="forceHttps" value="false"/>
</bean>
[编辑] HTTP安全请求
[编辑] httpSessionContextIntegrationFilter
每次request前 HttpSessionContextIntegrationFilter从Session中获取Authentication对象,在request完后, 又把Authentication对象保存到Session中供下次request使用,此filter必须其他Acegi filter前使用,使之能跨越多个请求。
<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
</bean>
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
[编辑] httpRequestAccessDecisionManager
经过投票机制来决定是否可以访问某一资源(URL或方法)。allowIfAllAbstainDecisions为false时如果有一个或以上的decisionVoters投票通过,则授权通过。可选的决策机制有ConsensusBased和UnanimousBased
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
[编辑] roleVoter
必须是以rolePrefix设定的value开头的权限才能进行投票,如AUTH_ , ROLE_ 必须是以rolePrefix设定的value开头的权限才能进行投票,如AUTH_ , ROLE_
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter"> <property name="rolePrefix" value="AUTH_"/> </bean>
[编辑] exceptionTranslationFilter
异常转换过滤器,主要是处理AccessDeniedException和AuthenticationException,将给每个异常找到合适的"去向"
<bean id="exceptionTranslationFilter"
class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint"
ref="authenticationProcessingFilterEntryPoint"/>
</bean>
[编辑] authenticationProcessingFilter
和servlet spec差不多,处理登陆请求.当身份验证成功时,AuthenticationProcessingFilter会在会话中放置一个Authentication对象,并且重定向到登录成功页面
authenticationFailureUrl定义登陆失败时转向的页面
defaultTargetUrl定义登陆成功时转向的页面
filterProcessesUrl定义登陆请求的页面
rememberMeServices用于在验证成功后添加cookie信息
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureUrl">
<value>/security/login.jsp?login_error=1</value>
</property>
<property name="defaultTargetUrl">
<value>/admin/index.jsp</value>
</property>
<property name="filterProcessesUrl">
<value>/j_acegi_security_check</value>
</property>
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
[编辑] filterInvocationInterceptor
在执行转向url前检查objectDefinitionSource中设定的用户权限信息。首先,objectDefinitionSource中定义了访问URL需要的属性信息(这里的属性信息仅仅是标志,告诉accessDecisionManager要用哪些voter来投票)。然后,authenticationManager掉用自己的provider来对用户的认证信息进行校验。最后,有投票者根据用户持有认证和访问url需要的属性,调用自己的voter来投票,决定是否允许访问。
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager"
ref="httpRequestAccessDecisionManager"/>
<property name="objectDefinitionSource" ref="filterDefinitionSource"/>
</bean>
[编辑] filterDefinitionSource
自定义DBFilterInvocationDefinitionSource从数据库和cache中读取保护资源及其需要的访问权限信息
<bean id="filterDefinitionSource"
class="org.springside.modules.security.service.acegi.DBFilterInvocationDefinitionSource">
<property name="convertUrlToLowercaseBeforeComparison" value="true"/>
<property name="useAntPath" value="true"/>
<property name="acegiCacheManager" ref="acegiCacheManager"/>
</bean>
[编辑] 方法调用安全控制
[编辑] methodSecurityInterceptor
在执行方法前进行拦截,检查用户权限信息
[编辑] methodDefinitionSource
自定义MethodDefinitionSource从cache中读取权限
<bean id="methodSecurityInterceptor"
class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager"
ref="httpRequestAccessDecisionManager"/>
<property name="objectDefinitionSource" ref="methodDefinitionSource"/>
</bean>
<bean id="methodDefinitionSource"
class="org.springside.modules.security.service.acegi.DBMethodDefinitionSource">
<property name="acegiCacheManager" ref="acegiCacheManager"/>
</bean>
[编辑] OSGi中Acegi的配置
虽然Acegi 没有要求必须使用Spring Framework,但事实上Acegi很大程度上利用了Spring的IOC和AOP,很难脱离Spring的单独使用。因此Acegi的配置是在Spring-osgi基础上的。
[编辑] 其它
[编辑] 引用的类库
系统引用的类库都要做成bundle,因为如果一个jar文件分别出现在两个bundle时,系统就会出现错误。
- 有些类库官方已经提供了bundle,这些可以直接使用;
- 有些类库官方没有提供bundle,而第三方把它打包成bundle,这样的也可以直接使用;
- 其它的,官方和第三方都没有提供bundle,对于这些类库就需要我们把它打包成bundle。
[编辑] web.xml
删除webroot\WEB-INF下的classes和lib目录,并且修改web.xml文件,删除web-app元素内的所有内容。
[编辑] demo的结构
在这个demo中我们做了5个bundle,分别是:
- book.hibernate:提供统一的数据库连接服务
- book.common:提供公用的、常用的工具类
- book.web:提供web接入服务
- book.service:提供实际的业务逻辑
- book.dao:提供持久化的服务
它们间的关系如下图:
上图是我们按照WebX样例中最简单的情况做的,在实际的应用中可能会很复杂。
下图是我们建议的结构,web接入层的bundle可以为一个也可以拆解成多个,图中有3个service的bundle和3个dao的bundle,在实际的应用中也可以把service和dao合成一个bundle。三个dao的bundle都享用book.hibernate提供的数据库连接服务,这些web、service和dao的bundle会用到common的bundle提供的公共的、工具服务。
[编辑] 结果
根据WebX框架所提供的例子,去掉acegi和Hibernate这两部分,保留Spring、Struts这两部分,进行基于OSGi的改造,对比修改前后的性能情况。主要测试WebX基于Tomcat5容器、OSGi基于Equinos框架的性能情况。
我们测试的环境如下:
- 服务器配置
| IP | 211.157.98.87 |
| CPU | Intel(R) Xeon(TM) CPU 2.80GHz*2 |
| 内存 | 4G |
| 网络设备 | 百兆网卡 |
| 操作系统 | SUSE Linux Enterprise Server 10 (i586) |
| 其它软件 | Tomcat5.5.25,MySQL5.0.37 |
| OSGi环境 | Equinox 3.3.1 |
- 测试机器配置
| IP | |
| CPU | Intel(R) Pentium(R) CPU 3.0GHz |
| 内存 | 1G |
| 网络设备 | 百兆网卡 |
| 操作系统 | Ubuntu 7.10 |
| 其它软件 | JMeter 2.3.1 |
下图是我们对此测试的结果:
从图中可以看出,webx和基于OSGi的实现在性能上几乎没有差异,从性能上讲可以对webx进行OSGi的改造。




