Google Protocol RPC 程式庫總覽

注意:如要進行 Google Protocol RPC 驗證,您可以透過 Google Cloud Platform 主控台使用 App Engine 應用程式目前可用的驗證方法。此外,您也需要在 app.yaml 檔案中指定使用登入要求。App Engine 內含的 Google Protocol RPC 程式庫目前不支援其他驗證方法。

Google Protocol RPC 程式庫是在實作以 HTTP 為基礎的遠端程序呼叫 (RPC) 服務時所使用的架構。RPC 服務是各種訊息類型和遠端方法的集合,可供外部應用程式透過結構化的方式與網路應用程式互動。由於您能夠使用 Python 程式語言定義訊息與服務,因此可以輕鬆開發 Protocol RPC 服務、測試服務,並在 App Engine 調整資源配置。

您可以將 Google Protocol RPC 程式庫用於以 HTTP 為基礎的各類型 RPC 服務,常見的使用情境包括:

  • 發佈網路 API 供第三方使用
  • 建立結構化的 Ajax 後端
  • 複製長期執行的伺服器通訊

您可以透過包含各種宣告遠端方法的單一 Python 類別來定義 Google Protocol RPC 服務。每個遠端方法都會接受一組特定參數作為要求,並傳回特定回應。這些要求與回應參數屬於使用者定義的類別,一般稱為訊息。

Google Protocol RPC 的 Hello World

本節提供的範例簡單地定義了一項接收遠端用戶端訊息的服務。此訊息包含使用者名稱 (HelloRequest.my_name) 並會傳回該使用者的問候語 (HelloResponse.hello):

from protorpc import messages
from protorpc import remote
from protorpc.wsgi import service

package = 'hello'

# Create the request string containing the user's name
class HelloRequest(messages.Message):
    my_name = messages.StringField(1, required=True)

# Create the response string
class HelloResponse(messages.Message):
    hello = messages.StringField(1, required=True)

# Create the RPC service to exchange messages
class HelloService(remote.Service):

    @remote.method(HelloRequest, HelloResponse)
    def hello(self, request):
        return HelloResponse(hello='Hello there, %s!' % request.my_name)

# Map the RPC service and path (/hello)
app = service.service_mappings([('/hello.*', HelloService)])

開始使用 Google Protocol RPC

本節說明如何透過 Google Protocol RPC 使用 App Engine 入門指南 (Python) 開發的留言板應用程式。使用者可以造訪線上留言板 (Python SDK 中也有這項示範)、撰寫留言,以及瀏覽所有使用者的留言。使用者可以直接與介面互動,但網路應用程式無法輕易存取該資訊。

此時就是 Protocol RPC 能派上用場的時候。在本教學課程中,我們會將 Google Protocol RPC 套用到這個基本留言板,讓其他網路應用程式能存取留言板資料。本教學課程僅說明如何使用 Google Protocol RPC 擴充留言板功能,後續要執行的操作需由您自行決定。舉例來說,您可以編寫相關工具,用來讀取使用者張貼的訊息,並且製作每日留言的時間序列圖表。Protocol RPC 的使用方式會因應用程式而異;重點在於 Google Protocol RPC 大幅擴展了您對應用程式資料所能執行的作業範圍。

為了開始使用,請先建立 postservice.py 檔案,用來實作遠端方法以存取留言板應用程式資料儲存庫中的資料。

建立 PostService 模組

如要開始使用 Google Protocol RPC,您必須先在應用程式目錄中建立名為 postservice.py 的檔案。您將使用這個檔案定義新的服務,而這個服務會實作兩個方法:一個是遠端張貼資料,另一個是遠端取得資料。

此時還不需要新增任何內容到這個檔案,但您在後續章節中定義的所有程式碼都會放到這個檔案。在下一節中,您將建立訊息,代表張貼到留言板應用程式資料儲存庫的記事。

處理訊息

訊息是 Google Protocol RPC 中所使用的基礎資料類型。訊息的定義方式是透過宣告自 Message 基礎類別所繼承的類別。接著,您需要指定對應至各個訊息欄位的類別屬性。

