前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 Go 语言写一个数据库—5 命令行

使用 Go 语言写一个数据库—5 命令行

作者头像
roseduan
发布2021-07-28 17:55:55
3610
发布2021-07-28 17:55:55
举报
文章被收录于专栏:roseduan写字的地方

Hello 大家好,我是 roseduan,上一次给大家分享了 rosedb 项目当中所涉及到的一些数据结构,有链表、哈希表、跳表、有序集合,内容比较的硬核,你可以温习一下前面的几篇文章:

使用 Go 语言写一个数据库—4 数据结构

使用 Go 语言写一个数据库—3 数据库操作

使用 Go 语言写一个数据库—2 基本数据操作

使用 Go 语言写一个数据库—1 基本结构


今天的内容比较轻松,相信你一定可以完全掌握,参考视频在文章的底部。

我们常常使用 redis cli 来操作 redis ,比如 set、get 等等,那你知道它是怎么实现的吗?

这里我以 rosedb 为例,为你实现一个具备类似功能的命令行操作。首先来看一下完成后的效果示例:

在项目 rosedb/cmd/server 下面,有一个 main.go ,使用 go run main.go 命令来启动它:

然后在 rosedb/cmd/cli 下面,同样执行 go run main.go 来启动一个客户端,启动完成之后,就能够进行命令操作了,比如 set、get 等等:


接下来再看具体的实现,其实也比较简单,分为了两部分,服务端和客户端。

服务端

服务端的本质,就是启动一个 TCP 服务器,监听一个端口,主要代码如下:

代码语言:javascript
复制
func (s *Server) Listen(addr string) {
  var err error
  // 启动 TCP 服务器
  s.listener, err = net.Listen("tcp", addr)
  if err != nil {
    log.Printf("tcp listen err: %+v\n", err)
    return
  }

  log.Println("rosedb is running, ready to accept connections.")
  // 获取客户端的连接
  for {
    select {
    case <-s.done:
      return
    default:
      conn, err := s.listener.Accept()
      if err != nil {
        continue
      }
      // 处理连接
      go s.handleConn(conn)
    }
  }
}

服务端还做一件事情,就是获取客户端的连接,然后取出命令执行,并获取到执行的结果。

获取到结果之后,再返回到客户端,你可以看这个 handleConn 方法:

代码语言:javascript
复制

func (s *Server) handleConn(conn net.Conn) {
  defer conn.Close()
  for {
    // ...
    
    // 读取客户端的数据
    size := binary.BigEndian.Uint32(b[:4])
    if size > 0 {
      data := make([]byte, size)
      _, err := bufReader.Read(data)
      if err != nil {
        log.Printf("read cmd data err: %+v\n", err)
        break
      }

      // 执行命令,获取到结果
      cmdAndArgs := reg.FindAllString(string(data), -1)
      reply := s.handleCmd(cmdAndArgs[0], cmdAndArgs[1:])
      info := wrapReplyInfo(reply)
      
      // 将结果写回到客户端
      _, err = conn.Write(info)
      if err != nil {
        log.Printf("write reply err: %+v\n", err)
      }
    }
  }
}

客户端

客户端的工作,就是与服务端建立连接,然后获取用户的输入,并且传到服务端。

然后从服务端获取到运行的结果,并将结果打印出来,关键代码如下:

代码语言:javascript
复制
func main() {
  flag.Parse()

  addr := fmt.Sprintf("%s:%d", *host, *port)
  // 连接到服务器
  conn, err := net.Dial("tcp", addr)
  if err != nil {
    log.Println("tcp dial err: ", err)
    return
  }
  // ... 不重要的代码就忽略了
  
  // 命令数据传到服务端
      wInfo := wrapCmdInfo(cmd)
      _, err := conn.Write(wInfo)
      if err != nil {
        fmt.Println(err)
      }

  // 获取服务端的结果,并打印出来
      reply := readReply(conn)
      fmt.Println(reply)
    }
  }
}

这就是全部的实现了,是不是非常简单呢?当然我目前实现的这个版本,还是比较简陋的,很多命令都还没有支持,可完善的地方还是挺多的。

视频参考


题图:from wallheaven.cc

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 roseduan写字的地方 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档