initial import
This commit is contained in:
4
doc/breaker.md
Normal file
4
doc/breaker.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# 熔断机制设计
|
||||
|
||||
## 设计目的
|
||||
* 依赖的服务出现大规模故障时,调用方应该尽可能少调用,降低故障服务的压力,使之尽快恢复服务
|
||||
BIN
doc/images/shedding_flying.jpg
Normal file
BIN
doc/images/shedding_flying.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 158 KiB |
142
doc/kubernetes_setup.md
Normal file
142
doc/kubernetes_setup.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# kubernetes集群搭建(centos7)
|
||||
|
||||
* 修改每台主机的hostname,如果需要的话
|
||||
* `hostname <hostname>`
|
||||
* 修改/etc/hostname
|
||||
|
||||
* 选择一台机器安装ansible,为了便于从一台机器上操作所有机器
|
||||
* 安装zsh & oh-my-zsh,为了更方便的使用命令行(可选)
|
||||
|
||||
```
|
||||
yum install -y zsh
|
||||
yum install -y git
|
||||
sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
|
||||
```
|
||||
|
||||
* `yum install -y ansible`
|
||||
* 解决错误 `RequestsDependencyWarning: urllib3 (1.22) or chardet (2.2.1) doesn't match a supported version`
|
||||
|
||||
```
|
||||
pip uninstall -y urllib3
|
||||
pip uninstall -y chardet
|
||||
pip install requests
|
||||
```
|
||||
|
||||
* 禁用command_warnings,在/etc/ansible/ansible.cfg里将`command_warnings = False`前面的#去掉
|
||||
* 将所有机器的内网ip按照分组增加到/etc/ansible/hosts,如下:
|
||||
|
||||
```
|
||||
[master]
|
||||
172.20.102.[208:210]
|
||||
|
||||
[node]
|
||||
172.20.102.[211:212]
|
||||
```
|
||||
|
||||
* 用root账号通过ssh-keygen生成内网无需密码root登录其它服务器,使用默认选项
|
||||
* 用ssh-copy-id将生成的id_rsa.pub传送到所有主机的authorized_hosts里,包括本机,如:
|
||||
|
||||
`ssh-copy-id root@172.20.102.208`
|
||||
* 验证ansible是否可以登录所有服务器,如下:
|
||||
|
||||
```
|
||||
[root@172 ~]# ansible all -m ping -u root
|
||||
172.20.102.208 | SUCCESS => {
|
||||
"changed": false,
|
||||
"ping": "pong"
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
* 更新所有服务器
|
||||
|
||||
`ansible all -u root -m shell -a "yum update -y"`
|
||||
|
||||
* 所有服务器上安装docker
|
||||
|
||||
```
|
||||
ansible all -u root -m shell -a "yum remove docker docker-client docker-client-latest docker-core docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine"
|
||||
ansible all -u root -m shell -a "yum install -y yum-utils"
|
||||
ansible all -u root -m shell -a "yum install -y device-mapper-persistent-data"
|
||||
ansible all -u root -m shell -a "yum install -y lvm2"
|
||||
ansible all -u root -m shell -a "yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo"
|
||||
ansible all -u root -m shell -a "yum install -y docker-ce"
|
||||
ansible all -u root -m shell -a "systemctl enable docker"
|
||||
ansible all -u root -m shell -a "systemctl start docker"
|
||||
```
|
||||
|
||||
* 每台机器上添加阿里云的kubernetes repo
|
||||
|
||||
```
|
||||
# cat k8srepo.yaml
|
||||
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
|
||||
[kubernetes]
|
||||
name=Kubernetes
|
||||
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
repo_gpgcheck=0
|
||||
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
|
||||
http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
|
||||
EOF
|
||||
|
||||
# ansible-playbook k8srepo.yaml
|
||||
```
|
||||
|
||||
* 安装kubelet, kubeadm, kubectl, ipvsadm
|
||||
|
||||
`ansible all -u root -m shell -a "yum install -y kubelet kubeadm kubectl ipvsadm"`
|
||||
|
||||
* 禁用所有服务器上的swap
|
||||
|
||||
`ansible all -u root -m shell -a "swapoff -a"`
|
||||
|
||||
* 允许所有服务器进行转发,因为k8s的NodePort需要在所有服务器之间进行转发
|
||||
|
||||
`ansible all -u root -m shell -a "iptables -P FORWARD ACCEPT"`
|
||||
|
||||
* 由于k8s.gcr.io不能访问,需要从本机科学上网docker pull如下几个image
|
||||
|
||||
```
|
||||
k8s.gcr.io/kube-proxy-amd64:v1.11.1
|
||||
k8s.gcr.io/kube-controller-manager-amd64:v1.11.1
|
||||
k8s.gcr.io/kube-scheduler-amd64:v1.11.1
|
||||
k8s.gcr.io/kube-apiserver-amd64:v1.11.1
|
||||
k8s.gcr.io/coredns:1.1.3
|
||||
k8s.gcr.io/etcd-amd64:3.2.18
|
||||
k8s.gcr.io/pause:3.1
|
||||
```
|
||||
|
||||
通过命令一次完成拉取
|
||||
|
||||
`while IFS= read -r line; do docker pull $line; done`
|
||||
|
||||
然后上传到一台服务器
|
||||
|
||||
`while IFS= read -r line; do docker save <image> | pv | ssh <user>@<server ip> "docker load"; done`
|
||||
|
||||
同步到所有k8s服务器,其中$i是为了匹配所有内网ip
|
||||
|
||||
`while IFS= read -r line; do for ((i=208;i<213;i++)); do docker save $line | ssh root@172.20.102.$i "docker load"; done; done`
|
||||
|
||||
* 在一台master服务器上初始化集群
|
||||
|
||||
`kubeadm init --api-advertise-addresses <本机内网ip> --kubernetes-version=v1.11.1`
|
||||
|
||||
注意最后的`kubeadm join`一行,用来在其它服务器加入集群(稍后用)
|
||||
|
||||
初始化配置,master上执行如下命令
|
||||
|
||||
```
|
||||
mkdir -p $HOME/.kube
|
||||
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
|
||||
sudo chown $(id -u):$(id -g) $HOME/.kube/config
|
||||
```
|
||||
|
||||
添加calico网络
|
||||
|
||||
`kubectl apply -f https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/kubeadm/1.7/calico.yaml`
|
||||
|
||||
* 从所有其它服务器执行master上获得的kubeadm join那行命令,里面包含了加入的token
|
||||
|
||||
* 执行`kubectl get nodes`验证集群是否成功
|
||||
46
doc/loadshedding.md
Normal file
46
doc/loadshedding.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 服务自适应降载保护设计
|
||||
|
||||
## 设计目的
|
||||
* 保证系统不被过量请求拖垮
|
||||
* 在保证系统稳定的前提下,尽可能提供更高的吞吐量
|
||||
|
||||
## 设计考虑因素
|
||||
* 如何衡量系统负载
|
||||
* 是否处于虚机或容器内,需要读取cgroup相关负载
|
||||
* 用1000m表示100%CPU,推荐使用800m表示系统高负载
|
||||
* 尽可能小的Overhead,不显著增加RT
|
||||
* 不考虑服务本身所依赖的DB或者缓存系统问题,这类问题通过熔断机制来解决
|
||||
|
||||
## 机制设计
|
||||
* 计算CPU负载时使用滑动平均来降低CPU负载抖动带来的不稳定,关于滑动平均见参考资料
|
||||
* 滑动平均就是取之前连续N次值的近似平均,N取值可以通过超参beta来决定
|
||||
* 当CPU负载大于指定值时触发降载保护机制
|
||||
* 时间窗口机制,用滑动窗口机制来记录之前时间窗口内的QPS和RT(response time)
|
||||
* 滑动窗口使用5秒钟50个桶的方式,每个桶保存100ms时间内的请求,循环利用,最新的覆盖最老的
|
||||
* 计算maxQPS和minRT时需要过滤掉最新的时间没有用完的桶,防止此桶内只有极少数请求,并且RT处于低概率的极小值,所以计算maxQPS和minRT时按照上面的50个桶的参数只会算49个
|
||||
* 满足以下所有条件则拒绝该请求
|
||||
1. 当前CPU负载超过预设阈值,或者上次拒绝时间到现在不超过1秒(冷却期)。冷却期是为了不能让负载刚下来就马上增加压力导致立马又上去的来回抖动
|
||||
2. `averageFlying > max(1, QPS*minRT/1e3)`
|
||||
* averageFlying = MovingAverage(flying)
|
||||
* 在算MovingAverage(flying)的时候,超参beta默认取值为0.9,表示计算前十次的平均flying值
|
||||
* 取flying值的时候,有三种做法:
|
||||
1. 请求增加后更新一次averageFlying,见图中橙色曲线
|
||||
2. 请求结束后更新一次averageFlying,见图中绿色曲线
|
||||
3. 请求增加后更新一次averageFlying,请求结束后更新一次averageFlying
|
||||
|
||||
我们使用的是第二种,这样可以更好的防止抖动,如图:
|
||||

|
||||
* QPS = maxPass * bucketsPerSecond
|
||||
* maxPass表示每个有效桶里的成功的requests
|
||||
* bucketsPerSecond表示每秒有多少个桶
|
||||
* 1e3表示1000毫秒,minRT单位也是毫秒,QPS*minRT/1e3得到的就是平均每个时间点有多少并发请求
|
||||
|
||||
## 降载的使用
|
||||
* 已经在ngin和rpcx框架里增加了可选激活配置
|
||||
* CpuThreshold,如果把值设置为大于0的值,则激活该服务的自动降载机制
|
||||
* 如果请求被drop,那么错误日志里会有`dropreq`关键字
|
||||
|
||||
## 参考资料
|
||||
* [滑动平均](https://www.cnblogs.com/wuliytTaotao/p/9479958.html)
|
||||
* [Sentinel自适应限流](https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81)
|
||||
* [Kratos自适应限流保护](https://github.com/bilibili/kratos/blob/master/doc/wiki-cn/ratelimit.md)
|
||||
29
doc/mapreduce.md
Normal file
29
doc/mapreduce.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# mapreduce用法
|
||||
|
||||
## Map
|
||||
|
||||
> channel是Map的返回值
|
||||
|
||||
由于Map是个并发操作,如果不用range或drain的方式,那么在使用返回值的时候,可能Map里面的代码还在读写这个返回值,可能导致数据不全或者`concurrent read write错误`
|
||||
|
||||
* 如果需要收集Map生成的结果,那么使用如下方式
|
||||
|
||||
```
|
||||
for v := range channel {
|
||||
// v is with type interface{}
|
||||
}
|
||||
```
|
||||
|
||||
* 如果不需要收集结果,那么就需要显式的调用mapreduce.Drain,如
|
||||
|
||||
```
|
||||
mapreduce.Drain(channel)
|
||||
```
|
||||
|
||||
## MapReduce
|
||||
|
||||
* mapper和reducer方法里可以调用cancel,调用了cancel之后返回值会是`nil, false`
|
||||
* mapper里面如果有item不写入writer,那么这个item就不会被reduce收集
|
||||
* mapper里面如果有处理item时panic,那么这个item也不会被reduce收集
|
||||
* reduce是单线程,所有mapper出来的结果在这里串行处理
|
||||
* reduce里面不写writer,或者panic,会导致返回`nil, false`
|
||||
15
doc/periodicalexecutor.md
Normal file
15
doc/periodicalexecutor.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# PeriodicalExecutor设计
|
||||
|
||||
# 添加任务
|
||||
|
||||
* 当前没有未执行的任务
|
||||
* 添加并启动定时器
|
||||
* 已有未执行的任务
|
||||
* 添加并检查是否到达最大缓存数
|
||||
* 如到,执行所有缓存任务
|
||||
* 未到,只添加
|
||||
|
||||
# 定时器到期
|
||||
|
||||
* 清除并执行所有缓存任务
|
||||
* 再等待N个定时周期,如果等待过程中一直没有新任务,则退出
|
||||
13
doc/rpc.md
Normal file
13
doc/rpc.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# rpc设计规范
|
||||
|
||||
* 目录结构
|
||||
* service/remote目录下按照服务所属模块存放,比如用户的profile接口,目录如下:
|
||||
|
||||
`service/remote/user/profile.proto`
|
||||
|
||||
* 生成的profile.pb.go也放在该目录下,并且profile.proto文件里要加上`package user;`
|
||||
|
||||
* 错误处理
|
||||
* 需要使用status.Error(code, desc)来定义返回的错误
|
||||
* code是codes.Code类型,尽可能使用grpc已经定义好的code
|
||||
* codes.DeadlineExceeded, codes.Internal, codes.Unavailable, codes.DataLoss错误会被自动熔断
|
||||
24
doc/sql-cache.md
Normal file
24
doc/sql-cache.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# DB缓存机制
|
||||
|
||||
## QueryRowIndex
|
||||
|
||||
* 没有查询条件到Primary映射的缓存
|
||||
* 通过查询条件到DB去查询行记录,然后
|
||||
* **把Primary到行记录的缓存写到redis里**
|
||||
* **把查询条件到Primary的映射保存到redis里**,*框架的Take方法自动做了*
|
||||
* 可能的过期顺序
|
||||
* 查询条件到Primary的映射缓存未过期
|
||||
* Primary到行记录的缓存未过期
|
||||
* 直接返回缓存行记录
|
||||
* Primary到行记录的缓存已过期
|
||||
* 通过Primary到DB获取行记录,并写入缓存
|
||||
* 此时存在的问题是,查询条件到Primary的缓存可能已经快要过期了,短时间内的查询又会触发一次数据库查询
|
||||
* 要避免这个问题,可以让**上面粗体部分**第一个过期时间略长于第二个,比如5秒
|
||||
* 查询条件到Primary的映射缓存已过期,不管Primary到行记录的缓存是否过期
|
||||
* 查询条件到Primary的映射会被重新获取,获取过程中会自动写入新的Primary到行记录的缓存,这样两种缓存的过期时间都是刚刚设置
|
||||
* 有查询条件到Primary映射的缓存
|
||||
* 没有Primary到行记录的缓存
|
||||
* 通过Primary到DB查询行记录,并写入缓存
|
||||
* 有Primary到行记录的缓存
|
||||
* 直接返回缓存结果
|
||||
|
||||
Reference in New Issue
Block a user