优雅地实现协程同步

image-20230116183527890

实际使用:在开启n个协程的时候Add(n)

在每个协程结束都defer Done()

在主协程使用Wait()阻塞等待子协程结束

Go中的互斥锁和原子操作

互斥锁(Golang实现sync.Mutexsync.RWMutex

原子操作(Golang实现sync/atomic

(补充:原子操作和互斥锁

Go限流器

官方的基于令牌桶的限流器,见time/rate包的Limiter类型

Go协程通信

  • Go提倡通过通信来共享内存,而不是通过共享内存来通信

    前者就是channel

    尽管不提倡后者,但go也支持,即通过sync.Mutex来实现对临界区的并发安全保证

    image-20230116184037355

Go中的依赖管理

GOPATH→Go Vendor→Go Module

  • GOPATH

    GOPATH是Go语言支持的一个环境变量,其value是Go项目的工作区

    弊端:无法实现package的多版本控制

    image-20230116185220667
  • Go Vendor

    Vendor 是当前项目中的一个目录,其中存放了当前项目依赖的副本(等于是给每个项目添加了其依赖的副本)

    弊端:

    image-20230116185443255
  • Go Module

    image-20230116190147295
  • 新建项目后

    go mod init projectName

    go mod tidy

VSCode很难优雅地对Golang项目进行依赖管理,还得是Goland

Go框架三件套

  • gorm

    官方文档具体使用看文档吧,下面的笔记感觉会有问题

    ORM框架

    对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中

    ORM框架是连接数据库的桥梁

    • db.First()的使用踩坑
      使用First 时,需要注意查询不到数据会返回ErrRecordNotFound
      使用Find查询多条数据,查询不到数据不会返回错误。

      所以一般使用db.Find(),在查询不到数据的时候会返回一个空切片,不会抛出错误

    • 使用结构体作为查询条件

      当使用结构作为条件查询时,GORM只会查询非零值字段。

      这意味着如果您的字段值为0、’’、false 或其他零值,该字段不会被用于构建查询条件,使用Map来构建查询条件

    • 更新时同理,如果要更新为零值,需要使用map;通过select选择字段

      {ID: 111}是兜底处理,当使用了 Model 方法,且该对象主键有值,该值会被用于构建条件

    • 删除数据

      物理删除db.Delete()

      软删除:在结构体中加上gorm.DeletedAt字段

      image-20230117210311872
    • 链式调用时,Where()这种api是用来拼SQL的

      First()Find()Create()Update()Delete()这种api会直接执行SQL,后面再加Where()不会生效

    • 事务

      db.Begin()会返回固化链接,所以要使用tx,而不是db,db是golang维护的链接池

    • Hook

    • GORM性能提升

      • 关闭默认事务
      • 缓存预编译语句
      • 等等
  • Kitex

    RPC框架

    RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP/IP或UDP,为通信程序之间携带信息数据。RPC将原来的本地调用转变为调用远端的服务器上的方法,给系统的处理能力和吞吐量带来了近似于无限制提升的可能。在OSI网络通信模型中,RPC跨域了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

    有点晕

  • Hertz

    Hertz是一个 Golang 微服务 HTTP 框架

    看文档吧,感觉就是在读文档,但是还没文档讲的细

Go自动内存管理

  • 自动内存管理是对于动态内存而言的

    如程序在运行时根据需求动态分配的内存:malloc()

  • 自动内存管理(垃圾回收):由程序语言的运行时系统管理动态内存

    • 避免手动内存管理,专注于实现业务逻辑

    • 保证内存使用的正确性和安全性:double-free problem, use-after free problem

  • 三个任务:

    • 为新对象分配空间
    • 找到存活对象
    • 回收死亡对象的内存空间
  • 相关概念

    Mutator: 业务线程,分配新对象,修改对象指向关系

    Collector: GC线程,找到存活对象(内存标记),回收死亡对象的内存空间(内存回收)

    Serial GC: 只有一个collector

    Parallel GC: 支持多个collectors 同时回收的GC算法

    Concurrent GC: mutator(s)和collector(s)可以同时执行

    示意图:

    image-20230126153502225
    • concurrent GC的难点在于,collectors必须要感知到对象指向关系的改变

      如下图中的GC过程中新添加的b,也需要被标记

      image-20230126154832414
    • 评价GC算法
      • 安全性(Safety): 不能回收存活的对象 基本要求
      • 吞吐率(Throughput): $$1-\frac{GC时间}{程序执行总时间}$$
      • 暂停时间(Pause time): stop the world (STW) 业务是否感知
      • 内存开销(Space overhead) GC元数据开销
    • 追踪垃圾回收(Tracing garbage collection)
    • 引用计数(Reference counting)
  • 追踪垃圾回收(Tracing garbage collection)

    image-20230126165348060

    清理不可达对象时会有三种策略

    • Copying GC

      image-20230126165615645
    • Mark-Sweep GC

      image-20230126165640236
    • Mark-Compact(标记-压缩) GC

      image-20230126165710927
  • 分代GC(Generational GC)场景

    分代假说:most objects die young

    年轻代采用copying GC,因为其存活时间短,所以使用copying GC不会特别耗时

    老年代采用Mark-sweep GC,对象存活时间长,反复复制开销较大

    image-20230126170732333

    若发现碎片较多,可以采取一次Compact(压缩)

  • 引用计数(Reference counting)

    计算该对象被多少个指针“引用”,即被多少个指针指向

    image-20230126171321147

    优点:

    • 内存管理的操作被平摊到程序执行过程中

    • 内存管理不需要了解runtime的实现细节,如C++的智能指针(smart pointer)

      追踪垃圾回收(Tracing garbage collection)需要和runtime耦合的更紧密

    缺点:

    • 维护引用计数的开销较大: 通过原子操作保证对引用计数操作的原子性可见性

    • 无法回收环形数据结构,如下图中已经不可达的红色结点组成的环

      image-20230126171745361

      通过weak reference解决这个问题

    • 内存开销: 每个对象都引入的额外内存空间存储引用数目

    • 回收内存时依然可能引发暂停,如下图中橙色圈出的节点释放后,会引起很多内存的回收,这个过程中仍然可能引发暂停

      image-20230126172009386

Go内存分配

  • 分块

    image-20230126173409944
  • 缓存

    image-20230126173733746
  • 优化原因:

    • 分配路径过长
    • 小对象过多
    image-20230126173916953

    优化案例:Balanced GC(指针碰撞风格对小对象进行分配)

    image-20230126174127142 image-20230126174312023

Go编译器优化

image-20230126180628190
  • 函数内联

    image-20230126181028947
  • Beast Mode

    image-20230126181331485
  • 逃逸分析:分析代码中指针的动态作用域,即指针在何处可以被访问

    函数内联可以减少逃逸对象的数量

    image-20230126181701934