三个晚上与三个星期。
这是我的系列文章中的第四篇博客文章,以五种不同的语言实现同一Web服务器。 先前的文章是:
- Clojure :“使用Clojure和ClojureScript成为全栈开发人员!”
- Javascript / Node :“ Node Land中Java Man的邪恶任务”
- Java :“ Java Man感到困惑”
这篇博客文章是关于我的第四种语言Python的 。 我并不是为了学习Python而做这个练习的-我已经使用Python编程了大约20年了。 我想实现相同的Web服务器,只是为了了解与以前的Java实现相比,实现它的速度。 结果大约是3个晚上(Python)– 3个星期(Java)。 Java的悲惨故事,Python的伟大胜利。
您可以在Github中找到该项目。
我再次尝试复制文件/命名空间/类名称,以便比较实现(例如server.py-server.js-server.clj-Server.java)。


实现此简单服务器时,我在Ubuntu18上使用了Python 3.6.6。 我使用Python虚拟环境来保持我的Ubuntu安装清洁:
python3 --version =>检查您的Python版本(我的版本是3.6.6)。
virtualenv --version =>检查您的虚拟环境版本(我的是16.0.0)。
哪个python3 => python3在哪里? (我的在/ usr / bin / python3中)。
virtualenv -p / usr / bin / python3 venv =>创建虚拟环境。
来源venv / bin / activate =>激活您的虚拟环境
python3-版本? => Python 3.6.6(在此虚拟环境中)。
pipenv install flask =>使用pipenv安装软件包。
ls -l Pipfile * =>列出生成的Pipfile。
停用=>离开虚拟环境。
因此,我们完成了对“ Python虚拟环境和包管理”的简短浏览。
对于那些不了解Python的人,我告诉他们有两个主要的Python版本:2和3-它们不兼容。 建议始终尽可能使用较新的版本3。
为了加速REST实现,我决定使用Flask(版本1.0.2),因为它是为此目的而广泛使用的Python微框架。
我使用PyCharm进行Python编程(在远程* nix机器Emacs上)。 PyCharm是用于Python编程的非常好的IDE –编辑器很棒,并且有许多实用程序可以使您的Python编程更加高效(代码完成,测试运行器,自动Linter(PEP),出色的调试器,Python控制台(REPL)等) )。
我使用IntelliJ IDEA进行Java编程,由于PyCharm和IDEA由同一家公司(JetBrains)提供,因此它们提供非常相似的外观。 我还将IntelliJ IDEA和草书插件一起用于Clojure编程,它也提供了非常相似的外观。 因此,将相同的IDE用于多种编程语言有很多协同优势。
PEP8为Python编程提供了权威的样式指南。 PyCharm提供了与此样式指南相关的Python代码自动整理功能。
这些测试是使用pytest实现的。 Pytest非常易于使用,而且PyCharm还提供了与使用pytest(调试器等)实现的单元测试的良好集成。 在PyCharm中,只需创建一个新的pytest配置并将其配置为使用您的特定单元测试文件,您便可以运行该测试及其在调试器中调用的代码。
让我们比较一下在控制台中运行整个测试套件时不同实现之间的测试性能:
Clojure :
时间./run-tests.sh
19:52:18.637 [main]信息simpleserver.util.prop-使用poperty文件:resources / simpleserver.properties
lein测试simpleserver.testutils.users-util
莱因测试simpleserver.userdb.users-test
莱因测试simpleserver.webserver.server-test
莱因测试simpleserver.webserver.session-test
跑了11个测试,其中包含47个断言。
0个故障,0个错误。
真正的0m6.027s
Java的 :
$ time ./gradlew-重新运行任务测试
测试结果:成功
测试摘要:15个测试,15个成功,0个失败,0个被跳过
在5秒钟内成功建立
5个可执行任务:5个已执行
真正的0m5.757s
Javascript :
时间./run-tests-with-trace.sh
28通过(94ms)
真正的0m0.775s
Python :
时间./run-pytest.sh
===========================================测试会话开始
平台linux-Python 3.6.6,pytest-3.9.3,py-1.7.0,pluggy-0.8.0
rootdir:/ mnt / edata / aw / kari / github / python / webstore-demo / simple-server,inifile:setup.cfg
收集了14个项目
tests / domaindb / test_domain.py .... [28%]
tests / userdb / test_users.py ... [50%]
tests / webserver / test_server.py ...... [92%]
tests / webserver / test_session.py。 [100%] ========================================= 14个通过0.11秒
真正的0m0.416s
结果是:


