首页 > 其他分享 >golang grpc使用示例

golang grpc使用示例

时间:2022-11-22 23:32:10浏览次数:66  
标签:pb return err 示例 grpc fmt golang go


疑问写前面

  1. grpc有内部对心跳的处理吗,还是说,双工需要自己作心跳管理,有懂的留言一下。

SEO优化

grpc如何双工通信?
grpc如何从服务端推送消息给客户端?
gprc环境如何搭建?
grpc生成go文件的命令是?
grpc牛比.
grpc stream 服务端如何Close?

简介

gprc的详细描述不多介绍

这里仅与http作横向对比,集中体现在如下差异:

  1. 传输协议: grpc为tcp(http2),http为http1,http2,使用者感知度不大,不作介绍.
  2. 序列化协议: grpc为protobuf,http为json,xml等,json居多,前者为二进制协议,后者为文本协议,所以grpc序列化的速度和大小远优于http,但对普通的业务,性能感知差异不大。
  3. 服务路由: grpc根据预先在proto文件里声明好的服务方法,客户端可以对生成的客户端代码直接调用这些声明好的服务方法,而http则是根据url地址与method协同,来路由到正确的服务方法.
  4. 服务调用: grpc直接面向ip与端口,http面向域名,即一般是限定在80(http)和443(https)

选择

选择http还是grpc,这个可以根据简介里的差异来分析。

  1. 面向外部用户与否。grpc直接面向ip和端口,所以肯定不方便拿给外部用,选http最佳,但这不是绝对的,毕竟socket本身也是面向ip和端口的,需要tcp的场景也没有谁会刻意转http.
  2. 需不需要苛求带宽与速度 。grpc因为他的序列化protobuf优势,小而快,苛求带宽和速度刻意选用grpc.
  3. 长连接与否。如果只再grpc和http里选择,选grpc,当然可以,面向长连接我更推荐原生socket(tcp,ws)之类的

go示例

示例repo:
​​​https://github.com/fwhezfwhez/TestX/tree/master/test_grpc​​​ 依赖:
protoc:​​https://github.com/golang/protobuf​​ protoc-gen-go:​​https://github.com/golang/protobuf/tree/master/protoc-gen-go​

需要在path下,找到 protoc和protoc-gen-go命令,执行​​protoc --version​​​输出版本号即安装正常.
proto文件生成go命令:
​​​protoc --go_out=plugins=grpc:. hello.proto​

hello.proto

syntax = "proto3";
package pb;

message HelloRequest {
string username = 1;
}

message HelloResponse {
string message = 1;
}

service HelloService {
rpc SayHello(HelloRequest) returns (HelloResponse){}
}

client

package main

import (
"context"
"fmt"
"google.golang.org/grpc"
"test_X/test_grpc/pb"
)

func main() {
conn, err := grpc.Dial("localhost:6001", grpc.WithInsecure())
if err != nil {
fmt.Println(err.Error())
return
}
defer conn.Close()
c := pb.NewHelloServiceClient(conn)
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Username: "ft"})
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(r.Message)
}

server

package main

import (
"context"
"fmt"
"google.golang.org/grpc"
"net"
"test_X/test_grpc/pb"
)

type HelloService struct {
}

func (hs HelloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
return &pb.HelloResponse{Message: fmt.Sprintf("你好,%s", in.Username)}, nil
}

func main() {
lis, err := net.Listen("tcp", ":6001")
if err != nil {
fmt.Println(err.Error())
return
}
s := grpc.NewServer()
pb.RegisterHelloServiceServer(s, HelloService{})
s.Serve(lis)
}

拓展

1. grpc如何双工,服务端推送

在上述代码里直接拓展:
第一步,增加proto接口描述并重新生成go文件:
​​​protoc --go_out=plugins=grpc:. hello.proto​

// How to generate hello.proto to go file:
// protoc --go_out=plugins=grpc:. hello.proto
syntax = "proto3";
package pb;

message HelloRequest {
string username = 1;
}

message HelloResponse {
string message = 1;
}

// +
message ClientStream {
bytes stream = 1;
}
message ServerStream {
bytes stream = 1;
}
service HelloService {
rpc SayHello(HelloRequest) returns (HelloResponse){}

rpc Chat(stream ClientStream) returns (stream ServerStream){}
}
// +

第二步,增加服务端实现Chat的方法实例
server

package main

import (
"context"
"fmt"
"google.golang.org/grpc"
"io"
"net"
"test_X/test_grpc/pb"
)

type HelloService struct {
}

func (hs *HelloService) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
return &pb.HelloResponse{Message: fmt.Sprintf("你好,%s", req.Username)}, nil
}

// ++++++++++++++++++++++++++++++++
func (hs *HelloService) Chat(conn pb.HelloService_ChatServer)error {
for {
stream, err:=conn.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
fmt.Println("receive from client:",stream.Stream)

conn.Send(&pb.ServerStream{
Stream: newBytes(1,2,3,4,5),
})
}
return nil
}
// ++++++++++++++++++++

