Posts Mentioning RSS Toggle Comment Threads | Keyboard Shortcuts

  • tin 9:06 pm on February 16, 2006 Permalink | Log in to leave a Comment
    Tags:   

    Webwork 2.2的Action是否使用Spring的prototype­获取的性能对比 

    本文在060216进行了修改,因为发现了测试中的错误!注意5.5和7的内容。

    1、引子:
    其实是ajoo的这篇“Nuts和Spring 1.2.6 效率对比”和“IoC容器的prototype性能测试 ”,他们在Javaeye上详细讨论了Spring的prototype的缺陷。
    Spring的prototype指的就是singleton="false"的bean,具体可以看Spring参考手册“3.2.5. To singleton or not to singleton”介绍。

    2、Webwork 2.2的Spring结合问题:
    Webwork 2.2已经抛弃自己的IoC,默认使用Spring的IoC。
    上在OpenSymphony的官方Wiki,和jscud后来的几篇文章中没有特别提出prototype的问题。但是托他们的福,我们已经顺利的使Spring和Webwork良好的协同工作起来了。
    可是而后的一些问题却把prototype的问题搞得神秘起来……
    ajoo的测试中指出Spring的prototype性能很差,参见后面参考中的一篇文章和Javaeye的讨论。
    而后又发现robbin在Javaeye的Wiki上面的“集成webwork和spring”中的最后注到:
    “注意:目前并不推荐使用Spring来管理Webwork Action,因为对于prototype类型的bean来说,Spring创建bean和调用bean的效率是很低的!更进一步信息请看IoC容器的prototype性能测试”
    这就使我们常用的Spring+Webwork2.2的连接中使用的prototype的问题被摆出来了。
    我现在的项目中使用了prototype的方式将Webwork Action使用Spring进行显示的装配,我担心这个性能的问题会很严重,所以今天花了半天时间具体测试了一下。

    3、Prototype VS autowire的解释:
    我不知道怎么命名两种方式好,所以这里先做个解释:
    spring的配置中Action会有个id,如:

    <bean id="someAction" class="com.tin.action.SomeAction" parent="basicActionWithAuthtication" singleton="false">
    <property name="someDAO">
    <ref bean="someDAO" />
    </property>
    </bean>

    我指的prototype方式就是在xwork中这样配置:

    <action name="someAction" class="someAction">

    而autowire方式就是指在xwork中这样配置:

    <action name="someAction" class="com.tin.action.SomeAction">

    看起来相同,但其实不同(我以前发过帖子,其中说这几种方法都可,但是其实它们的机制是不同的。

    4、Portotye和autowire在XWork的SpringObjectFactory中是如何运作的:
    我们先看一下代码,就能明白两者的区别了:

    public Object buildBean(String beanName, Map extraContext) throws Exception {
    try {
    return appContext.getBean(beanName);
            }
    catch (NoSuchBeanDefinitionException e) {
                Class beanClazz 
    = getClassInstance(beanName);
    return buildBean(beanClazz, extraContext);
            }

        }


    public Object buildBean(Class clazz, Map extraContext) throws Exception {
            Object bean;

    try {
                bean 
    = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
            }
    catch (UnsatisfiedDependencyException e) {
    // Fall back
                bean = super.buildBean(clazz, extraContext);
            }


            bean 
    = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());
    // We don’t need to call the init-method since one won’t be registered.
            bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());

    return autoWireBean(bean, autoWiringFactory);
        }


    public Object autoWireBean(Object bean) {
    return autoWireBean(bean, autoWiringFactory);
        }

    如果按照autowire配置会使用第二个buildBean方法,而prototype会使用第一个buildBean方法。

    5、我的测试,首先测试SpringObjectFactory的理论效率:

    public class testSpringObjectFactory extends TestCase {
    protected FileSystemXmlApplicationContext appContext;
    protected SpringObjectFactory sof = null;
    protected Map map = null;
    final String[] paths = {
    "WebRoot/WEB-INF/applicationContext.xml",
    "WebRoot/WEB-INF/spring-daos.xml",
    "WebRoot/WEB-INF/spring-actions.xml"
            }
    ;

    protected void setUp() throws Exception {
    super.setUp();
            appContext 
    = new FileSystemXmlApplicationContext(paths);

            sof 
    = new SpringObjectFactory();
            sof.setApplicationContext(appContext);
            sof.setAutowireStrategy(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);

            map 
    = new HashMap();
        }


    public void testSpringObjectFacotyWithAutowire() {
    long begin = System.currentTimeMillis();

    try {
    for (int i = 0; i < n style="color:#000000">100000
    ; i++{
                    sof.buildBean(
    "com.wqh.action.XinfangNewsAction", map);
                }

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


    long end = System.currentTimeMillis();
            System.out.println(
    "**************************Used time:" +
                (begin 
    - end));
        }


    public void testSpringObjectFacotyWithPrototype() {
    long begin = System.currentTimeMillis();

    try {
    for (int i = 0; i < 100000; i++{
                    sof.buildBean(
    "xinfangNewsAction", map);
                }

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


    long end = System.currentTimeMillis();
            System.out.println(
    "**************************Used time:" +
                (begin 
    - end));
        }


    public void testSpringObjectFacotyWithSpringProxyableObjectFactory() {
            sof 
    = new SpringProxyableObjectFactory();
            sof.setApplicationContext(appContext);
            sof.setAutowireStrategy(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);

    long begin = System.currentTimeMillis();

    try {
    for (int i = 0; i < 100000; i++{
                    sof.buildBean(
    "com.wqh.action.XinfangNewsAction", map);
                }

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


    long end = System.currentTimeMillis();
            System.out.println(
    "**************************Used time:" +
                (begin 
    - end));
        }

    }

    重要的是测试结果:
    **************************Used time:-16875
    **************************Used time:-80500
    **************************Used time:-12703(使用SpringProxyableObjectFactory()这个实现)

    prototype是autowire运行时间的4.77X倍,十分可观。

    5.5 巨大的反差,原来是我搞错了配置,发现了幕后黑手:
    第二天,我又重新运行了5里面的测试。但是结果令人吃惊,运行了十多次,结果于昨天反差巨大,prototype方式获得的bean反而性能最快!
    摘要两次测量结果
    **************************Autowire Used time:-17578
    **************************Prototype Used time:-7609
    **************************Proxy Used time:-13063
    ———————————————–
    **************************Autowire Used time:-17047
    **************************Prototype Used time:-7609
    **************************Proxy Used time:-12797

    这是为什么呢?我百思不得其解,问题出在哪里呢?后来经过跟踪svn里面的提交纪录。我发现,我在昨天测试以后,把spring配置文件中的<beans default-autowire="autodetect">变成了<beans>。也就是没有打开自动检测的autowire!
    而后就真相大白了。我有配置上default-autowire="autodetect"进行测试,结果:
    **************************Autowire Used time:-16937
    **************************Prototype Used time:-79750
    **************************Proxy Used time:-12578

    这和昨天的测试结果完全相同。也就是说我昨天写的4.77x的结果其实没有实际意义。倒是说明了Spring和Webwork集成的文章上面说的default-autowire="autodetect"是很坏的实践,即失去了name的灵活性也带来了巨大的性能损失。
    而如果使用默认的Spring autowire配置下,prototype的性能已经很好了,实际上它工作起来应该是最快的。

    6、在实际的Web项目中的性能对比:
    我使用了我的一个小项目,就是反复调用一个action获取一个页面,其中有一个DAO注入。使用了JMeter进行了一个测试:2个线程,间隔0.5秒,循环50次,对比“据和报告中的”Throughput,单位/sec。
    使用autowire方式:Avg. 148.34(吞吐量越高越好)
    使用prototype方式:Avg. 138.5

    也就是说在实际应用中两者也是有性能差距的,后者大约是前者性能的93%。
    具体代码我不放出了,因为意义不大,大家也可以自己动手试验一下。
    补充说明:
    首先注意这个测试是在default-autowire="autodetect"下进行的。
    测试的这个Action其实是一个空Action,它没有调用service和DAO,只是直接return SUCCESS,然后dispatcher到一个静态内容的jsp页面。我的本意是为了能够在获取Action占据的时间比例比较高的情况下分析性能区别。但是实际上却间接的夸大了在真正的实际应用中的性能差距。实际应用中如果加上service、DAO等逻辑的执行时间、模板View的渲染时间还有广域网上的网络传输时间,那么获取Action实例的时间差距可能就微乎其微了。

    7、后续:
    经过今天的思考,可以说完全改变了想法,重新汇总一下:
    a、在不使用default-autowire="autodetect"时,Webwork 2.2的xwork中的action class使用spring的bean id配置的理论性能最好。而且,我认为如果不是为了追求配置上的简单,严重推荐关闭spring的default-autowire。
    b、在使用default-autowire="autodetect、name、class"时,需要考虑你的需求。如果不使用Spring AOP提供的功能则在Webwork 2.2的xwork中的action class使用class全名比较好。如果使用Spring AOP的功能,则还是使用bean id。
    c、在Spring中是否使用default-autowire是个需要慎重考虑的问题。autowire如果打开,命名会受到限制(class则更不推荐,受限更大,参考相关文档),它所带来的配置简化我认为只算是小小的语法糖果,背后却是吃掉它所埋下的隐患。
    d、6中的测试还是有些说明意义的。7%的性能差距是在使用了default-autowire的方式下得出的,其中测试的那个action其实没有执行什么逻辑,而是一个直接dispatcher到success view的action,如果有商业逻辑包装,则性能差据估计会更小。因为实际上Action的执行过程、service、DAO等逻辑的执行过程和模板View的渲染过程(网络延迟)才是耗时大户。所以,关于性能应该下的结论是,prototype与否,在实际应用中性能差距是很小的,基本可以忽略不计。我们讨论的更多是编码的更好的实践。
    e、autowire不使用Spring AOP相对还是trade off,因为虽然配置简单一点,但是对于使用Spring的声明性事务等内容会带来麻烦。虽然XML不那么好,但是显示配置带来的好处还是很多的。
    f、谢谢robbin的提示。关于事务我也是无奈,放弃Action事务后难道给DAO多封装一层事务?如何没有事务依然使用HibernateDAOSurpport?Acegi的确不适合Web,使用WW的Inteceptor可以实现更舒适的解决方案。
    g、SpringProxyableObjectFactory的问题……使用上难道只能改代码?找了半天没有这个东西的介绍。看来还是需要看看代码。不过发现现在Webwork和Xwork的代码又变动了很多……
    h、我的测试是在Webwork2.2+Spring 1.2.6环境下测试的

    8、参考资源:
    Nuts和Spring 1.2.6 效率对比
    http://www.javaeye.com/pages/viewpage.action?pageId=786
    IoC容器的prototype性能测试
    http://forum.javaeye.com/viewtopic.php?t=17622&postdays=0&postorder=asc&start=0
    JavaEye的Wiki:集成webwork和spring
    http://www.javaeye.com/pages/viewpage.action?pageId=860
    WebWork – Spring官方Wiki
    http://www.opensymphony.com/webwork/wikidocs/Spring.html
    webwork2 + spring 结合的几种方法的小结
    http://forum.javaeye.com/viewtopic.php?t=9990
    WebWork2.2中结合Spring:"新的方式"
    http://www.blogjava.net/scud/archive/2005/09/21/13667.html
    我为什么不推荐对Action进行事务控制
    http://www.javaeye.com/pages/viewpage.action?pageId=1205
    我为什么不推荐使用Acegi
    http://www.javaeye.com/pages/viewpage.action?pageId=1199

     
    • 女孩 10:32 am on February 20, 2006 Permalink

      我知道2月14、15号你都在干什么,工作工作,你太努力了,你太想早些成功,来完美我们的生活。我知道,我都知道!我真的惭愧让你一个人如此的努力,而我却站在远方不能帮助你什么。我埋怨我们没有过属于我们的日子,甚至周末我们都没有腾出时间只有两个人享受的美好时光。但是我把14、15号两天都深深刻在心理,在心里我们已经在一起了,不是吗?
      不要负担太重。累了就歇歇吧。我永远站在你那边……
  • tin 8:57 pm on February 13, 2006 Permalink | Log in to leave a Comment
    Tags:   

    关于存储过程和直接执行SQL对比 

    关于存储过程和直接执行SQL:
    需要注意以下几点:
    1、从数据库角度,存储过程总是要比它所对应的纯SQL要慢。
    2、存储过程目的在于简化特别复杂的SQL复合应用的场景。
    3、但是对于一个拥有多条SQL的存储过程来说,它可以提升效率。因为减少了SQL传输的网络延迟。所以说SQL复杂时,存储过程可以增加实际的运行效率。注意对比第1条,第一条是对应服务器调用,这条对应实际的网络环境。
    4、还有就是存储过程很容易减少多条SQL之间数据传递的麻烦(有可能带来没有实际意义的中间变量),可以在服务器端把它们隐藏。
    所以我想应该考虑这几点来选择存储过程:
    1、拥有复杂的数据操作,需要SQL复合。
    2、中间传递的临时数据过多或者过大的时候。
    使用存储过程的Java代码:JDBC call Stored Procedure
    CallableStatement cstmt = conn.prepareCall("{ ? = call md5( ? ) }");
    // CallableStatement cstmt = conn.prepareCall("begin ? := md5( ? ); end;"); // oracle syntax
    cstmt.registerOutParameter(1, Types.VARCHAR); // set out parameters
    cstmt.setString(2, "idea"); // set in parameters
    cstmt.execute();
    String md5Str = cstmt.getString(1); // Getting OUT Parameter Values
    cstmt.close();
     
  • tin 12:05 am on February 11, 2006 Permalink | Log in to leave a Comment
    Tags:   

    对不起,我的Angel 

    工作到这么晚,真是没办法。
    春节为了长假陪着你,多休息了两天,结果周三回来开始就加油工作。
    我给我们小组配置了WebCalendar,我认真计算了一下。这几3天的一周我实际工作了34.5小时,自己都觉得可怕……
    可是,我们每晚的电话却明显的减少了时间,这三天中只有一天电话时间比较长,其它两天只能匆匆聊上两句…………
    今天白天,你给我婚纱网站的链接,我却忙得没时间仔细看,宝贝我对不起你,我知道你想做我的新娘。而我,我也迫不及待把我的一生托付给你,陪你。但是工作是对你好的基础,我真的是认真的工作拼命的在学习,虽然我不一定能赶上最好的,但我努力去追赶了,我想在工作上我是对得起你和我的父母的。
    我是爱你的,你的照片就在我电脑的前面,我不时的看看你,看你冲我噘嘴,我想亲亲你!
    我是爱你的,你的声音的一切影像都在我心里,我无时不刻不在想你,我想抱着你!
    比起被时空分隔的其它恋人我们是幸福的,因为我们相互信任。
    最后,叮嘱几句:
    1、多喝水,按时吃饭。
    2、多做眼睛保健操,用你的眼药。
    3、看Angel和Buffy别太上瘾,注意休息。
    4、多关心你爸爸妈妈。
    5、如果有时间去浩沙健身吧:D
     
    • (没有名字) 11:24 pm on February 11, 2006 Permalink

      小麒,哪有你说的那么上纲上线。没人急,每个人都很急。情人节是情人过的吧,我们是爱人,哈哈。
      谢谢tiffany%……
    • tiffany 6:23 pm on February 11, 2006 Permalink

      感动ing
    • (没有名字) 1:29 pm on February 11, 2006 Permalink

      慢慢来把   迫切反倒给对方压力   所以慢慢来就好
      大男人怎么急着说把自己送出去干什么   当然说“我爱你”一万遍都是不嫌够的
      好好把握把 情人节要到了 兄弟!
  • tin 11:56 pm on February 10, 2006 Permalink | Log in to leave a Comment
    Tags:   

    Mysql5的存储过程的权限Bug 

    晚上搞了半天procedure的问题,提示:
    java.sql.SQLException: Driver requires declaration of procedure to either contain a ‘\nbegin’ or ‘\n’ to follow argument declaration, or SELECT privilege on mysql.proc to parse column types.
     at com.mysql.jdbc.DatabaseMetaData.getCallStmtParameterTypes(DatabaseMetaData.java:6953)
     at com.mysql.jdbc.DatabaseMetaData.getProcedureColumns(DatabaseMetaData.java:2721)
     at com.mysql.jdbc.CallableStatement.determineParameterTypes(CallableStatement.java:1048)
     at com.mysql.jdbc.CallableStatement.<init>(CallableStatement.java:83)
     at com.mysql.jdbc.Connection.prepareCall(Connection.java:1255)
     at com.mysql.jdbc.Connection.prepareCall(Connection.java:1232)
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
     at java.lang.reflect.Method.invoke(Method.java:585)
     at com.mchange.v2.c3p0.stmt.GooGooStatementCache$2.run(GooGooStatementCache.java:333)
     at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:354)
    找了半天存储过程的问题,因为都着眼于’\nbegin’ or ‘\n’这个地方了,重建了procedure也不起作用,找不到问题所在。
    仔细思考,换用了root登陆就可以了。感觉明显是权限privileges的问题。
    所以其实应该是这个提示or SELECT privilege on mysql.proc to parse column types。
    可是google不到这个问题所在,因为没有mysql.proc这个权限,但是有人说这是一个bug
    后来我发现,其实它是指mysql这个库的proc表的SELECT权限,添加上就工作正常了……
    其实原因是这样的,原先测试时使用的xinfangweb的用户有两个,一个是localhost,另一个是%,前者的权限是全的,后者被限制了,哈哈。
     
  • tin 11:51 pm on February 10, 2006 Permalink | Log in to leave a Comment
    Tags:   

    HQL的中文问题小议 

    遇到了HQL的中文问题:
    hql的String中包括了中文以后就会出错。
    经过多次查询,转码(多字节、单字节)还是不能解决问题,说明某些环节压根就对多字节支持不正常。
    后来经过多次试验,发现是hibernate3的hql语法解释器org.hibernate.hql.ast.ASTQueryTranslatorFactory出的问题,看到有人推荐用org.hibernate.hql.classic.ClassicQueryTranslatorFactory代替。
    那当然是不可以的,否则相当于退回Hibernate2。
    解决方法是不要在hql中写中文。
    而是去使用?占位符和setParameters。
    比如在Spring HibernateTemplate里面可以:
    String hql = "select count(*) from Xinfangnews xfnews where xfnews.shifoufabu = 0 and (xfnews.title like ? or xfnews.content like ?)";
    Object[] parameters = { "%" + keyWord + "%", "%" + keyWord + "%" };
    getHibernateTemplate().find(hql, parameters);
    如此可以运行了。
    不过我走了点弯路:
    本来写成"like ‘%?%’",然后往里面插keyWord,告诉我找不到?占位符。后来才想到”中的?已经不是占位符了。
    然后就解决了:
    所以就是说不能在HQL进行语法解析之前插入中文,而需要在解析之后插入。
    还要注意,我试验了一下,在Mysql5中已经指定了使用UTF-8语言的时候,不必须在Hibernate连接的地方指明使用jdbc的encoding:
    如很多人说需要这样配置:<prop key="hibernate.connection.url">jdbc:mysql://10.206.21.171:3306/xinfangweb?useUnicode=true&amp;characterEncoding=GBK&amp;autoReconnect=true</prop>
    但实际上是用这样的配置:<prop key="hibernate.connection.url">jdbc:mysql://10.206.21.171:3306/xinfangweb</prop>
    也是可以工作的,当然还是写上比较稳妥。
     
c
compose new post
j
next post/next comment
k
previous post/previous comment
r
reply
e
edit
o
show/hide comments
t
go to top
l
go to login
h
show/hide help
esc
cancel