处理高并发无非就是四大的方法 分流、缓存、降级、限流
1、分流
实现方式最简单的是增加多台机器,目的是为了扩容
基本思路是 Tomcat(万以下,tomcat顶峰700并发)=》Nginx(3-4万并发)=》lvs(30万并发)=》F5(百万并发)=》DNS
集群负载均衡
集群:
tomcat、jboss、weblogic、websphere
负载均衡:
软件:nginx、Apache、haproxy … L7(网络第7层)
lvs L4(网络第4层)开源免费
硬件:f5 付费价格贵
DNS 负载均衡 域名解析 同一个域名根据ip解析到不同地域的数据中心,
(dns如何做到负载均衡例:
1) 客户端访问www.tmall.com , 用户向本机配置的本地DNS服务器发出查询请求,如果本地DNS服务器有该域名的缓存记录,则返回给用户,否则进行第2步;
2) 本地DNS服务器进行递归查询,最终会查询到域名服务商商处的授权DNS服务器;
3) 授权DNS服务器返回一条记录给本地DNS服务器,这里的域名可能对应多个vip(虚拟ip),会根据全局负载均衡策略设定的不同可能返回一个或多个随机返回一个vip;
6) 本地服务器将查询结果通过一条A记录返回给用户,并将缓存这条记录。)
均衡算法:轮询、随机等
假设天猫www.tmall.com的数据中心位于杭州,中心集群向各运行商(电信、联通、移动)要最大限度的带宽以提高高并发能力,这是一种分流方式,但类似双11的情况,带宽达不到要求,高并发能力不足,可能只占真实并发量的1/10的话就需要考虑cdn的方式,例如在全国多个人口密集的城市建立cdn集群节点,当深圳的用户访问天猫时,向深圳的cdn获取资源。
这里需要解释一下我理解的cdn的运作原理,下图中访问了www.tmall.com后会向杭州的中心集群发送请求,返回html中会有很多css、img、js的链接资源需要加载,这时就是根据相应的负载均衡的算法去获取离客户端最近的cdn中去获取。
大致是从上倒下 两台lvs 服务器用来负载均衡、三台haproxy做二级负载均衡、squid集群
客户端发送请求,会去随机选择一台lvs服务器,lvs服务器随机下发到任意一台haproxy服务器,再由haproxy服务器随机选择一台squid节点进行查询,如果有则直接返回,没有的话会向源站(www.tmall.com)进行资源获取,所有的squid统一调度,如果说用户需要高相应,比如修改了商品的图案,因为修改的东西是存放到中心集群上的,此时cdn集群中的squid节点要做到实时更新的话,需要源站统一修改。
缓存
上面说到的dns 、 cdn 分流都是实现缓存的方式
缓存的目的主要是为了提高访问速率,以空间换时间。
限流
如果说扩容和提速的方式都用上了还是遇到了瓶颈该如何?答案是限流
限流的目的在于保护、保持正常可用
下面用代码实现一个简单的秒杀抢购功能
@RestController
public class OrderController {
//1 抢购,抢购数量为5
static long limit = 5;
private AtomicLong count = new AtomicLong(0l);//原子类型保证线程安全
@GetMApping(“add”)
public String doOrder(String name){ //此处不用加锁的方式保证线程,性能不高
long c = count.incrementAndGet();
if(c > limit){
return “秒杀结束,谢谢参与!count =” + c;
}
return “恭喜,秒杀成功! count =” + c;
}
}
上面代码只能保证一台服务器的情况做到count统一。多台机器如果采用这种方式,秒杀数量就成倍增加了,商家得亏死。如果多台集群要保证数值统一,可以采用将数值存在数据库中的方式,此处我采用的redis。redis是单线程且线程安全的。
pom中加入redis依赖:
redis.clients
jedis
3.0.1
代码如下:
@GetMapping(“add2”)
public String doOrder2 (String name) { //此处不用加锁的方式保证线程,性能不高
try (Jedis jedis = new Jedis(“localhost”, 6379)) {
long c = jedis.incr(name);
if (c > limit) {
return “秒杀结束,谢谢参与!count =” + c;
}
return “恭喜,秒杀成功! count =” + c;
}
}
降级
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
降级按照是否自动化可分为:自动开关降级和人工开关降级。
降级按照功能可分为:读服务降级、写服务降级。
降级按照处于的系统层次可分为:多级降级。