前言
- 每个服务器的能够处理的最大IO数量是有限的,根据当前服务器能开辟的IO数量决定,最终决定权是内存大小
- 现在我们要为Zinx框架增加链接个数的限定,如果超过⼀定量的客户端个数,Zinx为了保证后端的及时响应,⽽拒绝链接请求
一、链接管理模块
1 - 实现思路
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a51b3113856f948f39433afeaed3ece2.png)
2 - 接口:zinx/ziface/iconnmanager.go
package ziface
type IConnManager interface {
Add(conn IConneciton)
Remove(conn IConneciton)
Get(connID uint32) (IConneciton, error)
Len() int
ClearConn()
}
3 - 实现:zinx/znet/connmanager.go
- ConnManager 中,其中⽤⼀个map来承载全部的连接信息,key是连接ID,value则是连接本身。其中有⼀个读写锁 connLock 主要是针对map做多任务修改时的保护作⽤
- Remove() ⽅法只是单纯的将conn从map中摘掉,⽽ ClearConn() ⽅法则会先停⽌链接业务, c.Stop() ,然后再从map中摘除
package znet
import (
"errors"
"fmt"
"sync"
"zinx/ziface"
)
type ConnManager struct {
connections map[uint32] ziface.IConneciton
connLock sync.RWMutex
}
func NewConnManager() *ConnManager {
return &ConnManager{
connections:make(map[uint32] ziface.IConneciton),
}
}
func (connMgr *ConnManager) Add(conn ziface.IConneciton) {
connMgr.connLock.Lock()
defer connMgr.connLock.Unlock()
connMgr.connections[conn.GetConnID()] = conn
fmt.Println("connID = ", conn.GetConnID()," add to ConnManager successfully: conn num = ", connMgr.Len())
}
func (connMgr *ConnManager) Remove(conn ziface.IConneciton) {
connMgr.connLock.Lock()
defer connMgr.connLock.Unlock()
delete(connMgr.connections, conn.GetConnID())
fmt.Println("connID = ", conn.GetConnID()," remove from ConnManager successfully: conn num = ", connMgr.Len())
}
func (connMgr *ConnManager) Get(connID uint32) (ziface.IConneciton, error) {
connMgr.connLock.RLock()
defer connMgr.connLock.RUnlock()
if conn, ok := connMgr.connections[connID]; ok {
return conn, nil
} else {
return nil, errors.New("connection not FOUND!")
}
}
func (connMgr *ConnManager) Len() int {
return len(connMgr.connections)
}
func (connMgr *ConnManager) ClearConn() {
connMgr.connLock.Lock()
defer connMgr.connLock.Unlock()
for connID, conn := range connMgr.connections {
conn.Stop()
delete(connMgr.connections, connID)
}
fmt.Println("Clear All connections succ! conn num = ", connMgr.Len())
}
二、Zinx框架集成链接管理模块
1 - 实现思路
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f14b8d32b5d0368b4d4e752df4f021a4.png)
2 - Server集成ConnManager
type Server struct {
Name string
IPVersion string
IP string
Port int
MsgHandler ziface.IMsgHandle
ConnMgr ziface.IConnManager
}
func NewServer(name string) ziface.IServer {
s := &Server{
Name: utils.GlobalObject.Name,
IPVersion: "tcp4",
IP: utils.GlobalObject.Host,
Port: utils.GlobalObject.TcpPort,
MsgHandler: NewMsgHandle(),
ConnMgr: NewConnManager(),
}
return s
}
func (s *Server) Start() {
...
for {
conn, err := listenner.AcceptTCP()
if err != nil {
fmt.Println("Accept err", err)
continue
}
if s.ConnMgr.Len() >= utils.GlobalObject.MaxConn {
fmt.Println("====> Too Many Connections MaxConn = ", utils.GlobalObject.MaxConn)
conn.Close()
continue
}
dealConn := NewConnection(s, conn, cid, s.MsgHandler)
cid++
go dealConn.Start()
}
}()
}
func (s *Server) Stop() {
fmt.Println("[STOP] Zinx server name ", s.Name)
s.ConnMgr.ClearConn()
}
func (s *Server) GetConnMgr() ziface.IConnManager {
return s.ConnMgr
}
3 - Connection集成ConnManager
type Connection struct {
TcpServer ziface.IServer
Conn *net.TCPConn
ConnID uint32
isClosed bool
ExitChan chan bool
msgChan chan []byte
MsgHandler ziface.IMsgHandle
}
func NewConnection(server ziface.IServer, conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection {
c := &Connection{
TcpServer: server,
Conn: conn,
ConnID: connID,
MsgHandler: msgHandler,
isClosed: false,
msgChan: make(chan []byte),
ExitChan: make(chan bool, 1),
}
c.TcpServer.GetConnMgr().Add(c)
return c
}
func (c *Connection) Stop() {
fmt.Println("Conn Stop().. ConnID = ", c.ConnID)
if c.isClosed == true {
return
}
c.isClosed = true
c.Conn.Close()
c.ExitChan <- true
c.TcpServer.GetConnMgr().Remove(c)
close(c.ExitChan)
close(c.msgChan)
}
三、自定义链接前后的业务
1 - 实现思路
- 需求分析:有的时候,在创建链接的时候,希望在创建链接之后、和断开链接之前,执⾏⼀些⽤户⾃定义的业务。那么我们就需要给Zinx增添两个链接创建后和断开前时机的回调函数,⼀般也称作Hook(钩⼦)函数
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d59c56d12e9e55f4ba8933ea9e1a7cb8.png)
2 - iserver接口注册conn的hook方法
package ziface
type IServer interface {
Start()
Stop()
Serve()
AddRouter(uint32, IRouter)
GetConnMgr() IConnManager
SetOnConnStart(func(conneciton IConneciton))
SetOnConnStop(func(conneciton IConneciton))
CallOnConnStart(conneciton IConneciton)
CallOnConnStop(conneciton IConneciton)
}
3 - server实现
type Server struct {
Name string
IPVersion string
IP string
Port int
MsgHandler ziface.IMsgHandle
ConnMgr ziface.IConnManager
OnConnStart func(conn ziface.IConneciton)
OnConnStop func(conn ziface.IConneciton)
}
func (s *Server) SetOnConnStart(hookFunc func(conneciton ziface.IConneciton)) {
s.OnConnStart = hookFunc
}
func (s *Server) SetOnConnStop(hookFunc func(conneciton ziface.IConneciton)) {
s.OnConnStop = hookFunc
}
func (s *Server) CallOnConnStart(conn ziface.IConneciton) {
if s.OnConnStart != nil {
fmt.Println("----> Call OnConnStart() ...")
s.OnConnStart(conn)
}
}
func (s *Server) CallOnConnStop(conn ziface.IConneciton) {
if s.OnConnStop != nil {
fmt.Println("---> Call OnConnStop() ...")
s.OnConnStop(conn)
}
}
4 - 选定start和stop的Hook方法调用位置
func (c *Connection) Start() {
fmt.Println("Conn Start() ... ConnID = ", c.ConnID)
go c.StartReader()
go c.StartWriter()
c.TcpServer.CallOnConnStart(c)
}
func (c *Connection) Stop() {
fmt.Println("Conn Stop().. ConnID = ", c.ConnID)
if c.isClosed == true {
return
}
c.isClosed = true
c.TcpServer.CallOnConnStop(c)
c.Conn.Close()
c.ExitChan <- true
c.TcpServer.GetConnMgr().Remove(c)
close(c.ExitChan)
close(c.msgChan)
}
四、测试Hook
- myDemo/zinxV0.9/Server.go
...
func DoConnectionBegin(conn ziface.IConneciton) {
fmt.Println("===> DoConnectionBegin is Called ... ")
if err := conn.SendMsg(202, []byte("DoConnection BEGIN")); err != nil {
fmt.Println(err)
}
}
func DoConnectionLost(conn ziface.IConneciton) {
fmt.Println("===> DoConnectionLost is Called ...")
fmt.Println("conn ID = ", conn.GetConnID(), " is Lost...")
}
func main() {
s := znet.NewServer("[zinx V0.9]")
s.SetOnConnStart(DoConnectionBegin)
s.SetOnConnStop(DoConnectionLost)
s.AddRouter(0, &PingRouter{})
s.AddRouter(1, &HelloZinxRouter{})
s.Serve()
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/3f19eb208ab92ebfb813fdf3fe72a731.png)
五、项目结构
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a711bdb1695e7f316ca8072b834ee09f.png)
六、完整源码
下载zinxV0.9