3.微服务--GRPC

1.gRPC入门

1.1gRPC简介

 gRPC 由 google 开发,是一款语言中立、平台中立、开源的远程过程调用系统
 gRPC 客户端和服务端可以在多种环境中运行和交互,例如用 java 写一个服务端,可以用 go 语言写客户端调用

1.2gRPC与Protobuf介绍

微服务架构中,由于每个服务对应的代码库是独立运行的,无法直接调用,彼此间的通信就是一个大问题。
gRPC可以实现微服务,将大的项目拆分成多个小且独立的业务模块,即服务,各服务间使用搞笑的protobuf协议进行RPC调用,gRPC默认使用protocol buffers,这是google开源的一套成熟的结构数据序列化机制(也可以使用其他数据格式如JSON)
可以用proto files创建gRPC服务,用message类型来定义方法参数和返回类型

1.3安装gRPC和Protobuf

? go get -u -v github.com/golang/protobuf/proto
? go get google.golang.org/grpc(无法使用,用如下命令代替) ? git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
? git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
? git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
? go get -u github.com/golang/protobuf/{proto,protoc-gen-go} ? git clone https://github.com/google/go-genproto.git
$GOPATH/src/google.golang.org/genproto
? cd $GOPATH/src/
? go install google.golang.org/grpc
? go get github.com/golang/protobuf/protoc-gen-go

? 上面安装好后,会在 GOPATH/bin 下生成 protoc-gen-go.exe
? 但还需要一个 protoc.exe,windows 平台编译受限,很难自己手动编译,直接去网 站下载一个,地址:https://github.com/protocolbuffers/protobuf/releases/tag/v3.9.0 ,
同样放到 GOPATH/bin下

2 Protobuf语法

2.1基本规范

? 文件以.proto 做为文件后缀,除结构定义外的语句以分号结尾
? 结构定义可以包含:message、service、enum
? rpc 方法定义结尾的分号可有可无
? Message 命名采用驼峰命名方式,字段命名采用小写字母加下划线分隔方式
message SongServerRequest {
required string song_name = 1;
}
? Enums 类型名采用驼峰命名方式,字段命名采用大写字母加下划线分隔方式
enum Foo {
FIRST_VALUE = 1;
SECOND_VALUE = 2;
}
? Service 与 rpc 方法名统一采用驼峰式命名

2.2字段规则

? 字段格式:限定修饰符 | 数据类型 | 字段名称 | = | 字段编码值 | [字段默认值]
? 限定修饰符包含 required\optional\repeated
? Required: 表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required 字段或者无法识别 required 字段都会引发编解码异常,导致消息被丢 弃
? Optional:表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。
---因为 optional 字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为 optional 字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡
? Repeated:表示该字段可以包含 0~N 个元素。其特性和 optional 一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值
? 数据类型
? Protobuf 定义了一套基本数据类型。几乎都可以映射到 C++\Java 等语言的基础数据类型

? N 表示打包的字节并不是固定。而是根据数据的大小或者长度
? 关于 fixed32 和 int32 的区别。fixed32 的打包效率比 int32 的效率高,但是使用的空间一般比 int32 多。因此一个属于时间效率高,一个属于空间效率高
? 字段名称
? 字段名称的命名与 C、C++、Java 等语言的变量命名方式几乎是相同的
? protobuf 建议字段的命名采用以下划线分割的驼峰式。例如 first_name 而不是firstName
? 字段编码值
? 有了该值,通信双方才能互相识别对方的字段,相同的编码值,其限定修饰符和数据类型必须相同,编码值的取值范围1~2^32(4294967296)
? 其中 1~15 的编码时间和空间效率都是最高的,编码值越大,其编码的时间和空间效率就越低,所以建议把经常要传递的值把其字段编码设置为1-15 之间的值
? 1900~2000 编码值为 Google protobuf 系统内部保留值,建议不要在自己的项目中使用
? 字段默认值
? 当在传递数据时,对于 required 数据类型,如果用户没有设置值,则使用默认值传递到对端

2.3service如何定义

? 如果想要将消息类型用在 RPC 系统中,可以在.proto 文件中定义一个 RPC 服务接口,protocol buffer 编译器会根据所选择的不同语言生成服务接口代码
? 例如,想要定义一个 RPC 服务并具有一个方法,该方法接收 SearchRequest 并返回一个 SearchResponse,此时可以在.proto 文件中进行如下定义:
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse) {}
}
? 生成的接口代码作为客户端与服务端的约定,服务端必须实现定义的所有接口方法,客户端直接调用同名方法向服务端发起请求,比较麻烦的是,即便业务上不需要参数也必须指定一个请求消息,一般会定义一个空 message

