zoukankan      html  css  js  c++  java
  • velocity的一些优化记录

    背景

    前段时间做了个项目,主要优化一个产品页面。整个优化过程中,针对velocity的分析过程占了比较大的比重,这里做一下整理和记录。

    描述

    velocity版本: 

    Java代码  收藏代码
    1. <dependency>  
    2.     <groupId>org.apache.velocity</groupId>  
    3.     <artifactId>velocity</artifactId>  
    4.     <version>1.6.4</version>  
    5. </dependency>  

    优化1: 锁优化

    通过velocimacro.library.autoreload=false进行关闭autoreload,因为使用了同步锁,非常影响性能

    Java代码  收藏代码
    1. "TP-Processor20" daemon prio=10 tid=0x00002aab4c7cb800 nid=0x3d46 waiting for monitor entry [0x00000000423a3000]  
    2.    java.lang.Thread.State: BLOCKED (on object monitor)  
    3.     at org.apache.velocity.runtime.VelocimacroFactory.getVelocimacro(VelocimacroFactory.java:571)  
    4.     - waiting to lock <0x00002aaad964ca48> (a org.apache.velocity.runtime.VelocimacroFactory)  
    5.     at org.apache.velocity.runtime.RuntimeInstance.getVelocimacro(RuntimeInstance.java:1563)  
    6.     at org.apache.velocity.runtime.directive.RuntimeMacro.render(RuntimeMacro.java:199)  
    7.     at org.apache.velocity.runtime.parser.node.ASTDirective.render(ASTDirective.java:175)  
    8.     at org.apache.velocity.runtime.parser.node.ASTBlock.render(ASTBlock.java:72)  
    9.     at org.apache.velocity.runtime.parser.node.ASTIfStatement.render(ASTIfStatement.java:87)  
    10.     at org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.java:336)  
    11.     at org.apache.velocity.Template.merge(Template.java:328)  
    12.     at org.apache.velocity.Template.merge(Template.java:235)  

    说明: 

    1.  velocity针对macros的自动reload,采用了同步排他锁进行控制

    2.  在velocity实现中,org.apache.velocity.runtime.VelocimacroFactory.getVelocimacro中,line 569,每次获取一个宏对象,都会进行一个是否需要自动reload的逻辑控制

    3.  针对设置了autoReloadLibrary为true时,velocity就会先获取一个同步锁,然后进行相应检查判断是否需要重新载入

    默认是关闭的,因为公司开发的框架中针对线上和线下velocity参数设置有所不同,在开发环境开启了autoreload,所以导致在测试时发现block现象很严重。

    优化2: veocity cache策略

    velocity针对template查找有一定的cache策略,比如是否启用cache,cache的数量大小。resource.manager.defaultcache.size=89(默认值为89,0代表不限制) 

    Java代码  收藏代码
    1. TP-Processor12" daemon prio=10 tid=0x00002aab4c868800 nid=0x3d3c waiting on condition [0x0000000042abf000]  
    2.    java.lang.Thread.State: RUNNABLE  
    3.     at org.springframework.core.io.ClassPathResource.getFile(ClassPathResource.java:175)  
    4.     at org.springframework.core.io.AbstractResource.exists(AbstractResource.java:51)  
    5.     at com.alibaba.citrus.service.velocity.impl.AbstractResourceLoader.getLastModified(AbstractResourceLoader.java:72)  
    6.     at org.apache.velocity.runtime.VelocimacroFactory.getVelocimacro(VelocimacroFactory.java:601)  
    7.     - locked <0x00002aaad964ca48> (a org.apache.velocity.runtime.VelocimacroFactory)  
    8.     at org.apache.velocity.runtime.RuntimeInstance.getVelocimacro(RuntimeInstance.java:1563)  
    9.     at org.apache.velocity.runtime.directive.RuntimeMacro.render(RuntimeMacro.java:199)  
    10.     at org.apache.velocity.runtime.parser.node.ASTDirective.render(ASTDirective.java:175)  
    11.     at org.apache.velocity.runtime.parser.node.ASTBlock.render(ASTBlock.java:72)  
    12.     at org.apache.velocity.runtime.parser.node.ASTIfStatement.render(ASTIfStatement.java:87)  
    13.     at org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.java:336)  
    14.     at org.apache.velocity.Template.merge(Template.java:328)  
    15.     at org.apache.velocity.Template.merge(Template.java:235)  

    1. velocity中使用ResourceManager进行资源查找,在ResourceManagerImpl资源管理查找中,定义了一份resource globalCache

    2. 在globalCache.initialize()方法中,会读取定义 resource.manager.defaultcache.size配置,默认值只有89

    3. global cache生效,必须要开启对应xxx.resource.loader.cache=true,这样的size调整才有意义,不然velocity个根本不会进行global cache

    优化3:modificationCheckInterval优化

      如果开启了优化2,则会对该Resource定期进行是否已经进行了修改的扫描,file.resource.loader.modificationCheckInterval = 2 (单位为秒,如果不需要hot deploy,可以设置更大点)

    说明:

    1.  velocity通过ResourceManagerImpl进行资源加载,在开启优化2后,会针对从cache中返回的resource资源(velocity中模板都认为是一个resource)。

    2.  针对resource会进行requiresChecking()判断,主要的依据就是modificationCheckInterval。 0代表永不做检查处理

    优化4: MapGetExecutor优化处理

    背景知识

    1. Uberspect : velocity中用于Node render时进行数据解析的一个操作 (introspection/reflection interface)
      • UberspectImpl : Uberspect的默认实现类
      • SecureUberspector(安全调用) , LinkingUberspector(链式支持)
    2. AbstractExecutor :velocity中实现具体$bean交互操作的工具(getMethod , render数据)
      • PropertyExecutor : pojo bean的处理规则,通过getXXX()进行处理
      • BooleanPropertyExecutor : boolean方法处理,通过isXXX()进行处理
      • MapGetExecutor : 如果是Map的子类,通过调用map.get()方法进行处理
      • GetExecutor : 默认调用bean的get()方法进行处理。 我们使用的一些PullTool,比如$form.preTest.defaultInstance,就是调用了$form.get("preTest")
    3. SetExecutor : 和AbstractExecutor相对应,是针对set进行处理.
      • SetPropertyExecutor , MapSetExecutor , PutExecutor 与get处理类似。
    问题代码代码  收藏代码
    1. protected void discover (final Class clazz)  
    2.     {  
    3.         Class [] interfaces = clazz.getInterfaces();  
    4.         for (int i = 0 ; i < interfaces.length; i++)  
    5.         {  
    6.             if (interfaces[i].equals(Map.class))  
    7.             {  
    8.                 try  
    9.                 {  
    10.                     if (property != null)  
    11.                     {  
    12.                         setMethod(Map.class.getMethod("get", new Class [] { Object.class }));  
    13.                     }  
    14.                 }  
    15.   
    16.         .......  
    17.             }  
    18.         }  
    19.     }  

    profile截图:


    代码分析:MapGetExecutor的本意是对一个class如果是Map.class的之类,就委托对应的map.get方法进行处理。但在判断是否是Map之类的过程,通过getInterfaces后进行便利匹配,性能比较差。

    优化: 

    1. 自定义MapGetExecutor, 直接调用native方法isAssignableFrom进行Map转型判断

    Custommapgetexecutor代码  收藏代码
    1. if (Map.class.isAssignableFrom(<span style="font-size: 1em; line-height: 1.5;">clazz</span><span style="font-size: 1em; line-height: 1.5;">)) { // 直接调用native方法进行,Map接口判断</span>  
    2.             try {  
    3.                 if (property != null) {  
    4.                     // 通过introspector进行method cache,同时直接查找对象clazz实例的method方法  
    5.                     setMethod(introspector.getMethod(clazz, "get", new Class[] { Object.class }));  
    6.                 }  
    7.            }  

    2. 自定义UberspectImpl,引入前面自定义的CustomxMapGetExecutor

    Customuberspectimpl代码  收藏代码
    1. public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) throws Exception {  
    2. .......  
    3. if (!executor.isAlive()) {  
    4.             executor = new CustomxMapGetExecutor(log, claz, identifier);  
    5.         }  
    6. .....  
    7. }  

     3. 配置velocity参数,将自定义的CustomUberspectImpl引入到velocity调用

    Velocity.properties文件代码  收藏代码
    1. runtime.introspector.uberspect=xxxx.velocityx.CustomUberspectImpl  

    总结一下

    最后的优化参数列表

    Velocity.properties代码  收藏代码
    1. file.resource.loader.cache = true  
    2. file.resource.loader.modificationCheckInterval = 0 #不检查  
    3. resource.manager.defaultcache.size=0 #无限制  
    4.   
    5. velocimacro.library.autoreload=false  
    6.   
    7. runtime.introspector.uberspect = xxxx.velocityx.CustomUberspectImpl  

    velocity的几个概念

    • Resource : 在velocity中每个模板都可以用一个Resource来表示,类似于velocity资源概念。
    • Node : 在velocity中系统引擎对resource解析后的生成的Node对象,可以理解为语法树等概念。主要的基类:SimpleNode
    • Directive : 在velocity系统中定义的一系统指令,比如#foreach ,#if, #macro等。
    • EventHandler & EventCartridge : velocity系统中提供的扩展机制,可以监听在velocity解析,渲染,异常过程中进行自定义的业务处理。比如我们的校长大人的安全框架,在 webx3上已经被宝宝内嵌到velocity指令级别,而不是原先定义的宏概念。比如$sql和#escapse('sql')$value#end

    说明:

    其他

    上次在qcon中,taobao的一个分享文章中提到一个char to byte的优化部分。它认为每次的String.tobytes[]都涉及一次StringCoding.encode的转化过程,有点浪费。

    一个新的想法: 缓存velocity中的静态文本的String.toBytes()的数据,每次都只做动态数据的toBytes,提升系统性能。

    这个就做的有点深度了,佩服taobao同学对性能的追求

  • 相关阅读:
    Windows 服务多语言化时读取配置文件失败的问题。
    从开始界面卸载Windows服务时,不小心点击了 取消,此后再次卸载会卸载不掉
    从数据库导出数据时,有的字段是时间,不同的时间向在窗口中去掉时用正则表达式匹配找到不同的时间
    C#使用ManagementObjectSearcher来获取系统信息时会有out of memory的异常
    C# TreeView设置SelectedNode设置无效的问题
    C#判断ListBox是否显示了水平滚动条/横向滚动条
    Jmeter运行不显示cmd对话框
    监控Linux系统所选的服务所占进程内存占用
    Linux的date用法
    shell的循环嵌套语法
  • 原文地址:https://www.cnblogs.com/interdrp/p/3436785.html
Copyright © 2011-2022 走看看