这篇“java怎么实现高性能的秒杀系统”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我免费云主机域名们一起来看看这篇“java怎么实现高性能的秒杀系统”文章吧。首先来看看最终架构图:先简单根据这个图谈下请求的流转,因为后面不管怎么改进,这些都是不变的:前端请求进入 Web 层,对应的代码就是 Controller。之后将真正的库存校验、下单等请求发往 Service 层,其中 RPC 调用依然采用的 Dubbo,只是更新为***版本。Service 层再对数据进行落地,下单完成。***制抛开秒杀这个场景来说,正常的一个下单流程可以简单分为以下几步:校验库存扣库存创建订单支付基于上文的架构,我们有了以下实现,先看看实际项目的结构:还是和以前一样:提供出一个 API 用于 Service 层实现,以及 Web 层消费。Web 层简单来说就是一个 Spring MVC。Service 层则是真正的数据落地。SSM-SECONDS-KILL-ORDER-CONSUMER 则是后文会提到的 Kafka 消费。数据库也是只有简单的两张表模拟下单:Web 层 Controller 实现:其中 Web 作为一个消费者调用看 OrderService 提供出来的 Dubbo 服务。Service 层, OrderService 实现,首先是对 API 的实现(会在 API 提供出接口):这里只是简单调用了 DBOrderService 中的实现,DBOrderService 才是真正的数据落地,也就是写数据库了。DBOrderService 实现:预先初始化了 10 条库存。手动调用下 createWrongOrder/1 接口发现:库存表订单表一切看起来都没有问题,数据也正常。但是当用 JMeter 并发测试时:测试配置是:300 个线程并发。测试两轮来看看数据库中的结果:请求都响应成功,库存确实也扣完了,但是订单却生成了 124 条记录。这显然是典型的超卖现象。其实现在再去手动调用接口会返回库存不足,但为时晚矣。乐观锁更新怎么来避免上述的现象呢?最简单的做法自然是乐观锁了,来看看具体实现:其实其他的都没怎么改,主要是 Service 层:对应的 XML:同样的测试条件,我们再进行上面的测试 /createOptimisticOrder/1:这次发现无论是库存订单都是 OK 的。查看日志发现:很多并发请求会响应错误,这就达到了效果。提高吞吐量为了进一步提高秒杀时的吞吐量以及响应效率,这里的 Web 和 Service 都进行了横向扩展:Web 利用 Nginx 进行负载。Service 也是多台应用。再用 JMeter 测试时可以直观的看到效果。由于我是在阿里云的一台小水管服务器进行测试的,加上配置不高、应用都在同一台,所以并没有完全体现出性能上的优势( Nginx 做负载转发时候也会增加额外的网络消耗)。Shell 脚本实现简单的 CI由于应用多台部署之后,手动发版测试的痛苦相信经历过的都有体会。这次并没有精力去搭建完整的 CICD,只是写了一个简单的脚本实现了自动化部署,希望给这方面没有经验的同学带来一点启发。构建 Web:构建 Service:之后每当我有更新,只需要执行这两个脚本就可以帮我自动构建。都是最基础的 Linux 命令,相信大家都看得明白。乐观锁更新 + 分布式限流上文的结果看似没有问题,其实还差得远呢。这里只是模拟了 300 个并发没有问题,但是当请求达到了 3000,3W,300W 呢?虽说可以横向扩展支撑更多的请求,但是能不能利用最少的资源解决问题呢?仔细分析下会发现:假设我的商品一共只有 10 个库存,那么无论你多少人来买其实最终也最多只有 10 人可以下单成功。所以其中会有 99% 的请求都是无效的。大家都知道:大多数应用数据库都是压倒骆驼的***一根稻草。通过 Druid 的监控来看看之前请求数据库的情况:因为 Service 是两个应用:数据库也有 20 多个连接。怎么样来优化呢?其实很容易想到的就是分布式限流。我们将并发控制在一个可控的范围之内,然后快速失败这样就能***程度的保护系统。①distributed-redis-tool ⬆v1.0.3因为加上该组件之后所有的请求都会经过 Redis,所以对 Redis 资源的使用也是要非常小心。②API 更新修改之后的 API 如下:这里构建器改用了 JedisConnectionFactory,所以得配合 Spring 来一起使用。并在初始化时显示传入 Redis 是以集群方式部署还是单机(强烈建议集群,限流之后对 Redis 还是有一定的压力)。③限流实现既然 API 更新了,实现自然也要修改:如果是原生的 Spring 应用得采用 @SpringControllerLimit(errorCode=200) 注解。实际使用如下,Web 端:Service 端就没什么更新了,依然是采用的乐观锁更新数据库。再压测看下效果 /createOptimisticLimitOrderByRedis/1:首先是看结果没有问题,再看数据库连接以及并发请求数都有明显的下降。乐观锁更新+分布式限流+Redis 缓存仔细观察 Druid 监控数据发现这个 SQL 被多次查询:其实这是实时查询库存的 SQL,主要是为了在每次下单之前判断是否还有库存。这也是个优化点。这种数据我们完全可以放在内存中,效率比在数据库要高很多。由于我们的应用是分布式的,所以堆内缓存显然不合适,Redis 就非常适合。这次主要改造的是 Service 层:每次查询库存时走 Redis。扣库存时更新 Redis。需要提前将库存信息写入 Redis。(手动或者程序自动都可以)主要代码如下:
这篇文章主要介绍了java如何继承Thread类创建线程类,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Thread 的实现步骤:定义 Thread 的子类,重写 run()方法,run()方法…
免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。