例如,留言板服務允許使用者張貼記事,如果您尚未在應用程式目錄中建立名為 postservice.py 的檔案,請執行此操作,並視情況查看留言板教學課程。在教學課程中,留言板問候語會使用 guestbook.Greeting 類別放入資料儲存庫中。PostService 也會使用 Greeting 類別在資料儲存庫中儲存留言。那麼代表這類記事的訊息即可定義為:

from protorpc import messages

class Note(messages.Message):

    text = messages.StringField(1, required=True)
    when = messages.IntegerField(2)

記事訊息由 textwhen 這兩個欄位定義。每個欄位都有特定的類型。文字欄位是 Unicode 字串,代表使用者張貼到留言板頁面的內容。when 欄位是代表留言時間戳記的整數。定義字串時,我們也會:

  • 為各個欄位提供唯一的數值 (text1when2),基本的網路通訊協定將使用這些數值來識別欄位。
  • text 設為必要欄位。這些欄位預設為選用欄位,設定 required=True 可將這些欄位標示為必要欄位。您必須為必要欄位設定數值,才能將訊息初始化。Google Protocol RPC 服務方法只接受已正確初始化的訊息。

您可以使用 Note 類別的建構函式為欄位設定數值:

# Import the standard time Python library to handle the timestamp.
import time

note_instance = Note(text=u'Hello guestbook!', when=int(time.time()))

您也可以讀取和設定訊息中的值,就像讀取和設定一般 Python 屬性值一樣。以下示範如何變更訊息:

print note_instance.text
note_instance.text = u'Good-bye guestbook!'
print note_instance.text
輸出內容如下:
Hello guestbook!
Good-bye guestbook!

定義服務

「服務」是繼承自 Service 基本類別的類別定義。服務的遠端方法是使用 remote 修飾符表示。服務的每個方法都會接受單一訊息做為參數,並傳回單一訊息做為回應。

以下說明如何定義 PostService 的第一個方法。將以下內容加到 postservice.py 檔案:

import datetime

from protorpc import message_types
from protorpc import remote

import guestbook

class PostService(remote.Service):

    # Add the remote decorator to indicate the service methods
    @remote.method(Note, message_types.VoidMessage)
    def post_note(self, request):

        # If the Note instance has a timestamp, use that timestamp
        if request.when is not None:
            when = datetime.datetime.utcfromtimestamp(request.when)

        # Else use the current time
        else:
            when = datetime.datetime.now()
        note = guestbook.Greeting(content=request.text, date=when, parent=guestbook.guestbook_key)
        note.put()
        return message_types.VoidMessage()

remote 修飾符使用兩個參數:

  • 預期的要求類型。post_note() 方法接受 Note 執行個體做為要求類型。
  • 預期的回應類型。Google Protocol RPC 程式庫隨附名為 VoidMessage (在 protorpc.message_types 模組中) 的內建類型,定義為沒有欄位的訊息。這表示 post_note() 訊息不會將任何實質內容傳回給呼叫者。如果傳回結果沒有錯誤,會將訊息視為已張貼。

由於 Note.when 是選用欄位,可能尚未由呼叫者進行設定。在此情況下,when 值會設為 None。當 Note.when 設為 None 時,post_note() 會使用接收訊息的時間建立時間戳記。

遠端方法會將回應訊息轉化為執行個體,並當做傳回值。

註冊服務

您可以使用 protorpc.wsgi.service 程式庫將新服務發佈為 WSGI 應用程式。請在應用程式目錄中建立名為 services.py 的新檔案,並且加入下列程式碼以建立服務:

from protorpc.wsgi import service

import postservice

# Map the RPC service and path (/PostService)
app = service.service_mappings([('/PostService', postservice.PostService)])

此時,將下列處理常式新增至 app.yaml 檔案現有的全部接收項目上方:

- url: /PostService.*
  script: services.app
- url: .*
  script: guestbook.app

透過指令列測試服務

現在,您已經建立了服務,可使用 curl 或類似的指令列工具進行測試。

