【Zinx】Day5-Part1:实现 Zinx 的读写分离模型

Day5:完成 Zinx-v1.0 的开发

最近花了较长的时间整理八股文,没有进一步推进 Zinx 项目的开发。今天让我们完成 Zinx-v1.0 整体项目的开发。

今天要完成的任务如下:

  • 实现 Zinx 的读写分离模型(之前的 Connection 只有 Reader,读取消息之后写回,现在我们将读写分离,将业务处理得到的结果通过 Writer 写回);
  • 实现 Zinx 的消息队列及多任务机制;
  • 实现 Zinx 的连接管理;
  • 实现 Zinx 的连接属性设置。

Day5-Part1:实现 Zinx 的读写分离模型

请添加图片描述
接下来我们要对 Zinx 做一些小的改变,具体来说,就是将与客户端进行交互的 Connection 管理的 goroutine 从一个变为两个,一个专门负责从客户端读取数据(Reader),而另一个专门向客户端写数据(Writer)。

读写分类的好处是高内聚、模块功能单一,使得我们之后可以更加方便地拓展功能。

Server 依然用于监听客户端的连接请求,当建立与客户端的套接字之后,就会开启两个 goroutine,分别处理读数据业务和写数据业务,读写数据之间的消息通过 channel 传递。

添加读写模块交互数据的通道

type Connection struct {
	Conn         *net.TCPConn      // 当前连接的 socket TCP 套接字
	ConnID       uint32            // 当前连接的 ID, 也可称为 SessionID, 全局唯一
	isClosed     bool              // 当前连接的开启/关闭状态
	Msghandler   ziface.IMsgHandle // 将 Router 替换为消息管理模块
	ExitBuffChan chan bool         // 告知该连接一经退出/停止的 channel
	msgChan      chan []byte       // 无缓冲 channel, 用于读/写两个 goroutine 之间的消息通信
}

func NewConntion(conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection{
	c := &Connection{
		Conn:     conn,
		ConnID:   connID,
		isClosed: false,
		MsgHandler: msgHandler,
		ExitBuffChan: make(chan bool, 1),
		msgChan:make(chan []byte),
	}
	return c
}

我们为 Connection 新增一个 msgChan 成员,它的作用是用于 Reader 和 Writer 两个 goroutine 之间的通信。

创建 Writer Goroutine

 func (c *Connection) StartWriter() {
	fmt.Println("[Writer Goroutine is running]")
	defer fmt.Println(c.RemoteAddr().String(), "[conn Writer exit!]")

 	for {
 		select {
 			case data := <-c.msgChan:
 				//有数据要写给客户端
 				if _, err := c.Conn.Write(data); err != nil {
 					fmt.Println("Send Data error:, ", err, " Conn Writer exit")
 					return
				}
 			case <- c.ExitBuffChan:
 				//conn已经关闭
 				return
		}
	}
 }

可以看到,在 Writer Goroutine 当中,开启了一个 for loop,使用 select 等待来自 msgChan 这个通道当中的数据。如果等到了 msgChan 当中的数据,那么就将数据通过c.Conn.Write(data)写入。

到此我们重新理清一下思路,不管 Connection,还是 Connection 的 Reader 和 Writer,本质上它们都是属于 Server 的。Server 要做的是首先开启服务,监听端口,收到请求之后新建一个 Connection 类型来管理当前这条连接,同时,根据 msgId 将业务与 Connection 进行绑定。

Connection 在建立之后会 Start,同时开启 Reader 和 Writer。Reader 的作用就是阻塞地等待来自 client 的消息,得到消息之后进行拆包,并将得到的消息组装成 request,将 request 发送给处理相应业务的 router。router 的主要方法是 handle,也就是根据得到的消息进行具体的业务处理,在业务处理的过程当中,可能会需要将输出顺着连接写回给 client,这个时候就需要用到 Writer,具体做法是在 Handle 函数当中将业务的输出打包之后输入到通道 msgChan,由于 Writer 同样在阻塞地等待着数据的发送,因此当 msgChan 收到数据之后,Writer 开始工作,将数据通过c.Conn.Write()写回给 client。

将 Reader 当中直接发送给客户端的数据改为直接发送给 msgChan 这个 channel

func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	if c.isClosed == true {
		return errors.New("Connection closed when send msg")
	}
	//将data封包,并且发送
	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("Pack error msg id = ", msgId)
		return  errors.New("Pack error msg ")
	}

	//写回客户端
	c.msgChan <- msg   //将之前直接回写给conn.Write的方法 改为 发送给Channel 供Writer读取

	return nil
}

启动 Reader 和 Writer

最后,我们需要在 Connection 的 Start 方法当中同时启动 Reader 和 Writer:

// Start 实现 IConnection 中的方法, 它启动连接并让当前连接开始工作
func (c *Connection) Start() {
	// 开启处理该连接读取到客户端数据之后的业务请求
	go c.StartWriter()
	go c.StartReader()

	for {
		select {
		case <-c.ExitBuffChan:
			// 得到退出消息则不再阻塞
			return
		}
	}
}

至此,我们便完成了 Zinx 的读写分离。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值