Home >> gRPC 简易实现
gRPC 简易实现
2024-03-05 16:39 AtmosphereMao
gRPC 简易实现流程
Prerequisites
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
Operation
Proto
./src/service/service.proto
syntax = "proto3";
package hello;
option go_package = "grpc-server/src/service";
message Request{
string value = 1;
}
message Response{
string value = 1;
}
// The HelloService service definition
// service 关键字
// HelloService 服务名称 对应接口的名称
// service服务会对应.pb.go文件里interface,里面的rpc对应接口中的函数
service HelloService{
rpc Hello (Request) returns (Response){}
rpc Channel(stream Request) returns (stream Response) {}
}
生成go文件
$ protoc -I . --go_out=./src/service --go_opt=module="grpc-server/src/service" --go-grpc_out=./src/service --go-grpc_opt=module="grpc-server/src/service" ./src/service/service.proto
Server
./src/server/main.go
package main
import (
"context"
"google.golang.org/grpc"
"log"
"net"
"zyy-grpc-server/src/service"
)
type HelloService struct {
// UnimplementedHelloServiceServer这个结构体是必须要内嵌进来的
// 也就是说我们定义的这个结构体对象必须继承UnimplementedHelloServiceServer。
// 嵌入之后,我们就已经实现了GRPC这个服务的接口,但是实现之后我们什么都没做,没有写自己的业务逻辑,
// 我们要重写实现的这个接口里的函数,这样才能提供一个真正的rpc的能力。
service.UnimplementedHelloServiceServer
}
var _ service.HelloServiceServer = (*HelloService)(nil)
// Hello 重写实现的接口里的Hello函数
func (p *HelloService) Hello(ctx context.Context, req *service.Request) (*service.Response, error) {
resp := &service.Response{}
resp.Value = "hello:" + req.Value
log.Printf("Received: %v", resp.Value)
return resp, nil
}
func main() {
// 首先通过grpc.NewServer() 构造一个grpc服务对象
grpcServer := grpc.NewServer()
// 然后通过grpc插件生成的RegisterHelloServiceServer函数注册我们实现的HelloService服务。
service.RegisterHelloServiceServer(grpcServer, new(HelloService))
listen, err := net.Listen("tcp", ":8888")
if err != nil {
log.Fatal("Listen TCP err:", err)
}
//最后通过grpcServer.Serve(listen) 在一个监听端口上提供gRPC服务
grpcServer.Serve(listen)
}
Client
./src/client/main.go
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
"zyy-grpc-server/src/service"
)
func main() {
// grpc.Dial负责和gRPC服务建立连接
//conn, err := grpc.Dial("localhost:8888", grpc.WithInsecure())
// 这里会提示,WithInsecure已被弃用,
// 如果你不想继续使用WithInsecure,可以使用
// 函数insecure.NewCredentials()返回credentials.TransportCredentials的一个实现。
// 您可以将其作为DialOption与grpc.WithTransportCredentials一起使用:
// 但是,API标记为实验性的,因此即使他们已经添加了弃用警告,您也不必立即切换。
conn, err := grpc.Dial("localhost:8888", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal("Dial err: ", err)
}
defer conn.Close()
// NewHelloServiceClient函数是xxx_grpc.pb.go中自动生成的函数,
// 基于已经建立的连接构造HelloServiceClient对象,
// 返回的client其实是一个HelloServiceClient接口对象
//
client := service.NewHelloServiceClient(conn)
// 通过接口定义的方法就可以调用服务端对应gRPC服务提供的方法
req := &service.Request{Value: "test"}
reply, err := client.Hello(context.Background(), req)
if err != nil {
log.Fatal(err)
}
fmt.Println(reply.GetValue())
}
Stream gRPC
server
./src/server/main.go
package main
import (
"google.golang.org/grpc"
"io"
"log"
"net"
"zyy-grpc-server/src/service"
)
type HelloService struct {
service.UnimplementedHelloServiceServer
}
var _ service.HelloServiceServer = (*HelloService)(nil)
func (p *HelloService) Channel(stream service.HelloService_ChannelServer) error {
// 服务端在循环中接收客户端发来的数据
for {
args, err := stream.Recv()
if err != nil {
// 如果遇到io.EOF表示客户端流关闭
if err == io.EOF {
return nil
}
return err
}
// 响应一个请求
// 生成返回的数据通过流发送给客户端
resp := &service.Response{
Value: "hello," + args.GetValue(),
}
log.Printf("Received: %v", resp.Value)
err = stream.Send(resp)
if err != nil {
// 服务端发送异常,函数退出,服务端流关闭
return err
}
}
}
func main() {
// 1. 构造一个gRPC服务对象
grpcServer := grpc.NewServer()
// 2. 通过gRPC插件生成的RegisterHelloServiceServer 函数注册我们实现的HelloService服务。
service.RegisterHelloServiceServer(grpcServer, new(HelloService))
// 3. 监听:8888端口
listen, err := net.Listen("tcp", ":8888")
if err != nil {
log.Fatal("Listen TCP err:", err)
}
// 4. 通过grpcServer.Serve(listen) 在一个监听端口上提供gRPC服务
grpcServer.Serve(listen)
}
Client
./src/client/main.go
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"io"
"log"
"time"
"zyy-grpc-server/src/service"
)
func main() {
conn, err := grpc.Dial("localhost:8888", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := service.NewHelloServiceClient(conn)
// 客户端先调用Channel方法,获取返回的流对象
stream, err01 := client.Channel(context.Background())
if err01 != nil {
log.Fatal(err01)
}
// 在客户端我们将发送和接收放到两个独立的Goroutine
// 首先向服务端发送数据:
go func() {
for {
req := &service.Request{
Value: "test",
}
if err := stream.Send(req); err != nil {
log.Fatal(err)
}
time.Sleep(time.Second)
}
}()
// 然后再循环中接收服务端返回的数据
for {
reply, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
fmt.Println(reply.GetValue())
}
}
评论
暂无评论
* 登录后即可评论