尽量把用户请求拦截在系统上游进行处理。
一、前端请求拦截
JS层面限制用户在x秒内只能提交一次请求,提交后按钮置灰不可操作,提示操作频率过快请稍后再试,从而降低系统负载。
二、站点层请求拦截
如何扛住程序循环式的http接口调用。首先确定用户的唯一标识,对于频繁访问的用户予以拦截。比如用户的ID、请求的IP,对同一个用户限制5秒才能透过1个请求,多余的请求返回上次成功请求的页面或信息。
三、服务层请求拦截
在清楚业务库存和数据库抗压能力的情况下,可以根据具体情况削峰限速。如果库存只有100,但是进来了100000个请求,此时都去查数据库是没有意义的,这时只放行最前面的100个请求,其余的返回“已售罄”。同时可以引入消息队列,将请求放入队列进行后面的逻辑处理。
四、数据库
数据库可以做分库分表、读写分离来分摊单个数据库的压力。
以上已经拦截了绝大多数的非正常请求,打到数据库的请求都是可控的。如果真实请求还是有很多,超过每秒百万级别,可以水平扩展服务器提供支撑。
以下是架构图
域名映射多个LVS或F5的负载均衡服务器IP,通过DNS轮询来线性扩展入口。LVS通过IP负载均衡把请求转发到nginx,nginx再通过路径拦截将不同的请求转发到具体业务处理服务,每一层都可以水平扩展,不存在单点接入问题,从而实现系统的高性能、高可用、高可靠。
那么有人可能会问:上图中的nginx层是否多余,是否可以去掉?直接通过LVS转发到tomcat。
答案是否定的。
因为LVS或者F5都是四层负载均衡,如果从应用层发起一个请求会在「传输层」,「网络层」,「数据链路层」分别加上各自层的包头。比如现在 A 电脑要发一个「I‘m Deepon」数据给 B 电脑,则在各层的转化流程如下图所示
但最终在互联网上要传输的包(数据链路层传输的包叫祯,统称为包)是有大小限制的,如下图所示
在互联网上传输的包不能超过 14 + 20 + 20 + 1460 + 4 = 1518 byte,其中包含的应用层(即 payload)数据一次性不能超过 1460 个 byte,也就是说如果一个 HTTP 请求有 2000 byte,那么它必须分成两个包发送才能在网络上传输。如果一个 HTTP POST 请求很大,超过了 1460 byte(一个包 payload 的最大值),那么它必须分成两个包才能传输,也就意味着一个包可能包含 URI,另一个包不包含 URI,不包含URI 就无法转发给相应的集群。其实最关键的原因是四层以下其实只负责包的转发,只要拿出包头查看一下 ip 地址就可知道该转发哪里,很高效,让客户端和服务端直接建立连接,但是不能做一些其他的配置。
但是基于第七层的nginx负载均衡可以根据URI配置不同的转发规则,如果有多个分包,由于 Nginx 与 client 建立了 TCP 连接,可以在 Nginx 先拿到 client 发出的所有的分包再组装成完整的报文, 然后根据 url 选择其中一台 server 与之建立 TCP 连接后将数据分批完整地传给tomcat服务器。