让代码取代你的配置文件吧
浏览:1677次 出处信息
最近, 在编写一个专门压测NameNode的工具(以下简称s4nn), 它有两个难点 :
- s4nn需要可以模拟上万个DataNode ;
- s4nn 需要灵活的支持对NameNode访问行为的定义.
后者导致了本文的思考.
命令行参数和配置文件是最常用来配置系统的方法, 前者用于配置项较少, 后者则适合配置复杂情况. 这两种方式都有共同令人痛苦的地方:
- 编写代码去载入->解析->转换, 通常如同处理协议般无聊(要是有个什么变更, KMN!!);
- 对于复杂的配置文件编写而言, 总是没有顺手的编辑器支持, 写起来既累又易错.
要是用代码取代配置文件呢?
呃… 这会很麻烦吧, 像java这样的改了代码那还不要重新编译啊?
嗯, 确实, 但并没有想象中那么麻烦, 一些技巧可以让它变得简单(至少java是这样), 如Btrace.
用代码取代独立的配置文件不是新鲜的做法, 像Guice, Gant 等已经以Internal DSL的代码代替了XML. 好处很明显:
- 良好的DSL风格, 简洁易懂 ;
- 免去对配置文件的解析转换 ;
- 最好的编辑器支持, 语法高亮, 一键格式化, 提示补全, 重构 ;
- 编译器帮助查错;
- 与代码无缝结合, 能够容易在变化中保持一致性.
简言之两个字, “高效”! 这倒是挺适合s4nn的, 不妨一试!
从需求的角度出发, 配置应该能够完成:
- 定义一组Client RPC调用行为, 其调用参数, 次数;
- 定义DataNode的运行时特征, 个数.
为满足这两点, 代码设计上可能会有:
class ClientDriver { void addRpc(times, name, args) void setNameNode(address) } class DataNodeSimulator { void setCapacity(size) void setHeartbeatInterval(sec) void setBlockReportInterval(sec) void setNameNode(address) }
那么其配置文件会是:
1 <configuration> 2 <client> 3 <rpc times="100000" name="getFileInfo"> 4 <arguments> 5 <param name="src">/foo</param> 6 </arguments> 7 </rpc> 8 </client> 9 <namenode> 10 <address>host:port</address> 11 </namenode> 12 <datanode> 13 <simulator num="10000"> 14 <capacity unit="GB">200</capacity> 15 <heartbeatInterval unit="SECOND">1</heartbeatInterval> 16 <blockReportInterval unit="HOUR">1</blockReportInterval> 17 </simulator> 18 </datanode> 19 </configuration>
简单吗? 简单, 那是因为这只是最基本的, 实际的配置应考虑到:
- 有30种RPC方法, 其中参数个数最多的有6, 参数类型并非都是基本类型, 参数值可能需要按照某种规则随即生成;
- DataNode模拟器也会有可能需要支持多种选项组合, 如实际集群种不是所有的机器的容量都一样的等.
这样的XML会臃肿到什么程度…
好吧, 看看用代码的效果:
1 import com.taobao.s4nn.*; 2 3 public class Main extends Bootstrap { 5 6 protected void config() { 7 setConcurrency(16); 8 setMaxRandomPathDepth(10); 9 10 /* 11 * DataNode simulator config 12 */ 13 simulate(3, new DataNodeSimulatorGenerator() { 14 protected void config() { 15 setCapacity(gibibyte(1)); 16 setTickPeriodSecond(1); 17 setHeartbeatInterval(1); 18 setBlockReportInterval(3); 19 setNameNode(proxyOfNameNodeAt("localhost:10001")); 20 } 21 }); 22 23 /* 24 * NameNode Status pre-setting config 25 */ 26 parallel(1, new StatusPresetterBuilder() { 27 protected void config() { 28 setName(client("InitalStatus")); 29 setNamenode(proxyOfNameNodeAt("localhost:10001")); 30 times(1, create(randomSrc, defaultFsPermission, overwrite(true), replication((short) 3), blocks(1))); 31 } 32 }); 33 34 /* 35 * Client RPC driver config 36 */ 37 parallel(10, new ClientDriverBuilder() { 38 protected void config() { 39 setName(seqNameWith("client")); 40 setNamenode(proxyOfNameNodeAt("localhost:10001")); 41 42 times(1, getBlockLocations(src("/foo"), randomOffsetIn(0, 1024), randomLengthIn(0, 2048))); 43 times(2, getListing(src("/foo"))); 44 times(3, getLocatedListing(src("/foo"))); 45 times(4, getStats()); 46 times(5, getDatanodeReport(all)); 47 times(6, getPreferriedBlockSize(src("/foo"))); 48 times(7, getFileInfo(src("/foo"))); 49 times(8, getContentSummary(src("/foo"))); 50 times(9, setOwner(src("/foo"), username("jushi"), groupname("dwbasis"))); 51 times(10, setReplication(src("/foo"), replication((short) 4))); 52 times(11, setPermission(src("/foo"), defaultFsPermission)); 53 times(12, rename(src("/foo"), src("/bar"))); 54 times(13, mkdirs(src("/bar"), defaultFsPermission)); 55 times(14, setQuota(src("/foo"), randomNamespaceQuotaIn(1024, 2048), randomDiskspaceQuotaIn(1024, 4096))); 56 times(15, setTime(src("/foo"), currentTimeMillis, unchanged)); 57 } 58 }); 59 } 60 }
嗯, 既保持易读性又更为简洁, 重点是不用再写那些额外处理XML的代码了.
这里效仿了Guice中AbstractModule的DSL做法, 提供了setXxx, times等内置方法, 用Java IDE编写起来那是相当轻快啊~~
要是改用Scala或Groovy来实现, 那么还要简化, 使得代码味更少些, 而且直接运行简单到一条命令就搞定了:)
建议继续学习:
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
扫一扫订阅我的微信号:IT技术博客大学习
<< 前一篇:Google:《关于浏览器和网络的20项须知》
后一篇:分布式文件系统Ceph调研1 >>
文章信息
- 作者:聚石 来源: 淘宝数据平台与产品部官方博客 tbdata.org
- 标签: 配置文件
- 发布时间:2011-08-22 12:30:25
建议继续学习
近3天十大热文
- [41] 界面设计速成
- [35] Oracle MTS模式下 进程地址与会话信
- [33] 如何拿下简短的域名
- [32] 视觉调整-设计师 vs. 逻辑
- [32] 程序员技术练级攻略
- [32] IOS安全–浅谈关于IOS加固的几种方法
- [31] 图书馆的世界纪录
- [30] 【社会化设计】自我(self)部分――欢迎区
- [30] android 开发入门
- [27] 读书笔记-壹百度:百度十年千倍的29条法则