grpc 入門(一)--hello world


 

  • 一,從rpc接口的定義說起,下面給一個最簡單的grpc示例--hello world

 

在這個rpc橫行的世界里,實現一個rpc很重要的一件事就是定義一個好接口,一個好的接口定義會讓你省去很多麻煩。熟悉protobuf的人應該知道它所用的結構體都是用.proto文件來描述的:

// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  //這是一個單例模式,就是一次請求一次應答,即為1:1
//下面我們會介紹 帶有stream的rpc接口,N:N rpc SayHello (HelloRequest) returns (HelloReply) {} }
// The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; }

我們按照編寫規則定義好rpc后,使用 protoc工具按照對應語言版本生成代碼包,我們是grpc要在go語言中應用,自然要編譯成golang格式:

#go:generate 
protoc -I ../helloworld --go_out=plugins=grpc:../helloworld ../helloworld/helloworld.proto

上面的接口看起來寥寥數行,但是生成出來的代碼很長--helloworld.proto.go:

package helloworld

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

import (
    context "golang.org/x/net/context"
    grpc "google.golang.org/grpc"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package

// The request message containing the user's name.
type HelloRequest struct {
    Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}

func (m *HelloRequest) Reset()                    { *m = HelloRequest{} }
func (m *HelloRequest) String() string            { return proto.CompactTextString(m) }
func (*HelloRequest) ProtoMessage()               {}
func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }

func (m *HelloRequest) GetName() string {
    if m != nil {
        return m.Name
    }
    return ""
}

// The response message containing the greetings
type HelloReply struct {
    Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
}

func (m *HelloReply) Reset()                    { *m = HelloReply{} }
func (m *HelloReply) String() string            { return proto.CompactTextString(m) }
func (*HelloReply) ProtoMessage()               {}
func (*HelloReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }

func (m *HelloReply) GetMessage() string {
    if m != nil {
        return m.Message
    }
    return ""
}

func init() {
    proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest")
    proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply")
}

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn

// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4

// Client API for Greeter service

type GreeterClient interface {
    // Sends a greeting
    SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}

type greeterClient struct {
    cc *grpc.ClientConn
}

func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
    return &greeterClient{cc}
}

func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
    out := new(HelloReply)
    err := grpc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, c.cc, opts...)
    if err != nil {
        return nil, err
    }
    return out, nil
}

// Server API for Greeter service

type GreeterServer interface {
    // Sends a greeting
    SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}

func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
    s.RegisterService(&_Greeter_serviceDesc, srv) }

func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
    in := new(HelloRequest)
    if err := dec(in); err != nil {
        return nil, err
    }
    if interceptor == nil {
        return srv.(GreeterServer).SayHello(ctx, in)
    }
    info := &grpc.UnaryServerInfo{
        Server:     srv,
        FullMethod: "/helloworld.Greeter/SayHello",
    }
    handler := func(ctx context.Context, req interface{}) (interface{}, error) {
        return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
    }
    return interceptor(ctx, in, info, handler)
}

var _Greeter_serviceDesc = grpc.ServiceDesc{
    ServiceName: "helloworld.Greeter",
    HandlerType: (*GreeterServer)(nil),
    Methods: []grpc.MethodDesc{
        {
            MethodName: "SayHello",
            Handler:    _Greeter_SayHello_Handler,
        },
    },
    Streams:  []grpc.StreamDesc{},
    Metadata: "helloworld.proto",
}

func init() { proto.RegisterFile("helloworld.proto", fileDescriptor0) }

var fileDescriptor0 = []byte{
    // 175 bytes of a gzipped FileDescriptorProto
    0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9,
    0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88,
    0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42,
    0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92,
    0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71,
    0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a,
    0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64,
    0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x14, 0xe4, 0x54, 0x2a, 0x31, 0x38, 0x19, 0x70, 0x49, 0x67, 0xe6,
    0xeb, 0xa5, 0x17, 0x15, 0x24, 0xeb, 0xa5, 0x56, 0x24, 0xe6, 0x16, 0xe4, 0xa4, 0x16, 0x23, 0xa9,
    0x75, 0xe2, 0x07, 0x2b, 0x0e, 0x07, 0xb1, 0x03, 0x40, 0x5e, 0x0a, 0x60, 0x4c, 0x62, 0x03, 0xfb,
    0xcd, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xb7, 0xcd, 0xf2, 0xef, 0x00, 0x00, 0x00,
}

這時用protoc工具自動生成,你可以去修改,但是關鍵性的接口一定要吻合,而且它自動生成的代碼效率較高,沒有特殊情況下無需要修改。而且我們仔細觀察可以發現它生成接口自動分為server和client兩種模式,我們先分析一下服務端。

  • 服務端解析

其中server端比較關鍵的一個函數舊時注冊服務的函數 RegisterGreeterServer,我們已經將它標紅了,這個函數就是我們在main函數中調用注冊服務實例的函數

// Server API for Greeter service

type GreeterServer interface {
    // Sends a greeting
    SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}

這個接口就是我們需要實現的接口,這個不難理解,就像是c++里的callback函數一樣,我們現在看看這個注冊函數:

func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
    s.RegisterService(&_Greeter_serviceDesc, srv)
}

除了我們自己實現的接口之外,還有一個ServiceDesc:

var _Greeter_serviceDesc = grpc.ServiceDesc{
    ServiceName: "helloworld.Greeter",
    HandlerType: (*GreeterServer)(nil),
    Methods: []grpc.MethodDesc{
        {
            MethodName: "SayHello",
            Handler:    _Greeter_SayHello_Handler,
        },
    },
    Streams:  []grpc.StreamDesc{},
    Metadata: "helloworld.proto",
}

