PyCon 是全国际最大的以 Python 编程言语 为主题的技能大会。大会由 Python 社区组织,每年举行一次。在大会上,来自国际各地的 Python 用户与中心开发者齐聚一堂,共同同享 Python 国际的新鲜事、Python 言语的应用案例、运用技巧等等内容。

Instagram 简介

<p "="">Instagram 是一款移动端的照片与视频同享软件,由 Kevin Systrom 和 Mike Krieger 在 2010 年创办。Instagram 在发布后开端快速流行。于 2012 年被 Facebook 以 10 亿美元的价格收买。而其时 Instagram 的员工仅有区区 13 名。<p "="">如今,Instagram 的总注册用户到达 30 亿,月活用户超越 7 亿 (作为对比,微信最新披露的月活泼用户为 9.38 亿)。而令人吃惊的是,这么高的拜访量背后,竟彻底是由以速度慢著称的 Python + Django 支撑。<p "="">在 Python 2017 上,Instagram 的工程师们带来了一个有关 Python 在 Instagram 的主题演讲,一起还同享了 Instagram 怎么将整个项目运转环境晋级到 Python 3 的故事。<p "="">本文为该次演讲的内容摘要。

Python @Instagram

为什么挑选 Python 和 Django

<p "="">Instagram 挑选 Django 的原因很简略,Instagram 的两位创始人 (Kevin Systrom and Mike Krieger) 都是产品经理出身。在他们想要创造 Instagram 时,Django 是他们所知道的最稳定和成熟的技能之一。<p "="">时至今日,即使现已具有超越 30 亿的注册用户。Instagram 依然是 Python 和 Django 的重度运用者。Instagram 的工程师 Hui Ding 说到: 『一直到用户 ID 现已超越了 32bit int 的限额(约为 20 亿),Django 自身依然没有成为咱们的瓶颈地点。』<p "="">不过,除了运用 Django 的原生功用外,Instagram 还对 Django 做了许多定制化作业:<ul "="">

Python 言语的优势地点

<p "="">Instagram 的联合创始人 Mike Krieger 说过: 『咱们的用户根本不关心 Instagram 运用了哪种联系数据库,他们当然也不关心 Instagram 是用什么编程言语开发的。』<p "="">所以,Python 这种 简略 并且 实用至上 的编程言语终究赢得了 Instagram 的喜爱。他们以为,运用 Python 这种简略的言语有助于刻画 Instagram 的工程师文明,那就是:<ol "="">

  • 专心于定位问题、处理问题 - 而不是东西自身的各种花花绿绿的特性
  • 运用那些经过商场验证过的成熟技能计划 - 而不用被东西自身的问题所干扰
  • 用户至上:专心于用户所能看到的新特性,为用户带去价值

<p "="">可是,即使运用 Python 言语有这么多优点,它仍是很慢,不是吗?<p "="">不过,这关于 Instagram 不是问题,由于他们以为:『Instagram 的最大瓶颈在于开发功率,而不是代码的履行功率』<blockquote "="">

At Instagram, our bottleneck is development velocity, not pure code execution.

<p "="">所以,终究的结论是:你彻底能够运用 Python 言语来完结一个超越几十亿用户运用的产品,而根本不用忧虑言语或框架自身的功用瓶颈。

怎么提高运转功率

<p "="">可是,即就是选用了具有诸多优点的 Python 和 Django。在 Instagram 的用户数迅速增长的进程中,功用问题仍是呈现了:效劳器数量的增长率现已慢慢的超越了用户增长率。Instagram 是怎么应对这个问题的呢?<p "="">他们运用了这些手法来缓解功用问题:<ul "="">

  • 开发东西来协助调优:Instagram 开发了许多包括各个层面的东西,来协助他们进行功用调优以及找到功用瓶颈。
  • 运用 C/C++ 来重写部分组件:把那些稳定并且对功用最敏感的组件,运用 C 或 C++ 来重写,比方拜访 memcache 的 library。
  • 运用 Cython:Cython 也是他们用来提高 Python 功率的法宝之一。

<p "="">除了上面这些手法,他们还在探索异步 IO 以及新的 Python Runtime 所能带来的功用可能性。

晋级到 Python 3

