君 山 GreenTeaJUG 2013-2-23
花 名 : 君 山 真 名 : 许 令 波 博 客 :http://xulingbo.net 微 博 :@ 淘 宝 君 山 邮 箱 :xulingbo0201@163.com 简 介 :2009 年 毕 业 加 入 淘 宝, 一 直 在 做 淘 宝 商 品 详 情 系 统 的 团 队 做 开 发 和 性 能 优 化 方 面 的 工 作, 开 发 过 一 个 Sketch 模 板 引 擎 给 developerworks 投 稿 获 得 过 最 佳 作 者, 著 有 深 入 分 析 Java Web 技 术 内 幕 一 书
前 端 优 化 assets 合 并 整 合 页 面 中 inline 的 js\css combo 合 并 css js 文 件 BigRender( 使 用 textarea, 控 制 浏 览 器 渲 染 节 奏 ) 服 务 端 优 化 将 iframe 改 为 jsonp 调 用 建 立 异 步 系 统 (JS 触 发 加 载 ) Velocity 模 板 优 化 (sketch 框 架 ) 逐 步 去 除 DB 依 赖 ( 各 个 C 走 Tair 缓 存 ) 网 络 优 化 TCP 初 始 拥 塞 窗 口 优 化 去 除 空 格 TAB 压 缩 字 符 串, 减 少 页 面 大 小 静 态 化 CDN 化 今 年 要 做 的 事 交 易 链 路 优 化 欢 迎 加 入 我 们 的 团 队
1. 当 前 存 在 的 问 题 2. 解 决 问 题 的 思 路 3. 优 化 的 原 则 和 目 标 4. 传 统 办 法 解 决 思 路 5. 激 进 的 解 决 问 题 的 思 路 具 体 实 例 介 绍 和 成 果 6. 遇 到 的 一 些 问 题 介 绍 7. Q&A
发 现 模 板 方 面 的 问 题 1. Velocity 是 动 态 解 释 性 语 言, 执 行 效 率 较 差 2. 页 面 复 杂, 反 射 调 用 非 常 多 3. CPU 压 力 较 大, 压 力 测 试 时 CPU 基 本 都 达 到 80% 左 右, 通 过 检 测 工 具 可 以 发 现 模 板 渲 染 占 用 了 60% 以 上 的 CPU 时 间 4. 模 版 渲 染 占 去 大 部 分 响 应 时 间 是 前 台 系 统 的 一 个 瓶 颈, 模 板 渲 染 时 产 生 很 多 临 时 对 象, 对 JVM 的 GC 影 响 很 大, 导 致 系 统 频 繁 GC 5. 整 个 页 面 输 出 比 较 大, 平 均 在 80KB 左 右, 大 部 分 时 间 都 在 out.print 页 面 模 板 中 空 白 字 符 比 较 多, 浪 费 网 络 传 输 量
针 对 性 解 决 问 题 1. 将 Velocity 模 板 直 接 转 成 Java 类 去 执 行, 将 Velocity 语 法 转 成 Java 语 法 2. 将 方 法 的 反 射 调 用 转 成 直 接 Java 原 生 方 法 调 用 3. 减 少 页 面 大 小, 删 除 空 行 等 无 效 字 符 输 出 4. 将 页 面 中 的 字 符 转 成 字 节 输 出 减 少 编 码 转 换
三 角 结 构 减 少 翻 译 的 代 价 一 步 到 位 将 变 的 转 化 为 不 变 的 对 不 变 的 做 预 处 理
1. 试 图 减 少 代 码 量, 从 而 减 少 代 码 的 执 行 时 间 2. 减 少 临 时 对 象, 降 低 内 存 开 销 3. Velocity 静 态 化, 类 型 确 定 4. 动 态 编 译, 类 似 JIT 技 术
减 少 树 的 总 节 点 数 量 #set($one=-1) #set($pageid = "$!page.pageid") #set($pages = $tbstringutil.getint("$pageid")+$one) #set($offsets=$pages*($count+$rightcount)) 优 化 成 #set($offsets=($!page.pageid - 1)*($count + $rightcount)) 这 样 可 以 减 少 很 多 语 法 节 点
减 少 树 的 总 节 点 数 量 $parent.getchildren().getson().getname() $parent.getchildren().getson().getage() 优 化 成 #set($son= $parent.getchildren().getson()) $son.getname() $son.getage() 这 样 可 以 减 少 方 法 的 反 射 调 用
1. 减 少 宏 的 调 用, 因 为 每 次 调 用 都 要 都 要 reload, 而 Velocity 针 对 macros 的 自 动 reload, 采 用 了 同 步 排 他 锁 进 行 控 制, 比 较 影 响 性 能 2. 减 少 vm 模 板 的 检 查 频 率, 设 置 modificationcheckinterval 3. 页 面 Cache 减 少 生 成 JJTree 的 频 率 4. MapGetExecutor 改 造, 用 clazz.isassignablefrom(map.class) 替 代 Class [] interfaces = clazz.getinterfaces(); for (int i = 0 ; i < interfaces.length; i++) { if (interfaces[i].equals(map.class))
1. vm 模 板 如 何 被 转 成 java 类 2. 方 法 调 用 的 无 反 射 优 化 3. 字 符 输 出 改 成 字 节 输 出
1. 如 何 将 Velocity 的 语 法 转 成 Java 语 法 2. 如 何 构 建 Java 的 代 码 结 构 3. 如 何 生 成 Java 类 4. 如 何 编 译 Java 类 5. 如 何 执 行 Java 类 6. 出 错 处 理 7. 如 何 和 其 他 框 架 结 合 起 来
仍 然 沿 用 Velocity 中 将 一 个 vm 模 板 解 释 成 一 棵 AST 语 法 树, 但 是 重 新 修 改 这 棵 树 的 渲 染 规 则, 我 们 将 重 新 定 义 每 个 语 法 节 点 生 成 对 应 的 Java 语 法, 而 不 是 渲 染 出 结 果 在 SimpleNode 类 中 重 新 定 义 一 个 generate 方 法, 如 下 : public Object generater(object data, Writer writer) throws IOException, ParseException { SketchCompilationContext context = (SketchCompilationContext) data; int i, k = jjtgetnumchildren(); for (i = 0; i < k; i++) { StringWriter spwriter = new StringWriter(); jjtgetchild(i).generater(data, spwriter); writer.write(spwriter.tostring()); } return data; }
#foreach ($i in [0..10]) #if ($i % 2 == 0) #set ($str = " 偶 数 ") #else #set ($str = " 奇 数 ") #end $i 是 $str #end
private Object _foreach_3414368_43072917262352(final PageContext pagecontext, final I _I) throws Exception { final ContextAdapter context = pagecontext.getcontext(); final PageWriter out = pagecontext.getout(); Iterator _it = _COLLE((_I.exampleDO == null? null : ((Mode) _I.exampleDO).getItemList())); int _VelocityCount = 1; while (_it.hasnext()) { Object _i = _it.next(); pagecontext.addforvarsdef("_i", _i); pagecontext.addforvarsdef("_velocitycount", _VelocityCount); out.write(_s0); if (EPUT.is(EPUT.eq(EPUT.mod(_i, 2), 0))) { context.put("str", " 偶 数 "); out.write(_s0); } else { context.put("str", " 奇 数 "); out.write(_s0); } out.write(_s0); out.write(_evtck(context, "$i", _i)); out.write(_s1); out.write(_evtck(context, "$str", context.get("str"))); out.write(_s2); _VelocityCount++; } return Boolean.TRUE; }
1. 大 部 分 情 况 下 $exampledo.getitemlist() 方 法 调 用 这 种 调 用 类 型 都 是 确 定 的 2. 通 过 在 模 板 渲 染 时 跟 踪 变 量 $exampledo 的 java 类 型 就 可 以 将 $exampledo.getitemlist() 的 反 射 调 用 转 成 原 生 的 java 方 法 调 用 如 通 过 _TRACE() 方 法 跟 踪 exampledo 的 执 行 Iterator _it = _COLLE(_TRACE("", "_I.exampleDO", "-209571699", context.get("exampledo"),"getitemlist", new Object[]{})); 最 终 直 接 转 成 Iterator _it = _COLLE(( ExampleDO )(I.exampleDO).getItemList()));
由 于 一 个 模 板 中 一 次 执 行 并 不 能 执 行 到 所 有 的 方 法, 所 以 一 次 执 行 并 不 能 将 所 有 的 方 法 调 用 转 变 成 反 射 方 式 这 种 情 况 下 就 会 多 次 生 成 模 板 对 应 的 Java 类 及 多 次 编 译
1. 将 模 板 中 的 静 态 字 符 串 直 接 是 out.write(_s0) 输 出, 这 里 的 _S0 是 一 个 字 节 数 组, 而 vm 模 板 中 是 字 符 串, 将 字 符 串 转 成 字 节 数 组 是 在 这 个 模 板 类 初 始 化 时 完 成 的 你 可 能 有 疑 问, 为 何 将 字 符 串 转 成 字 节 数 组 来 输 出, 如 果 看 过 Java 中 文 编 码 一 章 你 就 会 知 道, 字 符 的 编 码 是 非 常 耗 时 的, 如 果 我 们 将 静 态 字 符 串 提 前 编 码 好, 那 么 在 最 终 写 Socket 流 时 就 会 省 去 这 个 编 码 时 间, 从 而 提 高 执 行 效 率 从 实 际 的 测 试 来 看, 这 对 提 升 性 能 很 有 帮 助 2. 删 除 空 格 tab 等 无 用 字 符 串, 减 少 java 系 统 往 外 突 出 的 字 节 数
1. #foreach 难 题 --- 重 复 定 义 变 量 #foreach($i in $list) #foreach($i in $list) $i #end #end 2. #define 难 题 --- 变 量 中 的 变 量 #define( $hello ) Hello $who #end #set( $who = "World!") $hello 3. #break #stop 等
1. #foreach 难 题 --- 重 复 定 义 变 量 #foreach($i in $list) #foreach($i in $list) $i #end #end 2. #define 难 题 --- 变 量 中 的 变 量 #define( $hello ) Hello $who #end #set( $who = "World!") $hello 3. #break #stop 等
1. 必 须 能 够 重 复 能 够 渲 染 语 法 树 2. 确 定 方 法 中 的 参 数 变 量 类 型 3. 方 法 反 射 调 用 改 成 正 常 的 方 法 调 用 : 如 $foo.var 改 成 foo.getvar()/foo.get( var ) 4. 构 建 新 的 classloader, 重 新 编 译 的 模 版 类 重 复 加 载 到 jvm 中 5. JavaC 编 译 器 的 限 制
1. 模 版 的 动 静 分 离 2. 静 态 模 版 的 CDN 化 3. JS 渲 染 orapache 渲 染
一 起 交 流