3. Google App Engine (GAE)

‧簡介:
Google App Engine是一個存放網頁應用程式的平台,由Google資料中心管理。在2008年4月釋出Beta版支援Python語言,並在2009年4月推出第二個支援JAVA語言的版本。
Google所引領的雲端運算,這個話題近年來持續發燒。而Google App Engine正是Google想用打入雲端運算市場的產品之一。
   
1.屬於web application開發環境。
2.允許用Python及Java程式語言來開發。
3.用Google的Bigtable資料儲存系統。
4.以及Google的GFS來處理資料。
5.Google會幫你hosting這些web application。 

*Bigtable:寫網站程式幾乎都會用database,不同於傳統常見的Relational Database。是一種分散式的資料庫架構,能在資料數量龐大的情況下,都能正常操作。
*GFS:Google File System是Google自行研製的分散式檔案系統,是一套能快速穩定及安全的資料儲存系統。


‧目的:
GAE首重的就是使用簡單,讓使用者專心讓設計應用程式的部份,資料庫、作業系統、網路資源的部份交給Google操心。GAE首先支援的Python也是一個標榜簡單易學的Scripting language。
GAE另一個目標就是延展性(Scalability)。GAE有信心在使用者擁有百萬、千萬筆資料的情況下都能正常的運作。


‧特色:
1.GAE嘗試去幫助掌管應用程式的生命週期,例如:Dashboard,Log記綠,版本管理,資料庫等等。
2.應用程式是執行在Google 的infrastructure,在Google File System的架構下,能快速、可靠地運作。
3.在有限的配額下完全免費。
4.可以免費使用多種Google API。
5.支援 Python , JAVA 兩種程式語言開發。

-Datastore:
因為GAE使用的是BigTable架構,使用者沒有辦法直接存取資料庫。GAE提供資料庫的操作API供使用者存取資料。
另外,GAE的Datastore是一種Schema-less Style,也就是使用者並不需要特別去設計資料庫的Schema。而直接用程式語言的類別去設計資料庫。GAE為常見的資料提供不同的種類,例如字串,使用者,日期等等。因為是使用BigTable的架構,資料很可能是分佈在不同的電腦之上。所以在執行join指令的時侯會耗費很多資源。所以GAE不支援join指令。所以在設計Datastore時要特別注意。

-免費的額度:
主要是希望讓使用者不會耗盡整個網路資源。不會因為某些過大的流量而影響正常的使用者。


‧比較Google App Engine And Google Apps:

Google App Engine (GAE)跟Google App是不同的,不要搞錯囉!

GAE是提供一個平台,有註冊的使用者可以建立web application然後Google會幫你hosting,可以免費註冊及使用,但是有些限制在,十個registered application、1GB的disk storage、每天10GB的頻寬、每天 46 CPU-hours、每天兩千Emails以及一個query最多回應1000個結果,使用者也不需要去維持維護server只需上載application至GAE。

Google Apps 服務範圍十分的廣,包含了GMail信箱、GTalk、個人化首頁、文件、Google Calender...等。申請時可以分一些版本來申請,有標準版(適合個人)、專業版(適合企業需付費)、教育版(適合各學校)。這麼方便的服務當然也是有小缺點,例如是公司有機密性的聯絡事宜。


參考資料:課程投影片 &http://s9011514.googlepages.com/apply_google_apps.htm

‧Google App Engine 的應用程式環境:

使用GAE可以讓你在資料量極大的情況下,可以輕鬆建構安全的應用程式

-其環境有以下特性:

   1:動態網路服務,支援全部的網頁技術

   2:不間斷的查詢、排序以及交易

   3:自動擴展以及負載平衡

   4:功能完整的本機開發環境,直接在使用者電腦上模擬GAE

   5:使用API對用戶進行身份驗證和使用Google帳戶發送e-mail

   6:支援Cron,可進行排程工作,在指定時間與固定時段間隔啟動活動

 
-Sandbox:

在安全的環境下進行應用程式,提供一些基本限制權限,這些限制使App Engine可在多個server之間分享應用程式,並可以使用啟用或停止來滿足使用需求,Sandbox將應用程式放至安全的可靠環境中。

有這種Sandbox安全性的執行環境,可以確保當程式發生問題時,問題只會發生在安全的環境範圍內,而不會影響到該環境的網路server的硬體、操作系統等等。


-相對的也有缺點,安全的Sandbox環境的限制包含:

   1:應用程式必須透過提供的URL fetch及E-mail Services瀏覽其他的電腦。而其他的電腦只能透過HTTP或HTTPS的請求來連接應用程式。

   2:應用程式無法寫入檔案系統,只能讀通過應用程式編碼的檔案,而該應用程式必須使用App Engine資料庫區域、memcache或其他服務。

   3:應用程式只能在有網路請求或Cron任務時運行,且無論任何狀況都必須30秒之內得到回應。而一個請求無法在回應之後才產生sub-process或是執行code.

 *Memcache:提供應用程式的多項instances作高效能內部記憶體的key-value擷取,適用於不需要持續保存於資料庫或交易功能的資料。

