高可用服务设计概述
1
1. 负载均衡与反向代理
当我们的应用单实例不能支撑用户请求时,就需要扩容,从一天服务器扩容到两台、几十台、几百台。然而用户访问时是通过如http://www.jd.com的方式访问,在请求时,浏览器首先会查询DNS服务器获取对应的IP,然后通过此IP访问对应的服务。
对于负载均衡需要关心的几个方面如下:
? ? ? ?
上游服务器配置:使用upstream server配置上游服务器。 负载均衡算法:配置多个上游服务器时的负载均衡机制。
失败重试机制:配置当超时或上游服务器不存活时,是否需要重试其他上游服务器。 服务器心跳检查:上游服务器的健康检查/心跳检查。
Nginx提供的负载均衡机制可以实现服务器的负载均衡、故障转移、失败重试、容错、健康检查等,当某些上游服务器出现问题时可以将请求转到其他上游服务器以保障高可用,并通过OpenResty实现更智能的负载均衡,如将热点与非热点流量分离、正常流量与爬虫流量分离等。Nginx负载均衡器本身也是一台反向代理服务器,将用户请求通过Ningx代理到内网中的某台上游服务器处理,反向代理服务器可以对响应结果进行缓存、压缩等处理以提升性能。
2
负载均衡算法
负载均衡算法用来解决用户请求到来时如何选择upstream server进行处理,默认采用的是round-robin(轮询),同时支持其他几种算法。
? round-robin:轮询,默认负载均衡算法,即以轮询的方式将请求转发到上游服务器,通过配合weight配置可以实现基于权重的轮询。
? ?
ip-hash:根据客户IP进行负载均衡,即相同的IP将负载均衡到同一个upstream server。 hash key [consistent]:对某一个key进行哈希或者使用一致性哈希算法进行负载均衡。使用Hash算法那存在的问题是,当添加/删除一台服务器时,将导致很多key被重新负载均衡到不同的服务器(从而导致后端肯可能出现问题);因此,建议考虑使用一致性hash算法,这样当添加/删除一台服务器时,只有少数key将被重新负载均衡到不同的服务器。
?
哈希算法:此处是根据请求uri进行负载均衡,可以使用Nginx变量,因此可以实现复杂的算法。
失败重试
当fail_timeout时间内失败了max_fails次请求,则认为该上游服务器不可用/不存活,然后将摘掉该上游服务器,fail_timeout时间后会再次将该服务器加入到存活上游服务器列表进行重试。
3
健康检查
Nginx对上游服务器的健康检查默认采用的是惰性策略,Nginx商业版提供了health_check进行主动健康检查。也可以集成nginx_upstream_check_module模块进行主动健康检查,它支持TCP心跳和HTTP心跳来实现健康检查。
HTTP动态负载均衡
Consul是一款开源的分布式服务注册与发现系统,通过HTTP API可以使得服务注册、发现实现起来非常简单,它支持以下特性:
? ? ? ? ?
服务注册:服务注册者可以通过HTTP API或DNS方式,将服务注册到Consul。
服务发现:服务消费者可以通过HTTP API或DNS方式,从Consul获取服务的IP和PORT。 故障检测:支持如TCP、HTTP等方式的健康检查机制,从而当服务有故障时自动摘除。 K/V存储:使用K/V存储实现动态配置中心,其使用HTTP长轮询方式实现变更触发和配置更改。 多数据中心:支持多数据中心,可以按照数据中心注册和发现服务,即支持只消费本地机房服务,使用多数据中心集群还可以避免单数据中心的单点故障。
?
Raft算法:Consul使用Raft算法实现集群数据一致性。
4
2. 隔离术
隔离是指将系统或资源分隔开。系统隔离是为了在系统发生故障时能限定传播范围和影响范围,即发生故障后不会出现滚雪球效应,从而保证只有出问题的服务不可用,其他服务还是可用的。资源隔离是通过隔离来减少资源竞争,保障服务间的相互不影响和可用性。
线程隔离
线程隔离主要是指线程池隔离,在实际使用时,我们会把请求分类,然后交给不同的线程池处理。当一种业务的请求处理发生问题时,不会将故障扩散到其他线程池,从而保证其他服务可用。
进程隔离
在公司发展初期,一般是先进行从零到一,不会一上来就进行系统拆分,这样就会开发出一些大而全的系统,系统中的一个模块/功能出现问题,整个系统就不可用了。首先想到的解决方案是通过部署多个实例,通过负载均衡进行路由转发。但是这种情况无法避免某个模块因为BUG而出现如OOM导致整个系统不可用的风险。因此这种解决方案只能是一个过渡,较好的解决方案是通过将系统拆分为多个子系统来实现物理隔离。通过进程隔离使得某一个子系统出现问题时不会影响到其他子系统。
5