高职院校选课系统性能优化研究与实现

2023-03-29 08:18
智库时代 2023年8期
关键词:课程班令牌序列化

倪 明

(江阴职业技术学院)

一、背景

随着我国高职教育的迅速发展,我校招生规模逐年扩大,为了适应这些变化,必须改变以往僵硬的教学体系,推进学分制是我校教学管理改革的一个重要举措。根据实际情况,我校没有采用纯学分制,而是选择了学年学分制。

学生选课是学分制的前提和基础,目前我校选修课主要分为公共选修课、专业选修课及体育选修课等几种。随着我校数字化校园的建设和发展,学生选课已经从人工选课转换为网络选课。如何提高选课系统的性能,为学生选课提供更好的服务,成为选课系统的一项关键指标。

二、现状与原因

我校选课系统问题主要出现在公共选修课及体育选修课,学生在集中选课的过程中,系统经常无法响应,造成学生无法正常选课。分析其原因,主要存在以下两方面的问题:

(一)选课的高并发,造成系统无法响应

图1 负载均衡

选课过程中,热门课程必定成为学生优先的选择。由于选修课人数限制,学生必然会在选课系统开放的同时进行选课,短时间内大量学生的涌入,这是一个高并发的过程,当系统无法在这个时间段抗住高并发,就会造成系统无法响应。

(二)选课约束条件复杂,造成系统负担过重

选课约束条件复杂体现在多个方面,如选课学分约束、选课门数约束、学生专业约束、上课时间约束、选修前导课约束、选修课人数等。如此多的约束条件处理,必将消耗大量系统资源,造成系统运行缓慢。

三、解决方案

(一)服务器集群与负载均衡

如果只用一台服务器,服务器处理的并发数是有限的,选课系统开启后,大量学生进入选课,Tomcat服务器一般默认只能开启150个线程来处理并发任务,一旦并发数超过这个线程,新的请求只能排队等候处理。这里我们采用Nginx组为负载均衡服务器,Nginx服务器将所有请求分配到多个Tomcat服务器。

Nginx主要有轮询、最少连接数、加权、IP-Hash四种负载均衡策略。轮询负载策略是将请求轮流发送到响应的Tomcat服务器,最少连接数负载策略是将请求发送到当前连接数最少的Tomcat服务器,加权负载策略是优先把请求发送到权重高的Tomcat服务器。以上三种策略不能将每个客户端的请求固定分配到同一台Tomcat服务器上,当用户登录信息是保存在某一台服务器上时,用户的下次请求可能就会分配到另外一台服务器上,这时候就需要用户重新进行登录验证,所以我们采用IPHash负载策略,此策略可以将每个客户端的请求随机固定到某一台Tomcat服务器。

(二)课程班信息存入Redis缓存

当选课系统启动时,首先将课程班信息存入Redis缓存。

课程班信息包括课程班Id、课程名称、学分、课程类别、任课教师Id、任课教师姓名、限选人数、已选人数、未选人数、上课时间、上课地点、选修状态等相关信息,并为课程班添加UUID随机码加密链接。这些课程班信息通过Redisson生成一个分布式信息量,信息量的Key为UUID随机码,Value为课程班的限选人数。

学生选课时,为了防止恶意请求,将选课系统开启时产生的UUID作为课程班信息的关键字,禁止使用课程班Id作为关键字,这样就能防止学生在选课系统未开启时进行抢课。

选课开始产生的选课的课程班信息及每个课程班的分布式信息量需要存入Redis缓存。存入Redis缓存一般有Key-Value和Hash两种形式。

采用Key-Value形式又能分为两种方法。

第一种方法,将课程班UUID作为Key,课程班的其它信息作为一个序列化对象作为Value存储,如图2所示。

图2 Key-序列化Value存储

采用这种方法的缺点是,学生选课后,修改选课人数时,需要将整个Value对象取出,增加序列化/发序列化开销,并且在修改操作中需要对数据进行并发保护。

第二种方法是将课程班信息按课程班UUID+数据项存成多个Key-Value,如图3所示。

图3 Key-Vlaue存储

采用这种方法可以解决第一种方法中序列化/反序列化的开销问题,但是存在大量课程班UUID重复数据,存储空间浪费较大。

采用以上两种Key-Value形式存储时虽然实现比较简单,但存在系统开销比较大、存储空间浪费等缺点,采用Hash形式能很好的解决这些问题。

Hash形式将课程班信息数据项作为一个HashMap存入,并且Redis提供了直接存取Map成员的接口,如图4所示。

从图4可以看出,Key仍然是课程班UUID,value是一个Map,这个Map的field是课程班信息数据项的属性名,value是数据项的值,这样对课程班信息的修改和存取都可以直接通过其内部Map的field,也就是通过Key(课程班UUID)+field(数据项标签)就可以操作对应数据了。这样就解决Key-Value序列化/反序列化开销过大,在修改操作中需要对数据进行并发保护以及重复存储课程班UUID数据的问题。

图4 Hash存储

由于选课系统部署在Tomcat服务器集群,每个Tomcat服务器都会同时把数据存入Redis缓存,为了保证Redis中只存在一条线程的缓存数据,因此在缓存前需要判断Key是否已经存在,并且还需要通过Redisson加分布式锁,由于多线程同时缓存只会在选课系统开启时出现,因此不需要采用默认的看门狗机制,只需要对分布式锁设置超时时间,超时后自动解锁。

