这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。
学习笔记
记录学习过程中方便以后Go语言开发用来查看的笔记。
并发
通道
Channel类似一个管道,方便并发核心单元通讯。
操作符:<-,箭头指向数据传输方向
创建:make(chan 元素类型,[缓冲大小])
Lock
声明:lock sync.Mutex
使用:
lock.Lock()
/* code */
lock.Unlock()
WaitGroup
方法:Add(delta int)计数器+delta,Done()计数器-1,Wait()阻塞直到计数器为0。
依赖管理
依赖顺序:Go Module->Go Vendor->GOPATH
Go Module:go.mod文件管理依赖包版本,go get/mod指令管理依赖包。
go get example.org/pkg@update(默认)/none(删除依赖)/v1.1.2(tag版本)/23dfdd5(特定commit)/master(最新commit)
go mod init(初始化go.mod)/download(下载到本地缓存)/tidy(增加需要的依赖,删除不需要的依赖)
编译时会选择最低的兼容版本。
测试
单元测试
规则
- 测试文件以
_test.go结尾 - 测试函数
func TestXxx(t *testing.T) - 初始化逻辑放到
TestMain
assert
import增加github.com/stretchr/testify/assert
覆盖率
go test xxx_test.go xxx.go --cover
项目实践
这节课的项目实践是一个简单的社区话题页面的后端处理逻辑。
项目clone到本地后默认处于master分支,master分支貌似需要本地MySQL数据库开放3306端口,我不太熟悉数据库,所以切换到了v0.1分支上,使用以下命令:
git clone https://github.com/Moonlight-Zhao/go-project-example.git
cd go-project-example
git checkout v0.1
没有接触过相关的业务,所以梳理代码用了比较长的时间。
-
先从
server.go开始,这里用到了一个web框架gin,这个框架可以查看手册学习一下大概的使用方法,这里用到了Param和GetPostForm获取参数,之后变调用了下一层cotroller。 -
cotroller的代码有query_page_info.go,用来请求获得页面信息,以及publish_post.go,用来给帖子回帖。cotroller层的代码会调用service层。 -
service的功能是具体实现请求页面和发布的功能,包括检查参数(内容长度不超过500),为新回帖创建实体存储数据等,这里还用到了一个工具idworker,为每个帖子和回复生成不重复的ID。 -
在请求页面和发布时,会调用最底层
repository,这一层的db_init.go实现了初始化数据库,在v0.1分支上是实现文件系统管理内容。这一层还有post.go文件,实现新回帖时对文件系统的操作,保存数据。
梳理完代码,就可以尝试使用一下功能了,因为是老师给定的代码,所以不需要调试了,直接go run server.go。
可以看到监听了8080端口,那么另起一个终端,获取数据。
初始一共只有2个帖子,URL后面用1和2分别获取2个帖子的内容,这里用到了jq,让json的数据更直观,在archlinux可以用sudo pacman -S jq安装。直接打开浏览器也一样可以看到。
网页查看的时候可以安装JSON-handle插件让内容更直观,这个插件是群里的小伙伴推荐的,感谢!
我们也可以测试一下回帖,可以看到返回了success:
再GET一下,可以看到回帖了:
课后实践
课后作业要求增加发帖功能,保证ID唯一,Append文件更新索引,注意Map的并发安全问题。
既然梳理清楚了代码,仿照回帖功能做发帖功能即可。
- 修改
server.go,在main函数中增加发帖的请求:
r.POST("/community/topic/do", func(c *gin.Context) {
title, _ := c.GetPostForm("title")
content, _ := c.GetPostForm("content")
data := cotroller.PublishTopic(title, content)
c.JSON(200, data)
})
- 上面我们需要在cotroller层调用一个新函数
PublishTopic,在cotroller层新增一个文件publish_topic.go来实现它:
package cotroller
import (
"github.com/Moonlight-Zhao/go-project-example/service"
)
func PublishTopic(title, content string) *PageData {
//获取service层结果
topicId, err := service.PublishTopic(title, content)
if err != nil {
return &PageData{
Code: -1,
Msg: err.Error(),
}
}
return &PageData{
Code: 0,
Msg: "success",
Data: map[string]int64{
"topic_id": topicId,
},
}
}
- 这一层又调用了service层的新函数
PublishTopic,在service层新增一个文件publish_topic.go来实现它:
package service
import (
"errors"
"time"
"unicode/utf16"
"github.com/Moonlight-Zhao/go-project-example/repository"
)
func PublishTopic(title, content string) (int64, error) {
return NewPublishTopicFlow(title, content).Do()
}
func NewPublishTopicFlow(title, content string) *PublishTopicFlow {
return &PublishTopicFlow{
title: title,
content: content,
}
}
type PublishTopicFlow struct {
title string
content string
topicId int64
}
func (f *PublishTopicFlow) Do() (int64, error) {
if err := f.checkParam(); err != nil {
return 0, err
}
if err := f.publish(); err != nil {
return 0, err
}
return f.topicId, nil
}
func (f *PublishTopicFlow) checkParam() error {
if len(utf16.Encode([]rune(f.content))) >= 500 {
return errors.New("content length must be less than 500")
}
return nil
}
func (f *PublishTopicFlow) publish() error {
topic := &repository.Topic{
Title: f.title,
Content: f.content,
CreateTime: time.Now().Unix(),
}
id, err := idGen.NextId()
if err != nil {
return err
}
topic.Id = id
if err := repository.NewTopicDaoInstance().InsertTopic(topic); err != nil {
return err
}
f.topicId = topic.Id
return nil
}
- 这一层又调用了repository的新函数
InsertTopic,在这一层的topic.go文件中实现它:
func (*TopicDao) InsertTopic(topic *Topic) error {
f, err := os.OpenFile("./data/topic", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return err
}
defer f.Close()
marshal, _ := json.Marshal(topic)
if _, err = f.WriteString(string(marshal)+"\n"); err != nil {
return err
}
rwMutex.Lock()
_, ok := topicIndexMap[topic.Id]
if !ok {
topicIndexMap[topic.Id] = topic
}
rwMutex.Unlock()
return nil
}
OK,不出意外的话,处理一下import就可以尝试运行了,发个贴看看:
收到了success,查看一下有没有保存好新帖子,注意使用返回的topic_id:
帖子发出去了,我们给新帖子回复一下:
查看回帖有没有保存好:
也可以查看项目中data文件夹看到发布的新帖子:
哎呀,没有换行,不过作业里面可没要求这一条,累了,歇歇再搞,初学后端,如果有错误的地方和表述不正确的地方,请多指正,谢谢~