LoginSignup
1
1

More than 3 years have passed since last update.

gRPCサーバーから"unknown service"エラーが返ってきた時の対処方法について

Last updated at Posted at 2020-10-18

最近流行りのgRPC.
スキーマ言語であるProtocol Buffersによってデータをシリアライズ化する事ができ、
これまで主流だったjson通信よりもより高速に通信を行えるとして、
マイクロサービス開発で採用されるケースが増えている。

この間gRPCで構築したクライアント・サーバー間の通信をやろうとした時にタイトルの通りのバグが発生し
だいぶ手こずったので、忘備録として原因と解決策を残しておこうと思う。

ソースコード

ソースはこちら。
https://github.com/yzmw1213/PostService

やろうとした事

goで書いたgRPCサーバーで、以下2つのサービスを実装する。
- 投稿サービス
- 投稿につけるタグの管理サービス(マスタデータとしての扱い)

そして、typescriptで実装している client側のコードから、上のサービスにリクエストを送り処理を行う。

client.ts
import { Tag, CreateTagRequest } from "~/grpc/tag_pb"
import { TagServiceClient } from "~/grpc/TagServiceClientPb"

post() {
  const client = new TagServiceClient(
    "http://localhost:8080", {}, {}
  )

  const request = new CreateTagRequest()
  var tag = new Tag()
  tag.setTagId(postTag.tagID)
  tag.setTagName(postTag.tagName)
  tag.setStatus(postTag.status)
  request.setTag(tag)
  // TagServiceのcreateTagメソッドにリクエストを送る
  client.createTag(request, {}, (err, res) => {
    if (err != null) {
      console.log(err)
    }
    console.log(res)
  })
}

上記のようにして、クライアント側からタグサービスにタグ作成のリクエストを行った際、次のエラーが起こった。

{ code: 12, message: "unknown service post_grpc.TagService" }

解決策

code: 12 は何の事や...と思って公式のgitを見てみた。

すると、

// HTTP Mapping: 501 Not Implemented
UNIMPLEMENTED = 12;

要するに、
「そのサービス、実装されてないで」 っていう意味の内容だった。

 そこで、呼び出しを行っているサービスがgRPCサーバーに登録されているかどうか確認する。

grpcサーバーの動作確認にはgrpcurlを使うといい。

この記事に色々詳しい事が書かれていた。
https://qiita.com/yukina-ge/items/a84693f01f3f0edba482

例えばポート50051番でgRPCサーバーを構築しているとすると、以下のように叩くと良い。

# ポートに登録されているサービスの一覧
$ grpcurl -plaintext localhost:50051 list
grpc.reflection.v1alpha.ServerReflection
post_grpc.PostService

ServerReflectionとPostServiceは登録されているようだ。
あれ、じゃあTagServiceは...と思ってサーバー側のコード読んだら明らかなミスに気がついた。

server.go
package grpc

import (
    "fmt"
    "log"
    "net"
    "os"
    "os/signal"

    "github.com/yzmw1213/PostService/grpc/post_grpc"
    "github.com/yzmw1213/PostService/usecase/interactor"
    "google.golang.org/grpc"
    "google.golang.org/grpc/reflection"
)

type server struct {
    PostUsecase interactor.PostInteractor
    TagUsecase  interactor.TagInteractor
}

// NewPostGrpcServer gRPCサーバー起動
func NewPostGrpcServer() {
    lis, err := net.Listen("tcp", "0.0.0.0:50051")
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
    }

    server := &server{}

    s := makeServer()
     // PostServiceを serverに登録
    post_grpc.RegisterPostServiceServer(s, server)

     // TagServiceの登録が抜けている!!!


    // Register reflection service on gRPC server.
    reflection.Register(s)
    log.Println("main grpc server has started")

    go func() {
        if err := s.Serve(lis); err != nil {
            log.Fatalf("failed to serve: %v", err)
        }
    }()

    ch := make(chan os.Signal, 1)
    signal.Notify(ch, os.Interrupt)

    // Block until a sgnal is received
    <-ch
    fmt.Println("Stopping the server")
    s.Stop()
    fmt.Println("Closing the client")
    lis.Close()
    fmt.Println("End of Program")
}

func makeServer() *grpc.Server {
    s := grpc.NewServer(
        grpc.UnaryInterceptor(grpc.UnaryServerInterceptor(transmitStatusInterceptor)),
    )

    return s
}

post_grpc.RegisterPostServiceServer で、PostServiceを登録はしているが
TagServiceも同様にgRPCサーバーに登録しなければならない。

次のコードを追加し、解決した。

server.go
     // PostServiceを serverに登録
    post_grpc.RegisterPostServiceServer(s, server)
    // 以下を追加
    // タグサービス登録
    post_grpc.RegisterTagServiceServer(s, server)

振り返り

今回、原因の特定にかなり時間を費やしてしまった。
clientとserverの間にenvoy Proxyを置いており、proxyの方に気を取られまくっていたので。。
悩んであれこれ試した割にはだいぶ初歩的なミスだった...。
grpcurl、これからは初手から使っていこう。

補足

マイクロサービス運用を想定しているgRPCサーバーの構築では、
1 service / 1 server の前提で書かれている記事が数多い印象があるが、必ずしも
全てのサービス毎に細くサーバーを分ける分ける必要もなく、上で実装しているように
それぞれRegisterServiceすれば運用に支障は無いと思っている。

関連性の高いサービス(たとえば、ユーザー登録サービスと認証サービスとか)は
この様に同一サーバーでの運用することが十分可能だと思う。

今後、認証サービスも書く予定なので試してみようと思う。

参考記事

gRPCサーバーの動作確認をgrpcurlでやってみた

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1