grpc是一个服务器,用于定义服务,指定方法,客户端可以通过他直接调用不同服务器上的方法。轻松创建分布式服务。
实现客户端跨语言调用不同服务器服务端的方法
proto buffer 称为协议缓冲区,用于定义结构化数据,并使用编译器,将数据生成指定语言的接口方法,其中包括客户端和服务器代码,还有其他序列化代码;
结构化定义(定义方法和参数),参考:Protocol Buffers官方文档
由定义生成go代码,使用命令
protoc --go_out=. snowflake.proto
或
protoc --go_out=plugins=grpc:. snowflake.proto
或
protoc -I proto --go_out=plugins=grpc:proto proto/snowflake.proto
其他语言转化
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto
使用grpc完成GetName接口,从数据持久化到测试。
用于持久化数据库;实现最基础的底层GetName方法。
package service
import (
"errors"
"github.com/gitstliu/go-id-worker"
"imcs/common/config"
"strconv"
)
var worker *idworker.IdWorker
//初始化
func init() {
workerId := config.GetInt64("snowflake.worker-id")
dataCenterId := config.GetInt64("snowflake.data-center-id")
worker = &idworker.IdWorker{}
if err := worker.InitIdWorker(workerId, dataCenterId); err != nil {
panic(err)
}
}
func GetName(mess string) (string, error) {
replee := "worker test +++" + mess
return replee, nil
}
组合底层方法,实现业务功能。
注意:
server中的方法必须同下面proto buffer中定义的GetName方法一致。
package server
import (
"context"
"errors"
"imcs/proto"
"imcs/snowflake/service"
)
//grpc server
type SnowflakeGRPCServer struct {
}
func (s *SnowflakeGRPCServer) GetName(ctx context.Context, request *proto.SnowflakeRequest) (*proto.SnowflakeResponse, error) {
response := new(proto.SnowflakeResponse)
if request.Messs != "" {
if replee, err := service.GetName(request.Messs); err == nil {
response.Replee = replee
} else {
return nil, err
}
} else {
return nil, errors.New("The count should greater than 0!")
}
return response, nil
}
定义客户端接口、服务端接口和代码语言,并完成调用;
syntax = "proto3"; // 最新版,支持多种语言转化
option java_package = "cc.iooc.common.rpc.snowflake.proto";
option java_multiple_files = true;
package proto;
service Snowflake { // 定义服务方法
rpc GetName (SnowflakeRequest) returns (SnowflakeResponse) {
}
}
//定义参数
message SnowflakeRequest {
int32 count = 1;
string messs = 2;
}
message SnowflakeResponse {
repeated string ids = 1;
string replee = 2;
}
然后,使用不同的proto buffer 实现接口的语言转化和代码的生成;
操作:在proto文件同级目录下,使用命令
protoc -I proto --go_out=plugins=grpc:proto proto/snowflake.proto
自动生成代码snowflake.pb.go
动态获取客户,调用转化层的GetName接口;
package client
import (
"context"
"google.golang.org/grpc"
"imcs/common/config"
"imcs/common/log"
"imcs/common/rpc"
"imcs/proto"
)
type SnowflakeGRPCClient struct {
Address string
}
func NewSnowflakeGRPCClient() *SnowflakeGRPCClient {
return &SnowflakeGRPCClient{Address: config.GetString("grpc.client.snowflake.address")}
}
func (s *SnowflakeGRPCClient) GetName(mess string) string {
// 到grpc连接池获取当前客户的grpc连接
conn, err := rpc.GetConn(s.Address)
if err != nil {
log.Errorf("snowflake client: %v", err)
return "nil"
}
// 方法最后,关闭连接
defer rpc.Close(s.Address, conn)
response, err := func(conn *grpc.ClientConn) (interface{}, error) {
// 调用grpc生成的客户端
client := proto.NewSnowflakeClient(conn)
// 调用grpc生成的接口及其实现方法
// 给proto生成的请求对象(SnowflakeRequest)的属性(Mess)设置值
response, err := client.GetName(context.Background(), &proto.SnowflakeRequest{Messs: mess})
return response, err
}(conn)
if err != nil {
log.Error(err)
return "nil"
}
// 从生成的相应对象(SnowflakeResponse)中获取属性值(Replee)作为返回值
return response.(*proto.SnowflakeResponse).Replee
}
使用go语言自带的测试类,对客户端获取到的接口进行测试,包括:功能测试、压力测试。
注意:
测试文件的名称和测试方法的名称按规范来,请网上查询。
package client
import (
"fmt"
"testing"
)
func TestGetName(t *testing.T) {
model := NewSnowflakeGRPCClient()
fmt.Println(model.GetName("name test ====>"))
}
用于微服务之间的调用和拦截。
这层可以选配
package server
import (
"context"
"encoding/json"
"errors"
"imcs/common/response"
"imcs/proto"
"imcs/snowflake/service"
)
//api server
type SnowflakeApiServer struct {
}
func (s *SnowflakeApiServer) Request(ctx context.Context, request *proto.GatewayRequest) (*proto.GatewayResponse, error) {
resp := new(proto.GatewayResponse)
method := request.Method
params := map[string]interface{}{}
err := json.Unmarshal(request.Param, ¶ms)
if err != nil {
return nil, err
}
var baseResp *response.BaseResponse
switch method {
case "snowflake.id":
baseResp, err = id()
case "snowflake.ids":
baseResp, err = ids(params)
case "snowflake.GetName":
baseResp, err = GetName(params)
default:
return nil, errors.New("snowflake method not found")
}
if err == nil {
if respBytes, err := json.Marshal(*baseResp); err == nil {
resp.Result = respBytes
return resp, nil
} else {
return nil, err
}
}
return nil, err
}
func GetName(params map[string]interface{}) (r *response.BaseResponse, err error) {
countInter := params["mess"]
if mess, ok := countInter.(string); ok {
if replee, e := service.GetName(mess); e != nil {
err = e
} else {
r = response.NewBaseResponseOkWithData(replee)
}
} else {
err = errors.New("snowflake replee params error")
}
return
}