11月 18

tornado 使用配置文件的问题测试

  使用tornado做个能承担高负载的接口,配置部分是否要使用配置文件(ConfigParser)。现在有两个问题需要测试。第一、配置文件是否一次性加载,我可不希望,每次调用都会加载配置文件。第二、修改配置文件是否可以自动加载。在tornado中py文件可以自动加载,这样服务就不需要重启,服务也不会间断。
  首先是做一个tornado的测试页。在目录opt下建立testconfig文件夹,在testconf下编写代码。共有4个文件。

  主文件 main.py 代码如下:

#!/bin/env python
# -*- coding:utf-8 -*-
# -------------------------------------------------------------------------------
# Filename:    main.py
# Revision:    1.0
# Date:        2012-11-18
# Author:      simonzhang
# Email:       simon-zzm@163.com
# Web:         www.simonzhang.net
# -----------------------------------------------------------------------------
import tornado.ioloop
import tornado.web
from index import *
 
application = tornado.web.Application([
    (r"/", MainHandler),
])
 
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

  主文件要调用的部分 index.py 代码如下:

#!/bin/env python
# -*- coding:utf-8 -*-
# -------------------------------------------------------------------------------
# Filename:    main.py
# Revision:    1.0
# Date:        2012-11-18
# Author:      simonzhang
# Email:       simon-zzm@163.com
# Web:         www.simonzhang.net
# -----------------------------------------------------------------------------
import tornado.ioloop
import tornado.web
import ConfigParser

# 配置进行全局加载,如果是放到类中肯定每次都有IO。
cf = ConfigParser.ConfigParser()
cf.read("config.properties")
get_index_file_path = cf.get(cf.sections()[0], "path")
 
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        read_file = open(get_index_file_path, "rb").read()
        self.write("%s" % read_file)

  配置文件名为config.properties内容如下:

[context_path]
path = index.txt

  创建一个index.txt文件,在里面写点要显示的文件。

  开始编写监控配置文件IO的脚本。监控文件变化的部分详见:http://www.simonzhang.net/?p=429。还不知道watchdog能不能做到这个。
  脚本名为 watchfile.py 代码如下:

#!/bin/env python
# -*- coding:utf-8 -*-
# -------------------------------------------------------------------------------
# Filename:    watchfile.py
# Revision:    1.0
# Date:        2012-11-17
# Author:      simonzhang
# Email:       simon-zzm@163.com
# Web:         www.simonzhang.net
# -----------------------------------------------------------------------------
import re
import pyinotify

wm = pyinotify.WatchManager()
mask = pyinotify.IN_OPEN

class EventHandler(pyinotify.ProcessEvent):
    def process_IN_OPEN(self, event):
        self.rebuild(event)
    def rebuild(self, event):
        if (event.dir == False) and (event.name == 'config.properties') :
            print "open config file"

def main():
    handler = EventHandler()
    notifier = pyinotify.Notifier(wm, handler)
    wdd = wm.add_watch('/opt/testconfig',mask, rec=True,auto_add=True )
    notifier.loop()

if __name__ == "__main__":
    main()

  最终测试结果。第一、配置文件是在服务启动时一次加载。第二、配置文件不能自动加载,修改完配置文件必须要重启服务。
  使用ConfigParser来做配置文件,自然非常方便,tornado重启速度很快,但是我还是希望能自动加载,因为在几百台服务的情况下,能自动加载自然比需要重启更方便。所以当前就是把配置直接写到代码中,然后找个文件记录配置位置。之后再研究一下能不能热重启。如果大家有好的办法也烦请请告诉我一声。

9月 27

我的 tornado 启停脚本

  之前在tornado学习中(一)中,写了一个启停的脚本,来启动多个端口,但是其中没有日志部分。我将脚本修改一下,使其能支持启动输出日志。具体可见早起笔记http://www.simonzhang.net/?p=1170

#!/bin/sh
#
# Filename:    main.sh
# Revision:    1.1
# Date:        2012-09-27
# Author:      simonzhang
# web:         www.simonzhang.net
# Email:       simon-zzm@163.com
#
### END INIT INFO

# Source function library.
. /etc/profile

# Set the base value
listen_line=1
listen_start=8000
## info|warning|error|none   
loglevel='info'
log_file_prefix='logs/pypixshow.log'
log_file_max_size=20480

# 
CWD=`pwd`
cd $CWD

# See how we were called.
case "$1" in
  start)
        /bin/rm -rf main.port
	for (( i=0 ; i<${listen_line} ; i++)); do
            listen_port=$[${listen_start}+${i}]
            echo ${listen_port} >> main.port
            python main.py ${listen_port} ${loglevel} ${log_file_prefix} ${log_file_max_size} &
	done
        echo "start ok !"
        ;;
  stop)
        get_port_line=`/bin/cat main.port`
        for i in ${get_port_line};do
             now_pid=`/bin/ps -ef|grep ${i}|grep -v grep|awk ' ''{print $2}'`
             /bin/kill -9 $now_pid
        done
        /bin/rm -rf *.pyc
        echo "stop"
        ;;
  status)
        get_port_line=`/bin/cat main.port`
        for i in ${get_port_line};do
             now_pid=`/bin/ps -ef|grep ${i}|grep -v grep`
             if [ -z "${now_pid}" ] ; then
                 echo ${i} "is stop"
             else
                 echo ${now_pid}
             fi
        done
	;;
  restart)
	$0 stop
	$0 start
	;;
  *)
        echo $"Usage: $0 {start|stop|restart|status}"
        exit 1
