K_Reverter的网页开发记录

要么不做,要么就当作艺术品来做!

向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 endas latlng

 

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

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

Train_stations.py


    可以看到这个文件指定了数据的格式(上面的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.如何知道表之中的记录的条数?

posted on 2009-03-12 18:47 K_Reverter 阅读(2737) 评论(20) 编辑 收藏

评论

#1楼 2009-03-13 13:16 Rover.Tang      

正需要这资料,我也按照你的写了一篇。不过不知道为什么,昨天晚上删除一些垃圾内容(主要是TEMP等目录下的),如何就不能上传数据了,晕死。今天还要参考此文继续研究。
另外,GAE的IP恢复访问了,又是一个,暂时是:72.14.235.121。你可以试试,我切过去可以用。
 回复 引用 查看   

#2楼[楼主] 2009-03-13 13:42 K_Reverter      

@Rover.Tang
那我暂时也不敢用,我希望能有一个比较好的解决方案来保证被墙之后和还能够访问,否则,太不稳定了
 回复 引用 查看   

#3楼 2009-03-13 23:35 秦锋[未注册用户]

“ --kind=Train_line_station”是不是写错了?应该是“ --kind= Train_stations”?  回复 引用   

#4楼 2009-03-14 01:05 秦锋[未注册用户]

你碰到过这个问题吗?
http://code.google.com/p/googleappengine/issues/detail?id=1143&q=bulkload&colspec=ID%20Type%20Status%20Priority%20Stars%20Owner%20Summary%20Log%20Component

谢谢!
 回复 引用   

#5楼[楼主] 2009-03-14 07:22 K_Reverter      

@秦锋
我没有遇到此问题,而且,我仅仅向Google服务器上传数据,倒没有试过向开发环境上传数据的
 回复 引用 查看   

#6楼[楼主] 2009-03-14 07:23 K_Reverter      

@秦锋
果然是写错了,多谢
 回复 引用 查看   

#7楼 2009-03-14 15:21 Rover.Tang      

报告,14日下午3点20分,step1.appspot.com不能访问,原因未知。  回复 引用 查看   

#8楼 2009-03-14 15:36 Rover.Tang      

另外帮我看看上传错误吧。
我是怎么也找不出原因来了。
怀疑错误问题是:11001, 'getaddrinfo failed',但找了些资料也没找到解决方法。
另外,上传一个带括弧的文件怎么传,比如abc(def).jpg文件,搞了半天最后只好放弃,改用下划线。


C:\Program Files\Google\google_appengine>bulkload_client.py --filename=myPlaceListUTF8.csv --kind=myPlaceList --url
=http://127.1:8080/upload --debug
INFO 2009-03-14 10:18:01,421 bulkload_client.py] Starting import; maximum 10
entities per post
INFO 2009-03-14 10:18:01,421 bulkload_client.py] Importing 10 entities in 38
18 bytes
DEBUG 2009-03-14 10:18:01,421 bulkload_client.py] Connecting to 127.1:8080
DEBUG 2009-03-14 10:18:01,421 bulkload_client.py] Posting 10219 bytes to http
://127.1:8080/upload
DEBUG 2009-03-14 10:18:01,437 bulkload_client.py] Encountered exception acces
sing HTTP server: (11001, 'getaddrinfo failed')
ERROR 2009-03-14 10:18:01,437 bulkload_client.py] An error occurred while imp
orting: (11001, 'getaddrinfo failed')
ERROR 2009-03-14 10:18:01,453 bulkload_client.py] Import failed
 回复 引用 查看   

#9楼 2009-03-14 16:00 Rover.Tang      

经查,所有appspot.com域名好像无法访问,至少我现在的情况是。但我用美国代理可以访问。不会这么快就被你说中了吧,把appspot.com封掉,那我们可真要疯掉的。  回复 引用 查看   

#10楼[楼主] 2009-03-14 20:57 K_Reverter      

@Rover.Tang
确实是如此,唉,这样也没有办法啊,看来Appspot真的要全挂了
 回复 引用 查看   

#11楼[楼主] 2009-03-14 20:59 K_Reverter      

@Rover.Tang
127.1:8080是一个怎样的地址啊?有错吧
 回复 引用 查看   

#12楼[楼主] 2009-03-14 21:02 K_Reverter      

现在通过在hosts里面指定209.85.171.118能够临时访问*.appspot.com  回复 引用 查看   

#13楼 2009-03-15 14:51 Rover.Tang      

@K_Reverter
127.1:8080是我本机地址,我在本机调试的,以前的时候我上传就是成功的,没有任何问题的,然后是同样的代码同样的数据,上传出错。具体原因是我清理了C盘一些垃圾文件,不知道删掉了什么东西,但我重新安装python和GAE SDK都没有成功,所以非常的郁闷,到现在还没有解决。照你的新方法传到本地,也是不成功。但是我用旧的方法传到GAE,是成功的,同样的代码同样的数据,说明我的Windows环境有问题,但不知道具体是因为删除了什么文件。不会让我重装系统吧,我都一年多没有重装系统了。
 回复 引用 查看   

#14楼 2009-03-15 14:54 Rover.Tang      

另外,今天appspot.com已经可以访问了。够郁闷吧。

赶紧写下篇吧,我在等着呢,最近也准备做个新东西要传大量数据呢。呵呵
 回复 引用 查看   

#15楼 2009-03-16 10:06 Rover.Tang      

不过,确实被你言中了,用127.1不行,最后准备放弃的时候,尝试了一下127.0.0.1,结果可用,晕死。

另外,昨天晚上我已经测试了你的代码了,完全可用,感觉速度要比以前的快,而且默认十线程比你的单线程要快。感觉这个非常的实用,不过你好像没有说清楚上传到本地开发环境,我看我要不要补充一下啊,因为我上传就是传到本地开发环境,GAE是本地调试好了以后再传。呵呵
 回复 引用 查看   

#16楼 2009-03-16 10:11 Rover.Tang      

再补充一点,以前用客户端服务端方式上传unicode的时候,不能查看数据库中的内容,是因为中文字的关系。
而用这个新的方法上传后,是可以浏览数据了,没有错误,并且修改也没有问题,可见此方法比上次的要好很多。
 回复 引用 查看   

#17楼[楼主] 2009-03-16 13:16 K_Reverter      

@Rover.Tang
对,我以前按照网上有个范例的做法,上传上去的中文数据在后台无法查看,我后来重新研究过,我现在的方法是可以的
 回复 引用 查看   

#18楼 2009-03-31 20:04 秦锋[未注册用户]

其实不用修改bulkloader.py,代码
properties[name] = converter(val)
的意思是用你自己的converter来转换字段的值,所以你只要在自己的loader里面这样写就可以了:

Loader.__init__(self, 'Record',
[('date', lambda x: datetime.datetime.strptime(x, "%Y-%m-%d")),
('dfmt', str),
('value', float),
('unit', lambda x: unicode(x, "utf-8")),
('tags', lambda x: unicode(x, "utf-8").split(' ')),
('source', str)
])

如上面的“unit”,进行一次Unicode的转换即相当于:
properties[name] = unicode(val, 'utf-8')
 回复 引用   

#19楼 2009-04-05 13:02 Rover.Tang      

@秦锋
测试通过,好用。
 回复 引用 查看   

#20楼 2009-06-30 20:17 墨尔本[未注册用户]

太有用了~
非常感谢这样的好文章

http://melbourne.at9t.com
 回复 引用