‧Java執行環境:

使用者可以透過通用Java的網路開發工具和API標準,在Java執行環境中開發,使GAE透過安全的Sandbox環境中,使用Java 6 JVM來執行使用者的web application。


-目前支援版本:

The Java runtime Environment(執行環境):Java 6,含Java SE Runtime Environment(JRE) 6

App Engine Java SDK(應用服務引擎):Java 5 和 Java 6

 
-使用規範:

1.在不超出Sandbox的限制,可使用所有JVM位元組碼或程式庫功能。

2.可透過JVM相容的編譯器或直譯器來使用其他語言開發web application,如JavaScript, Ruby或Scala。


-開發平台:
Google所提供的Google Plugin for Eclipse使的Eclipse這個開發平台的使用者能夠輕易的熟悉GAE的使用方法,而跳過長時間的熟悉訓練。
當一個開發者開創新的網路應用程式時,Eclipse便會替使用者開創出GAE所要求的資料構造以及一些基本檔案,其中包括appengine-web.xml以及web.xml。
web.xml是用來標明應用程式的資源是如何來滿足使用者的各項需求,每當使用者像web server發出一項request時,都需要透過web.xml來把request map到能夠解節這項request的某個code,而appengine-web.xml則是標明一些網路應用程式的基本屬性,像是應用程式的ID,版本以及所用到的各項資源列表。


-資料存取:
GAE Java 使用了Java Data Object (JDO) 以及Java Persistence API (JPA)來提供開發者穩固並且scalable的資料介面,GAE的datastore的開發理念除了針對網路應用之外,同時也著重在read還有query之上,所有的query在大量資料量下都有先被index起來。


-備妥開發環境:
1.取得Eclipse:
可從http://www.eclipse.org/取得 Eclipse。「Java EE 開發人員專用的 Eclipse IDE」包含所有開發網路應用程式所需的元件。

2.安裝 Eclipse 專用的 Google 外掛程式:
可以使用 Eclipse 的 Software Update (軟體更新) 功能,安裝「Eclipse 專用的 Google 外掛程式」。
1.選取 [Help] > [Install New Software]。
2.按一下 [Add]按鈕,即可開啟 [Add Site]視窗。然後,在 [Location] (位置) 輸入 Eclipse 3.5 版本外掛程式的安裝位置: http://dl.google.com/eclipse/plugin/3.5按一下 [OK]。對話方塊隨即關閉,而新的位置會加到可用軟體的清單中。
3.按一下新的安裝位置旁邊的三角形,然後按一下 [Google] 旁邊的三角形,即可顯示安裝選項。勾選 [Google Plugin for Eclipse 3.5] 以及 [Google App Engine Java SDK] 。如要在應用程式使用 Google Web Toolkit,您也可以選取 [Google Web Toolkit SDK]。按一下 [Install...] 按鈕。按照提示接受服務條款,並安裝外掛程式。
4.安裝完畢之後,Eclipse 會提示您重新啟動。按一下 [Yes]。Eclipse 會重新啟動。外掛程式完成安裝。


-HelloWorld專案 GAE/J的初體驗:
1.選取 [File] 功能表 > [New] > [Web Application Project] (如果您找不到這個功能表選項,請選取 [Window] > [Reset Perspective...] ,再按一下 [OK] ,然後重試 [File] )。或者,按一下工具列中的 [New Web Application Project)。
2.即可開啟 [New Web Application Project] 精靈。在 [Project name] 輸入專案的名稱,在 [Package] 輸入適當的套件名稱。
3.如果您使用的不是 Google Web Toolkit,請取消勾選 [Use Google Web Toolkit]。確定您已勾選 [Use Google App Engine]。
4.如果您是透過「軟體更新」安裝「應用服務引擎 SDK」, 這個外掛程式已經過設定‧將使用安裝的SDK。如果您想使用個別安裝的「應用服務引擎SDK」,請按一下 [Configure SDKs...],然後按照提示新增含 appengine-java-sdk/ 目錄的設定。
5.按一下 [Finish] ,以建立專案。

import java.io.IOException;
import javax.servlet.http.*;

@SuppressWarnings("serial")
public class HelloServlet extends HttpServlet {
 public void doGet(HttpServletRequest req, HttpServletResponse resp)
   throws IOException {
  resp.setContentType("text/plain");
  resp.getWriter().println("Hello, world");
 }
}


