jsonrpc
把jsonrpc.ServeConn(conn)改为grpc.ServeConn(conn)就是grpc了
为什么用jsongrpc?
因为grpc使用的为go语言独有的数据序列化gob,导致其他语言无法解析;
使用jsonrpc的话数据序列化就使用json这样其他语言都可以解析使用
服务端
type user struct {
}
func (u user) Hello(name string, res *string) error {
*res = name + " over!"
return nil
}
func main() {
// 注册服务
err := rpc.RegisterName("hello", new(user))
if err != nil {
fmt.Println("rpc.RegisterName err", err)
return
}
// 启动监听
listen, err := net.Listen("tcp", "localhost:8888")
if err != nil {
fmt.Println("net.Listen err", err)
return
}
for true {
// 建立连接
conn, err := listen.Accept()
if err != nil {
fmt.Println("listen.Accept err", err)
return
}
// 绑定服务
go jsonrpc.ServeConn(conn)
}
}
客户端
func main() {
// 连接
dial, err := jsonrpc.Dial("tcp", "localhost:8888")
if err != nil {
fmt.Println("jsonrpc.Dial", err)
return
}
var str string
// 调用
err = dial.Call("hello.Hello", "zz", &str)
if err != nil {
fmt.Println("dial.Call err", err)
}
fmt.Println("aaaa:", str)
}
缺点
这样写代码的话会导致代码量太高,注册服务不好查看
改进
使用protobuf来编写服务
protobuf
谷歌出的一种数据序列化语言,和其他数据序列化语言比较,protobuf简单、数据之间传输更小、更快
语法
详细语法百度查看
// 声明版本
syntax = "proto3";
// 包
package pb;
// 生成go语言所在包
option go_package = "../pb";
// 相当于struct
message Tea {
int32 age = 1;
string name = 2;
}
// 服务
service HelloWorld {
rpc Test1(Tea) returns (Tea);
}
命令
proto编译grpc
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
./*.proto
proto编译getaway
protoc \
--go_out . --go_opt paths=source_relative \
--go-grpc_out . --go-grpc_opt paths=source_relative \
--grpc-gateway_out . --grpc-gateway_opt paths=source_relative \
./proto/rest.proto
protoGRPC
入门篇
.proto
使用命令编译为go文件。编译命令看上文
syntax = "proto3";
package pb;
option go_package = "../pb";
message Tea {
int32 age = 1;
string name = 2;
}
service HelloWorld {
rpc Test1(Tea) returns (Tea);
}
server.go
type stu struct {
}
/* 实现pb下的服务端接口
type HelloWorldServer interface {
Test1(context.Context, *Tea) (*Tea, error)
}
*/
func (s stu) Test1(ctx context.Context, tea *pb.Tea) (*pb.Tea, error) {
tea.Name = "hello"
tea.Age = 66
return tea, nil
}
func main() {
// 创建服务
server := grpc.NewServer()
// 注册服务
pb.RegisterHelloWorldServer(server, new(stu))
// 启动监听
listen, err := net.Listen("tcp", "localhost:9999")
if err != nil {
fmt.Println(err)
}
// 启动服务
server.Serve(listen)
}
client.go
客户端不需要实现接口
grpc.WithInsecure():后边会解决这个问题
func main() {
// 连接
dial, err := grpc.Dial("localhost:9999", grpc.WithInsecure())
if err != nil {
fmt.Println(err)
}
// 创建客户端
client := pb.NewHelloWorldClient(dial)
tea := new(pb.Tea)
tea.Name = "abc"
// 调用服务
t, err := client.Test1(context.TODO(), tea)
if err != nil {
fmt.Println(err)
}
// 输出返回值
fmt.Println(t.Name, "-->", t.Age)
}
进阶篇
.proto
流式传输,相当于一个不断开的连接可以一直传输数据
syntax = "proto3";
package pb;
option go_package = "./proto";
message Req {
string message = 1;
}
message Res {
string message = 1;
}
service HelloGRPC {
rpc SayHi(Req) returns (Res);
// 输入流
rpc SayHiIn(stream Req) returns (Res);
// 输出流
rpc SayHiOut(Req) returns (stream Res);
// 输入输出流
rpc SayHiIO(stream Req) returns (stream Res);
}
server.go/client.go
服务端注册
func main() {
listen, err := net.Listen("tcp", ":8888")
if err != nil {
fmt.Println("注册失败", err.Error())
return
}
defer listen.Close()
newServer := grpc.NewServer()
pb.RegisterHelloGRPCServer(newServer, &server{
})
newServer.Serve(listen)
reflection.Register(newServer)
}
客户端调用
func main() {
conn, err := grpc.Dial(":8888", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Println("连接失败", err.Error())
return
}
defer conn.Close()
client := pb.NewHelloGRPCClient(conn)
//sayHi(client)
//sayHiIn(client)
//sayHiOut(client)
sayHiIO(client)
}
服务端实现接口
/*
实现pb的服务端接口
type HelloGRPCServer interface {
SayHi(context.Context, *Req) (*Res, error)
SayHiIn(HelloGRPC_SayHiInServer) error
SayHiOut(*Req, HelloGRPC_SayHiOutServer) error
SayHiIO(HelloGRPC_SayHiIOServer) error
mustEmbedUnimplementedHelloGRPCServer()
}
*/
type server struct {
pb.UnimplementedHelloGRPCServer
}
客户端不需要实现
普通传输
服务端
func (s server) SayHi(ctx context.Context, req *pb.Req) (*pb.Res