到新公司三个月了,上个月做的是Beacon项目,详细的设计思想还没有写文档。这两周开始搞Hadoop,开始阅读相关论文。开始编写MR程序。开始写java,大学时用java较多,工作后就一直在用c/c++/php/shell/js 等。并不排斥语言,各有各的优劣。只是需要从底层的角度去理解各程序的运行态。
Hadoop的原理架构方面的文章等再深入了解之后再写。先写下上周做的RTB日志分析MR程序的设计,懒得画图,仅用文字表述。
需求:
1. 读入RTB log日志,对log日志进行分析 按照特定格式输出,并对某些字段做 异常 检测。日志为json格式,顺序无关。默认输入格式不可靠(必填字段 可能会没有 等)
2. 对错误的字段进行出错次数统计,并记录错误日志。
现有代码:
1. RTB字段对应的一个类,字段 为类的属性,序列化及反序列化时使用。类似于C/C++的结构体
2. 一次读入字段,对每个值进行parse,在Parse的辅助类内添加异常报警代码。大量的if else 语句
3. 依赖于公共的RTB对象,RTB对象属性变化时,线上对他依赖的程序会宕机。(强制检测,是好处,亦是风险)。
现有代码的不足:
1. 代码量多,代码逻辑不少重复。过量的if语句
2. 报警相关的数据存储结构跨越三个类,三个文件。辅助类型判断解析类承载了业务逻辑。
3. 字段变更或者增删时 改动较多,库的依赖将导致牵一发而动全身。维护成本较高。
4. 配置相关的代码文件跨越度太大,用的地方过于随意。机器间迁移运行时无法通过代码直观知道配置,易人维护成本高。环境迁移调试成本高。
现有代码的优点:
1. 主要业务逻辑代码直观,RTB字段封装为jar类库,后期增加输入配置时,便于接入。字段类型变更时会导致依赖于其的程序宕机(也是一种好处,强制检测)
2. RTB字段设计为类属性,代码效率较高。运行时直接栈上读取。
新业务环境下的改进:
目标:
1. 业务逻辑清晰,输入输出字段对应关系明确。
2. 字段的异常检测统一处理,统一报告错误。不能遍处理字段遍检测,因为输入字段可能没有,这种情况下如果对应的目标字段不允许为空就悲催了
3. 简化配置:去除不必要的配置。如报警时的字段名称配置。配置统一处理,统一管理。
4. warning异常时,记录原始输入字段
设计一:
设计的数据结构:
LinkedHashMap保存输入字段与目标字段的映射关系。 RecordField 为自定义对象类。保存了目标Key, 目标Value,异常检测标准。
private Map<String, RecordField> mapfields = new LinkedHashMap<String, RecordField>();
初始化时如下:
mapfields.put("vendor_id", new RecordField("vendorID", RecordField.E_NOT_EMPTY|RecordField.E_DIGITAL|RecordField.E_WARN));
mapfields.put("request_time", new RecordField("requestTime", RecordField.E_NOT_EMPTY|RecordField.E_FATAL));
mapfields.put("user_id", new RecordField("userID", RecordField.E_NOT_EMPTY));
mapfields.put("campaign_id", new RecordField("campaignID", RecordField.E_NOT_EMPTY));
异常处理:
RecordField 保存异常检测策略,检测时遍历每个字段,调用其Check方法获得异常结果即可。
交付代码时老员工强制要求使用现有的RTB类,出于尊重,只能用了。但又不想大量的if else语句及字符串的相等判断;于是有了设计二,主要用到了java的反射。
设计二:
linkedhaspmap 保存原始输入与目标输出之间的映射关系;
对现有类(包含近百个public属性字段)的每个属性编写Set方法,方法名为void SetFieldName(String), 基本是体力劳动了,基本是对字段的类型转换复制(上一个设计中为制定 了一些配置规则自动转换)
对每个原始输入字段,找到匹配的目标字段,通过目标字段反射获得其Set方法, 调用Set赋值;静态变量保存反射的函数地址,因反射查找函数的过程较慢
异常检测:对需要做异常检测的字段专门编写其对应的Check方法,并保存需要做检测的字段数组,字段映射完成后遍历需要检测的字段,根据字段名获取对应的Check方法并 执行