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同學對性能的追求

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM