A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 播妞 程序媛   /  2018-6-13 16:45  /  2262 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

Go语言构建微服务一站式解决方案(,本次我们主要分享关于上次没有讲述完的微服务相关的技术,赶紧一起来学习吧~~
protoBuf(Google旗下平台语言无关序列化数据协议)
protobuf是google旗下的一款平台无关,语言无关,可扩展的序列化结构数据格式。所以很适合用做数据存储和作为不同应用,不同语言之间相互通信的数据交换格式,只要实现相同的协议格式即同一 proto文件被编译成不同的语言版本,加入到各自的工程中去。这样不同语言就可以解析其他语言通过 protobuf序列化的数据。目前官网提供了 C++,Python,JAVA,GO等语言的支持。

protobuf语法定义
要想使用 protobuf必须得先定义 proto文件。所以得先熟悉 protobuf的消息定义的相关语法。下面就来介绍。
首先我们先定义一个 proto文件,结构如下:
[AppleScript] 纯文本查看 复制代码
message Article { 
required int32 article_id=1; 
optional string article_excerpt=2; 
repeated string article_picture=3; 
}

上面我们主要定义了一个消息,这个消息包括文章 ID,文章摘要,文章图片。下面给出消息定义的相关说明 :
message是消息定义的关键字。
a)required表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。
b)Optional:表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。---因为optional字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。
c) Repeated:表示该字段可以包含0~N个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值
d) int32和string是字段的类型。后面是我们定义的字段名。最后的 1,2,3则是代表每个字段的一个唯一的编号标签,在同一个消息里不可以重复。这些编号标签用与在消息二进制格式中标识你的字段,并且消息一旦定义就不能更改。需要说明的是标签在 1到15范围的采用一个字节进行编码。所以通常将标签 1到15用于频繁发生的消息字段。编号标签大小的范围是1到229 – 1。此外不能使用protobuf系统预留的编号标签(19000 -19999)

当然 protobuf支持更多的类型,比如 bool,double,float,枚举,也可以是其他定义过的消息类型譬如前面的消息 Article。支持的基本类型如下:
一般在我们的项目中肯定会有很多消息类型。我们总不能都定义在一个文件中。当一个 proto文件需要另一个 proto文件的时候,我们可以通过 import导入,就像下面这样:
[AppleScript] 纯文本查看 复制代码
import "article.proto"; 
message Book { 
//定义消息体
}
protoBuf使用
protobuf的使用方法是将数据结构写入到 .proto文件中,使用 protoc编译器编译(间接使用了插件)得到一个新的 go包,里面包含 go中可以使用的数据结构和一些辅助方法。
Golang&protoBuf
1. $GOPATH/src/创建 myproto文件夹
2. myproto文件夹中创建 test.proto文件 (protobuf协议文件 )
[AppleScript] 纯文本查看 复制代码
syntax = “proto2”;
package myproto;
enum FOO {X = 17;};
message Test {
required string label = 1;
optional int32 type = 2 [default=77];
repeated int64 reps = 3;
optional group OptionalGroup = 4 {
required string RequiredFiled = 5;
}
}
1. 编译 :执行
[AppleScript] 纯文本查看 复制代码
protoc --go_out=. *.proto
生成 test.pb.go文件
1. 使用 protobuf做数据格式转换
[AppleScript] 纯文本查看 复制代码
package main
import (
"fmt"
"github.com/golang/protobuf/proto"
"myproto"
)
func main() {
test := &myproto.Test{
Label: proto.String("hello"),
Type: proto.Int32(17),
Reps: []int64{1, 2, 3},
Optionalgroup: &myproto.Test_OptionalGroup{
RequiredFiled: proto.String("good bye"),
},
}
//将Struct test 转换成 protobuf
data, err := proto.Marshal(test)
if err != nil {
fmt.Println("marshaling error: ", err)
}
//得到一个新的Test结构体 newTest
newTest := &myproto.Test{}
//将 data 转换成 Test结构体
err = proto.Unmarshal(data, newTest)
if err != nil {
fmt.Println("unmarshaling error: ", err)
}
//将newTest的字符串序列化打出
fmt.Println(newTest.String())
//得到type字段
if test.GetType() != newTest.GetType() {
fmt.Println("type is not equal")
}
//...
}

