投稿日:2021年2月28日
grpcはサービス間の通信の効率化のためにGoogleが開発した技術です。grpcの実装方法はいくつかあるのですが、この記事ではその中でDjangoでの実装方法について解説しています。
Django gRPC FrameworkはDjango(+Django Rest Framework)でgrpcを実装するためのサードパーティのライブラリです。基本的にこのライブラリはDjangoとDjango Rest Frameworkについての基本的な知識を持っている前提で実装されています。この2つについてまだ触れたことがない場合には、まずこの2つの公式Tutorialをやってみるのが良いでしょう。
また現状、このフレームワークはProtocol Bufferのバージョン3にしか対応していないことも注意して置きましょう。
この記事のコードは以下の環境で確認されました。
この実践ではDjangoのadminアプリケーションにデフォルトで用意されているUserクラスのデータについてのgRPCサービスを作成してみます。
では始めましょう!
念の為ですが、はじめにパッケージマネージャpipのアップグレードをしておきます。
$ python -m pip install --upgrade pip
つぎに必要なパッケージのインストールをしましょう。
今回必要なパッケージは以下の5つです。
$ 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サービスの実装に移ります。
Django gRPC Frameworkで実装する場合にはおおまかに以下のような手順になります。
では1から始めまていきましょう。
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
各引数の意味は以下のようになっています。
コマンドがうまく成功すると以下のようなaccount.protoという名前でProtocol Bufferファイルが自動生成されます。
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から作成してもかまいません。
account.protoをもとにgrpcのpythonコードを生成してみましょう。
生成にはインストールしたgrpc_toolsを使用します。
$ python -m grpc_tools.protoc --proto_path=./ --python_out=./ --grpc_python_out=./ ./account.proto
詳しくは公式のドキュメントを参照していただくと良いと思います。
コマンドが成功すると、account_pb2.pyとaccount_pb2_grpc.pyというファイルが生成されます。
シリアライザはDjango gRPC Frameworkの基底クラスと先程作成したaccount_pb2.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クラスのそれぞれのフィールドは以下のような設定になっています。
作成したシリアライザとProtocol Bufferから生成されたgrpcコードを使ってサービスを作成します。サービスは実際にフロントからのリクエストを受け取り、処理する部分になります。
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を使用しています。
最後に作成したServiceをgrpcのサービスに紐づけます。
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.pyとaccount_pb2_grpc.pyをそのまま使うことができます。
Djangoプロジェクトに以下のようなファイルを作成してみましょう。
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コードを走らせて見ましょう。
$ 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.
% python client.py
id: 1
username: "marsquai"
email: "marsquai@example.com"
やったぜ!