golang python rpc_grpc - 使用 golang 带你从头撸一套 RPC 服务(一)

gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

安装 protobuf

grpc使用protobuf作为IDL(interface descriton language),且要求protobuf 3.0以上,这里我们直接选用当前最新版本 3.8,git下载地址。

选择操作系统对应的版本下载,这里我们直接使用已经编译好的protoc可执行文件(或者下载安装包编译安装)。wget https://github.com/protocolbuffers/protobuf/releases/download/v3.8.0-rc1/protoc-3.8.0-rc-1-linux-x86_64.zip

tar -zxvf protoc-3.8.0-rc-1-linux-x86_64.zip

mv protoc-3.8.0-rc-1-linux-x86_64 /usr/local/protoc

ln -s /usr/local/protoc/bin/protoc /usr/local/bin/protoc

#查看 protoc

protoc --version

安装 protoc-gen-go

protoc-gen-go是Go的protoc编译插件,protobuf内置了许多高级语言的编译器,但没有Go的。# 运行 protoc -h 命令可以发现内置的只支持以下语言

protoc -h

...

--cpp_out=OUT_DIR Generate C++ header and source.

--csharp_out=OUT_DIR Generate C# source file.

--java_out=OUT_DIR Generate Java source file.

--js_out=OUT_DIR Generate JavaScript source.

--objc_out=OUT_DIR Generate Objective C header and source.

--php_out=OUT_DIR Generate PHP source file.

--python_out=OUT_DIR Generate Python source file.

--ruby_out=OUT_DIR Generate Ruby source file.

...

所以我们使用protoc编译生成Go版的grpc时,需要先安装此插件。go get -u github.com/golang/protobuf/protoc-gen-go

安装 grpc-go 库

grpc-go包含了Go的grpc库。

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

git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto

cd $GOPATH/src/

go install google.golang.org/grpc

Grpc的四种模式

grpc借口的类型分为一下四种: A为接受参数,B为返回参数rpc GetFeature(Point) returns (Feature) {} 普通调用:A-B

rpc ListFeatures(Rectangle) returns (stream Feature) {} 单向流:A - B(流)

rpc RecordRoute(stream Point) returns (RouteSummary) {} 单向流:A(流) - B

rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} 双向流:A(流) - B(流)

我们只演示最基础的普通调用,开始撸。

Go实例

下面我们将创建一个提供CURD操作的User服务。#创建一个 grpc 服务库

mkdir $GOPATH/src/grpc/user -p && cd $GOPATH/src/grpc

定义服务接口

使用proto3 IDL 编写如下的用户服务,proto3语法文档。

同proto2相比,proto3更为简洁,不再需要使用required/optional来区分必须/可选参数,各参数根据自身数据类型自行设定默认值。

proto3的标量数据类型(repeated emun 复合数类型没有在其中提及):

vi user.protosyntax = "proto3";

// user 包

package user;

// 指定 go 的包路径及包名

// option go_package="app/services;services";

// 指定 php 的命名空间

// option php_namespace="App\\Services";

// User 服务及服务接口的定义

service User {

rpc UserIndex(UserIndexRequest) returns (UserIndexResponse) {}

rpc UserView(UserViewRequest) returns (UserViewResponse) {}

rpc UserPost(UserPostRequest) returns (UserPostResponse) {}

rpc UserDelete(UserDeleteRequest) returns (UserDeleteResponse) {}

}

// 枚举类型

enum EnumUserSex {

SEX_INIT = 0; // 枚举类型必须以 0 起始

SEX_MALE = 1;

SEX_FEMALE = 2;

}

// 用户实体模型

message UserEntity {

string name = 1;

int32 age = 2;

}

// User 服务的各个接口的请求/响应结构

message UserIndexRequest {

int32 page = 1;

int32 page_size = 2;

}

message UserIndexResponse {

int32 err = 1;

string msg = 2;

// 返回一个 UserEntity 对象的列表数据

repeated UserEntity data = 3;

}

message UserViewRequest {

int32 uid = 1;

}

message UserViewResponse {

int32 err = 1;

string msg = 2;

// 返回一个 UserEntity 对象

UserEntity data = 3;

}

message UserPostRequest {

string name = 1;

string password = 2;

int32 age = 3;

}

message UserPostResponse {

int32 err = 1;

string msg = 2;

}

message UserDeleteRequest {

int32 uid = 1;

}

message UserDeleteResponse {

int32 err = 1;

string msg = 2;

}

生成接口库# 加载 protoc-gen-go 插件 生成 grpc 的 go 服务端/客户端

# 注意要安装好 protoc-gen-go 插件

protoc -I. --go_out=plugins=grpc:./user user.proto

编写服务端

vi server.gopackage main

import (

"context"

"log"

"net"

"google.golang.org/grpc"

"google.golang.org/grpc/reflection"

user "grpc/user"

)

const (

port = ":50051"

)

type UserService struct {

// 实现 User 服务的业务对象

}

// UserService 实现了 User 服务接口中声明的所有方法

