Panoramica della libreria RPC di Google Protocol

Nota: per utilizzare l'autenticazione con il protocollo RPC di Google, puoi usare l'autenticazione attualmente disponibile per App Engine nella console Google Cloud. Dovrai anche specificare il requisito di accesso ai dati nel tuo app.yaml . Altre metodologie di autenticazione non sono attualmente supportate dalla libreria RPC di protocollo Google in in App Engine.

La libreria RPC del protocollo Google è un framework per l'implementazione di servizi di chiamata di procedura remota (RPC) basati su HTTP. Un servizio RPC è una raccolta di tipi di messaggi metodi che forniscono un modo strutturato per consentire alle applicazioni esterne di interagire con le applicazioni web. Poiché puoi definire messaggi e servizi nella Linguaggio di programmazione Python, è facile sviluppare servizi Protocol RPC, quei servizi e scalarli su App Engine.

Sebbene sia possibile utilizzare la libreria RPC del protocollo Google per qualsiasi tipo di servizio RPC basato su HTTP, alcuni casi d'uso comuni includono:

  • Pubblicazione di API web per l'utilizzo da parte di terze parti
  • Creazione di backend Ajax strutturati
  • Clonazione su comunicazione server a lunga esecuzione

Puoi definire un servizio RPC del protocollo Google in un'unica classe Python che contiene un numero qualsiasi di metodi remoti dichiarati. Ogni metodo remoto accetta un insieme specifico di parametri come richiesta e restituisce una risposta specifica. Questi parametri di richiesta e risposta sono classi definite dall'utente chiamate messaggi.

RPC (Hello World of Google Protocol)

Questa sezione presenta un esempio di definizione di servizio molto semplice che riceve un messaggio da un client remoto. Il messaggio contiene il nome di un utente (HelloRequest.my_name) e invia un messaggio di benvenuto per quella persona (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)])

Introduzione a Google Protocol RPC

Questa sezione mostra come iniziare a utilizzare il protocollo RPC di Google utilizzando l'applicazione guestbook sviluppata in Python. Gli utenti possono visitare il guestbook (incluso come demo nell'SDK Python) online, scrivere voci e visualizzare quelle di tutti gli utenti. Gli utenti interagiscono direttamente con l'interfaccia, ma le applicazioni web non sono in grado di accedere facilmente a tali informazioni.

È qui che entra in gioco Protocol RPC. In questo tutorial applicheremo la RPC del protocollo Google a questo guestbook di base, consentendo ad altre applicazioni web di accedere ai dati del guestbook. Questo tutorial illustra solo l'utilizzo della RPC del protocollo Google per estendere la funzionalità guestbook; sta a te decidere cosa fare. Ad esempio, potresti voler scrivere uno strumento che legga i messaggi pubblicati dagli utenti e crei un grafico di una serie temporale dei post al giorno. La modalità di utilizzo dell'RPC protocollo dipende dall'app specifica. il punto importante è che Google Protocol RPC espande notevolmente ciò che puoi fare con i dati della tua applicazione.

Per iniziare, dovrai creare un file, postservice.py, che implementa metodi remoti per accedere ai dati nel datastore dell'applicazione guestbook.

Creazione del modulo PostService

Il primo passaggio per iniziare a utilizzare Google Protocol RPC consiste nel creare un file denominato postservice.py nella directory delle applicazioni. Utilizzerai questo file per definire il nuovo servizio, che implementa due metodi: uno che invia i dati da remoto e un altro che li recupera da remoto.

Al momento non è necessario aggiungere nulla a questo file, ma è qui che inserirai tutto il codice definito nelle sezioni successive. Nella sezione successiva creerai un messaggio che rappresenta una nota pubblicata nel datastore dell'applicazione guestbook.

Utilizzo di Messaggi

I messaggi sono il tipo di dati fondamentale utilizzato nel protocollo RPC di Google. I messaggi vengono definiti mediante la dichiarazione di una classe che eredita dalla classe di base Message. Quindi devi specificare gli attributi di classe che corrispondono a ciascuno dei campi del messaggio.

Ad esempio, il servizio guestbook consente agli utenti di pubblicare una nota. Se non lo hai già fatto, crea un file denominato postservice.py nella directory dell'applicazione. PostService utilizza la classe Greeting per archiviare un post nel datastore. Definiamo un messaggio che rappresenta una nota di questo tipo:

from protorpc import messages

class Note(messages.Message):

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

Il messaggio della nota è definito da due campi, text e when. Ogni campo ha un tipo specifico. Il campo di testo è una stringa Unicode che rappresenta i contenuti di un post di un utente nella pagina del guestbook. Il campo when è un numero intero che rappresenta il timestamp del post. Nella definizione della stringa, occorre anche:

  • Assegna a ogni campo un valore numerico univoco (1 per text e 2 per when) che il protocollo di rete sottostante utilizza per identificare il campo.
  • Rendi text un campo obbligatorio. I campi sono facoltativi per impostazione predefinita; puoi contrassegnarli come obbligatori impostando required=True. I messaggi devono essere inizializzati impostando un valore per i campi obbligatori. I metodi del servizio RPC del protocollo Google accettano solo messaggi inizializzati correttamente.