<p "="">在相当长的一段时间,Instagram 都跑在 Python 2.7 + Django 1.3 的组合之上。在这个现已落后社区许多年的环境上,他们的工程师们还打了十分十分多的小 patch。难道他们要被永久卡在这个版别上吗?<p "="">所以,在经过一系列的评论后,他们终究做出一个重大的决议:晋级到 Python 3!!<p "="">事实上,Instagram 现在现已完结了将运转环境搬迁到 Python 3 的作业 - 他们的整套效劳现已在 Python 3 上跑了好几个月了。那么他们是怎么做到的呢?接下来就是由 Instagram 工程师 Lisa guo 带来的 Instagram 怎么搬迁到 Python 3 的故事。

Instagram 晋级到 Python 3 的故事

为什么要晋级到 Python 3

<p "="">关于 Instagram 来说,下面这些因素是推动他们将运转环境搬迁到 Python 3 的主要原因:

1. 新特性:类型注解 Type Annotations

<p "="">看看下面这段代码:

def compose_from_max_id(max_id): '''@param str max_id''' 

<p "="">图中函数的 max_id 参数究竟是什么类型呢?int?tuple?或是 list? 等等,函数文档里边说它是 str 类型。<p "="">但随着时间推移,万一这个参数的类型发作改变了呢?假如某位大意的工程师修正代码的一起忘了更新文档,那就会给函数的运用者带来很大麻烦,终究还不如没有注释呢。

2. 功用

<p "="">Instagram 的整个 Django Stack 都跑在 uwsgi 之上,全部运用了同步的网络 IO。这意味着同一个 uwsgi 进程在同一时间只能接收并处理一个恳求。这让怎么调优每台机器上应该运转的 uwsgi 进程数成了一个麻烦事:<p "="">为了更好使用 CPU,运用更多的进程数?但那样会消耗许多的内存。而过少的进程数量又会导致 CPU 不能被充分使用。<p "="">为此,他们决议跳过 Python 2 中哪些糟糕的异步 IO 完结 (不幸的 gevent、tornado、twisted 众),直接晋级到 Python 3,去探索规范库中的 asyncio 模块所能带来的可能性。

3. 社区

<p "="">由于 Python 社区现已中止了对 Python 2 的支撑。假如把整个运转环境晋级到 Python 3,Instagram 的工程师们就能和 Python 社区走的更近,能够更好的把他们的作业回馈给社区。

断定搬迁计划

<p "="">在 Instagram,进行 Python 3 的搬迁需求有必要满意两个前提条件:<ol "="">

  • 不停机,不能有任何的效劳因而不可用
  • 不能影响产品新特性的开发

<p "="">可是,在 Instagram 的开发环境中,要满意上面这两点来完结搬迁到 Python 3.6 这种巨大的工程是十分困难的。

根据主分支的开发流程

<p "="">即使运用了以多分支功用著称的 git,Instagram 一切的开发作业都是主要在 master 分支上进行的,Instagram 所奉行的开发哲学是:『不管是多大的新特性或代码重构,都应该拆解成较小的 Commit 来进行。』<p "="">那些被兼并进 master 分支的代码,都将在一个小时内被发布到线上环境。而这样的发布进程每天将会发作上百次。在这么频频的发布频率下,怎么在满意之前的那两个前提下来完结搬迁变得特别困难。

被弃用的搬迁计划

<p "="">创立一个新分支<p "="">许多人在处理这类问题时,榜首个蹦进脑子的主意就是: 『让咱们创立一个分支,当咱们开发完后,再把分支兼并进来』<p "="">但在 Instagram 这么高的迭代频率上,运用一个独立分支并不是好主意:<ol "="">

  • Instagram 的 Codebase 每天都在频频更新,在开发 Python 3 分支的进程中,让新分支与现有 master 分支坚持同步开支极大,一起极易犯错
  • 终究将 Python 3 分支这个改动十分多的分支兼并回 Master 具有十分高的风险
  • 只需少量几个工程师在 Python 3 分支上专职担任晋级作业,其他想协助搬迁作业的工程师无法参与进来

