黄素萍,刘敏娜,朱亚兵,田知佩
(咸阳师范学院 计算机学院,陕西 咸阳 712000)
便捷的网络使得互联网用户基数越来越大,并发请求量随之增多。当大量用户通过网络同时访问同一站点时, 就会出现高并发的情况,Web 系统性能瓶颈也会随之出现。相对于用户对服务性能提出的更高要求, 服务器对Web 访问请求处理速度的提升却远远低于应用服务的增长需求,单靠提升服务器硬件性能已不能满足一些Web 应用系统[1]的需求。 目前,大型分布式系统采用的高并发解决方案并不适用部署在单服务器上的中小型Web 应用。通过集群等方式处理高并发流量的方案成本过高[2]。 因此,需要为部署在单服务器的Web 应用提出可用的网络流量高并发优化处理方案。
Web 应用系统在大量用户进行访问的情况下,易出现系统响应的时间过长、系统崩溃和瘫痪等现象。同时,随着社会不断发展,使用互联网的用户越来越多。 所以,要对Web 应用系统功能进行优化,以保证Web 应用系统的服务质量。
解决高并发的一些常用关键技术包含:
(1)缓存技术。它是把数据库的数据存入缓存,请求不经过数据库,直接从缓存中读取数据,能够大大提高效率。 其中,Redis 是单线程的 NoSQL[3]技术,单线程注定了它是线程安全的。并且,它作为内存技术, 访问和处理速度都远大于如MySQL 等硬盘技术。
(2)消息队列技术。RabbitMQ 是目前使用最多的消息队列。它典型的使用场景就是在大量访问涌入时,服务器可能无法及时处理需要进行业务削峰时发挥作用。
(3)Zookeeper 技术。Zookeeper 是一门分布式技术。 它通过序列节点机制实现分布式锁,能够解决多个线程竞争同一个共享资源时的线程安全问题。
通过研究Web 系统,为了应对高并发问题,可将结合 Redis 缓存、RabbitMQ 消息队列、Zookeeper的不可重入锁等中间件技术的实现方案, 应用于Web 应用系统产生高并发访问功能的模块,以保证系统的快速响应,解决异步消息、流量削峰和线程安全等问题。 其中,使用Redis 对信息存取处理进行缓存。 同时, 结合RabbitMQ 消息队列技术,把Redis 处理的大量请求,不直接请求数据库访问,采用异步操作方式,先进入消息队列,然后排队处理,将瞬间并发访问变成一段时间正常访问数据库。其中,消息队列可以解决应用耦合、异步消息、流量削峰等的问题,实现系统的高性能、高可用和可伸缩等特点。 另外,通过Zookeeper 实现一个分布式锁来解决线程安全问题。
为应用以上高并发技术解决方案, 现设计实现一个商品秒杀系统。该系统的用户分为两类:管理员和商品秒杀用户。系统的业务功能有秒杀管理、商品管理、订单管理、客户管理、人员管理、购物中心和个人中心等功能模块。 系统功能模块如图1 所示。
图1 系统功能模块Figure 1 System function module
系统中,管理员功能模块分为秒杀管理、订单管理、商品管理、客户管理和人员管理模块。
(1)秒杀管理:分为秒杀信息的添加、修改、删除和查询功能。
(2)商品管理: 包括商品基本信息管理、商品类别信息管理和库存管理。
(3)订单管理:包括订单的查询、删除、出货功能。
(4)客户管理:包含对客户信息的查看、删除功能。
(5)人员管理:包括管理员的信息查询、添加、删除和修改。
商品秒杀用户功能模块分为注册登录、购物中心和人个中心。
(1)注册登录:未注册用户可进行信息填写并注册,已经注册用户可输入账号、密码、验证码进行登录。
(2)购物中心:包括所有商品、限时抢购、即将开始商品查看功能。
(3)个人中心:包括我的订单、地址管理、个人信息功能。
本系统设计的数据库实体主要有商品信息、库存信息、订单信息、秒杀活动信息等。 针对这些实体, 商品秒杀系统的数据库创建了对应的数据表。其中,与秒杀功能有关的主要表字段设计如下:
(1)商品数据表:字段有ID、商品名、类别ID、图片地址、是否上架、商品描述、差价、创建时间、更新时间、逻辑删除。
(2)库存数据表:字段有 ID、商品 ID、进价、成本价、售价、数量、预警量(默认10)、快递费用、是否删除。
(3)秒杀活动数据表:字段有ID、库存ID、收藏人数、剩余数量、总数、销售价格、开始时间、结束时间、创建时间、修改时间、是否删除。
系统中包含多个模块,每个功能模块又包含多个子功能。本系统主要研究的是处理高并发功能的实现,下面将着重阐述应用高并发技术方案的核心模块——商品秒杀用户购物中心模块。购物中心模块包括:商品浏览、商品查询、限时抢购三个功能。限时抢购功能的设计中应用了高并发处理方案,其他功能不再赘述。
限时抢购是出现高并发的核心功能。它涉及大量用户同时操作时的库存安全、系统可用性、单用户多次操作的幂等性、要避免用户点击一次产生多笔相同订单和商品超卖等问题。该功能的实现逻辑是,当秒杀用户参与秒杀操作提交请求时,系统程序首先判断当前商品的库存是否充足。若库存不充足,返回商品售空提示;若库存充足,程序申请一个基于Zookeeper 的不可重入锁,如果申请成功进入队列排队等待执行,否则返回秒杀失败提示。 队列中的任务会依次执行, 执行到的任务会先从Redis拿出进入队列前生成的订单ID, 进行实际的下单操作,下单成功后将保存数据库的操作放入消息队列,返回下单成功提示,并跳转到支付界面。功能实现的流程如图2 所示。
图2 限时抢购功能流程图Figure 2 The flow chart of the limited-time snap-up function
由于本系统是使用SpringBoot 框架来实现的,在此功能的实现中,首先在SpringBoot 框架的pom.xml 配置文件里加入依赖整合, 使用Redis 对商品库存进行缓存,以减小查询数据库的压力。 用户每次进行商品秒杀时, 系统程序通过调用AddOrder-Controller 添加订单控制类的 killGoods()方法,使用 其 中 的 语 句 redisTemplate.opsForValue ().get(RedisEnum.USER_KILL_NUM + id + goodsId)从缓存中获取商品库存。但这一步并不能完全解决此时的高并发问题,还需要应用RabbitMQ 消息队列技术,通过Redis 的大量请求,不直接请求数据库下单采用异步下单方式,调用rabbitTemplate. convertAndSend()方法先进入消息队列,然后排队消费,可将瞬间并发访问变成一段时间正常访问数据库。 同时,通过Zookeeper 的序列节点机制来实现一个分布式锁, 调用AddOrderServiceImpl 添加订单服务类的killOrder()方法。 在该方法使用lock.acquire(60,TimeUnit.SECONDS)的结果来判断是否获取到锁,解决多个线程竞争同一个共享资源时候的线程安全问题。 基于SpringBoot 框架,系统实现限时抢购功能的业务关系如图3 所示。
图3 限时抢购功能实现的业务关系Figure 3 The business relationship of the flash sale function
通过以上几个环节, 本功能将Redis、Rabbit-MQ、Zookeeper 这几种中间件技术应用到具体实现中,解决了用户在商品秒杀产生高并发情况下的异步消息、流量削峰和线程安全问题,实现了系统的快速响应。
在系统开发完成之后, 使用了开源测试工具JMeter 对系统实现高并发处理商品秒杀功能模块中的限时抢购功能进行了性能测试。JMeter 是开源测试工具,其运行原理是通过线程组来驱动多个线程的方式运行,可以进行接口测试、性能测试、接口及性能的自动化测试。
在JMeter 中, 通过不断增加线程数模拟多个用户同时秒杀同一件商品。线程总共启动时间设置为0 表示并发,随着线程数量不断增加,吞吐量在线程数为600 时为987.8/sec。线程数为600 时的聚合报告如图4 所示。之后,随线程数增加,吞吐量开始逐渐降低并且平均响应时间变长,当线程数逐渐增大至650 时,吞吐量为866.4/sec。 可以看出系统进行商品秒杀操作能支持的最大的用户数为600。
图4 线程数为600 时的聚合报告Figure 4 Aggregate report with 600 threads
另外,在系统的限时抢购功能中,针对高并发方案对应程序进行相应的注释和修改后,获得了功能实现的常规方案程序。对不应用本文介绍的高并发案的常规方案实现的功能程序进行测试发现,在多个用户同时秒杀同一件商品时,当线程数逐渐增大至300 时,吞吐量为875.5/sec。 之后随线程数增加吞吐量逐渐降低,同时平均响应时间变长。 限时抢购功能性能测试如表1 所示。
本系统测试时,秒杀系统运行在普通性能的笔记本电脑上,当程序按常规方案实现时,系统的高性能并发承载量最高为300 个用户。在应用了高并发技术方案后,系统的高性能并发承载量最高可达600 个用户。 这说明此方案从软件实现的角度大大提升了Web 应用系统的并发性和吞吐量, 同时保证了系统的可用性。
本文研究了高并发的优化技术,提出了基于限流、缓存、消息队列等技术结合的方案,设计实现了商品秒杀系统。该系统中的限时抢购功能通过应用高并发技术方案,大大改善了系统在较高并发访问时的并发性和吞吐量,即使在大量用户进行访问的情况下也不易出现系统响应时间过长、系统崩溃和瘫痪等现象,能够保证用户有较好的应用体验。 由于本系统的实现和测试是在普通性能笔记本做为服务器的条件下完成的, 今后还可以从服务器性能、数据库软件性能、网络操作等方面进行进一步优化。
表1 限时抢购功能性能测试表Table 1 Time-limited snap-up function performance test table