bigflow开源~破茧成蝶

bigflow开源~破茧成蝶_第1张图片

本文会穿插各种技术问题


先对bigflow 做个简单介绍:

Baidu Bigflow Python是一个Python module,利用module中的数据抽象和API,你可以书写自己的计算任务。它提供了一套函数式编程风格的API,同时将这些API串联/级联成为数据流管道(bigflow pipeline)。Bigflow Python能够将Pipeline映射成为不同分布式计算引擎上的计算任务,例如(Spark,目前开源版本只支持local模式和spark模式),极大地简化分布式计算程序的编写和维护。
使用Bigflow Python主要的收益有:

  • 简单易用的API
    Bigflow Python提供了对分布式数据和计算的高层抽象API,易学易懂;同时作为一个Python module,你可以在Python交互Shell中使用它。
  • 任务自动优化
    所有的计算均为惰性求值(lazy evaluation),Bigflow Python能够看到尽可能大的计算过程并进行关键参数的自动优化,使用Bigflow Python写出的任务能够与那些对特定引擎非常有经验的程序员相当。
    在百度公司内部使用情况来看,Bigflow比用户手工实现的作业性能提升50%~200%。Bigflow开源版本Benchmark正在准备中,敬请期待。

介绍完bigflow,让时间回到一个月前~

当时团队在一起把排期一碰,开始了一场新的征程。

对于项目来说,最重要的两件事是:
一是编译通过&&跑通集成测试,二是优化用户体验。

编译

protobuf问题

遇到的第一个问题是protobuf version的问题。spark 自己的protobuf version 是2.5.0,其他的有用到的版本有2.4.1,有3.x.1。出现了一个问题是3.4.1编译出的 xx.pb.cc, xx.pb.h,被用到了2.4.1中。

最后统一成了2.5.0。解决问题。

顺便还解决了一个问题 java/scala包(A)的protobuf依赖问题。 我们提供了一个c++的so, 然后用A包,去调用so里的函数,但A包里没有protobuf的依赖,需要用spark的protobuf依赖。

之前用的是2.4.1的egg包。后改成protobuf-2.5.0-py2.7.egg
因为我们的protoc 用的是2.5.0的。*.py, .c,.java 等都是其生成的,要对应使用这些文件的程序,必须依赖protobuf 2.5.0.

fPIC