很明显,由于加载了JVM,Clojure和Java输掉了比赛。 但是令我惊讶的是,Python运行得如此之快。
Python REPL是我在Lisp世界之外使用的最好的REPL之一。 在Lisp世界中,REPL是真实的REPL,它使您可以用其他REPL或调试器无法用其他语言进行的方式在实时系统上进行实验-很难解释这一点,您只需学习一些Lisp并尝试一下即可(例如Clojure)。 因此,现在我们已经完成了我的强制性Clojure广告,让我们回到Python REPL。 与JShell相比,Java REPL Python更胜一筹。 Python REPL是Python的第一个版本(我们只需要等待20年才能使用Java REPL),并且由于Python是动态类型的语言,因此REPL非常易于使用(与Java JShell相比,它确实很难使用,甚至具有良好的IDE)。
PyCharm提供了一个不错的REPL,下面是一个示例:
>>> runfile('/ mnt / edata / aw / kari / github / python / webstore-demo / simple-server / simpleserver / domaindb / domain.py',wdir ='/ mnt / edata / aw / kari / github / python / webstore-demo / simple-server')
>>> myD = Domain()
2018-10-30 18:40:11,769-__main__-__init_product_db-调试-ENTER
2018-10-30 18:40:11,770-__main__-__read_product_groups-调试-ENTER
...
2018-10-30 18:40:11,771-__main__-__read_raw_products-调试-退出
2018-10-30 18:40:11,771-__main__-__init_product_db-调试-退出
>>> myD.get_raw_products(1)
[['2001','1','Kalevala','3.95','EliasLönnrot','1835','芬兰','芬兰'],...]
因此,使用runfile方法,您可以将任何模块重新加载到PyCharm Python控制台,然后在那里单独尝试这些方法。
Spring麻烦之后,Python日志记录配置是多么轻松。 您只需要创建logging.conf文件即可。
Python赢得了Javascript和几乎所有语言的可读性。 它可能是最易读的语言。 我要说的是,它比Clojure更具可读性,后者也是一种非常易读的语言(一旦您习惯于阅读一种功能性语言)。 Java的可读性仅次于Python,因此其可读性下降。
让我们使用Javascript和Python实现作为这些语言的可读性示例(您可以在我之前的博客文章中查看Java和Clojure的等效示例,请参见本文开头的链接):
Javascript :
describe('GET / product-groups',function(){
让jwt;
it('Get Json web token',async()=> {
//异步示例,我们在其中等待Promise
//准备就绪(即获取jwt的帖子已处理)。
const jsonWebToken =等待getJsonWebToken();
logger.trace('Got jsonWebToken:',jsonWebToken);
assert.equal(jsonWebToken.length> 20,true);
jwt = jsonWebToken;
});
它(“成功获取:/ product-groups”,功能(完成){
logger.trace('使用jwt:',jwt);
const authStr = createAuthStr(jwt);
超级测试(WebServer)
.get('/ product-groups')
.set('Accept','application / json')
.set('Authorization',authStr)
.expect('Content-Type',/ json /)
.expect(200,{
ret:“好”,
'产品组':{1:'书',2:'电影'}
},完成);
});
});
Python :
def test_get_product_groups(客户端):
myLogger.debug(输入)
令牌= get_token(客户端)
encoded_token =(b64encode(token.encode()))。decode()
mimetype ='应用程序/ json'
标头= {
“内容类型”:mimetype,
'接受':模仿类型,
'授权':'基本'+ encode_token
}
url ='/ product-groups'
响应= client.get(URL,标头=标头)
断言response.status =='200 OK'
json_data = json.loads(response.data)
断言json_data.get('ret')=='确定'
在response.data中声明b“产品组”
product_groups = json_data.get('product-groups')
断言product_groups ['1'] =='Books'
断言product_groups ['2'] =='电影'
myLogger.debug(退出)
我会说Python更易读。
在Java之后编程Python真是一件乐事。 动态输入语言! 简洁! 语法清晰! 简单! 与Java相比,Python编程的生产力来自另一个世界。我在第一天晚上探索了PyCharm的新功能和Flask,第二天晚上,我实现了domaindb模块以及大多数userdb模块和相关的单元测试。 第三天晚上,我实现了其余的应用程序和单元测试,仅此而已。 Python代码,配置或IDE(PyCharm)绝对没有麻烦。
Python(尤其是PyCharm) REPL绝对是我在Lisp世界之外使用的最好的REPL。 Python REPL使浏览小代码段变得轻而易举。
使用PyCharm调试器也是如此简单快捷。 如果您的代码中甚至有一些小问题,您都倾向于添加一个断点并点击调试器。 这实际上非常有趣,因为在Lisp世界中,您几乎从未使用过调试器-在添加新功能时,您倾向于在系统中使用实时REPL。 同样,您无法在Python系统上使用实时REPL,但PyCharm调试器是不错的第二选择。 当您将Python调试器与Java调试器进行比较时-Python快速启动。 在PyCharm中为单元测试创建运行配置也非常容易和直接。 PyCharm调试器还是一个检查各种实体内部内容的好工具(例如,我刚才使用调试器来检查Flask响应实体内部的http状态代码及其名称是什么)–如果您懒于在库API文档。 请参阅下面的示例。