<p "="">挨个替换接口<p "="">还有一个计划就是,挨个替换 Instagram 的 API 接口。可是 Instagram 的不同接口同享着许多通用模块。这个计划要施行起来也十分困难。<p "="">微效劳<p "="">还有一个计划就是将 Instagram 改造成微效劳架构。经过将那些通用模块重写成 Python 3 版别的微效劳来一步步完结搬迁作业。<p "="">可是这个计划需求重新组织海量的代码。一起,当发作在进程内的函数调用变成 RPC 后 ,整个站点的推迟会变大。此外,更多的微效劳也会引进更高的部署杂乱度。<p "="">所以,已然 Instagram 的开发哲学是:小步前进,快速迭代。他们终究决议的计划是:一步一步来,终究让 master 分支上的代码一起兼容 Python 2 和 Python 3 。

开端搬迁作业

<p "="">已然要让整个 codebase 一起兼容 Python 2 和 Python 3,那么首先要契合这点的就是那些被许多运用的第三方 package。针对第三方 package,Instagram 做到了下面几点:<ul "="">

  • 拒绝引进一切不兼容 Python 3 的新 package
  • 去掉一切不再运用的 package
  • 替换那些不兼容 Python 3 的 package

<p "="">在代码的搬迁进程中,他们运用了东西 modernize 来协助他们。<p "="">运用 modernize 时,有一个小技巧:每次修复多个文件的一个兼容问题,而不是一下修复一个文件中的多个兼容问题。 这样能够让 Code Review 进程简略许多,由于 Reviewer 每次只需求关注一个问题。

运用单元测试来协助搬迁

<p "="">关于 Python 这种灵活性极强的动态言语来说,除了真实去履行代码外,几乎没有其他比较好的查看代码错误的手法。<p "="">前面提到,Instagram 一切被兼并到 master 的代码提交会在一个小时内上线到线上环境,但这不是没有前提条件的。在上线前,一切的提交都需求经过不计其数个单元测试。<p "="">所以,他们开端参加 Python 3 来履行一切的单元测试。一开端,只需极少量的单元测试能够在 Python 3 环境下经过,但随着 Instagram 的工程师们不断的修复那些失败的单元测试,终究一切的单元测试都能够在 Python 3 环境下成功履行。

单元测试的局限性

<p "="">可是,单元测试也是有局限性的:<ul "="">

  • Instagram 的单元测试没有做到 100% 的代码覆盖率
  • 许多第三方模块都运用了 mock 技能,而 mock 的行为与真实的线上效劳可能会有所不同

<p "="">所以,当一切的单元测试都被修复后,他们开端在线上正式运用 Python 3 来运转效劳。<p "="">这个进程并不是一蹴而就的。首先,一切的 Instagram 工程师开端拜访到这些运用 Python 3 来履行的新效劳,然后是 Facebook 的一切雇员,随后是 0.1%、20% 的用户,终究 Python 3 覆盖到了一切的 Instagram 用户。

图:按部就班的发布流程

搬迁进程的技能问题

<p "="">Instagram 在搬迁到 Python 3 时碰到许多问题,下面是最典型的几个:

Unicode 相关的字符串问题

<p "="">Python 3 相比 Python 2 最大的改动之一,就是在言语内部咨询入库对 unicode 的处理。<p "="">在 Python 2 中,文本类型 (也就是 unicode) 和二进制类型 (也就是 str) 的边界十分模糊。许多函数的参数既能够是文本,也能够是二进制。可是在 Python 3 中,文本类型和二进制类型的字符串被彻底的区分开了。<p "="">所以,下面这段在 Python 2 下能够正常运转的代码在 Python 3 下就会报错:

mymac = hmac.new('abc')
TypeError: key: expected bytes or bytearray, but got 'str'

<p "="">处理办法其实很简略,只需加上判别:假如 value 是文本类型,就将其转换为二进制。如下所示:

value = 'abc' if isinstance(value, six.text_type):
value = value.encode(encoding='utf-8')
mymac = hmac.new(value)

可是,在整个代码库中,像上面这样的状况十分多。作为开发人员,假如需求在调用每个函数时都要想想: 这里到底是应该编码成二进制,或者是解码成文本呢? 将会是十分大的负担。

<p "="">所以 Instagram 封装了一些名为 ensure_str()、ensure_binary()、ensure_text() 的协助函数,开发人员只需对那些不断定类型的字符串,运用这些协助函数先做一次转换就好。

