gRPC入门教程(二)---gRPC简单使用

本文主要对 gRPC 框架做了简单的介绍,同时记录了具体安装方法与基本使用教程。


如果你对云原生技术充满好奇,想要深入了解更多相关的文章和资讯,欢迎关注微信公众号。

扫描下方二维码或搜索公众号【探索云原生】即可订阅


1. 概述

gRPC 是一个高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发语言。 gRPC基于HTTP/2标准设计,带来诸如双向流控、头部压缩、单TCP连接上的多复用请求等特性。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法

gRPC 默认使用 protocol buffers,这是 Google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON)。

2. 环境准备

env
  • gRPC 需要 go 1.6以上
安装gRPC
go get -u google.golang.org/grpc
$ go get -u google.golang.org/grpc
package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)

国内一般这样安装不上,具体解决办法:https://github.com/grpc/grpc-go#FAQ

如果用了go mod 可以用下面的方法

go mod edit -replace=google.golang.org/grpc=github.com/grpc/grpc-go@latest
go mod tidy
go mod vendor
go build -mod=vendor
安装protobuf

具体见gRPC入门教程(一)—Protobuf安装与基本使用

3. 使用步骤

  • 1)需要使用 protobuf 定义接口,即编写 .proto 文件
  • 2)然后使用 compile 工具生成特定语言的执行代码,比如 Java、C/C++、Python 等。类似于 thrift,为了解决跨语言问题。
  • 3)启动一个 Server 端,server 端通过侦听指定的 port,来等待 Client 链接请求,通常使用 Netty 来构建,gRPC 内置了 Netty 的支持。
  • 4)启动一个或者多个 Client 端,Client 也是基于 Netty,Client 通过与 Server 建立 TCP 长链接,并发送请求;Request 与 Response 均被封装成 HTTP2 的 stream Frame,通过 Netty Channel 进行交互。

4. 示例程序

1.hello.proto
syntax = "proto3";

package helloworld;

// The greeting service definition.
service Greeter {
    // Sends a greeting
    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;
}

编译

// 官方插件
protoc --go_out=plugins=grpc:. hello.proto
// protoc 编译命令
// go_out 编译成go代码 java_out 则编译成Java代码
// plugins=grpc 使用grpc插件提供对grpc的支持 否则不会生成Service的接口
// :. 编译到当前路径
// hello.proto 被编译的文件

// gofast插件
protoc --gofast_out=plugins=grpc:. hello.proto

生成对应的 pb.go 文件。这里用了 plugins 选项,提供对 grpc 的支持,否则不会生成 Service 的接口。

这里定义了一个服务 Greeter,其中有个API SayHello。其接受参数为HelloRequest类型,返回HelloReply类型。这里HelloRequestHelloReply就是普通的PB定义

服务定义为:

// The greeting service definition.

service Greeter {

  // Sends a greeting

  rpc SayHello (HelloRequest) returns (HelloReply) {}

}

service定义了一个server。其中的接口可以是四种类型

  • rpc GetFeature(Point) returns (Feature) {}
    类似普通的函数调用,客户端发送请求Point到服务器,服务器返回相应Feature.
  • rpc ListFeatures(Rectangle) returns (stream Feature) {}
    客户端发起一次请求,服务器端返回一个流式数据,比如一个数组中的逐个元素
  • rpc RecordRoute(stream Point) returns (RouteSummary) {}
    客户端发起的请求是一个流式的数据,比如数组中的逐个元素,服务器返回一个相应
  • rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
    客户端发起的请求是一个流式数据,比如数组中的逐个元素,二服务器返回的也是一个类似的数据结构
2.Server
package main
 
import (
    "log"
    "net"
 	//pb文件目录
    pb "your_path_to_gen_pb_dir/helloworld" 
    "golang.org/x/net/context"
    "google.golang.org/grpc"
)
 
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() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    s.Serve(lis)
}

这里首先定义一个 server 结构,然后实现 SayHello 的接口。

SayHello(context.Context, *HelloRequest) (*HelloReply, error)

然后调用grpc.NewServer() 创建一个server s。接着注册这个server s到结构server上面 pb.RegisterGreeterServer(s, &server{}) 最后将创建的net.Listener传给s.Serve()。就可以开始监听并服务了,类似HTTP的ListenAndServe。

3.client
package main
 
import (
    "log"
    "os"
 
    pb "your_path_to_gen_pb_dir/helloworld"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
)
 
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)
}


这里通过pb.NewGreeterClient()传入一个conn创建一个client,然后直接调用client上面对应的服务器的接口

SayHello(context.Context, *HelloRequest) (*HelloReply, error)

接口,返回*HelloReply 对象。

先运行服务器,在运行客户端,可以看到。

2019/07/02 17:07:50 Greeting: Hello world

5. 小结