2.4Message如何定义

? 一个 message 类型定义描述了一个请求或响应的消息格式,可以包含多种类型字段
? 例如定义一个搜索请求的消息格式,每个请求包含查询字符串、页码、每页数目
? 字段名用小写,转为 go 文件后自动变为大写,message 就相当于结构体
syntax = "proto3";
message SearchRequest {
string query = 1; // 查询字符串
int32 page_number = 2; // 页码
int32 result_per_page = 3; // 每页条数
}
? 首行声明使用的 protobuf 版本为 proto3
? SearchRequest 定义了三个字段,每个字段声明以分号结尾,.proto 文件支持双斜
线 // 添加单行注释

2.5更多Message类型

? 一个.proto 文件中可以定义多个消息类型,一般用于同时定义多个相关的消息,例如在同一个.proto 文件中同时定义搜索请求和响应消息
syntax = "proto3";
// SearchRequest 搜索请求
message SearchRequest {
string query = 1; // 查询字符串
int32 page_number = 2; // 页码
int32 result_per_page = 3; // 每页条数
}
// SearchResponse 搜索响应
message SearchResponse {
...
}

2.6如何使用其他Message

? message 支持嵌套使用,作为另一 message 中的字段类型
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}

2.7Message 嵌套的使用

? 支持嵌套消息,消息可以包含另一个消息作为其字段。也可以在消息内定义一个新的消息
? 内部声明的 message 类型名称只可在内部直接使用

message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}repeated Result results = 1;
}
? 另外,还可以多层嵌套
message Outer { // Level 0
message MiddleAA { // Level 1
message Inner { // Level 2
int64 ival = 1;
bool booly = 2;
} }
message MiddleBB { // Level 1
message Inner { // Level 2
int32 ival = 1;
bool booly = 2;
} } }

2.8proto3的Map类型

? proto3 支持 map 类型声明
map<key_type, value_type> map_field = N;
message Project {...}
map<string, Project> projects = 1;
? 键、值类型可以是内置的类型,也可以是自定义 message 类型
? 字段不支持 repeated 属性

2.9.proto文件编译

? 通过定义好的.proto文件生成Java, Python, C++, Go, Ruby,JavaNano, Objective-C, or C# 代码,需要安装编译器 protoc
? 当使用 protocol buffer 编译器运行.proto 文件时,编译器将生成所选语言的代码,用于使用在.proto 文件中定义的消息类型、服务接口约定等。不同语言生成的代码格式不同:
? C++: 每个.proto 文件生成一个.h  的文件和一个.cc 文件,每个消息类型对应一个类
? Java: 生成一个.java 文件,同样每个消息对应一个类,同时还有一个特殊的Builder 类用于创建消息接口
? Python: 姿势不太一样,每个.proto 文件中的消息类型生成一个含有静态描述符的模块,该模块与一个元类 metaclass 在运行时创建需要Python 数据访问类
? Go: 生成一个.pb.go 文件,每个消息类型对应一个结构体
? Ruby: 生成一个.rb 文件的 Ruby 模块,包含所有消息类型
? JavaNano: 类似 Java,但不包含 Builder 类
? Objective-C: 每个.proto 文件生成一个 pbobjc.h 和一个 pbobjc.m 文件
? C#: 生成.cs 文件包含,每个消息类型对应一个类

2.10import导入定义

? 可以使用 import 语句导入使用其它描述文件中声明的类型
? protobuf 接口文件可以像 C 语言的 h 的文件一个,分离为多个,在需要的时候通过import 导入需要对文件。其行为和 C 语言的#include 或java 的 import 的行为大致相同,例如 import "others.proto";
? protocol buffer 编译器会在 -I / --proto_path 参数指定的目录中查找导入的文件,如果没有指定该参数,默认在当前目录中查找

2.11包的使用

? 在.proto 文件中使用 package 声明包名,避免命名冲突
syntax = "proto3";
package foo.bar;
message Open {...}
? 在其他的消息格式定义中可以使用包名+消息名的方式来使用类型,如
message Foo {
...
foo.bar.Open open = 1;
...
}
? 在不同的语言中,包名定义对编译后生成的代码的影响不同
? C++ 中:对应 C++命名空间,例如 Open 会在命名空间 foo::bar 中 ? Java 中:package 会作为 Java 包名,除非指定了 option jave_package 选项
? Python 中:package 被忽略
? Go 中:默认使用 package 名作为包名,除非指定了 option go_package 选项
? JavaNano 中:同 Java
? C# 中:package 会转换为驼峰式命名空间,如 Foo.Bar,除非指定了 option csharp_namespace 选项