mymac = hmac.new(ensure_binary('abc'))

不同 Python 版别的 pickle 差异

<p "="">Instagram 的代码中许多运用了 pickle。比方用它序列化某个目标,然后将其存储在 memcache 中。如下面的代码所示:

memcache_data = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
data = pickle.loads(memcache_data)

问题在于,Python 2 与 Python 3 的 pickle 模块是有不同的。

<p "="">假如上文的榜首行代码,刚好是由 Python 3 运转的效劳进行序列化后存入 memcache。而反序列化的进程却是由 Python 2 进行,那代码运转时就会呈现下面的错误:

ValueError: unsupported pickle protocol: 4 

<p "="">这是由于在 Python 3 中,pickle.HIGHEST_PROTOCOL 的值为 4,而 Python 2 中的的 pickle 最高支撑的版别号却是 2。那么怎么处理这个问题呢?<p "="">Instagram 终究挑选让 Python 2 和 Python 3 运用彻底不同的 namespace 来拜访 memcache。经过将二者的数据读写彻底隔开来处理这个问题。

迭代器

<p "="">在 Python 3 中,许多内置函数被修正成了只返成迭代器 Iterator:

map()
filter()
dict.items()

迭代器有诸多优点,最大的优点就是,运用迭代器不需求一次性分配许多内存,所以它的内存功率比较高。

<p "="">可是迭代器有一个天然的特点,当你对某个迭代器做了一次迭代,拜访完它的内容后,就无法再次拜访那些内容了。迭代器中的一切内容都只能被拜访一次。<p "="">在 Instagram 的 Python 3 搬迁进程中,就由于迭代器的这个特性被坑了一次,看看下面这段代码:

CYTHON_SOURCES = [a.pyx, b.pyx, c.pyx]
builds = map(BuildProcess, CYTHON_SOURCES) while any(not build.done() for build in builds):
pending = [build for build in builds if not build.started()]

<p "="">这段代码的用处是挨个编译 Cython 源文件。当他们把运转环境切换到 Python 3 后,一个古怪的问题呈现了:CYTHON_SOURCES 中的榜首个文件永久都被跳过了编译。为什么呢?<p "="">这都是迭代器的锅。在 Python 3 中,map() 函数不再回来整个 list,而是回来一个迭代器。<p "="">所以,当第二行代码生成 builds 这个迭代器后,第三行代码的 while 循环迭代了 builds,刚好取出了榜首个元素。所以之后的 pending 目标便里边永久少了那榜首个元素。<p "="">这个问题处理起来也挺简略的,你只需手动的吧 builds 转换成 list 就能够了:

builds = list(map(BuildProcess, CYTHON_SOURCES))

<p "="">可是这类 bug 十分难定位到。假如用户的 feeds 里边永久少了那最新的榜首条,用户很少会注意到。

字典的次序

<p "="">看看下面这段代码:

>>> testdict = {'a': 1, 'b': 2, 'c': 3} >>> json.dumps(testdict)

<p "="">它会输出什么成果呢?

# Python2 '{"a": 1, "c": 3, "b": 2}' # Python 3.5.1 '{"c": 3, "b": 2, "a": 1}' # or '{"c": 3, "a": 1, "b": 2}' # Python 3.6 '{"a": 1, "b": 2, "c": 3}' 

<p "="">在不同的 Python 版别下,这个 json dumps 的成果是彻底不一样的。甚至在 3.5.1 中,它会彻底随机的回来两个不同的成果。Instagram 有一段判别装备文件是否发作改变的模块,就是由于这个原因出了问题。<p "="">这个问题的处理办法是,在调用 json.dumps 传入 sort_keys=True 参数:

>>> json.dumps(testdict, sort_keys=True) '{"a": 1, "b": 2, "c": 3}' 

搬迁到 Python 3.6 后的功用提高

<p "="">当 Instagram 处理了这些奇古怪怪的版别差异问题后,还有一个巨大的谜题困扰着他们:功用问题。<p "="">在 Instagram,他们运用两个主要指标来衡量他们的效劳功用:<ul "="">

  • 每次恳求发生的 CPU 指令数(越低越好)
  • 每秒能够处理的恳求数(越高越好)

