淘东电商项目(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》

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值