向Google App Engine上传数据的几个心得(上)

     经常访问我的网站的网友可能发现我的网站最近挂了几天,这是因为GAE的域名服务器被“墙”了,虽然我将网站迁移到GAE的时候就已经预料到这样的情况,还是觉得很难受,经过多方考虑之后,我决定逐步的放弃我原来的域名Step1.cn和Dituren.cn,而将网站全部迁移到GAE上使用GAE的二级域名来访问,这样做又将对网站产生很大的影响,我主要考虑到以下几点才决定完全考虑使用GAE:
    1.国内的主机和域名的相关服务实在是让我很失望,具体的原因我就不说了
    2.我太喜欢GAE了,提供的服务很好,而且免费服务已经完全可以够我使用
    3.我希望自己能够渐渐走向那个方向的研究
    因此,虽然appspot也有被彻底“墙”掉的危险,同时这样意味着网站很多地方都要考虑重写,不过我还是决定将网站逐步的迁移到GAE上。
    迁移的重要过程就是数据,上次我也曾经使用了BulkLoader上传了一些数据,并发现了GAE数据索引的一个BUG,这次我又开始在1.1.9的基础上上传了很多数据,也遇到了一些问题,将其中的一些心得说一下,我希望假如后面有朋友需要向GAE上传数据,只要按照我的方式来做即可。
    下面,我按照步骤来讲解:
    1. Google App Engine(1.1.9)对BulkLoader的模式进行了优化,优化之后,确实是好用了一些,至少不用像以前一样每次上传一个表都要先上传一个服务端的接收类,Google提供了一个remote_api专门来作为数据上传的服务端,因此首先要做的是在app.yaml里面设置这个remote_api的路径,例如,我的配置是这样的:

 

- url: /data/remote_api
  script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
  login: admin

 

    我将/data/remote_api指向到这个remote_api服务,这样,通过http://yourappid.appspot.com/data/remote_api就能够访问这个API了。

    2.在部署好remote_api之后,就要开始准备数据文件,将你的数据导出成CSV逗号分隔文件,我的建议是指定双引号作为文本限定符,并不要在文件的第一行显示列名称(虽然好像也支持这样做,后面我还会提到),例如:

 

"那曲","31.4467,91.9914","青藏铁路公司","西藏自治区那曲地区那曲县那曲镇门地乡俄玛迪格村","","542421100000","","",""

 

    偶的网站是做地理的,很多地方用到经纬度,我们在SQL数据库之中通常是用两个float字段(lat,lng)来标识一个经纬度的,可是在GAE之中,有专门来标识经纬度的类型叫GeoPt,你完全可以考虑继续使用lat和lng两个字段的方式,如果你想用GeoPt,就应该像上面范例数据那样将经纬度用逗号分隔,这种情况下,这个字段必须用引号包含起来,需要说明的是,假如你的lat和lng允许为空,可能会出现问题,我想了想,决定在为空的时候统一用0,0来代表,因此要在SQL之中输出这个列的话,可以考虑用一下SQL片段:

 

