系统大部分时间是可用的,即能为我们提供服务,即使在发生硬件故障或升级时。
99.9999%可用:有0.0001%时间不可用
成功次数与总请求次数之比
99%可用:网站请求100次,其中99次成功(也即1次失败)
(1)黑客攻击
(2)硬件故障,比如服务器坏掉。
(3)并发量/用户请求量激增导致整个服务宕掉或者部分不可用
(4)代码中原因导致内存泄漏或者其他问题导致程序挂掉
(5)网站架构某个重要组件如Nginx或者数据库突然不可用
(6)自然灾害或者人为破坏
最重要,包括常见的内存泄露、循环依赖等会大量损害系统可用性
提高代码神器:
Sonarqube
Alibaba 开源的 Java 诊断工具 Arthas
阿里巴巴 Java 代码规范(Alibaba Java Code Guidelines)
IDEA 自带的代码分析等工具
流量控制
原理: 监控应用流量的QPS、并发线程数等,当达到指定阈值时对流量进行控制,已避免被瞬时的流量高峰冲垮。
一旦用户请求超各某个时间得不到响应就抛出异常
(1)若未设置超时,导致请求响应速度慢,甚至导致请求堆积进而导致系统无法再处理请求
(2)超时设置方式不正确
(3)不适合重试或者重试次数过多(一般设为3次)
系统自动收集所依赖服务的资源使用情况或性能指标,当所依赖的服务恶化或者失败调用失败次数达到某个阈值时就迅速失败,并让当前系统立即切换依赖其他备用服务。
常用熔断降级框架是Netflix 的 Hystrix 和 alibaba 的 Sentinel。
用户请求完成使用异步调用就能立即返回结果,具体处理后续再做。
除了异步调用,还可以使用消息队列
并发量高时大量请求直接请求数据库可能会使数据库挂掉,使用缓存则可以避免。
核心应用和服务优先使用更好的硬件
监控系统资源使用情况增加报警设置
注意备份,必要时候回滚
灰度发布
每次只发布服务集群中若干机器,若无问题继续发布其他机器,若有问题则回滚已发布的机器
定期检查、更换、升级硬件
对请求的速率进行限制,避免瞬时的大量请求击垮系统。
规定了我们单位时间处理的请求数量。
固定窗口其实就是时间窗口。
1分钟只能访问60次:
设定当前接口处理的请求数量counter,初始值为0
1分钟内每处理一个请求counter+1,当counter=60之后就拒绝后面全部的请求
等到1分钟钟之后将counter重置0,重新开始计数。
无法保证限流速率,因而无法保证突然激增的流量
如:某个接口1分钟内只能访问1000次,该接口的QPS是500,前59s这个接口1个请求都没有接收,后1s突然接收了1000个请求,系统直接就被瞬时的大量请求给击垮了
在固定窗口计数器算法上把时间按一定比例分片。
1分钟只能访问60次:
把1分钟分为60个窗口
每隔1秒移动一次,每个窗口1s只能处理不大于60请求数/60窗口数=1个请求数的请求
如果当前窗口的请求计数总和超过限制数量的话,就不再处理其他请求
当滑动窗口的格子划分的越多,滑动窗口的滚动就越平滑,限流的统计就会越准确
准备一个队列用来保存请求,然后定期从队列中拿请求来执行(类似消息队列削峰/限流)
请求在被处理前需要从桶里拿到一个令牌,请求处理完将这个令牌丢弃(删除)
我们根据限流大小,按照一定的速率往桶里添加令牌。
如果桶装满了,就不能继续往里面继续添加令牌了。
针对单体架构应用
Google Guava 自带的限流工具类 ,不仅有令牌桶算法(平滑突发限流)实现,还有平滑预热限流算法实现。
平华突发限流就是按照指定的速率放令牌到桶里
平滑预热限流则会有一段预热时间,预热时间之内,速率会逐渐提升到配置的速率
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
import com.google.common.util.concurrent.RateLimiter;
public class RateLimiterDemo {
public static void main(String[] args) {
// 1s 放 5 个令牌到桶里也就是 0.2s 放 1个令牌到桶里
RateLimiter rateLimiter = RateLimiter.create(5);
for (int i = 0; i < 10; i++) {
double sleepingTime = rateLimiter.acquire(1);
System.out.printf("get 1 tokens: %ss%n", sleepingTime);
}
}
}
输出:
get 1 tokens: 0.0s
get 1 tokens: 0.188413s
get 1 tokens: 0.197811s
get 1 tokens: 0.198316s
get 1 tokens: 0.19864s
get 1 tokens: 0.199363s
get 1 tokens: 0.193997s
get 1 tokens: 0.199623s
get 1 tokens: 0.199357s
get 1 tokens: 0.195676s
import com.google.common.util.concurrent.RateLimiter;
import java.util.concurrent.TimeUnit;
public class RateLimiterDemo {
public static void main(String[] args) {
// 1s 放 5 个令牌到桶里也就是 0.2s 放 1个令牌到桶里
// 预热时间为3s,也就说刚开始的 3s 内发牌速率会逐渐提升到 0.2s 放 1 个令牌到桶里
RateLimiter rateLimiter = RateLimiter.create(5, 3, TimeUnit.SECONDS);
for (int i = 0; i < 20; i++) {
double sleepingTime = rateLimiter.acquire(1);
System.out.printf("get 1 tokens: %sds%n", sleepingTime);
}
}
}
输出:
get 1 tokens: 0.0s
get 1 tokens: 0.561919s
get 1 tokens: 0.516931s
get 1 tokens: 0.463798s
get 1 tokens: 0.41286s
get 1 tokens: 0.356172s
get 1 tokens: 0.300489s
get 1 tokens: 0.252545s
get 1 tokens: 0.203996s
get 1 tokens: 0.198359s
Guava 地址:
https://github.com/google/guava
Bucket4j基于令牌/漏桶算法不错的限流库
RateLimiter简单开箱即用,单机场景下实用
Bucket4j限流功能更全面,支持单机限流、分布式限流,还可以集成监控,搭配Prometheus 和 Grafana 使用。
Bucket4j 地址:
https://github.com/vladimir-bukhtoyarov/bucket4j
轻量级容错组件,Spring官方和Netflix推荐使用做限流熔断,SpringCloud Gateway 基于它实现。
提供限流、熔断、负载保护、自动重试等保障系统高可用开箱即用的功能
生态很好,很多网关都是用它做限流熔断
Resilience4j 地址:
https://github.com/resilience4j/resilience4j
针对分布式、微服务应用架构应用
如:用Sentinel、Redis来实现对应的限流逻辑
网关层限流通常也要用中间件/框架:
如SpringCloud Gateway 的分布式限流实现RedisRateLimiter基于Redis+Lua来实现
如SpringCloud Gateway还可以整合Sentinel来做限流
见我另一篇文章:
SpringCloud之Sentinel
优点:
减少网络开销
利用Lua脚本提交批量Redis命令到Redis服务器一次性执行批量
原子性
一段Lua脚本执行中不会有其他脚本或者Redis命令同时执行
项目实现:
Apache网关项目ShenYu的RateLimiter限流插件基于Redis+Lua实现了令牌桶算法/并发令牌桶算法/漏桶算法/滑动窗口算法。
ShenYu 地址:
https://github.com/apache/incubator-shenyu
备注:
网上也有很多现成的优秀的Redis+Lua限流脚本
下一篇跳转—高可用(二)
参考链接1-JavaGuide
持续更新中…
随心所往,看见未来。Follow your heart,see light!
欢迎点赞、关注、留言,一起学习、交流!