高并发
- 高并发读
- 高并发写
- 高并发读写
总结:想要高并发,首先要高性能,性能工程对于性能优化是有一些原则方法论的
单个数据库每秒两三千并发就基本扛不住了
系统拆分
将单体系统拆分成多个系统或者多个服务,每个子系统使用自己的数据库,提高并发度
大部分的系统都是需要经过多轮拆分的,第一轮拆分将系统粒度划的小一点,可能随着业务的发展,单个系统会变得更复杂,所以需要进一步的拆分
拆分的维度:
- 业务维度
- 功能维度
- 资源维度 资源使用多与使用少的场景划分
局部并发原则
将同步调用的外部服务拆分为多个无关异步调用,来提升性能
高性能流程
容量规划
使用压测监控预测等确定要支撑多少性能指标 确定哪些性能指标的基准水位线
负载测试
测试系统在正常运行压力下的各项指标,这个流程是一个长期的流程以保证系统性能可预测,出现问题也能发现问题在哪
压力测试
要压到发现系统失效点,远高于正常的负载 只对关键、瓶颈业务进行测试 好钢用在刀刃上
失效点关注于响应时间、内存、失败率等异常指标
APM监控
- 基础监控:通过时序数据等指标发现异常以告警
- 追踪监控:通过链路追踪以发现出错的原因
- 业务监控
弹性扩缩容
根据监控,使用策略,管理资源
高并发读
策略:加缓存
大部分高并发的场景,都是写多读少,使用缓存,可以有效抗住高并发
对于缓存需要考虑 雪崩 击穿 穿透 等缓存问题
- 本地缓存或以Redis为代表的集中式缓存
- 数据库主从复制分担主库压力
- CDN 静态加速
策略:并发读
- 异步RPC 要求各个调用之间是独立的
- 既然数据库每秒能撑住的请求是有限的,那么就可以使用MQ,大量的请求灌入MQ,利用MQ的削峰,让下游系统慢慢消费
- 冗余请求 通过每次调用多个服务器 哪个返回的快就使用的哪个
- 分库分表 让每个表的数据少一点,提高SQL的执行速度
策略:重写轻读
某些事件产生的数据提前聚合好 等需要的时候直接读取即可 而非在需要的时候实时计算
总结:读写分离
- 数据库读写分离
大部分对数据库的请求都是读多写少,所以读写分离,分配多一些机器给读请求,能有效提高性能
问题
- 读写分离带来的问题及解决方案
- 复制延迟
- 针对特定业务 读写都在主机上 非核心业务使用从机
- 读从机,发现从机上的数据是旧的 则二次读取主机
- 分配机制 如何让客户端将读写操作区分开来,然后访问不同的数据库服务器
- 客户端分离 TDDL shardingSphere 动态数据源也可以实现
- 数据库中间件 MyCat, MySQL Router,
高并发写
策略:数据分片
- 数据库分库分表
- JDK的ConcurrentHashMap 通过分段(之前的版本) 来降低竞争
- Kafka的分区 不同的分区可以并发地读写
- ES分布式索引
策略:任务分片
- 指令流水线
- mapreduce
策略:异步化
减少等待、Y轴扩展、削峰填谷
- 系统层面:异步网络模型
- JDK层面 NIO等
- 接口层面 线程池 异步调用 Future等
- 业务层面 RPC方式
策略:批量
- 批量写入数据
- 将小操作合并成一整个大操作
妥协
资源是有限的 并发无上限
- 排队系统
- 基于阻塞方式
- 基于令牌方式(令牌桶)
- 熔断降级
负载均衡
调度后方的多台机器,以统一的接口对外提供服务,承担此职责的技术组件被称为“负载均衡”。
- 四层负载均衡优势是性能高,七层负载均衡的优势功能强
- 做多级混合负载均衡,通常应是低层的负载均衡在前,高层的负载均衡在后
“四层”的意思是说这些工作模式的共同特点是都维持着同一个TCP连接,而不是说它就只工作在第四层
链路负载均衡
通过DNS解析来完成
实时性不强 一旦后端服务器宕机 这种负载方式无法及时发现
硬件负载均衡
- F5 A10 等能支持达到百万量级的并发 缺点就是贵
操作系统负载均衡
利用系统级别的中断以及多队列网卡等来充分利用资源 达到负载均衡的功能
客户端负载均衡
- 传统微服务
代理负载均衡
- 服务网格
集群负载均衡
网络级别的负载均衡
- 数据链路层负载均衡 直接修改请求的数据帧中的MAC目标地址,让用户原本是发送给负载均衡器的请求的数据帧,被二层交换机根据新的MAC目标地址转发到服务器集群中对应的服务器 二层负载均衡器直接改写目标MAC地址的工作原理决定了它与真实的服务器的通讯必须是二层可达的 效率很高,但必须是二层可达,这就导致了它适合做第一级负载设备
- 网络层负载均衡
- IP隧道模式:对IP包进行封装,封装成一个新包发送到后端服务器 这种方式需要后端服务器持有一个VIP 这样才能直接响应请求,不够透明
- NAT模式:直接由均衡器对IP头进行修改 所有数据报都要经过均衡器 这个均衡器很容易称为瓶颈
- 更彻底的NAT模式:均衡器在转发时,不仅修改目标IP地址,连源IP地址也一起改了,源地址就改成均衡器自己的IP,称作Source NAT(SNAT) 在后端服务器的视角看来,所有的流量都来自于负载均衡器
- 应用层负载均衡 该层的优势就在于比链路层 网络层可以得到更多的信息 从而做出更加智能的决策
负载均衡算法
任务平分类
- 轮循均衡
- 权重轮循均衡
- 随机均衡
- 权重随机均衡
压力均衡类
- 响应速度均衡
- 最少连接数均衡
hash类
- 一致性哈希均衡
- ip哈希
- id哈希
均衡器实现
- 硬件负载均衡 F5 A10
- 软件负载均衡
- 构建在内核的:LVS
- 应用程序的形式:Nginx、HAProxy、KeepAlived
- 构建在内核的:LVS
LVS
- D-NAT模式
- DR模式
将RS的VIP配置在内核中
- TUN模式
调度算法:
- 静态
- 轮询
- 加权轮询
- ...
- 动态
- 最少连接
- 加权最少连接
- ...
流程:
node01:
ifconfig eth0:8 192.168.150.100/24
node02~node03:
1)修改内核:
echo 1 > /proc/sys/net/ipv4/conf/eth0/arp_ignore
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/eth0/arp_announce
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
2)设置隐藏的vip:
ifconfig lo:3 192.168.150.100 netmask 255.255.255.255
RS中的服务:
node02~node03:
yum install httpd -y
service httpd start
vi /var/www/html/index.html
from 192.168.150.1x
LVS服务配置
node01:
yum install ipvsadm
ipvsadm -A -t 192.168.150.100:80 -s rr
ipvsadm -a -t 192.168.150.100:80 -r 192.168.150.12 -g -w 1
ipvsadm -a -t 192.168.150.100:80 -r 192.168.150.13 -g -w 1
ipvsadm -ln
验证:
浏览器访问 192.168.150.100 看到负载 疯狂F5
node01:
netstat -natp 结论看不到socket连接
node02~node03:
netstat -natp 结论看到很多的socket连接
node01:
ipvsadm -lnc 查看偷窥记录本
TCP 00:57 FIN_WAIT 192.168.150.1:51587 192.168.150.100:80 192.168.150.12:80
FIN_WAIT: 连接过,偷窥了所有的包
SYN_RECV: 基本上lvs都记录了,证明lvs没事,一定是后边网络层出问题
问题:
- LVS可能会发生单点故障
- 主备
- RS挂的话,部分请求会失败
keepalived
作为一个通用工具,解决高可用问题
配置
vrrp_instance VI_1 {
state MASTER // 备服务器BACKUP
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
172.17.0.100/16 dev eth0 label eth0:3
}
}
virtual_server 172.17.0.100 80 {
delay_loop 6
lb_algo rr
lb_kind DR
nat_mask 255.255.255.0
persistence_timeout 0
protocol TCP
real_server 172.17.0.4 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
real_server 172.17.0.6 80 {
weight 1
HTTP_GET {
url {
path /
status_code 200
}
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
}