高可用高并发系统设计概念学习 二
前言
本次学习是学习的隔离,超时与重试机制的知识点。
一、隔离术
隔离指的是将系统或者资源分割开。系统隔离是为了在系统发生故障时,控制影响范围,保证只出问题的服务不可用,其他服务不受影响。资源隔离是为了减少资源竞争,保障服务间数据相互不影响和可用性。比较常用的隔离有线程隔离、进程隔离、集群隔离、机房隔离、读写隔离、快慢隔离、动静隔离、爬虫隔离等。
线程隔离
线程隔离可以理解为不同业务的请求,使用不同的线程池处理,而不是全部统一一个线程池分配线程。这样做当某种业务出了问题,不会影响到其他业务的处理。
进程隔离
在公司发展初期,一般是先进行从零到一,不会一上来就进行系统拆分,这样就会开发出一些大而全的系统,系统中的一个模块/功能出现问题,整个系统就不可用了。首先,想到的解决方案时通过负载均衡多部署几台服务器。但是还是无法避免某个模块因BUG导出整个系统都不可用的奉先。因此可以通过系统拆分为多个子系统来实现隔离,通过进程隔离使某一个子系统出现问题时不会影响到其他子系统。
集群隔离
随着系统的发展,单例服务满足不了需求,就会需要服务化技术,通过部署多个服务形成服务集群,来提升系统容量,如下图所示。
随着调用方的增多,秒杀服务会影响到其他服务稳定性。这时可以考虑将秒杀服务提供单独的服务集群,这样当某一个集群出现问题不会影响到其他集群的稳定性。如下图所示。
机房隔离
机房隔离也可以理解为分区隔离,多备份几个不同地区的集群,当某一个地区出现了问题,可以快速切换到正常的地区,保障系统的可用性。
读写隔离
比如数据库,读写分离开来,当写入出现故障时,读还是正常读取的。
动静隔离
系统中是有许多js,图片,css等静态文件的,当某个时间点许多用户同时访问资源时,很有可能会访问量太大导致带宽被打满,从而休闲不可用。比如订单结算页,如下图所示。
此时就可以通过将静态资源放在CDN服务器上,进行节点缓存处理,如下图所示。
爬虫隔离
在实际业务中,系统可能因为爬虫访问量太大而导致服务不可用。一种办法是通过限流解决,另一种是在负载均衡上将爬虫路由到单独的集群,从而保证正常流量可用,爬虫流量也可用。
二、超时与重试机制
在实际开发中,有许多故障时因为没有设置超时或者设置得不对而造成的。而这些故障都是因为没有意识到超时设置的重要性而造成的。如果应用不设置超时,则可能会导致请求响应慢,慢请求累积导致连锁反应,甚至造成应用雪崩。所以才需要将每个节点合适的设置好超时。
代理层超时与重试
对于代理层我们以Nginx为案例进行学习。
Nginx主要有4类超时设置:客户端超时设置、DNS解析超时设置、代理超时设置,还有lua相关的超时设置。
客户端超时设置
client_header_timeout time
设置读取客户端请求头超时时间,默认60S。如果在这个时间内客户端没有发送完请求头,则响应408给客户端。
client_body_timeout time
设置读取客户端内容提超时时间,默认60S。此超时时间指的是两次成功读操作间隔时间,而不是发送整个请求的超时时间。
send_timeout time
设置发送响应到客户端的超时时间,默认60S。此超时时间指的是两次成功写操作间隔时间,而不是发送整个响应的超时时间。
keepalive_timeout timeout [header_timeout]
设置HTTP长连接超时时间。第一个参数timeout是告诉Nginx长连接超时时间,默认75S。第二个参数hearder_timeout用于设置响应头"Keep-Alive:timeout=time",即告知客户端长连接超时时间。Httpclient框架不设置响应头超时时,默认为永久,设置为0则表示禁用。
DNS解析超时设置
resolver_timeout
设置DNS解析超时时间,默认为30S。如果IP修改了Nginx配置的服务器列表不会自动更新,所以需要借助脚本进行IP更换,DNS解析超时时,会切换到新的IP中。
代理超时设置
proxy_connect_timeout time
与后端/上游服务器建立连接的超时时间,默认60S,此时间不超过75S。
proxy_read_timeout time
与后端/上游服务器读取响应的超时时间,默认60S,此事件只两次成功读操作间隔时间,而不是读取整个响应体的超时时间。
proxy_send_timeout time
与后端/上游服务器发送请求的超时时间,默认60S,此事件只两次成功写操作间隔时间,而不是读取整个响应体的超时时间。
proxy_next_upstream error | timeout | invalid_header | http_500 | http_502
配置什么情况需要请求下一台上游服务器进行重试。默认为"error_timeout"。error表示与上游服务器建立连接,写请求或者读响应头出错。timeout表示与上游服务器建立连接,写请求或者读响应头超时。invalid_header表示上游服务器返回空的或错误的响应头。http_xxx表示上游服务器返回特定的状态码。
proxy_next_upstream_tries number
设置重试次数,默认0表示不限制,注意此重试次数指的是所有请求次数(包括第一次和之后的重试次数之和)。
proxy_next_upstream_timeout time
设置重试最大超时时间,默认0表示不限制。
以上配置,表示当error/timeout时重试upstream中的下一台上游服务器,如果重试的总时间超过了6S或者重试了1次,则表示重试失败,Nginx结束重试并返回客户端响应。
max_fails和fail_timeout
配置什么时候Nginx将上游服务器认定为不可用/不存活。当上游服务器在fail_timeout时间内失败了max_fails次,则认为上游服务器不可用/不可活。并且接下来的fail_timeout时间内请求不会转发到该上游服务器。
如上图,在10S内失败了2次,接下来10S请求不会转发到该服务器中。
max_faild设置为0表示不检查服务器是否可用,表示永远不会被认为不可用,即一直都是可用的。
WEB容器超时
这里以tomcat为例子。
connectionTimeout
配置与客户端建立连接的超时时间,从接受到连接后,在配置的时间内没有接收到客户端的请求航,将被认定为连接超时,默认为60S。
socket.soTimeout
从客户端读取请求数据的超时时间,默认60S。
asyncTimeout
Servlet3异步请求的超时时间,默认30S。
disableUploadTimeout和connectionUploadTimeout
disableUploadTimeout默认为true,但设置为true时,文件上传则以connectionUploadTimeout作为超时时间。
keepAliveTimeout和maxKeepAliveRequests
keepAliveTimeout默认为connectionTimeout,配置为-1表示永不超时。maxKeepAliveRequests默认为100S。
数据库客户端超时
JDBC超时设置
connectTimeout:表示等待和MySQL数据库建立socket链接的超时时间,默认值0,表示不设置超时,单位毫秒,建议30000(30S)。
socketTimeout:表示客户端和MySQL数据库建立socket后,读写socket时的等待的超时时间,linux系统默认的socketTimeout为30分钟,可以不设置
连接池超时设置
maxWait:表示从数据库连接池取链接,连接池没有可用连接时的等待时间,默认值0,表示无限等待,单位毫秒,建议60000(60S)。
<!--等待获取连接池时间,不要太大 -->
<property name="maxWait" value = "1500" />
MyBatis查询超时
defaultStatementTimeout:表示在MyBatis配置文件中默认查询超时间,单位秒,不设置则无线等待。
业务超时
任务型
比如淘宝确认订单之后,未去支付,到了一定的时间,会自动取消订单。
服务调用性
比如某个服务的全局超时时间为500ms,单我们有多处服务调用,没出服务调用的超时时间可能都不一样,可以简单的使用Future来解决问题,Futrue是JAVA中多线程并发执行,并能收集所有并发线程执行结果的工具类。
前端Ajax超时
在请求时,带上timeout参数即可。
总结
今天在这里学习了系统设计的隔离,超时与重连机制,可以提高整个系统的可用性和稳定性,需要的时候可以使用。
引用《亿级流量网站架构核心技术-跟开涛学搭建高可用高并发系统》书籍