はじめに
CysharpからYetAnotherHttpHandlerがリリースされたので、
これを使ってgRPCでの通信ができるかどうか試してみました!
0. 実行環境
M1 Mac
$ sw_vers
ProductName: macOS
ProductVersion: 13.5.2
BuildVersion: 22G91
Unityのバージョン
(2021.3以降であれば、YetAnotherHttpHandlerがサポートしているので問題ないと思います)
$ cat ProjectSettings/ProjectVersion.txt
m_EditorVersion: 2022.3.10f1
m_EditorVersionWithRevision: 2022.3.10f1 (ff3792e53c62)
1. CloudRunの設定 (Server設定)
1-1. localでサーバの動作確認
確認用のサーバはGCPのCloud Runで動かします.
公式にhelloworldのサンプルがあるので、これ利用しました.
ただ、Cloud Runで動かすにはhealth checkを通す必要があるので若干の修正が必要です.
上記コードに少しパッチを当てたものをforkして用意しました.
パッチを当てた内容は下記のような感じです.
- go.modの追加、proto生成後のpackage参照を変更 (ビルド時の依存更新)
- PORTを環境変数で指定可能にした (Cloud Run対応)
- サーバ起動時のコードにhealth checkのコードを追記 (Cloud Run対応)
- リフレクションの導入 (提供するメソッド、型、その他のメタデータを公開)
- Dockerfileの追加 (ArtifactRegistoryへの登録用)
$ git clone https://github.com/mattak/grpc-go.git -b cloudrun --depth 1
$ cd grpc-go/examples/helloworld
これをcloud runで動く形にビルド.
$ docker build --platform linux/amd64 -t grpc_sample -f Dockerfile .
試しにlocalで動かしてみて、通信に成功すればOKです!
server
$ docker run --rm -p 50051:50051 -it grpc_sample
2023/09/26 11:06:57 server listening at [::]:50051
health check
$ grpcurl -plaintext localhost:50051 grpc.health.v1.Health.Check
{
"status": "SERVING"
}
request check
$ go mod download
$ go run greeter_client/main.go
2023/09/26 20:08:37 Greeting: Hello world
1-2. Cloud Runにimageアップロードして確認
先程のdocker imageをGCPのArtifact Registoryにpushします.
(事前にGCPのproject作成やartifact registroyへのレポジトリ作成はしておく)
# 定義:
# PROJECT=FIXME
# REPOSITORY=FIXME
# IMAGE=FIXME
# URI=asia-northeast1-docker.pkg.dev/$PROJECT/$REPOSITORY/$IMAGE:latest
docker tag grpc_sample $URI
docker push $URI
CloudRunのページからサービスを作成.
- 既存のコンテナイメージから1つのリビジョンをデプロイする (先程のURIを指定)
- リージョン: asia-northeast1
- 認証: 未認証の呼出を許可
- コンテナポート: 50051
- ヘルスチェック
- 種類: 起動時チェック
- プローブタイプ: gRPC
- 初期遅延: 0
- ネットワーキング
- HTTP/2 エンドツーエンドを使用する
下記のようにヘルスチェックが通っていれば成功です!
ここで表示されるURLはメモっておきます。
2. Unityでの設定
2-1. プロジェクトの設定
適当にUnityの新規プロジェクトを作成.
YetAnotherHttpHandlerをInstallするが、
デフォルトだとSystem.IO.Pipelineなどがmissingになるので事前に必要となる依存を追加する.
上記ページから下記2つのunitypackageをDLして、プロジェクトにインストールする.
- Cysharp.Net.Http.YetAnotherHttpHandler.Dependencies.unitypackage
- Grpc.Net.Client.Dependencies.unitypackage
Packages/manifest.json
にYetAnotherHttpHandlerの依存を追加する.
"com.cysharp.yetanotherhttphandler": "https://github.com/Cysharp/YetAnotherHttpHandler.git?path=src/YetAnotherHttpHandler",
また、生成後のファイルでGoogle.Protobufが必要となるのでこれのdllも追加しておく.
$ curl -Lo protobuf.zip https://www.nuget.org/api/v2/package/Google.Protobuf/3.24.3
$ unzip protobuf.zip -d protobuf
$ mkdir -p Assets/Plugins/Google.Protobuf
$ cp protobuf/lib/net45/Google.Protobuf.dll Assets/Plugins/Google.Protobuf/
2-2. protoファイルからC#コードを生成する
nugetのGrpc.Toolsから必要なコマンドを抜き出す.
$ curl -Lo grpctools.zip https://www.nuget.org/api/v2/package/Grpc.Tools/2.58.0
$ unzip grpctools.zip -d grpctools
梱包されている下記2つの実行バイナリを利用する.
grpctools/tools/macosx_x64/grpc_csharp_plugin
grpctools/tools/macosx_x64/protoc
実行バイナリに実行権限をつけておく.
$ chmod +x grpctools/tools/macosx_x64/protoc
$ chmod +x grpctools/tools/macosx_x64/grpc_csharp_plugin
serverで利用しているprotoをDL
$ curl -L -O https://raw.githubusercontent.com/mattak/grpc-go/cloudrun/examples/helloworld/helloworld/helloworld.proto
出力先を作っておく
$ mkdir -p Assets/Scripts/Protos
protocを使ってコード生成する
PROTOC=$PWD/grpctools/tools/macosx_x64/protoc
GRPC_PLUGIN=$PWD/grpctools/tools/macosx_x64/grpc_csharp_plugin
$PROTOC -I ./ \
--csharp_out=./Assets/Scripts/Protos \
--grpc_out=./Assets/Scripts/Protos \
--plugin=protoc-gen-grpc=$GRPC_PLUGIN \
./helloworld.proto
下記ファイルが生成されていればOK
$ find Assets/Scripts/Protos -type f
Assets/Scripts/Protos/Helloworld.cs
Assets/Scripts/Protos/HelloworldGrpc.cs
2-3. Unity上からリクエストを呼び出してみる
シンプルなスクリプトを記述して、適当にGameObjectにつける.
using Cysharp.Net.Http;
using Cysharp.Threading.Tasks;
using Grpc.Net.Client;
using Helloworld;
using UnityEngine;
namespace GrpcSample
{
public class SampleRequestRunner : MonoBehaviour
{
private const string URL = "https://FIXME.a.run.app"; // FIXME: 先ほどメモったCloudRunのURLを入力する
private void Start()
{
Request();
}
private void Request()
{
using var handler = new YetAnotherHttpHandler();
using var channel = GrpcChannel.ForAddress(URL, new GrpcChannelOptions
{
HttpHandler = handler,
DisposeHttpClient = true,
});
var client = new Greeter.GreeterClient(channel);
var reply = client.SayHello(new HelloRequest {Name = "UnityClient"});
Debug.Log("Greeting: " + reply.Message);
}
}
}
ログに下記のようにメッセージが表示されていれば、疎通成功!
表示されない場合は一度cloud runをデプロイし直して、サーバが立ち上がっていることを確認してから再度リクエストするとうまくいく.
感想
一昔前だと、macではC# + gRPC環境構築するのが難しかったりしたが、けっこう楽ちんに構築できた!
YetAnotherHttpHandlerをOSSで公開してくれたCysharpさんに圧倒的感謝!