Skip to content

02 Zinx Router

刘丹冰 edited this page May 11, 2023 · 2 revisions

Case Source Code : https://github.com/aceld/zinx-usage/tree/main/zinx_router

1. IRouter

Zinx supports setting different routing and callback business modes for binary message bodies with different MsgID. The interface definition of IRouter is as follows:

package ziface

/*
    The router interface, where the router is the processing business method customized by the framework user for the link.
    The IRequest in the router contains the link information of the link and the request data information of the link.
*/
type IRouter interface {
    PreHandle(request IRequest)  // The hook method before processing conn business
    Handle(request IRequest)     // The method for processing conn business
    PostHandle(request IRequest) // The hook method after processing conn business
}

The overall business invocation chain is PreHandle()-->Handle()-->PostHandle(). Developers can implement specific Router instances and bind routes to Server/Client instances through the AddRouter() method. An example of specific application instance is as follows.

2. Router in Zinx Server

The code directory structure is as follows:

└── router-usage
    ├── client
    │   ├── main.go
    │   └── router
    │       └── pong.go
    ├── common
    │   └── proto.go
    └── server
        ├── main.go
        └── router
            └── ping.go

server/main.go

package main

import (
    "github.com/aceld/zinx/znet"
    "zinx-usage/zinx_router/router-usage/server/router"
)

const (
    MsgIdPing = 1
)

func main() {
    //1 Create a server service
    s := znet.NewServer()

    //2 Configure routes
    s.AddRouter(MsgIdPing, &router.PingRouter{})

    //3 Start the service
    s.Serve()
}

common/proto.go

package common

const (
    MsgIdPing = 1
    MsgIdPong = 2
)

server/router/ping.go

package router

import (
    "fmt"
    "github.com/aceld/zinx/ziface"
    "github.com/aceld/zinx/znet"
    "zinx-usage/zinx_router/router-usage/common"
)

// PingRouter MsgIdPing route
type PingRouter struct {
    znet.BaseRouter
}

//Ping Handle method for MsgIdPing route
func (r *PingRouter) PreHandle(request ziface.IRequest) {
    //Read client data
    fmt.Println("PreHandle: recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))
}

//Ping Handle method for MsgIdPing route
func (r *PingRouter) Handle(request ziface.IRequest) {
    //Read client data
    fmt.Println("Handle: recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))
    request.GetConnection().SendMsg(common.MsgIdPong, []byte("pong...pong...pong...[FromServer]"))
}

//Ping Handle method for MsgIdPing route
func (r *PingRouter) PostHandle(request ziface.IRequest) {
    //Read client data
    fmt.Println("PostHandle: recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))
}

When the Server receives the MsgIdPing message, it will call the PreHandle, Handle, and PostHandle methods in turn. Among them, Handle sends the MsgIdPong message back to the client.

3. Router in Zinx Client

Clients can also set up message callback routing using the Router. The Client still provides the AddRouter() method. A specific Client example is shown below:

client/main.go

package main

import (
	"fmt"
	"github.com/aceld/zinx/ziface"
	"github.com/aceld/zinx/znet"
	"time"
	"zinx-usage/zinx_router/router-usage/client/router"
	"zinx-usage/zinx_router/router-usage/common"
)

// Custom business logic for the client
func pingLoop(conn ziface.IConnection) {
	for {
		err := conn.SendMsg(common.MsgIdPing, []byte("Ping...Ping...Ping...[FromClient]"))
		if err != nil {
			fmt.Println(err)
			break
		}

		time.Sleep(1 * time.Second)
	}
}

// Executed when creating a connection
func onClientStart(conn ziface.IConnection) {
	fmt.Println("onClientStart is Called ... ")
	go pingLoop(conn)
}

func main() {
	// Create the Client
	client := znet.NewClient("127.0.0.1", 8999)

	// Set the hook function after the connection is established
	client.SetOnConnStart(onClientStart)

	// Set the message reading routing
	client.AddRouter(common.MsgIdPong, &router.PongRouter{})

	// Start the client
	client.Start()

	// Prevent process exit, wait for interrupt signal
	select {}
}

The router for MsgIdPong messages in the Client is defined as follows:

client/router/pong.go

package router

import (
	"fmt"
	"github.com/aceld/zinx/ziface"
	"github.com/aceld/zinx/znet"
)

// PongRouter is the router for MsgIdPong
type PongRouter struct {
	znet.BaseRouter
}

// Handle is the router for MsgIdPong
func (r *PongRouter) Handle(request ziface.IRequest) {
	// Read the data from the client
	fmt.Println("Handle: recv from client : msgId=", request.GetMsgID(), ", data=", string(request.GetData()))
}

After starting the Server and the Client in two terminals, the following output is obtained:

Running the Server

 $ go run main.go 
PreHandle: recv from client : msgId= 1 , data= Ping...Ping...Ping...[FromClient]
Handle: recv from client : msgId= 1 , data= Ping...Ping...Ping...[FromClient]
PostHandle: recv from client : msgId= 1 , data= Ping...Ping...Ping...[FromClient]
PreHandle: recv from client : msgId= 1 , data= Ping...Ping...Ping...[FromClient]
Handle: recv from client : msgId= 1 , data= Ping...Ping...Ping...[FromClient]
PostHandle: recv from client : msgId= 1 , data= Ping...Ping...Ping...[FromClient]
PreHandle: recv from client : msgId= 1 , data= Ping...Ping...Ping...[FromClient]
Handle: recv from client : msgId= 1 , data= Ping...Ping...Ping...[FromClient]
PostHandle: recv from client : msgId= 1 , data= Ping...Ping...Ping...[FromClient]
...

Running the Client

$ go run main.go 
onClientStart is Called ... 
Handle: recv from client : msgId= 2 , data= pong...pong...pong...[FromServer]
Handle: recv from client : msgId= 2 , data= pong...pong...pong...[FromServer]
Handle: recv from client : msgId= 2 , data= pong...pong...pong...[FromServer]
Handle: recv from client : msgId= 2 , data= pong...pong...pong...[FromServer]
Handle: recv from client : msgId= 2 , data= pong...pong...pong...[FromServer]
...

The above example is the basic routing configuration mode of Zinx.

4. Router Link Jump

In the Router call, Abort() or Goto() can be used in the callback Handler() to terminate or jump to the corresponding callback node.

Abort

Terminate the link. Server-side code in router-goto/server.go:

package main

import (
	"fmt"
	"github.com/aceld/zinx/ziface"
	"github.com/aceld/zinx/znet"
)

type TestRouter struct {
	znet.BaseRouter
}

func (t *TestRouter) PreHandle(req ziface.IRequest) {
	fmt.Println("--> Call PreHandle")
}

func (t *TestRouter) Handle(req ziface.IRequest) {
	fmt.Println("--> Call Handle")

	//Terminate the route link call
	req.Abort()
}

func (t *TestRouter) PostHandle(req ziface.IRequest) {
	fmt.Println("--> Call PostHandle")
}

func main() {
	s := znet.NewServer()
	s.AddRouter(1, &TestRouter{})
	s.Serve()
}

Client-side code in router-goto/client.go:

package main

import (
	"fmt"
	"github.com/aceld/zinx/ziface"
	"github.com/aceld/zinx/znet"
	"time"
)

//Customize client business
func pingLoop(conn ziface.IConnection) {
	for {
		err := conn.SendMsg(1, []byte("Ping...Ping...Ping...[FromClient]"))
		if err != nil {
			fmt.Println(err)
			break
		}

		time.Sleep(1 * time.Second)
	}
}

//Execute when creating a connection
func onClientStart(conn ziface.IConnection) {
	fmt.Println("onClientStart is Called ... ")
	go pingLoop(conn)
}

func main() {
	//Create a Client
	client := znet.NewClient("127.0.0.1", 8999)

	//Set the hook function after the connection is established
	client.SetOnConnStart(onClientStart)

	//Start the client
	client.Start()

	//Prevent process exit and wait for interrupt signal
	select {}
}

Run the server and client, and observe the printed information in the server terminal:

 $ go run server.go  
--> Call PreHandle
--> Call Handle
--> Call PreHandle
--> Call Handle
--> Call PreHandle
--> Call Handle
--> Call PreHandle
--> Call Handle

The link only executed PreHandle() --> Handle() and terminated without executing PostHandle().

Goto

Note: Be careful, it will cause loop calling Jump to the link, the jump node provides three macros as follows:

znet.PRE_HANDLE
znet.HANDLE
znet.POST_HANDLE

Server-side code:

package main

import (
	"fmt"
	"github.com/aceld/zinx/ziface"
	"github.com/aceld/zinx/znet"
)

type TestRouter struct {
	znet.BaseRouter
}

func (t *TestRouter) PreHandle(req ziface.IRequest) {
	fmt.Println("--> Call PreHandle")

	//Jump
	req.Goto(znet.POST_HANDLE)
}

func (t *TestRouter) Handle(req ziface.IRequest) {
	fmt.Println("--> Call Handle")
}

func (t *TestRouter) PostHandle(req ziface.IRequest) {
	fmt.Println("--> Call PostHandle")
}

func main() {
	s := znet.NewServer()
	s.AddRouter(1, &TestRouter{})
	s.Serve()
}

Run the server and client, and observe the print information in the server terminal:

$go run server.go
--> Call PreHandle
--> Call PostHandle
--> Call PreHandle
--> Call PostHandle
--> Call PreHandle
--> Call PostHandle

The result is that it jumps directly from PreHandle() to PostHandle().