※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。


Django gRPC Frameworkで簡単にgRPCサービスを作成

Django grpc サーバー開発 django-grpc-framework チュートリアル

投稿日:2021年2月28日

このエントリーをはてなブックマークに追加
grpcはサービス間の通信の効率化のためにGoogleが開発した技術です。grpcの実装方法はいくつかあるのですが、この記事ではその中でDjangoでの実装方法について解説しています。

はじめに

Django gRPC Framework

Django gRPC FrameworkはDjango(+Django Rest Framework)でgrpcを実装するためのサードパーティのライブラリです。基本的にこのライブラリはDjangoとDjango Rest Frameworkについての基本的な知識を持っている前提で実装されています。この2つについてまだ触れたことがない場合には、まずこの2つの公式Tutorialをやってみるのが良いでしょう。

また現状、このフレームワークはProtocol Bufferのバージョン3にしか対応していないことも注意して置きましょう。

環境

この記事のコードは以下の環境で確認されました。

  • Python 3.8.5
  • pip 21.0.1

実践

この実践ではDjangoのadminアプリケーションにデフォルトで用意されているUserクラスのデータについてのgRPCサービスを作成してみます。

では始めましょう!

前準備

念の為ですが、はじめにパッケージマネージャpipのアップグレードをしておきます。

ターミナル
$ python -m pip install --upgrade pip

つぎに必要なパッケージのインストールをしましょう。

今回必要なパッケージは以下の5つです。

  • django:webフレームワーク
  • djangorestframework:djangoのAPI機能の拡張ライブラリ
  • djangogrpcframework:djangoのgrpcの拡張ライブラリ
  • grpc:pythonのgrpcライブラリ。djangogrpcframeworkに必要
  • grpcio-tools:protoファイルからpythonコードを生成するツール
ターミナル
$ pip install Django==3.1.7
$ pip install djangorestframework==3.12.2
$ pip install djangogrpcframework==0.2
$ pip install grpcio==1.36.0
$ pip install grpcio-tools==1.36.0

必要パッケージがインストールできたら、サンプルのdjangoプロジェクトアプリケーションを作成してみましょう。

プロジェクトはquickstart、アプリケーションにはaccountという名前をつけることにしましょう。

(この部分はまだ普通のdjangoプロジェクトを作成する場合とまったく一緒です。)

ターミナル
$ django-admin startproject quickstart
$ cd quickstart
$ django-admin startapp account
$ python manage.py migrate

gRPCサービスの作成

ここから本格的にgRPCサービスの実装に移ります。

Django gRPC Frameworkで実装する場合にはおおまかに以下のような手順になります。

  1. Protocol Bufferの定義
  2. Protocol Bufferからgrpcのpythonコードを自動生成
  3. データをシリアライズするSerializerを作成
  4. Serviceを作成
  5. 作成したServiceを登録

では1から始めまていきましょう。

1.Protocol Bufferの定義

Django gRPC FrameworkにはDjangoのModelクラスからProtocol Bufferを自動で作成するツールが付属しています。このツールはDjangoにコマンドとしてインストールされているためmanage.pyで実行できます。
今回はこのツールを使ってみましょう。
ツールから生成する際の対象のModelクラスとしてdjangoフレームワークにデフォルトでインストールされているユーザークラス(django.contrib.auth.models.User)を指定してみます。

ターミナル
$ python manage.py generateproto --model django.contrib.auth.models.User --fields id,username,email,groups --file account.proto

各引数の意味は以下のようになっています。

  • --model:Protocol Bufferを生成するもとの定義となるModelクラス
  • --fields:サービスでやり取りするModelのフィールド
  • --file:Protocol Bufferを書き込むファイル(.protoという拡張子にしましょう)

コマンドがうまく成功すると以下のようなaccount.protoという名前でProtocol Bufferファイルが自動生成されます。

quickstart/account.proto
syntax = "proto3";

package account;

import "google/protobuf/empty.proto";

service UserController {
    rpc List(UserListRequest) returns (stream User) {}
    rpc Create(User) returns (User) {}
    rpc Retrieve(UserRetrieveRequest) returns (User) {}
    rpc Update(User) returns (User) {}
    rpc Destroy(User) returns (google.protobuf.Empty) {}
}

message User {
    int32 id = 1;
    string username = 2;
    string email = 3;
    repeated int32 groups = 4;
}

message UserListRequest {
}

message UserRetrieveRequest {
    int32 id = 1;
}

作成されたProtocol BufferはAPIのインターフェイスの定義になります。
今回はツールを使用しましたが作成されたProtocol Bufferはあくまでテンプレートなので好きに変更してもいいし、ツールを使わず1から作成してもかまいません。

