1
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
// server/main.go
package main
import (
"net"
"net/http"
"net/rpc"
"net/rpc/jsonrpc"
"log"
)
type HelloService struct {}
// Hello的逻辑就是将对方发送的消息前面添加一个Hello然后返还给对方
// 由于是一个rpc服务,因此参数上面还是有约束:
// 第一个参数是请求
// 第二个参数是响应
// 可以类比 Http handler
func (p *HelloService) Hello(request string, reply *string) error {
*reply = "Hello: " + request
return nil
}
func (p *HelloService) Echo(request string, reply *string) error {
*reply = "Hello: " + request
return nil
}
// 不可跨语言,默认gob序列化
func tcpServer(){
//1. 实例化一个server
listener, err := net.Listen("tcp", ":8002")
if err != nil {
log.Fatal("ListenTCP error:", err)
}
//2. 注册处理逻辑 handler
// 把 HelloService 对象注册成一个 rpc 的 receiver
// 其中rpc.Register函数调用会将对象类型中所有满足RPC规则的对象方法注册为RPC函数,
// 所有注册的方法会放在"HelloService"服务空间之下
_ = rpc.RegisterName("HelloService", &HelloService{})
//3. 启动服务
// 通过 rpc.ServeConn函数在该TCP链接上为对⽅提供RPC服务。
// 每Accept一个请求,就创建一个 goroutie 进行处理
for {
// 从监听里获取一个连接
conn, err := listener.Accept() // Accept 调用阻塞,当一个新的连接进来的时候调用返回
if err != nil {
log.Fatal("Accept error:", err)
}
// 将获取的连接交给RPC
// 前面都是tcp的知识,到这个RPC就接管了
// 因此,可以认为 rpc 帮我们封装了消息到函数调用的这个逻辑,
// 提升了工作效率,逻辑比较简洁
go rpc.ServeConn(conn)
}
}
// 可跨语言
func tcpJsonServer(){
//1. 实例化一个server
listener, err := net.Listen("tcp", ":8001")
if err != nil {
log.Fatal("ListenTCP error:", err)
}
//2. 注册处理逻辑 handler
_ = rpc.RegisterName("HelloService", &HelloService{})
//3. 启动服务
for {
// 从监听里获取一个连接
conn, err := listener.Accept() // Accept 调用阻塞,当一个新的连接进来的时候调用返回
if err != nil {
log.Fatal("Accept error:", err)
}
go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
}
}
func httpServer(){
_ = rpc.RegisterName("HelloService", &HelloService{})
rpc.HandleHTTP()
http.ListenAndServe(":8000", nil)
}
func main(){
go tcpServer()
go tcpJsonServer()
httpServer()
}
// client/main.go
package main
import (
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
func TcpClient(){
//1. 建立连接
client, err := rpc.Dial("tcp", "localhost:8002")
if err != nil {
panic("连接失败")
}
var reply string //string有默认值
//调用连接。
// 然后通过client.Call调⽤具体的RPC⽅法
// 在调⽤client.Call时:
// 第⼀个参数是⽤点号链接的RPC服务名字和⽅法名字,
// 第⼆和第三个参数分别为定义RPC⽅法的两个参数:
// 第二个参数:请求参数
// 第三个参数:请求响应,必须是一个指针,由底层的rpc服务负责赋值
err = client.Call("HelloService.Hello", "bobby...", &reply)
if err != nil {
fmt.Println(err)
//panic("调用失败")
}
fmt.Println(reply)
err = client.Call("HelloService.Echo", "echo...", &reply)
if err != nil {
fmt.Println(err)
//panic("调用失败")
}
fmt.Println(reply)
}
func TcpJsonClient(){
//1. 建立连接
conn, err := net.Dial("tcp", "localhost:8001")
if err != nil {
panic("连接失败")
}
var reply string //string有默认值
client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
err = client.Call("HelloService.Hello", "bobby", &reply)
if err != nil {
panic("调用失败")
}
fmt.Println(reply)
err = client.Call("HelloService.Echo", "echo...", &reply)
if err != nil {
fmt.Println(err)
//panic("调用失败")
}
fmt.Println(reply)
}
func HttpClient(){
//1. 建立连接
client, err := rpc.DialHTTP("tcp", "localhost:8000")
if err != nil {
panic("连接失败")
}
var reply string //string有默认值
//调用连接。
err = client.Call("HelloService.Hello", "bobby", &reply)
if err != nil {
fmt.Println(err)
//panic("调用失败")
}
fmt.Println(reply)
err = client.Call("HelloService.Echo", "echo...", &reply)
if err != nil {
fmt.Println(err)
//panic("调用失败")
}
fmt.Println(reply)
}
func main() {
TcpClient()
HttpClient()
TcpJsonClient()
}
|