Puoi impostare i valori dei campi utilizzando il costruttore della classe Note:

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

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

Puoi anche leggere e impostare i valori su un messaggio come i normali valori degli attributi Python. Ad esempio, per modificare il messaggio:

print note_instance.text
note_instance.text = u'Good-bye guestbook!'
print note_instance.text
che restituisce il seguente codice
Hello guestbook!
Good-bye guestbook!

Definizione di un servizio

Un service è una definizione di classe che eredita dalla classe base Service. I metodi remoti di un servizio vengono indicati utilizzando il decorator remote. Ogni metodo di un servizio accetta un singolo messaggio come parametro e restituisce un singolo messaggio come risposta.

Definiamo il primo metodo di PostService. Aggiungi quanto segue al tuo file 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()

Il decorator remote richiede due parametri:

  • Il tipo di richiesta previsto. Il metodo post_note() accetta un'istanza di Note come tipo di richiesta.
  • Il tipo di risposta previsto. La libreria RPC del protocollo Google include un tipo integrato denominato VoidMessage (nel modulo protorpc.message_types), definito come messaggio senza campi. Ciò significa che il messaggio post_note() non restituisce nulla di utile al chiamante. Se viene restituito senza errori, il messaggio viene considerato pubblicato.

Poiché Note.when è un campo facoltativo, potrebbe non essere stato impostato dal chiamante. In questo caso, il valore di when è impostato su Nessuno. Se Note.when è impostato su None, post_note() crea il timestamp utilizzando l'ora in cui ha ricevuto il messaggio.

Il messaggio di risposta viene creato dal metodo remoto e diventa il valore restituito del metodo remoto.

Registrazione del servizio

Puoi pubblicare il nuovo servizio come applicazione WSGI utilizzando la libreria protorpc.wsgi.service. Crea un nuovo file denominato services.py nella directory dell'applicazione e aggiungi il codice seguente per creare il servizio:

from protorpc.wsgi import service

import postservice

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

Ora aggiungi il seguente gestore al tuo file app.yaml sopra la voce catch-all esistente:

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

Test del servizio dalla riga di comando

Ora che hai creato il servizio, puoi testarlo utilizzando curl o uno strumento a riga di comando simile.

# 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

Una risposta JSON vuota indica che la nota è stata pubblicata correttamente. Puoi visualizzare la nota andando all'applicazione guestbook nel browser (http://localhost:8080/).

Aggiunta di campi per i messaggi

Ora che possiamo pubblicare messaggi su PostService, aggiungiamo un nuovo metodo per ricevere i messaggi da PostService. Innanzitutto, devi definire un messaggio di richiesta in postservice.py che definisce alcuni valori predefiniti e un nuovo campo enum che indica al server come ordinare le note della risposta. Definiscilo sopra la classe PostService che hai definito in precedenza:

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)

Quando viene inviato a PostService, il messaggio richiede una serie di note entro una determinata data e in un determinato ordine. Il campo limit indica il numero massimo di note da recuperare. Se non viene configurato in modo esplicito, il valore predefinito di limit è 10 note (come indicato dall'argomento parola chiave default=10).

Il campo dell'ordine introduce la classe EnumField, che abilita il tipo di campo enum quando il valore di un campo è limitato a un numero limitato di valori simbolici noti. In questo caso, enum indica al server come ordinare le note nella risposta. Per definire i valori enum, crea una sottoclasse della classe Enum. A ogni nome deve essere assegnato un numero univoco per il tipo. Ogni numero viene convertito in un'istanza del tipo enum ed è accessibile dalla classe.

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

Ogni valore enum ha una caratteristica speciale che semplifica la conversione al nome o al numero. Anziché accedere all'attributo name e number, converti ogni valore in una stringa o in un numero intero:

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

I campi enum vengono dichiarati in modo simile agli altri campi, ad eccezione del fatto che deve avere il tipo enum come primo parametro prima del numero di campo. I campi enum possono anche avere valori predefiniti.

Definizione del messaggio di risposta

Definiamo ora il messaggio di risposta get_notes(). La risposta deve essere una raccolta di messaggi delle note. I messaggi possono contenere altri messaggi. Nel caso del campo Notes.notes definito di seguito, indichiamo che si tratta di una raccolta di messaggi fornendo la classe Note come primo parametro al costruttore messages.MessageField (prima del numero di campo):

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

Anche il campo Notes.notes è un campo ripetuto, come indicato dall'argomento parola chiave repeated=True. I valori dei campi ripetuti devono essere elenchi del tipo di campo della relativa dichiarazione. In questo caso, Notes.notes deve essere un elenco di istanze Note. Le liste vengono create automaticamente e non possono essere assegnate a Nessuna.

Ad esempio, ecco come creare un oggetto Notes:

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

Implementare get_notes

Ora possiamo aggiungere il metodo get_notes() alla classe 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)