参考grpc官方:  https://grpc.io/docs/quickstart/go.html

或官方中文翻译: http://doc.oschina.net/grpc?t=60133

安装proto buf 3、protoc编译器

1-安装grpc(需要红杏出墙):

# 因为本朝网络的安全原因,需要提前export http_proxy https_proxy 来红杏出墙到golang.org。 比如执行linux命令 export http{,s}_proxy=红杏出墙IP:红杏出墙端口号
执行:go get -u google.golang.org/grpc

2-安装

下载protoc:

从 https://github.com/google/protobuf/releases下载预编译的“protoc编译器”,用于生成gRPC服务代码。(文件名为:protoc-<版本>-<平台>.zip)

下载后,解压zip文件,并将protoc二进制文件所在bin目录添加到PATH环境变量中

安装protoc的go插件:

go get -u github.com/golang/protobuf/protoc-gen-go

go get -u github.com/golang/protobuf  //解决go build报错:” undefined: proto.ProtoPackageIsVersion3 “ 。高版本protoc搭配低版本protobuf就会出现这样的问题

$GOPATH/bin加入到PATH环境变量中

非流式RPC: 

1-使用proto buf 3的IDL(接口定义语言)语法编写 .proto 文件.  关于.proto文件的语法规则参考本博protocol buffer的整理

我的.proto文件路径是:$GOPATH/src/nonstream/non_stream.proto

 1 syntax = "proto3"; //必须指定,否则就认为是proto2版本
2
3 package nonstream; //包名,生成go代码的时候,protoc会用到这个包名,生成的.pb.go文件中有package nonstream,后面的服务端、客户端go源文件时需要导入该包名
4
5 //定义服务 ( 风格:服务名和rpc方法名是大驼峰风格 )
6 service EchoIface {
7 //自己的GO源码的类需要实现接口Echo函数
8 rpc Echo (Ping) returns (Pong) {}
9 }
10
11 // 定义消息 (风格: 消息名用大驼峰风格)
12 message Ping {
13 string request = 1; //(风格: 字段名用小写下划线风格,如果是枚举字段名则是大写下划线风格)
14 }
15
16 message Pong {
17 string reply = 1;
18 } 

2- 使用protoc生成go代码:

protoc命令的语法: protoc -I /PATH/TO/.proto_DIR/   /PATH/TO/TARGET.proto --go_out=plugins=grpc:/PATH/TO/OUTPUT_DIR  #不指定-I表示从当前工作目录寻找.proto文件。-I一般是指向项目根目录。
#本例编译.proto文件使用下面的命令(需要cd到.proto文件所在目录,再执行)
protoc non_stream.proto --go_out=plugins=grpc:. #执行完这条命令后,就可以在当前目录下看到non_stream.pb.go源码文件了。 命令中:后面的.表示当前目录,加入要生成代码到/tmp目录,则使用选项--go-out=plugins=grpc:/tmp
# 题外话: --go-out 选项中有无plugins=grpc的区别:
1 protoc --go_out=. ./helloworld.proto // -–go_out=后直接跟路径(点号表示当前路径)。只生成序列化/反序列化代码文件,不需要rpc通讯。
2 protoc --go_out=plugins=grpc:. ./helloworld.proto // --go_out=后跟了grpc插件和目录。生成序列化反序列化代码 和 客户端/服务端通讯代码。

3-编写非流式服务端go代码:

 1 package main
2
3 import (
4 "context"
5 "log"
6 "net"
7 "strings"
8
9 pb "nonstream" //刚才.proto文件定义的包名
10 "google.golang.org/grpc"
11 )
12
13 type server struct{}
14
15 /*server结构体需要实现EchoIface接口的Echo方法, 注意这里的参数多了context,返回值多了error。这可以查看protoc生成的.pb.go文件就发现有下面的定义:
16 type EchoIfaceServer interface {
17 Echo(context.Context, *Ping) (*Pong, error)
18 }
19 */
20 func (s *server) Echo(ctx context.Context, in *pb.Ping) (*pb.Pong, error) {
21 log.Printf("Received from client: %s", in.Request)
22 u := strings.ToUpper(in.Request) //注意.proto文件中字段是小写下划线,这里变成了大驼峰了
23 return &pb.Pong{Reply: u + "..." + in.Request + "..."}, nil
24 }
25
26 func main() {
27 lis, err := net.Listen("tcp", ":5555") //1.指定监听地址:端口号
28 if err != nil {
29 log.Fatalf("failed to listen: %v", err)
30 }
31
32 s := grpc.NewServer() //2.新建gRPC实例
33 pb.RegisterEchoIfaceServer(s, &server{}) //3.在gRPC服务器注册我们的服务实现。参数2是接口(满足服务定义的方法)。在.pb.go文件中搜索Register关键字即可找到这个函数签名
34 if err := s.Serve(lis); err != nil { //4.Serve()阻塞等待
35 log.Fatalf("failed to serve: %v", err)
36 }
37 }

