Zinx-V0.8消息队列及多任务机制

Zinx-V0.8消息队列及多任务机制

之前的zinx框架 是1个链接对应1个Reader和1个Writer, 1个消息对应一个Handler,如果说1个客户端发送10个消息 1w客户端 就10w消息,—> 10w Handler的go程来处理业务,cpu就会在10w handler之间进行切换,影响性能,这时就需要制定一个worker处理业务的工作池的机制。

在这里插入图片描述

1.在MsgHandler中添加WorkPoolSize(工作池)和TaskQueue(消息队列)
type MsgHandler struct {
	//存放路由集合的map
	Apis map[uint32] ziface.IRouter //就是开发者全部的业务,消息ID和业务的对应关系

	//负责Worker取任务的消息队列  一个worker对应一个任务队列
	TaskQueue  []chan ziface.IRequest

	//worker工作池的worker数量
	WorkerPoolSize uint32
}

在初始化的时候将WorkPoolSize和TaskQueue初始化,这里可以让用户自己进行配置

func NewMsgHandler() ziface.IMsgHandler {
	//给map开辟头空间
	return &MsgHandler{
		Apis:make(map[uint32]ziface.IRouter),
		WorkerPoolSize:config.GlobalObject.WorkerPoolSize,
		TaskQueue:make([]chan ziface.IRequest, config.GlobalObject.WorkerPoolSize),//切片的初始化
	}
}
2.在实现层中实现一个真正处理业务的方法
//一个worker真正处理业务的 goroutine函数
func (mh *MsgHandler) startOneWorker(workerID int, taskQueue chan ziface.IRequest) {
	fmt.Println(" worker ID = ", workerID , " is starting... ")

	//不断的从对应的管道 等待数据
	for {
		select {
			case req := <-taskQueue:
				mh.DoMsgHandler(req)
		}
	}
}
3.抽象层和实现层分别添加启动工作池和将消息添加到工作池的方法
type IMsgHandler interface {
 	//添加路由到map集合中
 	AddRouter(msgID uint32, router IRouter)
 	//调度路由, 根据MsgID
 	DoMsgHandler(request IRequest)
 	//启动Worker工作池
 	StartWorkerPool()
 	//将消息添加到Worker工作池中 (将消息发送给对应的消息队列)
 	SendMsgToTaskQueue(request IRequest)
 }
//启动Worker工作池 (在整个server服务中 只启动一次)
func (mh *MsgHandler) StartWorkerPool() {
	fmt.Println("WorkPool is  started..")

	//根据WorkerPoolSize 创建worker goroutine
	for i := 0; i < int(mh.WorkerPoolSize); i++ {
		//开启一个workergoroutine

		//1 给当前Worker所绑定消息channel对象 开辟空间  第0个worker 就用第0个Channel
		//给channel 进行开辟空间
		mh.TaskQueue[i] = make(chan ziface.IRequest, config.GlobalObject.MaxWorkerTaskLen)

		//2 启动一个Worker,阻塞等待消息从对应的管道中进来
		go mh.startOneWorker(i, mh.TaskQueue[i])
	}
}


//将消息添加到Worker工作池中 (将消息发送给对应的消息队列)
//应该是Reader来调用的
func (mh *MsgHandler) SendMsgToTaskQueue(request ziface.IRequest) {
	//1 将消息 平均分配给worker 确定当前的request到底要给哪个worker来处理
	//1个客户端绑定一个worker来处理
	workerID := request.GetConnection().GetConnID() % mh.WorkerPoolSize

	//2 直接将 request 发送给对应的worker的taskqueue
	mh.TaskQueue[workerID] <- request
}

这里是用每个Connection的id对工作池数量取余得到一个workerID,将request写入TaskQueue下标为workerID的channel中,这样就相对来说实现了将request轮询分配给每个工作池。

4.在Connection刚开始也就是还没有监听之前开启工作池
func (s *Server) Start() {
	fmt.Printf("[start] Server Linstenner at IP :%s, Port :%d, is starting..\n", s.IP, s.Port)

	//0 启动worker工作池
	s.MsgHandler.StartWorkerPool()

	//1 创建套接字  :得到一个TCP的addr
	addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))
	if err != nil {
		fmt.Println("resolve tcp addr error:", err)
		return
	}
	......
}
5.将连接的每个request添加到消息队列
func (c *Connection) StartReader() {
    //从对端读数据
	fmt.Println("[Reader Goroutine isStarted]...")
	defer fmt.Println("[Reader Goroutine Stop...] connID = ", c.ConnID, "Reader is exit, remote addr is = ", c.GetRemoteAddr().String())
	defer c.Stop()
    for {
        ......
		//将读出来的msg 组装一个request
		//将当前一次性得到的对端客户端请求的数据 封装成一个Request
		req := NewReqeust(c, msg)


		//将req交给worker工作池来处理
		if config.GlobalObject.WorkerPoolSize > 0 {
			c.MsgHandler.SendMsgToTaskQueue(req)
		} else {
			go c.MsgHandler.DoMsgHandler(req)
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值