您的位置:首页 > 其它

高并发下合并接口请求

2017-08-29 17:15 507 查看
       在有一次对商品详情页进行压力测试时,因为商详页的数据来源非常多,经过的服务多,调用链很长,所以查询数据库的次数也就非常多,数据库连接池很快就被用光,导致很多请求被阻塞,也导致应用整体线程数非常高。虽然通过增加数据库连接池大小可以缓解问题,并且可以通过压力测试,但这治标不治本。商详页中有很多查询已经做了缓存,但还是有些如促销、(活动)价格、库存等是不能缓存(或是不能缓存太长时间)。

       虽然促销、价格、库存等对实时性要求较高,数据可能变化频繁,但同一个商品的信息在同一时刻的信息应该是基本一致。数据实时性相差个几十毫秒上百毫秒对于用户来说是无感知。可以把和用户有关联的操作(如记录浏览历史)拆分开来,比如在接口层里先调用查询商品信息,查询到了之后再调用记录浏览历史等操作。

       查询商品信息的时候,如果同一商品同一时刻有100个请求,那么其中的99次查询是多余的,可以把100个请求合并成一个真实的后台接口调用,只要控制好线程安全即可。我的想法是使用并发计数器来实现再配合本地缓存,计数器可直接用JDK提供的AtomicInteger,线程安全又提供原子操作。

       以获取商品信息为例,每个商品id对应一个计数器,计数器初始值默认是0,当一个请求过来后通过incrementAndGet使计数器自增1并返回自增后的值。当该值等于1,表明该线程在这个时间点上是第一个到达的线程,然后就去调用真实的业务逻辑,在查询到结果后放入到本地缓存中。当该值大于1的时候,表明之前已有线程正在调用业务逻辑,则进入等待状态,并循环的查询本地缓存中是否已有数据可用。获取到结果后都调用decrementAndGet使计数器减1,计数器被减到0的时候就回到了初始状态,并且当减到0(代表最后一个线程)时清除缓存。

       此方案有个数据延迟的地方,就是每次循环时的等待状态的时间。因为一次包含多次查库的业务调用,耗时基本都在几十毫秒,甚至是上百毫秒,可以把该等待睡眠设置小一点,比如10毫秒。这样即不会浪费CPU时间,实时性也比较高,但然也可以通过主动唤醒等待线程的方式,这个操作起来就比较复杂些。在这其中还可以添加一些异常处理、超时控制、最大重试次数,最大并发数(超时最大并发数就快速失败)等。

------------------------
4000
-----

附上相关代码实现(Github):synchplay/easyJCommon
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: