一个程序员从Python转向Erlang的自述

摘要:在这篇文章中,我将会讲解我从Python转向Erlang的过程。 [attach]1288[/attach]   概览  在这篇文章中,我将会讲解我从Python转向Erlang的过程。如果你不是一位Python开发人员,或者你不需要或是不想要极度的扩大系统规模,那么这篇文章可能对你没什么用。如果你无意为企业打造基础设施,或是你开发的产品只是简单的博客、小型资产管理系统,或是非常简单的网页,那么这篇文章对你一点帮助都没有。另外,如果你是一名初学者,正考虑选择一种语言进行学习,请你千万不要根据我的这篇文章放弃Python。我要讲的,是我自己使用Python时所遇到的那些问题,以及Erlang帮我解决这些问题的过程。  开始的时候我会讲述一下我的过去,然后用自己的总结来结束这篇文章。如果你在阅读的过程中,能够与我有共鸣,请你来联系我,我们好好聊一聊。正是出于这个目的,我才决定把自己的经验分享出来。 

我的15年编程之路 

最早学习编程的时候,我是用MEL(Maya Embedded Language)起步的。之后,我找到了第一份工作,得到了第一份薪水。不久之后,为了获得更严肃的开发工作,我开始学习Python,并完成了K&R的阅读,使用C语言开发Python扩展程序。许多年之后,我对Web开发产生了浓厚的兴趣。我退出了动画行业,并且加入了德黑兰一家著名的科技公司。  不久之前,我用Python开发了一个名叫Appido.IR的产品,这是一个视频/音乐流媒体服务。    现在来说说我遇到的问题吧。   

选择正确的框架

所有人都热爱Django,但是我却讨厌它,而且没有任何理由!也许是因为帮曾帮助Massimo开发了Web2py,也或许是Web2py的简洁性惯坏了我,让我无法选择另外一个full-stack框架。但是在后来的一个无聊的项目上,我最终还是尝试了Django。  Full-Stack Python框架就是个蜜罐 Django和Web2py各自有什么问题?什么问题都没有!直到你在一些模板引擎和数据库ORM中开始使用Bottle/Falcon之后,你才会开始感觉那些企业框架的速度是如此之慢。在使用一个简单的RESTful API的时候,你不得不浪费你的CPU周期,而且没有好的理由。而在使用复杂的API服务的时候,你必须要找到一条越狱的道路,构建一个全新的架构,然后把它放在你所谓的full-stack框架中。我们来看看下面这个例子:  在Appido.ir Streaming Technology这家公司内,在FFMPEG的和大量其他开源工具的帮助下,我们实施了Dash协议。Appido有自己的OAuth2服务器,这是一个授权系统,一个工作流程引擎,可以用来制定时间表、监测、记录日志、寻找错误、创建报告等等。使用Web2py/Django进行了数周的艰苦研发之后,我们发现只凭借一个单一的框架根本没法成功。在大的框架内创建文件夹,尝试MVC模式、或是创建一个不错的数据库控制面板根本没法帮你扩大规模!而且最后那些你原本不想要的功能会反过头来阻挡你的脚步。是的,你在这里能找到一些蜂蜜,但是你却必须要地方那些愤怒的蜜蜂。因此,我发明了自己的基于Falcon的框架。  Python框架同样也是蜜罐!  于是你开始使用Bottle、Falcon、Flask……然后发现自己需要安装任务队列,并且安排模块时刻表。因为在web 2.0中,每一个需要500ms以上的请求,都需要是有状态的!这是一条不成文的规则。对于长时间等待的请求,你需要给用户提供一些状态。你的客户没时间等待你完成计算过程。你需要将发送邮件、转换图片等负担放在Celery上(也可以是你自己发明的自定义多进程队列)。它有什么问题?我们来看一下:  假设你正在开发一个流媒体服务。你的客户上传了20GB大小的4K视频文件,你将这段视频转换成了10种不同的分辨率,然后发邮件告知客户这个转换过程已经完成了。  由于你有40个worker在使用Celery,在视频转换的过程中那个,服务器出现过载,转换速度越来越慢。于是你找到了一个自以为是天才的解决方案!安装另一台服务器,上面配备了流媒体代码和工具,将Celery作为worker。好了,问题解决了!然而并没有!半夜的时候,你发现6台服务器中,有5台的CPU利用率为0,而第六台的利用率为100%。为什么?原来Redis在配合Celery的时候,会出现时序问题,这个问题会阻碍worker拣选任务。安装RabbitMQ或许能解决问题。但是在寻找蜂蜜的过程中,你依然会被蜜蜂蛰的满身都是包。如果你在搭建这个系统的过程中没有遇到上述问题,那么恭喜你,你是这个世界上最幸运的人。   