4-编写非流式客户端go代码:

 1 package main
2
3 import (
4 "context"
5 "log"
6 "time"
7 "fmt"
8
9 pb "nonstream"
10 "google.golang.org/grpc"
11 )
12
13
14 func main() {
15 //1.建立连接
16 conn, err := grpc.Dial("127.0.0.1:5555", grpc.WithInsecure()) //如果需要授权认证或tls加密,则可以使用DialOptions来设置grpc.Dial
17 if err != nil {
18 log.Fatalf("grpc.Dial: %v", err)
19 }
20 defer conn.Close()
21
22 c := pb.NewEchoIfaceClient(conn) //2.新建一个客户端stub来执行rpc方法
23
24 ctx, cancel := context.WithTimeout(context.Background(), time.Second) //超时1秒执行cancel
25 defer cancel()
26
27 r, err := c.Echo(ctx, &pb.Ping{Request: "yahoo"}) //3.调用rpc方法
28 if err != nil {
29 log.Fatalf("c.Echo: %v", err)
30 }
31
32 fmt.Printf("echo from server: %s\n", r.Reply)
33 }

---------------------------------- 分割线 ------------------------------

单向流式RPC -- 服务流式响应

流式的好处之一是可以实现异步

1- 编写.proto文件:

 1 // 这个文件的路径: $GOPATH/src/stream/stream.proto 。包名是stream,后面的服务端/客户端代码会导入这个包
2 syntax = "proto3";
3
4 package stream;
5
6 service EchoIface {
7 rpc Echo(Ping) returns (stream Pong) {} //server-side-stream
8 }
9
10 message Ping {
11 string request = 1;
12 }
13
14 message Pong {
15 string reply = 1;
16 }

2-使用protoc生成go代码(此处省略,具体参考上面protoc的例子)

3-服务端代码:

package main

import (
"log"
"net"
"strconv" "google.golang.org/grpc"
pb "stream"
) type server struct{} /* 从.pb.go文件中,我们发现了下面的定义:
type EchoIfaceServer interface {
Echo(*Ping, EchoIface_EchoServer) error
}
而参数EchoIface_EchoServer的定义为(有Send函数):
type EchoIface_EchoServer interface {
Send(*Pong) error
grpc.ServerStream
}
*/
func (s *server) Echo(in *pb.Ping, es pb.EchoIface_EchoServer) error {
n := in.Request
for i := 0; i < 10; i++ { //发10次Hello
es.Send(&pb.Pong{Reply: "Hello " + n + ":" + strconv.Itoa(i)})
}
return nil
} func main() {
conn, err := net.Listen("tcp", ":6666")
if err != nil {
log.Fatalf("net.Listen: %v", err)
}
svr := grpc.NewServer() pb.RegisterEchoIfaceServer(svr, &server{})
if err := svr.Serve(conn); err != nil {
log.Fatalf("s.Serve(): %v", err)
}
}

4-客户端代码:

 1 package main
2
3 import (
4 "context"
5 "io"
6 "log"
7
8 "google.golang.org/grpc"
9 pb "stream"
10 )
11
12 func main() {
13 conn, err := grpc.Dial("127.0.0.1:6666", grpc.WithInsecure())
14 if err != nil {
15 log.Fatalf("grpc.Dial: %v", err)
16 }
17 defer conn.Close()
18
19 c := pb.NewEchoIfaceClient(conn)
20
21 /* 从protoc生成的.pb.go文件中,我们发现如下定义:
22 type EchoIfaceClient interface {
23 Echo(ctx context.Context, in *Ping, opts ...grpc.CallOption) (EchoIface_EchoClient, error)
24 }
25 而上面接口函数的第一个返回值在.pb.go文件中的的定义是:
26 type EchoIface_EchoClient interface {
27 Recv() (*Pong, error)
28 grpc.ClientStream
29 }
30 */
31 stream, err := c.Echo(context.Background(), &pb.Ping{Request: "google"})
32 if err != nil {
33 log.Fatalf("c.Echo: %v", err)
34 }
35
36 for {
37 pong, err := stream.Recv()
38 if err == io.EOF {
39 break
40 }
41
42 if err != nil {
43 log.Printf("stream.Recv: %v", err)
44 }
45
46 log.Printf("echo from server: %s", pong.Reply)
47 }
48
49 }

