Sentinel-秒杀活动

  • 一、背景
  • 二、sentinel是什么
  • 三、sentinel怎么用
    • 1、引入核心库
    • 2、定义资源
    • 3、控制台
    • 4、规则
      • 4.1 流控规则
      • 4.2 降级规则
    • 5、回归秒杀活动
  • 四、sentinel原理简单剖析
  • 五、总结

一、背景

之前在博客中写过秒杀活动开发过程细节,之后用数据库锁简单实现了一次,今天再给大家介绍一个工具Sentinel。

二、sentinel是什么

Sentinel中文含义是哨兵,阿里巴巴给出的定义:Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。我们可以从定义中的简单的认识到该工具就是针对类似秒杀活动一样的高流量、高并发场景下的一个保证服务稳定的工具。具体的介绍请参考:https://github.com/alibaba/Sentinel/wiki。

三、sentinel怎么用

使用sentinel时,我们明确以下概念:
核心库:sentinel以jar包的形式加入到自己工程中,不依赖的其他库
控制台:可以管理资源、定义规则的可视化工具
资源:它就是我们工程中的一个方法、类或者业务服务。sentinel正是要控制资源的工具。很显然在我们的秒杀活动中,获取商品库存的方法即为资源。当有大流量过来时,通过sentinel的控制台指定的规则,来控制这些流量,保证我们的服务稳定性。

1、引入核心库

在自己的工程中引入sentinel-core和sentinel-transport包。

compile 'com.alibaba.csp:sentinel-core:1.6.3'

Transport 模块来与 Sentinel 控制台进行通信。

compile 'com.alibaba.csp:sentinel-transport-simple-http:1.6.3'

2、定义资源

官方给出5种方式:主流框架适配、抛异常方式、返回布尔值方式、注解、异步调用。我们这里使用抛异常方式作为示例,使用SphU提供的方法将我们的资源(获取库存代码块)包裹起来。可能有人好奇为什么没在方法上定义?这里是因为方法还有其他处理逻辑,而这些并不受高并发影响,如果我们将范围扩大至整个方法,那么会导致等个请求变慢。我们只需要在核心的逻辑上定义即可。