我没遇到这些问题!那你请继续……

假如你的服务器运转的很正常,你需要的是增加web服务的RPS,无论你是使用增加WSGI worker的方法,还是使用Tornado/Gevent的方法,两者都可以帮你解决问题。你还会注意到,使用SQLAlchemy会让你的请求速度变慢(因为SQLAlchemy极其复杂)。写Raw SQL命令能解决你的问题。我们看看下面这个简单的例子,一个有着几百万条记录的Postgres数据库:
def  pure_python():
   max_per_task = db.DBSession.query(
       Version.task_id, func.max(Version.version_number).label( 'max'))\
       .join(Task)\
       .filter(Task.project_id == proj_id)\
       .group_by(Version.task_id)\
       .subquery()
    return Version.query\
       .join(max_per_task,
             tuple_(max_per_task.c.task_id, max_per_task.c.max) ==
             tuple_(Version.task_id, Version.version_number))\
       .all()

def  simple_sql():
   sql =  """
    select
        max("Versions".id) as id
    from "Versions"
        join "Tasks" on "Versions".task_id = "Tasks".id
        join "Projects" on "Tasks".project_id = "Projects".id
        where "Projects".id = %s
        group by "Versions".task_id, "Versions".take_name
    """   %  proj_id
   conn =  db.DBSession.connection()
   result =  conn.execute(sql)
    return  result.fetchall()

And Results

pure_python: 3.284  sec
        simple_sql: 0.228 sec 
我已经解决了所有问题。到底哪里出问题了?  你真的解决了所有问题吗?好吧,就算是吧,你现在想要尝试在Python代码中添加一些Erlang功能。你会发现,Erlang中根本不存在那些分配/规模化问题。在Python世界中,寻找规模化问题的解决方式是非常普遍的事情。要想实现规模化,你需要进行分配。而要想分配,你又需要优秀的服务导向架构,而且它还要拥有协同工作的能力。你一定要有足够的耐心,能够忍耐错误的频繁出现,并且做好失效备援。其实在Python中,这些功能都是可以实现的,不过代价非常高。你必须要规避Python的问题,还需要正确的SQL,创建自定义索引。Python的Global Interpreter Lock(GIL)会给你设置障碍。共享状态通常情况下在开始的时候能为你提供一定的帮助,但是有的时候会导致灾难性的后果。除此之外,对于每一次真实世界的计算,你都需要为Python编写扩展程序(原生C API、Swig、Cython或是pypy)。  Erlang虽然没有C语言那么快,但是它的分配模式让我们可以很轻松的编写程序,来保护数据中心中的每一个核心(如果你能理解NIF,你就无往不利了)。用Erlang连接数据库需要你拥有SQL方面的知识(这一点和Python一样)。   

总结 

如果你需要创建一个云服务,或是为了给数以万计的用户提供服务,而需要扩大系统规模,你一定要选择正确的工具。Python在快速测试和模拟方面很强大,学会Python可以帮你找到工作。使用Python你可以在一夜之间就将复杂的创意变成产品。Python适合拥有大量用户的大型项目。但是在大规模扩大系统规模方面,我个人还是觉得它不够好用,有时候甚至会给你制造困难。  长话短说,你可以用Python来找工作,或是获得一份合作合同,之后用Erlang来完成工作。

原    文:WHY AND HOW I SWITCHED FROM PYTHON TO ERLANG 译    文:https://www.sdk.cn/news/4125  作    者:Christian(编译)

0 个评论

要回复文章请先登录注册