zoukankan      html  css  js  c++  java
  • tornado with MySQL, torndb, django model, SQLAlchemy ==> JSON dumped

    现在,我们用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':'0','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':'0','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':'0','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("
    ","",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 10000 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!

  • 相关阅读:
    网页加速的14条优化法则 网站开发与优化
    .NET在后置代码中输入JS提示语句(背景不会变白)
    C语言变量声明内存分配
    SQL Server Hosting Toolkit
    An established connection was aborted by the software in your host machine
    C语言程序设计 2009春季考试时间和地点
    C语言程序设计 函数递归调用示例
    让.Net 程序脱离.net framework框架运行
    C语言程序设计 答疑安排(2009春季 110周) 有变动
    软件测试技术,软件项目管理 实验时间安排 2009春季
  • 原文地址:https://www.cnblogs.com/spaceship9/p/3511915.html
Copyright © 2011-2022 走看看