python引入 3.4 怎么引入gmty2函数

python通过get方式,post方式发送http请求和接收http响应-urllib urllib2 - Poer李 - 博客园
随笔 - 11, 文章 - 0, 评论 - 0, 引用 - 0
python通过get方式,post方式发送http请求和接收http响应--
import&urllib模块,urllib2模块, httplib模块&&
http://blog.163.com/xychenbaihu@yeah/blog/static//python通过get方式,post方式发送http请求和接收http响应--
import&urllib模块,urllib2模块, httplib模块&&
http://blog.163.com/xychenbaihu@yeah/blog/static//
阅读(...) 评论()python中,导入time模块使用的命令是
import time
可以使用以下命令查看time模块内置的能够使用的方法:
可以使用以下命令查看time模块中每个内置方法的说明:
help(time.time_method)
比如time模块下有一个time.time的方法,现在我想查看这个方法的官方文档,就可以使用这样的命令:
help(time.time)
时间的表示形式:
在python中,通常有三种方式来表示时间:时间戳,元组(结构化时间,struct_time),格式化的时间字符串。
时间戳(timestamp):通常来说,时间戳表示从日00:00:00开始按秒计算的偏移量,它的值是float类型
格式化的时间字符串(Format String):‘’
结构化时间(struct_time):struct_time元组共有9个元素:(年,月,日,时,分,秒,一年中的第几周,一年中的第几天等)
time模块中内置的常用的方法:
接受时间元组并返回一个可读的形式"Tue May 30 17:17:30 年5月30日周二17时17分30秒)的24个字符的字符串
Convert a time tuple to a string, e.g. 'Sat Jun 06 16:26:11 1998'.
When the time tuple is not present, current time as returned by localtime()
&&& time.asctime()
'Thu Jun 22 19:27:19 2017'
作用相当于asctime(localtime(secs)),未给参数相当于asctime()
ctime(seconds) -& string
Convert a time in seconds since the Epoch to a string in local time.
This is equivalent to asctime(localtime(seconds)). When the time tuple is
not present, current time as returned by localtime() is used.
&&& time.ctime()
'Thu Jun 22 19:34:35 2017'
接收时间辍(1970纪元年后经过的浮点秒数)并返回格林威治天文时间下的时间元组t(t.tm_isdst始终为0)
gmtime([seconds]) -& (tm_year, tm_mon, tm_mday, tm_hour, tm_min,
tm_sec, tm_wday, tm_yday, tm_isdst)
Convert seconds since the Epoch to a time tuple expressing UTC (a.k.a.
When 'seconds' is not passed in, convert the current time instead.
If the platform supports the tm_gmtoff and tm_zone, they are available as
attributes only.
&&& time.gmtime()
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=22, tm_hour=11, tm_min=35, tm_sec=12, tm_wday=3, tm_yday=173, tm_isdst=0)
接收时间辍(1970纪元年后经过的浮点秒数)并返回当地时间下的时间元组t(t.tm_isdst可取为0或1,取决于当地当时是不是夏令时)
localtime([seconds]) -& (tm_year,tm_mon,tm_mday,tm_hour,tm_min,
tm_sec,tm_wday,tm_yday,tm_isdst)
Convert seconds since the Epoch to a time tuple expressing local time.
When 'seconds' is not passed in, convert the current time instead.
&&& time.localtime()
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=22, tm_hour=19, tm_min=35, tm_sec=35, tm_wday=3, tm_yday=173, tm_isdst=0)
接受时间元组并返回时间辍(1970纪元年后经过的浮点秒数)
mktime(tuple) -& floating point number
Convert a time tuple in local time to seconds since the Epoch.
Note that mktime(gmtime(0)) will not generally return zero for most
instead the returned value will either be equal to that
of the timezone or altzone attributes on the time module.
&&& time.mktime(time.localtime())
推迟调用线程的运行,secs的单位是秒
Delay execution for a given number of seconds.
The argument may be
a floating point number for subsecond precision.
把一个代表时间的元组或者struct_time(如由time.localtime()和time.gmtime()返回)转化为格式化的时间字符串.如果t未指定,将传入time.localtime(),如果元组中任命一个元素越界,将会抛出ValueError异常
strftime(format[, tuple]) -& string
Convert a time tuple to a string according to a format specification.
See the library reference manual for formatting codes. When the time tuple
is not present, current time as returned by localtime() is used.
Commonly used format codes:
Year with century as a decimal number.===&完整的年份
Month as a decimal number [01,12].===&月份(01-12)
Day of the month as a decimal number [01,31].===&一个月中的第几天(01-31)
Hour (24-hour clock) as a decimal number [00,23].===&一天中的第几个小时(24小时制,00-23)
Minute as a decimal number [00,59].===&分钟数(00-59)
Second as a decimal number [00,61].===&秒(01-61)
Time zone offset from UTC.===&用+HHMM或者-HHMM表示距离格林威治的时区偏移(H代表十进制的小时数,M代表十进制的分钟数)
Locale's abbreviated weekday name.===&本地(local)简化星期名称
Locale's full weekday name.===&本地完整星期名称
Locale's abbreviated month name.===&本地简化月份名称
Locale's full month name.===&本地完整月份名称
Locale's appropriate date and time representation.===&本地相应的日期和时间表示
Hour (12-hour clock) as a decimal number [01,12].===&一天中的第几个小时(12小时制,01-12)
Locale's equivalent of either AM or PM.===&本地am或者pm的相应符
&&& time.strftime("%Y-%m-%d")
&&& time.strftime("%Y-%m-%d %H-%H-%S")
' 19-19-28'
把一个格式化时间字符串转化为struct_time,实际上它和strftie()是逆操作
strptime(string, format) -& struct_time
Parse a string to a time tuple according to a format specification.
See the library reference manual for formatting codes (same as
strftime()).
&&& time.strptime("","%Y-%m-%d")
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=21, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=2, tm_yday=172, tm_isdst=-1)
&&& time.strptime(" 12-34-45","%Y-%m-%d %H-%M-%S")
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=21, tm_hour=12, tm_min=34, tm_sec=45, tm_wday=2, tm_yday=172, tm_isdst=-1)
把一个时间转换成结构化时间
The time value as returned by gmtime(), localtime(), and strptime(), and
accepted by asctime(), mktime() and strftime().
May be considered as a
sequence of 9 integers.
&&& time.struct_time(time.localtime())
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=22, tm_hour=19, tm_min=42, tm_sec=7, tm_wday=3, tm_yday=173, tm_isdst=0)
返回当前时间的时间戳(1970元年后的浮点秒数
Return the current time in seconds since the Epoch.
Fractions of a second may be present if the system clock provides them.
&&& time.time()
&&& time.time()
几种时间形式的转换
1.把时间戳转换成结构化时间,使用的是time.localtime或time.gmtime命令。
&&& t1=time.time()
&&& print(t1)
&&& t2=time.localtime(t1)
&&& print(t2)
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=22, tm_hour=19, tm_min=55, tm_sec=26, tm_wday=3, tm_yday=173, tm_isdst=0)
2.把结构化时间转换成时间戳,使用time.mktime命令
&&& t3=time.struct_time(time.localtime())
&&& print(t3)
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=22, tm_hour=19, tm_min=58, tm_sec=29, tm_wday=3, tm_yday=173, tm_isdst=0)
&&& t4=time.mktime(t3)
&&& print(t4)
3.把结构化时间转换成时间字符串,使用time.strftime命令
&&& t1=time.localtime()
&&& print(t1)
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=22, tm_hour=20, tm_min=0, tm_sec=37, tm_wday=3, tm_yday=173, tm_isdst=0)
&&& t2=time.strftime("%Y-%m-%d",t1)
&&& print(t2)
4.把字符串时间转换成结构化时间,使用的是time.strptime命令
&&& t1=" 08:08:10"
&&& print(t1)
&&& t2=time.strptime(t1,"%Y-%m-%d %H:%M:%S")
&&& print(t2)
time.struct_time(tm_year=2017, tm_mon=5, tm_mday=20, tm_hour=8, tm_min=8, tm_sec=10, tm_wday=5, tm_yday=140, tm_isdst=-1)
假如我有一个时间字符串,然后想得到这个时间之后3天的时间字符串,可以使用如下的命令:
import time
time1 = ""
#把时间字符串转换为时间戳,然后加上3天时间的秒数
time2 = time.mktime(time.strptime(time1,"%Y-%m-%d"))+3 * 24 * 3600
#把转换后的时间戳再转换成时间字符串
time3 = time.strftime("%Y-%m-%d", time.localtime(time2))
print(time3)
阅读(...) 评论()&p&&b&收藏是点赞两倍哟!顺手点赞呗~&/b&&/p&&p&&br&&/p&&p&很多人在学习编程之初都会碰到这种问题:&b&学会了基础的语法了,但是还是做不了项目,不知道如何下手。&/b&&/p&&p&当初,我学习C的时候是这样、Java的时候是这样、Python的时候也是这样,其实不管什么语言、什么知识都是这样:&b&理论基础知识 - 能动手做项目是有一道鸿沟的&/b&。&/p&&p&&br&&/p&&p&那么如何突破这条鸿沟?中间的桥梁是什么?&/p&&p&其实题主自己已经回答出来了:&b&照抄!&/b&&/p&&p&&br&&/p&&p&所谓照抄前提是有样本。&/p&&p&首先找到一些简单易上手的项目,这些项目大多散落在Python实践相关的书籍中、Github上,这些实战项目知乎上都有很多推荐。&/p&&p&&br&&/p&&p&1.一些比较好的适合初学者动手的项目:&/p&&ul&&li&&a href=&//link.zhihu.com/?target=https%3A//github.com/Show-Me-the-Code/show-me-the-code& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Show-Me-the-Code/show-me-the-code&/a&&/li&&li&&a href=&//link.zhihu.com/?target=https%3A//github.com/aosabook/500lines/blob/master/README.md& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&aosabook/500lines&/a&&/li&&/ul&&p&另外知乎上这个问题下的一些推荐的项目还是非常适合新手练习的,可以作为参考:&a href=&https://www.zhihu.com/question/& class=&internal&&Python 的练手项目有哪些值得推荐?&/a&&/p&&p&2.大多数的Python书里面(除了纯理论书)都是有小项目的,而且书的一个优点是它会一步一步解释这样做的原因。&/p&&p&&br&&/p&&p&先照抄这些项目,实现这些小功能在电脑上能运行确认无误之后,回过头来看代码:&/p&&ul&&li&有没有你不理解的地方,不理解的地方标记去搜索引擎或者书中找解释。&/li&&li&学习作者设计这个项目的思路方法,并运用到接下来的项目,如果时间充裕,建议隔天再重新再不看书的情况下重新自己实现一遍这些小项目。&/li&&/ul&&p&&br&&/p&&p&如果你是跟着实战的书敲代码的,很多时候项目都不会一遍运行成功,那么你就要根据各种报错去寻找原因,这也是一个学习的过程。&/p&&p&&br&&/p&&p&总结起来从Python入门跳出来的过程分为三步:&b&照抄、照抄之后的理解、重新自己实现。&/b&&/p&&p&&br&&/p&&p&&b&很多人问我如何学习Python爬虫,为此我整理编写了一本Python爬虫相关的电子书,主要包括Python入门、Python爬虫入门到进阶、Python爬虫面试总结等等。可以在微信公众号【一个程序员的日常】后台回复关键词【1】获取这本电子书。&/b&&/p&
收藏是点赞两倍哟!顺手点赞呗~ 很多人在学习编程之初都会碰到这种问题:学会了基础的语法了,但是还是做不了项目,不知道如何下手。当初,我学习C的时候是这样、Java的时候是这样、Python的时候也是这样,其实不管什么语言、什么知识都是这样:理论基础知…
&p&我是真正零基础开始学Python的,从一开始的一窍不通,到3个月后成功搭建了一个动态网站(没有用任何框架)。相比于计算机大牛,我更加知道一个小白将会遇到什么坑,遇到哪些难点。我把我的学习过程写在下面,并附上在每个阶段的学习资料,希望对零基础的Python学习者有所帮助。&/p&&p&注:本文只做经历分享,不是技术探讨。&/p&&p&&i&---&/i&&/p&在知乎上常常看到想要转行IT,或者是想学习编程但不知如何开始的朋友。回答这类问题的人往往只是列出书单资源然后给出一个大致的方向。有些朋友一开始就扎入了理论学习的汪洋大海,从苦读类似《算法导论》开始,能够坚持读下来的寥寥无几,学习的积极性也被不断的挫败感消磨的所剩无几。&p&一直以来,编程对我而言是一种「黑魔法」般的存在。今年阿里月饼门,当很多人都参与到是非之争的时候,我更加着迷于程序员区区几行代码的脚本所展现出的威力,对于外行而言,这是超乎他们想象之外的某种能力——为什么我守在电脑面前盯着秒针然后拼命点击鼠标都不一定能抢到的礼物,程序员只需要提前花5分钟写3行代码就轻松搞定?&/p&&p&所以,我的心底深处一直都想变成这样的一位魔法师。今年的8月,刚好工作上想要开发一个基于微信的英语学习网站,借此机会,我决定好好学习一下编程。在此之前,我所有关于程序的知识仅仅来源于两部分,一是本科时期的C语言必修课,现在已经忘的一干二净,不过好歹我从中明白编程是怎么回事;第二部分是大概两年前由于工作关系学的一些Python,不过只是皮毛,现在忘的七七八八。&/p&&p&简单说,我的目标是建立一个网站,不过这个网站是动态的,也就是说它能识别谁登陆,然后对不同的人显示不同的内容。而当时的现实是,我完全不明白网页是如何显示出来的?譬如每个人登陆知乎的时候看到的是定制化的页面,后台是如何基于每个ID来组织出不同的页面的?所有我们看到的问题,回答,参与的评论在背后的服务器上是以一种什么样的形式存在的?又比如为什么有些页面只能在微信端打开,在电脑上就会出错?(不知道你有没有发现这一点)&/p&&p&当时我的心中充满了无数个类似的问号?当然如果你也是小白,一定有着同样的疑问。&/p&&p&好处是一开始就明确了学习的目的:Web建站。所以我的学习方案基本就是通过做项目学习,哪里不懂就解决哪里,边做边学习,不断推进。另外,由于知道很多成功的网站在用Python做服务器开发,比如知乎,所以我就自然选择了Python。&br&&/p&&p&于是我就开始了我充满着挫败感和成就感的编程之路……&/p&&p&从8月到12月的四个月里,除去本来的工作,为了学习质量,我会保证平均每天4个小时的学习时间,周末也不例外。另外,所有的文档,问答都尽量看英文的,这可以帮你剩下大量的时间。12月13日,我做的网站上线了,3天时间大概有5000人访问了这个网站,我有时在后台看着日志,不免有些心潮喷涌,我想把自己的经验写下来,希望对于那些有心学习编程但无从下手的朋友提供一些帮助和鼓励。&br&&/p&&h2&1. 我的自学编程之路&/h2&&p&1)基础&br&&/p&&p&刚开始的时候,我对搭建网站一无所知。为了给自己迅速建立一个框架,我在Google上面泡了整整一天,了解了HTML,CSS,JavaScript,Ajax,jQuery,React,SQL,服务器脚本等等知识,不求精通,只是了解每一项技术是干嘛的,另一方面建立起了一个学习的roadmap,这样大概知道做一个Web App需要哪些知识,分别学习的主次顺序。重点推荐两个资源:&/p&&p&Python基础知识:&a href=&//link.zhihu.com/?target=https%3A//learnpythonthehardway.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Learn Python the Hard Way&/a&&/p&&p&Web App基础知识:@张秋怡的&a href=&https://www.zhihu.com/question//answer/& class=&internal&&答案&/a&写的通俗易懂,极力推荐。&/p&&p&有了这个roadmap,我明白了前端三大必须掌握技能HTML,CSS和JavaScript,花了大概10天左右把W3Schools上的教程全部过了一遍,然后试着写了几个网页,感觉自己写的很没有底气。于是根据知乎和豆瓣上的推荐,买了《JaveScript DOM》和《Head First HTML与CSS》,边看书边把例子过了一遍。&/p&&p&W3Schools: &a href=&//link.zhihu.com/?target=http%3A//www.baidu.com/link%3Furl%3DIYCvCXzSkGQLt7ulZZFmBNaPMEd_6GSSHjvs-BKvWu-oYzYfW7aAUyj8JGqiQpVB& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&www.w3schools.com/&/a&&/p&&p&JavaScript:&a href=&//link.zhihu.com/?target=https%3A//book.douban.com/subject/1921890/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript DOM编程艺术&/a&&/p&&p&HTML & CSS : &a href=&//link.zhihu.com/?target=https%3A//book.douban.com/subject//& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Head First HTML与CSS(第2版)&/a&&/p&&p&前端是需要慢慢学习的,在看完上面的资料后,虽然能写出来一些挺漂亮的页面,但是我自己知道很多都是不符合标准的,更不要说代码风格什么的。这只能通过不断地积累和增加代码量来提高。由于明白服务器端需要耗费自己大量的时间,所以在发现自己能够按照构思勉强实现网页之后,我就把学习中心放到了服务器端上。不过每天还是会抽空写一写网页,避免手生。&/p&&p&2)服务器端&/p&&p&最先了解的是HTTP协议,也就是浏览器和服务器之间是如何通信的。也就是当你在浏览器里键入网址按下回车直到网页显示在你浏览器的这个过程中,浏览器和浏览器之间发生了什么事情。这是很有意思的内容,我是以读小说的心情了解了这部分内容。了解这部分后,你就会明白类似为什么有时候会有404页面?在百度搜索框里键入的搜索词是如何提交到百度服务器的?为什么重新登录知乎的时候就不用再输入密码了?之类的问题了。&/p&&p&HTTP协议学习资料:&a href=&//link.zhihu.com/?target=https%3A//www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&In Introduction to HTTP Basics&/a&&/p&&p&&a href=&//www.zhihu.com/people/a179aa706eacbd2f7088& data-hash=&a179aa706eacbd2f7088& class=&member_mention& data-title=&@Vamei& data-editable=&true& data-hovercard=&p$b$a179aa706eacbd2f7088&&@Vamei&/a&的博客:&a href=&//link.zhihu.com/?target=http%3A//www.cnblogs.com/vamei/archive//2802811.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&协议森林&/a&&/p&&p&了解了HTTP协议之后,我就多少有些入迷了。看似神秘难懂的现象其实原理并不复杂,你反而会被吸引。接下来就进入到我投入时间最多的部分了——后端开发。记得当时了解Web开发的MVC(Model-View-Controller)模式后,有一种心血喷涌的感觉,觉得太有意思了(程序员别喷,我就是这么没见过世面)。我们以知乎为例子来说明MVC是个啥:&/p&&figure&&img src=&https://pic1.zhimg.com/50/v2-0f9a88b7e281cc7ffd5e_b.jpg& data-rawwidth=&1029& data-rawheight=&623& class=&origin_image zh-lightbox-thumb& width=&1029& data-original=&https://pic1.zhimg.com/50/v2-0f9a88b7e281cc7ffd5e_r.jpg&&&/figure&&br&&p&每个人的主页都是相同的布局和风格,例如最上面的菜单搜索栏,颜色分割,左边显示动态等,右边是个人信息等,然后具体的内容却因人而异——每个人的头像,名字,动态都是不一样的。那么知乎是如何保证每一个人看到的都是自己的主页呢?&/p&&p&你可以把这个相同的布局想象成一个模板,里面有一个个空格子,当你用你的账户登陆页面时,想象你的电脑里有无数个小人根据你的账号从知乎后台的数据库里取出你的头像,动态,认证信息等等内容,然后对应着模板上规定好的位置,把对应的内容填进去。这些小人的动作实在是太快了,以至于你觉得这是在瞬间完成的。&/p&&p&上面所说的模板就是MVC中的V,是View的缩写,负责显示。这样做的好处在于,如果知乎有一天突然想改变一下个人主页的风格,那么只需要改变这一个模板,然后几千万注册用户的主页就相应的变化了,因为模板是公用的,是不是省了很多事情?(早期的Web开发可不是这样哟,你可能要一个个用户去改,非常麻烦。)&/p&&p&而这些小人除了摆放内容,它们真正负责的是业务逻辑,我们把他们叫做Controller,也就是MVC中的C。例如当你登陆的时候,这些小人要检查你的用户名是不是准确的,如果准确,它们要去数据库里取出你请求的信息等,如果用户名错误,它们要拦截住你的登陆。它们的职责还有很多,无法一一列举。在实际中,这些小人做的事情其实就是Python(或者其它脚本语言)做的事情。&/p&&p&最后,MVC中的Model其实就是传给View的数据,包括上面的头像,用户名,动态等因人而异的数据。这些数据在知乎服务器上是以数据库表格(table)的形式存在的,你可以把它们想象成很多不同的excel表格,不同的表格储存着不同的信息,有些记录着知乎用户的个人信息,有些记录着回答,有些记录着评论等等,而这些表格之间又彼此联系,当你在知乎的不同网页间跳转的时候,上面说的那些小人就根据你的要求,组合对应的表格取出对应的数据,然后把他们放到模板对应的空格里,发送给浏览器。然后浏览器根据你写的CSS,用不同的颜色,大小等等,将数据很漂亮的显示出来。&/p&&p&这样做的好处是什么呢?虽然你最终在浏览器里看到的是一个完整的页面,但是在后端逻辑上它们都是区分开的——模型(M),视图(V)和控制器(C)的区分就保证了较高的可维护性——我可以随时修改主页的显示并看到效果,同样我可以随时加入一些业务逻辑。&/p&&p&如果你的学习坚持到这里了,首先要恭喜你。其次你可能已经知道一些非常成熟的Python Web框架了,例如Django,Flask等等,并且你可能看到了很多小白教程教你直接使用,毕竟大部分人可能觉得没有必要重复造轮子。&/p&&p&本来为了省事,我也打算直接用框架。我是在设计数据库的时候,当时在看SQLAlchemy文档,觉得相对自己的项目SQLAlchemy太过复杂,所以我决定自己写自己的ORM(名词不懂没关系),这对于当时的我来说是一件难度非常大的事情。于是我投入了极大的精力每天都在看关于SQL和Python相关的教程和资料,&a href=&//link.zhihu.com/?target=https%3A//book.douban.com/subject/3112503/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Python核心编程(第二版) &/a&给了我很大的启发。在自己完成了ORM后,又写了URL处理函数,同样没有用任何现成的Web框架。&/p&&p&现在回头看,我认为这一段时间的造轮子是提升编程能力最快的时候。比如为了写ORM,就必须去花很多时间学习SQL,去了解Python里面的metaclass,而如果用一个现成的框架,我很有可能偷懒不去关注某些细节。而不出问题还好,一旦出问题,我就只能跪。另外,造轮子迫使我在开始的时候就构思整个框架,因为我必须尽可能的考虑到所有的情况,于是就会不断的强迫自己完善知识体系,和别人的代码作对比从而改进自己的,这个过程充满了无尽的挫败感,但是得来的成就和快乐也是无可比拟的。&/p&&p&SQL书籍:&a href=&//link.zhihu.com/?target=https%3A//www.amazon.com/Sams-Teach-Yourself-MySQL-Days/dp/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Sams Teach Yourself MySQL in 21 Days&/a&&/p&&p&Python:&a href=&//link.zhihu.com/?target=https%3A//book.douban.com/subject/3112503/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Python核心编程(第二版)&/a&&/p&&p&Github上的优质Python资源:&a href=&//link.zhihu.com/?target=https%3A//github.com/CodementorIO/Python-Learning-Resources& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&CodementorIO/Python-Learning-Resources&/a&&/p&&p&过程中还牵涉到部署,我的网站是跑在Linux上的。关于部署网上有非常多的优质教程,一搜一大把。这里就不再赘述。&/p&&p&这些是我学习大致路线,当然过程中充满着小的磕磕绊绊,虽然网站上线了,貌似运行还比较顺利,但是如果以一个程序员的标准来要求自己,自己依然非常菜鸟。不过我并没有以前那样惧怕技术了,就像你明白魔术的背后的原理后,会更多的思考原理本身。&/p&&h2&2. 自学编程需要注意的问题&/h2&&p&很多人都推荐小白第一门语言选Python,因为语法简单。这句话只说了一半,Python确实容易上手,对初学者的门槛很低。但我发现,&b&&u&对于小白真正的门槛在于系统知识,这就和用什么语言完全没有任何关系了。&/u&&/b&例如很多人学完了Python的语法,觉得确实简单,但是转头去用Python标准库的时候,却发现自己连文档都看不懂。标准库提供了Python和其它系统功能的接口,最终实现了Python和系统之间的互动。读标准库需要系统知识,比如操作系统,数据库,进程和线程,socket编程,网络协议等等,这些对于编程小白来才构成很高的门槛,但是只有学会这些,才能真正发挥出Python的威力来。&/p&&p&这也是我觉得自己的经历对小白是有价值的一个原因。因为设计一个动态的网页是一个很不错的练手Project。建立网页(Web App)会逼迫你了解从你在浏览器里键入地址按下回车到网页显示在浏览器的过程中,浏览器,网络,服务器都干了些什么。具体到技术上面,你不得不去学习前端的HTML,CSS和JavaScript,后端的脚本,数据库,操作系统等。也就是说,这个过程能够促使你去主动学习上面提到的系统知识,如果你再做另外一个项目,你就不会像现在这样无从下手,而有能力去进行一些技术性的探讨,所以我认为这是一个非常好的练手项目。&br&&/p&&p&1)一定要空出时间补充理论知识&/p&很多人会强调learn by doing,边做项目边学习,这也是我自己采用的方式。在这种方式中,你不断犯错,改正……学习效率非常高。但是,很多人走了极端,最后的结果就是不注意理论知识的学习。你会发现自己Google的能力越来越高,但是真实的编程能力并没有得到提升。如果有这种情况,你需要反思一下。&p&一种可能是你太过于依赖各种成熟的框架,结果编程就变成了用「胶水」去粘合不同的框架完成需求。就好比你的Web App用了SQLAlchemy,虽然自己不太懂SQL,但是网站跑的也还不错。这时候如果数据库出现了问题,那你就只能跪了。&/p&&p&另外一种可能是你完全沉浸在做项目中,忽略了学习理论知识。做项目虽然充满困难,但回报是强烈的成就感,很容易沉浸其中。我觉得这是极其错误的。首先半路出家的程序员都没有经过系统的学习,没有形成自己的知识体系,如果你不懂数据结构,算法复杂度,操作系统这些理论,那么你能达到的高度就极其有限。所以,在每天做项目的同时,一定要保证抽出一定的时间,恶补理论知识。这部分的书单在豆瓣和知乎上都有很多总结,可以自行搜索。&/p&&p&2)不要太纠结于无意义的问题,比如什么框架好,XX语言比XX语言好啦这种问题。前期确定了练手项目,就去专心积累代码量,积累基础知识。那些你现在还看不懂的炫酷技术你慢慢也就能明白是怎么回事了,反而没有基础,再炫酷的框架对你而言都是天书。&/p&&p&3)学会发问。好问题是建立在你自己已经实践或者思考的基础上问出来的,这是对自己的负责,也是对别人的尊重。不要一遇到困难就喜欢直接上网搜索:「这个问题是怎么回事啊?」,「我不明白你能不能帮我看看……」。&/p&&p&4)学好英语。&/p&&p&THE END&/p&&br&&p&最后,给自己打个广告,如果学英语,一定要关注我。&/p&&p&&a href=&//link.zhihu.com/?target=http%3A//weixin.qq.com/r/ETt0bGzEkzHsrSgP924D& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&weixin.qq.com/r/ETt0bGz&/span&&span class=&invisible&&EkzHsrSgP924D&/span&&span class=&ellipsis&&&/span&&/a& (二维码自动识别)&/p&
我是真正零基础开始学Python的,从一开始的一窍不通,到3个月后成功搭建了一个动态网站(没有用任何框架)。相比于计算机大牛,我更加知道一个小白将会遇到什么坑,遇到哪些难点。我把我的学习过程写在下面,并附上在每个阶段的学习资料,希望对零基础的Pyt…
&figure&&img src=&https://pic3.zhimg.com/v2-6afa2a639ea529ffba293_b.jpg& data-rawwidth=&589& data-rawheight=&371& class=&origin_image zh-lightbox-thumb& width=&589& data-original=&https://pic3.zhimg.com/v2-6afa2a639ea529ffba293_r.jpg&&&/figure&&p&本文将以个人(开发)的角度,讲述如何从零开始,编写、搭建和部署一个基于Python的Web应用程序。&/p&&p&从最简单的出发点来剖析,一个web应用后端要完成的工作抽象出来无非就是3点:&/p&&ol&&li&接收和解析请求。&/li&&li&处理业务逻辑。&/li&&li&生产和返回响应。&/li&&/ol&&p&对于初学者来说,我们关心的只需这些步骤就够了。要检验这三个步骤,最简单的方法是先写出一个hello world。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&request-&&hello world&-&response
&/code&&/pre&&/div&&p&python有许多流行的web框架,我们该如何选择呢?试着考虑三个因素:&/p&&ul&&li&易用:该框架是面对初学者友好的,而且具有健全的文档,灵活开发部署。例如flask,bottle。&/li&&li&效率:该框架适合快速开发,拥有丰富的轮子,注重开发效率。例如django。&/li&&li&性能:该框架能承载更大的请求压力,提高吞吐量。例如falcon,tornado,aiohttp,sanic。&/li&&/ul&&p&根据场景使用合适的框架能少走许多弯路,当然,你还能自己写一个框架,这个下面再说。&/p&&p&对于缺乏经验的人来说,易用性无疑是排在第一位的,推荐用flask作为python web入门的第一个框架,另外也推荐django。&/p&&p&首先用virtualenv创建python的应用环境,为什么用virtualenv呢,virtualenv能创建一个纯净独立的python环境,避免污染全局环境。(顺便安利kennethreitz大神的&a href=&http://link.zhihu.com/?target=https%3A//github.com/kennethreitz/pipenv& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&pipenv&/a&)&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&mkdir todo
virtualenv venv
source venv/bin/activate
pip install flask
touch server.py
&/code&&/pre&&/div&&p&代码未写,规范先行。在写代码之前要定义好一套良好代码规范,例如PEP8。这样才能使得你的代码变的更加可控。&/p&&p&心中默念The Zen of Python:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
&/code&&/pre&&/div&&p&下面用flask来编写第一个程序:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&from&/span& &span class=&nn&&flask&/span& &span class=&kn&&import&/span& &span class=&n&&Flask&/span&&span class=&p&&,&/span& &span class=&n&&jsonify&/span&
&span class=&n&&app&/span& &span class=&o&&=&/span& &span class=&n&&Flask&/span&&span class=&p&&(&/span&&span class=&n&&__name__&/span&&span class=&p&&)&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/index'&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&index&/span&&span class=&p&&():&/span&
&span class=&k&&return&/span& &span class=&n&&jsonify&/span&&span class=&p&&(&/span&&span class=&n&&msg&/span&&span class=&o&&=&/span&&span class=&s1&&'hello world'&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&__name__&/span& &span class=&o&&==&/span& &span class=&s1&&'__main__'&/span&&span class=&p&&:&/span&
&span class=&n&&app&/span&&span class=&o&&.&/span&&span class=&n&&run&/span&&span class=&p&&(&/span&&span class=&n&&host&/span&&span class=&o&&=&/span&&span class=&s1&&'0.0.0.0'&/span&&span class=&p&&,&/span& &span class=&n&&port&/span&&span class=&o&&=&/span&&span class=&mi&&8000&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&在命令行输入python server.py&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&python server.py
* Running on http://0.0.0.0:8000/ (Press CTRL+C to quit)
&/code&&/pre&&/div&&p&打开浏览器,访问&a href=&http://link.zhihu.com/?target=http%3A//127.0.0.1%3A8000/index%25EF%25BC%258C%25E5%25A6%%%25E6%E5%25A4%2596%25EF%25BC%258C%25E4%25BC%259A%25E7%259C%258B%25E5%%25E4%25B8%258B%25E9%259D%25A2%25E7%259A%%E5%25BA%%& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&http://127.0.0.1:8000/index&/a&,如无意外,会看到下面的响应。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&{
&msg&: &hello world&
&/code&&/pre&&/div&&p&一个最简单的web程序就完成了!让我们看下过程中都发生了什么:&/p&&ol&&li&&p&客户端(浏览器)根据输入的地址&a href=&http://link.zhihu.com/?target=http%3A//127.0.0.1%3A8000/index%25E6%2589%25BE%25E5%%25E5%258D%258F%25E8%25AE%25AE%25EF%25BC%2588http%29%25EF%25BC%258C%25E4%25B8%25BB%25E6%259C%25BA%25EF%25BC%.0.1%25EF%25BC%2589%25EF%25BC%258C%25E7%25AB%25AF%25E5%258F%25A3%25EF%25BC%EF%25BC%%E8%25B7%25AF%25E5%25BE%2584%25EF%25BC%2588/index%25EF%25BC%2589%25EF%25BC%258C%25E4%25B8%258E%25E6%259C%258D%25E5%258A%25A1%25E5%%25EF%25BC%2588application& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&http://127.0.0.1:8000/index&/a&找到协议(http),主机(127.0.0.1),端口(8000)和路径(/index),与服务器(application server)建立三次握手,并发送一个http请求。&/p&&/li&&li&&p&服务器(application server)把请求报文封装成请求对象,根据路由(router)找到/index这个路径所对应的视图函数,调用这个视图函数。&/p&&/li&&li&&p&视图函数生成一个http响应,返回一个json数据给客户端。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 27
Server: Werkzeug/0.11.15 Python/3.5.2
Date: Thu, 26 Jan :36 GMT
&/code&&/pre&&/div&&/li&&/ol&&p&当我们输入python server.py时,会建立一个服务器(也叫应用程序服务器,即application server)来监听请求,并把请求转给flask来处理。那么这个服务器是如何跟python程序打交道的呢?答案就是&a href=&http://link.zhihu.com/?target=http%3A//archimedeanco.com/wsgi-tutorial/%23& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&WSGI&/a&(Web Server Gateway Interface)接口,它是server端(服务器)与application端(应用程序)之间的一套约定俗成的规范,使我们只要编写一个统一的接口,就能应用到不同的wsgi server上。用图表示它们的关系,就是下面这样的:&/p&&br&&figure&&img src=&https://pic3.zhimg.com/v2-4f9eb56fab93afba5f3ca_b.png& data-rawwidth=&589& data-rawheight=&371& class=&origin_image zh-lightbox-thumb& width=&589& data-original=&https://pic3.zhimg.com/v2-4f9eb56fab93afba5f3ca_r.jpg&&&/figure&&br&&p&只要application端(flask)和server端(flask内建的server)都遵循wsgi这个规范,那么他们就能够协同工作了,关于WSGI规范,可参阅Python官方的&a href=&http://link.zhihu.com/?target=https%3A//www.python.org/dev/peps/pep-0333/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&PEP 333&/a&里的说明。&/p&&p&目前为止,应用是下面这个样子的:&/p&&br&&figure&&img src=&https://pic2.zhimg.com/v2-73e7f74fcdc3fe0cb30a911_b.png& data-rawwidth=&948& data-rawheight=&167& class=&origin_image zh-lightbox-thumb& width=&948& data-original=&https://pic2.zhimg.com/v2-73e7f74fcdc3fe0cb30a911_r.jpg&&&/figure&&br&&p&一切都很简单,现在我们要做一个Todo应用,提供添加todo,修改todo状态和删除todo的接口。&/p&&p&先不考虑数据库,可以迅速地写出下面的代码:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&from&/span& &span class=&nn&&flask&/span& &span class=&kn&&import&/span& &span class=&n&&Flask&/span&&span class=&p&&,&/span& &span class=&n&&jsonify&/span&&span class=&p&&,&/span& &span class=&n&&request&/span&&span class=&p&&,&/span& &span class=&n&&abort&/span&&span class=&p&&,&/span& &span class=&n&&Response&/span&
&span class=&kn&&from&/span& &span class=&nn&&time&/span& &span class=&kn&&import&/span& &span class=&n&&time&/span&
&span class=&kn&&from&/span& &span class=&nn&&uuid&/span& &span class=&kn&&import&/span& &span class=&n&&uuid4&/span&
&span class=&kn&&import&/span& &span class=&nn&&json&/span&
&span class=&n&&app&/span& &span class=&o&&=&/span& &span class=&n&&Flask&/span&&span class=&p&&(&/span&&span class=&n&&__name__&/span&&span class=&p&&)&/span&
&span class=&k&&class&/span& &span class=&nc&&Todo&/span&&span class=&p&&(&/span&&span class=&nb&&object&/span&&span class=&p&&):&/span&
&span class=&k&&def&/span& &span class=&nf&&__init__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&content&/span&&span class=&p&&):&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&id&/span& &span class=&o&&=&/span& &span class=&nb&&str&/span&&span class=&p&&(&/span&&span class=&n&&uuid4&/span&&span class=&p&&())&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&content&/span& &span class=&o&&=&/span& &span class=&n&&content&/span& &span class=&c1&&#todo内容&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&created_at&/span& &span class=&o&&=&/span& &span class=&n&&time&/span&&span class=&p&&()&/span& &span class=&c1&&#创建时间&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&is_finished&/span& &span class=&o&&=&/span& &span class=&bp&&False&/span& &span class=&c1&&#是否完成&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&finished_at&/span& &span class=&o&&=&/span& &span class=&bp&&None&/span& &span class=&c1&&#完成时间&/span&
&span class=&k&&def&/span& &span class=&nf&&finish&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&):&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&is_finished&/span& &span class=&o&&=&/span& &span class=&bp&&True&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&finished_at&/span& &span class=&o&&=&/span& &span class=&n&&time&/span&&span class=&p&&()&/span&
&span class=&k&&def&/span& &span class=&nf&&json&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&):&/span&
&span class=&k&&return&/span& &span class=&p&&{&/span&
&span class=&s1&&'id'&/span&&span class=&p&&:&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&id&/span&&span class=&p&&,&/span&
&span class=&s1&&'content'&/span&&span class=&p&&:&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&content&/span&&span class=&p&&,&/span&
&span class=&s1&&'created_at'&/span&&span class=&p&&:&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&created_at&/span&&span class=&p&&,&/span&
&span class=&s1&&'is_finished'&/span&&span class=&p&&:&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&is_finished&/span&&span class=&p&&,&/span&
&span class=&s1&&'finished_at'&/span&&span class=&p&&:&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&finished_at&/span&
&span class=&p&&}&/span&
&span class=&n&&todos&/span& &span class=&o&&=&/span& &span class=&p&&{}&/span&
&span class=&n&&get_todo&/span& &span class=&o&&=&/span& &span class=&k&&lambda&/span& &span class=&n&&tid&/span&&span class=&p&&:&/span& &span class=&n&&todos&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&(&/span&&span class=&n&&tid&/span&&span class=&p&&,&/span& &span class=&bp&&False&/span&&span class=&p&&)&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/todo'&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&index&/span&&span class=&p&&():&/span&
&span class=&k&&return&/span& &span class=&n&&jsonify&/span&&span class=&p&&(&/span&&span class=&n&&data&/span&&span class=&o&&=&/span&&span class=&p&&[&/span&&span class=&n&&todo&/span&&span class=&o&&.&/span&&span class=&n&&json&/span&&span class=&p&&()&/span& &span class=&k&&for&/span& &span class=&n&&todo&/span& &span class=&ow&&in&/span& &span class=&n&&todos&/span&&span class=&o&&.&/span&&span class=&n&&values&/span&&span class=&p&&()])&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/todo'&/span&&span class=&p&&,&/span& &span class=&n&&methods&/span&&span class=&o&&=&/span&&span class=&p&&[&/span&&span class=&s1&&'POST'&/span&&span class=&p&&])&/span&
&span class=&k&&def&/span& &span class=&nf&&add&/span&&span class=&p&&():&/span&
&span class=&n&&content&/span& &span class=&o&&=&/span& &span class=&n&&request&/span&&span class=&o&&.&/span&&span class=&n&&form&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&(&/span&&span class=&s1&&'content'&/span&&span class=&p&&,&/span& &span class=&bp&&None&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&ow&&not&/span& &span class=&n&&content&/span&&span class=&p&&:&/span&
&span class=&n&&abort&/span&&span class=&p&&(&/span&&span class=&mi&&400&/span&&span class=&p&&)&/span&
&span class=&n&&todo&/span& &span class=&o&&=&/span& &span class=&n&&Todo&/span&&span class=&p&&(&/span&&span class=&n&&content&/span&&span class=&p&&)&/span&
&span class=&n&&todos&/span&&span class=&p&&[&/span&&span class=&n&&todo&/span&&span class=&o&&.&/span&&span class=&n&&id&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&n&&todo&/span&
&span class=&k&&return&/span& &span class=&n&&Response&/span&&span class=&p&&()&/span& &span class=&c1&&#200&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/todo/&tid&/finish'&/span&&span class=&p&&,&/span& &span class=&n&&methods&/span&&span class=&o&&=&/span&&span class=&p&&[&/span&&span class=&s1&&'PUT'&/span&&span class=&p&&])&/span&
&span class=&k&&def&/span& &span class=&nf&&finish&/span&&span class=&p&&(&/span&&span class=&n&&tid&/span&&span class=&p&&):&/span&
&span class=&n&&todo&/span& &span class=&o&&=&/span& &span class=&n&&get_todo&/span&&span class=&p&&(&/span&&span class=&n&&tid&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&todo&/span&&span class=&p&&:&/span&
&span class=&n&&todo&/span&&span class=&o&&.&/span&&span class=&n&&finish&/span&&span class=&p&&()&/span&
&span class=&n&&todos&/span&&span class=&p&&[&/span&&span class=&n&&todo&/span&&span class=&o&&.&/span&&span class=&n&&id&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&n&&todo&/span&
&span class=&k&&return&/span& &span class=&n&&Response&/span&&span class=&p&&()&/span&
&span class=&n&&abort&/span&&span class=&p&&(&/span&&span class=&mi&&404&/span&&span class=&p&&)&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/todo/&tid&'&/span&&span class=&p&&,&/span& &span class=&n&&methods&/span&&span class=&o&&=&/span&&span class=&p&&[&/span&&span class=&s1&&'DELETE'&/span&&span class=&p&&])&/span&
&span class=&k&&def&/span& &span class=&nf&&delete&/span&&span class=&p&&(&/span&&span class=&n&&tid&/span&&span class=&p&&):&/span&
&span class=&n&&todo&/span& &span class=&o&&=&/span& &span class=&n&&get_todo&/span&&span class=&p&&(&/span&&span class=&n&&tid&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&todo&/span&&span class=&p&&:&/span&
&span class=&n&&todos&/span&&span class=&o&&.&/span&&span class=&n&&pop&/span&&span class=&p&&(&/span&&span class=&n&&tid&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&n&&Response&/span&&span class=&p&&()&/span&
&span class=&n&&abort&/span&&span class=&p&&(&/span&&span class=&mi&&404&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&__name__&/span& &span class=&o&&==&/span& &span class=&s1&&'__main__'&/span&&span class=&p&&:&/span&
&span class=&n&&app&/span&&span class=&o&&.&/span&&span class=&n&&run&/span&&span class=&p&&(&/span&&span class=&n&&host&/span&&span class=&o&&=&/span&&span class=&s1&&'0.0.0.0'&/span&&span class=&p&&,&/span& &span class=&n&&port&/span&&span class=&o&&=&/span&&span class=&mi&&8000&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&这个程序基本实现了需要的接口,现在测试一下功能。&/p&&ul&&li&添加一个todo&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&http -f POST http://127.0.0.1:8000/todo content=好好学习
HTTP/1.0 200 OK
Content-Length: 0
Content-Type: text/ charset=utf-8
Date: Thu, 26 Jan :37 GMT
Server: Werkzeug/0.11.15 Python/3.5.2
&/code&&/pre&&/div&&ul&&li&查看todo列表&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&http http://127.0.0.1:8000/todo
HTTP/1.0 200 OK
Content-Length: 203
Content-Type: application/json
Date: Thu, 26 Jan :16 GMT
Server: Werkzeug/0.11.15 Python/3.5.2
&{\&created_at\&: .305699, \&id\&: \&6f2b28c4-1e83-45b2-8b86-20e28e21cd40\&, \&is_finished\&: false, \&finished_at\&: null, \&content\&: \&\\u597d\\u597d\\u5b66\\u4e60\&}&
&/code&&/pre&&/div&&ul&&li&修改todo状态&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&http -f PUT http://127.0.0.1:8000/todo/6f2b28c4-1e83-45b2-8b86-20e28e21cd40/finish
HTTP/1.0 200 OK
Content-Length: 0
Content-Type: text/ charset=utf-8
Date: Thu, 26 Jan :18 GMT
Server: Werkzeug/0.11.15 Python/3.5.2
http http://127.0.0.1:8000/todo
HTTP/1.0 200 OK
Content-Length: 215
Content-Type: application/json
Date: Thu, 26 Jan :22 GMT
Server: Werkzeug/0.11.15 Python/3.5.2
&{\&created_at\&: .305699, \&id\&: \&6f2b28c4-1e83-45b2-8b86-20e28e21cd40\&, \&is_finished\&: true, \&finished_at\&: .650981, \&content\&: \&\\u597d\\u597d\\u5b66\\u4e60\&}&
&/code&&/pre&&/div&&ul&&li&删除todo&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&http -f DELETE http://127.0.0.1:8000/todo/6f2b28c4-1e83-45b2-8b86-20e28e21cd40
HTTP/1.0 200 OK
Content-Length: 0
Content-Type: text/ charset=utf-8
Date: Thu, 26 Jan :20 GMT
Server: Werkzeug/0.11.15 Python/3.5.2
http http://127.0.0.1:8000/todo
HTTP/1.0 200 OK
Content-Length: 17
Content-Type: application/json
Date: Thu, 26 Jan :22 GMT
Server: Werkzeug/0.11.15 Python/3.5.2
&data&: []
&/code&&/pre&&/div&&p&但是这个的程序的数据都保存在内存里,只要服务一停止所有的数据就没办法保存下来了,因此,我们还需要一个数据库用于持久化数据。&/p&&p&那么,应该选择什么数据库呢?&/p&&ul&&li&传统的rdbms,例如mysql,postgresql等,他们具有很高的稳定性和不俗的性能,结构化查询,支持事务,由ACID来保持数据的完整性。&/li&&li&nosql,例如mongodb,cassandra等,他们具有非结构化特性,易于横向扩展,实现数据的自动分片,拥有灵活的存储结构和强悍的读写性能。&/li&&/ul&&p&这里使用mongodb作例子,使用mongodb改造后的代码是这样的:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&from&/span& &span class=&nn&&flask&/span& &span class=&kn&&import&/span& &span class=&n&&Flask&/span&&span class=&p&&,&/span& &span class=&n&&jsonify&/span&&span class=&p&&,&/span& &span class=&n&&request&/span&&span class=&p&&,&/span& &span class=&n&&abort&/span&&span class=&p&&,&/span& &span class=&n&&Response&/span&
&span class=&kn&&from&/span& &span class=&nn&&time&/span& &span class=&kn&&import&/span& &span class=&n&&time&/span&
&span class=&kn&&from&/span& &span class=&nn&&bson.objectid&/span& &span class=&kn&&import&/span& &span class=&n&&ObjectId&/span&
&span class=&kn&&from&/span& &span class=&nn&&bson.json_util&/span& &span class=&kn&&import&/span& &span class=&n&&dumps&/span&
&span class=&kn&&import&/span& &span class=&nn&&pymongo&/span&
&span class=&n&&app&/span& &span class=&o&&=&/span& &span class=&n&&Flask&/span&&span class=&p&&(&/span&&span class=&n&&__name__&/span&&span class=&p&&)&/span&
&span class=&n&&mongo&/span& &span class=&o&&=&/span& &span class=&n&&pymongo&/span&&span class=&o&&.&/span&&span class=&n&&MongoClient&/span&&span class=&p&&(&/span&&span class=&s1&&'127.0.0.1'&/span&&span class=&p&&,&/span& &span class=&mi&&27017&/span&&span class=&p&&)&/span&
&span class=&n&&db&/span& &span class=&o&&=&/span& &span class=&n&&mongo&/span&&span class=&o&&.&/span&&span class=&n&&todo&/span&
&span class=&k&&class&/span& &span class=&nc&&Todo&/span&&span class=&p&&(&/span&&span class=&nb&&object&/span&&span class=&p&&):&/span&
&span class=&nd&&@classmethod&/span&
&span class=&k&&def&/span& &span class=&nf&&create_doc&/span&&span class=&p&&(&/span&&span class=&n&&cls&/span&&span class=&p&&,&/span& &span class=&n&&content&/span&&span class=&p&&):&/span&
&span class=&k&&return&/span& &span class=&p&&{&/span&
&span class=&s1&&'content'&/span&&span class=&p&&:&/span& &span class=&n&&content&/span&&span class=&p&&,&/span&
&span class=&s1&&'created_at'&/span&&span class=&p&&:&/span& &span class=&n&&time&/span&&span class=&p&&(),&/span&
&span class=&s1&&'is_finished'&/span&&span class=&p&&:&/span& &span class=&bp&&False&/span&&span class=&p&&,&/span&
&span class=&s1&&'finished_at'&/span&&span class=&p&&:&/span& &span class=&bp&&None&/span&
&span class=&p&&}&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/todo'&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&index&/span&&span class=&p&&():&/span&
&span class=&n&&todos&/span& &span class=&o&&=&/span& &span class=&n&&db&/span&&span class=&o&&.&/span&&span class=&n&&todos&/span&&span class=&o&&.&/span&&span class=&n&&find&/span&&span class=&p&&({})&/span&
&span class=&k&&return&/span& &span class=&n&&dumps&/span&&span class=&p&&(&/span&&span class=&n&&todos&/span&&span class=&p&&)&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/todo'&/span&&span class=&p&&,&/span& &span class=&n&&methods&/span&&span class=&o&&=&/span&&span class=&p&&[&/span&&span class=&s1&&'POST'&/span&&span class=&p&&])&/span&
&span class=&k&&def&/span& &span class=&nf&&add&/span&&span class=&p&&():&/span&
&span class=&n&&content&/span& &span class=&o&&=&/span& &span class=&n&&request&/span&&span class=&o&&.&/span&&span class=&n&&form&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&(&/span&&span class=&s1&&'content'&/span&&span class=&p&&,&/span& &span class=&bp&&None&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&ow&&not&/span& &span class=&n&&content&/span&&span class=&p&&:&/span&
&span class=&n&&abort&/span&&span class=&p&&(&/span&&span class=&mi&&400&/span&&span class=&p&&)&/span&
&span class=&n&&db&/span&&span class=&o&&.&/span&&span class=&n&&todos&/span&&span class=&o&&.&/span&&span class=&n&&insert&/span&&span class=&p&&(&/span&&span class=&n&&Todo&/span&&span class=&o&&.&/span&&span class=&n&&create_doc&/span&&span class=&p&&(&/span&&span class=&n&&content&/span&&span class=&p&&))&/span&
&span class=&k&&return&/span& &span class=&n&&Response&/span&&span class=&p&&()&/span& &span class=&c1&&#200&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/todo/&tid&/finish'&/span&&span class=&p&&,&/span& &span class=&n&&methods&/span&&span class=&o&&=&/span&&span class=&p&&[&/span&&span class=&s1&&'PUT'&/span&&span class=&p&&])&/span&
&span class=&k&&def&/span& &span class=&nf&&finish&/span&&span class=&p&&(&/span&&span class=&n&&tid&/span&&span class=&p&&):&/span&
&span class=&n&&result&/span& &span class=&o&&=&/span& &span class=&n&&db&/span&&span class=&o&&.&/span&&span class=&n&&todos&/span&&span class=&o&&.&/span&&span class=&n&&update_one&/span&&span class=&p&&(&/span&
&span class=&p&&{&/span&&span class=&s1&&'_id'&/span&&span class=&p&&:&/span& &span class=&n&&ObjectId&/span&&span class=&p&&(&/span&&span class=&n&&tid&/span&&span class=&p&&)},&/span&
&span class=&p&&{&/span&
&span class=&s1&&'$set'&/span&&span class=&p&&:&/span& &span class=&p&&{&/span&
&span class=&s1&&'is_finished'&/span&&span class=&p&&:&/span& &span class=&bp&&True&/span&&span class=&p&&,&/span&
&span class=&s1&&'finished_at'&/span&&span class=&p&&:&/span& &span class=&n&&time&/span&&span class=&p&&()&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&result&/span&&span class=&o&&.&/span&&span class=&n&&matched_count&/span& &span class=&o&&==&/span& &span class=&mi&&0&/span&&span class=&p&&:&/span&
&span class=&n&&abort&/span&&span class=&p&&(&/span&&span class=&mi&&404&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&n&&Response&/span&&span class=&p&&()&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/todo/&tid&'&/span&&span class=&p&&,&/span& &span class=&n&&methods&/span&&span class=&o&&=&/span&&span class=&p&&[&/span&&span class=&s1&&'DELETE'&/span&&span class=&p&&])&/span&
&span class=&k&&def&/span& &span class=&nf&&delete&/span&&span class=&p&&(&/span&&span class=&n&&tid&/span&&span class=&p&&):&/span&
&span class=&n&&result&/span& &span class=&o&&=&/span& &span class=&n&&db&/span&&span class=&o&&.&/span&&span class=&n&&todos&/span&&span class=&o&&.&/span&&span class=&n&&delete_one&/span&&span class=&p&&(&/span&
&span class=&p&&{&/span&&span class=&s1&&'_id'&/span&&span class=&p&&:&/span& &span class=&n&&ObjectId&/span&&span class=&p&&(&/span&&span class=&n&&tid&/span&&span class=&p&&)}&/span&
&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&result&/span&&span class=&o&&.&/span&&span class=&n&&matched_count&/span& &span class=&o&&==&/span& &span class=&mi&&0&/span&&span class=&p&&:&/span&
&span class=&n&&abort&/span&&span class=&p&&(&/span&&span class=&mi&&404&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&n&&Response&/span&&span class=&p&&()&/span&
&span class=&k&&if&/span& &span class=&n&&__name__&/span& &span class=&o&&==&/span& &span class=&s1&&'__main__'&/span&&span class=&p&&:&/span&
&span class=&n&&app&/span&&span class=&o&&.&/span&&span class=&n&&run&/span&&span class=&p&&(&/span&&span class=&n&&host&/span&&span class=&o&&=&/span&&span class=&s1&&'0.0.0.0'&/span&&span class=&p&&,&/span& &span class=&n&&port&/span&&span class=&o&&=&/span&&span class=&mi&&8000&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&这样一来,应用的数据便能持久化到本地了。现在,整个应用看起来是下面这样的:&/p&&br&&figure&&img src=&https://pic1.zhimg.com/v2-0aa027a5a04db7acb94f78_b.png& data-rawwidth=&1240& data-rawheight=&205& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&https://pic1.zhimg.com/v2-0aa027a5a04db7acb94f78_r.jpg&&&/figure&&br&&p&现在往mongodb插入1万条数据。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import requests
for i in range(10000):
requests.post('http://127.0.0.1:8000/todo', {'content': str(i)})
&/code&&/pre&&/div&&p&获取todo的接口目前是有问题的,因为它一次性把数据库的所有记录都返回了,当数据记录增长到一万条的时候,这个接口的请求就会变的非常慢,需要500ms后才能发出响应。现在对它进行如下的改造:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/todo'&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&index&/span&&span class=&p&&():&/span&
&span class=&n&&start&/span& &span class=&o&&=&/span& &span class=&n&&request&/span&&span class=&o&&.&/span&&span class=&n&&args&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&(&/span&&span class=&s1&&'start'&/span&&span class=&p&&,&/span& &span class=&s1&&''&/span&&span class=&p&&)&/span&
&span class=&n&&start&/span& &span class=&o&&=&/span& &span class=&nb&&int&/span&&span class=&p&&(&/span&&span class=&n&&start&/span&&span class=&p&&)&/span& &span class=&k&&if&/span& &span class=&n&&start&/span&&span class=&o&&.&/span&&span class=&n&&isdigit&/span&&span class=&p&&()&/span& &span class=&k&&else&/span& &span class=&mi&&0&/span&
&span class=&n&&todos&/span& &span class=&o&&=&/span& &span class=&n&&db&/span&&span class=&o&&.&/span&&span class=&n&&todos&/span&&span class=&o&&.&/span&&span class=&n&&find&/span&&span class=&p&&()&/span&&span class=&o&&.&/span&&span class=&n&&sort&/span&&span class=&p&&([(&/span&&span class=&s1&&'created_at'&/span&&span class=&p&&,&/span& &span class=&o&&-&/span&&span class=&mi&&1&/span&&span class=&p&&)])&/span&&span class=&o&&.&/span&&span class=&n&&limit&/span&&span class=&p&&(&/span&&span class=&mi&&10&/span&&span class=&p&&)&/span&&span class=&o&&.&/span&&span class=&n&&skip&/span&&span class=&p&&(&/span&&span class=&n&&start&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&n&&dumps&/span&&span class=&p&&(&/span&&span class=&n&&todos&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&每次只取十条记录,按创建日期排序,先取最新的,用分页的方式获取以往记录。改造后的接口现在只需50ms便能返回响应。&/p&&p&现在对这个接口进行性能测试:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&wrk -c 100 -t 12 -d 5s http://127.0.0.1:8000/todo
Running 5s test @ http://127.0.0.1:8000/todo
12 threads and 100 connections
Thread Stats
220 requests in 5.09s, 338.38KB read
Socket errors: connect 0, read 0, write 0, timeout 87
Requests/sec:
Transfer/sec:
&/code&&/pre&&/div&&p&rps只有43。我们继续进行改进,通过观察我们发现我们查询todo时需要通过created_at这个字段进行排序再过滤,这样以来每次查询都要先对10000条记录进行排序,效率自然变的很低,对于这个场景,可以对created_at这个字段做索引:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&db.todos.ensureIndex({'created_at': -1})
&/code&&/pre&&/div&&p&通过explain我们轻易地看出mongo使用了索引做扫描&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&& db.todos.find().sort({'created_at': -1}).limit(10).explain()
&queryPlanner& : {
&plannerVersion& : 1,
&namespace& : &todo.todos&,
&indexFilterSet& : false,
&parsedQuery& : {},
&winningPlan& : {
&stage& : &LIMIT&,
&limitAmount& : 10,
&inputStage& : {
&stage& : &FETCH&,
&inputStage& : {
&stage& : &IXSCAN&,
&keyPattern& : {
&created_at& : -1.0
&indexName& : &created_at_-1&,
&isMultiKey& : false,
&multiKeyPaths& : {
&created_at& : []
&isUnique& : false,
&isSparse& : false,
&isPartial& : false,
&indexVersion& : 2,
&direction& : &forward&,
&indexBounds& : {
&created_at& : [
&[MaxKey, MinKey]&
&rejectedPlans& : []
&serverInfo& : {
&host& : &841bf506b6ec&,
&port& : 27017,
&version& : &3.4.1&,
&gitVersion& : &5e103c4fd0baacfbd7&
&ok& : 1.0
&/code&&/pre&&/div&&p&现在再做一轮性能测试,有了索引之后就大大降低了排序的成本,rps提高到了298。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&wrk -c 100 -t 12 -d 5s http://127.0.0.1:8000/todo
Running 5s test @ http://127.0.0.1:8000/todo
12 threads and 100 connections
Thread Stats
47.51ms 357.47ms
1511 requests in 5.06s, 2.27MB read
Requests/sec:
Transfer/sec:
&/code&&/pre&&/div&&p&再把重心放到app server上,目前我们使用flask内建的wsgi server,这个server由于是单进程单线程模型的,所以性能很差,一个请求不处理完的话服务器就会阻塞住其他请求,我们需要对这个server做替换。关于python web的app server选择,目前主流采用的有:&/p&&ul&&li&gunicorn&/li&&li&uWSGI&/li&&/ul&&p&我们看&a href=&http://link.zhihu.com/?target=http%3A//www.jianshu.com/p/docs.gunicorn.org/en/latest/design.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&gunicorn&/a&文档可以得知,gunicorn是一个python编写的高效的WSGI HTTP服务器,gunicorn使用pre-fork模型(一个master进程管理多个child子进程),使用gunicorn的方法十分简单:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&gunicorn --workers=9 server:app --bind 127.0.0.1:8000
&/code&&/pre&&/div&&p&根据文档说明使用(2 * cpu核心数量)+1个worker,还要传入一个兼容wsgi app的start up方法,通过Flask的源码可以看到,Flask这个类实现了下面这个接口:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&
&span class=&k&&def&/span& &span class=&nf&&__call__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&environ&/span&&span class=&p&&,&/span& &span class=&n&&start_response&/span&&span class=&p&&):&/span&
&span class=&sd&&&&&Shortcut for :attr:`wsgi_app`.&&&&/span&
&span class=&k&&return&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&wsgi_app&/span&&span class=&p&&(&/span&&span class=&n&&environ&/span&&span class=&p&&,&/span& &span class=&n&&start_response&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&也就是说我们只需把flask实例的名字传给gunicorn就ok了:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&gunicorn --workers=9 server:app --bind 127.0.0.1:8000
[ 11:20:01 +0800] [5855] [INFO] Starting gunicorn 19.6.0
[ 11:20:01 +0800] [5855] [INFO] Listening at: http://127.0.0.1:)
[ 11:20:01 +0800] [5855] [INFO] Using worker: sync
[ 11:20:01 +0800] [5889] [INFO] Booting worker with pid: 5889
[ 11:20:01 +0800] [5890] [INFO] Booting worker with pid: 5890
[ 11:20:01 +0800] [5891] [INFO] Booting worker with pid: 5891
[ 11:20:01 +0800] [5892] [INFO] Booting worker with pid: 5892
[ 11:20:02 +0800] [5893] [INFO] Booting worker with pid: 5893
[ 11:20:02 +0800] [5894] [INFO] Booting worker with pid: 5894
[ 11:20:02 +0800] [5895] [INFO] Booting worker with pid: 5895
[ 11:20:02 +0800] [5896] [INFO] Booting worker with pid: 5896
[ 11:20:02 +0800] [5897] [INFO] Booting worker with pid: 5897
&/code&&/pre&&/div&&p&可以看到gunicorn启动了9个进程(其中1个父进程)监听请求。使用了多进程的模型看起来是下面这样的:&/p&&br&&figure&&img src=&https://pic2.zhimg.com/v2-7ecc09cd02f41e092605d_b.png& data-rawwidth=&1240& data-rawheight=&548& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&https://pic2.zhimg.com/v2-7ecc09cd02f41e092605d_r.jpg&&&/figure&&br&&p&继续进行性能测试,可以看到吞吐量又有了很大的提升:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&wrk -c 100 -t 12 -d 5s http://127.0.0.1:8000/todo
Running 5s test @ http://127.0.0.1:8000/todo
12 threads and 100 connections
Thread Stats
16.10ms 251.01ms
4373 requests in 5.07s, 6.59MB read
Requests/sec:
Transfer/sec:
&/code&&/pre&&/div&&p&那么gunicorn还能再优化吗,答案是肯定的。回到之前我们发现了这一行:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&[ 11:20:01 +0800] [5855] [INFO] Using worker: sync
&/code&&/pre&&/div&&p&也就是说,gunicorn worker使用的是sync(同步)模式来处理请求,那么它支持async(异步)模式吗,再看gunicorn的文档有下面一段说明:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Async Workers
The asynchronous workers available are based on Greenlets (via Eventlet and Gevent). Greenlets are an implementation of cooperative multi-threading for Python. In general, an application should be able to make use of these worker classes with no changes.
&/code&&/pre&&/div&&p&gunicorn支持基于greenlet的异步的worker,它使得worker能够协作式地工作。当worker阻塞在外部调用的IO操作时,gunicorn会聪明地把执行调度给其他worker,挂起当前的worker,直至IO操作完成后,被挂起的worker又会重新加入到调度队列中,这样gunicorn便有能力处理大量的并发请求了。&/p&&p&gunicorn有两个不错的async worker:&/p&&ul&&li&meinheld&/li&&li&gevent&/li&&/ul&&p&&a href=&http://link.zhihu.com/?target=https%3A//github.com/mopemope/meinheld& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&meinheld&/a&是一个基于picoev的异步WSGI Web服务器,它可以很轻松地集成到gunicorn中,处理wsgi请求。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&gunicorn --workers=9 --worker-class=&meinheld.gmeinheld.MeinheldWorker& server:app --bind 127.0.0.1:8000
[ 11:47:01 +0800] [7497] [INFO] Starting gunicorn 19.6.0
[ 11:47:01 +0800] [7497] [INFO] Listening at: http://127.0.0.1:)
[ 11:47:01 +0800] [7497] [INFO] Using worker: meinheld.gmeinheld.MeinheldWorker
[ 11:47:01 +0800] [7531] [INFO] Booting worker with pid: 7531
[ 11:47:01 +0800] [7532] [INFO] Booting worker with pid: 7532
[ 11:47:01 +0800] [7533] [INFO] Booting worker with pid: 7533
[ 11:47:01 +0800] [7534] [INFO] Booting worker with pid: 7534
[ 11:47:01 +0800] [7535] [INFO] Booting worker with pid: 7535
[ 11:47:01 +0800] [7536] [INFO] Booting worker with pid: 7536
[ 11:47:01 +0800] [7537] [INFO] Booting worker with pid: 7537
[ 11:47:01 +0800] [7538] [INFO] Booting worker with pid: 7538
[ 11:47:01 +0800] [7539] [INFO] Booting worker with pid: 7539
&/code&&/pre&&/div&&p&可以看到现在使用的是meinheld.gmeinheld.MeinheldWorker这个worker。再进行性能测试看看:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&wrk -c 100 -t 12 -d 5s http://127.0.0.1:8000/todo
Running 5s test @ http://127.0.0.1:8000/todo
12 threads and 100 connections
Thread Stats
39.90ms 354.42ms
5684 requests in 5.04s, 8.59MB read
Requests/sec:
Transfer/sec:
&/code&&/pre&&/div&&p&果然提升了不少。&/p&&p&现在有了app server,那需要nginx之类的web server吗?看看&a href=&http://link.zhihu.com/?target=http%3A//www.aosabook.org/en/nginx.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&nginx&/a&反向代理能带给我们什么好处:&/p&&ul&&li&负载均衡,把请求平均地分到上游的app server进程。&/li&&li&静态文件处理,静态文件的访问交给nginx来处理,降低了app server的压力。&/li&&li&接收完客户端所有的TCP包,再一次交给上游的应用来处理,防止app server被慢请求干扰。&/li&&li&访问控制和路由重写。&/li&&li&强大的ngx_lua模块。&/li&&li&Proxy cache。&/li&&li&Gzip,SSL...&/li&&/ul&&p&为了以后的扩展性,带上一个nginx是有必要的,但如果你的应用没大的需求,那么可加可不加。&/p&&p&想让nginx反向代理gunicorn,只需对nginx的配置文件加入几行配置,让nginx通过proxy_pass打到gunicorn监听的端口上就可以了:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&
listen 8888;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $
proxy_set_header X-Real-IP $remote_
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_
&/code&&/pre&&/div&&p&现在应用的结构是这样的:&/p&&br&&figure&&img src=&https://pic3.zhimg.com/v2-86eb1b7e_b.png& data-rawwidth=&1240& data-rawheight=&485& class=&origin_image zh-lightbox-thumb& width=&1240& data-original=&https://pic3.zhimg.com/v2-86eb1b7e_r.jpg&&&/figure&&br&&p&但仅仅是这样还是不足以应对高并发下的请求的,洪水般的请求势必是对数据库的一个重大考验,把请求数提升到1000,出现了大量了timeout:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&wrk -c 1000 -t 12 -d 5s http://127.0.0.1:8888/todo
Running 5s test @ http://127.0.0.1:8888/todo
12 threads and 1000 connections
Thread Stats
4548 requests in 5.10s, 6.52MB read
Socket errors: connect 0, read 297, write 0, timeout 36
Non-2xx or 3xx responses: 289
Requests/sec:
Transfer/sec:
&/code&&/pre&&/div&&p&阻止洪峰的方法有:&/p&&ul&&li&限流(水桶算法)&/li&&li&分流(负载均衡)&/li&&li&缓存&/li&&li&访问控制&/li&&/ul&&p&等等..这里重点说缓存,缓存系统是每个web应用程序重要的一个模块,缓存的作用是把热点数据放入内存中,降低对数据库的压力。&/p&&p&下面用redis来对第一页的数据进行缓存:&/p&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&rds&/span& &span class=&o&&=&/span& &span class=&n&&redis&/span&&span class=&o&&.&/span&&span class=&n&&StrictRedis&/span&&span class=&p&&(&/span&&span class=&s1&&'127.0.0.1'&/span&&span class=&p&&,&/span& &span class=&mi&&6379&/span&&span class=&p&&)&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/todo'&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&index&/span&&span class=&p&&():&/span&
&span class=&n&&start&/span& &span class=&o&&=&/span& &span class=&n&&request&/span&&span class=&o&&.&/span&&span class=&n&&args&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&(&/span&&span class=&s1&&'start'&/span&&span class=&p&&,&/span& &span class=&s1&&''&/span&&span class=&p&&)&/span&
&span class=&n&&start&/span& &span class=&o&&=&/span& &span class=&nb&&int&/span&&span class=&p&&(&/span&&span class=&n&&start&/span&&span class=&p&&)&/span& &span class=&k&&if&/span& &span class=&n&&start&/span&&span class=&o&&.&/span&&span class=&n&&isdigit&/span&&span class=&p&&()&/span& &span class=&k&&else&/span& &span class=&mi&&0&/span&
&span class=&n&&data&/span& &span class=&o&&=&/span& &span class=&n&&rds&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&(&/span&&span class=&s1&&'todos'&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&data&/span& &span class=&ow&&and&/span& &span class=&n&&start&/span& &span class=&o&&==&/span& &span class=&mi&&0&/span&&span class=&p&&:&/span&
&span class=&k&&return&/span& &span class=&n&&data&/span&
&span class=&n&&todos&/span& &span class=&o&&=&/span& &span class=&n&&db&/span&&span class=&o&&.&/span&&span class=&n&&todos&/span&&span class=&o&&.&/span&&span class=&n&&find&/span&&span class=&p&&()&/span&&span class=&o&&.&/span&&span class=&n&&sort&/span&&span class=&p&&([(&/span&&span class=&s1&&'created_at'&/span&&span class=&p&&,&/span& &span class=&o&&-&/span&&span class=&mi&&1&/span&&span class=&p&&)])&/span&&span class=&o&&.&/span&&span class=&n&&limit&/span&&span class=&p&&(&/span&&span class=&mi&&10&/span&&span class=&p&&)&/span&&span class=&o&&.&/span&&span class=&n&&skip&/span&&span class=&p&&(&/span&&span class=&n&&start&/span&&span class=&p&&)&/span&
&span class=&n&&data&/span& &span class=&o&&=&/span& &span class=&n&&dumps&/span&&span class=&p&&(&/span&&span class=&n&&todos&/span&&span class=&p&&)&/span&
&span class=&n&&rds&/span&&span class=&o&&.&/span&&span class=&n&&set&/span&&span class=&p&&(&/span&&span class=&s1&&'todos'&/span&&span class=&p&&,&/span& &span class=&n&&data&/span&&span class=&p&&,&/span& &span class=&mi&&3600&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&n&&data&/span&
&/code&&/pre&&/div&&p&只有在第一次请求时接触到数据库,其余请求都会从缓存中读取,瞬间就提高了应用的rps。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&wrk -c 1000 -t 12 -d 5s http://127.0.0.1:8888/todo
Running 5s test @ http://127.0.0.1:8888/todo
12 threads and 1000 connections
Thread Stats
15255 requests in 5.10s, 22.77MB read
Socket errors: connect 0, read 382, write 0, timeout 0
Non-2xx or 3xx responses: 207
Requests/sec:
Transfer/sec:
&/code&&/pre&&/div&&p&上面的这个示例只展示了基础的缓存方式,并没有针对多用户的情况处理,在涉及到状态条件的影响下,应该使用更加复杂的缓存策略。&/p&&p&现在再来考虑使用缓存不当会造成几个问题,设置缓存的时间是3600秒,当3600秒过后缓存失效,而新缓存又没完成的中间时间内,如果有大量请求到来,就会蜂拥去查询数据库,这种现象称为&b&缓存雪崩&/b&,针对这个情况,可以对数据库请求这个动作进行加锁,只允许第一个请求访问数据库,更新缓存后其他的请求都会访问缓存,第二种方法是做二级缓存,拷贝缓存比一级缓存设置更长的过期时间。还有&b&缓存穿透&/b&和&b&缓存一致性&/b&等问题虽然这里没有体现,但也是缓存设计中值得思考的几个点。&/p&&p&下面是加入缓存后的系统结构:&/p&&br&&figure&&img src=&https://pic4.zhimg.com/v2-207c6dfdda5b507ec4

我要回帖

更多关于 python如何引入 的文章

 

随机推荐