<p "="">所以,当一切的搬迁作业完结后,他们十分惊喜的发现:榜首个功用指标,每次恳求发生的 CPU 指令数居然足足下降了 12% !!!<p "="">可是,按理说第二个指标 - 每秒恳求数也应该获得挨近 12% 的提高。不过最后的改变却是 0%。究竟是出了什么问题呢?<p "="">他们终究定位到,是由于不同 Python 版别下的内存优化装备不同,导致 CPU 指令数下降带来的功用提高被抵消了。那为什么不同 Python 版别下的内存优化装备会不一样呢?<p "="">这是他们用来查看 uwsgi 装备的代码:

if uwsgi.opt.get('optimize_mem', None) == 'True':
optimize_mem()

注意到那段 ... ... == 'True' 了吗?在 Python 3 中,这个条件判别总是不会被满意。问题就在于 unicode。在将代码中的 'True' 换成 b'True'(也就是将文本类型换成二进制,这种判别在 Python 2 中彻底不区分的)后,问题处理了。

<p "="">所以,终究由于加上了一个小小的字母 'b',程序的整体功用提高了 12%。

结论

<p "="">在今年二月份,Instagram 的后端代码的运转环境彻底切换到了 Python 3 下: 

图:Instagram 版别搬迁时间线

<p "="">当一切的代码都都搬迁到 Python 3 运转环境后:<ul "="">

  • 节省了 12% 的整体 CPU 运用率(Django/uwsgi)
  • 节省了 30% 的内存运用(celery)

<p "="">一起,在整个搬迁期间,Instagram 的月活用户阅历了从 4 亿到 6亿 的巨大增长。产品也发布了评论过滤、直播等十分多新功用。<p "="">那么,那几个最开端驱动他们搬迁到 Python 3 的意图呢?<ul "="">

  • 类型注解:Instagram 的整个 codebase 里现已有 2% 的代码增加上了类型注解,一起他们还开发了一些东西来辅佐开发者增加类型提示
  • asyncio:他们在单个接口中使用 asynio 平行的去做多件作业,终究降低了 20-30% 的恳求推迟。
  • 社区:他们与 Intel 的工程师联合,协助他们更好的对 CPU 使用率进行调优。一起还开发了许多新的东西,协助他们进行功用调优

Instagram 带给咱们的启示

<p "="">Instagram 的演讲视频时间不长,可是内容很丰富,在编写此文前,我彻底没有想到终究的文章会这么长。<p "="">那么,Instagram 的视频能够给咱们哪些启示呢?<ul "="">

  • Python + Django 的组合彻底能够负载用户数以 10 亿记的效劳,假如你正准备开端一个项目,放心运用 Python 吧!
  • 完善的单元测试关于杂乱项目是十分有必要的。假如没有那『不计其数的单元测试』。很难幻想 Instagram 的搬迁项目能够成功进行下去。
  • 开发者和同事也是你的产品用户,使用好他们。用他们为你的新特性发布前多一道测试。
  • 彻底根据主分支的开发流程,能够给你更快的迭代速度。前提是具有完善的单元测试和继续部署流程。
  • Python 3 是大势所趋,假如你正准备开端一个新项目,无需踌躇,拥抱 Python 3 吧!

<p "="">好了,就到这儿吧。Happy Hacking!

