Zinx-Golang轻量级TCP服务器框架(2)

全局配置

package utils

type GlobalObj struct {
	//Server
	TcpServer ziface.IServer //Zinx全局Server对象
	Host      string         //服务器监听ip
	TcpPort   int            //服务器监听端口号
	Name      string         //服务器名称

	//Zinx
	Version          string //当前服务器版本
	MaxConn          int    //当前服务器最大连接数
	MaxPackageSize   uint32 //当前Zinx框架数据包的最大值
	WorkerPoolSize   uint32 //当前业务工作Worker池的Goroutine数量
	MaxWorkerTaskLen uint32 //Zinx框架允许用户最多开辟多少个Worker(限定条件)
}

var GlobalObject *GlobalObj

func (g *GlobalObj) Reload() {
	data, err := ioutil.ReadFile("conf/zinx.json")
	if err != nil {
		panic(err)
	}
	err = json.Unmarshal(data, &GlobalObject)
	if err != nil {
		panic(err)
	}
}

func init() {
	GlobalObject = &GlobalObj{
		Name:             "ZinxServerApp",
		Version:          "V0.6",
		TcpPort:          8999,
		Host:             "0.0.0.0",
		MaxConn:          1000,
		MaxPackageSize:   4096,
		WorkerPoolSize:   10,
		MaxWorkerTaskLen: 1024,
	}

	GlobalObject.Reload()//如果不用默认数据,用户可以创建json文件自定义
}

声明一个GlobalObject全局变量,其中有用户自定义的参数,通过init()函数去初始化这个变量,同时给GlobalObject类绑定一个方法,用来加载用户创建的json文件数据。

Message

抽象层

type IMessage interface {
	GetMsgId() uint32
	GetMsgLen() uint32
	GetMsg() []byte

	SetMsgId(uint32)
	SetMsgLen(uint32)
	SetMsg([]byte)
}

实现层

type Message struct {
	Id      uint32
	DataLen uint32
	Data    []byte
}

// 创建一个Msg包
func NewMsgPackage(id uint32, data []byte) *Message {
	return &Message{
		Id:      id,
		DataLen: uint32(len(data)),
		Data:    data,
	}
}

func (m *Message) GetMsgId() uint32 {
	return m.Id
}

func (m *Message) GetMsgLen() uint32 {
	return m.DataLen
}

func (m *Message) GetMsg() []byte {
	return m.Data
}

func (m *Message) SetMsgId(Id uint32) {
	m.Id = Id
}

func (m *Message) SetMsgLen(DataLen uint32) {
	m.DataLen = DataLen
}

func (m *Message) SetMsg(Data []byte) {
	m.Data = Data
}

DataPack

网络上传输数据是以二进制流的形式传输,这就可能导致数据与数据之间连续,无法区分,也就是粘包问题,解决此问题有俩种方法:TLV(Type、Len、Value)或者设置结束符。
在这里插入图片描述
抽象层

type IDataPack interface {
	GetHeadLen() uint32

	Pack(msg IMessage) ([]byte, error)

	Unpack([]byte) (IMessage, error)
}

实现层

type DataPack struct {
}

func NewDataPack() *DataPack {
	return &DataPack{}
}

func (d *DataPack) GetHeadLen() uint32 {
	//DataLen uint32 + Id uint32 = 4 + 4 = 8 byte
	return 8
}

func (d *DataPack) Pack(msg ziface.IMessage) ([]byte, error) {
	//dataLen|dataId|data
	dataBuff := bytes.NewBuffer([]byte{})

	if err := binary.Write(dataBuff, binary.LittleEndian, msg.GetMsgLen()); err != nil {
		return nil, err
	}

	if err := binary.Write(dataBuff, binary.LittleEndian, msg.GetMsgId()); err != nil {
		return nil, err
	}

	if err := binary.Write(dataBuff, binary.LittleEndian, msg.GetMsg()); err != nil {
		return nil, err
	}

	return dataBuff.Bytes(), nil
}

func (d *DataPack) Unpack(binaryData []byte) (ziface.IMessage, error) {
	dataBuff := bytes.NewReader(binaryData)

	msg := &Message{}

	if err := binary.Read(dataBuff, binary.LittleEndian, &msg.DataLen); err != nil {
		return nil, err
	}

	if err := binary.Read(dataBuff, binary.LittleEndian, &msg.Id); err != nil {
		return nil, err
	}

	if utils.GlobalObject.MaxPackageSize > 0 && msg.DataLen > utils.GlobalObject.MaxPackageSize {
		return nil, errors.New("too large msg data recv")
	}

	return msg, nil
}

MsgHandler

抽象层

// 消息管理抽象层
type IMsgHandle interface {
	DoMsgHandler(request IRequest)

	AddRouter(msgID uint32, router IRouter)

	StartWorkerPool()

	SendMsgToTaskQueue(request IRequest)
}

实现层

type MsgHandle struct {
	//存放每个MsgID所对应的处理方法
	Apis map[uint32]ziface.IRouter
	//负责Worker取任务的消息队列
	TaskQueue []chan ziface.IRequest
	//业务工作worker池的worker数量
	WorkerPoolSize uint32
}

