现在,我们用torndo做web开发框架,用他内部机制来处理HTTP请求。传说中的非阻塞式服务。

整来整去,可谓之一波三折。可是,无论怎么样,算是被我做成功了。

在tornado服务上,采用三种数据库模型--》

1)torndb

2)django model

3)SQLAlchemy model

    都同时输出相应的JSON,做API服务。(一个比一个强大的ORM model)

都同时实现了一样的功能

注意:要说明的一点是,在数据库建立模型的时候,是用到的django model建立的数据库模型。所以在使用torndb(SQL 操作语言的ORM) 和 django model的时候,非常简单操作,直接引用就可以使用了。而当再利用SQLAlchemy的时候,将会不得不将django的models 改写成 SQLAlchemy 支持的格式

一、先来说说torndb

使用很简单。

请看代码

import torndb
from tornado.options import define, options # define 完成后,同时生成一个options里面的属性,在下面方便 torndb.Connection
define("port", default=8000, help="run on the given port", type=int)
define("mysql_host", default="127.0.0.1:3306", help="database host")
define("mysql_database", default="center", help="database name")
define("mysql_user", default="root", help="database user")
define("mysql_password", default="root", help="database password")
define("secret", default="justsecret", help="secret key") db = torndb.Connection(
host=options.mysql_host, database=options.mysql_database,
user=options.mysql_user, password=options.mysql_password) # 设定JSON跳出规则,配合torndb取数据用 def __default(obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj, date):
return obj.strftime('%Y-%m-%d')
else:
raise TypeError('%r is not JSON serializable' % obj) def json_dumps(s):
""" """
return json.dumps(s,ensure_ascii=False,default=__default)

然后,看看响应的文档,

对http://localhost:8000/testpage1 的HTTP请求会导入到下面的例程中,

@require_basic_auth
class TestPage1Handler(BaseHandler):
"""This is a test page to show the asker's utmost parent's all details
"""
def post(self, **kwargs):
ret = {'ret':'','msg':''}
parent_asker = kwargs['a']
#boss_profile = db.get("SELECT * from sometable where username=%s", parent_asker)
boss_profile = db.query("SELECT * FROM kms_sgroup")
ret['result'] = { parent_asker : boss_profile }
self.write(json_dumps(ret))
return

注意: db.query("") 跳出的是一个列表,多组数据。多row

db.get("")跳出的是单个元素,单组数据

class Application(tornado.web.Application):
def __init__(self):
handlers = [
# 测试用torndb model用的URL
(r"/testpage1", TestPage1Handler), # 通过 测试结果10000次请求,花费时间10.3秒 每秒处理970次请求
# 测试 django model 用的URL
(r"/testpage2", TestPage2Handler), # 通过 测试结果10000次请求,花费时间18.6秒 每秒处理538次请求
# 测试 sqlalchemy model 用的URL
(r"/testpage3", TestPage3Handler), # 通过 测试结果10000次请求,花费时间24.1秒 每秒处理415次请求 ] settings = dict(
cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
debug=True,
)
tornado.web.Application.__init__(self, handlers, **settings)

二、来看看tornado 和 django model的配合

( tornado 的center.py 文件放在django 的项目下,和 django 的 manage.py 同级 ,  此文件放在 ./center.py )

# let's get userprofile with django model instead! :)
#from api.models import SGroup
# 下面的这个引入django默认目录的环境的行为是必须的!
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'project.settings'

# 这里的SGroup 就是生成数据模型的时候用的。django model生成之后,我们才选择的torndb或者django model ORM的
from api.models import SGroup as SG
from django.core import serializers
@require_basic_auth
class TestPage2Handler(BaseHandler):
def post(self, **kwargs):
ret = {'ret':'','msg':''}
parent_asker = kwargs['a']
boss_profile = SG.objects.all()
# the type of 'data' is string
data = serializers.serialize("json", boss_profile)
# make 'data' a list
data = json.loads(data) # to get data[0]['fields'] . that's what we need beyond the model's "pk" and "model" property of the object data
# data[0]['fields'] is a dict
ret['result'] = { parent_asker : data[0]['fields'] }
self.write(json_dumps(ret))
return

django 的ORM 的自带的serializers 在格式化JSON的时候,有点小区别。

这里式serializers  的官方地址:https://docs.djangoproject.com/en/1.6/topics/serialization/

这里是默认会给出的结果,

JSON

When staying with the same example data as before it would be serialized as JSON in the following way:

[
{
"pk": "4b678b301dfd8a4e0dad910de3ae245b",
"model": "sessions.session",
"fields": {
"expire_date": "2013-01-16T08:16:59.844Z",
...
}
}
]

貌似长的跟想要的不一样,我们只需要fields里面的东西。这就是为什么上面我做了