当学生点击确认选课按钮后,经过Nginx网关过滤,来到Tomcat选课接口。首先经过身份验证,验证通过后获取学生Id,然后从Redis中获取选课开始时间和结束时间,用来判断请求是否在可选课时间段内。以上验证通过后,查询Redis中选课状态Hash结构中是否存在该学生Id及所选课程班Id,不存在则为该学生选择了该课程班,存在则为该学生退选了该课程班,然后修改该课程班的分布式信息量。

在学生点击确认选课按钮时,由于前端卡顿无法及时设置按钮灰色不可使用,学生有可能会连续点击多次按钮,造成一瞬间发起多个请求,从而造成连续扣减分布式信息量,结果就是该学生重复多次选择了该课程班。

这里我们采用Token令牌来解决这个幂等性问题。首先在学生点击选课按钮时,在返回给课程班详细信息中生成一个Token令牌,并在Redis中也保存这个Token令牌,设置过期时间如10分钟。学生点击确认选课后,相应的数据和Token令牌都传给后端,后端收到数据后,将数据中的Token令牌和Redis中的令牌进行比较,如果两者相等,则删除Redis中的Token令牌。这个比较删除操作必须作为一个整体执行,中间不能被其它命令插入,因此采用Lua脚本来执行这个原子操作。这样学生一瞬间发起的多个请求,后端接收第一个请求后就会立即删除Redis中的Token令牌,后续请求因无法匹配到相应的Token令牌而失败,从而避免了该学生重复多次选择该课程班。

学生点击确认选课后,为了保证实时性要求,需要在Redis中根据课程班Id和学号Id,在选课状态Hash结构中设置为“已选”状态,同时根据课程班Id从课程班Hash结构中获取该课程班的信息,再将其结果存入选课信息Hash结构里,其中Key为课程班Id+学号Id,Value为课程班信息。这样学生就能实时查到课程的选修状态及选修了哪些课程。

(三)RabbitMQ消息队列处理选课结果

前面学生选课操作都在Redis缓存中进行,速度较快,但是最终数据还是要写入数据库中。由于对数据库进行读写操作相对于内存读写是一个缓慢的过程,同步把数据写入数据库,这又将成为选课系统的一个瓶颈。

要解决这个瓶颈,首先要做到系统解耦,采用的方式是学生选课成功后生成一个选课单,选课单包括课程班Id、学号Id、选课状态(选课、退选)以及用于解决可能存在的消息乱序问题而加入的创建时间,然后这个选课单传给RabbitMQ消息队列,然后由RabbitMQ消息队列把选课单存入数据库,如图5所示。

图5 RabbitMQ消息队列

RabbitMQ消息发送后可能因为网络原因没有到达Broker或者Broker未持久化消息而宕机。为了解决这个问题,RabbitMQ发送消息之前,首先将相关信息存入Redis,Redis采用AOF持久化,消息状态为未投递并设置投递计数器为0,每次该消息投递后,计数器就加1,Broker收到消息后通过ConfirmCallback回调修改消息为已投递,并通过定时器把投递失败的消息重新发送,当投递计数器到5时,就将该消息交给死信队列进行人工处理。通过这些操作就可以解决RabbitMQ消息丢失的问题,保障消息的传输可靠。

当RabbitMQ消息投递后,可能由于网络原因造成Broker收到消息通过ConfirmCallback回调失败,RabbitMQ会将消息重复投递,从而会产生消息重复发送的问题。此处采用后台接口处理,后台接口接收到数据后,根据课程班Id和学号Id来判断是否接收过此数据,从而保证数据幂等性。

当选课高峰时,系统无法及时处理RabbitMQ中的消息,未及时处理的消息将积压在消息队列中,等选课高峰一过,系统就会把积压的消息处理完成,从而达到消峰的作用。

通过RabbitMQ消息队列解耦、消峰操作,从而解决在学生选课高峰时的高并发操作,顺利把选课数据存入数据库。

四、实现效果

本次选课系统优化的目的是保证大量学生同时选课的情况下系统不宕机,并提高系统的QPS,为学生提供更好的选课体验。为了验证优化效果,采用Apache JMeter5.5进行压力测试。

首先导出系统中5000个学生的学号,用于模拟5000个学生同时选课,并使用JMeter运行测试方案,优化前及优化后的测试结果如表1所示。

表1 JMeter汇总报告

从以上JMeter汇总报告可以得出优化后系统性能得到很大的提升,在学生实际使用中,系统也未出现无法响应甚至宕机,此次优化效果结果比较满意。

五、总结

根据我校选课系统目前存在的问题,在学生选课高峰时,系统容易出现运行缓慢甚至宕机。硬件上采用服务器集群与负载均衡,软件上使用Redis缓存来处理选课时高速的读写操作及使用RabbitMQ消息队列处理选课结果。最后使用JMeter进行压力测试,在学生正式选课中也取得了良好的效果。

猜你喜欢
课程班令牌序列化
称金块
如何建构序列化阅读教学
基于路由和QoS令牌桶的集中式限速网关
西安美术学院艺术金融博士课程班第三次授课举行
2018艺术金融博士课程班开学典礼在西安美院举行
首届中国艺术金融博士课程班毕业典礼举行
动态令牌分配的TCSN多级令牌桶流量监管算法
Java反序列化漏洞探析及其修复方法研究
Java 反序列化漏洞研究
作文训练微格化、序列化初探