淘东电商项目(78) -秒杀系统(服务保护)

简介: 淘东电商项目(78) -秒杀系统(服务保护)

引言

本文代码已提交至Github(版本号:dd8742a8348b4c64a8ca794d544a3271e94365a9),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop

秒杀系统的代码在前面博客已经实现了,有兴趣的同学可以参阅下:

秒杀的逻辑代码完成了,剩下还有一个问题,就是关于服务的保护了,本文来讲解。

本文目录结构:

l____引言

l____ 1.服务保护

l________ 1.1 限流

l________ 1.2 降级、熔断、限流概念

l____ 2.代码

l________ 2.1 网关限流

l________ 2.2 服务保护

l____ 3.测试

l________ 3.1 网关限流测试

l________ 3.2 服务保护测试

1.服务保护

服务保护在《互联网并发与安全》栏目有讲解过,有兴趣的同学可以参阅下:

下面来简单的描述下。

1.1 限流

常见限流算法常用的限流算法有:令牌桶算法、漏桶算法

  • 令牌桶算法:在秒杀活动中,用户的请求速率是不固定的,这里我们假定为10r/s,令牌按照5个每秒的速率放入令牌桶,桶中最多存放20个令牌。仔细想想,是不是总有那么一部分请求被丢弃。
  • 漏桶算法:漏桶算法的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量

市面上常用实现限流框架:

  • Nginx+Lua、Guava、hystrix等

1.2 降级、熔断、限流概念

服务雪崩效应:服务雪崩效应产生与服务堆积在同一个线程池中,因为所有的请求都是同一个线程池进行处理,这时候如果在高并发情况下,所有的请求全部访问同一个接口,这时候可能会导致其他服务没有线程进行接受请求,这就是服务雪崩效应效应。

服务降级:在高并发情况下,防止用户一直等待,使用服务降级方式(直接返回一个友好的提示给客户端,调用fallBack方法)。

服务熔断:熔断机制目的为了保护服务,在高并发的情况下,如果请求达到一定极限(可以自己设置阔值)如果流量超出了设置阈值,让后直接拒绝访问,保护当前服务。使用服务降级方式返回一个友好提示,服务熔断和服务降级一起使用。

服务隔离:因为默认情况下,只有一个线程池会维护所有的服务接口,如果大量的请求访问同一个接口,达到tomcat 线程池默认极限,可能会导致其他服务无法访问。

解决服务雪崩效应:使用服务隔离机制(线程池方式和信号量),使用线程池方式实现隔离的原理: 相当于每个接口(服务)都有自己独立的线程池,因为每个线程池互不影响,这样的话就可以解决服务雪崩效应。

  • 线程池隔离:每个服务接口,都有自己独立的线程池,每个线程池互不影响。
  • 信号量隔离:使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器+1,请求返回成功后计数器-1。

2.代码

2.1 网关限流

网关限流,淘东电商项目采用的是基于谷歌RateLimiter实现限流。Google的Guava工具包中就提供了一个限流工具类——RateLimiter,本文也是通过使用该工具类来实现限流功能,RateLimiter是基于“令牌桶算法”来实现限流的。之前的的网关是使用“责任链设计模式”来重新构造了,本文也使用责任链模式添加RateLimiter来实现限流。

①添加 guava maven依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
</dependency>

②添加限流Handler:

/**
 * description: 服务限流
 * create by: YangLinWei
 * create time: 2020/5/26 3:00 下午
 */
@Component
@Slf4j
public class CurrentLimitHandler extends BaseHandler implements GatewayHandler {
    private RateLimiter rateLimiter = RateLimiter.create(1);
    @Autowired
    private GenerateToken generateToken;
    @Override
    public Boolean service(RequestContext ctx, String ipAddres, HttpServletRequest request, HttpServletResponse response) {
        // 1.用户限流频率设置 每秒中限制1个请求
        boolean tryAcquire = rateLimiter.tryAcquire(0, TimeUnit.SECONDS);
        if (!tryAcquire) {
            resultError(ctx, "There are too many people snapping up goods now. Please wait a moment!");
            return Boolean.FALSE;
        }
        // 2.使用redis限制用户访问频率
        String seckillId = request.getParameter("seckillId");
        String seckillToken = generateToken.getListKeyToken(seckillId + "");
        if (StringUtils.isEmpty(seckillToken)) {
            log.info(">>>seckillId:{}, The second kill has sold out, please come again next time!", seckillId);
            resultError(ctx, "The second kill has sold out, please come again next time!");
            return Boolean.FALSE;
        }
        if (gatewayHandler != null) {
            gatewayHandler.service(ctx, ipAddres, request, response);
        }
        return Boolean.TRUE;
    }
}

