go语言进阶| 青训营笔记

58 阅读4分钟

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

一、本堂课重点内容:

  • go语言并发编程(go高性能的本质)
  • go语言的依赖管理
  • go语言的单元测试
  • go语言项目实战

二、详细知识点介绍:

  • 并发

    • 并发与并行(并发 多线程程序在一个核上运行 并行 多线程程序在多个核上运行)
    • go 协程 用户态的轻量级线程
    • 协程之间通信
      • 提倡通过通信共享内存
      • channel 通道 通过通道来实现通信
      • 有缓冲channel make(chan int,2) 无缓冲channel(同步channel) make(chan int)
    • 并发安全Lock 加锁 释放锁操作 (lock sync.Mutex)
    • WaitGroup 内部维护了一个计数器
      • Add 方法 计数器+1
      • Done 方法 计数器-1
      • Wait 方法 阻塞知道计数器为0
  • go依赖管理

    • GOPATH -> GOVendor -> GOMoudle
    • GOMoudle
      • 通过go.mod 文件来管理依赖包版本
      • go get/mod 工具来管理依赖
  • go测试

    • 回归测试 集成测试 单元测试(单元:接口 函数 模块...)
    • 单元测试
      • 所有测试文件以 _test.go 结尾
      • func Teestxxx(T *testing.T)
      • 初始化逻辑放到了 TestMain 中
      • 运行 go test [flags][packages]
      • assert包 不需要使用if了
      • 代码覆盖率 加上 --cover 这个选项 保证测试的完备性
    • Mock测试
    • 基准测试 保证性能
  • 社区话题项目

    • 需求
      • 展示话题和回帖列表
      • 仅实现本地web服务
      • 数据使用文件存储

三、实践练习例子:

  • 协程间通信 channel
func CalSquare() {
   src := make(chan int)
   dest := make(chan int,3)

   go func() {
      defer close(src)
      for i := 0; i < 10; i++ {
         src <- i
      }
   }()

   go func() {
      defer close(dest)
      for i := range src {
         dest <- i * i
      }
   }()

   for i := range dest {
      fmt.Println(i)
   }
}
  • waitGroup
func ManyGoWait() {
   var wg sync.WaitGroup
   wg.Add(5)
   for i := 0; i < 5; i++ {
      fmt.Println("hello goroutine---",i)
      wg.Done()
   }
   wg.Wait()
}
  • 社区话题项目实战
  • repository
package topicproject

import (
   "bufio"
   "encoding/json"
   "os"
   "sync"
)

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

func Init(filePath string) error {
   if err := initTopicIndexMap(filePath); err != nil {
      return err
   }
   if err := initPostIndexMap(filePath); err != nil {
      return err
   }
   return nil
}

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
}

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
}

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]
}

type Post struct {
   Id         int64  `json:"id"`
   ParentId   int64  `json:"parent_id"`
   Content    string `json:"content"`
   CreateTime int64  `json:"create_time"`
}
type PostDao struct {
}

var (
   postDao  *PostDao
   postOnce sync.Once
)

func NewPostDaoInstance() *PostDao {
   postOnce.Do(
      func() {
         postDao = &PostDao{}
      })
   return postDao
}
func (*PostDao) QueryPostsByParentId(parentId int64) []*Post {
   return postIndexMap[parentId]
}
  • service
package topicproject

import (
   "errors"
   "sync"
)

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

func QueryPageInfo(topicId int64) (*PageInfo, error) {
   return NewQueryPageInfoFlow(topicId).Do()
}

func NewQueryPageInfoFlow(topId int64) *QueryPageInfoFlow {
   return &QueryPageInfoFlow{
      topicId: topId,
   }
}

type QueryPageInfoFlow struct {
   topicId  int64
   pageInfo *PageInfo

   topic *Topic
   posts []*Post
}

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) checkParam() error {
   if f.topicId <= 0 {
      return errors.New("topic id must be larger than 0")
   }
   return nil
}

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

func (f *QueryPageInfoFlow) packPageInfo() error {
   f.pageInfo = &PageInfo{
      Topic:    f.topic,
      PostList: f.posts,
   }
   return nil
}
  • controller
package topicproject

import "strconv"

type PageData struct {
   Code int64       `json:"code"`
   Msg  string      `json:"msg"`
   Data interface{} `json:"data"`
}

func QueryPageInfoController(topicIdStr string) *PageData {
   topicId, err := strconv.ParseInt(topicIdStr, 10, 64)
   if err != nil {
      return &PageData{
         Code: -1,
         Msg:  err.Error(),
      }
   }
   pageInfo, err := QueryPageInfo(topicId)
   if err != nil {
      return &PageData{
         Code: -1,
         Msg:  err.Error(),
      }
   }
   return &PageData{
      Code: 0,
      Msg:  "success",
      Data: pageInfo,
   }

}
  • controller
package topicproject

import "strconv"

type PageData struct {
   Code int64       `json:"code"`
   Msg  string      `json:"msg"`
   Data interface{} `json:"data"`
}

func QueryPageInfoController(topicIdStr string) *PageData {
   topicId, err := strconv.ParseInt(topicIdStr, 10, 64)
   if err != nil {
      return &PageData{
         Code: -1,
         Msg:  err.Error(),
      }
   }
   pageInfo, err := QueryPageInfo(topicId)
   if err != nil {
      return &PageData{
         Code: -1,
         Msg:  err.Error(),
      }
   }
   return &PageData{
      Code: 0,
      Msg:  "success",
      Data: pageInfo,
   }

}
  • server
package topicproject

import (
   "github.com/gin-gonic/gin"
   "os"
)

func main() {
   if err := InitServer("./data/"); err != nil {
      os.Exit(-1)
   }
   r := gin.Default()
   r.GET("/community/page/get/:id", func(c *gin.Context) {
      topicId := c.Param("id")
      data := QueryPageInfoController(topicId)
      c.JSON(200, data)
   })
   err := r.Run()
   if err != nil {
      return
   }
}

func InitServer(filePath string) error {
   if err := Init(filePath); err != nil {
      return err
   }
   return nil
}

四、课后个人总结:

  • 本章内容讲述了go语言高阶知识,其中就包括go中最重要的并发编程,对于channel的使用还需要多加练习,以及waitGroup和锁的使用也是非常重要的。
  • 在golang的测试中,测试是一个项目完成的重要部分,保证我们的代码质量以及性能,也是非常重要的,掌握测试相关的功能对我们的提升很大。