grpc支持多种语言, 也支持Golang
安装: 由于无法直接获取对应的包, 因此安装需要费点功夫折腾下
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}# 下载grpc-go
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genprotogit 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# 安装
cd $GOPATH/src/
go install google.golang.org/grpc
备注:
1.
github.com/golang/protobuf/{proto,protoc-gen-go}
可以直接下载访问github.com/golang/protobuf
下载对应文件2.net/text包可以不用下载,默认应该有,若没有,则下载,可不用放入
golang.org/x/
下, 可以引用即可【Golang grpc 入门示例】3.安装过程若执行不成功, 建议手动clone github文件,并将其放入到$GOPATH/src/golang.org 对应的目录下
4.proto 需加入path,方便进行命令执行
net/context 放入$GOPATH/src/golang.org/x
proto,protoc-gen-go放入$GOPATH/src/google.golang.org/genproto
grpc放入$GOPATH/src/google.golang.org/grpc
定义proto文件:
syntax = "proto3";
option java_package = "io.grpc.examples";
package helloworld;
// The greeter service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}// The request message containing the user's name.
message HelloRequest {
string name = 1;
}// The response message containing the greetings
message HelloReply {
string message = 1;
}
转换go文件:
protoc --go_out=plugins=grpc:. helloworld.proto
helloword入门
服务端server.go:
package mainimport (
"log"
"net"
"golang.org/x/net/context"
"google.golang.org/grpc"
pb "sr/helloworld"
)const (
port = ":50051"
)type server struct {}func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatal("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
s.Serve(lis)
}
客服端client.go:
package main//client.goimport (
"log"
"os" "golang.org/x/net/context"
"google.golang.org/grpc"
pb "sr/helloworld"
)const (
address= "localhost:50051"
defaultName = "world"
)func main() {
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatal("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn) name := defaultName
if len(os.Args) >1 {
name = os.Args[1]
}
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
if err != nil {
log.Fatal("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
}
运行server.go 和client.go
客户端输出:
2019/08/19 21:46:04 Greeting: Hello world
远程调用计算函数:
服务端:
package mainimport (
"errors"
"fmt"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"os"
)// 算数运算结构体
type Arith struct {
}// 算数运算请求结构体
type ArithRequest struct {
A int
B int
}// 算数运算响应结构体
type ArithResponse struct {
Pro int // 乘积
Quo int // 商
Rem int // 余数
}// 乘法运算方法
func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
res.Pro = req.A * req.B
return nil
}// 除法运算方法
func (this *Arith) Divide(req ArithRequest, res *ArithResponse) error {
if req.B == 0 {
return errors.New("divide by zero")
}
res.Quo = req.A / req.B
res.Rem = req.A % req.B
return nil
}func resp(conn net.Conn) {
fmt.Fprintf(os.Stdout, "%s", "new client in coming\n")
//jsonrpc.ServeConn(conn)
newcode := jsonrpc.NewServerCodec(conn)
//newcode.ReadRequestHeader(n)
rpc.ServeCodec(newcode)
}func main() {
rpc.Register(new(Arith)) // 注册rpc服务
rpc.HandleHTTP()
lis, err := net.Listen("tcp", "127.0.0.1:8096")
if err != nil {
log.Fatalln("fatal error: ", err)
} fmt.Fprintf(os.Stdout, "%s", "start connection") for {conn, err := lis.Accept() // 接收客户端连接请求
fmt.Println(conn.RemoteAddr().String())
if err != nil {
continue
}//rpc.ServeConn(conn)
go resp(conn)
//go func(conn net.Conn) { // 并发处理客户端请求
// fmt.Fprintf(os.Stdout, "%s", "new client in coming\n")
// jsonrpc.ServeConn(conn)
//}(conn)
//go jsonrpc.ServeConn(conn)
}
}
客户端:
package mainimport (
"fmt"
"log"
"net/rpc/jsonrpc"
)// 算数运算请求结构体
type ArithRequest struct {
A int
B int
}// 算数运算响应结构体
type ArithResponse struct {
Pro int // 乘积
Quo int // 商
Rem int // 余数
}func main() {
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8096")
if err != nil {
log.Fatalln("dailing error: ", err)
} req := ArithRequest{9, 2}
var res ArithResponse err = conn.Call("Arith.Multiply", req, &res) // 乘法运算
if err != nil {
log.Fatalln("arith error: ", err)
}
fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro) err = conn.Call("Arith.Divide", req, &res)
if err != nil {
log.Fatalln("arith error: ", err)
}
fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem)
}
输出:
服务端:
start connection127.0.0.1:49582
new client in coming
127.0.0.1:49583
new client in coming
客户端:
9 * 2 = 18
9 / 2, quo is 4, rem is 1
支持加密通信
SSL/TSL 加密: 服务端:
package main// server.goimport (
"fmt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/peer"
"log"
"net" "golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
pb "sr/helloworld"
)const (
port = ":50051"
)type server struct {}func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
fmt.Println("replay ... ")
pr, ok := peer.FromContext(ctx)
fmt.Println(pr.Addr.String()) md, ok := metadata.FromIncomingContext(ctx)
fmt.Println(md)
if !ok {
return nil, grpc.Errorf(codes.Unauthenticated, "无Token认证信息")
} var (
appidstring
appkey string
) if val, ok := md["appid"];
ok {
appid = val[0]
} if val, ok := md["appkey"];
ok {
appkey = val[0]
} if appid != "101010" || appkey != "i am key" {
return nil, grpc.Errorf(codes.Unauthenticated, "Token认证信息无效: appid=%s, appkey=%s", appid, appkey)
}
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatal("failed to listen: %v", err)
}
creds, err := credentials.NewServerTLSFromFile("./key/svr.pem", "./key/svr.key")
if err != nil {
grpclog.Fatalf("Failed to generate credentials %v", err)
}
s := grpc.NewServer(grpc.Creds(creds))
pb.RegisterGreeterServer(s, &server{})
s.Serve(lis)
}
客户端:
package main//client.goimport (
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"log"
"os" "golang.org/x/net/context"
"google.golang.org/grpc"
pb "cl/helloworld"
)const (
address= "localhost:50051"
defaultName = "world"
OpenTLS = true
)// customCredential 自定义认证
type customCredential struct{}func (c customCredential) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"appid":"101010",
"appkey": "i am key",
}, nil
}func (c customCredential) RequireTransportSecurity() bool {
if OpenTLS {
return true
}
return false
}func main() {
var opts []grpc.DialOption if OpenTLS {
creds, err := credentials.NewClientTLSFromFile("./key/svr.pem", "test.aaa.com")
if err != nil {
grpclog.Fatalf("Failed to create TLS credentials %v", err)
}
opts = append(opts, grpc.WithTransportCredentials(creds))
}else {
opts = append(opts, grpc.WithInsecure())
}
opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))
//creds, err := credentials.NewClientTLSFromFile("./key/svr.pem", "test.aaa.com")
//if err != nil {
// grpclog.Fatalf("Failed to create TLS credentials %v", err)
//}
//conn, err := grpc.Dial(address, grpc.WithInsecure())
//conn, err := grpc.Dial(address, grpc.WithTransportCredentials(creds))
conn, err := grpc.Dial(address, opts...)
if err != nil {
log.Fatal("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn) name := defaultName
if len(os.Args) >1 {
name = os.Args[1]
}
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
if err != nil {
log.Fatal("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
}
输出:
服务端:
replay ...
[::1]:49638
map[:authority:[test.aaa.com] content-type:[application/grpc] user-agent:[grpc-go/1.20.0-dev] appkey:[i am key] appid:[101010]]
客户端:
2019/08/19 22:01:42 Greeting: Hello world
如果证书异常或者证书过期, 提示如下错误:
2019/08/19 20:03:48 could not greet: %vrpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: authentication handshake failed: x509: certificate has expired or is not yet valid"
则需要更新证书