里面包含了注冊服務的名字(MethodName)和操作的Handler:

func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
    in := new(HelloRequest)
    if err := dec(in); err != nil {
        return nil, err
    }
    if interceptor == nil {
        return srv.(GreeterServer).SayHello(ctx, in)
    }
    info := &grpc.UnaryServerInfo{
        Server:     srv,
        FullMethod: "/helloworld.Greeter/SayHello",
    }
    handler := func(ctx context.Context, req interface{}) (interface{}, error) {
        return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
    }
    return interceptor(ctx, in, info, handler)
}

在里面它會有調用到我們實現的接口 SayHello,簡單的包含關系如下:

grpc.Server---
            |--grpc.ServiceDesc---
|--Handler |--Customer Interface
  • 客戶端解析

我們分析完了服務端再分析一下客戶端,客戶端很簡單,只有一個發送函數:

func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
    out := new(HelloReply)
    err := grpc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, c.cc, opts...)
    if err != nil {
        return nil, err
    }
    return out, nil
}

發送后自動返回Reply,這個我們就不深入解讀了,下面說一下我們生成的包怎么用?

  • 服務端實現
//go:generate protoc -I ../helloworld --go_out=plugins=grpc:../helloworld ../helloworld/helloworld.proto

package main

import (
    "log"
    "net"

    "golang.org/x/net/context"
    "google.golang.org/grpc" pb "google.golang.org/grpc/examples/helloworld/helloworld" "google.golang.org/grpc/reflection" ) const ( port = ":50051" ) // server is used to implement helloworld.GreeterServer. type server struct{} // SayHello implements helloworld.GreeterServer func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: "Hello " + in.Name}, nil } func main() {
  #創建一個net.Listener對象,指定協議和端口號 lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) }
  #創建一個空白的grpc server s := grpc.NewServer()
  #注冊服務對應的實例 pb.RegisterGreeterServer(s, &server{}) // Register reflection service on gRPC server. reflection.Register(s)
  #啟動grpc服務 if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } }

這段代碼里面的grpc.NewServer沒有填入任何的ServerOption,可以說是最簡單的方式了。

 

  • 客戶端實現
package main

import (
    "log"
    "os"

    "golang.org/x/net/context"
    "google.golang.org/grpc"
    pb "google.golang.org/grpc/examples/helloworld/helloworld"
)

const (
    address     = "localhost:50051"
    defaultName = "world"
)

func main() {
    // Set up a connection to the server.
    conn, err := grpc.Dial(address, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewGreeterClient(conn)

    // Contact the server and print out its response.
    name := defaultName
    if len(os.Args) > 1 {
        name = os.Args[1]
    }
    r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.Message)
}

 

工程的下載地址:https://github.com/grpc/grpc-go/tree/master/examples

 下附一些grpc api的簡介:

package credentials

import "google.golang.org/grpc/credentials"

Package credentials implements various credentials supported by gRPC library, which encapsulate all the state needed by a client to authenticate with a server and make various assertions, e.g., about the client's identity, role, or whether it is authorized to make a particular call.

數據包憑證(Package credentials)實現了gRPC庫所支持的各種證書,它為了便與服務器進行認證,封裝了客戶端所需的所有狀態,並作出各種斷言(例如關於客戶端的身份,角色或者是否被授權進行特定的呼叫)。

func Creds

Creds returns a ServerOption that sets credentials for server connections.

Creds函數返回一個ServerOption, 用於設置服務器鏈接證書

func WithTransportCredentials

func WithTransportCredentials(creds credentials.TransportCredentials) DialOption

WithTransportCredentials returns a DialOption which configures a connection level security credentials (e.g., TLS/SSL).

WithTransportCredentials函數返回一個 DialOption,用於配置一個傳輸層的安全證書

func NewServer

func NewServer(opt ...ServerOption) *Server

NewServer creates a gRPC server which has no service registered and has not started to accept requests yet.

NewServer 創建一個沒有注冊service,也沒有啟動去接收請求的gRpc服務。

如果沒有輸入任何ServerOption那它就是一個沒有任何權限限制的gRpc.

func (*Server) Serve

func (s *Server) Serve(lis net.Listener) error

Serve accepts incoming connections on the listener lis, creating a new ServerTransport and service goroutine for each. The service goroutines read gRPC requests and then call the registered handlers to reply to them. Serve returns when lis.Accept fails with fatal errors. lis will be closed when this method returns. Serve will return a non-nil error unless Stop or GracefulStop is called.

Serve接受監聽器上的傳入連接,為每個服務器創建一個新的ServerTransport和服務配置。服務例程讀取gRPC請求,然後調用註冊的處理程序來回復它們。服務返回lis.Accept失敗,致命錯誤。當此方法返回時,lis將被關閉。 Serve將返回一個非零錯誤,除非Stop或GracefulStop被調用。

func (*Server) RegisterService

func (s *Server) RegisterService(sd *ServiceDesc, ss interface{})

RegisterService registers a service and its implementation to the gRPC server. It is called from the IDL generated code. This must be called before invoking Serve.

RegisterService將服務及其實現註冊到gRPC服務器。它是從IDL生成的代碼中調用的。這必須在調用Serve之前調用。

 

func Dial

func Dial(target string, opts ...DialOption) (*ClientConn, error)

Dial creates a client connection to the given target.

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM