Google App Engine tips&tricks
A while ago I was writing some sample applications (source) for Google App Engine. I noted the things that can be useful for other GAE programmers.
I used Google's webapp framework, my code here is using it.
Please take a look at the shell application, it can help you test simple code.
How to dynamically get application name and version?
This question was asked before. You can use os.getcwd() or os.environ['PATH_TRANSLATED'].>>> os.getcwd()
'/base/data/home/apps/shell/1.21'
>>> os.getcwd().split('/')[-2]
'shell'
>>> os.getcwd().split('/')[-1]
'1.21'
>>> os.environ['PATH_TRANSLATED']
'/base/data/home/apps/shell/1.21/shell.py'
>>> os.environ['PATH_TRANSLATED'].split('/')[-3]
'shell'
How to identify current host?
There's a very interesting file that should be unique for every server:>>> open('/base/python_dist/search.config').read()You can identify the machine on which the process is deployed by using hash based on this file. Something like that:
'datapath .\nsorttempdir .\ndisk /export/hdc3/borgletdata/dirs/prod-appengine.\
mpm_python_dist_v12.apphosting.77627982/bigfiledata/466024'
>>> open('/base/python_dist/search.config').read()
'datapath .\nsorttempdir .\ndisk /export/hdc3/borgletdata/dirs/prod-appengine.\
mpm_python_dist_v12.apphosting.77627739/bigfiledata/465336'
def get_server_id():Google doesn't inform you on how many machines your application is going to be deployed (this probably depends on the traffic your site generates). But you can add this server_id to your site footer. Than you can do multiple wget's to know on how many unique machines your app is being deployed.
try:
fd = open('/base/python_dist/search.config')
data = fd.read()
fd.close()
except IOError:
return 'unknown'
return '%s' % data.__hash__()
$ for i in `seq 20`; doIt seems that my app is deployed on only one server.
curl -s http://cometchat.appspot.com|\
grep server_id; \
done |sort -n|uniq -c
20 server_id: '7341146770217830363'
How to identify current process?
Yet again, how many processes with your app are deployed? This time a trick with global variable:the_process_global = "something"Now I know that my application is deployed using two processes:
def get_process_id():
return '%s' % id(the_process_global)
$ for i in `seq 20`; do
curl -s http://cometchat.appspot.com|\
grep _id;
done |sort -n|uniq -c
13 process_id: '12457625149327067176'
7 process_id: '3996238433791648184'
Are we on production or development server?
I use this snippet:if os.environ.get('SERVER_SOFTWARE','').startswith('Devel'):
HOST='local'
elif os.environ.get('SERVER_SOFTWARE','').startswith('Goog'):
HOST='google'
else:
# logging.error('Unknown server. Production/development?')
HOST='unknown'
Captcha on GAE?
Joscha Feth wrote tutorial about using reCaptcha on GAE.Cookies?
Google suggests that request and response objects follow the WebOb interfaces. This works for getting cookies from request:username = self.request.cookies.get('username', '')Unfortunately you can't use WebOb method response.set_cookie. But you can set cookies by hand:
self.response.headers.add_header(You can find some other hints on google-app-engine discussions. I don't know if cookies work from django-helper.
'Set-Cookie',
'username=%s; expires=Fri, 31-Dec-2020 23:59:59 GMT' \
% username.encode())
Debugging datastore access
I created very simple datastore debugger. It appends some debugging info to the footer of generated page. To use it you must just change your classes to inherit from debug.DebugMiddleware instead of webapp.RequestHandler.For example:
class List(debug.DebugMiddleware):Sample footer can look like that:
def get(self):
... blabla ...
**** Request took: 830ms/170ms (real time/cpu time)This GQL log was caused by the code:
**** GQLs, datastore accessed 1 times.
98ms GQL app: ":self"
kind: "Image"
Order {
property: "modified"
direction: 2
}
args: (50,) {}
ims = Image.all().order("-modified").fetch(50)Yet another example of output footer:
**** Request took: 150ms/130ms (real time/cpu time)This datastore debugger can be easily modified to be used as Django middleware.
**** GQLs, datastore accessed xx times.
219ms PUT ({'full':...
178ms PUT ({'full':...
6ms GET ([datastore_types.Key.from_path('Image', 350L, _app=u'srv')],) {}
2ms GET ([datastore_types.Key.from_path('Image', 349L, _app=u'srv')],) {}
2ms GET ([datastore_types.Key.from_path('Image', 348L, _app=u'srv')],) {}
Dynamic images uploading
This is the code I use. The template:<form action="." method="post" enctype="multipart/form-data">Server side:
<label>File: </label><input name="file" type="file"><br />
<input type="submit">
</form>
class Image(db.Model):
name = db.StringProperty()
content = db.BlobProperty()
class UploadImage(webapp.RequestHandler):
def post(self):
if 'file' not in self.request.POST:
self.error(400)
self.response.out.write("file not specified!")
return
if (self.request.POST.get('file', None) is None or
not self.request.POST.get('file', None).filename):
self.error(400)
self.response.out.write("file not specified!")
return
file_data = self.request.POST.get('file').file.read()
file_name = self.request.POST.get('file').filename
im = Image()
im.name = file_name
im.content = file_data
im.save()
self.response.out.write("image %r saved." % im.name)
How to get image size and type
Tj9991 found an implementation of function getImageInfo that can extract image size without any external libraries. The usage is straightforward:content_type, width, height = getImageInfo(im.content)
Dynamic images serving
There's an article about this topic in the official docs. Here's my non-optimal code:class ServeImage(webapp.RequestHandler):
def get(self, key):
im = db.get(db.Key(key))
if not im:
self.error(404)
return
content_type, width, height = getImageInfo(im.content)
self.response.headers.add_header("Expires", "Thu, 01 Dec 2014 16:00:00 GMT")
self.response.headers["Content-Type"] = content_type
self.response.out.write(im.content)
Image resizing
Google doesn't support image converting libraries like PiL. You have to convert images using some external services. You need to upload your data somewhere far from GAE and then somehow get the resized image. Especially for this I created a service (which is not the-most-stable way unfortunately). You can try other people methods as well.Is comet/http-push/long polling supported by GAE?
No, but keep reading. You could try to do normal polling. For example by loading ajax data every second. But the GAE resources are limited, there are only 650k requests/day available. This limit is going to be reached with only 8 constant users for 24 hours (using ajax polling every second). I created external service that allow you to use comet techniques from GAE.You can also take a look at my some sample applications that use my external services (source).

3 comments:
BTW, there's a ticket about the lack of a WebOb Response in issue 200
Thanks Majek! Very helpful.
Do you know anything about using SSL on App Engine?
SSL on Google App Engine?
Good question!
Post a Comment