双向流式RPC,客户端流式RPC

无非就是:

1-编写.proto文件的服务定义时,在需要变成流式的参数或返回值前加stream修饰。

2-对.pb.go文件执行正则搜索,找到函数/方法的定义。再根据定义写go代码

但是要注意一些差异(细节代码参考):

1-客户端流式RPC (客户端一次或多次发数据到服务端,服务端响应一次):

客户端: 使用 流 的Send()方法 发送请求,发送完后使用 流 的CloseAndRecv方法让gRPC服务端知道客户端已经完成请求并且期望获得一个响应。如果CloseAndRecv()返回的err不为nil,那么返回的第一个值就是一个有效的服务端响应。

服务端:  使用 流 的Recv() 方法接收客户端消息,并且用 流 的SendAndClose() 方法返回它的单个响应。

2-双向流式RPC中(客户端:等待接收,并同时发送一次或多次数据到服务端,最后一次发送结束标识--CloseSend()方法。 服务端:来一个,处理一个,响应一个,不用等待客户端发送完成才处理。):

客户端:一个goroutine用来执行 流的 Recv()方法 等待服务端发来的流式响应(阻塞等待状态)。另一个goroutine用来将一次或多次请求通过流的Send()方法发送到服务端,发完后,使用CloseSend()结束发送。

服务端: 服务端用 流 的Recv()方法接收客户端的请求流,接收一个、处理一个、发送一个响应,直到出错,或因为客户端CloseSend()导致的服务端接收完请求。

更多细节,结合源代码https://github.com/grpc/grpc-go/blob/master/examples/route_guide和官方文档中译版看

不需要入参结构体或返回结构体的情况:

方法1:  可以在.proto文件中定义空的消息(也就是空结构体,这样做的好处是以后可以给结构体添加字段, 接口函数签名不变).

message MyRespone {
}

务必注意,在源代码编写的时候,不能直接直接返回空指针, 否则会报"rpc error: code = Internal desc = grpc: error while marshaling: proto: Marshal called with nil".  意思大概就是不能序列化nil指针

if err != nil {
return nil, err //错误! 正确写法 return &pb.MyRespone{},err
}

方法2: 使用protobuf的内置空类型.

.proto文件需要类似这样定义(以上面非流式的例子做修改成不需要返回结构体为例子)

 syntax = "proto3";
import "google/protobuf/empty.proto"; //这个就是protobuf内置的空类型
package nonstream; service EchoIface {
rpc Echo (Ping) returns (google.protobuf.Empty) {}
} message Ping {
string request = 1;
}

源码文件的编写需要这样(以上面非流式的例子做修改):

import "github.com/golang/protobuf/ptypes/empty"
...
func (s *server) Echo(ctx context.Context, in *pb.Ping) (*empty.Empty, error)