func main() {
lis, err := net.Listen("tcp", ":6001")
if err != nil {
fmt.Println(err.Error())
return
}
s := grpc.NewServer()
pb.RegisterHelloServiceServer(s, &HelloService{})
go func() {
s.Serve(lis)
}()
fmt.Println(s.GetServiceInfo())
select {}
}

func newBytes(a ...byte)[]byte{
return a
}

第三步,客户端调用
client

package main

import (
"context"
"errorX"
"fmt"
"google.golang.org/grpc"
"io"
"test_X/test_grpc/pb"
"time"
)

func main() {
conn, e := grpc.Dial("localhost:6001", grpc.WithInsecure())
if e != nil {
fmt.Println(e.Error())
return
}
defer conn.Close()
c := pb.NewHelloServiceClient(conn)
// say hello
r, e := c.SayHello(context.Background(), &pb.HelloRequest{Username: "ft"})
if e != nil {
fmt.Println(e.Error())
return
}
fmt.Println(r.Message)

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// chat
chatClilent, e :=c.Chat(context.Background())
if e != nil {
fmt.Println(e.Error())
return
}
go func(){
for{
stream, e:=chatClilent.Recv()
if e == io.EOF {
fmt.Println("EOF")
return
}
if e != nil {
fmt.Println(errorx.Wrap(e).Error())
return
}
fmt.Println("receive from server:", stream.Stream)
}
}()
chatClilent.Send(&pb.ClientStream{
Stream: newBytes(10,9,8,7),
})
select{
case <-time.After(20 * time.Second):
}
}

func newBytes(a ...byte)[]byte{
return a
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

结束:
​​​cd server​​​​go run main.go​​​​cd client​​​​go run main.go​

receive from client: [10 9 8 7]
你好,ft
receive from server: [1 2 3 4 5]

服务端如何Close

rpc面向每个连接,即服务端chat函数return即Close连接

func (hs *HelloService) Chat(conn pb.HelloService_ChatServer)error {
for {
stream, err:=conn.Recv()
if err == io.EOF {
fmt.Println("EOF")
return nil
}
if err != nil {
return err
}
fmt.Println("receive from client:",stream.Stream)

conn.Send(&pb.ServerStream{
Stream: newBytes(1,2,3,4,5),
})
// 关闭连接
// return nil
}
return nil
}


标签:pb,return,err,示例,grpc,fmt,golang,go
From: https://blog.51cto.com/u_11553781/5878749

相关文章

  • Linux线程同步介绍和示例
     线程同步的概念    线程同步?怎么同步?一起运行?一起停止?我当年听说线程同步这个词的时候,也是一头雾水。    在人们的日常生活中,所说的锁大概有两种:一种是不允......
  • Linux进程间通信的方法和示例
     进程通信的概念    进程数据空间是相互独立的,不能相互访问的。但某些情况下进程之间需要互相通信,来完成系统的某项功能或交换数据。进程通信的场景    1)......
  • Linux信号signal的介绍和示例
     如何让程序在后台运行    在之前的章节中,如果要运行程序,在命令提示行下输入程序名后回车,程序被执行,然后等待程序运行完成。在程序运行的过程中,可以用Ctrl+c中止它......
  • linux多进程介绍和示例
     进程的概念什么是进程?进程这个概念是针对系统而不是针对程序员的,对程序员来说,我们面对的概念是程序。当程序员敲入命令执行一个程序的时候,对系统而言,它将启动一个进程。进......
  • C++语言类的多态介绍和示例
         多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就可能会用到多态。    C++多态就是当调用成员函数时,会根据调用函数......
  • C++语言类的继承与派生介绍和示例
     继承    继承是面向对象程序设计中最重要的一个概念。继承允许我们根据一个类来定义另一个类,达到了代码功能重用效果。    当创建一个类时,如果待创建的类与......
  • C++语言动态内存管理介绍和示例
     C++动态内存    在C++程序中,所有内存需求都是在程序执行之前通过定义所需的变量来确定的。但是可能存在程序的内存需求只能在运行时确定的情况。在这些情况下,程序......
  • C++语言vector容器介绍和示例
         之前我们在声明数组的时候,采用的是datatype array[len]的形式,数组在分配之后,不能调整大小,删除和插入数据时操作十分的繁琐,虽然可以采用链表,但是链表的操作更......
  • C++语言类的详解和示例
     超女选秀的例子我们玩了很久,为了教学的需要,暂时离开美眉们,我将采用实际项目开发的例子。在C语言基础知识中已学习过文件操作,在实际开发中,我会把文件操作封装成一个类,类的......
  • C++语言引用的介绍和示例
         对于习惯使用C进行开发的程序员来说,在看到C++中出现的&符号,可能会犯迷糊,因为在C语言中这个符号表示了取地址符,但是在C++中,它除了取地址,还有其它的用途,叫做引用......