如何使用grpc

第一步,先把環境安裝好

官網的安裝命令:

go get -u google.golang.org/grpc

貌似用不了,連不上服務器,即便我掛上vpn也沒有用,沒辦法只有迂迴安裝了, 反正代碼在github上都有,就從github上clone下來, 需要的庫包括grpc-go, golang/net, golang/text, protobuf/proto, protobuf/protoc-gen-go, google/go-genproto

# 如果已經安裝了proto(編譯proto文件的工具)和protoc-gen-go(proto將proto文件編譯成go語言形式的插件)的話就不用安裝了
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

# 下載grpc-go
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc

# 下載golang/net
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net

# 下載golang/text
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text

# 下載go-genproto
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto

# 安裝
cd $GOPATH/src/
go install google.golang.org/grpc

 

第二步,編寫proto文件

構造message,message是參數,一般有請求參數和響應參數

寫一個接口,接口中的方法供客戶端和服務端來調用

$GOPATH/src/helloworld/helloworld/helloworld.proto代碼:

syntax = "proto3";

package helloworld;

//定義服務
service Greeter{ //定義了這麼一個接口,protoc這個工具將會生成兩個,一個是client一個是server,因爲這是一個rpc接口,裏面的方法SayHello,客戶端會調用這個方法,調用的這個請求會發送給服務端,服務端那邊也要實現,服務端那邊實現,客戶端這邊用
    rpc SayHello(HelloRequest) returns(HelloReply){}; //接口中的方法
}
message HelloRequest{ //方法的請求參數
    string name = 1;
}
message HelloReply{ //方法的響應參數
    string message = 1;
}

通過protoc生成go代碼:

$GOPATH/src/helloworld/helloworld/helloworld.pb.go代碼

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: helloworld/helloworld.proto

package helloworld

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/proto"
	"google.golang.org/grpc"
	"math"
)

// 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.ProtoPackageIsVersion3 // please upgrade the proto package

type HelloRequest struct {
	Name                 string   `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

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

func (m *HelloRequest) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_HelloRequest.Unmarshal(m, b)
}
func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic)
}
func (m *HelloRequest) XXX_Merge(src proto.Message) {
	xxx_messageInfo_HelloRequest.Merge(m, src)
}
func (m *HelloRequest) XXX_Size() int {
	return xxx_messageInfo_HelloRequest.Size(m)
}
func (m *HelloRequest) XXX_DiscardUnknown() {
	xxx_messageInfo_HelloRequest.DiscardUnknown(m)
}

var xxx_messageInfo_HelloRequest proto.InternalMessageInfo

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

type HelloReply struct {
	Message              string   `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

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

func (m *HelloReply) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_HelloReply.Unmarshal(m, b)
}
func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic)
}
func (m *HelloReply) XXX_Merge(src proto.Message) {
	xxx_messageInfo_HelloReply.Merge(m, src)
}
func (m *HelloReply) XXX_Size() int {
	return xxx_messageInfo_HelloReply.Size(m)
}
func (m *HelloReply) XXX_DiscardUnknown() {
	xxx_messageInfo_HelloReply.DiscardUnknown(m)
}

var xxx_messageInfo_HelloReply proto.InternalMessageInfo

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")
}

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

