美文网首页bigflow程序员
bigflow开源~破茧成蝶

bigflow开源~破茧成蝶

作者: 苗栋栋 | 来源:发表于2017-11-03 11:01 被阅读0次

    本文会穿插各种技术问题


    先对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<str_name,Base*>,子类对应的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 <local branch>:master
    更新代码:
    git checkout master && git pull origin master && git checkout <local branch> && 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://www.haomeiwen.com/subject/mrsgpxtx.html