关于抢购活动技术方案的一些思考

关于抢购活动技术方案的一些思考

  • 前言
  • 抢购活动到底要不要限流?
  • 超时问题的排查过程
  • 关于压测
  • 总结
  • 题外话

前言

去年的合作项目,随着业务量的不断增加,之前的技术方案最终还是被流量给打烂了。所以说技术方案是需要不断地升级和改良的,不该在有点成绩的时候就沾沾自喜,而失去了对业务量增大会造成后续问题的技术敏感性。

抢购活动到底要不要限流?

之前根据活动的数量,在nginx做了第一步的限流措施,当流量达到阈值时,请求只能等待,但合作方的做法是,请求时间超过5秒就当作失败处理,导致了一些时间过长的请求,双方的数据状态是不一致的。造成这个问题的原因有两点,一是沟通不到位,在问题出现前不知道对方有超过5秒就当作失败处理的逻辑。二是根据业务的属性,失败是没有办法做补偿的,必须要保证接受到的请求返回都是成功的。所以这里不适合限流策略。只能去尽可能的提升系统的吞吐量。

超时问题的排查过程

高并发的接口,对于查询操作都是基于缓存,尽量减少对数据库的操作,包括insert语句。当对方提出生产接口超时的情况后,我根据相关的日志输出,发现接口的耗时基本都是毫秒级的,但从nginx的日志来看,确实有响应超过6秒的请求,并且不少。于是我根据异常的处理在日志中搜索,也没有发现有任何的堆栈信息。最后架构师提醒我,可能不是exception,而是error了。果然,活动期间有大量的error日志。
在这里插入图片描述
再跟进去看,问题就很明显了。异常没有捕获到的原因也很简单,SQLTransientConnectionException不属于RuntimeException,所以在异常捕获的地方没有日志。
在这里插入图片描述
下单接口最后在保存订单数据时,等待数据库连接资源时超时了。百度一下相关的错误信息,得出了以下结论。
springboot的HikariDataSource默认配置的线程池大小和核心线程数都是10。这在高并发场景下接口需要同步保存数据是远远不够的。当然,这里的解决方案除了修改数据库连接池配置外,还可以把订单保存做成异步的。这个方案的代码也已经完成,作为了春节的应急方案,但最开始没这么做的原因也很简单,接口同步返回下单成功后,如果用户马上去兑换的话,可能会出现异步的保存还没完成,导致订单不存在的情况。所以目前生产的解决方案还是通过修改数据库连接池的配置来解决这个问题。

关于压测

压测的重要性和必要性不必多说,但全链路的压测在实际工作中确实存在很多的客观的原因无法充分进行。之前进行的压测更多的是对内的接口,对外的接口压测缺失导致了数据库连接池的问题没有被及时发现。并且测试环境的压测报告只能作为生产环境的参考。org.testng包已经可以通过代码去实现多线程的并发压测了,所以测试环境的压测开发人员可以使用单元测试的方式来进行了。

总结

业务级的项目往往会因为沟通造成不少后续的问题,包括结算时间点、对方的异常处理措施等等。以前的流程图更多的是单边的业务逻辑,之后在画流程图时,我会把对方的异常流程也加上,与对方的技术进行多次的确认,减少由于沟通产生的问题。最后在开发结束后,要开始计划生产环境的压测,如果对方不配合,我该怎么去写伪代码来完成生产环境的压测。新的一年要更多的去关注性能上的解决方案,作为开发也要充分掌握压测的能力。

题外话

在这次的问题排查中,引申出了一个新的问题探讨。被@Transactional标注的方法,是在方法开始执行就开启事务,还是在该方法第一次执行数据库操作时才开始事务呢?我和同事两个人意见不一,各有各的理由,最后在请教架构师后,写了一个简单的单元测试。并且把日志级别调成trace,数据库连接池设置成1。

    @Transactional
    public void transactionalWhenStartTest(){
     
        log.info("start");
        try {
     
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
        industrialBankOrderService.save(new IndustrialBankOrder());
    }

用postman进行简单压测发现,事务的开启从trace级别的日志中可以看到,是方法执行就进行了标记,但与数据库建立连接开启事务,是在第一次操作数据库时。

你可能感兴趣的:(Java,java)