func (userService *UserService) UserIndex(ctx context.Context, in *user.UserIndexRequest) (*user.UserIndexResponse, error) {

log.Printf("receive user index request: page %d page_size %d", in.Page, in.PageSize)

return &user.UserIndexResponse{

Err: 0,

Msg: "success",

Data: []*user.UserEntity{

{Name: "big_cat", Age: 28},

{Name: "sqrt_cat", Age: 29},

},

}, nil

}

func (userService *UserService) UserView(ctx context.Context, in *user.UserViewRequest) (*user.UserViewResponse, error) {

log.Printf("receive user view request: uid %d", in.Uid)

return &user.UserViewResponse{

Err: 0,

Msg: "success",

Data: &user.UserEntity{Name: "james", Age: 28},

}, nil

}

func (userService *UserService) UserPost(ctx context.Context, in *user.UserPostRequest) (*user.UserPostResponse, error) {

log.Printf("receive user post request: name %s password %s age %d", in.Name, in.Password, in.Age)

return &user.UserPostResponse{

Err: 0,

Msg: "success",

}, nil

}

func (userService *UserService) UserDelete(ctx context.Context, in *user.UserDeleteRequest) (*user.UserDeleteResponse, error) {

log.Printf("receive user delete request: uid %d", in.Uid)

return &user.UserDeleteResponse{

Err: 0,

Msg: "success",

}, nil

}

func main() {

lis, err := net.Listen("tcp", port)

if err != nil {

log.Fatalf("failed to listen: %v", err)

}

// 创建 RPC 服务容器

grpcServer := grpc.NewServer()

// 为 User 服务注册业务实现 将 User 服务绑定到 RPC 服务容器上

user.RegisterUserServer(grpcServer, &UserService{})

// 注册反射服务 这个服务是CLI使用的 跟服务本身没有关系

reflection.Register(grpcServer)

if err := grpcServer.Serve(lis); err != nil {

log.Fatalf("failed to serve: %v", err)

}

}

编写客户端

vi client.gopackage main

import (

"context"

"fmt"

"log"

"time"

"google.golang.org/grpc"

user "grpc/user"

)

const (

address = "localhost:50051"

)

func main() {

//建立链接

conn, err := grpc.Dial(address, grpc.WithInsecure())

if err != nil {

log.Fatalf("did not connect: %v", err)

}

defer conn.Close()

userClient := user.NewUserClient(conn)

// 设定请求超时时间 3s

ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)

defer cancel()

// UserIndex 请求

userIndexReponse, err := userClient.UserIndex(ctx, &user.UserIndexRequest{Page: 1, PageSize: 12})

if err != nil {

log.Printf("user index could not greet: %v", err)

}

if 0 == userIndexReponse.Err {

log.Printf("user index success: %s", userIndexReponse.Msg)

// 包含 UserEntity 的数组列表

userEntityList := userIndexReponse.Data

for _, row := range userEntityList {

fmt.Println(row.Name, row.Age)

}

} else {

log.Printf("user index error: %d", userIndexReponse.Err)

}

// UserView 请求

userViewResponse, err := userClient.UserView(ctx, &user.UserViewRequest{Uid: 1})

if err != nil {

log.Printf("user view could not greet: %v", err)

}

if 0 == userViewResponse.Err {

log.Printf("user view success: %s", userViewResponse.Msg)

userEntity := userViewResponse.Data

fmt.Println(userEntity.Name, userEntity.Age)

} else {

log.Printf("user view error: %d", userViewResponse.Err)

}

// UserPost 请求

userPostReponse, err := userClient.UserPost(ctx, &user.UserPostRequest{Name: "big_cat", Password: "123456", Age: 29})

if err != nil {

log.Printf("user post could not greet: %v", err)

}

if 0 == userPostReponse.Err {

log.Printf("user post success: %s", userPostReponse.Msg)

} else {

log.Printf("user post error: %d", userPostReponse.Err)

}

// UserDelete 请求

userDeleteReponse, err := userClient.UserDelete(ctx, &user.UserDeleteRequest{Uid: 1})

if err != nil {

log.Printf("user delete could not greet: %v", err)

}

if 0 == userDeleteReponse.Err {

log.Printf("user delete success: %s", userDeleteReponse.Msg)

} else {

log.Printf("user delete error: %d", userDeleteReponse.Err)

}

}

启动服务/请求服务go run server.go

# 新建一个窗口

go run client.go

运行结果#server

[root@localhost rpc]# go run server.go

2019/05/16 00:28:01 receive user index request: page 1 page_size 12

2019/05/16 00:28:01 receive user view request: uid 1

2019/05/16 00:28:01 receive user post request: name big_cat password 123456 age 29

2019/05/16 00:28:01 receive user delete request: uid 1

#client

[root@localhost rpc]# go run client.go

2019/05/16 00:28:01 user index success: success

big_cat 28

sqrt_cat 29

2019/05/16 00:28:01 user view success: success

james 28

2019/05/16 00:28:01 user post success: success

2019/05/16 00:28:01 user delete success: success

grpc面向服务接口编程大致就是这样啦,你可以更好的组织业务库,我这里全放在package main里了,后面的工作就是把数据库拉进来了。

你可能感兴趣的:(golang,python,rpc)