3.使用gRPC构建微服务

做一个处理用户信息的微服务
客户端通过用户名,可以从服务端查询用户的基本信息
gRPC
proto
    user.proto 定义客户端请求、服务端响应的数据格式
    user.pb.go 自动生成的,为数据交互提供的函数
    server.go 微服务服务端
    client.go 微服务客户端

3.1编写proto文件-user.proto

//版本号
syntax = "proto3";

//指定包名
package proto;

//定义结构体
message UserRequest {
    //定义用户名
    string name = 1;
}

//响应结构体
message UserResponse{
    int32 id = 1;
    string name = 2;
    int32 age = 3;
    //repeated修饰符是可变数组,go转切片
    repeated string hobby = 4;
}

//service定义方法
service UserInfoService {
    rpc GetUserInfo (UserRequest) returns (UserResponse){}
}

3.2生成.go文件

? pycharm 中打开命令行,输入命令生成接口文件:
? protoc -I . --go_out=plugins=grpc:. ./user.proto

3.3编写服务端-server.go

package main

import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    "net"
    pb "test/gRPC/proto"
)

//1.需要监听
//2.需要实例化gRPC服务端
//3.在gRPC上注册微服务
//4.启动服务端

//定义空接口
type UserInfoService struct{}

var u = UserInfoService{}

//实现方法
func (s *UserInfoService) GetUserInfo(ctx context.Context, req *pb.UserRequest) (resp *pb.UserResponse, err error)  {
    //通过用户名查询用户信息
    name := req.Name
    //数据库中查询用户信息
    if name == "zs" {
        resp = &pb.UserResponse{
            Id:                   1,
            Name:                 name,
            Age:                  0,
            Hobby:                []string{"Sing", "Run"},

        }
    }
    return
}

func main()  {
    //地址
    addr := "127.0.0.1:8080"
    //1.监听
    listener ,err := net.Listen("tcp", addr)
    if err != nil {
        fmt.Printf("监听异常:%s\n", err)
    }
    fmt.Printf("监听端口:%s\n",addr)
    //2.实例化gRPC
    s := grpc.NewServer()
    //3.在gRPC上注册微服务
    pb.RegisterUserInfoServiceServer(s, &u)
    //4.启动服务端
    s.Serve(listener)
}

3.4编写客户端-client.go

package main
import (
    "context"
    "fmt"
    "google.golang.org/grpc"
    pb "test/gRPC/proto"
)

//1.连接服务端
//2.实例gRPC
//3.调用

func main()  {
    //1.连接
    conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())
    if err != nil {
        fmt.Printf("连接异常:%s\n",err)
    }
    defer conn.Close()
    //2.实例化gRPC客户端
    client := pb.NewUserInfoServiceClient(conn)
    //3.组装请求参数
    req := new(pb.UserRequest)
    req.Name = "zs"
    //4.调用接口
    response, err := client.GetUserInfo(context.Background(),req)
    if err != nil {
        fmt.Printf("连接异常:%s\n",err)
    }
    fmt.Printf("响应结果:%v\n", response)
}


原文地址:https://blog.51cto.com/10983441/2482842

时间: 2024-11-05 23:37:10

3.微服务--GRPC的相关文章

Go微服务 grpc的简单使用

作者:薇文文链接:https://www.jianshu.com/p/20ed82218163来源:简书 准备工作 先安装Protobuf 编译器 protoc,下载地址:https://github.com/google/protobuf/releases 我的是windows,将压缩包bin目录下的exe放到环境PATH目录中即可. 然后获取插件支持库 // gRPC运行时接口编解码支持库 go get -u github.com/golang/protobuf/proto // 从 Pro

腾讯正式对外开源高性能 RPC 开发框架与微服务平台Tars

Tars 是将腾讯内部使用的微服务架构 TAF(Total Application Framework)多年的实践成果总结而成的开源项目,目前已于4月10日正式对外开源. 作为支持多语言的高性能 RPC 开发框架和配套一体化的服务治理平台,Tars可以帮助企业或者用户以微服务的方式快速构建稳定可靠的分布式应用,它的设计灵感来源于采取分层思想,实现开发与运营之间的分离.目前该框架在腾讯内部,已经在 160 多个业务(如手机浏览器.应用宝.手机管家.手机QQ.手机游戏等).1.6 多万台服务器上运行

.NET平台微服务项目汇集

