处理器
在前面协议解析完成之后就需要将协议解析完成的 resp.Reply 结构体交给处理器进行处理了,处理会将 net.Conn 的连接包装成一个 Connection 的接口体交给后续进行使用,然后调用 协议解析器 获取到返回的 chan 管道然后监听管道中的数据进行操作,将对应的响应返回给客户端
Connection
连接对象,用于包装每个客户端的结构体,每个客户端的请求到了 handler 之后都会将其包装为一个 Connection 对象然后保存起来;在 interface/resp/conn.go中进行定义
// Connection 用于封装连接客户端的接口
type Connection interface {
//Write 写入数据
Write([]byte) error
//GetDBIndex 获取到对应的DB数据库
GetDBIndex() int
//SelectDB 切换DB
SelectDB(dbNum int) int
}
conn.go
创建 Connection 的结构体用于包装客户端连接对象
字段说明
- conn:net包中的连接
- waitingReply:给客户端回发指令的时候,如果连接被关闭了则需要处理完所有的指令
- mu:在操作一个客户端连接的时候需要上锁,防止并发出现问题
- selectedDB:指定当前连接正在操作的数据库
方法说明
- NewConn:堆外提供的公共方法
- RemoteAddr:获取到远程客户端的ip地址
- Close:关闭客户端连接
- Write:给客户端返回数据
- GetDBIndex:获取到当前连接操作数据库的索引
- SelectDB:切换数据库
// Connection 协议层对每一个连接的描述
type Connection struct {
//客户端的连接信息
conn net.Conn
//给客户端回发指令的时候,如果连接被关闭了则需要处理完所有的指令
waitingReply wait.Wait
//在操作一个客户端连接的时候需要上锁,防止并发出现问题
mu sync.Mutex
//指定当前连接正在操作的数据库
selectedDB int
}
//NewConn 创建一个新的连接
func NewConn(conn net.Conn) *Connection {
return &Connection{
//连接需要新的,其它的参数都用初始化默认的
conn: conn,
}
}
// RemoteAddr 获取远程客户端连接的地址
func (c *Connection) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
// Close 用于关闭连接,
func (c *Connection) Close() error {
//10秒的超时,等待指令处理完成
c.waitingReply.WaitWithTimeout(10 * time.Second)
_ = c.conn.Close()
return nil
}
//Write 给客户端发送数据
func (c *Connection) Write(bytes []byte) error {
defer func() {
c.waitingReply.Done()
c.mu.Unlock()
}()
//空的数组直接返回
if len(bytes) <= 0 {
return nil
}
//同一个时间只有一个协程能够往客户端发送数据
c.mu.Lock()
//表示有一个协程正在发送数据
c.waitingReply.Add(1)
_, err := c.conn.Write(bytes)
return err
}
// GetDBIndex 获取到db的索引
func (c *Connection) GetDBIndex() int {
return c.selectedDB
}
// SelectDB 选择db
func (c *Connection) SelectDB(dbNum int) int {
c.selectedDB = dbNum
return c.selectedDB
}
Handler
前面已经说了,在 interface/tcp/handler.go 中创建了一个处理器的顶级接口,里面定义了两个方法
- Handler:ctx传递上下文对象,conn net包中的连接对象
- Close:关闭方法
// Handler tcp抽象出处理函数接口
type Handler interface {
//Handler ctx上下文,conn tcp连接
Handler(ctx context.Context, conn net.Conn)
//Close 关闭
Close() error
}
RespHandler
resp协议的处理实体类,在 resp/handler/handler.go 中定义
handler.go
字段说明
- activeConn:保存所有的客户端连接 sync.Map 包中线程安全的 Map
- db:自定义的数据库层实现,后续再实现
- closing:是否关闭
方法说明
- MakeHandler:对外提供创建 RespHandler 的方法
- closeClient:关闭客户端
- Handler:处理方法,前面tcp服务接受到连接之后会将对应的请求交给 Handler 进行处理,handler然后去调用 parser.ParseStream() 前面实现的协议解析进行解析,获取到返回的 chan 管道然后循环处理获取到的 Payload 类
- Close:关闭handler
// RespHandler 处理Resp协议的处理器
type RespHandler struct {
//保存所有的连接
activeConn sync.Map
//数据库核心业务层
db databaseface.Database
//是否正在关闭
closing atomic.Boolean
}
//MakeHandler 创建一个handler
func MakeHandler() *RespHandler {
var db databaseface.Database
db = database.NewDatabase()
return &RespHandler{
db: db,
}
}
//closeClient 关闭单个客户端
func (r *RespHandler) closeClient(client *connection.Connection) {
//客户端连接进行关闭
_ = client.Close()
//客户端连接关闭时进行一些善后的工作
r.db.AfterClientClose(client)
//删除客户端
r.activeConn.Delete(client)
}
//Handler 进行处理
func (r *RespHandler) Handler(ctx context.Context, conn net.Conn) {
if r.closing.Get() {
//判断是否正在关闭
_ = conn.Close()
}
client := connection.NewConn(conn)
//暂时用空结构体,value就不会占用空间,map就成为了一个Set结构
r.activeConn.Store(client, struct{}{})
//将连接发送给parser进行tcp报文解析
ch := parser.ParseStream(conn)
//相当于死循环,一直获取到解析出来的数据
for payload := range ch {
err := payload.Err
//错误的逻辑
if err != nil {
//说明客户端给我们发送的是四次挥手需要断开连接,或者使用的是一个被关闭的连接
if err == io.EOF ||
err == io.ErrUnexpectedEOF ||
strings.Contains(err.Error(), "use of closed network connection") {
r.closeClient(client)
logger.Info("Connection closed:" + client.RemoteAddr().String())
}
//协议的错误
errReply := reply.MakeStandardErrReply(err.Error())
//回写给客户端错误的数据信息
err := client.Write(errReply.ToBytes())
//如果回写出错,直接关闭客户端
if err != nil {
r.closeClient(client)
logger.Info("Connection closed:" + client.RemoteAddr().String())
}
//继续执行
continue
}
//执行正常的请求逻辑
data := payload.Data
if data == nil {
continue
}
multiBulkReply, ok := data.(*reply.MultiBulkReply)
if !ok {
logger.Error("require multi bulk reply............")
continue
}
//通过内核进行执行指令
result := r.db.Exec(client, multiBulkReply.Args)
if result != nil {
_ = client.Write(result.ToBytes())
} else {
_ = client.Write(reply.MakeUnknownErrReply().ToBytes())
}
}
}
//Close 关闭客户端
func (r *RespHandler) Close() error {
logger.Info("handler shutting down...............")
r.closing.Set(true)
r.activeConn.Range(func(key, value any) bool {
//关闭所有的客户端
client := key.(*connection.Connection)
_ = client.Close()
r.db.AfterClientClose(client)
//返回true才会下一个遍历
return true
})
r.db.Close()
return nil
}