使用gRPC的3个步骤

1. 写proto文件
syntax = "proto3";

package helloworld;

// 定义一个服务
service UserService {
	// 定义服务中的某个方法 请求参数User 返回值Resp
    rpc Create (User) returns (Resp) {
    }
}
// 请求参数
message User {
    string name = 1;
    string age = 2;
}
// 返回值
message Resp {
    string message = 1;
}

2. 编译后的文件

部分代码如下:

// UserServiceClient is the client API for UserService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
// 客户端调用接口
type UserServiceClient interface {
	Create(ctx context.Context, in *User, opts ...grpc.CallOption) (*Resp, error)
}

type userServiceClient struct {
	cc *grpc.ClientConn
}

func NewUserServiceClient(cc *grpc.ClientConn) UserServiceClient {
	return &userServiceClient{cc}
}
// 客户端调用的方法具体实现
func (c *userServiceClient) Create(ctx context.Context, in *User, opts ...grpc.CallOption) (*Resp, error) {
	out := new(Resp)
    // invoke 大概是反射调用 service中的方法
	err := c.cc.Invoke(ctx, "/helloworld.UserService/Create", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// UserServiceServer is the server API for UserService service.
type UserServiceServer interface {
	Create(context.Context, *User) (*Resp, error)
}

func RegisterUserServiceServer(s *grpc.Server, srv UserServiceServer) {
	s.RegisterService(&_UserService_serviceDesc, srv)
}

func _UserService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(User)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(UserServiceServer).Create(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/helloworld.UserService/Create",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(UserServiceServer).Create(ctx, req.(*User))
	}
	return interceptor(ctx, in, info, handler)
}

var _UserService_serviceDesc = grpc.ServiceDesc{
	ServiceName: "helloworld.UserService",
	HandlerType: (*UserServiceServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "Create",
			Handler:    _UserService_Create_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "hello.proto",
}


3. Service

定义结构体,实现proto中定义的接口。

package main

import (
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	pb "i-go/grpc/proto"
	"log"
	"net"
)
// 定义一个结构体
type userServer struct {
}
// 然后实现proto中定义的方法
func (s *userServer) Create(ctx context.Context, user *pb.User) (msg *pb.Resp, err error) {
	return &pb.Resp{Message: "Create Success"}, nil
}

func main() {
	listener, err := net.Listen("tcp", "50052")
	if err != nil {
		log.Fatalf("net.Listen fail: %v", err)
	}
	newServer := grpc.NewServer()
	pb.RegisterUserServiceServer(newServer, &userServer{})
	newServer.Serve(listener)
}


4. client
package main

import (
	"context"
	"google.golang.org/grpc"
	pb "i-go/grpc/proto"
	"log"
)

func main() {
	// grpc.WithInsecure() 禁用传输安全性
	conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
    // 创建一个client
	client := pb.NewUserServiceClient(conn)
    // 调用的是UserServiceClient中的方法
	resp, err := client.Create(context.Background(), &pb.User{Name: "illusory", Age: "23"})
	if err != nil {
		log.Fatalf("could not Create: %v", err)
	}
	log.Printf("Create Resp: %s", resp.Message)
}



如果你对云原生技术充满好奇,想要深入了解更多相关的文章和资讯,欢迎关注微信公众号。

扫描下方二维码或搜索公众号【探索云原生】即可订阅


  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,让我来回答你的问题:Java如何调用远程gRPC服务? 首先,你需要定义一个.proto文件来描述你的服务和消息。proto文件是gRPC的核心文件之一,它定义了服务的接口和消息的格式。 接下来,你需要使用gRPC的代码生成工具来生成客户端和服务端的代码。你可以选择使用不同的编程语言,例如Java、C++或Python等。 在Java中,你需要使用gRPC的Java库来编写客户端代码。你可以使用Maven或Gradle来管理你的依赖项。在你的Java代码中,你需要实现一个Stub来调用远程服务。Stub是一个自动生成的类,它提供了一个简单的API来调用远程服务。 下面是一个简单的Java gRPC客户端的示例代码: ``` ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051) .usePlaintext() .build(); HelloWorldGrpc.HelloWorldBlockingStub stub = HelloWorldGrpc.newBlockingStub(channel); HelloRequest request = HelloRequest.newBuilder().setName("World").build(); HelloResponse response = stub.sayHello(request); System.out.println(response.getMessage()); channel.shutdown(); ``` 在这个例子中,我们创建了一个ManagedChannel来连接到远程服务。然后,我们创建了一个HelloWorldBlockingStub来调用远程服务。最后,我们使用这个Stub来发送一个HelloRequest消息并接收一个HelloResponse消息。 最后,你需要启动你的gRPC服务,并在客户端代码中使用正确的地址和端口来连接到它。 希望这个简单的指南能够帮助你入门gRPC
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值