2020年11月份

AI 摘要: 本文主要介绍了金字塔PPT设计、微服务基础设施、Go并发编程实战中的关键信息。金字塔PPT设计、框图规整、技术影响力等是答辩PPT设计的关键点;微服务基础设施包括分布式系统通信、服务集群流量管理、注册中心等内容;Go并发编程实战中介绍了互斥锁解决方案、同步原语/并发原语、race detector检测与分析等知识。

寻找好的方法,只要方向对了,剩下的只是时间问题,做时间的朋友!

2020-11-23

从答辩PPT内看到的关键信息

  1. 金字塔PPT设计
  2. 框图规整
  3. 技术影响力(内部:码客、开源协同、工蜂,外部:Infq、StackOverFlow、Github)
  4. 系统、框图成体系化
  5. 对团队贡献(招聘、带新人、分享),解决团队痛点问题

Tips:多参与、产出、总结,内化为自己的能力,帮助团队和自己成功!一定要做好总结材料收集,日常工作中,多记录内容;

2020-11-26

微服务基础设施

  1. 分布式系统中,进程如何通信
    1. RPC框架选项:社区活跃、文档、多语言、开源、
    2. 性能:序列化协议 Hession
    3. 替换成本
    4. 微服务组件、生态:通信、注册、配置、负载均衡、断路器
    5. ServerMesh
  2. 实践经验
    1. 规范化、标准化
    2. 服务保护:过载保护(做限流、做熔断)、降级、异常处理
    3. 服务健壮:
      1. 流量容错(failfast、failover、backup request)
      2. 调用方对服务状态感知、框架端到端心跳弹活检测
      3. 多样化调用方式支持:异步、Oneway调用、泛化调用
  3. 服务集群流量管理
    1. 服务调用,调用方如何找到对端地址
    2. 服务扩容,调用方如何发现新节点
    3. 服务保存,如何让故障节点失效
  4. 注册中心
    1. 服务注册中心:服务节点向注册中心登记(ServiceName、IP、Port)->客户端订阅(ServiceName)->中心推送(IP、Port)->业务调用
    2. 服务扩容:新注册节点注册->中心推送->业务调用
    3. 服务节点异常:服务节点周期与注册中心健康检测、中心异常推送给客户端、客户端摘除异常流量节点
    4. 路由策略:
    5. 选项:ETCD、Zookeeper、Eureka、Consul、Nacos
    6. 注册中心搭建经验:
      1. 弱化注册中心依赖
      2. 减少不必要数据交互
      3. 优先保证可用而非一致(AP > CP)
  5. 问题定位
    1. 分布式链路复杂
    2. 监控:https://peter.bourgon.org/blog/2017/02/21/metrics-tracing-and-logging.html
      1. 日志分散,将日志进行集中化
      2. Metric指标:QPS、分位数延时等
      3. 分布式链路追踪Tracing
  6. 团队如何协作
    1. 业务职能组织(产品、设、前、后、运维)
    2. 康威定律

Go并发编程实战

  1. 互斥锁解决方案:Go通过使用Mutex互斥锁/排它锁实现对临界区访问解决,解决多个GoRoutine同时访问临界区导致的并发问题
  2. 同步原语/并发原语,解决并发的数据结构,Mutex、读写RWMutex、并发编程WaitGroup、条件变量Cond、Channel都是同步原语
  3. 场景
    1. 共享资源:数据竟态条件,并发共享资源,需要通过Mutex、RWMutex来保护共享资源
    2. 任务编排:让Goroutine按一定规律执行,通常使用WaitGroup或者Channel实现
    3. 消息传递:不同Goroutine之间通过Channel通信
  4. Locker接口,包含Lock()Unlock()方法,Mutex、与RWMutex都实现了Locker接口,一个Goroutine持有Lock()锁后,其他Goroutine再执行Lock()会被阻塞
  5. 经典并发问题,count++非原子操作,成为了竟态资源,参考:https://blog.golang.org/race-detector, 编译器通过探测所有内存访问,加入代码对内存地址读写访问的监控,代码运行时候,race detector就可以监控到对共享变量的非同步访问,出现race时候告警;race detector已成为Go持续集过程中的一部分。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    import (
        "fmt"
        "sync"
    )
    
    func main() {
        var count = 0
        // 使用WaitGroup等待10个goroutine完成
        var wg sync.WaitGroup
        wg.Add(10)
        for i := 0; i < 10; i++ {
            go func() {
                defer wg.Done()
                // 对变量count执行10次加1
                for j := 0; j < 100000; j++ {
                    count++
                }
            }()
        }
        // 等待10个goroutine完成
        wg.Wait()
        fmt.Println(count)
    }
  1. race detector检测与分析
    1. 在编译、测试、运行Go代码时候加上race参数,就可能发现并发问题
    2. go run -race counter.go,可以发现不同Goroutine对同一内存地址有读写操作
    3. 因为要通过真实的地址进行读写访问,所以并不能在编译时候发现,只有在触发了data race时候才能检测到
    4. 开启data race比较影响性能
    5. 查看编译后的汇编代码:go tool compile -race -S counter.go,可以发现runtime的racefunceter、raceread、racewrite、racefuncexit等方法
  2. Mutex
    1. 零值表示未加锁状态
    2. 支持将Mutex嵌入到其他struct结构体中
    3. 通常将Mutex放在控制共享字段之上,并加行空格(风格问题)
    4. 可以将获取锁、计算逻辑、释放锁封装成一个方法
    5. 释放锁通常基于defer mu.unlock()实现,但注意如果跨度很大,锁定时间会比较长,考虑拎出来
  3. 锁释放,等待锁的哪个Goroutine被唤醒?
  4. tradeoff:复杂度、性能、结构设计权衡
    1. 简单实现:flag标记是否持有锁,基于CAS(compare-and-swap或者compare-and-set)将flag设置成1,标志当前goroutine持有锁
    2. 给个机会:新goroutine有机会竞争锁
    3. 多些机会:新的和被唤醒会有更多机会竞争锁
    4. 解决饥饿:防止gorotuine等待太久