func NewMsgHandle() *MsgHandle {
	return &MsgHandle{
		Apis:           make(map[uint32]ziface.IRouter),
		WorkerPoolSize: utils.GlobalObject.WorkerPoolSize,
		TaskQueue:      make([]chan ziface.IRequest, utils.GlobalObject.WorkerPoolSize),
	}
}

func (m *MsgHandle) DoMsgHandler(request ziface.IRequest) {
	handler, ok := m.Apis[request.GetMsgId()]
	if !ok {
		fmt.Println("api msgID = ", request.GetMsgId(), " is not found!")
	}
	handler.PreHandle(request)
	handler.Handle(request)
	handler.PostHandle(request)
}

func (m *MsgHandle) AddRouter(msgID uint32, router ziface.IRouter) {
	if _, ok := m.Apis[msgID]; ok {
		panic("repeat api , msgid = " + strconv.Itoa(int(msgID)))
	}
	m.Apis[msgID] = router
	fmt.Println("add api router succ ", msgID)
}

// 启动一个Worker工作池
func (m *MsgHandle) StartWorkerPool() {
	for i := 0; i < int(m.WorkerPoolSize); i++ {
		m.TaskQueue[i] = make(chan ziface.IRequest, utils.GlobalObject.MaxWorkerTaskLen)
		go m.StartOneWorker(i, m.TaskQueue[i])
	}
}

// 启动一个Worker工作流程
func (m *MsgHandle) StartOneWorker(workerID int, taskQueue chan ziface.IRequest) {
	fmt.Println("Worker ID = ", workerID, " is started...")

	for {
		select {
		case request := <-taskQueue:
			m.DoMsgHandler(request)
		}
	}
}

func (m *MsgHandle) SendMsgToTaskQueue(request ziface.IRequest) {
	workerID := request.GetConnection().GetConnID() % m.WorkerPoolSize
	fmt.Println("add connID = ", request.GetConnection().GetConnID(), " request MsgID = ", request.GetMsgId(),
		" to WorkerID = ", workerID)

	m.TaskQueue[workerID] <- request
}

使用MsgHandler模块集成管理路由方法,可以添加多路由函数到map中。

读写协程分离

在这里插入图片描述
客户端发数据—》服务端Reader拆包读数据—》Dohandler处理数据—》Dohandler通过管道将消息发给Writer—》Writer发数据给客户端

消息队列及工作池

在这里插入图片描述

// 启动一个Worker工作池
func (m *MsgHandle) StartWorkerPool() {
	for i := 0; i < int(m.WorkerPoolSize); i++ {
		m.TaskQueue[i] = make(chan ziface.IRequest, utils.GlobalObject.MaxWorkerTaskLen)
		go m.StartOneWorker(i, m.TaskQueue[i])
	}
}

// 启动一个Worker工作流程
func (m *MsgHandle) StartOneWorker(workerID int, taskQueue chan ziface.IRequest) {
	fmt.Println("Worker ID = ", workerID, " is started...")

	for {
		select {
		case request := <-taskQueue:
			m.DoMsgHandler(request)
		}
	}
}```
func (m *MsgHandle) SendMsgToTaskQueue(request ziface.IRequest) {
	// 将消息平均分配给不同的Worker
	workerID := request.GetConnection().GetConnID() % m.WorkerPoolSize
	fmt.Println("add connID = ", request.GetConnection().GetConnID(), " request MsgID = ", request.GetMsgId(),
		" to WorkerID = ", workerID)

	m.TaskQueue[workerID] <- request
}

增加链接个数限定,如果超过一定量的客户端个数,Zinx为了保证后端及时响应,而拒绝链接请求。
**抽象层**

```go
type IConnManager interface {
	Add(conn IConnection)
	Remove(conn IConnection)
	Get(connID uint32) (IConnection, error)
	Len() int
	ClearConn()
}

实现层

type ConnManager struct {
	connections map[uint32]ziface.IConnection
	connLock    sync.RWMutex
}

func NewConnManager() *ConnManager {
	return &ConnManager{
		connections: make(map[uint32]ziface.IConnection),
	}
}

func (cm *ConnManager) Add(conn ziface.IConnection) {
	cm.connLock.Lock()
	defer cm.connLock.Unlock()

	cm.connections[conn.GetConnID()] = conn
	fmt.Println("connID = ", conn.GetConnID(), " add to ConnManager success. conn num = ", cm.Len())
}

func (cm *ConnManager) Remove(conn ziface.IConnection) {
	cm.connLock.Lock()
	defer cm.connLock.Unlock()

	delete(cm.connections, conn.GetConnID())
	fmt.Println("connID = ", conn.GetConnID(), " remove to ConnManager success. conn num = ", cm.Len())
}

func (cm *ConnManager) Get(connID uint32) (ziface.IConnection, error) {
	cm.connLock.RLock()
	defer cm.connLock.RUnlock()

	if conn, ok := cm.connections[connID]; ok {
		return conn, nil
	} else {
		return nil, errors.New("conn not found")
	}
}

func (cm *ConnManager) Len() int {
	return len(cm.connections)
}

func (cm *ConnManager) ClearConn() {
	cm.connLock.Lock()
	defer cm.connLock.Unlock()

	for connID, conn := range cm.connections {
		conn.Stop()
		delete(cm.connections, connID)
	}

	fmt.Println("Clear All conn success! conn num = ", cm.Len())
}

在这里插入图片描述
内容来自 @刘丹冰Aceld

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值