data = serializers.serialize("json", boss_profile)
# make 'data' a list
data = json.loads(data) # to get data[0]['fields'] . that's what we need beyond the model's "pk" and "model" property of the object data
# data[0]['fields'] is a dict
ret['result'] = { parent_asker : data[0]['fields'] }

这个操作。这样,我们就会得到需要的东西了。一般情况下,字段"pk", "model" 是我们不需要的。fields里面的才是数据库里面保存的鲜活的数据。

三、SQLAlchemy 来配合tornado 表达MySQL

这个才是重头戏,喜欢DIY还有有特定需求的童鞋可以使用这样的方法,就是什么东西都要自己写。很爽!

首先是建立让SQL Alchemy 与 SQL 会话。话说回来了,SQLAlchemy有自己的格式的model,而django有自己的一套。而纯SQL语言将是万能的,因为纯SQL没有任何封装。

所以,为了使用SQLAlchemy,我们需要针对数据结构,封装一个SQLAlchemy的数据模型。

先看看此前的django 的model, 次文件为 ./api/models.py

from django.db import models
class SGroup(models.Model):
""" Subscriber & Group """
# 车机组名称
name = models.CharField(_(u"Subscriber Group Name"), max_length=100, unique=True)
is_active = models.BooleanField(_(u'Active Status'), default=True,
help_text=_('Designates whether this user should be treated as '
'Active. Unselect this instead of deleting accounts.')) date_created = models.DateTimeField(_(u'Account Created'), auto_now_add=True) class Meta:
verbose_name = _(u'Subscriber Group')
verbose_name_plural = _(u'Subscriber Groups') db_table = 'sometable'
ordering = ['-id'] def __unicode__(self):
return self.name

再看看我们封装成SQLAlchemy后的model类型,此文件为 ./sqla/models.py

#coding: utf-8
from sqlalchemy import (
ForeignKey,
Column,
Index,
Integer,
String,
Boolean,
Text,
Unicode,
UnicodeText,
DateTime,
) from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base() import datetime
class SGroup(Base):
""" Subscriber & Group """ __tablename__ = 'sometable'
id = Column(Integer, primary_key=True)
name = Column(String(100), unique=True)
is_active = Column(Boolean, default=True)
date_created = Column(DateTime, default=datetime.datetime.utcnow) #name = models.CharField(_(u"Subscriber Group Name"), max_length=100, unique=True)
#is_active = models.BooleanField(_(u'Active Status'), default=True,
# help_text=_('Designates whether this user should be treated as '
# 'Active. Unselect this instead of deleting accounts.')) #date_created = models.DateTimeField(_(u'Account Created'), auto_now_add=True) #class Meta:
#verbose_name = _(u'Subscriber Group')
#verbose_name_plural = _(u'Subscriber Groups') # db_table = 'sometable'
# ordering = ['-id']

新建数据模型后,我们再来看看如何修改tornado 服务 center.py的代码,添加这些进去

# 先建立sqlalchemy 到 数据库的链接会话
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqla.models import SGroup
mysql_engine = create_engine('mysql://root:root@localhost:3306/center?charset=utf8',encoding = "utf-8",echo =True)
DB_Session = sessionmaker(bind=mysql_engine)
session = DB_Session() # 将SQLAlchemy 对象打包成JSON格式
# 这里重写json.dump(data, cls=json.JSONEncoder) from sqlalchemy.ext.declarative import DeclarativeMeta
def new_alchemy_encoder():
_visited_objs = []
class AlchemyEncoder(json.JSONEncoder):
def default(self, obj):
fields = {}
if isinstance(obj.__class__, DeclarativeMeta):
# don't re-visit self
if obj in _visited_objs:
return None
_visited_objs.append(obj) # an SQLAlchemy class for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
fields[field] = obj.__getattribute__(field)
# a json-encodable dict
else:
# 这里是我新添加的,用来处理JSON对象中,如果存在时间类型的话
# 将按这种方式返回
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
if isinstance(obj, date):
return obj.strftime('%Y-%m-%d') return fields return json.JSONEncoder.default(self, obj)
return AlchemyEncoder @require_basic_auth
class TestPage3Handler(BaseHandler):
"""This is a test page to show the asker's utmost parent's all details
"""
def post(self, **kwargs):
ret = {'ret':'','msg':''}
#name_queue = get_outersystem_org(kwargs)
parent_asker = kwargs['a'] data = session.query(SGroup).all() # 使用新建的JSON encoder对象,作为json.dumps的标准 来dumps数据出去
# 请注意:这里是 json.dumps 与 torndb的例子中的 json_dumps不同
self.write(json.dumps(data, cls=new_alchemy_encoder() ) )
return

好了,SQLAlchemy的道路比较曲折,什么都得重写,而且比较深入,但是可以高度定制!有特殊要求的可以使用SQLAlchemy