// sentinel-1.5后支持try-with-resource特性。CAMPAIGN_CARD_PROMOTION即为我们的资源名
boolean isHaveStock = false;
try (Entry youEntry = SphU.entry("CAMPAIGN_CARD_PROMOTION")) {if (isNewUser && isJulyCampaignDate && isNANJINGUser && !isHadBuyCampaignCard) {try {// 库存数RAtomicLong cardStock = redissonClient.getAtomicLong("JULY_CAMPAIGN_ONE_YUAN_CARD_STOCK");boolean isSuccess = true;if (!cardStock.isExists() || cardStock.get() <= 0) {isSuccess = false;} else if (cardStock.decrementAndGet() < 0) {isSuccess = false;}if (isSuccess) {// 建立用户和库存关系RBucket<Integer> userStock = redissonClient.getBucket("JULY_CAMPAIGN_USER_STOCK_RELATION:" + uid);userStock.set(1, 60, TimeUnit.DAYS);// 订单抢购成功发短信通知threadPoolTaskExecutor.submit(() -> mqService.sendRpcMessageService(uid, MessageActionEnum.PROMOTION, cardServiceConfig.getJulyCampaignSms2(), Maps.newHashMap()));}isHaveStock = isSuccess;} catch (Exception e) {log.error("1元月卡库存操作异常 {} ", uid, e);}}
} catch (BlockException e){// 当抛出阻塞异常,定义自己的异常处理策略
}

3、控制台

控制台是一个springboot工程。我们可以下载jar包或者github下载源码打包。有了jar包,在本地启动。启动命令如下,本人以将下好的包放在Sentinel下。命令中包含Hosts、port、projectName。启动后在浏览器使用localhost:8080访问,默认用户名和密码都是 sentinel。


JVM 参数-Dcsp.sentinel.dashboard.server=consoleIp:port是我们在启动服务时需要添加的,其中consoleIp代表控制台IP,port是端口。我在idea中启动活动服务campaign-service。过几分钟刷新控制台可以看到我们的服务,如下图。红色为我们的服务,蓝色时控制台本身。黄色框为规则配置栏。接下来会介绍如何配置规则。

我们通过postman-runner请求1000次,通过控制台-实时监控查看有什么变化?可以看到当前请求不断发起时,在实时监控看到访问曲线,在8~10左右/QPS浮动。由此也可以看出来服务在本人机器上所能提供的访问极限了。


下图是我们在启动服务后通过postman调用http接口时出现的,代表资源访问日志。
打开相应目录下的日志是如下内容,其中第3列是我们配置的资源名称,其他列含义,自左向右:|–timestamp-|------date time----|–resource-|p |block|s |e|rt,其中 p 代表通过的请求, block 代表被阻止的请求, s 代表成功执行完成的请求个数, e 代表用户自定义的异常, rt 代表平均响应时长。

4、规则

因为调本地请求,响应事件太短,导致控制台响应时间都为0,怕引起误会。为了方便测试,我在获取库存方法内加了线程休眠代码,增加请求响应时长。让每次请求的响应时间随机增加0~50毫秒。

Random random = new Random();
Thread.sleep(random.nextInt(50));

可在实时监控看到,QPS降到6~7左右,响应时间也在我们的预期范围(50ms以内)。接下来我们就以流量规则和降级规则,示例sentinel的规则配置。

4.1 流控规则

首先看簇点链路,如下图。给出了我们的资源名称-CAMPAIGN_CARD_PROMOTION、QPS、以及一分钟内的请求通过数,最右侧则给出了四个控制按钮:流控、降级、热点、授权。其实这四个按钮就对应簇点链路下方的菜单栏,只不过放在这里统一了,每个菜单栏可以看的更详细。

首先,点击流控或者流控规则菜单栏里的增加流控规则按钮,弹窗可以看到阈值类型有QPS或线程数,其含义就是我们使用的限制类型。我们选择QPS,单机阈值指定3,点击新增按钮。含义就是限制QPS<=3,即使请求数过高,请求响应也是在规则指定的范围内。

当我们增加好流量控制规则后,刷新实时监控,可看到左侧的访问曲线,绿色(代表通过的请求数)出现下降趋势并稳定在2和4QPS中间,蓝色(代表拒绝的数量)则由之前的0上升到2和6QPS之间。右侧也可以看到通过QPS都变成了3,拒绝QPS则在3~5之间,二者相加的数值也符合流控前的QPS。

而删除流控规则后,两个曲线也开始回归,右侧的QPS也说明了,不再受流量规则控制。

4.2 降级规则

在配置降级规则前,我们可以看到资源响应平均时间为23ms。这将作为降级配置的参考值。

点击降级按钮,出现如下弹窗。可看到降级策略包含:RT(平均相应事件)、异常比例、异常数。RT(response-time)的含义时影响时间,即一次请求响应时间超过该值,将会被熔断,特别时当服务不稳定时,响应时间忽高忽低,有了这个规则后就可以将不稳定,响应时间过长的拦截。异常比例含义是当资源的每秒请求量 >= 5,并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态。异常数含义当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。我们以RT为例。时间指定为30ms,时间窗口指定为20s。即熔断响应时间超过30毫秒请求,在第一次触发该规则后的20s内,都将会自动熔断。

增加RT降级策略后刷新实时监控,可以看到两条曲线呈现上下交替波动,右侧的QPS下方有通过有拒绝。也就是将响应时间超过30ms,时间窗口在20s的请求熔断。可是图中明明有也请求时间超过30ms的,

5、回归秒杀活动

经过以上分析讲解。再回归到我们的秒杀活动中。库存是用户抢购的热点资源。会有大量的请求在短时间内进来,服务即使在数据库层面、缓存层面的的做很多优化,仍然可能无法抵挡。此时就需要sentinel。当我们在控制台发现抢购商品的流量很大,可能会超过我们在上线前的压测数值,那就必须采取措施,限制请求处理。也正是上面示例sentinel控制访问请求,对请求进行限制、熔断,挡住一部分流量。而且相比于Netflix提供的Hystrix,sentinel可动态配置,弹性更高。

四、sentinel原理简单剖析

该章节简单的分析下sentinel的工作原理。我们在资源保护处会写如下代码:

Entry entry = SphU.entry("resource name");

它的作用是创建entry,同时会创建slot(官方文档解使为插槽)。一个资源会创建多个插槽,每个插槽都有不同的功能,如统计、限流、熔断等,如下图。

当我们创建了资源名称为”CAMPAIGN_CARD_PROMOTION“的entry后,便会自动创建多个插槽,我们以流量控制为例,探究下工作过程。进入SphU.entry方法内,检查资源下所有规则,继续计入entry方法

/**
* 检查资源下的所有规则
*/
public static Entry entry(String name) throws BlockException {return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0);
}

进来后首先是使用StringResourceWrapper对资源做简单的包装,之后执行entry。

@Override
public Entry entry(String name, EntryType type, int count, Object... args) throws BlockException {StringResourceWrapper resource = new StringResourceWrapper(name, type);return entry(resource, count, args);
}

之后我们看到entryWithPriority。我们关注chain.entry()。此处的chain即为封装了多了slot的责任链,其中FlowSlot为其中一个,即实现流量控制。

private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)throws BlockException {Context context = ContextUtil.getContext();if (context instanceof NullContext) {// The {@link NullContext} indicates that the amount of context has exceeded the threshold,// so here init the entry only. No rule checking will be done.return new CtEntry(resourceWrapper, null, context);}if (context == null) {// Using default context.context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);}// Global switch is close, no rule checking will do.if (!Constants.ON) {return new CtEntry(resourceWrapper, null, context);}ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);/** Means amount of resources (slot chain) exceeds {@link Constants.MAX_SLOT_CHAIN_SIZE},* so no rule checking will be done.*/if (chain == null) {return new CtEntry(resourceWrapper, null, context);}Entry e = new CtEntry(resourceWrapper, chain, context);try {chain.entry(context, resourceWrapper, null, count, prioritized, args);} catch (BlockException e1) {e.exit(count, args);throw e1;} catch (Throwable e1) {// This should not happen, unless there are errors existing in Sentinel internal.RecordLog.info("Sentinel unexpected exception", e1);}return e;
}

将进入FlowSlot,找到entry()下的checkFlow()方法。进入方法实现后,checker.checkFlow()开始获取控制台配置的规则,并检查是否可以通过

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,boolean prioritized, Object... args) throws Throwable {checkFlow(resourceWrapper, context, node, count, prioritized);fireEntry(context, resourceWrapper, node, count, prioritized, args);
}void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)throws BlockException {checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);
}