-執行專案:
請選取 [Run] 功能表的 [Run As](除錯方式) > [Web Application]。
如果伺服器啟動成功,將會看到The server is running at http://localhost:8080/

 

‧Python執行環境:

透過GAE的Python執行環境,使用者可以使用Python程式設計實做個人應用程式,並在GAE的安全Sandbox環境中,使用預先載入的最佳化Python直譯器上執行應用程式。GAE支持適用於Python網路應用程式開發的API和工具,以及各種管理和存取應用程式資料的工具,並可用各種完善的程式庫與架構來開發。

-目前支援版本:

Python 2.5.2(官方資料,2.6.5可執行)

-使用規範:

1.必須在不超出Sandbox的限制下使用程式庫功能,故必須停用不支援此環境下的模組功能,否則在匯入時就會出錯。

2.Python環境設計下的應用程式碼必須全部使用Python編寫,故不支援由C語言編寫的擴充套件。

3.可隨著應用程式上傳第三方程式庫,但該程式庫必須為純Python實作和沒有任何不支援的標準程式庫模組。

 

How to start using GAE with Hello World example on windows XP(Python)

My Example of the application please look here
Now, let's get started...

  1. Register in http://appengine.google.com/ (follow the step, including activation)
  2. Download and run python 2.5.4 from http://www.python.org/download/releases/2.5.4/
  3. Download and run google SDK from http://code.google.com/appengine/downloads.html
  4. Create a new folder named helloworld in Google App Engine
  5. Create a helloworld.py file, with the code:

    print 'Content-Type: text/plain'
    print ''
    print 'hello, world'
  6. Create a app.yaml file with the code:


    application: helloworld
    version: 1
    runtime: python
    api_version: 1

    handlers:
    - url: /.*
    script: helloworld.py


  7. Open command prompt, go to google_appengine/dev_appserver.py helloworld/
  8. Run http://localhost:8080/ on the web browser
  9. To close the command prompt, press Ctrl-c or just close it
  10. Go to http://appengine.google.com/ again, and click on create an application button (Remember your application identifier name)
  11. Edit the app.yaml file, and change the value of application: to your application identifier name (originally helloworld)
  12. To upload, run the command google_appengine/appcfg.py update helloworld/ in command prompt
  13. To check, go to http://application identifier name.appspot.com
  14. To learn more. go to http://code.google.com/appengine/docs/python/gettingstarted/helloworld.html

 

.Datastore

其實 GAE 也是遵從 MVC 的架構來開發,加上有豐富的說明文件開發上並不困難,比較難以適應的就是 Datastore 的部份,因為它不是關聯式資料庫,所以一開始資料庫的規劃上就要有不同的規劃方式,像是沒有關聯鍵這種東西,查詢語法無法 join 、查詢結果無法 count 、無法大量刪除資料等等…,這邊只筆記使用方式,其他關於 GAE Datastore 的介紹很多了,就不贅述

1
2
3
4
5
6
7
8
9
from google.appengine.ext import db
 
class Pet(db.Model):
    name = db.StringProperty(required=True)
    type = db.StringProperty(required=True, choices=set(["cat", "dog", "bird"]))
    birthdate = db.DateProperty()
    weight_in_pounds = db.IntegerProperty()
    spayed_or_neutered = db.BooleanProperty()
    owner = db.UserProperty(required=True)

和一般資料庫的使用不同,不是先建立 Table ,而是先將 Model 建立出來,當第一筆資料輸入資料庫後 Table 就會自動產生了。上面的是官方文件的範例,比較要注意的是資料型態的部份,可以先查一下有哪些可以用的資料型態,有一些比較特別的資料型態其實很好用,像是 GeoPtProperty 表示地理位置等等…
Model 定義好之後,當需要建立資料時只要產生相對的實體就可以指定內容,如果定義時有加上 required=True 的參數則為必填的欄位,要在建立實體時就輸入。建立完成後使用 put() function 就會輸入到資料庫中了。

1
2
3
4
5
6
7
from google.appengine.api import users
 
pet = Pet(name="Fluffy",
          type="cat",
          owner=users.get_current_user())
pet.weight_in_pounds = 24
pet.put()

比較重要的是可以利用 ReferenceProperty 建立 One-to-One 的關係,下面範例假設一個人可以有很多隻寵物:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Owner(db.Model):
    name = db.StringProperty(required=True)
 
class Pet(db.Model):
    name = db.StringProperty(required=True)
    owner = db.ReferenceProperty(Owner, collection_name='pets')
 
jeffean = Owner(name='Jeff Tsai')
jeffean.put()
 
pet1 = Pet(name="Jimmy", owner=jeffean)
pet1.put()
 