我们来测试一下

开始测试

测试用的脚本写好了。

testauth.py

import urllib
import urllib2
import base64
import time def url_post(url):
"""Let's do an analogical browser authentication loop """
TOKEN_KEY_OS1="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
token_encoded = base64.b64encode("level1:"+level2:"+TOKEN_KEY_OS1)
token_encoded = token_encoded.replace("\n","",1)
token = token_encoded postDict = {
#查询指定username的客户信息
#"username":"", } postData = urllib.urlencode(postDict)
request = urllib2.Request(url, postData)
request.get_method = lambda : 'POST'
basic = "Basic "+ token
# 这里是实现了一个HTTP header 的加密验证等服务
request.add_header('Authorization', basic)
response = urllib2.urlopen(request) #return response.readlines() , response.headers.dict
return response

testrequest.py

from testauth import url_post
import datetime def mega_attack(number=100000): start = datetime.datetime.now()
for i in range(number):
r = url_post('http://localhost:8000/testpage1')
print r.readlines()
end = datetime.datetime.now()
td = end - start
print "It takes " + str(td.total_seconds()/60) + " minutes to response " + str(number) + " requests!"
print "Need to upgrade your computer ? :P" if __name__ == "__main__":
mega_attack(10000)

然后我们来测试一下:

$~ python testrequest.py

我们会得到这些数据:

['{"msg": "", "result": {"level1": [{"date_created": "2014-01-08 08:11:03", "is_active": 1, "id": 1, "name": "VIP_level1"}, {"date_created": "2014-01-08 08:11:03", "is_active": 1, "id": 2, "name": "VIP_level2"}]}, "ret": "0"}']
...
...
['{"msg": "", "result": {"level1": [{"date_created": "2014-01-08 08:11:03", "is_active": 1, "id": 1, "name": "VIP_level1"}, {"date_created": "2014-01-08 08:11:03", "is_active": 1, "id": 2, "name": "VIP_level2"}]}, "ret": "0"}']
['{"msg": "", "result": {"level1": [{"date_created": "2014-01-08 08:11:03", "is_active": 1, "id": 1, "name": "VIP_level1"}, {"date_created": "2014-01-08 08:11:03", "is_active": 1, "id": 2, "name": "VIP_level2"}]}, "ret": "0"}']
It takes 0.179841 minutes to response requests!
Need to upgrade your computer ? :P

这里是服务器端看到的内容:

我们把三个不同的数据库模型都进行测试,得到的结果为:

           # 测试用torndb model用的URL
(r"/testpage1", TestPage1Handler), # 通过 测试结果10000次请求,花费时间10.3秒 每秒处理970次请求
# 测试 django model 用的URL
(r"/testpage2", TestPage2Handler), # 通过 测试结果10000次请求,花费时间18.6秒 每秒处理538次请求
# 测试 sqlalchemy model 用的URL
(r"/testpage3", TestPage3Handler), # 通过 测试结果10000次请求,花费时间24.1秒 每秒处理415次请求

总结:

纯SQL最快,毫无疑问!

django model 和 SQLAlchemy model (以下称sqla)各有千秋,

django model里面做了很多优化,sqla 高度封装,里面也做了一些优化,但是如果每次都是徒手来写的话,不一定能一定用到最优方法。针对特殊需求,需要改动的,则需要使用sqla。django model几乎什么都自带了,自带了太多比如serializer 输出的东西,不一定都是我们想要的。但是通过不同方法,都能实现同样的功能。

不同点在于,开发使用思考难度,开发使用效率,服务器相应时间。

从上面来看,

在开发过程中:

        torndb 和 django model 比较好用。当需求发生改变时, torndb 和 django model 可以很快改变以提供所见所得效果。只不过torndb需要的SQL语句标点等校验,是非常耗费开发时间的。

        而,sqla来说,也可以达到共同的效果,但是,每次都要dive into 数据库模型里面,但是对特殊的需求有非常多的变身受应面。

从服务器角度:

        torndb 最快毫无疑问了。django model 是高度封装的ORM, 服务器也还好,不过一般要消耗一半的性能。sqla,可以有多种形态变身,因为sqla支持纯SQL语言也支持sqla自身的model类型。当然,优化做好了sqla也可以速度非常快!

不同的学习曲线。django model 看起来只是算一个比较简单的数据model,而sqlalchemy 几乎覆盖了所有数据能操作的范畴,很强大。

总之,各有千秋。

Happy Hacking and Happy Coding!

tornado with MySQL, torndb, django model, SQLAlchemy ==> JSON dumped的更多相关文章

  1. 【转】Django Model field reference学习总结

    Django Model field reference学习总结(一) 本文档包含所有字段选项(field options)的内部细节和Django已经提供的field types. Field 选项 ...

  2. tornado中使用torndb,连接数过高的问题

    问题背景 最近新的产品开发中,使用了到了Tornado和mysql数据库.但在基本框架完成之后,我在开发时候发现了一个很奇怪的现象,我在测试时,发现数据库返回不了结果,于是我在mysql中输入show ...

  3. 基于python3.x,使用Tornado中的torndb模块操作数据库

    目前Tornado中的torndb模块是不支持python3.x,所以需要修改部分torndb源码即可正常使用 1.开发环境介绍 操作系统:win8(64位),python版本:python3.6(3 ...

  4. Django Model field reference

    ===================== Model field reference ===================== .. module:: django.db.models.field ...

  5. Django model对象接口

    Django model查询 # 直接获取表对应字段的值,列表嵌元组形式返回 Entry.objects.values_list('id', 'headline') #<QuerySet [(1 ...

  6. Django学习之四:Django Model模块

    目录 Django Model 模型 MODEL需要在脑子里记住的基础概念 区分清楚,必须不能混淆的 class Meta 内嵌元数据定义类 简单model创建实例 数据源配置 接着通过models在 ...

  7. mysql 连接 django

    版本: django:1.11.9 python3 mysql 5.7.18 在这里我们认为你已经安装好了mysql,python ,django 下面是来自django官方教程的一段话 If you ...

  8. Linux下安装Python3的django并配置mysql作为django默认数据库(转载)

    我的操作系统为centos6.5 1  首先选择django要使用什么数据库.django1.10默认数据库为sqlite3,本人想使用mysql数据库,但为了测试方便顺便要安装一下sqlite开发包 ...

  9. Django和SQLAlchemy,哪个Python ORM更好?

    ORM是什么? 在介绍Python下的两个ORM框架(Django和SQLAlchemy)的区别之前,我们首先要充分了解ORM框架的用途. ORM代表对象关系映射.ORM中的每个单词解释了他们在实际项 ...

随机推荐

  1. 【转】Oracle修改表空间为自动扩展

    1.数据文件自动扩展的好处1)不会出现因为没有剩余空间可以利用到数据无法写入2)尽量减少人为的维护3)可以用于重要级别不是很大的数据库中,如测试数据库等 2.数据文件自动扩展的弊端1)如果任其扩大,在 ...

  2. 如何实现TWaver 3D颜色渐变

    一般而言,须要实现3D物体的渐变,通常的思路就是通过2D绘制一张渐变canvas图片作为3D对象的贴图.这样的方式是能够解决这类问题的.只是对于一般用户而言,通过2D生成一张渐变的图片.有一定的难度, ...

  3. YII相关资料(干货)

    Sites 网站 yiifeed:Yii 最新动态都在这里 yiigist:Yii 专用的 Packages my-yii:Yii 学习资料和新闻 Docs 文档 Yii Framework 2.0 ...

  4. 《C语言及程序设计初步》网络课程主页

    题记 CSDN要开在线教育频道,向我发出邀请,看能否开些课程. 我近日一直在关注着翻转课堂,试图在传统课堂中引入新的元素,这须要资源建设的积累.没有时间表的工作,非常难把握. 为CSDN做在线课程,为 ...

  5. jquery 部分效果

    $(selector).hide()            隐藏被选元素 $(selector).show()           显示被选元素 $(selector).toggle()        ...

  6. C# 通过扩展WebBrowser捕获网络连接错误信息

    想捕获WebBrowser连接指定网站过程中发生的错误信息,包括网络无法连接.404找不到网页等等错误!经过网上的搜集,找到了以下解决方案,该解决方案不会在网站连接前发出多余的测试请求. 向Webbr ...

  7. Spring Resource之ResourceLoader

    ResourceLoader接口意味着任何实现的对象都能够返回Resource实例. public interface ResourceLoader { Resource getResource(St ...

  8. Android多画面幻灯片:ViewPager基础上,利用与PagerTabStrip出生缺陷(源代码)

    近期使用ViewPager.读了几个人说是不是很清晰的信息,干脆自己写demo总结下. 样例非常easy.Activity里有三个界面能够滑动.每个界面都有一个button并设置好了监听.PagerT ...

  9. Android高仿雅虎天气(两)---代码结构分析

    版本已经升级到1.0.1 源码地址: GitHub:https://github.com/way1989/WayHoo OsChina:http://git.oschina.net/way/WayHo ...

  10. Android高效开发环境(Genymotion,Gradle,Andriod Studio)

    临近十一,项目接近上线,终于有些碎片时间可以查看一些博客. 这篇博客是Android开发大牛Cyril Mottier在去年写的博客,我把它翻译一下共享给国内志同道合的朋友,同时也是对自己一个很好的锻 ...