Tornado学习笔记(一)【tornado的优势】

文章来源:http://slaytanic.blog.51cto.com/2057708/1638403/


最近开始用Tornado做开发了,究其原因,主要是Tornado基于Python,一来代码量少开发速度快,二来采用epoll方式,能够承载的并发量很高。在我的i5台式机上用ab测试,不连接数据库的情况下,单用get生成页面,大概平均的并发量在7900左右。这比php或者java能够承载并发量都高很多很多。三来Python代码可维护性相对来说比php好很多,语法结构清晰。四来,tornado的框架设计的很黄很暴力,以HTTP请求方式作为方法名称,通常情况下,用户写一个页面只需要有get和post两种方式的方法定义就够了。


在学习的过程中遇到一些比较重要的问题,记录下来以后备查,在学习的过程中遇到不少问题,基本都是靠翻墙解决,百度实在是令人痛苦不堪。记录比较零散一些,可能不仅限于tornado,也会包括python的一些知识。由于我也还在学习过程中,所以有些东西不一定详尽或者理解到位,tornado高人勿拍。


tornado入门不是很难,只要理解了他处理的方式就很好做了。tornado在处理网页的时候,针对于URL的连接,实际就是对class类的一个路由映射。而类中的方法通常无非就两种,处理连接请求的get或者post。所以tornado的页面编写很简单。比如,这是一个用作验证登录用户的类,逐行解释一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class  SigninHandler(BaseHandler):  #引入BaseHandler
     def  post( self ):  #HTTP的POST方法,是GET渲染的form中的post method所对应
         username  =  self .get_argument( 'username' #获取form中username的值
         password  =  self .get_argument( 'password' #获取form中password的值
         conn  =  MySQLdb.connect( 'localhost' , user  =  'root' , passwd  =  ' ', db = ' datacenter ', charset = ' utf8', cursorclass  =  MySQLdb.cursors.DictCursor)  #连接数据库,指定cursorclass的目的是要让返回结果以字典的形式呈现,如果不写,是以元组形式返回
         cursor =  conn.cursor()  #定义数据库指针
 
         sql  =  'SELECT * FROM dc_users WHERE username=%s AND password=password(%s)'  #写sql,为何这样写后面再说
         cursor.execute(sql, (username, password,))  #执行SQL
         row  =  cursor.fetchone()  #获取一条,返回值为dict,因为前面连接数据库时定义了cursorclass = MySQLdb.cursors.DictCursor,当然,你需要import MySQLdb.cursors的包
         if  row:  #如果存在记录
             self .set_secure_cookie( 'id' str (row[ 'id' ]).encode( 'unicode_escape' ),  expires_days = None #设置安全cookie,防止xsrf跨域
             self .set_secure_cookie( 'username' , row[ 'username' ].encode( 'unicode_escape' ),  expires_days = None #same
             self .set_secure_cookie( 'role' , row[ 'role' ].encode( 'unicode_escape' ),  expires_days = None #same
             ip  =  self .request.remote_ip  #获取来访者IP
             sql  =  'UPDATE dc_users SET last_access = NOW(), last_ip=%s WHERE id = %s'  #认证审计变更的SQL
             cursor.execute(sql, (ip, row[ 'id' ],))  #执行SQL
             conn.commit()  #提交执行
             cursor.close()  #关闭指针
             conn.close()  #关闭数据库连接
             self .redirect( '/' #转入首页
             return  #返回,按照官方文档的要求,在redirect之后需要写空的return,否则可能会有问题,实测确实会有问题
         else #如果不存在记录
             self .redirect( '/Signin' #跳转回登录页面
             return
     def  get( self ):  #HTTP GET方式
         self .render( 'users/login_form.html' #渲染登录框HTML


login_form.html内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{% include 'header.html' %}  <!--引入header文件,需要跟login_form在同一路径下,否则写相对路径,如 {% include '../header.html' %} -->
< div  class = "container" >
     < h2 >< script >document.write(language.Title + ' ' + language.Version + ' - ' + language.Codename)</ script ></ h2 >
     < form  class = "form-horizontal"  method = "post"  action = "/Signin" <!--这里的action对应的上面Python代码中SigninHandler的post方法-->
         {% module xsrf_form_html() %}  <!--防跨域cookie模块-->
         < div  class = "form-group" >
             < label  class = "col-sm-2 control-label" >< script >document.write(language.Username + language.Colon)</ script ></ label >
             < div  class = "col-sm-4" >< input  class = "form-control"  type = "text"  name = "username"  placeholder = "Username" ></ div >
         </ div >
         < div  class = "form-group" >
             < label  class = "col-sm-2 control-label" >< script >document.write(language.Password + language.Colon)</ script ></ label >
             < div  class = "col-sm-4" >< input  class = "form-control"  type = "password"  name = "password"  placeholder = "Password" ></ div >
         </ div >
         < div  class = "form-group" >
             < div  class = "col-sm-2" ></ div >
             < div  class = "col-sm-4" >
                 < button  type = "submit"  class = "col-sm-4 btn btn-info" >< script >document.write(language.Signin)</ script ></ button >
             </ div >
         </ div >
     </ form >
</ div >
{% include 'footer.html' %}


对于主代码,应如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#-*- coding: utf-8 -*-
 
import  sys
reload (sys)
sys.setdefaultencoding( 'utf-8' )
import  tornado.ioloop
import  tornado.web
import  tornado.httpserver
import  tornado.autoreload
import  os
 
class  BaseHandler(tornado.web.RequestHandler):  #BaseHandler
     def  get_current_user( self ):
         user  =  self .get_secure_cookie( 'username' )
         return  user
 
class  IndexHandler(BaseHandler):
     @tornado.web.authenticated
     def  get( self ):
         if  not  self .current_user:
             self .redirect( '/Signin' #如未登录,则跳转Signin,Signin的GET方法调用的就是login_form.html页面
             return
         self .render( 'welcome.html' #否则渲染welcome.html
 
settings  =  \
     {
         "cookie_secret" "HeavyMetalWillNeverDie" #Cookie secret
         "xsrf_cookies" True #开启跨域安全
         "gzip" False #关闭gzip输出
         "debug" False #关闭调试模式,其实调试模式是很纠结的一事,我喜欢打开。
         "template_path" : os.path.join(os.path.dirname(__file__),  "./templates" ),  #定义模板,也就是login_form.html或header.html相对于本程序所在的位置
         "static_path" : os.path.join(os.path.dirname(__file__),  "./static" ),  #定义JS, CSS等文件相对于本程序所在的位置
         "login_url" "/Signin" #登录URL为/Signin
     }
 
application  =  tornado.web.Application([
     (r "/" , IndexHandler),  #路由设置/ 使用IndexHandler
     (r "/signin" , SigninHandler)  # Signin使用SigninHandler
],  * * settings)
 
if  __name__  = =  "__main__" #启动tornado,配置里如果打开debug,则可以使用autoload,属于development模式,如果关闭debug,则不可以使用autoload,属于production模式。autoload的含义是当tornado监测到有任何文件发生变化,不需要重启server即可看到相应的页面变化,否则是修改了东西看不到变化。
     server  =  tornado.httpserver.HTTPServer(application)
     server.bind( 10002 #绑定到10002端口
     server.start( 0 #自动以多进程方式启动Tornado,否则需要手工启动多个进程
     tornado.ioloop.IOLoop.instance().start()


对于sql部分,执行最好写成cursor.execute(sql, (id,)),将%s的东西以元组形式传递给execute方法,这样做的目的是最大程度避免SQL注入的发生。如果直接写为 'select * from xxx where id = ' + id 或者 'select * from xxx where id = %s' % id 的话,会被注入。另外,如果是sqlite3的话,需要写成 'select * from xxx where id=?' ,然后execute方式一样。


另外,如果开启了禁止xsrf跨域功能的话,在每个HTML的form表单里必须加上{% module xsrf_form_html() %}否则会出现禁止访问的错误。


下篇记录一下编码格式处理,这个在python2上最讨厌。


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值