青训营Day1&Day2 | 青训营笔记

132 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

Day1

作业一

_, err := fmt.Scanf("%d", &guess)

作业二

选用搜狗翻译,收获最大的是学会了如何自动生成json字段结构体以及繁杂的curl请求。值得一提的是,json字段的代码生成,是使用一个具体的json实例来推测字段类型的,因此会出现精度不够的情况,比如float64被推测为int。

type DictRequest struct {
    // json结构体代码生成
}

type DictResponse struct {
    // json结构体代码生成
}

func query(word string) {
    client := &http.Client{}
    request := DictRequest{...}
    buf, err := json.Marshal(request)
    if err != nil {
        log.Fatal(err)
    }

    var data = bytes.NewReader(buf)
    req, err := http.NewRequest("POST", "https://fanyi.sogou.com/api/transpc/text/result", data)
    if err != nil {
        log.Fatal(err)
    }

    // 繁杂地设置header
    resp, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    bodyText, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }

    if resp.StatusCode != 200 {
        log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
    }
    var dictResponse DictResponse
    err = json.Unmarshal(bodyText, &dictResponse)
    if err != nil {
	log.Fatal(err)
    }
    fmt.Println(dictResponse) // 字段太多,懒得筛选了
}

func main() {
    if len(os.Args) != 2 {
	fmt.Fprintf(os.Stderr, `usage: simpleDict WORD 
                                example: simpleDict hello`)
	os.Exit(1)
    }
    word := os.Args[1]
    query(word)
}

image.png

作业三

go语言非常大的一个优势是,使用go程能够较容易地写出并发代码。

var wt sync.WaitGroup
wt.Add(2)
go func() {
    defer wt.Done()
    query_caiyun(word)
}()
go func() {
    defer wt.Done()
    query_sogou(word)
}()
wt.Wait()

Day2

并发编程

Channel

有无缓冲: make(chan int[, 2])

Mutex

var mutex sync.Mutex
Mutex.Lock()
x += 1
Mutex.Unlock()

WaitGroup

var wt sync.WaitGroup
wt.Add(5)
for i := 0; i < 5; i++{
    go func(){
        defer wt.Done()
        ...
    }
}
wt.Wait()

依赖管理

Gopath

Go Vendor

go mudule

  1. go mod init,初始化,创建go.mod文件
  2. go get example.org/pkg
  3. go mod tidy

测试

单元测试

  1. 目标:将函数、模块的输出结果与期望值进行校对;
  2. 规则
    • 所有测试文件以 _test.go 结尾;
    • func TestXxxYyy(t *testing.T) 函数名驼峰;
    • 初始化逻辑放到 TestMain 中: func TestMain(m *testing.M);
    • go test [flags] [packages]运行
  3. assert包
  4. 覆盖率

Mock测试

  1. 幂等
  2. 稳定

基准测试

  1. 确定瓶颈,优化代码;
  2. 例子:rand有一个全局锁,高并发场景像能查;fastrand牺牲部分一致性,但有更高的性能。

项目实战

跟着老师的思路过了一遍,课后作业暂时没有完成。

需求背景

青训营话题页

需求

  1. 展示话题(标题、文字描述)和回帖列表;
  2. 暂不考虑前端页面实现,仅仅实现一个本地web服务;
  3. 话题和回帖数据用文件存储。

实体关系

1个话题可以对应多个帖子,但一个帖子只能属于一个话题。

思路

数据层(Repository)

数据可以存储在文件中(目前),也可以存在数据库中。

// Topic
{
    "id":1,
    "title":"青训营开始了!",
    "content":"欢迎踊跃报名!",
    "create_time":1000000007
}

// Post
{
    "id":1,
    "parent_id":1,
    "content":"欢迎欢迎热烈欢迎!",
    "create_time":1000000007
}

知道了数据的json格式,可以通过自动代码生成定义相关的结构体。由于数据存储在文件中,相关索引需要自己设立以提高查询速度,目前创建的两个map分别是话题ID到话题的映射,以及话题ID到帖子列表的映射。

var (
    topicIndexMap map[int64]*Topic
    postIndexMap  map[int64][]*Post
)

func initTopicIndexMap(filePath string) error{
}

func initPostIndexMap(filePath string) error {
    open, err := os.Open(filePath + "post")
    if err != nil {
            return err
    }
    scanner := bufio.NewScanner(open)
    postTmpMap := make(map[int64][]*Post)
    for scanner.Scan() {
            text := scanner.Text()
            var post Post
            if err := json.Unmarshal([]byte(text), &post); err != nil {
                    return err
            }
            posts, ok := postTmpMap[post.ParentId]
            if !ok {
                    postTmpMap[post.ParentId] = []*Post{&post}
                    continue
            }
            posts = append(posts, &post)
            postTmpMap[post.ParentId] = posts
    }
    postIndexMap = postTmpMap
    return nil
}
逻辑层(service)

抽象出一个页面实体,包含一个话题,以及该话题下的所有帖子。

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

server端收到一个页面请求ID,也即话题ID(页面ID和话题ID一一对应),需要返回话题信息和该话题下所有帖子的信息。话题信息和帖子列表信息存在两个不同的map中,可以并发读取。

func (f *QueryPageInfoFlow) prepareInfo() error {
    //获取topic信息
    var wg sync.WaitGroup
    wg.Add(2)
    go func() {
        defer wg.Done()
        topic := repository.NewTopicDaoInstance().QueryTopicById(f.topicId)
        f.topic = topic
    }()
    //获取post列表
    go func() {
            defer wg.Done()
            posts := repository.NewPostDaoInstance().QueryPostsByParentId(f.topicId)
            f.posts = posts
    }()
    wg.Wait()
    return nil
}
视图层(controller)

将所有信息,例如code,msg,data(包括topic和postlist)整合成一个结构变量,返回给client。

测试结果

使用 jshon 工具对输出结果格式化 image.png