Service Extension アプリの作成
注釈:本資料はAI技術を用いて翻訳されています。
Overview
Extend Service Extension アプリは、gRPC サーバーと gRPC Gateway を含むスタックを使用して作成された RESTful ウェブサービスです。
一般的に、このスタックを使用してゼロから RESTful ウェブサービスを構築する手順は次のとおりです。
- protobuf (
*.proto) ファイルを使用して gRPC サーバーを定義する。 *.protoファイルからスタブを生成し、gRPC サーバーを実装する。*.protoファイルから gRPC Gateway コードを生成し、そのエントリーポイントを記述する。*.protoファイルから OpenAPI 2.0 仕様を生成する。
ただし、Service Extension アプリテンプレートを使用すると、手順 1 と 2 のみを実行すればよくなります。残りの手順はビルド時に自動的に実行されるようパッケージ化されています。このアプリテンプレートには、認可が必要な RESTful エンドポイントを作成するのに役立つ gRPC サーバーインターセプターも含まれています。さらに、可観測性のための組み込みインスツルメンテーションも備えており、デプロイ時にメトリクスとログが利用可能になります。
この記事では、Extend Service Extension アプリテンプレートを変更し、要件に合わせた独自のアプリに変換する方法を説明します。
Prerequisites
Extend Service Extension アプリテンプレートをクローンしていること。
- C#
- Go
- Java
- Python
git clone https://github.com/AccelByte/extend-service-extension-csharp
git clone https://github.com/AccelByte/extend-service-extension-go
git clone https://github.com/AccelByte/extend-service-extension-java
- uv
- pip + venv
git clone https://github.com/AccelByte/extend-service-extension-python-uv
git clone https://github.com/AccelByte/extend-service-extension-python
Project structure
- C#
- Go
- Java
- Python
Extend Service Extension アプリのカスタマイズには、service.proto ファイルと MyService.cs ファイルの変更が含まれます。アプリは Program.cs の中で、gRPC サーバーなどの主要なコンポーネントを初期化します。RESTful エンドポイントへのリクエストが行われると、gRPC gateway がそれを処理し、対応する gRPC メソッドに転送します。myService.cs がリクエストに基づくカスタムロジックを実行する前に、authServerInterceptor.cs がリクエストに必要なアクセストークンと認可があることを最初に検証します。さらにカスタマイズが必要な場合を除き、他のファイルを変更する必要はありません。
.
├── src
│ ├── AccelByte.Extend.ServiceExtension.Server
│ │ ├── AccelByte.Extend.ServiceExtension.Server.csproj
│ │ ├── Classes
│ │ │ ├── AuthorizationInterceptor.cs # gRPC server interceptor for access token authentication and authorization
│ │ │ └── ...
│ │ ├── Program.cs # App starts here
│ │ ├── Protos
│ │ │ ├── service.proto # gRPC server protobuf with additional options for exposing as RESTful web service
│ │ │ └── ...
│ │ ├── Services
│ │ │ └── MyService.cs # gRPC server implementation containing the custom logic
│ └── extend-service-extension-server.sln
└── ...
Extend Service Extension アプリのカスタマイズには、service.proto ファイルと myService.go ファイルの変更が含まれます。アプリは main.go の中で、gRPC サーバーなどの主要なコンポーネントを初期化します。RESTful エンドポイントへのリクエストが行われると、gRPC gateway がそれを処理し、対応する gRPC メソッドに転送します。myService.go がリクエストに基づくカスタムロジックを実行する前に、authServerInterceptor.go がリクエストに必要なアクセストークンと認可があることを最初に検証します。さらにカスタマイズが必要な場合を除き、他のファイルを変更する必要はありません。
.
├── main.go # App starts here
├── pkg
│ ├── common
│ │ ├── authServerInterceptor.go # gRPC server interceptor for access token authentication and authorization
│ │ ├── ...
│ ├── pb # gRPC stubs generated from gRPC server protobuf
│ │ └── ...
│ ├── proto
│ │ ├── service.proto # gRPC server protobuf with additional options for exposing as RESTful web service
│ │ └── ...
│ ├── service
│ │ ├── myService.go # gRPC server implementation containing the custom logic
│ │ └── ...
│ └── ...
└── ...
Extend Service Extension アプリのカスタマイズには、service.proto ファイルと MyService.java ファイルの変更が含まれます。アプリは Application.java の中で、gRPC サーバーなどの主要なコンポーネントを初期化します。RESTful エンドポイントへのリクエストが行われると、gRPC gateway がそれを処理し、対応する gRPC メソッドに転送します。MyService.java がリクエストに基づくカスタムロジックを実行する前に、AuthServerInterceptor.java がリクエストに必要なアクセストークンと認可があることを最初に検証します。さらにカスタマイズが必要な場合を除き、他のファイルを変更する必要はありません。
.
├── src
│ ├── main
│ │ ├── java
│ │ │ └── net
│ │ │ └── accelbyte
│ │ │ └── extend
│ │ │ └── serviceextension
│ │ │ ├── Application.java # App starts here
│ │ │ ├── grpc
│ │ │ │ ├── AuthServerInterceptor.java # gRPC server interceptor for access token authentication and authorization
│ │ │ │ └── ...
│ │ │ ├── service
│ │ │ │ └── MyService.java # gRPC server implementation containing the custom logic
│ │ │ └── ...
│ │ ├── proto
│ │ │ ├── service.proto # gRPC server protobuf with additional options for exposing as RESTful web service
│ │ │ └── ...
│ │ └── ...
│ └── ...
└── ...
Extend Service Extension アプリのカスタマイズには、service.proto ファイルと my_service.py ファイルの変更が含まれます。アプリは __main__.py の中で、gRPC サーバーなどの主要なコンポーネントを初期化します。RESTful エンドポイントへのリクエストが行われると、gRPC gateway がそれを処理し、対応する gRPC メソッドに転送します。my_service.py がリクエストに基づくカスタムロジックを実行する前に、authorization.py がリクエストに必要なアクセストークンと認可があることを最初に検証します。さらにカスタマイズが必要な場合を除き、他のファイルを変更する必要はありません。
.
├── proto
│ ├── service.proto # gRPC server protobuf with additional options for exposing as RESTful web service
│ ├── permission.proto
│ ├── ...
├── src
│ ├── accelbyte_grpc_plugin
│ │ ├── interceptors
│ │ │ ├── authorization.py # gRPC server interceptor for access token authentication and authorization
│ │ │ └── ...
│ │ └── ...
│ ├── app
│ │ ├── __main__.py # App starts here
│ │ ├── services
│ │ │ ├── my_service.py # gRPC server implementation containing the custom logic
│ │ │ └── ...
│ │ └── ...
│ └── ...
└── ...
Modify the protobuf
- C#
- Go
- Java
- Python
アプリテンプレートでは、このファイルは src/AccelByte.Extend.ServiceExtension.Server/Protos/service.proto にあります。
アプリテンプレートでは、このファイルは pkg/proto/service.proto にあります。
アプリテンプレートでは、このファイルは src/main/proto/service.proto にあります。
アプリテンプレートでは、このファイルは proto/service.proto にあります。
import "google/api/annotations.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "permission.proto";
service Service {
rpc CreateOrUpdateGuildProgress (CreateOrUpdateGuildProgressRequest) returns (CreateOrUpdateGuildProgressResponse) {
option (permission.action) = CREATE;
option (permission.resource) = "ADMIN:NAMESPACE:{namespace}:CLOUDSAVE:RECORD";
option (google.api.http) = {
post: "/v1/admin/namespace/{namespace}/progress"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Update Guild progression"
description: "Update Guild progression if not existed yet will create a new one"
security: {
security_requirement: {
key: "Bearer"
value: {}
}
}
};
}
message CreateOrUpdateGuildProgressRequest {
string namespace = 1;
GuildProgress guild_progress = 2;
}
message CreateOrUpdateGuildProgressResponse {
GuildProgress guild_progress = 1;
}
}
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
info: {
title: "Service API";
version: "1.0";
};
schemes: HTTP;
schemes: HTTPS;
base_path: "/service";
security_definitions: {
security: {
key: "Bearer";
value: {
type: TYPE_API_KEY;
in: IN_HEADER;
name: "Authorization";
}
}
};
};
Extend Service Extension の service.proto ファイルは、基本的に通常の gRPC サーバー定義に、以下の追加オプションを加えたものです。
-
option (google.api.http)gRPC メソッドと RESTful エンドポイントの関係を記述します。詳細については、gRPC-Gateway のドキュメントを参照してください。
-
option (permission.resource)およびoption (permission.action)各 RESTful エンドポイントを呼び出すために必要なパーミッションリソースとアクションを記述します。これにより、有効な AGS アクセストークンとパーミッションを必要とするエンドポイントを作成できます。
パーミッションリソースとアクションの値は、同梱の gRPC サーバーインターセプターが認可を行う際に使用します。
-
option (permission.resource): AGS フォーマットを使用したパーミッションリソース文字列をここに指定できます。 -
option (permission.action): このオプションの有効な値はCREATE、READ、UPDATE、またはDELETEです。詳細については、AGS パーミッションアクションを参照してください。
備考AGS Public Cloud でカスタムパーミッションを作成するには、Public Cloud カスタムパーミッションを参照してください。その後、新しく作成したカスタムパーミッションを
option (permission.resource)の値として使用できます。 -
-
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger)およびoption (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation)OpenAPI 2.0 仕様を生成するための情報を提供します。詳細については、gRPC-Gateway のドキュメントを参照してください。
Generate stubs from protobuf
- C#
- Go
- Java
- Python
以下のコマンドを実行して、proto ファイルからスタブを生成します。
make proto # Generate gateway code and swagger JSON
make build # Some protobuf code is generated on-the-fly during build
以下のコマンドを実行して、proto ファイルからスタブを生成します。
make proto # Generate protobuf code, gateway code, and swagger JSON
以下のコマンドを実行して、proto ファイルからスタブを生成します。
make proto # Generate gateway code, and swagger JSON
make build # Some protobuf code is generated on-the-fly during build
以下のコマンドを実行して、proto ファイルからスタブを生成します。
make proto # Generate protobuf code, gateway code, and swagger JSON
service.proto ファイルを変更した後は、必ず上記のコマンドを実行してスタブを再生成してください。
Implement request handlers
- C#
- Go
- Java
- Python
アプリプロジェクトでは、以下の内容が src/AccelByte.Extend.ServiceExtension.Server/Services/MyService.cs にあります。
サービスをセットアップするには、Service.ServiceBase から派生したクラスを作成します。このクラスがサービス実装として機能します。
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Grpc.Core;
using AccelByte.Sdk.Api;
using AccelByte.Extend.ServiceExtension.Server.Model;
namespace AccelByte.Extend.ServiceExtension.Server.Services
{
public class MyService : Service.ServiceBase
{
public MyService()
{
}
// Implement your service logic in here
}
}
CreateOrUpdateGuildProgress 関数を実装するには、以下のようにメソッドをオーバーライドします。
public override Task<CreateOrUpdateGuildProgressResponse> CreateOrUpdateGuildProgress(CreateOrUpdateGuildProgressRequest request, ServerCallContext context)
{
// Implementation goes here
}
GetGuildProgress 関数についても同様です。
public override Task<GetGuildProgressResponse> GetGuildProgress(GetGuildProgressRequest request, ServerCallContext context)
{
// Implementation goes here
}
アプリプロジェクトでは、以下の内容が src/master/pkg/service/myService.go にあります。
サービスをセットアップするには、UnimplementedServiceServer を埋め込んだ構造体を作成します。
import pb "extend-custom-guild-service/pkg/pb"
type MyServiceServerImpl struct {
pb.UnimplementedServiceServer
// Other fields
}
CreateOrUpdateGuildProgress 関数は以下のように実装します。
func (g MyServiceServerImpl) CreateOrUpdateGuildProgress(
ctx context.Context, req *pb.CreateOrUpdateGuildProgressRequest,
) (*pb.CreateOrUpdateGuildProgressResponse, error) {
// Your implementation
}
GetGuildProgress 関数についても同様です。
func (g MyServiceServerImpl) GetGuildProgress(
ctx context.Context, req *pb.GetGuildProgressRequest,
) (*pb.GetGuildProgressResponse, error) {
// Your implementation
}
アプリプロジェクトでは、以下の内容が src/main/java/net/accelbyte/extend/serviceextension/service/MyService.java にあります。
サービスをセットアップするには、ServiceGrpc.ServiceImplBase から派生したクラスを作成します。このクラスがサービス実装として機能します。
import lombok.extern.slf4j.Slf4j;
import net.accelbyte.extend.serviceextension.*;
import net.accelbyte.sdk.core.AccelByteSDK;
import org.lognet.springboot.grpc.GRpcService;
@GRpcService
@Slf4j
public class MyService extends ServiceGrpc.ServiceImplBase {
// Implementation goes here
}
CreateOrUpdateGuildProgress 関数を実装するには、以下のようにメソッドをオーバーライドします。
public void createOrUpdateGuildProgress(
CreateOrUpdateGuildProgressRequest request, StreamObserver<CreateOrUpdateGuildProgressResponse> responseObserver
) {
// Implementation goes here
}
GetGuildProgress 関数についても同様に実装できます。
public void getGuildProgress(
GetGuildProgressRequest request, StreamObserver<GetGuildProgressResponse> responseObserver
) {
// Implementation goes here
}
アプリプロジェクトでは、以下の内容が src/master/src/app/services/my_service.py にあります。
サービスをセットアップするために、まず ServiceServicer から派生したクラスを作成します。
このクラスがサービス実装として機能します。
from ..proto.service_pb2 import (
CreateOrUpdateGuildProgressRequest,
CreateOrUpdateGuildProgressResponse,
GetGuildProgressRequest,
GetGuildProgressResponse,
DESCRIPTOR,
)
from ..proto.service_pb2_grpc import ServiceServicer
class AsyncService(ServiceServicer):
full_name: str = DESCRIPTOR.services_by_name["Service"].full_name
# Implement your service logic in here.
CreateOrUpdateGuildProgress 関数を実装するには、以下のようにメソッドをオーバーライドします。
async def CreateOrUpdateGuildProgress(
self, request: CreateOrUpdateGuildProgressRequest, context: Any
) -> CreateOrUpdateGuildProgressResponse:
...
GetGuildProgress 関数についても同様です。
async def GetGuildProgress(
self, request: GetGuildProgressRequest, context: Any
) -> GetGuildProgressResponse:
...
gRPC のリクエスト処理の詳細については、リクエストの処理を参照してください。
Run the app locally
リクエストハンドラーの実装が完了したら、アプリを実行して変更をローカルでテストします。
- C#
- Go
- Java
- Python
make run
make run
make run
プロジェクトのトップレベルディレクトリで、依存関係をインストールし、gRPC サーバーを実行します。
- uv
- pip + venv
uv sync
PYTHONPATH=src uv run python -m app
Linux、macOS、または Windows (WSL2) の場合。
source .venv/bin/activate
PYTHONPATH=src python -m app
その後、別のターミナルで gRPC gateway を実行します。
make run_gateway