分布式总结 - 823126028/book_reader GitHub Wiki
分布式治理
隔离
- 异地多活。
- 服务隔离。
- 线程隔离。
限流:
- 漏桶限流。(平均流出流量固定)。
- 令牌桶限流。(可以积累来等待一次大的流量,guava 内置的令牌桶)。
- sentinel 限流方式,滑动窗口限流: 每10ms一个格子,通过平均算法,算最近10个格子的总和来判断。
- guava cache expire 限流(性能较令牌桶优秀。令牌桶内部有锁)。
- 信号量。
- nginx 内部限流,如tegine的 tmd模块。
下线开关
降级:
- Hytrix 功能
服务治理:
对服务调用方,根据namespace 进行流量统计和针对性的限流。
服务依赖:
分强依赖和弱依赖。弱依赖提供降级,强依赖可以用客户端加缓存。
缓存作用:
短时间内能通过开关,完全替代数据库的能力
分布式缓存
Redis
redis lock: http://redis.cn/topics/distlock.html
redis 高可用方案:
- redis setinel。
- redis 一致性hash方案。
- redis 热备的目的 主要还是防止数据量过大,重新恢复时间比较长。
- redis back log 调大防止出现不断重同步问题。(同步dump文件的同时,还在更新这时候操作会记录在back log中)
- redis 主从同步是根据run id(不同机器)和 tranx id(不同的version),这样当主机换的时候,所有从机都要重新同步。
- 高可用cluster.
redis connection 的计算。
connection_num = QPS * 访问redis的次数 * redis平均访问时间 / 1s.
1000(qps) * 5 (单次接口调用redis访问次数) * 20ms(redis平均访问时间) = 100 (连接数设置为100)
redis connection 不够的时候,线程池可能会报,can't get resource
redis 结构:
- 外部引用: https://mp.weixin.qq.com/s/m-RdJQdG-qW-BLquorhMlQ
- sds:动态String,(可用大小,编码类型,byte[] 数组)。
- hashmap rehash的时候,是每次移动一个槽位,读先读其中一个,更新删除,两边都执行,增加在新的增加。
- expire 是记录在另一个结构的kv map。惰性删除 + 随机删除。
- zipmap, ziplist 在大小不大的情况下使用(时间复杂度换空间复杂度)。
- 在一些情况下可以用crc key 来做bloom filter,同样可以用bit 和 setRange来节省空间。
- zset是二分插排的列表。当zset的被remove空的时候这个key 也不存在了。 如果zset add的时候判断是否为空,否则会使得expire的数据只剩下刚添加进去的数据。
/* 字符串结构体(字符串就是字符数组) */
struct sdshdr {
// 字符串当前长度
unsigned int len;
// 剩余可用长度
unsigned int free;
// 字符数组(具体存放字符串的地方)
char buf[];
};
事务实现
- redis 用watch 做事务,且中间不会被打断,https://www.cnblogs.com/liuchuanfeng/p/7190654.html
redis 主线程流程
-
redis 单线程。
-
基本代码流程:
do_net_work(); do_timer() : 1. 删除过期的值。 2. 尝试aof,dump数据
redis 特性:
- 在网络拥塞情况比较大的时候,可以通过redis pipeline来减少redis 网络延迟的影响。
- redis watch 用来作为redis transactions。
- redis AOF 在磁盘写满的情况下就不能提供写服务了。
redis 阻塞操作
- 单台redis的性能瓶颈大多是网卡,可以看下ping的时延,来判断网卡是否打满。单个key值的时候及时是一致性hash也没有办法解决,因为单个key都打在了同一台机器,这个时候可以考虑,同时写入多个key。
- save 命令不能使用.会阻塞主进程
- redis 时延处理文档。http://www.cnblogs.com/me115/p/5032177.html
- redis bgsave流程 ==== 复制页表,cow(有改动 复制页表4K)====dump到硬盘
- Redis 服务设置了 appendfsync everysec, 主进程每秒钟便会调用 fsync(), 要求内核将数据”确实”写到存储硬件里. 但由于子进程同时也在写硬盘, 从而导致主进程 fsync()/write() 操作被阻塞, 最终导致 Redis 主进程阻塞了.解决方法便是设置 no-appendfsync-on-rewrite yes, 在子进程处理和写硬盘时, 主进程不调用 fsync() 操作. 注意, 即使进程不调用 fsync(), 系统内核也会根据自己的算法在适当的时机将数据写到硬盘(Linux 默认最长不超过 30 秒).
- 在某些低版本的redis上,数据是lazy_free的 exists 操作会清除数据,这时候会导致一定延时。
redis 结构应用:
- zset 正向拉取,反向拉取,分数获取。
- 存储数据的时候在value 的head留好一定位数的头,可以动态调整value的解析方式,序列化方式可以考虑fst。
redis 结合 lua脚本
LevelDB:
适合写入量比较大的key,value情况,有持久化存储。
软负载
配置中心
xdiamond:
client:
- client从地址服务器获得配置服务器(定时轮询),通过groupId, 和 dataId 长轮询方式获取配置服务器的配置信息(带上自己的md5,timeout=30s)。
- 对拉取的数据的version 和 本地数据version 进行md5比较,如果大于本地version那么就进行替换。
- 将拉下来的数据dump到本地,当连不上网络服务器的时候,获取的都是本地数据。
server:
- 服务器构成: 配置服务器,DB,地址服务器
- 修改步骤: 提交修改请求接口,更新DB增加版本号,落到本地磁盘,md5存入本地缓存。
- notify 其他服务器,失败重试。
zk对比:
- zk 写有瓶颈。
- zk 可用性不高,即使只有一台机器也应该能提供服务。
- zk 本身数据难以维护。
非中心负载均衡:
vs:
基本结构:
1. 负载中心的机器。
2. 检查各个peer的机器通过同维度轮询判断其他检测机器是否在线。
3. VirtualDomianCluster 是每个域名传递的数据,init 每个cluster来检查待检查数据的状态。
4. 如果数据修改,递交给raft的master去做,master 广播给各个 follower去检查状态。
5. 检查的时候如果失败,立刻开启第二次failFastCheck。快速下线。
6. 通过取余来获取resposible的ip机器。
特点:
1. 因为不能像diamond 一样有db作为唯一性的保证,所以不能通过notify方式通知其他服务器,所以必须有raft作为分布式一致性存储。
2. 探测式的不像其他的需要主动注册。
3. 通过拦截DNS 来保证域名解析到我们定义的ip地址。
注册中心式
cs:
基本机构:
1. connection server.
2. data server.
特点优化:
1. 合并推送(zk 每次更改都要推送给所有client),延迟成每秒推送,这样在大规模发布的时候优势较大。
2. 方便scale out, data server 能横向扩容,根据指定的 client key。
3. 写入性能scale out了。
中心式负载均衡
nginx:
主要功能:
- proxy: 反向代理
- backend:负载均衡。
- rewrite 地址重写,内部重定向。
- 结合lua 进行一些限流操作(openresty)。 content_by_lua, init_by_lua等
- 带有白名单和黑名单限流等功能内部。
- 根据ip 限制流量。
server_name
可以根据域名对 server 进行匹配(多域名转发) ,server_name 可以正则匹配。
location 规则:
location 则是根据uri 进行转发
- = 完全等于
- ~ 可以正则
RocketMq:
rocket mq的push状态的thread设置:
- 每生成一个pull命令,都会调用线程池去拉取metaQ的服务消息,如果thread设置不够,那么拉取速度就会比较慢。
hsf
- client 动态代理:
- 利用xml, factoryBean返回动态代理包装后的数据。
- 利用自定义autoProxyCreator。
- CustomerTypeEditor 修改autowired 的数据。
分布式锁:
redis,tair的实现方式
- 实现方式: redis,tair setNx, 锁key是业务逻辑相关的key, value:uuid + thread号。
- 实现可重入的锁的关键在于double check,首先在 getValue 的时候判断下锁是否是自己的,如果是expire 延长时间。如果不是的话,锁就不是自己的。
- 优化方式: 客户端加上本地锁lock, 解锁的时候注意 isHeldLock, 是否已经全部释放了可重入锁。
数据库悲观锁实现
select * from table where a = b for update.
数据库乐观锁实现
update table set a = 7 where a = 6; 如果update不成功,证明不行。
分布式任务调度器:
QUARZ + ZK, 注册zk用来判断存活的机器。 以及关注的机器。quarz 定时任务生成。