- google.appengine.api.labs.taskqueue开始支持计划任务列队
http://code.google.com/appengine/docs/python/taskqueue- 开始支持Django 1.0,需要在本地环境中安装django但不需要上传到服务器
http://code.google.com/p/googleappengine/issues/detail?id=872- Urlfetch开始支持异步请求
http://code.google.com/p/googleappengine/issues/detail?id=958- Urlfetch在开发套件中更接近于在App Engine的服务器中:默认中会设置referer header,而并不设置Accept header,而且会设置gzip.Urlfetch的Accept-Encoding
http://code.google.com/p/googleappengine/issues/detail?id=970- Fixed issue with httplib and absolute URLs.修复httplib和绝对路径的问题
http://code.google.com/p/googleappengine/issues/detail?id=1311- Memcache的key长度不再限制为250字节:更长的keys将会被key的hash取代
- transactions中支持使用Datastore的ancestor进行查询
- 在开发套件中会对第一个operation进行快照,所以不会显示在transaction的所有写入。和在服务器中一样。
- 程序版本号并不是number类型而是string
尽管在多数的例子中都将app.yaml和appengine-web.xml中的'version'字段作为number类型,实际上这只是人们的一种习惯。程序版本可以是任何URL中允许的string类型。例如,你可以将你的版本设置成"live"和"dev",这样你就可以通过"live.latest.yourapp.appspot.com"和"dev.latest.yourapp.appspot.com"这样的地址来访问了。 - 你可以在程序中同时使用多个版本
这里有一点是很NB的,不同的版本可以使用不同的运行环境。换言之,你可以为你的程序开发使用Java语言的版本和Python语言的版本。 - Java运行时支持任何编译成Java bytecode的语言
这就是为什么人们需要Java runtime的原因,一旦这家伙被支持了就等于你可以在GAE上使用什么JRuby,Groovy,Scala,Rhino(JavaScript编译器),Quercus(PHP编译器),甚至Jython! - 使用'IN'和'!='运算符可以生成多重数据查询
例如,查询"SELECT * FROM People WHERE name IN ('Bob', 'Jane')"将会被编译成两个查询等价于使用"SELECT * FROM People WHERE name = 'Bob'"和"SELECT * FROM People WHERE name = 'Jane'"并合并查询的结果。
查询"SELECT * FROM People WHERE name IN ('Bob', 'Jane') AND age != 25"将会为每种可能生成生成查询,(age小于或者等于25, 和name是否为'Bob'或者'Jane'),然后将每种可能合并成结果。 - 你可以使用批处理执行put,get和delete操作来提高效率。
每当你做出一个数据请求,比如一个查询或者get()操作,你的程序将会对数据库发出请求,而数据库将会执行这些操作并返还一个response。请求到响应的循环过程将会花费一定时间,所以如果你执行的请求过多的话将会导致用户等待结果的时间过长(万恶的datastore timeout: operation took too long异常)。
很幸运的是,GAE提供一个简单的方法来减少数据查询中的这种循环:批处理操作。db.put(),db.get(),和db.delete()函数都可以接受可选的list参数来执行操作。当一个传递一个list以后这些操作将会并行的遍历list的每一个元素,这样就大大节省了操作的时间。例如:for entity in MyModel.all().filter("color =", old_favorite).fetch(100): entity.color = new_favorite entity.put()更新操作需要一个数据库往返的查询,还需要加上一次操作来更新数据库中实体的属性————也就是说一共需要101次的往返!再看下面的例子:updated = [] for entity in MyModel.all().filter("color =", old_favorite).fetch(100): entity.color = new_favorite updated.append(entity) db.put(updated)这个例子中只需要2次数据库往返,也就是说我们将101次往返减少到了2次。 - 数据库的性能并不取决于其中实体的数量
- 建立索引的时间开销并不全部取决于它的大小
- 'Stored Data'的值是按日更新的
- app.yaml,web.xml和appengine-web.xml中指明了项目的处理顺序
人们经常犯的一个错误是忘记配置程序处理文件的顺序是自上而下的。比如,当使用remote_api时候很多人这样做:handlers: - url: /.* script: request.py - url: /remote_api script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py login: admin
上面的例子看起来没有什么问题,但是由于handler是按照顺序处理的,request.py是首先出现的,而所有请求包括对remote_api的请求都是使用request.py处理的。由于request.py加载后并没有出现remote_api,所以就会造成a 404 Not Found error的错误。解决的方法是将request.py的handler放置到remote_api的handler下面。在Java环境的配置文件中也是一样。 - 你不需要手动构造GQL字符
下面是一个反例:q = db.GqlQuery("SELECT * FROM People " "WHERE first_name = '" + first_name + "' AND last_name = '" + last_name + "'")很幸运,GAE支持在GqlQuery语句中使用参数替换,上面例子可以改写成:q = db.GqlQuery("SELECT * FROM People " "WHERE first_name = :1 " "AND last_name = :2", first_name, last_name)GqlQuery还支持使用name来代替数字参数:q = db.GqlQuery("SELECT * FROM People " "WHERE first_name = :first_name " "AND last_name = :last_name", first_name=first_name, last_name=last_name)这样做出了能使代码看起来更加简洁以外,还可以进行一些优化。如果你需要使用不同的值来进行多次同样的查询,你可以使用bind()函数来为每个查询重新绑定参数。这比起每次都要构造新的查询更加快捷,因为查询只需要分析一次:q = db.GqlQuery("SELECT * FROM People " "WHERE first_name = :first_name " "AND last_name = :last_name") for first, last in people: q.bind(first, last) person = q.get() print person
作者Nick Johnson, App Engine Team[via]
某一天你发现饭否不能登录了、豆瓣不能更改签名了、VeryCD的首页上出现了显著的维护公告,而在是与此同时,某些人紧张了,所以twitter、blogger、youtube、flickr不能访问了。这里仅用只言片语摘录我所看到的维护:
为所有的死者感到的愧疚
新闻早报
每年这个时候,我们莫名而生的纪念更应该献给那个风华绝代的群体,而不是少数几个人。或许有人会说那件事不过是进一退二,可那一代人再也不会出现,除了爱国热情之外,他们那个时候一无所有。现在我们好像什么都有,可是,就是不再关心我们的社会和国家。 二十年后,依然只是无力的力量。 Fenng@dbanotes.net
平日报道官方新闻的门户网站, 今日顺从民意推动了部分事件真实情况的传播. eDonkey Network 和 BitTorrent 平日是版权的破坏者, 今日是资讯通畅的保证. 各类论坛, 博客, 维基与社会化网络, 平日运作方式虽各有不同, 但今日都承担了一份解除信息隔离的责任. fcicq’s blog-beta
die ("说一句谎话,要编造十句谎话来弥补,何苦呢?");
BigCat's Paw by bigCat
防民之口,甚于防川,川壅而溃,伤人必多,民亦如之。
国语·周语上
googlecode已经正式开始支持Mercurial了。我在googlecode上新建了一个wow-dkp-engine的Mercurial Repository。实际上同时使用Mercurial和Git并不冲突,两者保存版本的数据文件都相对独立,这比起同时使用SVN要简单了不少。
另外,Github也开始支持Mercurial了,在本地使用Mercurial并想要使用Github作为Web端的用户可以通过使用他们提供的hg-git来实现。
终于还是想起来以前用过修改hosts文件来访问gae的方法,209.85.171.118这个ip居然还能用,这招还是看到有人介绍twitter穿墙的方法想起来的。twitter墙就墙了吧,blogger、flickr、youtube、bing都墙twitter不墙实在是说不过去,下一个会被墙的是哪?非死不可?
Wolfram|Alpha发布了,用开发者的话来说它并非一个传统意义上的搜索引擎,而是一个计算知识引擎或者一个在线版的Mathematica,它返还的搜索结果是经过其服务器内部数据计算产生的,并非像Google那样是通过存储网页数据产生的。Wolfram|Alpha似乎更像是wikipedia这类百科全书样的程序,不过Wolfram|Alpha靠的是计算而不是像wikipedia是用户输入。
Wolfram|Alpha涵盖的范围看起来也很广泛各种领域的信息都有涉足。比方说搜索"weather"这个单词它会直接给你返还你当前IP的地理位置的天气情况和相关的天气信息。Wolfram|Alpha作为Mathematica的功能用起来也很强大。可以直接输入5*9+(sqrt 10)^3=这样的等式计算结果,还可以对方程式组进行求值等运算。Wolfram|Alpha会通过用户输入提出正确的公式,这一点用起来也很方便。
- 新的quota API将当前请求转向CPU使用
- Urlfetch开始支持自定义deadlines
- SDK中的Urlfetch允许设置在header中设置Encoding来匹配appengine
- urllib现在增添了对https支持
- 数据库的indexes现在可以通过设置在属性构造函数中设置indexed=False来禁用
- 数据库api支持Key-only查询,或者使用SELECT __key__ or db.Query(Model, keys_only=True)
- 修复IN关键字的筛选和排序问题
- 修复开发服务器控制台(/_ah/admin/datastore)当某属性是NoneType时不能显示的问题
- Bulk Loader改进:新的appcfg下载数据命令,更好的后台支持和debug长请求输出
- appcfg中新增参数--vhost会为特定的host选择记录
- 可以通过import导入Python的_ast模块
- 修复Images API颜色参数问题
另外最近还加入了web2py的支持,不过依然是不支持管理后台数据库等等,总之和django一样反正是你什么用的顺手就不支持什么。
先前我需要处理的问题是生成一个类似这个连接的表格,由于这个表格的各项数据是直接从数据库提取数据计算出来然后在提交给Google Visualization API,最后返还结果。这样做最大的弊端是如果数据比较多的情况下会引发一个datastore timeout: operation took too long的异常,于是我重写了这个程序把计算的结果变成一个数据库,这样做就避免了引发这个异常的问题。
使用正则表达式匹配中英文字符
这个问题也困扰了我很长时间。我需要从类似:
MiDKPData = {};
MiDKPData["dkp"] = {};
MiDKPData["dkp"][1] = {};
MiDKPData["dkp"][1]["name"] = "开心圈圈DKP";
MiDKPData["dkp"][1]["members"] = {};
MiDKPData["dkp"][1]["members"]["Donesee"] = 5;
MiDKPData["dkp"][1]["members"]["Latrogue"] = 0;
MiDKPData["dkp"][1]["members"]["Longwalker"] = 15;
MiDKPData["dkp"][1]["members"]["Lovelace"] = 56;
MiDKPData["dkp"][1]["members"]["Lucid"] = 232;
MiDKPData["dkp"][1]["members"]["Mitnick"] = 0;
MiDKPData["dkp"][1]["members"]["Neverlose"] = 33;
MiDKPData["dkp"][1]["members"]["Ooxxoo"] = 10;
MiDKPData["dkp"][1]["members"]["Shadowghost"] = 3;
MiDKPData["dkp"][1]["members"]["Steafinie"] = 14;
MiDKPData["dkp"][1]["members"]["一脸猥琐"] = 5;
MiDKPData["dkp"][1]["members"]["三聚氰胺"] = 31;
MiDKPData["dkp"][1]["members"]["人生真谛"] = 20;
MiDKPData["dkp"][1]["members"]["伟大的乌瑟尔"] = 5;
MiDKPData["dkp"][1]["members"]["光之留香"] = 73;
这样的数据中提取字符,这应该是Lua中数据类型。开始我考虑用处理字符串的方法提取,但经过测试效率很低而且容易出错。然后我尝试使用正则表达式匹配这些数据,使用正则表达式匹配的话需要对里面可能出现的中文进行匹配,该文件正常情况下应该是采用UTF-8编码。
def retext(data):
data = data.split('\n')
dict0 = {}
for li in data:
m = re.match(r"(\[\")(\w+|[\x80-\xff]+|[\u3400-\u9FFF]+)(\"\]\s\=\s)(\d+)(\;)",li)
try:
dict0[m.group(3)]=m.group(5)
except AttributeError:
pass
return dict0
retext()中的[\x80-\xff]+可以匹配任意长度的UTF-8编码的中文,而[\u3400-\u9FFF]+可以匹配任意长度的unicode编码中文。我在函数中加入了对unicode编码进行匹配的原因是我需要处理的这个字符串是通过request.POST.get()发送到服务器的。而这个request.POST.get()正是使用unicode编码。如果在数据库中查询这些数据则还需要使用decode('utf-8')将字符转化成UTF-8编码。
数据对象key_name属性
设置数据库字段的key_name属性:如果某些字符串和数据库的对象是一一对应的,而这些字符串刚好又符合设置成key_name的条件,那么把这些字符串设置成key_name属性会方便许多。