# After starting the development web server:
# NOTE: ProtoRPC always expect a POST.
% curl -H \
   'content-type:application/json' \
   -d '{"text": "Hello guestbook!"}'\
   http://localhost:8080/PostService.post_note

空的 JSON 回應表示記事已成功張貼,您可以透過瀏覽器前往留言板應用程式,從中查看記事 (http://localhost:8080/)。

新增訊息欄位

現在,我們可以將訊息張貼到 PostService,您不妨再加入從 PostService 取得訊息的方法:首先,在 postservice.py 定義要求訊息,為要求訊息定義特定預設值以及新的 enum 欄位,enum 欄位將指示伺服器如何在回應中排序記事。請在先前定義的 PostService 類別「上方」進行定義:

class GetNotesRequest(messages.Message):
    limit = messages.IntegerField(1, default=10)
    on_or_before = messages.IntegerField(2)

    class Order(messages.Enum):
        WHEN = 1
        TEXT = 2
    order = messages.EnumField(Order, 3, default=Order.WHEN)

傳送至 PostService 時,這個訊息會要求特定日期之前或當日的一些記事,並依特定順序排列。limit 欄位表示要擷取的記事數上限。如未明確設定,limit 預設會設為 10 個記事 (如 default=10 關鍵字引數所示)。

順序欄位導入 EnumField 類別來啟用 enum 欄位類型,將欄位值限制為特定範圍的已知符號值。在此情況下,enum 會指示伺服器如何在回應中排序記事。如要定義 enum 值,請建立 Enum 類別的子類別。您必須為每個類型的名稱指派唯一的數值,每個數值都會轉換為列舉類型的執行個體,可以從類別中存取。

print 'Enum value Order.%s has number %d' % (GetNotesRequest.Order.WHEN.name,
                                             GetNotesRequest.Order.WHEN.number)

每個 enum 值都具備方便將這些數值轉換為其名稱或數值的特性。您無需存取名稱和數值屬性,只需將每個值轉換為字串或整數即可:

print 'Enum value Order.%s has number %d' % (GetNotesRequest.Order.WHEN,
                                             GetNotesRequest.Order.WHEN)

列舉欄位的宣告方式與其他欄位類似,不過第一個參數必須是列舉類型,接著才是欄位數值。列舉欄位也可以包含預設值。

定義回應訊息

現在要來定義 get_notes() 回應訊息。回應必須是 Note 訊息的集合,訊息可以包含其他訊息。以下方定義的 Notes.notes 欄位為例,我們提供 Note 類別做為 messages.MessageField 建構函式的第一個參數 (在欄位數值之前),藉此表示此為訊息的集合:

class Notes(messages.Message):
    notes = messages.MessageField(Note, 1, repeated=True)

依照 repeated=True 關鍵字引數所示,Notes.notes 欄位也是重複欄位。重複欄位的值必須是其宣告的欄位類型清單。在這種情況下, Notes.notes 必須是 Note 執行個體的清單。清單會自動建立,且無法指派為 None。

以下舉例說明如何建立 Note 物件:

response = Notes(notes=[Note(text='This is note 1'),
                        Note(text='This is note 2')])
print 'The first note is:', response.notes[0].text
print 'The second note is:', response.notes[1].text

實作 get_notes

現在可將 get_notes() 方法加入到 PostService 類別中:

import datetime
import time
from protorpc import remote

class PostService(remote.Service):
    @remote.method(GetNotesRequest, Notes)
    def get_notes(self, request):
        query = guestbook.Greeting.query().order(-guestbook.Greeting.date)

        if request.on_or_before:
            when = datetime.datetime.utcfromtimestamp(
                request.on_or_before)
            query = query.filter(guestbook.Greeting.date <= when)

        notes = []
        for note_model in query.fetch(request.limit):
            if note_model.date:
                when = int(time.mktime(note_model.date.utctimetuple()))
            else:
                when = None
            note = Note(text=note_model.content, when=when)
            notes.append(note)

        if request.order == GetNotesRequest.Order.TEXT:
            notes.sort(key=lambda note: note.text)

        return Notes(notes=notes)
本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Python 2 適用的 App Engine 標準環境