day1 go语言上手-基础语言

https://bytedance.feishu.cn/file/boxcnQnHXuDOdzd8CqVid7nQLmg

随机数

rand.Intn () 函数是个伪随机函数,不管运行多少次都只会返回同样的随机数,因为它默认的资源就是单一值,所以必须调用 rand.Seed (), 并且传入一个变化的值作为参数,如 time.Now().UnixNano() , 就是可以生成时刻变化的值.

1
2
3
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)

输入

输入bufio的reader

1
2
3
4
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')  //以空格结束
input = strings.TrimSuffix(input, "\r\n")
uess, err := strconv.Atoi(input)  //转为int
fmt.Scan 获取输入
fmt.Scanf 获取输入,但是可以指定格式,go语言会根据格式解析参数
fmt.Scanln 获取一行的输入,只会获取到一行
1
2
3
4
5
6
7
8
9
var guess int
_ , err := fmt.Scanf("%v\n",&guess)


var a1,a2 string
fmt.Scanln(&a1,&a2)//如果换行输出不了a2了

var a1,a2 string
fmt.Scanf("%s %s",&a1,&a2)

自动生成爬虫代码

https://fanyi.caiyunapp.com/#/

要求是bash

image-20220506151241567

https://curlconverter.com/#go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

func main() {
	client := &http.Client{}
	var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Accept", "application/json, text/plain, */*")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("Sec-Fetch-Dest", "empty")
	req.Header.Set("Sec-Fetch-Mode", "cors")
	req.Header.Set("Sec-Fetch-Site", "cross-site")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36")
	req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
	req.Header.Set("app-name", "xy")
	req.Header.Set("os-type", "web")
	req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Windows"`)
	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)
	}
	fmt.Printf("%s\n", bodyText)
}

运行代码输出

1
{"rc":0,"wiki":{"known_in_laguages":63,"description":{"source":"tangible and intangible thing, except labor tied services, that satisfies human wants and provides utility","target":null},"id":"Q28877","item":{"source":"good","target":"\u5546\u54c1"},"image_url":"http:\/\/www.caiyunapp.com\/imgs\/link_default_img.png","is_subject":"true","sitelink":"https:\/\/www.caiyunapp.com\/read_mode\/?id=625b4b949c0120504d1e7b69"},"dictionary":{"prons":{"en-us":"[g\u028ad]","en":"[gud]"},"explanations":["a.\u597d\u7684;\u5584\u826f\u7684;\u5feb\u4e50\u7684;\u771f\u6b63\u7684;\u5bbd\u5927\u7684;\u6709\u76ca\u7684;\u8001\u7ec3\u7684;\u5e78\u798f\u7684;\u5fe0\u5b9e\u7684;\u4f18\u79c0\u7684;\u5b8c\u6574\u7684;\u5f7b\u5e95\u7684;\u4e30\u5bcc\u7684","n.\u5229\u76ca;\u597d\u5904;\u5584\u826f;\u597d\u4eba","ad.=well"],"synonym":["excellent","fine","nice","splendid","proper"],"antonym":["bad","wrong","evil","harmful","poor"],"wqx_example":[["to the good","\u6709\u5229,\u6709\u597d\u5904"],["good, bad and indifferent","\u597d\u7684,\u574f\u7684\u548c\u4e00\u822c\u7684"],["good innings","\u957f\u5bff"],["good and ...","\u5f88,\u9887;\u5b8c\u5168,\u5f7b\u5e95"],["do somebody's heart good","\u5bf9\u67d0\u4eba\u7684\u5fc3\u810f\u6709\u76ca,\u4f7f\u67d0\u4eba\u611f\u5230\u6109\u5feb"],["do somebody good","\u5bf9\u67d0\u4eba\u6709\u76ca"],["be good for","\u5bf9\u2026\u6709\u6548,\u9002\u5408,\u80dc\u4efb"],["be good at","\u5728\u2026\u65b9\u9762(\u5b66\u5f97,\u505a\u5f97)\u597d;\u5584\u4e8e"],["as good as one's word","\u4fe1\u5b88\u8bfa\u8a00,\u503c\u5f97\u4fe1\u8d56"],["as good as","\u5b9e\u9645\u4e0a,\u51e0\u4e4e\u7b49\u4e8e"],["all well and good","\u4e5f\u597d,\u8fd8\u597d,\u5f88\u4e0d\u9519"],["a good","\u76f8\u5f53,\u8db3\u8db3"],["He is good at figures . ","\u4ed6\u5584\u4e8e\u8ba1\u7b97\u3002"]],"entry":"good","type":"word","related":[],"source":"wenquxing"}}

https://oktools.net/json2go

转为go结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
type DictResponse struct {
	Rc int `json:"rc"`
	Wiki struct {
		KnownInLaguages int `json:"known_in_laguages"`
		Description struct {
			Source string `json:"source"`
			Target interface{} `json:"target"`
		} `json:"description"`
		ID string `json:"id"`
		Item struct {
			Source string `json:"source"`
			Target string `json:"target"`
		} `json:"item"`
		ImageURL string `json:"image_url"`
		IsSubject string `json:"is_subject"`
		Sitelink string `json:"sitelink"`
	} `json:"wiki"`
	Dictionary struct {
		Prons struct {
			EnUs string `json:"en-us"`
			En string `json:"en"`
		} `json:"prons"`
		Explanations []string `json:"explanations"`
		Synonym []string `json:"synonym"`
		Antonym []string `json:"antonym"`
		WqxExample [][]string `json:"wqx_example"`
		Entry string `json:"entry"`
		Type string `json:"type"`
		Related []interface{} `json:"related"`
		Source string `json:"source"`
	} `json:"dictionary"`
}

SOCKS5

https://zhuanlan.zhihu.com/p/438521117

image-20220506111513080

image-20220506111526219

image-20220506113933058

image-20220506114341603

老师,请问go的字符串拼接效率问题,一般考虑到综合易用性和性能,推荐使用 strings.Builder 来拼接字符串。但为什么有的文章会说标准编译器对使用+运算符的字符串衔接做了特别的优化。 所以,一般说来,在被衔接的字符串的数量是已知的情况下,使用+运算符进行字符串衔接是比较高效的。请问下一般在项目中如何拼接字符串是比较高效合理的呢

day2 go语言上手-应用实践

https://bytedance.feishu.cn/file/boxcnRmlw9MjbtAMBnOW44y8dZd?hash=7cfc75acc80372c08463b622df90a4b5

go依赖管理引进

2个目标

不同环境依赖的版本不同

控制依赖库的版本

GOPATH

GO Vendor

GO Module

go.mod

Proxy

go get/mod

测试

单元测试

1
2
3
4
5
package concurrence

func HelloTom() string {
	return "Tom"
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package concurrence

import "testing"

func TestHelloTom(t *testing.T) {
	output := HelloTom()
	expectOutput := "Tom"
	if output != expectOutput {
		t.Errorf("Expected %s do not match actual %s", expectOutput, output)
	}
}
assert
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package concurrence

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestHelloTom(t *testing.T) {
	output := HelloTom()
	expectOutput := "Tom"
	assert.Equal(t, expectOutput, output)
}
覆盖率
1
2
3
4
5
6
7
8
package concurrence

func JudgePassLine(score int16) bool {
	if score >= 60 {
		return true
	}
	return false
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package concurrence

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestJudgePassLineTrue(t *testing.T) {
	isPass := JudgePassLine(70)
	assert.Equal(t, true, isPass)
}

func TestJudgePassLineFalse(t *testing.T) {
	isPass := JudgePassLine(50)
	assert.Equal(t, false, isPass)
}
1
go test .\demo_test.go .\demo.go --cover

Mock测试

基准测试

day3 高质量编程与性能调优实战

https://bytedance.feishu.cn/file/boxcnqqWtT0xgWAIMGWVs7wM6fd

高质量编程

高质量-正确可靠、简洁清晰

编码规范

代码格式

gofmt

goimports

注释

解释代码作用–适合注释公共符号

解释代码代码是如何做的–适合代码逻辑比较多的

解释代码实现的原因–提供额外上下文

解释代码什么情况下会出错–

公共符号始终要注释,不需要注释实现接口的方法

命名规范

简洁胜于冗长

缩略词全大写

有特定的含义

函数名不带包的上下文信息

控制流程

优先处理错误情况,尽早返回

image-20220508185654638

image-20220508185726866

错误和异常处理

性能优化建议

benchmark
Slice

尽量初始化容量信息,避免内存发生拷贝

因为切片操作并不复制切片指向的元素,创建一个新的切片会复用原来切片的底层数组,所以会有大内存未释放的陷阱

image-20220508191454635

Map

预分配内存

字符串处理

+最慢

strings.Builder

bytes.Buffer最快

image-20220508195908309

image-20220508200629914

image-20220508200610917

空结构体

可以用来实现set

atomic包

锁的实现是由操作系统来实现,属于系统调用

atomic操作通过硬件实现,效率比锁高

性能调优实战

要依靠数据,而不是猜测

要定位最大瓶颈而不是细枝末节

不要过早优化

不要过度优化

pprof实战

https://blog.wolfogre.com/posts/go-ppof-practice/

cpu占用情况

go tool pprof http://localhost:6060/debug/pprof/profile

内存分配情况

go tool pprof http://localhost:6060/debug/pprof/heap

阻塞操作情况

go tool pprof http://localhost:6060/debug/pprof/block

goroutine堆栈

go tool pprof http://localhost:6060/debug/pprof/goroutine

锁竞争

go tool pprof http://localhost:6060/debug/pprof/mutex

gc

go tool pprof http://localhost:6060/debug/pprof/allocs

pprof采样过程和原理

性能调优案例

业务服务优化

建立服务性能评估手段

  1. 服务性能评估手段
  2. 请求流量构造
  3. 压测范围
  4. 性能数据采集

分析性能数据,定位性能评价

重点优化项改造

优化效果验证

进一步优化,服务整体链路分析

AB实验SDK的优化

编译器和运行时优化

课后作业-重点内容 Review

  1. 了解下其他语言的编码规范,是否和 Go 语言编码规范有相通之处,注重理解哪些共同点?

  2. 编码规范或者性能优化建议大部分是通用的,有没有方式能够自动化对代码进行检测?

  3. github.com/golang/go/t… 中选择感兴趣的包,看看官方代码是如何编写的?

  4. 使用 Go 进行并发编程时有哪些性能陷阱或者优化手段?

  5. 在真实的线上环境中,每个场景或者服务遇到的性能问题也是各种各样,搜索下知名公司的官方公众号或者博客,里面有哪些性能优化的案例?比如 eng.uber.com/category/os…

  6. Go 语言本身在持续更新迭代,每个版本在性能上有哪些重要的优化点?

    < 作业提交截止时间:5月9日 10:00前 >

正确答案:

  1. 可以了解下开源项目的编码规范,比如Google开源项目风格指南,

    google.github.io/styleguide

    https://zh-google-styleguide.readthedocs.io/en/latest/

  2. Go 语言有代码检查工具,可以和CI进行集成,https://github.com/golangci/golangci-lint

  3. 可以优先看看sync和net包的内容,实际在服务端编程过程中会经常用到

    https://github.com/golang/go/tree/master/src/sync

    https://github.com/golang/go/tree/master/src/net

  4. 可以看看Effective Go的并发编程章节,

    https://go.dev/doc/effective_go#concurrency

    https://github.com/geektutu/high-performance-go

  5. 实际优化案例,

    https://eng.uber.com/how-we-saved-70k-cores-across-30-mission-critical-services/

    https://medium.com/coralogix-engineering/optimizing-a-golang-service-to-reduce-over-40-cpu-366b67c67ef9

  6. Go 的每次版本更新都有详细的文档说明,可以从中了解性能相关的信息,

    https://go.dev/doc/devel/release

day4 高性能go语言发行版优化与落地实践

https://bytedance.feishu.cn/file/boxcnRcx62rX5X22Q2WFR5Xm5Oh?hash=cd874ae31e355b9a1f34ae0dad2fad5e

内存管理优化

编译器优化

性能优化的基本问题

性能优化是什么?

提升软件系统处理能力,减少不必要的耗费、充分发掘计算机算力

为什么要性能优化

用户体验

资源高效利用

性能优化的2个层面

业务层优化

语言运行时优化

性能优化的可维护性

自动内存管理

自动内存管理:由程序语言的运行时系统管理动态内存

避免手动处理内存

保证内存使用的正确性和安全性

image-20220512100143414

评价gc算法:安全性、吞吐率、暂停时间、内存开销

image-20220512100551366

分代GC

引用计数

image-20220512101527133

go内存管理及优化

分块

缓存

Go对象分配的性能问题

分配路径过长

小对象居多

Balanced GC

指针碰撞风格的对象分配

实现了copying GC

性能收益

编译器和静态分析

编译器的结构与编译的流程

编译器后端优化

数据流和控制流分析

过程内和过程间分析

go编译器优化

Tradeoff

:用编译时间换取更高效的机器码

Beast mode

函数内联

逃逸分析

课后作业- 重点内容 Review

  1. 从业务层和语言运行时层进行优化分别有什么特点?

  2. 从软件工程的角度出发,为了保证语言SDK的可维护性和可拓展性,在进行运行时优化时需要注意什么?

  3. 自动内存管理技术从大类上分为哪两种,每一种技术的特点以及优缺点有哪些?

  4. 什么是分代假说?分代 GC 的初衷是为了解决什么样的问题?

  5. Go 是如何管理和组织内存的?

  6. 为什么采用 bump-pointer 的方式分配内存会很快?

  7. 为什么我们需要在编译器优化中进行静态代码分析?

  8. 函数内联是什么,这项优化的优缺点是什么?

  9. 什么是逃逸分析?逃逸分析是如何提升代码性能的?

    < 作业提交截止时间:5月12日 10:00前 >

正确答案:

  1. 业务层的优化需要针对具体问题具体分析;而语言运行时层的优化针对的是更通用的问题,需要在多方面进行 tradeoff。两者都需要自动化性能分析工具的支持。

  2. 因为语言 SDK 会被大量广泛使用,保证接口的稳定性和一致性至关重要。对已有接口的改动,需要保证兼容性;对新增的接口,需要提供明确的文档说明。对于功能的改动,最好通过选项进行隔离,保证新增改动在不打开的情况下不影响原本的功能。

  3. 主要分为追踪垃圾回收和引用计数:

    a. 追踪垃圾回收

    1
    2
    3
    4
    5
    
    1️⃣ 回收内存的条件:回收不可达的对象;
    2️⃣ 回收时,首先会扫描 GC roots,例如栈上的对象、全局变量等;
    3️⃣ 从 GC roots 出发,沿着指针指向,追踪所有的可达对象;
    4️⃣ 追踪完成后,回收所有不可达对象的内存。
    复制代码
    

    b. 引用计数

    1
    2
    3
    4
    5
    
    1️⃣ 对象有一个与之关联的引用数目。当且仅当引用数大于 0 时对象存活;
    2️⃣ 当对象的引用数目为 0 时,内存可以被回收;
    3️⃣ 优点:内存管理的开销被平摊到程序运行中,且内存管理不需要了解 runtime 的实现细节;
    4️⃣ 缺点:维护引用计数的开销较大;额外的内存开销存储引用计数;无法回收带有环的数据结构;回收大数据结构时依然会造成程序暂停。
    复制代码
    
  4. 大量的对象会很快死去。分代 GC 的思想是:针对不同生命周期的对象采取不同策略的内存管理机制。

  5. Go 使用 TCMalloc 风格的内存管理方式。

    a. TC 是 thread caching 的简写。每个线程都绑定 cache 方便线程快速分配内存;

    b. 内存被划分为特定大小的块。根据对象是否包含指针,将内存块分为 scan 和 noscan 两种;

    c. 根据内存分配请求的大小,选择合适的内存块返回,完成一次内存分配操作;

    d. 回收的内存不会立刻还给操作系统,而是在 Go 内部缓存起来,方便下次分配。

  6. 每个线程都持有用于对象分配的 buffer,因此指针碰撞方式的内存分配无需加锁或使用 CAS 操作;对象分配的操作非常简单。

  7. 通过静态分析,我们可以获取更多关于程序的非平凡特性 (non-trivial properties);这些关于程序的知识可以指导编译器优化。例如通过逃逸分析得知对象并未逃逸出当前函数,因此对象可以在栈上分配,避免频繁在堆上分配对象,降低 GC 的压力。

  8. 函数内联:将被调用函数的函数体的副本替换到调用位置上,同时重写代码以反映参数的绑定。

    a. 优点

    1
    2
    3
    
    ① 消除函数调用;
    ② 由于没有了函数调用,过程间分析转化为过程内分析;
    复制代码
    

    b. 缺点

    1
    2
    3
    
    ① 函数体变大;
    ② 编译生成的 Go 镜像变大。
    复制代码
    
  9. 逃逸分析:分析代码中指针的动态作用域,即指针在何处可以被访问。

    通过逃逸分析得知对象并未逃逸出当前函数,因此对象可以在栈上分配,避免频繁在堆上分配对象,降低 GC 的压力。

day5 GORM

https://bytedance.feishu.cn/file/boxcnct7Jc8Td2Oolfbm6t4HfJg?hash=e52044cf65cc597854fbf299f2a26073

理解database/sql

基本用法

设计原理

基础概念

GORM使用简介

基本用法

Model定义

惯例约定

关联

GORM设计原理

SQL生成

插件扩展

ConnPool

Dialector

GORM最佳实践

数据序列化与SQL表达式

批量数据操作

代码复用、分库分表、Sharding

混沌工程/压测

Logger/Trace

Migrate

Gen代码生成/Raw SQL

安全

课后作业

作业要求:首先实现一个脚本工具:包含一个 User struct, 只包含 UUID string, Name string,Age int,Version int 四个字段,在脚本中使用 gorm + mysql 初始化 DB, 并使用初始化后的 DB 的 AutoMigrate 迁移数据表。

然后完成一个 Gen (github.com/go-gorm/gen/) 项目,基于上面创建的数据库及表名,通过 Gen 的自动同步库表功能生成 struct People,并给该 struct 生成基本 CRUD 方法,基于 OnConflict Upsert 功能实现 100 个随机用户的创建,其中需要包含重复的 UUID 用户的 Upsert, 在 Upsert 时,如果遇到重复 UUID 中,需要将 Version 更新为 Version + 1。最后再通过一条自定义的Raw SQL 实现,将数据按 Version 分组,并取出 Version 最高的一组的用户总数的功能,该 Raw SQL 需要通过自定义查询方法的形式实现,需要给 People 生成相应的方法名: GetMaxVersionCount。完成后提交代码到 github

< 作业提交截止时间:5月14日 10:00前 >

正确答案:

通过 docker compose 跑起来环境后,go test 测试通过成后即可

  • 通过实现插件的 callbacks 接口,然后通过 Use 注册使用加解密码功能,在实现中需要从 db.Statement.Context 读取到当前租户 ID,然后通过该 ID 进行加解密

通过 docker compose 跑起来环境后,go test 测试通过成后即可

day6.1 实战项目

https://bytedance.feishu.cn/docx/doxcnDExcSSmRx5R9s8uKmZRpNc

https://live.juejin.cn/4354/yc_teacher

day6.2 从需求到上线全流程

https://bytedance.feishu.cn/file/boxcnBFwiH4ItgbiBBADJcFTKBc

https://live.juejin.cn/4354/yc_demand

为什么要有流程

分类 英文 中文 解释
研发模式 Waterfall Model 瀑布模型 瀑布模型(Waterfall Model)最早强调软件或系统开发应有完整之周期,且必须完整的经历周期之每一开发阶段,并系统化的考量分析与设计的技术、时间与资源之投入等。由于该模式强调系统开发过程需有完整的规划、分析、设计、测试及文件等管理与控制,因此能有效的确保系统质量,它已经成为软体业界大多数软件开发的最初标准
The Scaled Agile Framework(SAFe) 规模化敏捷框架
Scrum Scrum 在软件工程中,Scrum是以经验过程为依据,采用迭代、增量的方法来提高产品开发的可预见性并控制风险的理论,Scrum不是一种过程,也不是一项构建产品的技术,而是一个框架,在Scrum框架中可以应用各种过程和技术,Scrum的作用是让开发实践方法的相对功效显现出来以便随时改进。 Scrum是敏捷(Agile)开发的一种实践模式,敏捷开发强调拥抱需求变化,快速响应不断变化的需求,并尽可能快地提供可以工作的软件产品,敏捷最强调的是可以正常工作的软件产品,文档等不是非常的强调(并非不要文档,只是需要必要的文档),敏捷理论认为面对面的沟通交流远比文档更有效。 敏捷开发的Scrum模式是以价值驱动(Value-Driven)的开发模式,即认为用户的需求并不一定需要100%实现,最重要的是将对用户最有价值的功能实现并交付.
流程中的概念 Scrum Master 敏捷教练 Scrum Master是Scrum教练和团队带头人,确保团队合理的运作Scrum,并帮助团队扫除实施中的障碍
Product Owner 产品负责人 产品负责人,确定产品的方向和愿景,定义产品发布的内容、优先级及交付时间,为产品投资回报率负责;
Agile Release Train 敏捷发布火车 敏捷开发的一种发布模式
RD 研发工程师 RD一般指Research and Development Engineer,即研发工程师。
PM 产品经理 产品经理
PRD 产品需求文档 产品需求文档
RD 研发工程师 RD一般指Research and Development Engineer,即研发工程师。
UED 交互设计师 用户体验设计师,交互设计师,界面设计师
QA 测试工程师 指理解产品的功能要求,并对其进行测试,检查软件有没有缺陷(Bug),测试软件是否具有稳定性(Robustness)、安全性、易操作性等性能,写出相应的测试规范和测试用例的专门工作人员。
Backlog 待办事项 产品订单(product backlog)是整个专案的概要文档。产品订单包括所有所需特性的粗略的描述。产品订单是关于将要生产什么样的产品。产品订单是开放的,每个人都可以编辑。产品订单包括粗略的估算,通常以天为单位。估算将帮助产品负责人衡量时程表和优先级(例如,如果"增加拼写检查"特性的估计需要花3天或3个月,将影响产品负责人对该特性的渴望)。 冲刺订单(sprint backlog)是大大细化了的文档,包含团队如何实现下一个冲刺的需求的信息。任务被分解为以小时为单位,没有任务可以超过16个小时。如果一个任务超过16个小时,那么它就应该被进一步分解。冲刺订单上的任务不会被分派,而是由团队成员签名认领他们喜爱的任务。
Grooming Meeting Grooming会议 这个会议上面会由PO来描述下个迭代需要实现的功能,大家讨论要不要干
Planning Meeting Planning会议 这个会议讨论功能具体什么时候干,要估算任务的工作量
基础知识 CNCF 云原生计算基金会 云原生计算是软件开发中的一种方法,它利用云计算“在现代动态环境(例如公共云、私有云和混合云)中构建和运行可扩展的应用程序”。 通过声明性代码部署的容器、微服务、无服务器功能和不可变基础设施等技术是这种架构风格的常见元素。
Kubernetes K8S 生产级别的容器编排系统。Kubernetes 是一个可移植的、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态系统。Kubernetes 的服务、支持和工具广泛可用。
FAAS 函数即服务 函数即服务。仅通过编写函数(function)就能够发布为一个 API 或者服务,实现业务功能的技术体系。由于处理单元为函数粒度,往往底层也能够支持自动扩缩容地更精细化使用计算资源,开发侧支持事件驱动,可由消息或多种 Hook 触发,同时拥有快速上线、按需付费等优点。
APAAS 平台即服务 是一个为应用程序服务提供开发和部署环境的云服务
IDE IDE 用于提供程序开发环境的应用程序。一般包括代码编辑器、编译器、调试器和图形用户界面等工具
Git Git 分布式的版本管理系统
Merge/Rebase 合并/变基 处理代码分支的操作,将不同的分支整合成一个的两种方式

后端的定位

  • 瀑布模式
    • 按照时间节点参与会议,产出文档(系统分析,概要设计,详细设计,接口文档,提测文档等)
    • 按照时间节点交付测试
    • 按照时间节点发布
  • 敏捷团队
    • 跟随迭代制定规划,进行开发
    • 参与待办事项整理会议(Backlog Grooming Meeting)
      • PO描述下个迭代希望实现的用户故事
    • 迭代计划会议(Sprint Planning Meeting)
      • 选择迭代的任务和估算工作量
    • 每日站会(Standup Meeting)
      • 昨天你做了什么?
      • 今天你将要做什么?
      • 你有需要帮助的地方吗?
    • 评审会(Retrospective Meeting)
      • 小组向产品负责人展示迭代工作结果
    • 反思会(Retrospective Meeting)
      • 在每个迭代后召开简短的反思会,总结哪些事情做得好,哪些事情做得不好

团队协作

一个具体的迭代时间表:

2. 有哪些流程

需求阶段

  • 不要浪费时间讨论不应该存在的问题

  • 站在用户的角度思考

  • 给出后端系统视角的建议,估算任务优先级

开发阶段

  • 云原生下的开发:

    • 容器化技术
    • 微服务技术
    • WebIDE
  • 团队分支策略:

    • 为什么会有分支策略
    • 有哪些分支策略
    • 合并的方式
  • 代码规范

    • 养成良好的注释习惯,超过三个月的代码,自己都会忘了当时在想什么
    • 不要有魔法数字,魔法字符串
    • 重复的逻辑抽象成公共的方法,不要copy代码
    • 正确使用IDE的重构功能,防止修改错误
  • 自测

    • 单元测试
    • 功能环境测试
    • 测试数据构造
  • 文档

    • 大型改造需要有技术设计文档,方案评审
    • 好的接口文档能更方便的和前端进行沟通

测试阶段

  • 功能测试

功能测试,是为了测试一个新开发的功能,因此需要有能模拟线上的开发和测试环境,环境之间能相互隔离,这样可以独立验证不同的新功能

  • 集成测试:集成测试,是为了把几个功能合在一起测试,因为可能各个新功能独立测试没有问题,但是合在一起却产生了bug

  • 回归测试:回归测试是为了验证老的功能不被新的改动影响

发布阶段

  • 各种发布模式

    • 蛮力发布:简单粗暴,直接用新版本覆盖老版本。
    • 金丝雀发布:由于金丝雀对瓦斯极其敏感,因此以前矿工开矿下矿洞前,先会放一只金丝雀进去探是否有有毒气体,看金丝雀能否活下来,金丝雀发布由此得名。
    • 滚动发布:每个实例都通过金丝雀的方式逐步放大流量,对用户影响小,体验平滑
    • 蓝绿发布:常备两个集群,先把流量全部切换到Group 1,升级Group2,然后再把流量全部切换到Group 2,升级Group 1。最终恢复流量。
    • 红黑发布:与蓝绿发布类似,但是日常只有一个集群工作,发布时扩容一个集群升级新版本,切换流量后下掉老版本的集群。
  • 发布过程要做的事

    • 发布负责人
      • 负责按照计划执行发布
      • 需要通知各个相关人员发布进展
      • 观察各个服务的发布状态,及时处理异常
    • 变更服务的相关RD
      • 按照上线checklist检查服务的日志,监控,响应上线过程中的告警
      • 对于自己负责的改动,在小流量或者是预览环境进行功能验证
      • 执行发布计划中的其他操作(如线上配置,数据处理等)
    • 值班同学
      • 发布过程中的监控和告警需要特别关注,如果有异常需要立刻判断是否由变更引起
      • 如果有变更引起的告警或者用户反馈,需要及时中止发布

运维阶段

3. 怎样执行流程

DevOps

  • 效率竖井
    • 流程中实际产生价值的部分很短
    • 大量的时间用在等待和传递上
    • 人和人之间的沟通很慢

img

  • DevOps解决方案
    • 代码管理
    • 自动化测试
    • 持续集成
    • 持续交付

全流程自动化

  • 通过效能平台串联各个阶段

    • 需求发起研发流程的自动化
    • 写代码,测试环境部署的自动化
    • 自动化测试触发和报告分析
    • 发布过程可观测融入流程
  • 减少无价值的等待

    • 分析整个流程的耗时,计算真正产生价值的时间
    • 不断优化流程,让有价值的流程时间占比上升

参考文献

  1. 瀑布模型 zh.wikipedia.org/wiki/%E7%80…

  2. Scrum: zh.wikipedia.org/wiki/Scrum

  3. SAFe:

    a. www.woshipm.com/pd/4331832.… b. en.wikipedia.org/wiki/Scaled…

  4. CNCF:en.wikipedia.org/wiki/Cloud_…

  5. 常用的发布模式:www.cnblogs.com/Leo_wl/p/14…

课后作业:

  1. 敏捷宣言是什么?

  2. Grooming meeting 的作用是什么?

  3. 我们为什么需要进行集成测试?

  4. 遇到线上问题,我们要做的四个关键动作是什么?

  5. DevOps 包含了哪些流程?

    < 作业提交截止时间:5月17日 10:00前 >

正确答案:

  1. 敏捷宣言参考:agilemanifesto.org/iso/zhchs/m…

  2. PO描述下个迭代希望实现的用户故事,PM提出需求列表

  3. 不同人开发的功能合并在一起测试,相互之间的影响可能产生缺陷;迭代发布的所有功能合并在一起测试,确保发布的所有功能之间的影响不产生缺陷

  4. 止损,周知,定位,修复

  5. DevOps概念上,涵盖了软件交付生命周期的全流程,包含需求->开发->构建->测试->发布->运维的研发环节

day7.1 计算机网络基本概念

https://live.juejin.cn/4354/yc_computer

https://bytedance.feishu.cn/file/boxcneJVcYNfEn3umrE5K806h0g

https://juejin.cn/post/7097126973163454494#heading-0

刷抖音网络是怎么交互的?

怎么让我的手机能访问抖音服务器

网络接入-互联网

网络接入-路由

交换机-同网段发包交互?

找MAC地址就行,SDN(Software Defined Network)软件定义网络

路由一定是对称的吗?

不一定,去的时候可以是一条路,回来的时候另一条路

路由是工作在哪一层协议?

ip层 传输层

路由改的是IP地址吗?

源IP地址和⽬标IP地址在传输过程中是不会变化的,只有源 MAC 地址和⽬标 MAC ⼀直在变化。

动态路由BGP/OSPF?

https://blog.csdn.net/weixin_43914604/article/details/105084158

https://blog.csdn.net/weixin_43914604/article/details/105313629

网络接入-ARP协议

逻辑同网段才能发送ARP?

https://blog.csdn.net/weixin_43914604/article/details/105138313

ARP请求广播,应答单播

ARP的本质是寻求下一跳的MAC,而不是请求目标地址

非同网段需要借助路由器的帮助

免费ARP?

免费告诉别人我的MAC地址

免费 ARP(Gratuitous ARP)包是一种特殊的 ARP 请求,它并非期待得到 IP 对应的 MAC 地址

免费 ARP 报文与普通 ARP 请求报文的区别在于报文中的目标 IP 地址。普通 ARP 报文中的目标 IP 地址是其他主机的 IP 地址;而免费 ARP 的请求报文中,目标 IP 地址是自己的 IP 地址。

https://zhuanlan.zhihu.com/p/371088648

ARP代理?

image-20220516110900752

网络接入-IP协议

唯一标识,互联网通用。抖音客户端一个、抖音服务端一个

Mac地址不能替代IP地址吗?

https://www.cnblogs.com/botoo/p/7793036.html

  • IP地址是网络层使用的地址,它是分层次等级的。
  • 硬件地址是数据链路层使用的地址(如MAC地址),它是平面式的。

随着网络中的设备逐渐增多,人们发现路由(也就是寻找数据包从发送方到接收方的路径)变得越来越困难了

MAC 地址就像自己的 ID 号,而 IP 地址就像带着邮政编码的住址,各有各的用途。所以我们需要两个地址,缺一不可。

IPv4不够用,一般怎么解决?

NAT、IPv6

网络接入-NAT

家里路由器是怎样上网的?

(Network Address Translation)网络地址转换

私有地址(10 172 192)–公用地址

该网络中的主机使用私用IP地址.当私有网络内部主机和外部Internet通信时,网关(gateway)路由器负责将私有IP地址转换为全球IP地址

多个内网客户端访问同一个目标地址+端口,源端口恰好一样,冲突了?

NAPT

(Network Address and Port Translation)

把端口也变了

网络打通了怎么下载视频

网络传输-数据包

网络传输-数据包发送

网络传输-先请求DNS

https://blog.csdn.net/weixin_43914604/article/details/105583806

网络传输-DNS的传输协议UDP

由于过于简单,用好很难

发包每次发多少?怎么避免切片?

MTU(数据帧最大传输单元)有限制,∴要分片

怎么知道没有丢包?

怎么权衡传输效率与质量?

网络传输-TCP三次握手

拔了网线,连接会断吗?

看具体情况。

假设TCP有探活,假设刚好在一次探活后拔了网线,不会立马断开

你真的了解TCP三次握手?

最大报文段长度(MSS)是TCP协议的一个选项,用于在TCP连接建立时,收发双方协商通信时每一个报文段所能承载的最大数据长度(不包括文段头)。为了避免IP切片

区分MSS与MTU

最大报文段长度(MSS)与最大传输单元(Maximum Transmission Unit, MTU)均是协议用来定义最大长度的。不同的是,MTU应用于OSI模型的第二层数据链接层,并无具体针对的协议。MTU限制了数据链接层上可以传输的数据包的大小,也因此限制了上层(网络层)的数据包大小。例如,如果已知某局域网的MTU为1500字节,则在网络层的因特网协议(Internet Protocol, IP)里,最大的数据包大小为1500字节(包含IP协议头)。MSS针对的是OSI模型里第四层传输层的TCP协议。因为MSS应用的协议在数据链接层的上层,MSS会受到MTU的限制

TCP的有限状态机

为什么老问你Timewait?

等待足够的时间以确保远程 TCP 收到其连接终止请求的确认。

https://blog.csdn.net/mystyle_/article/details/119176327

丢包怎么办?

https://blog.csdn.net/weixin_43914604/article/details/105524592

滑动窗口

流量控制/拥塞控制

https://blog.csdn.net/weixin_43914604/article/details/105531547

https://blog.csdn.net/weixin_43914604/article/details/105532044

网络传输-HTTP/HTTP1.1

为什么不直接用TCP通信?

TCP内容太多了,编程麻烦

其实HTTP只是多加了一层规矩,HTTP仍然是TCP,只是这个规矩让用户更清晰

https://www.cnblogs.com/heluan/p/8620312.html

https://zhuanlan.zhihu.com/p/266578819

网络传输-HTTPS

网络传输-SSL/TLS握手

https://www.jianshu.com/p/6811285c577d

网络架构怎么给抖音提质?

网络提速-HTTP2.0

网络提速-如何理解多路复用/stream

单个TCP链接传输

实际上stream还是串行的

如果TCP丢包怎么办?

TCP队头阻塞,TCP有个option,可以指定ack的序列号,指定需要重传的

网络提速-QUIC/HTTP3.0

TCP or UDP?

改TCP动一发而牵全身

Kernel or Userspace?

0RTT

弱网优势

网络提速-数据中心分布

核心机房

POP接入

边缘机房

汇聚机房

网络提速-同运营商访问

网络提速-静态资源路径优化(CDN)

Content Delivery Network,即内容分发网络

网络提速-动态API路径优化(DSA)

网络稳定-容灾概念

故障发生

故障感知

自动切换

服务恢复

专线

外网容灾

云控?

web页面不能嵌入SDK、用户权限、

网络稳定-故障排查

故障明确

出现了什么故障?->沟通是前提

什么业务?什么接口障碍?

故障体现在哪里?

访问其他目标是否正常?

是否是修改导致的异常?

故障止损

先止损再排查

分段排查

image-20220516144052484

网络稳定-网络故障排查常用命令

image-20220516144146994

课后作业1- UDP socket 实现 ack,感知丢包重传

作业要求:

  1. 学会 UDP socket 编程

  2. 先从简单的 ack 学习,客户端等待 ack 再发包

  3. 什么时候客户端认为是丢包?

  4. 重传怎么考虑效率?

  5. 能不能不阻塞只穿丢掉的中间的段?

课后作业2- 三台同网段内的服务器,模拟实现一个路由器

方法一: Linux 操作系统配置法

提示:

  1. 了解Linux的路由配置方式

  2. 确保是同网段直连可达的环境。在三台机器上另外配置IP网段和路由

  3. 一台机器做客户端,一台机器做路由器,一台机器做服务端

  4. 客户端配置到达服务器的下一跳指向路由器,路由器上配置到达服务端的路由

方法二: 用户态 socket 编程实现简易 route 软件

提示:

  1. 收到指定的包后,做转发

  2. 注意是修改报文的 MAC ,不是修改 IP

  3. 实现一个对称路由。这样可以实现 TCP 交互

  4. 可以通过 ping 来验证

  5. 可以支持 traceroute 吗?

    < 作业提交截止时间:5月17日 10:00前 >

正确答案:

课后作业1:

开放性问题,答案只要合理即可。具体实现可以参考

https://github.com/networkprotocol/reliable

根据作业要求的顺序一点点去实现,由简单ack功能入手到复杂的流控。

课后作业2:

开放性问题,答案只要合理即可。具体实现可以参考:

  1. 静态路由表的注入可以在代码内用全局变量表示,也可以写一个配置文件去注入。

  2. socket编程收raw包

  3. 解析raw包的IP层数据,根据静态路由表找到目标IP的下一跳

  4. 获取下一跳的MAC地址(linux有命令/系统调用接口,也可以自己实现简单的arp请求,保存目标MAC地址)

  5. 修改目标MAC地址为下一跳的MAC,修改源MAC地址为本机的出口网卡MAC。

  6. ttl记得-1,然后转发数据包。

day7.2 如何将我的服务开放给用户

https://live.juejin.cn/4354/yc_open-serve

https://bytedance.feishu.cn/file/boxcnUDmxxNLZjEd8oC17wFRRTe

企业接入升级打怪之路

域名系统

host管理的问题

流量和负载

名称冲突

时效性

域名购买和配置迁移

image-20220515203844074

自建DNS服务器

权威DNS系统

https://www.bilibili.com/video/BV1Mf4y1C7V8?spm_id_from=333.337.search-card.all.click

常见的开源DNS:bind、nsd、knot、coredns

HTTPS协议

HTTP明文传输,弊端越来愈明显

对称加密和非对称加密

SSL的通信过程

证书链

接入全站加速

源站容量低,可承载的并发请求数低

报文经过的网络设备越多,出问题的概率越大

自主选路网络链路长

静态加速CDN

动态加速DCDN

基于智能选路技术,从众多回源路线中择优选择一条路线进行传输

四层负载均衡

https://blog.csdn.net/hanjinjuan/article/details/120274878

七层负载均衡

https://www.cnblogs.com/cheyunhua/p/10670070.html

https://blog.csdn.net/lamp_yang_3533/article/details/80383039

day8.1 架构初探

https://live.juejin.cn/4354/yc_cake

https://bytedance.feishu.cn/file/boxcne8xf0JBAiXgPJ08Vf0IdJg?hash=d3d3a460879da375f28cee06d470b5aa

什么是架构

有关软件整体结构与组件的抽象描述

用于指导软件系统各个方面的设计

单机

All in one,所有的东西都在一个进程里,部署在一个机器上。

优点:

  • 简单

缺点:

  • 运维需要停服,用户体验较差

  • 承载能力有限。了解下 c10k 问题

单体、垂直切分

在单机架构的基础上,将进程部署到多个机器上。

优点:

  • 具备水平扩容能力

  • 运维不需要停服

缺点:

  • 后端进程职责太多,越来越臃肿

  • 爆炸半径较大,进程中一个很小的模块出现问题,都可能导致整个进程崩溃

SOA

SOA 架构中,服务为一等公民,将进程按照不同的功能单元进行抽象,拆分为『服务』。有了服务之后,SOA 还为服务之间的通信定义了标准,保证各个服务之间通讯体验的一致性。

优点:

  • 各服务的职责更清晰

  • 运维粒度减小到服务,爆炸半径可控

缺点:

  • ESB (企业服务总线) 往往需要一整套解决方案

微服务

在 SOA 架构中,ESB 起到了至关重要的作用。但从架构拓扑来看,它更像是一个集中式的模块。有一个 SOA 分布式演进的分支,最终的形态便是微服务。

优点:

  • 兼具 SOA 解决的问题

  • 服务间的通信更敏捷、灵活

缺点:

  • 运维成本
  • 服务间通信成本问题;
  • 数据一致性问题

小结

  • 架构演进的初衷:满足软件迭代诉求,提高迭代效率

  • 架构演进的思路:垂直切分——分布式,水平切分——分层/模块化

企业级后端架构剖析

https://www.zhihu.com/question/20387284

云计算基础:

  • 虚拟化技术

    • 硬件层面(VM 虚拟机)- KVM/Xen/VMware
    • 操作系统层面(Container 容器)- LCX/Docker/Kata Container
    • 网络层面 - Linux Bridge/Open v Switch
  • 编排方案

    • VM - OpenStack/VMWare Workstation
    • Container - Kubernetes/Docker Swarm

云计算架构:

  • 云服务

    • IaaS - 云基础设施,对底层硬件资源池的抽象
    • PaaS - 基于资源池抽象,对上层提供的弹性资源平台
    • SaaS - 基于弹性资源平台构建的云服务
    • FaaS - 更轻量级的函数服务。好比 LeetCode 等 OJ,刷题时只需要实现函数,不需要关注输入输出流
  • 云部署模式(拓展)

    • 私有云 - 企业自用
    • 公有云 - AWS/Azure/Google Cloud/Huawei
    • 混合云

云原生

云原生,实际是云原生(计算)的简称,它是元计算发展到现在的一种形态。

云原生技术为组织(公司)在公有云、自由云、混合云等新型的动态环境中,构建和运行可弹性拓展的应用提供了可能。 它的代表技术:

  • 弹性资源

  • 微服务架构

  • DevOps

  • 服务网格

弹性资源

基于虚拟化技术,提供的可以快速扩缩容的能力。可以分为弹性计算资源和弹性存储资源两个方面。 弹性计算资源:

  • 计算资源调度

    • 在线计算 - 互联网后端服务
    • 离线计算 - 大数据分析。Map-Reduce/Spark/Flinnk
  • 消息队列

    • 在线队列 - 削峰、解耦
    • 离线队列 - 结合数据分析的一整套方案,如 ELK

弹性存储资源:

  • 经典存储

    • 对象存储 - 视频、图片等。结合 CDN 等技术,可以为应用提供丰富的多媒体能力
    • 大数据存储 - 应用日志、用户数据等。结合数据挖掘、机器学习等技术,提高应用的体验
  • 关系型数据库

  • 元数据

    • 服务发现
  • NoSQL

    • KV 存储 - Redis
    • 文档存储 - Mongo

在云原生的大背景下,不论是计算资源还是存储资源,他们都像是服务一样供用户使用。

微服务架构

微服务架构下,服务之间的通讯标准是基于协议而不是 ESB 的。

  • HTTP - H1/H2

  • RPC - Apache Thrift/gRPC

如何在 HTTP 和 RPC 之间选择?

  • 性能 - RPC 协议往往具备较好的压缩率,性能较高。如 Thrift, Protocol Buffers

  • 服务治理 - RPC 中间件往往集成了丰富的服务治理能力。如 熔断、降级、超时等

  • 可解释性 - HTTP 通信的协议往往首选 JSON,可解释性、可调试性更好

服务网格

什么是服务网格?

  • 微服务之间通讯的中间层

  • 一个高性能的 4 层网络代理

  • 将流量层面的逻辑与业务进程解耦

没有什么是加一层代理解决不了的问题,服务网格相比较于 RPC/HTTP 框架:

  • 实现了异构系统治理体验的统一化

  • 服务网格的数据平面代理与业务进程采取进程间通信的模式,使得流量相关的逻辑(包含治理)与业务进程解耦,生命周期也更容易管理

企业级后端架构的挑战

挑战

基础设施层面: Q:我们总说,云是弹性的,也就是说,在用户的角度,云提供的资源是无限的。然而,云背后的物理资源是有限的。在企业级后端架构里,云如何解决近乎无限的弹性资源和有限的物理资源之间的矛盾?

Q:闲事的资源就这么空着呢?如何提高资源利用率,提高物理资源的价值转换率?

用户层面: Q:上了云原生微服务后,服务之间的通信开销较大,应该如何做成本优化?

Q:微服务看起来没有那么美好,抖动导致的运维成本较高,如何解决?

Q:异构的物理环境应该对用户是透明的,如何屏蔽这些细节?

离在线资源并池

考虑到在线业务的潮汐性,物理资源的用量不是一成不变的。离在线资源并池,可以:

  • 提高物理资源利用率

  • 提供更多的弹性资源

后端架构实践

day8.2 git

https://bytedance.feishu.cn/file/boxcnsl8BgASj1TA9lzYb0TevEg?hash=8b34156117fb8fbc4eb72588c99e1adc

https://live.juejin.cn/4354/yc_Git-posture

day9 数据结构与算法

https://live.juejin.cn/4354/yc_data-correlation/preload

https://bytedance.feishu.cn/file/boxcnZoRBfHvkwiwXC5qKft4L7b

课后作业-实现 pdqsort-Version 1

作业要求:

完成 PPT 中 pdqsort v1 版本,可以正确对元素排序

< 作业提交截止时间:5月21日 10:00前 >

正确答案:

https://github.com/zhangyunhao116/sortlearning/blob/master/pdqsort.go

day10.1 RPC框架

https://live.juejin.cn/4354/yc_RPC-framework/preload

https://bytedance.feishu.cn/file/boxcnQ289aixfQUmvgtDDn98mmf

RPC 相关的基本概念

RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC它假定某些协议的存在,例如TPC/UDP等,为通信程序之间携带信息数据。在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易。

过程是什么? 过程就是业务处理、计算任务,更直白的说,就是程序,就是想调用本地方法一样调用远程的过程

==1:通讯协议== 比如:你需要找人在国外干活,那么你可以直接飞过去或者打电话或者通过互联网的形式,去找人,这个找人的过程就是通讯协议 ==2:寻址== 既然要找人干活,肯定要知道地址在哪,飞过去需要找到详细地址,打电话需要知道电话号码,互联网需要知道IP是多少 ==3:数据序列化== 就是说,语言需要互通,才能够让别人干活,之间需要一个大家都懂的语言去交流

  • RPC的概念模型:User、User-Stub、RPC-Runtime、Server-Stub、Server

  • IDL(Interface Definition Language) 文件

    • Thrift
    • Protobuf
  • 生成代码

  • 编解码(序列化/反序列化)

  • 通信协议

    • 应用层协议
  • 网络通信

    • IO 网络模型
      • blocking IO
      • unblocking IO
      • IO multiplexing
      • signal driven IO
      • asynchronous IO
    • 传输层协议
      • TCP
      • UDP

RPC 框架的分层设计

  • 编解码层

    • 数据格式:
      • 语言特定格式
      • 文本格式
      • 二进制编码
        • TLV 编码:Thrift 使用 TLV 编码
        • Varint 编码:Protobuf 使用 Varint 编码
    • 选项:
      • 兼容性
      • 通用型
      • 性能
  • 传输协议层

    • 消息切分
      • 特殊结束符
      • 变长协议:length+body
    • 协议构造
      • 以 Thrift 的 THeader 协议为例讲解
  • 网络通信层

    • 网络库
    • 核心指标
      • 吞吐高
      • 延迟低

衡量 RPC 框架的一些核心指标

  • 稳定性

    • 保障策略
      • 熔断
      • 限流
      • 超时
    • 请求成功率
      • 负载均衡
      • 重试
    • 长尾请求
      • BackupRequest
  • 易用性

    • 开箱即用
    • 周边工具
  • 扩展性

  • 观测性

    • Log
    • Metric
    • Tracing
    • 内置观测性服务
  • 高性能

字节内部 RPC 框架 Kitex 实践分享

课后作业- 重点内容 Review

  1. 行业内各个流行的 RPC 框架的优劣对比?

  2. 从第三章节 RPC 的核心指标来看,Kitex 还有哪些功能是欠缺或者需要加强的?

  3. 了解微服务的新趋势 ServiceMesh,以及 RPC 框架和 ServiceMesh 的关系

  4. 关于 RPC 框架,业界有哪些新的趋势和概念?

  5. Netpoll 的优势在哪?相比其他高性能网络库例如 Netty 还有什么不足?

  6. Flatbuffer 和 Cap’n Proto 等编解码协议为什么高性能?

    < 作业提交截止时间:5月24日 10:00前 >

day10.2 HTTP修炼知道

https://live.juejin.cn/4354/yc_practise/preload

https://bytedance.feishu.cn/file/boxcnfWxLoNVpn36D041DdKP6Vg

再谈HTTP协议

HTTP框架的设计与实现

性能修炼之道

企业实践

课后作业-重点内容 Review

作业要求:

  1. 为什么 HTTP 框架做要分层设计?分层设计有哪些优势与劣势。

  2. 现有开源社区 HTTP 框架有哪些优势与不足。

  3. 中间件还有没有其他实现方式?可以用伪代码说明。

  4. 完成基于前缀路由树的注册与查找功能?可以用伪代码说明。

  5. 路由还有没有其他的实现方式?

    < 作业提交截止时间:5月24日 10:00前 >

正确答案:

该问题为开放性问题,答案合理即可。

  1. 优势:

    1. 分层设计可以提高框架的扩展性、可维护性、复用性
    2. 分层设计可以让相关同学聚焦在核心层上而不用关心其他层的实现
    3. 劣势:
    4. 提高代码设计的复杂性,设计不好可能会导致循环依赖
    5. 由于使用接口进行解耦,可能会对代码性能造成影响
  2. gin:易用性强;生态丰富,但扩展性一般,性能一般。

    1. fasthttp:性能强;扩展性一般,生态一般。
    2. go-zero:开箱即用,提供全套微服务能力,但扩展性一般,性能一般。
  3. 课程中讲的中间件模型需要有一个地方保存 index,对于没有 index 的场景,可以将中间件构造为递归函数进行调用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Endpoint represent one method for calling from remote.
type Endpoint func ( ctx context.Context, req, resp interface {})  ( err error )

// Middleware deal with input Endpoint and output Endpoint.
type Middleware func ( Endpoint ) Endpoint

// Chain connect middlewares into one middleware.
func chain ( mws ...Middleware ) Middleware {
return func ( next Endpoint ) Endpoint {
for i := len ( mws ) - 1; i >= 0; i-- {
next = mws [ i ]( next )
}
return next
   }
} 
复制代码
  1. 伪代码如下,可以使用递归实现,也可以使用其他更高性能的实现,这里提供递归的实现方式。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
func ( r *router ) addRoute ( path string, h app.HandlersChain ) {
// 检查 path 合理性
   checkPathValid ( path )
// 循环添加 path 中的每一段
   for i range len ( path ) {
// 处理 : 类型参数路由
      if path [ i ] == ":" {
// 添加 : 之前的静态路由
         Insert ( path [ :i ] , staticKind, nil )
// 处理参数名称
         paramNames.add ( paramName )
// 判断当前是否是路由 path 的最后一段
         if last {
// 插入最后一段
            Insert ( path [ i: ] , paramKind, handler )
return
         } else {
// 不是最后一段,插入该段后继续插入之后的部分。
            Insert ( path [ i: ] , paramKind, nil )
}
// 处理 * 号类型参数路由
      } else if path [ i ] == "*" {
// 添加 * 路由之前的部分
         Insert ( path [ :i ] , staticKind, nil )
// 处理参数名称
         paramNames.add (paramName)
// 添加最后一段
         Insert ( path [ i: ] , allKind, handler )
return
      }
}
// 添加最后的静态路由
   Insert ( path [ i: ] , staticKind, handler )
}

func find ( path string )  () {
   // 匹配静态路由
   if matchStaticKind {
nextSearchPath = handleStaticKind ( path, node )
path = nextSearchPath
   }
// 匹配 param 路由
   if matchParamKind {
nextSearchPath = handleParamKind ( path, node )
path = nextSearchPath
   }
// 匹配 * 路由
   if matchAllKind {
nextSearchPath = handleAllKind ( path, node )
path = nextSearchPath
   }
// 判断是否找到
   if endcondition {
return
   }

// 递归搜索下一段静态路由
   for node := range allStaticKindNodeChildren {
if prefixMatch {
node.find ( path )
}
}
// 搜索下一段 param 路由
   if node.HasParamKindChild {
node.find ( path)
   }
// 搜索下一段 * 路由
   if node.HasAllKindChild {
node.find ( path)
   }
return
} 
复制代码
  1. 可以尝试使用正则匹配的方式注册路由

day11.1 微服务框架

https://bytedance.feishu.cn/file/boxcnPjF5oJxpZh4ZQYDwVCSxib

微服务架构介绍

微服务架构概览

微服务架构的核心要素

服务治理

可观测性

安全

微服务架构原理及特征

基本概念

服务

实例

集群

实例承载形式

服务注册及发现

无损的服务实例上下线流程

微服务架构中的基本流量特征

核心服务治理功能

服务发布

流量治理

负载均衡

稳定性治理

字节跳动服务治理实践

重试

课后作业- 重点内容 Review

  1. 结合 CAP 等原理,思考微服务架构有哪些缺陷?

  2. 微服务是否拆分得越“微”越好?为什么?

  3. Service Mesh 这一架构是为了解决微服务架构的什么问题?

    https://philcalcado.com/2017/08/03/pattern_service_mesh.html

    https://zhuanlan.zhihu.com/p/61901608

  4. 有没有可能有这样一种架构,从开发上线运维体验上是微服务,但实际运行又类似单体服务?

day11.2消息队列

https://bytedance.feishu.cn/file/boxcnKXjTxtmdCpdidtJZGckf5b

https://live.juejin.cn/4354/yc_message-queue/preload

解耦:系统崩溃

削峰:服务处理能力有限

异步:链路耗时长尾

日志处理:日志如何处理

前世今生

消息队列:支持高吞吐、高并发、高可用的队列

消息队列Kafka

kafka使用场景,业务日志、用户行为数据、Metrics数据

基本概念,Producer、Cluster、Consumer、Topic、Partition

数据迁移、Offset、Partition选主

一条消息从生产到消费是如何处理的,Producer端逻辑、Broker端逻辑、Consumer端逻辑

Producer端逻辑

批量发送、数据压缩

Broker端逻辑

顺序写、消息索引、零拷贝

Consumer端逻辑

Rebalance

问题:

运维成本高

对于负载不均衡的场景,解决方案复杂

没有自己的缓存,完全依赖于Page Cache

消息队列BMQ

课后作业:

  1. 消息队列的应用场景有哪些?

  2. Kafka 的哪些 Feature 让其可以支撑大吞吐写入的场景?

  3. Kafka Consumer Rebalance 的流程简述?

  4. BMQ 相比较 Kafka 有哪些优势?

  5. RocketMQ 有哪些特有的 Feature?

  6. RocketMQ 事务消息处理流程简述?

  7. 你认为 MQ 后面应该如何发展?(开放题)

day12.1 分布式定时器

https://bytedance.feishu.cn/file/boxcnxSIthZayPmlXZWdYx845qg

前言

  • 每年春节抖音都会有很多有意思的玩法,如果同学们是字节的后端同学,怎么设计今年春节集卡瓜分20亿的技术方案?

  • 业务流程

    • 定时扫描抖音用户集卡状态
    • 汇总计算用户的瓜分金额
    • 定时开奖
  • 技术体量

    • 亿级用户规模
    • 十亿级资金规模
    • 百万级读写QPS
  • 方案引出

    • 自动化 + 定时执行 + 海量数据 + 高效稳定 = 分布式定时任务

发展历程

Windows批处理

windows任务计划程序

Linux CronJob

单机定时任务Timer Ticker

单机定时任务 ScheduledExecutorService

任务调度Quartz

分布式定时任务

定义
  • 定时任务是指系统为了自动完成特定任务,实时、延时、周期性完成任务调度的过程。
  • 分布式定时任务是把分散的、可靠性差的定时任务纳入统一的平台,并实现集群管理调度和分布式部署的一种定时任务的管理方式。

按触发时机分类: 定时任务:特定时间触发,比如今天15:06执行 延时任务:延时触发,比如10s后执行 周期任务:固定周期时间,或固定频率周期调度触发,比如每隔5s或者每天12点执行

特点

自动化:全自动完成定时任务的调度和执行 平台化:基于平台化的思维管控一系列的分布式定时任务 分布式:在分布式系统环境下运行任务调度,突破单机定时任务的性能瓶颈 伸缩性:采用集群方式部署,可以随时按需扩缩容 高可用:单点故障不影响最终任务结果,可以做到故障转移

执行模式

单机任务:随机触发一台机器执行任务,适用于计算量小、并发度低的任务 广播任务:广播到所有机器上执行同一个任务,比如所有机器一起清理日志 Map任务:一个任务可以分出多个子任务,每个子任务负责一部分的计算。适用于计算量大,单机无法满足要求的任务 MapReduce任务:在Map任务的基础上,还可以对所有子任务的结果做汇总计算,适用于计算量大,并且需要对子任务结果做汇总的任务

分布式定时任务VS单机定时任务

关系: 都可以实现自动化的定时、延时、周期任务调度 差异: 分布式定时任务可支撑更大的业务体量 分布式定时任务的性能、伸缩性、稳定性更高

分布式定时任务VS大数据处理引擎

关系: 都可以对海量数据做处理 性能、伸缩性、稳定性都很高 差异: 定时并不是大数据处理引擎要解决的核心问题 大数据处理引擎往往致力于将源数据处理成结果数据,分布式定时任务除了能做这个之外,还可以调用HTTP和RPC服务

业内流行框架:Xxl-job、SchedulerX、TCT

实现原理

分布式定时任务核心要解决触发、调度、执行三个关键问题

触发器:Trigger,解析任务,生成触发事件 调度器:Scheduler,分配任务,管理任务生命周期 执行器:Executor,获取执行任务单元,执行任务逻辑

除此之外,还需要提供一个控制台(Admin),提供任务管理和干预的功能。

基本概念

任务:Job,任务元数据 任务实例:JobInstance,周期任务会生成多个任务实例 任务结果:JobResult,任务实例运行的结果 任务历史:JobHistory,用户可以修改任务信息,任务实例对应的任务元数据可以不同,因而使用任务历史存储

触发器

核心职责 给定一系列任务,解析它们的触发规则,在规定的时间点触发任务的调度

设计约束 需支持大量任务 需支持秒级的调度 周期任务需要多次执行 需保证秒级扫描的高性能,并避免资源浪费

方案一

定期扫描+延时消息(腾讯、字节方案)

方案二

时间轮( Quartz 所用方案) 时间轮是一种高效利用线程资源进行批量化调度的一种调度模型。时间轮是一个存储环形队列,底层采用数组实现,数组中的每个元素可以存放一个定时任务列表。

多级时间轮

触发器 高可用

课后作业- 重点内容 Review

  1. 分布式定时任务可以帮助我们处理哪些业务场景?

  2. 春节集卡瓜分20亿的玩法,发奖金额计算、实时开奖两个阶段分别用到分布式定时任务什么执行方式?

  3. 有了分布式定时任务,单机定时任务还有适用场景么?

  4. 时间轮这种数据结构,在定时/延时场景相比其他数据结构有哪些优势?

  5. 分布式定时任务的调度中心怎么判断一台执行器的机器处于可被调度状态?

  6. 你能想到哪些业务场景,实时计算引擎优于分布式定时任务?

    < 作业提交截止时间:5月26日 10:00前 >

正确答案:

  1. 所有需要定时、延时、周期性执行任务的业务场景,都可以考虑使用分布式定时任务。在电商、游戏、互动等多个业务领域中都有广泛应用。

  2. MapReduce任务和Map任务。由于发奖金额计算时需要汇总计算所有用户的集卡状态,因而在Map之后还需要对子任务的结果做汇总计算;而实时开奖则只需要对全量集齐的用户发奖即可。

  3. 有,比如定时刷新每台机器中的缓存、定期清理机器日志等。

  4. 相比于链表、最小堆,时间轮的查询和修改的时间复杂度都是O(1)。

  5. 执行器的机器首先需要调用调度中心的注册服务将本机器注册上去,并且需要定期状态上报供调度中心监控机器状态。

  6. 需要实时统计大规模流式数据,只需要做数据处理,无需调用RPC/HTTP接口做其他额外处理的业务场景,比较典型的例子是电商双十一的大屏,实时统计当前的GMV、订单量等

day12.2 存储与数据库

https://bytedance.feishu.cn/file/boxcn27GCEstXUOpBYFEpAY3EIh

课后作业-实现一个(分布式)key-value 存储系统

作业要求:

  1. 基于本地文件系统实现,支持常用的 put(k, v)、get(k, v)、scan_by_prefix(prefix) 接口

  2. 支持存储 server 独立进程部署,支持跨进程或者网络访问

  3. IO 操作做到低时延

*可选: 支持扩展成分布式架构,多台存储server组成一个分布式key-value存储系统,并保证全局的数据一致性。

< 作业提交截止时间:5月26日 10:00前 >

day13 深入理解RDBMS

https://bytedance.feishu.cn/file/boxcnXzUnOJI7nUFhvBMTW9sfBh?hash=a90b5ab6be81c4d6ca20bf103bd9ab65

课后作业- 重点内容 Review

  1. WAL 日志到底是如何保证数据的持久化,宕机后数据不丢失的?

  2. 相比于其他方案,WAL 日志都有什么优势?

  3. 除了 Undo Log 之外,是否还有其他方案可以实现 MVCC?

  4. 基于代价的优化器一般需要考虑哪些代价?

  5. 执行器的执行模型,除了本课中提到的火山模型是否还有其他模型?

  6. B+ Tree的优点有哪些?

  7. InnoDB 的 buffer pool 是怎么实现页面管理和淘汰的?

    < 作业提交截止时间:5月28日 10:00前 >

正确答案:

  1. WAL是预写日志,需要保证日志落盘才能够进行事务提交。日志中包含了事务对数据的修改,宕机后通过解析WAL日志,就可以对数据进行恢复。

  2. 相比于每次事务提交时都对数据页面进行flush操作,WAL日志有以下优点:

    1. 解决了IO放大的问题。
    2. 随机读写变为顺序读写,对磁盘更友好。
  3. 除了Undo Log之外,还有shadow paging的方式。例如PostgreSQL就是采用这种方式。这种方式的特点是,在数据页面中同时保留了一行数据的多个历史版本,通过trx_id来区分。

  4. 基于代价的优化器一般需要考虑IO消耗、网络消耗、CPU消耗等。

  5. 常见的执行模型有三类:火山模型(Volcano Model/Pipeline Model/Iterator Model/Pull Model)、物化模型(Materialization Model/Push Model)、向量化模型(Vectorized / Batch Model)。

  6. B+树的优点如下:

    1. B+树内部节点不存储数据,只存储键值,这样,每个节点就能存储更多的键值,一次性也就能将更多的键值读入内存,减少了对磁盘的IO操作次数;
    2. B+树的叶节点有一条链相连,所以对于区间内查询数据比较高效。
  7. InnoDB的buffer pool通过一个hash_map<page_id, page_ptr>来实现页面的快速查找访问,通过一个LRU来实现页面的冷热淘汰管理。

day14.1 TOS对象存储

https://live.juejin.cn/4354/yc_TOS

https://bytedance.feishu.cn/file/boxcn3SOO7DkrVWzb93qx08jtch

课后作业-实现一个对象存储客户端

作业要求:

  1. 在任意一个公有云中申请一个对象存储 Bucket

  2. 使用你熟悉的语言,实现一个对象存储命令行客户端,要求该客户端能够

    1. 创建对象:超过 1GB 的对象使用 MultiUpload 上传,小于 1GB 的使用 Put 上传
    2. 下载对象
    3. 删除对象
    4. 查看对象是否存在
    5. 列举对象及 CommonPrefix

    < 作业提交截止时间:5月29日 10:00前 >