警告
本文最后更新于 2020-12-27,文中内容可能已过时。
Router模块脑图
现在我们需要给用户一个自定的Conn处理业务的接口,需要用户传入所需要处理的业务,而不是写死的回显方法。我们需要定义一些 interface{}
来让用户填写任意格式的连接处理业务方法。
那么,很显然func不能满足我们的需求,接下来就是定义抽象的接口类。
我们需要把客户端请求的连接信息和请求的数据,放在一个 Request
的结构体里,这样的好处是我们可以从Request里面得到全部客户端的请求信息,也为我们之后扩展框架有一定的作用,一旦客户端有额外的含义的数据信息,都可以放在这个Request中。可以理解为每次客户端的全部请求数据,Zinx都会把它们放在一个Request结构体中。
在 ziface
下创建 irequest.go
1
2
3
4
5
6
7
8
|
// IRequest 接口
// 实际上是把客户端请求的链接信息和请求的数据包装到一个request中
type IRequest interface {
// 得到当前链接
GetConnection() IConnection
// 得到请求的消息数据
GetData() []byte
}
|
在 znet
下创建 request.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
type Request struct {
// 已经和客户端建立好的链接
conn ziface.IConnection
// 客户端请求的数据
data []byte
}
func (r *Request) GetConnection() ziface.IConnection {
return r.conn
}
func (r *Request) GetData() []byte {
return r.data
}
|
request
相关的封装好了,接下来是配置 Router
层,目的是为了在router里面携带服务要处理的业务
在 ziface
下创建 irouter.go
我们知道 router 实际上的作用就是,服务端应用可以给 Zinx 框架配置当前链接的处理业务方法,之前是写死的回显方法,有了router就可以通过router携带,并且会定义三个方法,且支持方法重写。
- PreHandle: 在处理conn业务之前的钩子方法Hook
- Handle: 在处理conn业务的主方法Hook
- PostHandle: 在处理conn业务之后的钩子方法Hook
每个方法的形参就是上面封装的 IRequest
对象,用来告知我们所要连的链接以及请求数据,作为我们业务方法的输入数据。
1
2
3
4
5
6
7
8
9
10
11
12
|
// IRouter 路由抽象接口
// 路由里的数据都是IRequest
type IRouter interface {
// 在处理conn业务之前的钩子方法Hook
PreHandle(request IRequest)
// 在处理conn业务的主方法Hook
Handle(request IRequest)
// 在处理conn业务之后的钩子方法Hook
PostHandle(request IRequest)
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
type BaseRouter struct{}
// PreHandle 在处理conn业务之前的钩子方法Hook
func (br *BaseRouter) PreHandle(request ziface.IRequest) {
}
// Handle 在处理conn业务的主方法Hook
func (br *BaseRouter) Handle(request ziface.IRequest) {
}
// PostHandle 在处理conn业务之后的钩子方法Hook
func (br *BaseRouter) PostHandle(request ziface.IRequest) {
}
|
我们注意到了,在实体的Router层里我们写了空的结构体 BaseRouter
,这里讲一下目的。在实现router时,我们要基于 BaseRouter
,也就是面向对象中的继承基类,继承之后就可以对这个基类的方法进行重写。
举个例子,有的router没有前置或后置处理业务,在继承基类后,有需要写的就直接重写方法即可,不需要的直接忽略即可。
我们需要给 IServer
结构体中添加一个方法 AddRouter
,目的是让框架使用者自定义一个Router处理业务方法。
在 zinx/ziface/iserver.go
修改如下:
1
2
3
4
5
6
7
8
9
10
|
type IServer interface {
// 启动
Start()
// 结束
Stop()
// 运行
Serve()
// 路由功能,给当前的服务注册一个路由方法
AddRouter(router IRouter)
}
|
在 zinx/znet/server.go
修改如下:
1
2
3
4
5
6
7
8
9
|
type Server struct {
Name string
IPVersion string
IP string
Port int
// 给当前的Server添加一个router,Server注册的链接对应的处理业务
Router ziface.IRouter
}
|
在初始化 NewServer()
方法中也要添加一个初始化成员
1
2
3
4
5
6
7
8
9
|
func NewServer(name string) ziface.IServer {
return &Server{
Name: name,
IPVersion: "tcp4",
IP: "0.0.0.0",
Port: 8999,
Router: nil, // 初始化为nil
}
}
|
还记得在之前临时在 Connection
中添加了一个 handleAPI ziface.HandleFunc
,目的是为了直接在业务处理方法,如今我们封装了Router,就是为了在Router中携带业务方法,所以可以删掉了,然后添加处理的 Router
对象,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 链接模块
type Connection struct {
// 当前链接的socket TCP套接字
Conn *net.TCPConn
// 链接的ID
ConnID uint32
// 当前的链接状态
isClosed bool
// 告知当前链接已经退出/停止的 channel
ExitChan chan bool
// 该链接处理的方法Router
Router ziface.IRouter
}
|
然后去实现接口中的 AddRouter
方法
1
2
3
4
|
func (s *Server) AddRouter(r ziface.IRouter) {
s.Router = r
log.Println("Add router success!")
}
|
同样在初始化 NewConnection
中添加router
1
2
3
4
5
6
7
8
9
10
|
// 初始化链接模块的方法
func NewConnection(conn *net.TCPConn, connID uint32, router ziface.IRouter) *Connection {
return &Connection{
Conn: conn,
ConnID: connID,
isClosed: false,
Router: router,
ExitChan: make(chan bool, 1),
}
}
|
接下来当然是去处理之前在 StartReader()
中写的调用 HandleFunc
方法
删掉:
1
2
3
4
|
if err := c.handleAPI(c.Conn, buf, cnt); err != nil {
log.Println("ConnID = ", c.ConnID, "Handle is error: ", err)
break
}
|
添加:
1
2
3
4
5
6
7
8
9
10
11
|
// 从路由中,找到注册绑定的Conn对应的router调用
req := Request{
conn: c,
data: buf,
}
// 执行注册的路由方法
go func(request ziface.IRequest) {
c.Router.PreHandle(request)
c.Router.Handle(request)
c.Router.PostHandle(request)
}(&req)
|
以上完成了Zinx的路由模块功能,接下来是测试了。
拷贝之前的 v2 测试包重命名为 v3。修改 Server.go
,我们实现一个 IRouter
,并重写里面的三个方法,如下:
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
|
type PingRouter struct {
znet.BaseRouter
}
// PreHandle 在处理conn业务之前的钩子方法Hook
func (p *PingRouter) PreHandle(request ziface.IRequest) {
log.Println("Call Router PreHandle")
_, err := request.GetConnection().GetTCPConnection().Write([]byte("before.ping...\n"))
if err != nil {
log.Println("before ping error is ", err)
}
}
// Handle 在处理conn业务的主方法Hook
func (p *PingRouter) Handle(request ziface.IRequest) {
log.Println("Call Router Handle")
_, err := request.GetConnection().GetTCPConnection().Write([]byte("ping...\n"))
if err != nil {
log.Println("ping error is ", err)
}
}
// PostHandle 在处理conn业务之后的钩子方法Hook
func (p *PingRouter) PostHandle(request ziface.IRequest) {
log.Println("Call Router PostHandle")
_, err := request.GetConnection().GetTCPConnection().Write([]byte("after.ping...\n"))
if err != nil {
log.Println("after ping error is ", err)
}
}
|
在 main
方法中添加 Router
1
2
3
4
5
6
7
8
|
func main() {
server := znet.NewServer("Zinx v3")
// 添加自定义router
server.AddRouter(&PingRouter{})
server.Serve()
}
|
测试结果如下图:(可以看出三个方法按照链式进行)
测试结果
现在Zinx框架的路由只是单路由结构,后续会增加多路由功能