③工厂定义网关过滤步骤:

public class FactoryHandler {
    public static GatewayHandler getHandler() {
        // 1.黑名单拦截
        GatewayHandler handler1 = (GatewayHandler) SpringContextUtil.getBean("blackListHandler");
        // 2.API接口参数接口验签
        GatewayHandler handler2 = (GatewayHandler) SpringContextUtil.getBean("verifySignHandler");
        handler1.setNextHandler(handler2);
        // 3.参数过滤
        GatewayHandler handler3 = (GatewayHandler) SpringContextUtil.getBean("filterParamHandler");
        handler1.setNextHandler(handler3);
        //4.服务限流
        GatewayHandler handler4 = (GatewayHandler) SpringContextUtil.getBean("currentLimitHandler");
        handler3.setNextHandler(handler4);
        //5.验证accessToken
        GatewayHandler handler5 = (GatewayHandler) SpringContextUtil.getBean("apiAuthorityHandler");
        handler4.setNextHandler(handler5);
        return handler1;
    }
}

2.2 服务保护

①添加Hystrix maven依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

②启动类开启Hystrix

③服务接口保护

@Override
@Transactional
@HystrixCommand(fallbackMethod = "spikeFallback")
public BaseResponse<JSONObject> spike(String phone, Long seckillId) {
  // 1.参数验证
  if (StringUtils.isEmpty(phone)) {
    return setResultError("手机号码不能为空!");
  }
  if (seckillId == null) {
    return setResultError("商品库存id不能为空!");
  }
  // 2.从redis从获取对应的秒杀token
  String seckillToken = generateToken.getListKeyToken(seckillId + "");
  if (StringUtils.isEmpty(seckillToken)) {
    log.info(">>>seckillId:{}, 亲,该秒杀已经售空,请下次再来!", seckillId);
    return setResultError("亲,该秒杀已经售空,请下次再来!");
  }
  // 3.获取到秒杀token之后,异步放入mq中实现修改商品的库存
  sendSeckillMsg(seckillId, phone);
  return setResultSuccess("正在排队中.......");
}
private BaseResponse<JSONObject> spikeFallback(String phone, Long seckillId) {
  return setResultError("服务器忙,请稍后重试!");
}

3.测试

3.1 网关限流测试

浏览器访问http://localhost/api-spike/spike?phone=13800000001&seckillId=100001,可以看到:

在1秒钟之内继续访问,可以看到限流如下:

3.2 服务保护测试

服务保护测试采用JMeter来测试,由于测试条件的不允许,本文不再演示。如果要看演示的效果,可以参考之前写的博客《微服务技术系列教程(22) - SpringCloud- 服务保护机制Hystrix》

目录
相关文章
|
6月前
|
数据挖掘 黑灰产治理
排队免单商城系统开发详细案例/方案项目/源码指南
排队免单商城系统开发设计是指开发一种商城系统,其中用户可以通过排队活动获得商品免单的机会。
|
缓存 前端开发 安全
淘东电商项目(73) -秒杀系统(前端优化)
淘东电商项目(73) -秒杀系统(前端优化)
129 0
|
开发者
淘东电商项目(52) -聚合支付开篇
淘东电商项目(52) -聚合支付开篇
60 0
|
数据库
淘东电商项目(54) -银联支付案例(同步与异步)
淘东电商项目(54) -银联支付案例(同步与异步)
106 0
|
SQL 前端开发 Java
淘东电商项目(74) -秒杀系统(库存超卖解决方案)
淘东电商项目(74) -秒杀系统(库存超卖解决方案)
152 0
|
前端开发 NoSQL 算法
淘东电商项目(77) -秒杀系统(小结)
淘东电商项目(77) -秒杀系统(小结)
43 0
|
消息中间件 NoSQL 前端开发
淘东电商项目(76) -秒杀系统(完整代码实现)
淘东电商项目(76) -秒杀系统(完整代码实现)
99 0
|
数据库
淘东电商项目(56) -支付系统分布式事务的解决方案
淘东电商项目(56) -支付系统分布式事务的解决方案
88 0
|
前端开发 Java 应用服务中间件
淘东电商项目(53) -银联支付案例源码分析
淘东电商项目(53) -银联支付案例源码分析
79 0
|
设计模式 Java Maven
淘东电商项目(60) -聚合支付(集成支付宝)
淘东电商项目(60) -聚合支付(集成支付宝)
71 0