gRPC(Google定义的PRC协议标准)
gRPC是什么?
在 gRPC里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC系统类似, gRPC也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。 gRPC客户端和服务端可以在多种环境中运行和交互 -从 google内部的服务器到你自己的笔记本,并且可以用任何 gRPC支持的语言 来编写。所以,你可以很容易地用 Java创建一个 gRPC服务端,用 Go、 Python、Ruby来创建客户端。此外, Google最新 API将有 gRPC版本的接口,使你很容易地将 Google的功能集成到你的应用里。
使用 protocol buffers
gRPC默认使用protoBuf,这是 Google开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON)。正如你将在下方例子里所看到的,你用 proto files创建 gRPC服务,用 protoBuf消息类型来定义方法参数和返回类型。你可以在 Protocol Buffers文档找到更多关于 protoBuf的资料。
虽然你可以使用 proto2 (当前默认的 protocol buffers版本 ),我们通常建议你在 gRPC里使用 proto3,因为这样你可以使用 gRPC支持全部范围的的语言,并且能避免 proto2客户端与 proto3服务端交互时出现的兼容性问题,反之亦然。
你好 gRPC
现在你已经对 gRPC有所了解,了解其工作机制最简单的方法是看一个简单的例子。 Hello World将带领你创建一个简单的客户端 —— 服务端应用,向你展示:
n 通过一个protoBuf模式,定义一个简单的带有 Hello World方法的 RPC服务。
n 用你最喜欢的语言 (如果可用的话 )来创建一个实现了这个接口的服务端。
n 用你最喜欢的 (或者其他你愿意的 )语言来访问你的服务端。
go语言实现 gRPC远程调用
创建一个 protobuf package,如: my_rpc_proto; 在$GOPATH/src/下创建 go_lession/gRPC_test/my_rpc_proto/文件夹里面创建 protobuf协议文件 helloServer.proto
[AppleScript] 纯文本查看 复制代码
syntax = "proto3";
package my_rpc_proto;
// The HelloServer service definition.
service HelloServer {
// 第一个远程调用接口
rpc SayHello (HelloRequest) returns (HelloReply) {}
// 第二个远程调用接口
rpc GetHelloMsg (HelloRequest) returns (HelloMessage) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
message HelloMessage {
string msg = 1;
}
在当前文件下,编译 helloServer.proto文件
[AppleScript] 纯文本查看 复制代码
protoc –go_out=plugins=grpc:./ *.proto
得到 helloServer.pb.go文件
1. gRPC-Server编写
[AppleScript] 纯文本查看 复制代码
package main
import (
"fmt"
"net"
pb "go_lession/gRPC_test/my_rpc_proto"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const (
port = ":18881"
)
type server struct{}
//实现RPC SayHello 接口
func (this *server) SayHello(ctx context.Context, in *pb.HelloRe
quest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "hello" + in.Name}, nil
}
//实现RPC GetHelloMsg 接口
func (this *server) GetHelloMsg(ctx context.Context, in *pb.Hell
oRequest) (*pb.HelloMessage, error) {
return &pb.HelloMessage{Msg: "this is from server HAHA!"}, nil
}
func main() {
listen, err := net.Listen("tcp", port)
if err != nil {
fmt.Println("failed to listen : ", err)
return
}
//得到一个gRPC 服务句柄
srv := grpc.NewServer()
//将 server 结构体注册到 gRPC 服务
pb.RegisterHelloServerServer(srv, &server{})
//启动监听gRPC服务
if err := srv.Serve(listen); err != nil {
fmt.Println("failed to serve, ", err)
return
}
}
1. gRPC-Client编写
[AppleScript] 纯文本查看 复制代码
package main
import (
"fmt"
pb "go_lession/gRPC_test/my_rpc_proto"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const (
address = "localhost:18881"
clientName = "GreenHat"
)
func main() {
//了客户端连接服务器 
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
fmt.Println("did not connetc : ", err)
return
}
defer conn.Close()
//获取一个 gRPC 句柄  
c := pb.NewHelloServerClient(conn)
//远程调用 SayHello接口
r1, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: clientName})
if err != nil {
fmt.Println("cloud not get Hello server ..", err)
return
}
fmt.Println("HelloServer resp: ", r1.Message)
//远程调用 GetHelloMsg接口
r2, err := c.GetHelloMsg(context.Background(), &pb.HelloRequest{Name: clientName})
if err != nil {
fmt.Println("cloud not get hello msg ..", err)
return
}
fmt.Println("HelloServer resp: ", r2.Msg)
}

运行 server,在运行 client
得到以下输出结果:
HelloServer resp: helloGreenHat
HelloServer resp: this is from server HAHA!

本次讲解结束,下节课我们将会讲解关于基于Go的服务发现工具Consul的相关内容,记得随时关注我们哦~另外关于Go语言与区块链如果大家想要了解的话,也可加添加播妞QQ3414556270进行详询。

1 个回复

倒序浏览
多谢分享
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马