我们编译so,但有一些依赖没有加fPIC。然后添加fPIC。 最难搞的是hdfs的lib,并没有添加fPIC编译参数。然后只能改CMakeLists.txt——`add_compile_options(-fPIC -fpermissive)。

代码漏合

有些代码由于合并方式选择不太对,导致代码漏合。
后面会说一下整体的解决方案。目前问题还不是太大,只是个别文件,所以先手动合并了一下。

thrift

thrift 下载很慢,以及版本不统一。
更换为清华的镜像,并统一为0.9.1版本。

依赖python统一

之前依赖python,分不清楚是依赖的系统的还是自己thirdparty。担心后面同样会带来不可预期的问题。然后把统一改为依赖thirdparty中python2.7,并改为动态依赖。

去 Profiler util 相关代码

目前开源版本并没有添加相关优化,讨论了下,先去掉,后面在考虑添加。

一次编译一个新包

保障更新的代码会被更新到使用tar里面。因为一旦发现包没有完全更新,整个流程就得重新走一边。

这样做可以大大降低成本。

集成测试

spark 集群问题

运行时发现java报错为高版本编译出的结果在低版本jre上运行。
我们编译用的java 是1.8.x的,是那个程序用的是1.7.x一开始没有找到。
这里面有一个大坑是spark web页面上显示的是1.8.x,但实际上是1.7.x。
登录机器发现,yarn 运行是1.7.x。这个是写死在yarn-env.sh中的。这个真的是个大坑哈~~

改了之后,解决问题~

厂内外 hadoop put 的差异

提交作业时,发现报目录不存在。 然后定位发现是:

场内的hadoop put 当目录不存在时,会先创建目录。而社区的需要提前创建目录,再put。

解决方案:在put前,检查目录是不是存在,不存在则用mkdir先创建。

libflpyrt 名字变更

由于libflpyrt.so 和厂内保持不一致导致,作业无法运行。

spark.executor.extraLibraryPath

运行时发现找不到对应的lib库,定位后,发现spark.executor.extraLibraryPath 设置有问题,发现和厂内版本不一致。 也是代码合并时,漏合了。

static 静态变量初始化问题

  • 背景:
    我们的libbflpyrt.so 需要被加载到spark中运行,so中需要初始化python interpreter。 这个初始化。本需要在spark executor main函数中执行,但是main函数在spark中,无法修改。 以前的做法,是在static 静态变量初始化时,完成python interpreter的初始化。
  • 问题:
    python interpreter 初始化时,需要用到gflags、glog的库,而这些库,并没有别初始化。
  • 方案:
    对这样的问题,最好是spark executor 提供相关的接口,初步了解了一下,并没有发现。 目前使用相同的方法实现的:
    在task build时,进行初始化,在task release时,进行释放。这样变成了task级别。

问题暂时得到了解决,但在后面跑case的过程中,出现了不可预期的问题——id()函数不可被调用。

这个问题发生,是那么的神奇,感觉简直就是不可思议,完全没有可能。在没有探索之中,想起了之前解决问题时,看到了这么一段话:

Some memory allocated by extension modules may not be freed. Some extensions may not work properly if their initialization routine is called more than once; this can happen if an application calls Py_Initialize()
and Py_Finalize()
more than once.

前面task中,python interper被多次初始化和释放。现在,不就是被多次调用吗,也许就是它,然后改策略为,在task build 中初始化一次, 相当于在main函数中调用初始化。

抽象出一个可以相当于在main函数中调用的全局方式。后面再有这样的调用可以直接添加。至于析构,进程都没有了,随它去吧~

后面继续寻求更优雅的方式。

还有个问题,虽然现在没有了,也记录一下:

中间还遇到interpreter 在析构时,遇到protobuf c 已经析构的问题。本质上也是一个静态变量析构顺序的问题,protobuf 在 interpreter 之前释放了。
解决方法:
protobuf-2.5.0-py2.7.egg中有两种实现方式,c和python的实现。
spark提供了一个可以指定protobuf 使用实现语言的接口,我们指定为python.
"spark.executorEnv.PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION":"python"

添加 LINK_ALL_SYMBOLS

在链接时,只会添加用到的符号,在代码中并没有用到的类,并不会添加相应符号。

具体场景是这样的。libA.a 依赖 libB.a 。在libB.a中有Base类 和其子类Sub1、Sub2,还有一个map,子类对应的Base*是在自己的文件中添加到map函数中的。但在libA.a 只有Base *指针, 通过str_name 在map找对应子类(Sub1)的Base*

这样在libA.a并没有用到Sub1, 所以在编译的时候,并没有在把Sub1的符号打到libB.a中,在map中也就没有对应的值,最终导致找不到符号。

一个解决方法,是把符号先都打进来。目前用的是这个。

但感觉应该会有更优雅的方案,后面在了解一下。

注:由于LINK_ALL_SYMBOLS,最终是要打到so里的,就引发了前面介绍的fPIC问题

version 自动生成

为了让用户提交作业更快,bigflow的相关包,并不会每次提交,只有version改变了才会提交。
但每次生成包时,version又没有变化,每次都要手动去生成。这个一旦忘记,成本会很大,很容易出现测试半天,白测的情况。

从手动改成自动生成。

urllib2.urlopen 超时时间。

urllib2.urlopen 需要设置超时时间,但作业跑的时间又不确定,目前先设置为365天。

python 报错not found Default()

因为没有上传,google.protobuf.json_format相关包。

改成了只在使用json_format代码处进行import。这样,json_format相关的函数只会在本地使用,远端并不会调用。

import google.protobuf.json_format as json_format
      res = json_format.Parse(response, service_pb2.VoidResponse())

顺便解决了,google.protobuf.json_format (3.x.x)和protobuf-2.5.0-py2.7.egg的冲突问题。

这里要感谢xianjin的帮助。

hadoop 配置路径

从 conf 变更到etc/hadoop 和场外保持一致。

代码合并

关于厂内外代码合并,这就是一个大坑,之前手动merge,而且还是部分merge,厂外代码也做了修改。导致后续merge无法进行。 而且测试中遇到很多的问题,也是由于merge代码不全导致。

后面还是主席,把厂内的代码做一个patch,然后提交到厂外。这样合并代码的问题才被解决。

开发模式

大家在一个主干上开发,导致有覆盖代码的情况出现。
然后,采用拉本地分支的方式来做:
提交代码:
git push :master
更新代码:
git checkout master && git pull origin master && git checkout && git rebase master

后面发现pull request 也很不错:
自己拉一个远程分支,提交后,手动创建pull request,可以让他人评审。

优化用户体验

bigflow 站点,中文网站自动生成脚本构建。

sphinx 编译时,报找不到'sphinxcontrib.napoleon'
换成'sphinx.ext.napoleon'即可。

主题没有了,换成了html_theme = 'bizstyle'

友好提示

提醒用户设置 JAVA_HOME、HADOOP_HOME。

其他开源准备工作

  • doc 目录变更到根目录下。
  • 修改example,保障正常运行。
  • 删除 dw 等无关代码
  • 添加 license文件 和在源文件中添加 copyright
    • 功能太复杂,用sed 就是一个大坑。 可以考虑sublime,神器。

bigflow 开源版本运行问题基本完成。优化用户体验工作已经做了部分,后面还有需要继续完善。

感谢

感谢jianwei支持
感谢yuncong给予了许多帮助。
感谢 lilishanhui并肩协作以及前期准备。
感谢xianjin 帮忙定位google.protobuf.json_format 等问题。
感谢gonglin 帮忙值周。

感谢团队中每个一起并肩奋斗的伙伴~

小结

bigflow 开源,已经踏出了第一步。

星星之火,开始燎原~

附录

  • bigflow 官网: https://baidu.github.io/bigflow/zh/index.html

  • github 地址:https://github.com/baidu/bigflow/

你可能感兴趣的:(bigflow开源~破茧成蝶)