ruleProvider.apple()通过资源名获取规则。如果规则不为空则使用canPassCheck检查是否可以通过,否则抛出FlowException异常。而该异常正是BlockException的子类。

public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource,Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {if (ruleProvider == null || resource == null) {return;}Collection<FlowRule> rules = ruleProvider.apply(resource.getName());if (rules != null) {for (FlowRule rule : rules) {if (!canPassCheck(rule, context, node, count, prioritized)) {throw new FlowException(rule.getLimitApp(), rule);}}}
}

检查是否可以通过的代码。

@Override
public boolean canPass(Node node, int acquireCount, boolean prioritized) {int curCount = avgUsedTokens(node);if (curCount + acquireCount > count) {if (prioritized && grade == RuleConstant.FLOW_GRADE_QPS) {long currentTime;long waitInMs;currentTime = TimeUtil.currentTimeMillis();waitInMs = node.tryOccupyNext(currentTime, acquireCount, count);if (waitInMs < OccupyTimeoutProperty.getOccupyTimeout()) {node.addWaitingRequest(currentTime + waitInMs, acquireCount);node.addOccupiedPass(acquireCount);sleep(waitInMs);// PriorityWaitException indicates that the request will pass after waiting for {@link @waitInMs}.throw new PriorityWaitException(waitInMs);}}return false;}return true;
}

同理,其他槽位指定的功能,也是通过该思路去工作,大家可以阅读源码。

五、总结

经过以上介绍,除了在秒杀活动保护库存资源,那么是不是也可大到保护一个类、一个服务,甚至一个服务集群。比如我们在某服务提供的rpc接口处使用sentinel。再比如我们在整个redis集群调用接口处使用sentinel。而且该工具控制台上显示的信息非常直观,我们可以知道服务QPS、响应时间,统计请求、拒绝数等。总之,本片文章只是个人对Sentinel的初步使用,还有很多地方值得探究。

Sentinel :https://github.com/alibaba/Sentinel

sentinel实现秒杀活动相关推荐

  1. 【硬核】秒杀活动技术方案,Redis申请32个G,被技术总监挑战了...

    作为一名技术从业人员,性能优化是每个人的必修课 就像大学时期给漂亮妹子修电脑的绝招就是"重启电脑一样",性能优化也有自己的必杀技 你一定听过一句话:性能不够,缓存来凑!对,你没听错 ...

  2. 业务总结002:秒杀活动架构设计

    一.秒杀商品模型 二.架构设计 2.1 Redis + MQ 缓存预热:秒杀商品一般时效性比较强,一场秒杀活动持续的时间不会很长,当在后台设置秒杀活动添加秒杀商品时,把商品对应的库存直接存到 Redi ...

  3. 阿里双11大促秒杀活动下的缓存技术与高水位限流实现

    秒杀最早来自天猫双11各种商品的促销活动中,现在已经有很多业务场景在使用,比如抢红包,抢票等.其特点有三高:瞬时并发高,数据一致性高,热点更新频度高.这样三高的场景下往往给数据库造成极大的压力,大量更 ...

  4. 小程序秒杀活动服务器,一套实用的小程序秒杀活动方案,亲这边建议你收藏哦...

    小伙伴们好~今天,上线君为大家给总结的是小程序秒杀活动的使用方案,大部分行业都能够参考使用,大家可以收藏学习. 图片来源:pexels ▼活动前期:确定目标与流程 与团购活动不一样,对秒杀活动来说,时 ...

  5. 华为官方翻新产品秒杀活动来袭,官方正品,7折优惠,真香!

    4月24日,@华为商城 微博官宣,4月26日12:00和20:00在华为商城APP内将举办两场超级秒杀节活动,其中包括7折优惠的2款华为官方翻新手机,分别是nova 9和nova 9 Pro.华为官方 ...

  6. 阿里、百度、美团都在用的‘’高并发秒杀系统‘’;抢红包、秒杀活动、微博热搜、12306抢票等高并发场景

    "秒杀活动"."抢红包"."微博热搜"."12306抢票"."共享单车拉新"等都是高并发的典型业务场 ...

  7. 一度智信:拼多多商家参与秒杀活动好处

    今天小编就给大家分享关于拼多多商家秒杀活动的一些相关内容,操作好了对店铺来说也是大大有益的. 一.什么是秒杀活动 拼多多的活动如果要划分等级的话,秒杀活动算是高级活动,流量短期内会有很大的导入,同样的 ...

  8. 秒杀活动,提高性能,防止超卖,订单超时

    目录 初步思考 秒杀活动 订单防止超卖 订单超时如何处理 初步思考 原文地址 前端:页面尽可能静态化,css/js合并,减少请求数 扩容:增加机器,提高处理请求能力 限流:应用限流(nginx,tom ...

  9. 高并发下商城秒杀活动的处理

    秒杀抢购活动是现在很多商城常见的营销手段,小米抢购.淘宝的整点免单.聚划算等都是成功的例子. 从简单处着手,秒杀是很好理解的:设置要秒杀的商品的数量,抢完为止.但是,实际应用中一瞬间的高并发压力.以及 ...

  10. 怎么参加淘宝秒杀活动?

    淘宝开店教程,不管是新店还是老店都会搞淘宝促销活动.毕竟淘宝促销活动是淘宝运营必不可少的营销手段之一.淘宝促销活动如果做得好的话,那么是可以快速提升店铺销售额的.但是如果做不好,那么就会面临着亏本的局 ...

最新文章

  1. linux 下网络流量监控
  2. mysql yum多实例_centos-7yum 安装 (mairadb) 实现 mysql 多实例
  3. poj2456 Aggressive cows(二分查找)
  4. MySQL查询时通过修改字段的排序规则来忽略大小写的操作讲解
  5. 团队-游戏《石头,剪刀,布》-团队一阶段互评
  6. platform平台总线
  7. 一上来,就问原理,问上亿(MySQL)大表的索引优化,我的天...
  8. 判断链表是否有环,并找出入环点☆
  9. Leetcode-SingleNumberII
  10. php判断简写,怎么简写php 中的三元运算符
  11. 动态加载子节点_简易数据分析 10 | Web Scraper 翻页—抓取「滚动加载」类型网页...
  12. java盛最多水的容器_Leetcode刷题java之11. 盛最多水的容器(top100)
  13. 树莓派搭建局域网内的静态网页
  14. Linux 系统中随机数在 KVM 中的应用
  15. C语言自学-简单的记录
  16. c语言接收rs232串口速率,基于C语言的RS232串行接口通信实现
  17. XJOI一级二段题解(g++,即C++),也可视作C++算法竞赛教程
  18. 对话阿里云弹性计算负责人褚霸:把计算做到极致,关键还不加价!
  19. oracle加密表空间
  20. 为找房方便,将sohu房产的rss 转成全文的!

热门文章

  1. 软件测试工程师...我是如何正确地申请加薪的!
  2. 关于员工的选用任留,我们谈谈留人
  3. apk编辑器android源码,apk编辑器电脑版_apk编辑器电脑版下载[apk编译]- 下载之家
  4. 可自定义存储数据总空间的的类int_128函数库
  5. 浅谈物联网的关键技术和难点
  6. `canvas`破苍穹
  7. python 类调用不存在的方法_找不到Python方法,但在类中定义
  8. Invisible Backdoor Attack with Sample-Specific Triggers 论文笔记
  9. 新事业,新征程:换屏哥,您身边的手机维修专家
  10. TSL237 简介