{"id":236,"date":"2026-08-29T12:46:23","date_gmt":"2026-08-29T04:46:23","guid":{"rendered":"https:\/\/yykcj.com\/?p=236"},"modified":"2026-08-29T12:46:23","modified_gmt":"2026-08-29T04:46:23","slug":"grpc-%e5%85%a5%e9%97%a8%e4%b8%8e%e5%ae%9e%e6%88%98%ef%bc%9a%e4%bb%8e%e9%9b%b6%e6%9e%84%e5%bb%ba%e9%ab%98%e6%95%88%e5%be%ae%e6%9c%8d%e5%8a%a1%e9%80%9a%e4%bf%a1","status":"publish","type":"post","link":"https:\/\/yykcj.com\/?p=236","title":{"rendered":"gRPC \u5165\u95e8\u4e0e\u5b9e\u6218\uff1a\u4ece\u96f6\u6784\u5efa\u9ad8\u6548\u5fae\u670d\u52a1\u901a\u4fe1"},"content":{"rendered":"<h1>gRPC \u5165\u95e8\u4e0e\u5b9e\u6218\uff1a\u4ece\u96f6\u6784\u5efa\u9ad8\u6548\u5fae\u670d\u52a1\u901a\u4fe1<\/h1>\n<h2>\u5f15\u8a00<\/h2>\n<p>\u5728\u5fae\u670d\u52a1\u67b6\u6784\u76db\u884c\u7684\u4eca\u5929\uff0c\u670d\u52a1\u95f4\u901a\u4fe1\u7684\u6548\u7387\u548c\u7a33\u5b9a\u6027\u81f3\u5173\u91cd\u8981\u3002\u4f20\u7edf\u7684 RESTful API \u57fa\u4e8e HTTP\/1.1\uff0c\u91c7\u7528\u6587\u672c\u683c\u5f0f\uff08\u5982 JSON\uff09\uff0c\u867d\u7136\u7b80\u5355\u6613\u7528\uff0c\u4f46\u5728\u9ad8\u5e76\u53d1\u3001\u4f4e\u5ef6\u8fdf\u7684\u573a\u666f\u4e0b\u9010\u6e10\u66b4\u9732\u51fa\u6027\u80fd\u74f6\u9888\u3002gRPC \u4f5c\u4e3a Google \u5f00\u6e90\u7684\u9ad8\u6027\u80fd RPC \u6846\u67b6\uff0c\u57fa\u4e8e HTTP\/2 \u548c Protocol Buffers\uff0c\u63d0\u4f9b\u4e86\u591a\u8bed\u8a00\u652f\u6301\u3001\u6d41\u5f0f\u4f20\u8f93\u3001\u5f3a\u7c7b\u578b\u63a5\u53e3\u5b9a\u4e49\u7b49\u7279\u6027\uff0c\u6210\u4e3a\u5fae\u670d\u52a1\u901a\u4fe1\u7684\u70ed\u95e8\u9009\u62e9\u3002<\/p>\n<p>\u672c\u6587\u5c06\u901a\u8fc7\u4e00\u4e2a\u5b8c\u6574\u7684\u7528\u6237\u670d\u52a1\u793a\u4f8b\uff0c\u5e26\u4f60\u4ece\u96f6\u5f00\u59cb\u638c\u63e1 gRPC \u7684\u6838\u5fc3\u6982\u5ff5\u3001\u5b9a\u4e49 proto \u6587\u4ef6\u3001\u5b9e\u73b0\u670d\u52a1\u7aef\u548c\u5ba2\u6237\u7aef\uff0c\u5e76\u63a2\u8ba8\u6700\u4f73\u5b9e\u8df5\u4e0e\u5e38\u89c1\u5751\u3002\u65e0\u8bba\u4f60\u662f\u540e\u7aef\u5f00\u53d1\u8fd8\u662f\u524d\u7aef\u5f00\u53d1\u8005\uff0c\u90fd\u80fd\u4ece\u4e2d\u83b7\u5f97\u5b9e\u7528\u7684\u6280\u80fd\u3002<\/p>\n<h2>1. gRPC \u6838\u5fc3\u6982\u5ff5<\/h2>\n<h3>1.1 \u4ec0\u4e48\u662f gRPC\uff1f<\/h3>\n<p>gRPC \u662f\u4e00\u4e2a\u9ad8\u6027\u80fd\u3001\u5f00\u6e90\u7684 RPC\uff08\u8fdc\u7a0b\u8fc7\u7a0b\u8c03\u7528\uff09\u6846\u67b6\uff0c\u7531 Google \u5f00\u53d1\u3002\u5b83\u4f7f\u7528 HTTP\/2 \u4f5c\u4e3a\u4f20\u8f93\u534f\u8bae\uff0cProtocol Buffers\uff08protobuf\uff09\u4f5c\u4e3a\u63a5\u53e3\u5b9a\u4e49\u8bed\u8a00\uff08IDL\uff09\u548c\u6d88\u606f\u5e8f\u5217\u5316\u534f\u8bae\u3002<\/p>\n<h3>1.2 \u6838\u5fc3\u7279\u6027<\/h3>\n<ul>\n<li><strong>HTTP\/2 \u57fa\u7840<\/strong>\uff1a\u652f\u6301\u591a\u8def\u590d\u7528\u3001\u53cc\u5411\u6d41\u3001\u5934\u90e8\u538b\u7f29\uff0c\u964d\u4f4e\u5ef6\u8fdf\u3002<\/li>\n<li><strong>Protocol Buffers<\/strong>\uff1a\u9ad8\u6548\u7684\u4e8c\u8fdb\u5236\u5e8f\u5217\u5316\uff0c\u4f53\u79ef\u5c0f\u3001\u901f\u5ea6\u5feb\u3002<\/li>\n<li><strong>\u5f3a\u7c7b\u578b\u63a5\u53e3<\/strong>\uff1a\u901a\u8fc7 <code>.proto<\/code> \u6587\u4ef6\u5b9a\u4e49\u670d\u52a1\u63a5\u53e3\u548c\u6d88\u606f\u7ed3\u6784\uff0c\u81ea\u52a8\u751f\u6210\u5ba2\u6237\u7aef\u548c\u670d\u52a1\u7aef\u4ee3\u7801\u3002<\/li>\n<li><strong>\u591a\u8bed\u8a00\u652f\u6301<\/strong>\uff1a\u5b98\u65b9\u652f\u6301 Go\u3001Java\u3001Python\u3001C++ \u7b49\u5341\u4f59\u79cd\u8bed\u8a00\u3002<\/li>\n<li><strong>\u6d41\u5f0f\u4f20\u8f93<\/strong>\uff1a\u652f\u6301\u56db\u79cd\u8c03\u7528\u6a21\u5f0f\uff1a\u4e00\u5143\uff08Unary\uff09\u3001\u670d\u52a1\u7aef\u6d41\uff08Server streaming\uff09\u3001\u5ba2\u6237\u7aef\u6d41\uff08Client streaming\uff09\u3001\u53cc\u5411\u6d41\uff08Bidirectional streaming\uff09\u3002<\/li>\n<\/ul>\n<h3>1.3 \u4e0e REST \u5bf9\u6bd4<\/h3>\n<p>| \u7279\u6027 | gRPC | REST | |&#8212;&#8212;|&#8212;&#8212;|&#8212;&#8212;| | \u4f20\u8f93\u534f\u8bae | HTTP\/2 | HTTP\/1.1\/2 | | \u6570\u636e\u683c\u5f0f | \u4e8c\u8fdb\u5236\uff08protobuf\uff09 | \u6587\u672c\uff08JSON\/XML\uff09 | | \u63a5\u53e3\u5b9a\u4e49 | \u5f3a\u7c7b\u578b\uff08.proto\uff09 | \u65e0\u6807\u51c6 | | \u6d41\u5f0f\u652f\u6301 | \u539f\u751f\u652f\u6301 | \u6709\u9650 | | \u6d4f\u89c8\u5668\u652f\u6301 | \u9700\u8981 gRPC-Web | \u539f\u751f\u652f\u6301 |<\/p>\n<h2>2. \u73af\u5883\u51c6\u5907<\/h2>\n<p>\u6211\u4eec\u5c06\u4f7f\u7528 Go \u8bed\u8a00\u8fdb\u884c\u5b9e\u6218\u6f14\u793a\u3002\u786e\u4fdd\u4f60\u5df2\u5b89\u88c5\uff1a<\/p>\n<ul>\n<li>Go 1.16+\uff08\u5efa\u8bae 1.20+\uff09<\/li>\n<li>protoc \u7f16\u8bd1\u5668\uff08Protocol Buffers \u7f16\u8bd1\u5668\uff09<\/li>\n<li>protoc-gen-go \u548c protoc-gen-go-grpc \u63d2\u4ef6<\/li>\n<\/ul>\n<p>\u5b89\u88c5 protoc \u548c\u63d2\u4ef6\uff1a<\/p>\n<pre><code class=\"language-bash\">\n# \u5b89\u88c5 protoc\uff08macOS \u4f7f\u7528 brew\uff0cLinux \u4f7f\u7528 apt \u6216\u4e0b\u8f7d\u4e8c\u8fdb\u5236\uff09\nbrew install protobuf  # macOS\n\n# \u5b89\u88c5 Go \u63d2\u4ef6\ngo install google.golang.org\/protobuf\/cmd\/protoc-gen-go@latest\ngo install google.golang.org\/grpc\/cmd\/protoc-gen-go-grpc@latest\n<\/code><\/pre>\n<p>\u786e\u4fdd <code>$PATH<\/code> \u4e2d\u5305\u542b <code>$GOBIN<\/code>\uff08\u9ed8\u8ba4 <code>$HOME\/go\/bin<\/code>\uff09\u3002<\/p>\n<h2>3. \u5b9a\u4e49 Protocol Buffers \u6587\u4ef6<\/h2>\n<p>\u9996\u5148\uff0c\u521b\u5efa\u9879\u76ee\u76ee\u5f55\u5e76\u521d\u59cb\u5316 Go module\uff1a<\/p>\n<pre><code class=\"language-bash\">\nmkdir grpc-demo &amp;&amp; cd grpc-demo\ngo mod init grpc-demo\n<\/code><\/pre>\n<p>\u521b\u5efa <code>proto\/user.proto<\/code> \u6587\u4ef6\uff1a<\/p>\n<pre><code class=\"language-protobuf\">\nsyntax = &quot;proto3&quot;;\n\npackage user;\n\noption go_package = &quot;grpc-demo\/pb\/user&quot;;\n\n\/\/ \u7528\u6237\u670d\u52a1\u5b9a\u4e49\nservice UserService {\n  \/\/ \u4e00\u5143\u8c03\u7528\uff1a\u83b7\u53d6\u7528\u6237\u4fe1\u606f\n  rpc GetUser (GetUserRequest) returns (GetUserResponse);\n  \/\/ \u670d\u52a1\u7aef\u6d41\uff1a\u83b7\u53d6\u7528\u6237\u5217\u8868\uff08\u6d41\u5f0f\u8fd4\u56de\uff09\n  rpc ListUsers (ListUsersRequest) returns (stream User);\n  \/\/ \u5ba2\u6237\u7aef\u6d41\uff1a\u6279\u91cf\u521b\u5efa\u7528\u6237\uff08\u6d41\u5f0f\u53d1\u9001\uff09\n  rpc CreateUsers (stream CreateUserRequest) returns (CreateUsersResponse);\n  \/\/ \u53cc\u5411\u6d41\uff1a\u804a\u5929\u5f0f\u4ea4\u4e92\n  rpc Chat (stream ChatMessage) returns (stream ChatMessage);\n}\n\n\/\/ \u8bf7\u6c42\u6d88\u606f\nmessage GetUserRequest {\n  int32 id = 1;\n}\n\nmessage GetUserResponse {\n  User user = 1;\n}\n\nmessage ListUsersRequest {\n  int32 page = 1;\n  int32 page_size = 2;\n}\n\nmessage CreateUserRequest {\n  string name = 1;\n  string email = 2;\n}\n\nmessage CreateUsersResponse {\n  repeated User users = 1;\n}\n\nmessage ChatMessage {\n  string message = 1;\n}\n\n\/\/ \u7528\u6237\u5b9e\u4f53\nmessage User {\n  int32 id = 1;\n  string name = 2;\n  string email = 3;\n}\n<\/code><\/pre>\n<h2>4. \u751f\u6210\u4ee3\u7801<\/h2>\n<p>\u5728\u9879\u76ee\u6839\u76ee\u5f55\u6267\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">\nmkdir -p pb\/user\nprotoc --go_out=. --go-grpc_out=. proto\/user.proto\n<\/code><\/pre>\n<p>\u8fd9\u4f1a\u751f\u6210 <code>pb\/user\/user.pb.go<\/code>\uff08\u6d88\u606f\u5b9a\u4e49\uff09\u548c <code>pb\/user\/user_grpc.pb.go<\/code>\uff08\u670d\u52a1\u63a5\u53e3\u548c\u5ba2\u6237\u7aef\uff09\u3002<\/p>\n<h2>5. \u5b9e\u73b0 gRPC \u670d\u52a1\u7aef<\/h2>\n<p>\u521b\u5efa <code>server\/main.go<\/code>\uff0c\u5b9e\u73b0 <code>UserService<\/code> \u63a5\u53e3\uff1a<\/p>\n<pre><code class=\"language-go\">\npackage main\n\nimport (\n    &quot;context&quot;\n    &quot;log&quot;\n    &quot;net&quot;\n    &quot;strconv&quot;\n    &quot;sync&quot;\n\n    &quot;google.golang.org\/grpc&quot;\n    &quot;google.golang.org\/grpc\/codes&quot;\n    &quot;google.golang.org\/grpc\/status&quot;\n\n    pb &quot;grpc-demo\/pb\/user&quot;\n)\n\ntype userServer struct {\n    pb.UnimplementedUserServiceServer\n    mu    sync.Mutex\n    users map[int32]*pb.User\n    nextID int32\n}\n\nfunc newUserServer() *userServer {\n    return &amp;userServer{\n        users: make(map[int32]*pb.User),\n        nextID: 1,\n    }\n}\n\n\/\/ \u4e00\u5143\u8c03\u7528\uff1a\u83b7\u53d6\u7528\u6237\nfunc (s *userServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    user, ok := s.users[req.Id]\n    if !ok {\n        return nil, status.Errorf(codes.NotFound, &quot;user %d not found&quot;, req.Id)\n    }\n    return &amp;pb.GetUserResponse{User: user}, nil\n}\n\n\/\/ \u670d\u52a1\u7aef\u6d41\uff1a\u5217\u51fa\u6240\u6709\u7528\u6237\nfunc (s *userServer) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    for _, user := range s.users {\n        if err := stream.Send(user); err != nil {\n            return err\n        }\n    }\n    return nil\n}\n\n\/\/ \u5ba2\u6237\u7aef\u6d41\uff1a\u6279\u91cf\u521b\u5efa\u7528\u6237\nfunc (s *userServer) CreateUsers(stream pb.UserService_CreateUsersServer) error {\n    var created []*pb.User\n    for {\n        req, err := stream.Recv()\n        if err == io.EOF {\n            return stream.SendAndClose(&amp;pb.CreateUsersResponse{Users: created})\n        }\n        if err != nil {\n            return err\n        }\n        s.mu.Lock()\n        user := &amp;pb.User{\n            Id:    s.nextID,\n            Name:  req.Name,\n            Email: req.Email,\n        }\n        s.users[user.Id] = user\n        s.nextID++\n        created = append(created, user)\n        s.mu.Unlock()\n    }\n}\n\n\/\/ \u53cc\u5411\u6d41\uff1a\u7b80\u5355\u804a\u5929\nfunc (s *userServer) Chat(stream pb.UserService_ChatServer) error {\n    for {\n        in, err := stream.Recv()\n        if err == io.EOF {\n            return nil\n        }\n        if err != nil {\n            return err\n        }\n        \/\/ \u56de\u663e\u6d88\u606f\n        if err := stream.Send(&amp;pb.ChatMessage{Message: &quot;echo: &quot; + in.Message}); err != nil {\n            return err\n        }\n    }\n}\n\nfunc main() {\n    lis, err := net.Listen(&quot;tcp&quot;, &quot;:50051&quot;)\n    if err != nil {\n        log.Fatalf(&quot;failed to listen: %v&quot;, err)\n    }\n    s := grpc.NewServer()\n    pb.RegisterUserServiceServer(s, newUserServer())\n    log.Println(&quot;gRPC server listening on :50051&quot;)\n    if err := s.Serve(lis); err != nil {\n        log.Fatalf(&quot;failed to serve: %v&quot;, err)\n    }\n}\n<\/code><\/pre>\n<p><strong>\u6ce8\u610f<\/strong>\uff1a\u9700\u8981\u5bfc\u5165 <code>io<\/code> \u5305\uff0c\u5e76\u5728 <code>CreateUsers<\/code> \u548c <code>Chat<\/code> \u4e2d\u4f7f\u7528\u3002<\/p>\n<h2>6. \u5b9e\u73b0 gRPC \u5ba2\u6237\u7aef<\/h2>\n<p>\u521b\u5efa <code>client\/main.go<\/code>\uff1a<\/p>\n<pre><code class=\"language-go\">\npackage main\n\nimport (\n    &quot;context&quot;\n    &quot;io&quot;\n    &quot;log&quot;\n    &quot;time&quot;\n\n    &quot;google.golang.org\/grpc&quot;\n    &quot;google.golang.org\/grpc\/credentials\/insecure&quot;\n\n    pb &quot;grpc-demo\/pb\/user&quot;\n)\n\nfunc main() {\n    \/\/ \u8fde\u63a5\u670d\u52a1\u7aef\n    conn, err := grpc.NewClient(&quot;localhost:50051&quot;, grpc.WithTransportCredentials(insecure.NewCredentials()))\n    if err != nil {\n        log.Fatalf(&quot;did not connect: %v&quot;, err)\n    }\n    defer conn.Close()\n\n    client := pb.NewUserServiceClient(conn)\n    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)\n    defer cancel()\n\n    \/\/ 1. \u4e00\u5143\u8c03\u7528\uff1a\u83b7\u53d6\u7528\u6237\uff08\u5148\u521b\u5efa\u518d\u83b7\u53d6\uff09\n    \/\/ \u521b\u5efa\u7528\u6237\n    createResp, err := client.CreateUsers(ctx)\n    if err != nil {\n        log.Fatalf(&quot;could not create stream: %v&quot;, err)\n    }\n    \/\/ \u53d1\u9001\u4e24\u4e2a\u521b\u5efa\u8bf7\u6c42\n    for _, name := range []string{&quot;Alice&quot;, &quot;Bob&quot;} {\n        if err := createResp.Send(&amp;pb.CreateUserRequest{Name: name, Email: name + &quot;@example.com&quot;}); err != nil {\n            log.Fatalf(&quot;could not send: %v&quot;, err)\n        }\n    }\n    created, err := createResp.CloseAndRecv()\n    if err != nil {\n        log.Fatalf(&quot;could not receive response: %v&quot;, err)\n    }\n    log.Printf(&quot;Created users: %v\\n&quot;, created.Users)\n\n    \/\/ \u83b7\u53d6\u7b2c\u4e00\u4e2a\u7528\u6237\n    if len(created.Users) &gt; 0 {\n        getResp, err := client.GetUser(ctx, &amp;pb.GetUserRequest{Id: created.Users[0].Id})\n        if err != nil {\n            log.Fatalf(&quot;could not get user: %v&quot;, err)\n        }\n        log.Printf(&quot;GetUser: %v\\n&quot;, getResp.User)\n    }\n\n    \/\/ 2. \u670d\u52a1\u7aef\u6d41\uff1a\u5217\u51fa\u6240\u6709\u7528\u6237\n    listStream, err := client.ListUsers(ctx, &amp;pb.ListUsersRequest{Page: 1, PageSize: 10})\n    if err != nil {\n        log.Fatalf(&quot;could not list users: %v&quot;, err)\n    }\n    for {\n        user, err := listStream.Recv()\n        if err == io.EOF {\n            break\n        }\n        if err != nil {\n            log.Fatalf(&quot;list stream error: %v&quot;, err)\n        }\n        log.Printf(&quot;Listed user: %v\\n&quot;, user)\n    }\n\n    \/\/ 3. \u53cc\u5411\u6d41\uff1a\u804a\u5929\n    chatStream, err := client.Chat(ctx)\n    if err != nil {\n        log.Fatalf(&quot;could not start chat: %v&quot;, err)\n    }\n    \/\/ \u53d1\u9001\u6d88\u606f\n    go func() {\n        for i := 0; i &lt; 3; i++ {\n            if err := chatStream.Send(&amp;pb.ChatMessage{Message: &quot;hello &quot; + strconv.Itoa(i)}); err != nil {\n                log.Fatalf(&quot;chat send error: %v&quot;, err)\n            }\n            time.Sleep(time.Second)\n        }\n        chatStream.CloseSend()\n    }()\n    \/\/ \u63a5\u6536\u54cd\u5e94\n    for {\n        msg, err := chatStream.Recv()\n        if err == io.EOF {\n            break\n        }\n        if err != nil {\n            log.Fatalf(&quot;chat recv error: %v&quot;, err)\n        }\n        log.Printf(&quot;Chat echo: %s\\n&quot;, msg.Message)\n    }\n}\n<\/code><\/pre>\n<p>\u6ce8\u610f\uff1a<code>grpc.NewClient<\/code> \u662f\u8f83\u65b0\u7248\u672c\u7684\u65b9\u6cd5\uff0c\u65e7\u7248\u672c\u4f7f\u7528 <code>grpc.Dial<\/code>\u3002\u786e\u4fdd\u4f60\u7684 grpc-go \u7248\u672c\u8f83\u65b0\u3002<\/p>\n<h2>7. \u8fd0\u884c\u4e0e\u6d4b\u8bd5<\/h2>\n<p>\u542f\u52a8\u670d\u52a1\u7aef\uff1a<\/p>\n<pre><code class=\"language-bash\">\ngo run server\/main.go\n<\/code><\/pre>\n<p>\u53e6\u5f00\u7ec8\u7aef\u8fd0\u884c\u5ba2\u6237\u7aef\uff1a<\/p>\n<pre><code class=\"language-bash\">\ngo run client\/main.go\n<\/code><\/pre>\n<p>\u89c2\u5bdf\u8f93\u51fa\uff0c\u5e94\u80fd\u770b\u5230\u521b\u5efa\u7684\u7528\u6237\u3001\u5217\u8868\u548c\u804a\u5929\u56de\u663e\u3002<\/p>\n<h2>8. \u6700\u4f73\u5b9e\u8df5\u4e0e\u5e38\u89c1\u5751<\/h2>\n<h3>8.1 \u6700\u4f73\u5b9e\u8df5<\/h3>\n<ul>\n<li><strong>\u4f7f\u7528 context \u63a7\u5236\u8d85\u65f6<\/strong>\uff1a\u5728\u5ba2\u6237\u7aef\u8c03\u7528\u65f6\u603b\u662f\u4f20\u9012 context\uff0c\u907f\u514d\u963b\u585e\u3002<\/li>\n<li><strong>\u9519\u8bef\u5904\u7406<\/strong>\uff1a\u4f7f\u7528 <code>status<\/code> \u5305\u8fd4\u56de\u6807\u51c6 gRPC \u9519\u8bef\u7801\uff0c\u4fbf\u4e8e\u5ba2\u6237\u7aef\u5904\u7406\u3002<\/li>\n<li><strong>\u6d41\u5f0f\u8c03\u7528\u6ce8\u610f\u8d44\u6e90\u91ca\u653e<\/strong>\uff1a\u786e\u4fdd\u6d41\u88ab\u5173\u95ed\uff0c\u907f\u514d\u8d44\u6e90\u6cc4\u6f0f\u3002<\/li>\n<li><strong>\u8fde\u63a5\u590d\u7528<\/strong>\uff1a\u5ba2\u6237\u7aef\u5e94\u590d\u7528\u8fde\u63a5\uff0c\u4e0d\u8981\u9891\u7e41\u521b\u5efa\u3002<\/li>\n<li><strong>\u4f7f\u7528\u62e6\u622a\u5668<\/strong>\uff1a\u5b9e\u73b0\u65e5\u5fd7\u3001\u8ba4\u8bc1\u3001\u76d1\u63a7\u7b49\u6a2a\u5207\u5173\u6ce8\u70b9\u3002<\/li>\n<\/ul>\n<h3>8.2 \u5e38\u89c1\u5751<\/h3>\n<ul>\n<li><strong>proto \u6587\u4ef6\u8def\u5f84\u9519\u8bef<\/strong>\uff1a\u751f\u6210\u4ee3\u7801\u65f6\u786e\u4fdd <code>--go<em>out<\/code> \u548c <code>--go-grpc<\/em>out<\/code> \u8def\u5f84\u6b63\u786e\u3002<\/li>\n<li><strong>\u7248\u672c\u4e0d\u517c\u5bb9<\/strong>\uff1aprotoc-gen-go \u548c grpc \u7248\u672c\u9700\u8981\u5339\u914d\uff0c\u5426\u5219\u53ef\u80fd\u751f\u6210\u9519\u8bef\u4ee3\u7801\u3002<\/li>\n<li><strong>\u672a\u5b9e\u73b0\u6240\u6709\u65b9\u6cd5<\/strong>\uff1a\u5d4c\u5165 <code>UnimplementedUserServiceServer<\/code> \u4ee5\u907f\u514d\u672a\u5b9e\u73b0\u7684\u65b9\u6cd5\u5bfc\u81f4 panic\u3002<\/li>\n<li><strong>\u8fde\u63a5\u5931\u8d25<\/strong>\uff1a\u68c0\u67e5\u670d\u52a1\u7aef\u5730\u5740\u548c\u7aef\u53e3\uff0c\u786e\u4fdd\u7f51\u7edc\u53ef\u8fbe\u3002<\/li>\n<\/ul>\n<h2>9. \u8fdb\u9636\uff1agRPC-Web \u4e0e\u524d\u7aef\u96c6\u6210<\/h2>\n<p>\u5bf9\u4e8e\u524d\u7aef\uff0cgRPC \u4e0d\u80fd\u76f4\u63a5\u5728\u6d4f\u89c8\u5668\u4e2d\u4f7f\u7528\uff0c\u9700\u8981 gRPC-Web \u4ee3\u7406\u3002\u53ef\u4ee5\u4f7f\u7528 Envoy \u6216 gRPC-Web \u5e93\u3002\u8fd9\u91cc\u7b80\u8981\u4ecb\u7ecd\uff1a<\/p>\n<ol>\n<li>\u90e8\u7f72\u4e00\u4e2a gRPC-Web \u4ee3\u7406\uff08\u5982 Envoy\uff09\u5c06 HTTP\/1.1 \u8f6c\u6362\u4e3a HTTP\/2\u3002<\/li>\n<li>\u524d\u7aef\u4f7f\u7528 <code>@grpc\/grpc-js<\/code> \u7684 Web \u7248\u672c\u6216 <code>grpc-web<\/code> \u5e93\u3002<\/li>\n<\/ol>\n<p>\u793a\u4f8b\uff08\u4f7f\u7528 grpc-web \u5e93\uff09\uff1a<\/p>\n<pre><code class=\"language-javascript\">\n\/\/ \u524d\u7aef\u4ee3\u7801\u793a\u4f8b\nconst { GetUserRequest } = require(&#x27;.\/user_pb.js&#x27;);\nconst { UserServiceClient } = require(&#x27;.\/user_grpc_web_pb.js&#x27;);\n\nconst client = new UserServiceClient(&#x27;http:\/\/localhost:8080&#x27;);\nconst req = new GetUserRequest();\nreq.setId(1);\nclient.getUser(req, {}, (err, response) =&gt; {\n  if (err) console.error(err);\n  else console.log(response.getUser());\n});\n<\/code><\/pre>\n<h2>\u603b\u7ed3<\/h2>\n<p>\u901a\u8fc7\u672c\u6587\u7684\u5b9e\u6218\uff0c\u4f60\u5df2\u7ecf\u638c\u63e1\u4e86 gRPC \u7684\u6838\u5fc3\u6982\u5ff5\u548c\u56db\u79cd\u8c03\u7528\u6a21\u5f0f\uff0c\u5e76\u6210\u529f\u5b9e\u73b0\u4e86\u4e00\u4e2a\u7528\u6237\u670d\u52a1\u3002gRPC \u7684\u9ad8\u6027\u80fd\u548c\u5f3a\u7c7b\u578b\u7279\u6027\u4f7f\u5176\u6210\u4e3a\u5fae\u670d\u52a1\u901a\u4fe1\u7684\u7406\u60f3\u9009\u62e9\u3002\u4e0b\u4e00\u6b65\uff0c\u4f60\u53ef\u4ee5\u5c1d\u8bd5\u96c6\u6210 gRPC \u62e6\u622a\u5668\u3001\u6dfb\u52a0 TLS \u52a0\u5bc6\u3001\u6216\u4f7f\u7528\u670d\u52a1\u53d1\u73b0\uff08\u5982 etcd\u3001Consul\uff09\u3002<\/p>\n<p>\u5ef6\u4f38\u9605\u8bfb\uff1a<\/p>\n<ul>\n<li><a href=\"https:\/\/grpc.io\/docs\/\">gRPC \u5b98\u65b9\u6587\u6863<\/a><\/li>\n<li><a href=\"https:\/\/grpc.io\/docs\/languages\/go\/\">Go \u8bed\u8a00 gRPC \u6559\u7a0b<\/a><\/li>\n<li><a href=\"https:\/\/grpc.io\/docs\/platforms\/web\/\">gRPC-Web \u6587\u6863<\/a><\/li>\n<\/ul>\n<p>\u5e0c\u671b\u8fd9\u7bc7\u6587\u7ae0\u80fd\u5e2e\u52a9\u4f60\u5728\u5b9e\u9645\u9879\u76ee\u4e2d\u66f4\u597d\u5730\u5e94\u7528 gRPC\uff01<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u672c\u6587\u5e26\u4f60\u4ece\u96f6\u5f00\u59cb\u638c\u63e1 gRPC\uff0c\u6db5\u76d6\u6838\u5fc3\u6982\u5ff5\u3001Protocol Buffers \u5b9a\u4e49\u3001\u670d\u52a1\u5b9e\u73b0\u4e0e\u8c03\u7528\uff0c\u5e76\u901a\u8fc7\u4e00\u4e2a\u7528\u6237\u670d\u52a1\u7684\u5b9e\u6218\u6848\u4f8b\uff0c\u5c55\u793a gRPC \u5728\u5fae\u670d\u52a1\u67b6\u6784\u4e2d\u7684\u5e94\u7528\u4e0e\u6700\u4f73\u5b9e\u8df5\u3002<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,4],"tags":[92,203,204,205,55],"class_list":["post-236","post","type-post","status-publish","format-standard","hentry","category-3","category-4","tag-go","tag-grpc","tag-protocol-buffers","tag-rpc","tag-55"],"_links":{"self":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/posts\/236","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=236"}],"version-history":[{"count":0,"href":"https:\/\/yykcj.com\/index.php?rest_route=\/wp\/v2\/posts\/236\/revisions"}],"wp:attachment":[{"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=236"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=236"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/yykcj.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=236"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}