总的来说,我认为Python一定是我使用过的生产力最高的语言。 经过几年的严重Clojure骇客入侵后,Clojure可能会在生产率方面胜出,但是Python在脚本类别中是无与伦比的-您在Python骇客会话之间可能有数月的空白,但是无论多长时间,该语言始终易于投入实际工作那是您上次对Python进行编程。
如果将Python与Java进行比较-Python胜出。 Java非常冗长-Python简洁明了。 Java具有较长的开发周期(编辑,编译,构建,加载到JVM,运行),而Python具有较短的开发周期(编辑,运行)。 Java的语法很困难-Python的语法非常简单。
如果将Python与Javascript / Node进行比较,Python会获得纯净的语法以及创建和测试代码的整体简便性。
Python本质上没有什么坏处。 如果在大型项目中使用数十个开发人员在同一个代码库中工作,我会谨慎使用,除非您有一些严格的规则,如何在大型项目中使用动态类型的语言来保护协作免受典型错误的影响(例如,强制使用类型提示) )。
决赛! 代码行! 让我们再次比较简单服务器的不同实现之间的代码行(生产代码,即不包括单元测试):


如果删除空的软件包文件(’__init__.py’),则生产源代码树中只有8个源代码文件,而总共只有582行代码。 因此,Python似乎是比赛这一部分的赢家。 这可能是比少量代码甚至是少量代码非常简单,简洁和易于阅读的语言更好的语言。
如果您尝试创建一个应响应大量事件/会话的系统,则全局解释器锁定(GIL)可能会导致一些问题。 Node也是单线程的,但是Node具有特殊的体系结构,其中Node在事件循环中运行单线程,并将I / O工作委托给工作池线程。 这使得Node在处理不占用大量CPU资源的任务时非常高效(另一方面,占用CPU资源的任务可能会大大降低Node的性能)。 相反,Java系统通常为每个请求旋转专用线程。 这比较昂贵(占用更多的机器资源),但一个线程(对于一个客户端)不会阻止另一线程(客户端)的处理。 Python具有臭名昭著的Global Interpreter Lock,在Python的一生中,它在Python社区中引起了很多争论。 在大多数情况下,这不是问题,因为您通常将Python用于小型任务。 但是,如果您使用Python进行CPU密集型工作,同时处理大量任务或请求,则必须为其找到一些特殊的解决方案(如果您将它们搜索到,则确实存在,请参阅“使用Python有效利用多个内核”)。
我使用Python已有20年了,可以完成各种任务。 例如,我在家中的个人备份脚本是使用Python实现的。 我的虚拟狗是使用Python实现的(观看我的IP摄像机,并在后院检测到任何动静的情况下,开始发疯狗吠叫声的音频文件-因为现在我有一只真正的大疯狗在看房子,所以我现在就可以揭露这一点)。 在工作中,我使用Python进行各种日志分析工作,监视过程,分析项目之间Java类的复制粘贴,将各种aws cli命令粘合在一起等等。 对于各种用途可能需要的快速临时脚本,Python确实是一种好语言。 就像我现在所做的那样,Python还是一种非常好的语言,用于实现Web服务器(要进行性能测试)。
这项使用五种语言(还有一种语言)实现同一台Web服务器的练习确实令人大开眼界。 我必须说,我对Java的生产力感到非常失望。 我以为20年后,我可以比使用全新的语言(Javascript)更快地实现Web服务器,但是两者花费的时间相同(约3周)。 Python-3个晚上。 现在,那些大型企业整体系统将成为历史,而云无服务和微服务的新时代正在兴起,人们可以自由选择语言。 我想我将Java留在它所属的那个庞大的整体企业界中。 最好使用Python或Javascript来实现新的无服务器实现。 面向数据的微服务可能使用Clojure。
最后的挑战仍在:走吧。 因为我对Go一无所知,所以它将非常有趣。 因此,我将再一次从黑社会中获得旧的Simple Server幽灵并将其成型为实现-这次使用Go。