秒杀活动主要涉及的前端页面有活动推广页、商品详情页,涉及到的后端服务主要有商品服务、库存服务、订单服务,简要流程图如下:
Q:为什么要缓存呢? A:缓存的主要目的是为了解决秒杀活动高并发的天然特性,减轻服务的压力。 Q:什么样的数据应该缓存,什么样的数据不应该缓存呢? A:在整个活动过程中不会变的数据缓存,比如商品信息;动态变的数据视情况缓存,比如库存信息。 在这种场景下,缓存可以分为前端页面缓存和接口数据缓存,怎么来实现呢?下面我们来探讨一下缓存的实现方式:
前端缓存主要采用的页面静态化,CDN缓存加速。
库存数据使用redis来缓存,例如:
set stock_{skuId} stockNum
限流的主要是为了防止非法流量对系统的冲击以及正常流量超过预期导致系统的不可用,那么限流主要采用nginx限流和sentinel限流。
NGINX速率限制使用漏斗算法,该算法广泛应用于电信和分组交换计算机网络中,以在带宽受限时处理突发性问题。比方说一个水桶,在水桶的顶部浇水,然后从底部漏水。如果倒水的速度超过漏水的速度,则水桶会溢出。在请求处理方面,水代表来自客户端的请求,存储桶代表队列,根据先进先出(FIFO)调度算法,请求等待处理。漏水表示退出缓冲区以供服务器处理的请求,溢出表示已丢弃且从未得到服务的请求。 配置基本速率限制
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location /login/ {
limit_req zone=mylimit;
proxy_pass http://my_upstream;
}
}
该limit_req_zone指令定义了速率限制的参数,同时limit_req在它出现的上下文中启用了速率限制(在示例中,针对/ login /的所有请求)。
该limit_req_zone指令通常在http块中定义,使其可在多个上下文中使用。它采用以下三个参数:
因此,现在每个唯一IP地址每秒只能对/ login /请求10个请求, 或者更确切地说,不能在前一个IP地址的100ms内对该URL发出请求。 资料来源:rate-limiting-nginx
一个活动的开始我们首先会对这个活动有一个预期值,进行压测最终给出一个比较合理的QPS值,用sentinel对这个QPS进行限流。
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
FlowSlot 会根据预设的规则,结合前面 NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot 统计出来的实时信息进行流量控制。
限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName) 的时候抛出 FlowException 异常。FlowException 是 BlockException 的子类,您可以捕捉 BlockException 来自定义被限流之后的处理逻辑。
同一个资源可以创建多条限流规则。FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。
一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:
资源示例:
public class OrderService {
// 原函数
@SentinelResource(value = "createOrder", blockHandler = "exceptionHandler")
public String createOrder(long s) {
//Do some create order here
return "order 对象";
}
// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
public String exceptionHandler(long s, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at " + s;
}
}
定义规则示例:
private static void initFlowRules(){
List rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("createOrder");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
资料来源:Sentinel 流量控制
当订单服务调用库存服务锁库存的时候,库存服务出现超时或者其它未知的一些异常,那么系统应该做异常降级处理。
示例:
DefaultHttpClient httpClient = new DefaultHttpClient();
int timeout = 5;
HttpParams httpParams = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(httpParams, timeout * 1000);
HttpConnectionParams.setSoTimeout(httpParams, timeout * 1000);
HttpGet getMethod = new HttpGet("锁库存接口");
try {
HttpResponse response = httpClient.execute(getMethod);
} catch (org.apache.http.conn.ConnectTimeoutException connectTimeoutException) {
//执行降级逻辑
} catch (java.net.SocketTimeoutException socketTimeoutException) {
//执行降级逻辑
} catch (Exception e) {
//执行降级逻辑
}
*注:库存服务库存锁定接口必须保证具有幂等性,防止重复锁库存。