go grpc流式和非流式的例子的更多相关文章

  1. 阻塞式和非阻塞式IO

    有很多人把阻塞认为是同步,把非阻塞认为是异步:个人认为这样是不准确的,当然从思想上可以这样类比,但方式是完全不同的,下面说说在JAVA里面阻塞IO和非阻塞IO的区别 在JDK1.4中引入了一个NIO的 ...

  2. 多态设计 zen of python poem 显式而非隐式 延迟赋值

    总结 1.python支持延迟赋值,但是给调用者带来了困惑: 2.显式而非隐式,应当显式地指定要初始化的变量 class Card: def __init__(self, rank, suit): s ...

  3. Spring学习(1):侵入式与非侵入式,轻量级与重量级

    一. 引言 在阅读spring相关资料,都会提到Spring是非侵入式编程模型,轻量级框架,那么就有必要了解下这些概念. 二. 侵入式与非侵入式 非侵入式:使用一个新的技术不会或者基本不改变原有代码结 ...

  4. Boostrap响应式与非响应式

    非响应式布局 在使用非响应式布局时,在<head>标签中需要加入一下内容,其中最主要的是non-responsive.css文件 <head> <meta http-eq ...

  5. 如何实现XA式、非XA式Spring分布式事务

    Spring应用的几种事务处理机制 Java Transaction API和XA协议是Spring常用的分布式事务机制,不过你可以选择选择其他的实现方式.理想的实现取决于你的应用程序使用何种资源,你 ...

  6. Java基础知识强化之多线程笔记07:同步、异步、阻塞式、非阻塞式 的联系与区别

    1. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就必须先得到返回值了. 换句话话说,调用者主动等待这个"调用"的结果. 对于 ...

  7. Spring 侵入式和非侵入式

    1.非侵入式的技术体现 允许在应用系统中自由选择和组装Spring框架的各个功能模块,并且不强制要求应用系统的类必须从Spring框架的系统API的某个类来继承或者实现某个接口. 2.如何实现非侵入式 ...

  8. 基于NIO写的阻塞式和非阻塞式的客户端服务端

    由于功能太过简单,就不过多阐述了,直接上阻塞式代码: package com.lql.nio; import org.junit.Test; import java.io.IOException; i ...

  9. PHP PDO_MYSQL 链式操作 非链式操作类

    <?php /* vim: set expandtab tabstop=4 shiftwidth=4: */ // +-------------------------------------- ...

  10. 登录式与非登录式&交互式与非交互式shell及其环境初始化过程

    交互式shell和非交互式shell(interactive shell and non-interactive shell) 交互式模式就是在终端上执行,shell等待你的输入,并且立即执行你提交的 ...

随机推荐

  1. 改变promise状态有三种resolve、reject、throw

    let p = new Promise((resolve, reject) => { // 改变Promise的状态由三种 // resolve('第一种成功1'); // reject('第一 ...

  2. Element-UI中Drawer抽屉去除标题自带黑色边框

    当点击事件drawer==true时,抽匣回打开 这时抽匣的标题会出现一个难看的蓝色边框,一会就会消失,但是好丑,所以要去掉它 解决方法 /deep/ :focus { outline: 0; } v ...

  3. 数字预失真(DPD)小试

    前言 射频功放的增益响应并非线性的,受到放大管饱和效应的影响,功放不可避免地出现非线性.甚至具有记忆效应的失真.这种非线性失真不仅产生高阶谐波,还会产生互调干扰,降低带内信噪比,影响带外信号.因此,需 ...

  4. 本地搭建playground

    本文主要是记录我搭建go playground的步骤. 1.安装docker 如果你使用的Ubuntu,docker的安装步骤可以参见这里,这是我之前写的在Ubuntu18.04下安装fabric,其 ...

  5. ChatGPT 中,G、P、T 分别是什么意思?

    流行的技术名词按发音难度排序,ChatGPT 肯定排在前面. 到底它为什么叫做 ChatGPT 呢? 先说 GPT:Generative Pre-Training Transformer Genera ...

  6. Paddlenlp之UIE关系抽取模型【高管关系抽取为例】

    往期项目回顾: Paddlenlp之UIE模型实战实体抽取任务[打车数据.快递单] Paddlenlp之UIE分类模型[以情感倾向分析新闻分类为例]含智能标注方案) 应用实践:分类模型大集成者[Pad ...

  7. C++ Qt开发:TableWidget表格组件

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍TableWi ...

  8. 【OpenCV】基于cv2的图像阈值化处理【超详细的注释和解释】掌握基本操作

    说在前面的话 博主今天给大家带来人工智能的一个重要领域的入门操作,opencv包的使用和基本操作,希望大家可以从中学到一些东西! 前言 那么这里博主先安利一下一些干货满满的专栏啦! 手撕数据结构htt ...

  9. Oracle ADG + Keepalived 切换演练

    客户的一套生产环境采用的架构是Oracle ADG + Keepalived,近期需要进行切换演练,要求我这边保障.ADG本身切换倒没啥可说的,但引入keepalived软件,就需要提前研究下这个架构 ...

  10. 安装Electron时卡在install.js不动的解决方案

    问题来源,发现即使 源切换成淘宝的之后,安装 electron的时候还是慢死,郁闷,后来百度才发现,原来,还需要设置一个地方!!! 经过试验,果然快了 爽.... 之前在安装Electron的时候,经 ...