PyCon大会Python主题演讲摘要的更多相关文章

  1. Instagram 在 PyCon 2017 的演讲摘要

    Instagram 在 PyCon 2017 的演讲摘要 PyCon 简介 PyCon 是全世界最大的以 Python 编程语言 为主题的技术大会.大会由 Python 社区组织,每年举办一次.在大会 ...

  2. 从程序到系统:建立一个更智能的世界——记Joseph Sifakis“21世纪的计算”大会主题演讲

    Sifakis"21世纪的计算"大会主题演讲" title="从程序到系统:建立一个更智能的世界--记Joseph Sifakis"21世纪的计算&q ...

  3. 由情感计算带来的惊喜发现——记Rosalind W. PICARD“21世纪的计算”大会主题演讲

    W. PICARD"21世纪的计算"大会主题演讲" title="由情感计算带来的惊喜发现--记Rosalind W. PICARD"21世纪的计算& ...

  4. 华为开发者大会主题演讲:3D建模服务让内容高效生产

    内容来源:华为开发者大会2021 HMS Core 6 Graphics技术论坛,主题演讲<3D建模服务使能3D内容高效生产>. 演讲嘉宾:华为消费者云服务 AI算法专家 3D建模服务(3 ...

  5. Connect() 2016 大会的主题 ---微软大法好

    文章首发于微信公众号"dotnet跨平台",欢迎关注,可以扫页面左面的二维码. 今年 Connect 大会的主题是 Big possibilities. Bold technolo ...

  6. 原生化:AnDevCon 2014 McVeigh 的主题演讲

    作者:Jeff McVeigh(Intel) 基于(至少部分)NDK的原生安卓应用程序占现在前1000 强的 60% 以上.该增长的原因很简单:开发商需要为用户提供超卓的体验(包括灵敏的反应.与丰富的 ...

  7. 【独家】硅谷创业公司在中国常跌的五个坑|禾赛科技CEO李一帆柏林亚太周主题演讲

    [独家]硅谷创业公司在中国常跌的五个坑|禾赛科技CEO李一帆柏林亚太周主题演讲 李一帆 Xtecher特稿作者 关注  Xtecher推荐   演讲者:李一帆   翻译:晓娜   网址:www.xt ...

  8. 第十六届“二十一世纪的计算”学术研讨会 密西根州立大学教授Anil K. Jain主题演讲

    Biometrics---How Do I Know Who You Are? 密西根州立大学教授Anil K. Jain主题演讲" title="第十六届"二十一世纪的 ...

  9. 第十六届“二十一世纪的计算”学术研讨会 图灵奖获得者Butler W. Lampson主题演讲

    Personal Control of Digital Data 图灵奖获得者Butler W. Lampson主题演讲" title="第十六届"二十一世纪的计算&qu ...

随机推荐

  1. yii表单输入元素

    InputElement http://www.yiichina.com/api/CFormInputElement CFormInputElement 可以代表以下基于type属性的表单输入类型: ...

  2. python2.7与3.5版本中:编码格式及编码转换

    主要说明编码之间的转换方法 2.7版本: 1 # -*- coding:utf-8 -*- 2 a = "迪丽热巴" 3 a_unicode = a.decode("ut ...

  3. zookeeper(2) zookeeper的核心原理

    zookeeper 的前世今生 分布式系统的很多难题,都是由于缺少协调机制造成的.在分布式协调这块做得比较好的,有 Google 的 Chubby 以及 Apache 的 Zookeeper. Goo ...

  4. 1. Java 基 础 部 分

    java" 源 文 件 中 是 否 可 以 包 括 多 个 类 ( 不 是 内 部 类 ) ? 有 什 么 限 制 ? 可以有多个类,但只能有一个 public 的类,并且 public 的 ...

  5. CSS3 过渡动画

    实现如下效果:当鼠标移动到图片上是图片有类似于放大镜放大的效果 transition : CSS属性 时间 当transition中监测的css属性发生变化时,会触发动画 .img_box img{ ...

  6. ThinkPHP3.2项目模块结构

    Demo --项目目录 Addons --插件目录 Application --应用模块目录 Admin --后台模块 Common --后台公共函数目录 function.php (可选) Conf ...

  7. delphi 第三方组件 log4cpp.dll

    log4cpp.dll LogHelper.dll TcxGridSite TcxSpreadSheetBook TcxSpreadSheetBook TcxTreeList TcxButtonEdi ...

  8. 通过Sonar的代码质量报告学习【如何写安全高质量的代码】

    1.不要用.size(),改用isEmpty() Using Collection.size() to test for emptiness works, but using Collection.i ...

  9. js ajax 数据获取

    在js中应用ajax 获取数据的方法,也写一个出来供复习所用 1.建议一个user.json 文件如下,保存名字为 user.json { "name": "huanyi ...

  10. Flex_概念

    1.Flex是事件驱动的面向对象应用程序框架和编程语言.Flex应用程序加载完毕后,需要做的就是捕获事件,然后作出响应.    Flex是一个庞大的技术组群中的一员.  2.RIA(Rich Inte ...