2.Protocol Bufferからgrpcのpythonコードを自動生成

account.protoをもとにgrpcのpythonコードを生成してみましょう。
生成にはインストールしたgrpc_toolsを使用します。

ターミナル
$ python -m grpc_tools.protoc --proto_path=./ --python_out=./ --grpc_python_out=./ ./account.proto
  • proto_path:ProtoBufferファイルの存在するディレクトリのパス
  • python_out:生成するPythonの定義コードの出力ディレクトリ
  • grpc_python_out:生成するgrpcで通信するコードの出力ディレクトリ

詳しくは公式のドキュメントを参照していただくと良いと思います。

コマンドが成功すると、account_pb2.pyaccount_pb2_grpc.pyというファイルが生成されます。

3.データをシリアライズするSerializerを作成

シリアライザはDjango gRPC Frameworkの基底クラスと先程作成したaccount_pb2.pyのデータ定義を使用します。

account/serializers.py
from django.contrib.auth.admin import User
from django_grpc_framework import proto_serializers
import account_pb2

class UserProtoSerializer(proto_serializers.ModelProtoSerializer):
    class Meta:
        model = User
        proto_class = account_pb2.User
        fields = ['id', 'username', 'email', 'groups']

シリアライザを作成するにはDjango Grpc Frameworkのproto_serializersから特定のシリアライザ基底クラスを継承することで作成します。困ったときはライブラリのコードを見に行くと良いでしょう。ほとんどの部分はDjango Rest Frameworkのシリアライザをそのまま使っていたりするので理解に時間はかからないはずです。
シリアライザのMetaクラスのそれぞれのフィールドは以下のような設定になっています。

  • model:ここに設定したクラスのインスタンスとメッセージデータはシリアライザを通じて相互変換できます。
  • proto_class:ProtoBufferメッセージを生成するのに必要なクラスです。この値の設定かdata_to_message()メソッドのオーバーライドのいずれかは必須の設定になっています。
  • fields:ProtoBufferデータとmodelインスタンスの変換で自動的にマッピングするフィールドです。

4.Serviceを作成

作成したシリアライザとProtocol Bufferから生成されたgrpcコードを使ってサービスを作成します。サービスは実際にフロントからのリクエストを受け取り、処理する部分になります。

account/services.py
from django.contrib.auth.admin import User
from django_grpc_framework import generics
from account.serializers import UserProtoSerializer

class UserService(generics.ModelService):
    queryset = User.objects.all().order_by("-date_joined")
    serializer_class = UserProtoSerializer

ここではgenericsに定義されている汎用的なServiceを使用しています。

5.作成したServiceを登録

最後に作成したServiceをgrpcのサービスに紐づけます。

./quickstart/urls.py
import account_pb2_grpc
from account.services import UserService

urlpatterns = []

def grpc_handlers(server):
    account_pb2_grpc.add_UserControllerServicer_to_server(UserService.as_servicer(), server)

先程作成したServiceをProtocol Bufferファイルから生成したServicerに登録すればgrpcのサーバーの準備は完了です。

grpcの開発用サーバーを起動するには以下のコマンドを使用します。

ターミナル
$ python manage.py grpcrunserver --dev

サンプルのクライアントを作成してテスト

作成したgrpcサービスに対してptyhonのサンプルクライアントを作成して動作を確認してみます。もちろんクライアント側のコードもgrpcに対応したコードである必要があるのですが、はじめにProtocol Bufferから生成したaccount_pb2.pyaccount_pb2_grpc.pyをそのまま使うことができます。

Djangoプロジェクトに以下のようなファイルを作成してみましょう。

./client.py
import grpc
import account_pb2
import account_pb2_grpc

with grpc.insecure_channel('localhost:50051') as channel:
    stub = account_pb2_grpc.UserControllerStub(channel)
    for user in stub.List(account_pb2.UserListRequest()):
        print(user, end='')

ここでサンプルデータを作成しておきましょう。
今回はUserデータについてのサービスなので、適当に管理者を作れば良いでしょう笑。

ターミナル
$ python manage.py createsuperuser

一つのターミナルでDjangoのgrpc開発用サーバーを起動させてもう一つのコマンドでclientコードを走らせて見ましょう。

ターミナル1
$ python manage.py grpcrunserver --dev
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
March 02, 2021 - 15:54:17
Django version 3.1.7, using settings 'quickstart.settings'
Starting development gRPC server at [::]:50051
Quit the server with CONTROL-C.
ターミナル2
% python client.py           
id: 1
username: "marsquai"
email: "marsquai@example.com"

やったぜ!

このエントリーをはてなブックマークに追加


関連記事

記事へのコメント