了解有关后端Web开发的一切:Golang,Postgres,Redis,GRPC,Docker,Kubernetes,AWS,CI/CD
创建一个简单银行项目,包含以下功能。
- 创建并管理帐号
- 记录余额变更
- 金额交易事务
数据库设计:
- 使用 dbdiagram.io 设计 SQL 数据库架构。
- 将架构另存为 PDF 或 PNG 图标。
- 生成SQL脚本。
拉取 postgres
镜像
docker pull postgres:12-alpine
创建容器
docker run --name postgres12 -p 5432:5432 -e POSTGRES_USER=root -e POSTGRES_PASSWORD=root -d postgres:12-alpine
进入容器
docker exec -it postgres12 psql -U root
退出容器
\q
安装 go
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.3.linux-amd64.tar.gz
修改 $HOME/.profile 文件追加如下命令
export PATH=$PATH:/usr/local/go
export GO111MODULE=on
export GOPROXY=https://goproxy.cn
安装 golang-migrate
mkdir /usr/local/golang-migrate
tar -C /usr/local/golang-migrate -xzf migrate.linux-arm64.tar.gz
修改 $HOME/.profile 文件追加如下命令
export PATH=$PATH:/usr/local/golang-migrate
查看 golang-migrate
版本
migrate version
生成迁移文件
migrate create -ext sql -dir db/migration -seq init_schema
migrate up
- 执行脚本更新数据库。
migrate down
- 从数据库中撤回更改。
查看正在运行中的容器
docker ps
停止正在运行的容器
docker stop postgres12
查看所有容器
docker ps -a
进入容器
docker exec -it postgres12 /bin/sh
创建数据库
createdb --username=root --owner=root simple_bank
psql simple_bank
\q
迁移数据库
migrate -path db/migration -database "postgresql://root:root@localhost:5432/simple_bank?sslmode=disable" -verbose up
安装 make
sudo apt-get install make
查看 make
版本
make --version
执行 Makefile
文件
# 向上迁移
make migrateup
# 向下迁移
make migratedown
- Create - 在数据库中插入新记录
- Read - 在数据库中选择或搜索记录
- Update - 更改数据库中记录的某些字段
- Delete - 从数据库中删除记录
- 优点是 速度快并且代码简单(Straightforward)
- 缺点是 必须手动将SQL字段映射到变量上,容易出错。
- 已经封装好CRUD的代码,只需声明模型。
- 必须学习如何使用gorm提供的函数编写查询。
- 当流量很高(high load)时运行很慢。
- 查询几乎和标准库一样快以及容易使用
- 字段映射是通过查询文本或结构标签完成的
- 速度快且容易使用
- 自动生成代码
- 缺点是只完全支持 PostgreSQL, MySQL处于实验阶段
安装 sqlc
sudo snap install sqlc
初始化 sqlc
sqlc init
生成代码
sqlc generate
初始化项目
go mod init github.com/grayjunzi/backend-master-class-golang
go mod tidy
安装依赖包
go get github.com/lib/pq
go get github.com/stretchr/testify
- 单一的工作单元。
- 通常由多个数据库操作组成。
- 提供可靠和一致的工作单元,即使在系统出现故障的情况下。
- 在并发访问数据库的程序之间提供隔离
数据库事务必须满足ACID属性
- Atomicity(原子性) - 要么所有操作都成功完成,要么事务失败,数据库保持不变。
- Consitency(一致性) - 数据库状态在事务执行之后必须有效。必须满足所有约束。
- Isolation(隔离性) - 并发事务不能相互影响。
- Durability(持久性) - 成功事务写入的数据必须记录在持久性存储中。
脏读(Dirty Read)
- 事务读取其他并发未提交事务写入的数据。不可重复读(Non Repeatable Read)
- 一个事务两次读取同一行并看到不同的值,因为它已被其他已提交的事务修改幻读(Phantom Read)
- 事务重新执行用于查找满足条件的行的查询,并且由于其他已提交事务的更改而看到一组不同的行。序列化异常(Serialization Anomaly)
- 一组并发提交的事务的结果是不可能实现的,如果我们试图以任何顺序依次运行它们而不重叠
American National Standards Institute - ANSI
- 读取未提交 - 可以看到未提交事务写入的数据。
- 读取已提交 - 只看到已提交事务写入的数据。
- 可重复读取 - 相同的读取查询总是返回相同的结果。
- 可序列化的 - 如果按某种顺序而不是并发地执行事务则可以实现相同的结果。
查看事务隔离级别
select @@transaction_isolation;
查看全局事务隔离界别
select @@global.transaction_isolation;
修改事务隔离级别
set session transaction isolation level read uncommitted;
开启事务
start transaction;
提交事务
commit;
回滚事务
rollback;
查看事务隔离级别
show transaction isolation level;
修改事务隔离级别
set transaction isolation level read uncommitted;
处理可能存在错误、超时或死锁。
- 是一个自动过程。
- 由一个以上的Job组成。
- 由事件触发、计划或手动。
- 将
.yml
添加到存储库。
name: build-and-test
on:
push:
branches: [ master ]
schedule:
- cron: '*/15 * * * *'
jobs:
build:
runs-on: ubuntu-latest
- 是运行Job的服务
- 一次运行一个Job。
- Github托管或自托管。
- 向Github报告进度、日志和结果。
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Build server
run: ./build_server.sh
test:
needs: build
runs-on: ubuntu-latest
steps:
- run: ./test_server.sh
- 是在同一个Runner上执行的一组步骤。
- 正常作业并行运行。
- 连续运行相关作业。
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Build server
run: ./build_server.sh
test:
needs: build
runs-on: ubuntu-latest
steps:
- run: ./test_server.sh
- 是一个单独的任务。
- 在作业中连续运行。
- 包含多个 Actions
- 是一个独立的命令。
- 在一个步骤内连续运行。
- 可重复使用。
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Build server
run: ./build_server.sh
- Gin
- Beego
- Echo
- Revel
- Martini
- Fiber
- Buffalo
- FastHttp
- Gorilla Mux
- HttpRouter
- Chi
go get -u github.com/gin-gonic/gin
轻松指定本地开发和测试的默认配置。
使用docker容器部署时轻松覆盖默认配置。
Viper
是一个非常受环境的包,它可以从配置文件中查找、浇在和解析配置文件。
- 支持多种类型的文件例如
JSON
、TOML
、YAML
、ENV
、INI
。 - 支持从远程系统中读取配置信息例如
ETCD
、Consul
。 - 支持监视配置文件中的更改,并通知应用程序。
- 使用
Viper
保存我们对文件所作的任何配置修改。
go get github.com/spf13/viper
- 独立测试 - 隔离测试数据以避免冲突。
- 测试更快 - 减少与数据库对话的大量时间。
- 100%覆盖率 - 轻松设置边缘案例:意外错误
首先,它可以帮助我们更轻松的编写独立的测试。 其次,我们的测试将运行的更快。不必花时间与数据库进行交互并等待返回结果。所有的操作都将再内存中执行,并在同一进程中执行。 第三个也是非常重要的原因是:它允许我们编写实现100%覆盖率的测试。
是的!我们真正的DB存储已经过测试。
模拟数据库和真实数据库应该实现相同的接口。
- 使用假数据库 - 内存(Memory), 实现一个假版本的数据库:在内存中存储数据。
- 使用数据库存根 - GoMock, 生成和构建返回硬编码值的存根。
安装 mockgen
go get github.com/golang/mock/[email protected]
查看 mockgen
路径
which mockgen
查看 mockgen
帮助
mockgen -help
mockgen --package mockdb -destination db/mock/store.go github.com/grayjunzi/backend-master-class-golang/db/sqlc Store
migrate create -ext sql -dir db/migration -seq add_users
密码将使用 brcypt
散列函数进行散列以产生散列值。
除了输入密码 brcypt
还需要一个 (COST, SALT) 参数,这将决定算法的密钥扩展轮数或迭代次数。
Brcypt
还会产生一个随即盐,用于这些迭代,将有助于防止彩虹 彩虹攻击(rainbow)。
在这个哈希字符串中,有四部分组成,第一部分是哈希算法标识符,第二部分是COST(2^*轮密钥扩展),第三部分是长度16字节或128位的盐,它使用base64格式编码,这将生成22个字符的字符串,最后一部分是24字节的哈希值,编码位31个字符。四个部分连接在一起形成一个哈希字符串。
JWT(Json web Token)是使用最广泛的一种,但 JWT
一些安全问题,主要是因为它设计的标准很差,使用PASETO
有望为应用程序带来更好的安全性。
它是一个base64编码的字符串,由三部分组成,用 .
分隔,第一部分是 token
的 header
用于签署令牌的算法和类型,第二部分是存储有关登录用户的信息 例如 id 用户名 以及令牌过期的时间戳,第三部分用于验证签名。
-
对称加密算法(Symmetric digital signature algorithm)
- 使用相同的密钥(secret key)来签署和验证令牌。
- 该算法适用与本地适用,对于内部服务可以共享密钥。
- HS256, HS384, HS512
- HS256 = HMAC + SHA256 的组合
- HMAC 代表基于哈希的消息认证代码(Hash-based Message Authentication Code)
- SHA 是安全散列算法(Secure Hash Algorithm)
- 256/384/512 是输出的位数。
-
非对称加密算法(Asymmetric digital signature algorithm)
- 该算法由一对密钥而不是一个单独的密钥,私钥(private key)用于对令牌进行签名,公钥(public key)用于验证签名。
- 该算法可轻松地与任何外部第三方服务共享我们的公钥,不用担心泄漏私钥。
- RS256, RS384, RS512 | PS256, PS384, PS512 | ES256, ES384, ES512
- RS256 = RSA PKCSv1.5 + SHA256 的组合算法,PKCS(Public-Key Cryptography Standards)
- PS256 = RSA PSS + SHA256 的组合,PSS(Probabilistic Signature Scheme)
- ES256 = ECDSA + SHA256 的组合,ECDSA(Elliptic Curve Digital Signature Algorithm)
-
弱签名算法(Weak algorithms)
- JWT给了开发者太多的算法选择。
- 包括易受攻击的算法,例如 RSA PKCSv1.5 容易受到 padding oracle 攻击,ECDSA 容易受到无效曲线攻击(invalid-curve attach)
-
伪造(Trivial Forgery)
- 将 token 的 header 中
alg
设置为 none。 - 将 token 中 header 的
alg
设置为HS256
进行验证。
- 将 token 的 header 中
PASETo 是最成功的设计之一,被社区广泛接受为JWT的最佳安全替代方案。
- 提供开箱即用的强大签名算法。
- 开发人员不必再选择算法。
- 只需选择PASETO版本。
- 每个版本都已经实现了一个强大的密码套件(cipher suite)。
- 最多只有两个最新版本的PASETO处于活动状态。
PASETO 它只对payload进行base64编码并对令牌进行签名,实际上使用密钥对令牌中的所有数据进行加密和验证。使用具有关联数据(AEAD)算法的强身份验证加密。PASETO版本1中使用的AEAD算法是带有 ASE256 CTR + HMAC SHA384。 对于公共情况,有外部服务需要验证token,我们必须使用非对称加密算法,只对它进行base64编码,并使用私钥数字签名对内容进行签名。PASETO版本1中选择的非对称加密算法是带有RSA PSS + SHA384 算法。
PASETO v2 对于本地对称密钥场景它使用 XChaCha20-Poly1305
算法,对于公共非对称密钥方案,使用带有曲线的 Ed25519(EdDSA + Curve25519)
算法。
适用于本地的令牌有四个主要部分用 .
分隔,第一部分是 PASETO的版本,第二部分是token的用途,表示是用于本地场景还是公共场景,第三部分是主要内容,即令牌的有效载荷(payload)数据,它是加密的,解密之后有三个较小的部分,
第一个小部分中具有消息和过期时间,其次是在加密和消息认证过程中使用的 nonce 的值,最后是消息认证标签 验证加密消息及其关联的未加密数据。第四部分是footer,可以在footer中存储任何公共信息,因为它不会像有效负载正文那样被加密,而是只进行base64编码。其中footer部分是可选的,可以拥有没有footer的PASETO令牌。
Paragon Initiative Enterprises 是PASETO的发明者
- Version: v2
- Purpose: local
- Payload
- Body
- Nonce
- Authentication tag
适用于公共的令牌有三个部分,用 .
分隔,第一个部分是PASETO的版本,第二部分是token的用途,第三部分是有效载荷数据不会被加密而是被base64编码,其中一部分是令牌的签名由数字签名算法使用私钥创建,服务器将使用其配对的公钥来验证签名的真实性。
- Version: v2
- Purpose: public
- Payload
安装 uuid
go get github.com/google/uuid
安装 jwt
go get github.com/dgrijalva/jwt-go
安装 paseto
go get github.com/o1egl/paseto
创建新分支
git checkout -b ft/docker
提交代码
git push origin ft/docker
构建镜像
docker build -t simplebank:latest .
运行容器
docker run --name simplebank -p 8080:8080 -e GIN_MODE=release simplebank:latest
移除容器
docker rm simplebank
移除镜像
docker rmi simplebank
查看容器的网络设置
docker container inspect postgres12
查看网络
docker network ls
docker network inspect container
创建网络
docker network create simplbank-network
连接网络
docket network connect simplebank-network postgres12
运行容器
docker run --name simplebank --network simplebank-network -p 8080:8080 -e GIN_MODE=release simplebank:latest
略过
略过
略过
略过
- 一个开源容器编排引擎。
- 用于自动部署、扩展和管理容器化应用程序。
-
Worker Node - 运行容器化应用程序的工作机器或节点。每个Worker节点中,都有一个Kubelet代理。
- Kubelet agent:确保容器在pods内运行
- 容器运行时: Kubernetes支持多种容器,Docker、Containerd、CRI-O。
- Kube-proxy: 维护网络规则,允许与pods通信
-
Master Node - 它的职责是管理集群的工作节点和Pod。
第二部分是 Control Plane,它在主节点上运行
略过
略过
略过
略过
略过
略过
- 使用 dbdocs.io 生成数据库文档。
全局安装 dbdocs
npm install -g dbdocs
查看 dbdocs
版本
dbdocs
登录 dbdocs
dbdocs login
构建 dbml
dbdocs build doc/db.dbml
vscode安装 vscode-dbml
-
gRPC
- 是远程过程调用框架(Remote Procedure Call Framework)- 客户端可以在服务器上执行远程过程
- 远程交互代码由gRPC处理
- API和数据结构代码自动生成
- 支持多种编程语言
-
最初由谷歌开发,现在是云原生计算基础的一部分。
- 定义API和数据结构
- 生成gRPC存根(stubs)
- 实现服务
- 客户端使用
- 高性能 - 归功于底层的 HTTP/2 协议,它提供了一些好处,例如以二进制格式传输数据、多路复用(multiplexing, 允许通过相同的TCP连接发送多个请求)、请求头压缩以及客户端与服务器之间的双向通信等。
- 更好的API契约 - 服务器与客户端使用强类型的 请求/响应 数据结构 共享API的相同协议缓冲区定义(protobuf)。
- 自动代码生成 - 是gRPC最重要的特性之一,它可以让我们更快地开发服务,因为所有在客户端和服务器之间进行序列化、反序列或传输数据的代码都已经由gRPC生成和处理,所以我们需要关注的是实现服务的核心逻辑。
- 一元gPRC(Unary gRPC) - 客户端发送1个请求,服务器回复1个响应。类似于普通的HTTP API。
- 客户端流式gRPC - 客户端发送多条消息流,并且它希望服务器只返回1个单一响应。
- 服务器流式gRPC - 客户端发送1个请求,服务器回复多条消息流。
- 双向流式gPRC(Bidirectional streaming gRPC) - 客户端和服务器以任意顺序并行发送和接收多条消息。它非常灵活且没有阻塞,这意味着在发送下一条消息之前,任何一方都不需要等待对方的响应。
只编写一次服务器代码,能够同时为gRPC和HTTP请求提供服务。
- gRPC网关是一个协议缓冲区(protobuf)插件。
- 根据protobuf生成代理代码。
- 将HTTP调用转换为gPRC
- 进程内(in-process)翻译,意味着网关可以直接在代码中调用gRPC处理程序而无需通过网络上的任何额外跳跃点,但是它仅适用于一元gRPC。
- 将HTTP网关作为单独的代理服务器运行,HTTP请求将被翻译并通过网络调用转发到gRPC服务器。
- 编写一次代码,同时满足gRPC和HTTP请求
- 定义API和数据结构
- 生成gRPC存根
- 实现服务
- 客户端使用
安装 protobuf
apt install -y protobuf-compiler
查看版本
protoc --version
安装go插件
go install google.golang.org/protobuf/cmd/[email protected]
go install google.golang.org/grpc/cmd/[email protected]
查看版本
protoc-gen-go --version
protoc-gen-go-gprc --version
安装vscode插件
vscode-proto3
修复依赖
go mod tidy
- 使用生成的代码运行gRPC服务器
- 使用Evans客户端连接到gRPC服务器
安装 evans
go install github.com/ktr0731/evans@latest
使用 evans
连接 gRPC 服务
evans --host localhost --port 9090 -r repl
查看所有服务
show service
调用服务
call CreateUser
退出 evans
控制台
exit
编写一次代码,同时支持gRPC与HTTP服务。
安装依赖
go install \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \
google.golang.org/protobuf/cmd/protoc-gen-go \
google.golang.org/grpc/cmd/protoc-gen-go-grpc
下载 googleapis
git clone https://github.com/googleapis/googleapis.git
复制文件
cp google/api/annotations.proto ../backend-master-class-golang/proto/google/api/annotations.proto
cp google/api/field_behavior.proto ../backend-master-class-golang/proto/google/api
cp google/api/http.proto ../backend-master-class-golang/proto/google/api
cp google/api/httpbody.proto ../backend-master-class-golang/proto/google/api
元数据只是有关特定RPC调用的一些信息,以键值对的形式出现,它允许客户端提供一些与服务器调用相关的额外信息。
下载 gprc-gateway
git clone https://github.com/grpc-ecosystem/grpc-gateway
复制文件
mkdir -p proto/protoc-gen-openapiv2/options
cp protoc-gen-openapiv2/options/*.proto ../backend-master-class-golang/proto/protoc-gen-openapiv2/options/
下载 swagger-ui
git clone https://github.com/swagger-api/swagger-ui
复制文件
cp -r dist/* ../backend-master-class-golang/doc/swagger
Statik
允许将静态文件目录嵌入到Go可执行二进制文件中。
go install github.com/rakyll/statik
安装 migrate
go get github.com/golang-migrate/migrate/v4
安装 zero-logger
go get -u github.com/rs/zerolog/log
- 后台处理任务
- 容易实现
- 可能丢失的任务
- 任务同时保存在内存和持久存储中
- 高可用: Redis哨兵和Redis集群
- 没有任务丢失
- 在数据库中创建新的用户记录
- 将发送验证邮件任务推送到Redis队列
- 后台worker从队列中提取任务并处理它
它是一个简单、可靠且高效的分布式任务队列库。使用后台工作程序异步排队并处理任务。它由redis提供支持,并设计为易于扩展。
go get -u github.com/hibiken/asynq
查看redis是否启动正常
docker exec -it redis redis-cli ping
安装 email
go get github.com/jordan-wright/email
迁移表
migrate create -ext sql -dir db/migration -seq add_verify_emails