rpc
# RPC
# 什么是RPC
Remote Procedure Call Protocol —— 远程过程调用协议
IPC: 进程间通信
RPC:远程进通信 —— 应用层协议(http协议同层)。底层使用 TCP 实现。
回顾:
OSI 7 层模型架构:物、数、网、传、会、表、应
TCP/IP 4 层架构:链路层、网络层、传输层、应用层
理解RPC:
- ==像调用本地函数一样,去调用远程函数。==
- 通过rpc协议,传递:函数名、函数参数。达到在本地,调用远端函数,得返回值到本地的目标。
- ==像调用本地函数一样,去调用远程函数。==
为什么微服务使用 RPC:
每个服务都被封装成 进程。彼此”独立“。
进程和进程之间,可以使用不同的语言实现。
# socket回顾
-远程网络通信-
回顾:Go语言 一般性 网络socket通信
server端:
net.Listen() —— listener 创建监听器
listener.Accpet() —— conn 启动监听,建立连接
conn.read()
conn.write()
defer conn.Close() / listener.Close()
client端:
net.Dial() —— conn
conn.Write()
conn.Read()
defer conn.Close()
# rpc使用步骤
---- 服务端:
注册 rpc 服务对象。给对象绑定方法( 1. 定义类, 2. 绑定类方法 )
rpc.RegisterName("服务名",回调对象)
1创建监听器
listener, err := net.Listen()
1建立连接
conn, err := listener.Accept()
1将连接 绑定 rpc 服务。
rpc.ServeConn(conn)
1
---- 客户端:
用 rpc 连接服务器。
conn, err := rpc.Dial()
1调用远程函数。
conn.Call("服务名.方法名", 传入参数, 传出参数)
1
# 代码
Server
package main
import (
"fmt"
"net"
"net/rpc"
"strings"
)
type World struct {
}
func (w World) HelloWorld(name string, resp *string) error {
*resp = name + strings.ToUpper(name)
return nil
}
func main() {
err := rpc.RegisterName("world", new(World))
if err != nil {
fmt.Println("Register world", err)
return
}
listen, err := net.Listen("tcp", "127.0.0.1:8088")
if err != nil {
fmt.Println("net.Listen", err)
}
defer listen.Close()
fmt.Println("开始监听...")
conn, err := listen.Accept()
if err != nil {
fmt.Println("accept error - >", err)
}
defer conn.Close()
fmt.Println("连接建立成功...")
rpc.ServeConn(conn)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
client
package main
import (
"fmt"
"net/rpc"
)
func main() {
// gob序列化
client, err := rpc.Dial("tcp", "127.0.0.1:8088")
if err != nil {
fmt.Println("rpc.Dial", err)
}
defer client.Close()
var resp = new(string)
err = client.Call("world.HelloWorld", "feng", &resp)
if err == nil {
fmt.Println("resp ===> ", *resp)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# json版
rpc替换为jsonrpc即可
echo -e '{"method":"world.HelloWorld","params":["李白"],"id":0}' | nc 127.0.0.1 8800
如果,绑定方法返回值的 error 不为空? 无论传出参数是否有值,服务端都不会返回数据。
# rpc封装
package main
import (
"net/rpc"
"net/rpc/jsonrpc"
)
type IHelloWorld interface {
HelloWorld(name string, resp *string) error
}
func registerHelloWorld(helloWorld IHelloWorld) error {
return rpc.RegisterName("world", helloWorld)
}
type IClient struct {
c *rpc.Client
}
func InitClient(network string, address string) (IClient, error) {
client, err := jsonrpc.Dial(network, address)
return IClient{client}, err
}
func (client *IClient) HelloWorld(arg string, resp *string) error {
return client.c.Call("world.HelloWorld", arg, resp)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Protobuf
# 编写的注意事项
- message 成员编号, 可以不从1开始, 但是不能重复. -- 不能使用 19000 - 19999
- 可以使用 message 嵌套。
- 定义数组、切片 使用 repeated 关键字
- 可以使用枚举 enum
- 可以使用联合体。 oneof 关键字。成员编号,不能重复。
# 编译protobuf
回顾:C++ 编译 命令:
protoc --cpp_out=./ *.proto ---> xxx.pb.cc 和 xxx.pb.h 文件
- go 语言中 编译命令:
protoc --go_out=./ *proto
---> xxx.pb.go 文件。
# https://github.com/protocolbuffers/protobuf-go/releases/tag/v1.28.1
export GOPATH=$HOME/sdk/go1.17.1/
export GOBIN=$GOPATH/bin
export PATH="$GOBIN:$PATH"
mv protoc-gen-go to $GOPATH/bin
# google.golang.org
mkdir google.golang.org
cd google.golang.org
git clone git@github.com:protocolbuffers/protobuf-go.git
mv protobuf-go protobuf
2
3
4
5
6
7
8
9
10
# 添加rpc服务
- 语法
service 服务名 {
rpc 函数名(参数,消息体) returns (返回值,消息)
}
2
3
Mac配置gRpc
brew install go #运行完可不修改环境变量
brew install automake
brew install libtool
brew install protobuf
2
3
4
5
6
7
# brew
brew install grpc
brew install protobuf
brew install protoc-gen-go
brew install protoc-gen-go-grpc
2
3
4
5
# gitclone/go get
git clone git@github.com:golang/protobuf
# 编译protoc-gen-go
go install /src/google.golang.org/protobuf
# 获取package
go get -u google.golang.org/grpc
# 失败的话
git clone https://github.com/grpc/grpc-go.git $env:GOPATH\src\google.golang.org\grpc
git clone https://github.com/golang/net.git $env:GOPATH\src\golang.org\x\net
git clone https://github.com/golang/text.git $env:GOPATH\src\golang.org\x\text
git clone https://github.com/google/go-genproto.git $env:GOPATH\src\google.golang.org\genproto
# 编译protoc-gen-go
protoc-gen-go-grpc
# 编译
protoc --go-grpc_out=./ *.proto
protoc --go_out=./ *proto
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
编译完成将exec文件放goroot/bin目录即可
运行protoc --go-grpc_out=./ *.proto
# grpc-server
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"net"
"pb"
)
type World2 struct {
pb.UnimplementedHelloServer
}
func (this *World2) HelloWorld(c context.Context, x *pb.Teacher) (*pb.Teacher, error) {
x.Name = "xx"
x.Age = "1"
return x, nil
}
func main() {
var grpcServer = grpc.NewServer()
pb.RegisterHelloServer(grpcServer, new(World2))
listen, err := net.Listen("tcp", "127.0.0.1:8800")
if err != nil {
fmt.Println("net.Listen", err)
}
defer listen.Close()
fmt.Println("开始监听...")
//conn, err := listen.Accept()
//if err != nil {
// fmt.Println("accept error - >", err)
//}
//defer conn.Close()
//fmt.Println("连接建立成功...")
//rpc.ServeConn(conn)
grpcServer.Serve(listen)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# grpc-client
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"pb"
)
func main() {
dial, err := grpc.Dial("127.0.0.1:8800", grpc.WithInsecure())
if err != nil {
fmt.Println("net.Listen", err)
}
defer dial.Close()
client := pb.NewHelloClient(dial)
teacher, err := client.HelloWorld(context.TODO(), new(pb.Teacher))
fmt.Println(teacher.Name)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21