esac

exit $rc

main.py 也需要修改,下面简单列出日志相关的代码。

import  tornado.options
import logging

if __name__ == "__main__":
    listen_port =  sys.argv[1]
    tornado.options.options['logging'].set(sys.argv[2])
    tornado.options.options['log_file_prefix'].set(sys.argv[3])
    tornado.options.options['log_file_max_size'].set(int(sys.argv[4]))
    tornado.options.parse_command_line()
    application.listen(listen_port)
    tornado.ioloop.IOLoop.instance().start()
9月 24

python 判断“NoneType”

  使用python+tornado,在数据库中根据用户id取用户名出错了。经查看是由于,手动删除了值,什么数据都没有取回,所以报错。这个用try是不行的,也不想用import types,搞个简单的,使用if判断一下,出错的人都叫“haha”。

_get_user_name = db_get_user(userid)
if type(_get_user_name) == type(None):
    _get_user_name = "haha"
7月 30

tornado 获得复选框组的值

  一个比较二的问题,因为在网上查了一下没有查到,用web.py的架构查看此类问题,有的说是用什么input之类。最后还是自己看看内建文档,原来就这么简单,加个s就搞定了。郁闷呀!
html如下:




tornado获得参数如下:
class xxxxHandler(BaseHandler):
def get(self):
_get_test_list = self.get_arguments(‘CheckboxGroup1’)
print “test values %s”%_get_test_list

6月 18

tornado学习笔记(二)

主要模块

web – FriendFeed 使用的基础 Web 框架,包含了 Tornado 的大多数重要的功能
escape – XHTML, JSON, URL 的编码/解码方法
database – 对 MySQLdb 的简单封装,使其更容易使用
template – 基于 Python 的 web 模板系统
httpclient – 非阻塞式 HTTP 客户端,它被设计用来和 web 及 httpserver 协同工作
auth – 第三方认证的实现(包括 Google OpenID/OAuth、Facebook Platform、Yahoo BBAuth、FriendFeed OpenID/OAuth、Twitter OAuth)
locale – 针对本地化和翻译的支持
options – 命令行和配置文件解析工具,针对服务器环境做了优化

底层模块

httpserver – 服务于 web 模块的一个非常简单的 HTTP 服务器的实现
iostream – 对非阻塞式的 socket 的简单封装,以方便常用读写操作
ioloop – 核心的 I/O 循环

  首先在建立一个mysql数据库,库名为test_tornado,建立一个有用户表,表中包含用户名密码,脚本如下。

CREATE TABLE `user` (
  `id` int(100) NOT NULL DEFAULT '0',
  `user` varchar(20) DEFAULT NULL,
  `passwd` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
);


INSERT INTO `user` VALUES ('0', 'simonzhang', '123456');

  建立监听,和url,启停脚本见上次笔记。
main.py

#!/bin/python
#-*- coding:utf-8 -*-
# Filename:    main.py
# Revision:    1.0
# Date:        2012-06-14
# Author:      simonzhang
# web:         www.simonzhang.net
# Email:       simon-zzm@163.com
### END INIT INFO
import sys
import tornado.ioloop
import tornado.web
from login import *


application = tornado.web.Application([
    (r"/", LoginHandler),
],  cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")


if __name__ == "__main__":
    listen_port =  sys.argv[1]
    application.listen(listen_port)
    tornado.ioloop.IOLoop.instance().start()

  在同级目录下建立templates目录,在templates目录下建立login.html。login.html的源码是


   
      {{title}}
   
   
        
User Name:
Password:

  做一个简单的验证页面,只是个简单判断,学习使用,权限认证和cookie部分不做记录了。编辑login.py源码

#!/bin/python
#-*- coding:utf-8 -*-
# Filename:    main.py
# Revision:    1.0
# Date:        2012-06-14
# Author:      simonzhang
# web:         www.simonzhang.net
# Email:       simon-zzm@163.com
### END INIT INFO
import sys
import tornado.ioloop
import tornado.locale
import tornado.web
import tornado.database
from dbmodel import *


class LoginHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("templates/login.html", title="simonzhan.net")
    def post(self):
        try:
            name = self.get_argument("login_username")
            passwd = self.get_argument("login_password")
            _passwd = get_passwd(name)
            if _passwd is not None:
                if _passwd == _passwd:
                    self.write("hello %s" % (_passwd))
                else:
                    self.render("templates/login.html", title="simonzhan.net")
            else:
               self.render("templates/login.html", title="simonzhan.net")
            #self.write("hello %s" % (name))
        except:
            self.render("templates/login.html", title="simonzhan.net")
            return

  因为要查数据,所以要用到database,将数据库部分放到一文件中去。编辑dbmodol.py源码如下:

#!/bin/env python
# -*- coding: utf-8 -*-
# Filename:    main.py
# Revision:    1.0
# Date:        2012-06-14
# Author:      simonzhang
# web:         www.simonzhang.net
# Email:       simon-zzm@163.com
### END INIT INFO
from tornado import database


def get_passwd(user_name):
    db = database.Connection("192.168.1.41","test_tornado","123456","simonzhang")
    for projects in db.query("SELECT user,passwd from user where user='%s'" % user_name):
        return projects.passwd

  启动服务,在ie里能看到页面,输入正确的账户密码,可以看到经典的话了。基本框架完成,剩下的慢慢学习,慢慢发挥了。