Tornado学习笔记(一)

最近开始用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的页面编写很简单。比如,这是一个用作验证登录用户的类,逐行解释一下:

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内容如下

{% 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‘ %}

对于主代码,应如下:

#-*- 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):
    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)
    server.start(0)
    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上最讨厌。

时间: 2024-10-07 04:01:15

Tornado学习笔记(一)的相关文章

python框架之 Tornado 学习笔记(一)

tornado 一个简单的服务器的例子: 首先,我们需要安装 tornado ,安装比较简单: pip  install tornado 测试安装是否成功,可以打开python 终端,输入: import     tornado.httpserver 若无报错,则证明安装成功 接下来就可以写一个简单的服务器了,如下: import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web

Tornado 学习笔记 Tornado基础 1

这是根据官方文档学习后总结的笔记: Tornado基础 Tornado是一套web框架和异步网络功能库,使用非阻塞是IO,可支持数万个活动连接.支持长活跃连接,支持 long polling长连接,支持WebSockets. A web framework (including RequestHandler which is subclassed to create web applications, and various supporting classes). Client- and se

Tornado学习笔记(三)

记录一些Tornado中的常用知识. 获取远端IP,直连tornado,用self.request.remote_ip,如果是走nginx反向代理方式,需要在nginx中的Server/Location配置如下                 proxy_pass http://data.xxx.com;                 #proxy_redirect off;                 proxy_set_header Host $host;               

Tornado 学习笔记3 tornado started

这个笔记是看Introduction.to.Tornado 一书时总结的. Tornado适合创建可灵活扩展的社区程序.实时的分析引擎,以及REST风格的API.由Bret Taylor等人为FriendFeed开发,后被Facebook收购. 多数的社区程序实时更新显示新消息,状态更改以及用户提示,需要客户端一直保持连接用来等待服务器的响应.使用线程模式的服务器如apache及其comet实现很容易因为达到线程池最大值,而无法接受新的请求. 相对于使用线程模式管理缓冲池的方式,Tornado使

tornado 学习笔记

import tornado.ioloop import tornado.web class MainHanlwe(tornado.web.RequestHandler): def get(self): login_user=self.get_secure_cookie('login_user',None) if login_user: self.write(login_user) else: self.redirect('/login') class LoginHanmder(tornado.

tornado学习笔记19 Tornado框架分析

19.1 Http服务器请求处理流程图   (1) 调用HTTPServer的bind方法,绑定Socket的监听端口号: (2) 调用HTTPServer的listen方法,初始化一个listen socket对象: (3) 调用add_hander方法,将初始化的listen socket对象添加至IOLoop池中,这时的socket就开始监听来自客户端的请求. (4) 客户端发送HTTP请求: (5) listen socket监听到客户端的请求,调用listen socket的accep

tornado学习笔记(四)

4 Databases 1 pymongo 2 simple word dictionary 3 Burts books read from database editing and adding books 4 Databases 1 pymongo pymongo是python用来连接MongoDB数据库的一个库,可以pip安装:pip install pymongo 命令行mongod运行mongodb服务器后,可以使用pymongo来进行连接 >>> import pymongo

tornado 学习笔记7 RequestHandler功能分析

       在第5部分讲到,构建一个tornado网站,必须包含一个或者多个handler,这些handler是RequestHandler的子类.每个请求都会被映射到handler中进行处理,处理后再将结果返回给客户端.所以,可以看到hanlder作为客户端请求跟业务服务逻辑间的桥梁,如果拿MVC的模式来类比的话,每个handler就相当于MVC中的Controller.        RequestHanlder作为所有hanlder的父类,我们看看他有哪些方法与接口,子类需要怎样继承?

tornado学习笔记(三)

3 Extending Templates 1 blocks and substitutions 2 autoescaping 3 UI modules basic module usage modules in depth embedding javascript and css 3 Extending Templates 1 blocks and substitutions 模板可以继承于基类模板,这样可以重复使用很多模块减少代码量 如在index.html中继承main.html,在ind