convert ( varchar ( 20 ),( case   when  lat  is   null   then   0   else  lat  end )) + ' , ' + convert ( varchar ( 20 ), case   when  lng  is   null   then   0   else  lng  end as  latlng

 

    将上面的CSV数据文件保存为Utf-8格式,我将其命名为Train_stations.csv,后面将会用到这个名称

    3.有了CSV数据文件,我们需要指定这个数据保存到GAE之中的格式,建立一个python文件,例如,包含如下内容

ContractedBlock.gif ExpandedBlockStart.gif Train_stations.py
 1# -*- coding: utf-8 -*- #   
 2import datetime
 3from google.appengine.api import datastore_types
 4from google.appengine.ext import db
 5
 6class Train_stations(db.Model):
 7  name = db.StringProperty()
 8  latlng = db.GeoPtProperty()
 9  superior = db.StringProperty()
10  address = db.StringProperty()
11  postcode = db.StringProperty()
12  regionCode = db.StringProperty()
13  level = db.StringProperty()
14  telephone = db.StringProperty()
15  oldName = db.StringProperty()
16
17class Train_stationsLoader(Loader):
18  def __init__(self):
19    Loader.__init__(self, 'Train_stations',
20                    [('name', unicode),
21                     ('latlng', datastore_types.GeoPt),
22                     ('superior', unicode),
23                     ('address', unicode),
24                     ('postcode', str),
25                     ('regionCode', str),
26                     ('level', unicode),
27                     ('telephone', str),
28                     ('oldName', unicode)
29                     ])
30


    可以看到这个文件指定了数据的格式(上面的Train_stations类)和数据读取方式(下面的Train_stationsLoader类),按照CSV之中的字段顺序一一指定即可,具体这些字段的类型可参考Types and Property Classes,我需要特别说明的是,如果该字段是不包含中文的ascii字符,可以考虑使用str类型,否则,必须使用unicode类型,必须要小心选择,否则上传可能出错,或者说上传之后查看是乱码。
    4.完成以上的工作之后,我们理论上就可以开始上传了,不过还有一个问题,就是Google的GAE对中文数据上传有问题,我们需要更改一下源码,在你的GAE文件夹下找到\google_appengine\google\appengine\tools\bulkloader.py(请注意,我是用的版本是1.1.9)在第1931行附近找到如下代码:

 

     for  (name, converter), val  in  zip(self. __properties , values):
      
if  converter  is  bool  and  val.lower()  in  ( ' 0 ' ' false ' ' no ' ):
          val 
=  False
      properties[name] 
=  converter(val)

 

    将它更改成以下代码:

     for  (name, converter), val  in  zip(self. __properties , values):
      
if  converter  is  bool  and  val.lower()  in  ( ' 0 ' ' false ' ' no ' ):
          val 
=  False
      
if  isinstance(val,str)  and   not  isinstance(val, unicode):
          val
= unicode(val, ' utf-8 ' )
      properties[name] 
=  converter(val)

 

    实际上就是插入了两行,请对应清楚,网上对于GAE不能上传中文也有一些解决方案,我参考了很多,但或多或少都有一些问题,建议一定时间内以我的方式为准,呵呵。

    5.这下确实可以开始上传数据了,不过假如你的数据比较大的话(超过5000行),可就要注意了,那个上传初始化的过程会相当的慢,我有一次上传一个75W的数据,初始化了6个小时硬是没有反应过来,被我杀了,那这个所谓的初始化过程是在干什么呢?经过我仔细研究,发现那个家伙居然只是在判断这个CSV文件的第一行是不是字段名称(是否有文件头),我真是被雷倒了!所以假如你确定首行不是字段名称,建议还是到上面的那个bulkloader.py,第300行左右,找到这一段:

     if  csv_content:
      has_headers 
=  csv.Sniffer().has_header(csv_content)
    
else :
      has_headers 
=  False

 

    更改成下面的简单的一句:

 

    has_headers  =  False


    这样的话,就可以忽略“初始化”进程,基本上是立即开始传输。

    6.然后你就可以按照以下方式运行传输过程,开始传输了:

 

bulkloader . py --num_threads = 1  --config_file = Train_stations . py --filename = Train_stations . csv --kind = Train_stations --url = http: // yourappid . appspot . com / data / remote_api

 

    中间可能要输入你的用户名和密码,然后就可以看到传输开始,在结束后要注意看最后的输出,看是全部完成了还是出错中断了。

    写到这里,发现本文已经很长了,不适合再写下去了,因此,打算先写到这里,后面再写一个(下),将包含如下内容:

    1.上传中断了,如何接着上次的继续上传?
    2.数据有问题,如何快速的删除一个表之中的所有数据?
    3.如何知道表之中的记录的条数?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值