最近博客园出现了一篇文章<微服务时代之2017年五军之战:Net PHP谁先死>,掀起了一波撕逼,作者只是从一个使用者的角度来指点江山,这个姿势是不对的..NET Core就是专门针对模块化的微服务架构而设计,在微服务架构这方面Java的Spring Cloud具有非常高的人气,这个正是这篇文章作者的立脚点.然后他没有看到蓬勃发展的.NET 社区的微服务的相关框架,本文主要梳理下当前.NET社区微服务的相关项目的汇集. 1. Service Fabric 微软作为.NET的主战场,自然在当前的

【架构】linkerd:来自Twitter为微服务而生的开源RPC解决方案

大家要如何以规模化方式运维微服务应用程序?实践当中会出现哪些问题,我们又该如何加以解决?在大规模与非预测性工作负载场景当中,我们需要满足哪些条件才能运行一款大型微服务应用程序,而又能够确保不必受到功能发布或者产品变更的影响? 在围绕微服务展开的探讨当中,我们发现几乎很少有人能够切实回答上述问题.以Docker.Mesos.Kubernetes以及gRPC为代表的各类新型技术成果的快速崛起使得我们能够轻松建立小型新架构.然而,高流量生产性用例又该如何实现?根据我们的推算,目前能够以规模化方式运行微

微服务的4个设计原则和19个解决方案

转载本文需注明出处:微信公众号EAWorld,违者必究. 微服务架构现在是谈到企业应用架构时必聊的话题,微服务之所以火热也是因为相对之前的应用开发方式有很多优点,如更灵活.更能适应现在需求快速变更的大环境. 本文将介绍微服务架构的演进.优缺点和微服务应用的设计原则,然后着重介绍作为一个"微服务应用平台"需要提供哪些能力.解决哪些问题才能更好的支撑企业应用架构. 微服务平台也是我目前正在参与的,还在研发过程中的平台产品,平台是以SpringCloud为基础,结合了普元多年来对企业应用的理

(转)微服务框架落地实践之路

http://www.primeton.com/read.php?id=2276&his=1 一.微服务架构产生的背景 近十年中,互联网给我们生活带来了翻天覆地的变化,消费者的生活方式日益数字化,人们可以在任何时间.任何地点利用网络进行购物体验,运用社交媒体进行自我表达,企业也在运用多种技术手段,发挥数字化潜力,改善客户联系,促进企业业务模式的转型.在这种背景下,互联网也好,传统企业也罢,都面临一个共同的需求:面对快速变化的需求,面对业务模式的升级,如何构建出灵活的,可扩展,可重用的系统? 前几

谈谈微服务中的 API 网关(API Gateway)

转载至:http://www.cnblogs.com/savorboard/p/api-gateway.html 背景 我们知道在微服务架构风格中,一个大应用被拆分成为了多个小的服务系统提供出来,这些小的系统他们可以自成体系,也就是说这些小系统可以拥有自己的数据库,框架甚至语言等,这些小系统通常以提供 Rest Api 风格的接口来被 H5, Android, IOS 以及第三方应用程序调用. 但是在UI上进行展示的时候,我们通常需要在一个界面上展示很多数据,这些数据可能来自于不同的微服务中,举

百度“百老汇”架构师深刻透视微服务架构

首先解释这个"百老汇"=百度老年架构师活动中心.......什么是微服务首先微服务并没有一个官方的定义,想要直接描述微服务比较困难,我们可以通过对比传统WEB应用,来理解什么是微服务.传统的WEB应用核心分为业务逻辑.适配器以及API或通过UI访问的WEB界面.业务逻辑定义业务流程.业务规则以及领域实体.适配器包括数据库访问组件.消息组件以及访问接口等.一个打车软件的架构图如下: 尽管也是遵循模块化开发,但最终它们会打包并部署为单体式应用.例如Java应用程序会被打包成WAR,部署在T

从经典架构项目中透析微服务架构的核心概念和充血模型

微服务架构和SOA区别 微服务现在辣么火,业界流行的对比的却都是所谓的Monolithic单体应用,而大量的系统在十几年前都是已经是分布式系统了,那么微服务作为新的理念和原来的分布式系统,或者说SOA(面向服务架构)是什么区别呢? 我们先看相同点: 需要Registry,实现动态的服务注册发现机制:需要考虑分布式下面的事务一致性,CAP原则下,两段式提交不能保证性能,事务补偿机制需要考虑:同步调用还是异步消息传递,如何保证消息可靠性?SOA由ESB来集成所有的消息:都需要统一的Gateway来汇