GO语言实战案例(三) | 青训营

115 阅读4分钟

需求描述

社区话题页面:

  • 展示话题(标题,文字描述)和回帖列表
  • 实现本地web服务
  • 话题和回帖数据用文件存储

需求用例

游览消费用户

  • 话题
  • 帖子

ER图-Entity Relationship Diagram

展示概念模型的工具,展示相关的属性和关系,为开发提供清晰的思路

Topic:(话题)

  • id
  • title
  • content
  • create_time

Post:(帖子)

  • id
  • topic_id
  • content
  • create_time

Topic和Post的关系为一对多,一个Topic可以对应多个Post

分层结构

image.png

  • 数据层:数据Model,外部数据的增删改查。可以屏蔽下游的数据差异,对下游的接口是可以不发生改变的,下游的service层只需要拿到接口即可
  • 逻辑层:业务Entity,处理核心业务逻辑输出。通过接收的数据做打包封装,输出实体
  • 视图层:视图view,处理和外部的交互逻辑

组件工具

  • Gin高性能go web框架github.com/gin-gonic/g…
  • Go Mod 使用go mod init初始化依赖管理文件,然后执行go get,gopkg.in/gin-gonic/gin.v1@v1.3.0

Repository

  • 对于Topic需要使用QueryTopicById,通过话题的id查询话题
  • 对于Post需要使用QueryPostsByParentId,通过话题的id查询所有与这个话题id相关的所有信息

index

index索引概念 来自:索引-识典百科 (baike.com)

定义:

索引是为了加速对表中数据行的检索而创建的一种分散的存储结构。索引是针对表而建立的,它是由数据页面以外的索引页面组成的,每个索引页面中的行都会含有逻辑指针,以便加速检索物理数据。 在数据库关系图中,可以在选定表的“索引键”属性页中创建、编辑或删除每个索引类型。当保存索引所附加到的表,或保存该表所在的关系图时,索引将保存在数据库中。

作用:

在数据库系统中建立索引主要有以下作用:

(1)快速取数据;

(2)保证数据记录的唯一性;

(3)实现表与表之间的参照完整性;

(4)在使用ORDER by、group by子句进行数据检索时,利用索引可以减少排序和分组的时间。

优缺点:

优点

1.大大加快数据的检索速度;

2.创建唯一性索引,保证数据库表中每一行数据的唯一性;

3.加速表和表之间的连接;

4.在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。

缺点

1.索引需要占物理空间。

2.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。

初始化话题数据索引

func initTopicIndexMap(filePath string) error {  
open, err := os.Open(filePath + "topic")  
if err != nil {  
return err  
}  
scanner := bufio.NewScanner(open)  
topicTmpMap := make(map[int64]*Topic)  
for scanner.Scan() {  
text := scanner.Text()  
var topic Topic  
if err := json.Unmarshal([]byte(text), &topic); err != nil {  
return err  
}  
topicTmpMap[topic.Id] = &topic  
}  
topicIndexMap = topicTmpMap  
return nil  
}

查询

type Topic struct {  
Id int64 `json:"id"`  
Title string `json;"title"`  
Content string `json:"content"`  
CreateTime int64 `json:"create_time"`  
}  
type TopicDao struct{}  
  
var (  
topicDao *TopicDao  
topicOnce sync.Once  
)  
  
func NewTopicDaoInstance() *TopicDao {  
topicOnce.Do(  
func() {  
topicDao = &TopicDao{}  
})  
return topicDao  
}  
func (*TopicDao) QueryTopicById(id int64) *Topic {  
return topicIndexMap[id]  
}

Service

实体

type PageInfo struct {  
Topic *respositor.Topic  
PostList []*respository.Post  
}

流程

image.png 代码流程编排

func (f *QueryPageInfoFlow) Do() (*PageInfo,error) {  
if err := f.checkParam(); err != nil{  
return nil,err  
}  
if err := f.prepareInfo(); err != nil {  
return nil,err  
}  
if err := f.packPageInfo(); err != nil {  
return nil,err  
}  
return f.pageInfo,nil  
}

可用性

话题信息和回帖信息是并行处理的

func (f *QueryPageInfoFlow) prepareInfo() error{  
//获取topic信息  
var wg sync.WaitGroup  
wg.Add(2)  
go func() {...}()  
//获取post列表  
go func() {...}()  
wg.Wait()  
return nil  
}

Controller

  • 构建view对象
  • 业务错误码
type PageData struct {  
Code int64 `json:"code"`  
Msg string `json: "msg"`  
Data interface{} `json:"data"`  
}  
func QueryPageInfo(topicIdStr string) *PageData {  
topicId, err := strconv.ParseInt(topicIdStr, 10, 64)  
if err != nil {  
return &PageData{...}  
}  
pageInfo,err := service.QueryPageInfo(topicId)  
if err != nil {  
return &PageData{...}  
}  
return &PageData{...}  
}

Router

  • 初始化数据索引
  • 舒适化引擎配置
  • 构建路由
  • 启动服务
func main() {  
if err := Init("./data/"); err != nil {  
os.Exit(-1)  
}  
r := gin.Default()  
r.GET("/community/page/get/:id", func(c *gin.Context) {  
topicId := c.Param("id")  
data := cotroller.QueryPageInfo(topicId)  
c.JSON(200, data)  
})  
err := r.Run()  
if err != nil {  
return  
}  
}

小结

本篇文章的代码均来自于后端基础课Go 语言工程实践之测试 - 掘金 (juejin.cn) 还是有很多地方不太理解的,因为自己也是没有任何相关的基础知识,所以学习起来会有点吃力,现在主要是了解了相关的流程知识,有一个大概的认知,不会毫无头绪了。从分析需求开始一步一步直到可以写完整个程序并且运行起来,也是学到了很多知识,有些代码还是不太理解,以后还得慢慢琢磨