pet2 = Pet(name="Anfa", owner=jeffean)
pet2.put()
 
# 列出某個人所有的寵物
for pet in jeffean.pets:
    print 'name: %s' % pet.name

另外可以利用 ListProperty 來做 Many-to-Many 的關係,下面是用文章和標籤的關係當範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Post(db.Model):
    title = db.StringProperty(required=True)
    tags = db.ListProperty(db.Key)
 
class Tag(db.Model):
    name = db.StringProperty(required=True)
    
    @property
    def posts(self):
        return Post.gql('WHERE tags = :1', self.key())
 
# 建立 entity
tag1 = Tag(name='food')
tag1.put()
tag2 = Tag(name='travel')
tag2.put()
 
post1 = Post(title='Blah')
post1.tags.append(tag1.key())
post1.tags.append(tag2.key())
post1.put()
 
# 從 Post 取得它所有標籤:
tags = db.get(post1.tags)
for tag in tags:
    print 'Tag: %s' % tag.name
 
# 查看某個標籤中有什麼文章:
for post in tag1.posts:
    print 'Title: %s' % post.title

雖然上面有用到查詢,但是還是寫一下查詢的說明。下面的範例展示如何對資料庫做查詢以及放入篩選條件,比較特別的是刪除資料也得像這樣做,無法像以往下 SQL 的 DETET 語法可以做大量刪除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Song(db.Model):
  title = db.StringProperty()
  composer = db.StringProperty()
  date = db.DateTimeProperty()
 
query = db.Query(Song)
query = Song.all() # 與上一行意思其實相同
query = GqlQuery("SELECT * FROM Song WHERE composer = 'Lennon, John'") # 也可以這樣
query = Song.gql("WHERE composer = 'Lennon, John'") # 這樣也可以
query.filter('title =', 'Imagine')
query.order('-date')
query.ancestor(key)
results = query.fetch(limit=5)
for song in results:
  print song.title
   
for song in query: # 和上一個迴圈相同意思但是沒限制數量
  print song.title
  song.delete()

另外每筆資料都有自己獨特的 key ,但是無法排序,雖然也會自動有 id 的欄位但是數字也不是壘加的,可以用來做選擇單一筆資料時的查詢依據,但要取得最新的 N 筆資料來排序,我自己的作法是用一個 modify = db.DateTimeProperty(auto_now = True) 的欄位,這樣輸入資料會自動記下現在的時間,方便我要取得最新資料時的篩選條件,或許有更好的方法但是我目前是這樣做啦。
最後來筆記一下取得 key 和 id 的方式:

1
2
3
4
query = Song.all()
for song in query:
  print song.key()
  print song.key().id()

 

 

.Template 系統
因為一個站內的網頁常常有很多重複的部份,像是 footer 或是選單,沒有 tempalte 每一頁都重複寫的話,一來麻煩二來要改的時候更麻煩!而 App Engine 支援的 template 系統有很多種,而 gae 預設的 webapp 架構中本來就包含了 django 的 template 系統了,官方的說明文件也是以此為範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import os
from google.appengine.ext.webapp import template
 
class MainPage(webapp.RequestHandler):
  def get(self):
    greetings_query = Greeting.all().order('-date')
    greetings = greetings_query.fetch(10)
 
    if users.get_current_user():
      url = users.create_logout_url(self.request.uri)
      url_linktext = 'Logout'
    else:
      url = users.create_login_url(self.request.uri)
      url_linktext = 'Login'
 
    template_values = {
      'greetings': greetings,
      'url': url,
      'url_linktext': url_linktext,
      }
 
    path = os.path.join(os.path.dirname(__file__), 'index.html')
    self.response.out.write(template.render(path, template_values))

後端程式碼的重點就是把資料裝入 dictionary 的資料結構裡面,並且用 template.render() 這個 function 把整個資料結構送給 template 的檔案就可以了,而不是直接用 response.out.write() 寫出。
按照以上範例,則要另外產生一個 index.html 的 template 檔案。基本上 template 也是一個 html 檔,但是在裡面可以用 {{ var }} 的方式將後方傳送來的變數給印出,也可以配合 django template 的 loop 和 filter 使用。和 PHP 不一樣的是,這裡插入的不是 Python 的程式碼,而是 django template 規範出來的語法,可以參考 django 的官方文件。另外 template 中可以自行定義出 block ,並且在其他的樣板檔案中繼承以及修改:
base.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
  <body>
    {% for greeting in greetings %}
      {% if greeting.author %}
        <b>{{ greeting.author.nickname }}</b> wrote:
      {% else %}
       An anonymous person wrote:
      {% endif %}
      <blockquote>{{ greeting.content|escape }}</blockquote>
    {% endfor %}
 
    <form action="/sign" method="post">