var fileDescriptor_73149fedf49f4319 = []byte{
	// 144 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xce, 0x48, 0xcd, 0xc9,
	0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x47, 0x30, 0xf5, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85,
	0xb8, 0x10, 0x22, 0x4a, 0x4a, 0x5c, 0x3c, 0x1e, 0x20, 0x5e, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71,
	0x89, 0x90, 0x10, 0x17, 0x4b, 0x5e, 0x62, 0x6e, 0xaa, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10,
	0x98, 0xad, 0xa4, 0xc6, 0xc5, 0x05, 0x55, 0x53, 0x90, 0x53, 0x29, 0x24, 0xc1, 0xc5, 0x9e, 0x9b,
	0x5a, 0x5c, 0x9c, 0x98, 0x0e, 0x53, 0x04, 0xe3, 0x1a, 0x79, 0x72, 0xb1, 0xbb, 0x17, 0xa5, 0xa6,
	0x96, 0xa4, 0x16, 0x09, 0xd9, 0x71, 0x71, 0x04, 0x27, 0x56, 0x82, 0x75, 0x09, 0x49, 0xe8, 0x21,
	0xb9, 0x00, 0xd9, 0x32, 0x29, 0x31, 0x2c, 0x32, 0x05, 0x39, 0x95, 0x4a, 0x0c, 0x49, 0x6c, 0x60,
	0x97, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xea, 0x27, 0x0c, 0x07, 0xc8, 0x00, 0x00, 0x00,
}

// 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

// GreeterClient is the client API for Greeter service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type GreeterClient interface {
	SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}

// greeterClient已經實現了那個接口,到時候調用SayHello直接掉就行了
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 := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...) //客戶端調用完SayHello自動就會往grpc上發
	if err != nil {
		return nil, err
	}
	return out, nil
}

// GreeterServer is the server API for Greeter service.
type GreeterServer interface {
	//服務端怎麼實現這個接口它根本就不知道怎麼實現,服務端向讓它實現什麼它就實現什麼,比如接收了SayHello了怎麼處理,完了怎麼回覆,這個工具是不知道,這個是留給我們來實現的,這個是代碼的核心邏輯
	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/helloworld.proto",
}

第三步,實現服務端

首先定義一個結構體,這個結構體要實現上面的接口,實現了這個接口才能對外提供服務,參數請求

然後就可以啓動主程序了

$GOPATH/src/helloworld/greeter_server/main.go代碼:

package main

import (
	"golang.org/x/net/context"
	"google.golang.org/grpc/examples/helloworld/helloworld"
	"fmt"
	"net"
	"log"
	"google.golang.org/grpc"
)

const(
	port = ":5001"
)

type service struct{ //用來實現前面生成的接口

}

//對外提供一個供客戶端調用的函數,只要客戶端按照我這裏要求傳參,我就能對外提供一個我這裏規定返回數據
func (s *service)SayHello(ctx context.Context, r *helloworld.HelloRequest) (*helloworld.HelloReply, error){ //通過Alt+Enter加入引入的包
	fmt.Println(r.Name)
	return &helloworld.HelloReply{Message:"hello," + r.Name},nil
}

func main(){
	lis,err := net.Listen("tcp", port) //監聽請求

	if err != nil{
		log.Fatal("fail to listen")
	}

	s := grpc.NewServer() //NewServer一個grpc
	helloworld.RegisterGreeterServer(s, &service{}) //起到橋樑的作用 把GreaterServer註冊到grpc裏面去 把前面實現的service服務的結構體註冊到grpc裏面去,目的是實現的東西要讓grpc系統知道
	if err := s.Serve(lis);err != nil{ //啓動 這個服務由grpc來完成 死循環永遠啓動在這裏
		log.Fatal("fail to server")
	}
}

第四步,實現客戶端

先連接服務端,然後產生一個客戶端的結構體

$GOPATH/src/helloworld/greeter_clinet/main.go代碼:

package main

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

const(
	address = "127.0.0.1:5001"
)
//在客戶端這裏就不需要實現接口,客戶端只管調用
func main(){
	conn,err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil{
		log.Fatal("didnot connect server")
	}

	defer conn.Close()

	c := helloworld.NewGreeterClient(conn) //創建一個客戶端 GreeterClient它內部維護了grpc鏈接 通過這個grpc鏈接就具有了遠程調用能力

	r,err := c.SayHello(context.Background(), &helloworld.HelloRequest{Name:"xww"})
	if err != nil{
		log.Fatal("could not greet")
	}
	fmt.Println(r.Message)

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章