go grpc流式和非流式的例子
参考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流式和非流式的例子的更多相关文章
- 阻塞式和非阻塞式IO
有很多人把阻塞认为是同步,把非阻塞认为是异步:个人认为这样是不准确的,当然从思想上可以这样类比,但方式是完全不同的,下面说说在JAVA里面阻塞IO和非阻塞IO的区别 在JDK1.4中引入了一个NIO的 ...
- 多态设计 zen of python poem 显式而非隐式 延迟赋值
总结 1.python支持延迟赋值,但是给调用者带来了困惑: 2.显式而非隐式,应当显式地指定要初始化的变量 class Card: def __init__(self, rank, suit): s ...
- Spring学习(1):侵入式与非侵入式,轻量级与重量级
一. 引言 在阅读spring相关资料,都会提到Spring是非侵入式编程模型,轻量级框架,那么就有必要了解下这些概念. 二. 侵入式与非侵入式 非侵入式:使用一个新的技术不会或者基本不改变原有代码结 ...
- Boostrap响应式与非响应式
非响应式布局 在使用非响应式布局时,在<head>标签中需要加入一下内容,其中最主要的是non-responsive.css文件 <head> <meta http-eq ...
- 如何实现XA式、非XA式Spring分布式事务
Spring应用的几种事务处理机制 Java Transaction API和XA协议是Spring常用的分布式事务机制,不过你可以选择选择其他的实现方式.理想的实现取决于你的应用程序使用何种资源,你 ...
- Java基础知识强化之多线程笔记07:同步、异步、阻塞式、非阻塞式 的联系与区别
1. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就必须先得到返回值了. 换句话话说,调用者主动等待这个"调用"的结果. 对于 ...
- Spring 侵入式和非侵入式
1.非侵入式的技术体现 允许在应用系统中自由选择和组装Spring框架的各个功能模块,并且不强制要求应用系统的类必须从Spring框架的系统API的某个类来继承或者实现某个接口. 2.如何实现非侵入式 ...
- 基于NIO写的阻塞式和非阻塞式的客户端服务端
由于功能太过简单,就不过多阐述了,直接上阻塞式代码: package com.lql.nio; import org.junit.Test; import java.io.IOException; i ...
- PHP PDO_MYSQL 链式操作 非链式操作类
<?php /* vim: set expandtab tabstop=4 shiftwidth=4: */ // +-------------------------------------- ...
- 登录式与非登录式&交互式与非交互式shell及其环境初始化过程
交互式shell和非交互式shell(interactive shell and non-interactive shell) 交互式模式就是在终端上执行,shell等待你的输入,并且立即执行你提交的 ...
随机推荐
- WebAssembly入门笔记[1]:与JavaScript的交互
前一阵子利用Balazor开发了一个NuGet站点,对WebAssembly进行了初步的了解,觉得挺有意思.在接下来的一系列文章中,我们将通过实例演示的方式介绍WebAssembly的一些基本概念和编 ...
- AI-WEB-1.0靶机
AI-WEB-1.0靶机 情报收集 扫描靶机,打开网站提示 Not even Google search my contents! dirb http://192.168.218.139 扫描网站 进 ...
- C# WPF 开发一个 Emoji 表情查看软件
微软在发布 Windows 11 系统的时候,发布过一个开源的 Emoji 表情 fluentui-emoji .因为我经常需要里面的一些表情图片,在仓库一个个查找特别的不方便,所以我做了一个表情查看 ...
- 人均瑞数系列,瑞数 5 代 JS 逆向分析
声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容.敏感网址.数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许 ...
- 【JS 逆向百例】某网站加速乐 Cookie 混淆逆向详解
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:加速乐加密 ...
- 阿里巴巴 ali1688 Date +0800的问题
package com.example.testredis.controller; import java.text.DateFormat; import java.text.ParseExcepti ...
- Label的背景色
Label的背景色是 color属性,但是这个属性是 必须 Transparent 为 false的时候 才生效,否则不生效
- .NET Core开发实战(第19课:日志作用域:解决不同请求之间的日志干扰)--学习笔记
19 | 日志作用域:解决不同请求之间的日志干扰 开始之前先看一下上一节的代码 // 配置的框架 var configBuilder = new ConfigurationBuilder(); con ...
- Java-生成字符串的MD5值
方法一:public static String getMd5(String str) { MessageDigest md5 = null; try { md5 = MessageDigest.ge ...
- 扒开源安卓性能测试工具moblieperf源码——开发属于你自己的性能稳定性测试工具
moblieperf下载和使用 moblieperf由阿里巴巴开源的Android性能测试工具 下载:官方源码地址mobileperf github 使用: 使用pycharm打开下载的项目 使用只需 ...