用于排队叫号系统的redis工具类
1. 分析
排队叫号系统的队列数据变化很频繁,因此可以考虑使用redis的list结构存储某一队列的数据,与前端采用websocekt连接,后端主动推送数据给前端,避免频繁轮询造成资源浪费。
为了满足排队系统的需求,需要设计以下几个api:
- 入队
- 出队
- 获取队列数据和队长
- 允许某人中途离队(已知其信息,从队列中剔除)
- 允许某人插队(在目标anchor前插队还是后插队)
- 得到某人在队列中的位置
2 redis配置和Vo类代码
yml配置
redis:
database: 0
host: 127.0.0.1
port: 6379
timeout: 3000
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
redis配置类
@EnableCaching
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.database}")
private Integer database;
@Value("${spring.redis.port}")
private Integer port;
// @Value("${spring.redis.password}")
// private String pwd;
@Primary
@Bean(name = "jedisPoolConfig")
@ConfigurationProperties(prefix = "spring.redis.pool")
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxWaitMillis(100000);
return jedisPoolConfig;
}
@Bean
public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setDatabase(database);
// redisStandaloneConfiguration.setPassword(pwd);
redisStandaloneConfiguration.setPort(port);
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb = (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration.builder();
jpcb.poolConfig(jedisPoolConfig);
JedisClientConfiguration jedisClientConfiguration = jpcb.build();
return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
}
/**
* 配置redisTemplate针对不同key和value场景下不同序列化的方式
*
* @param factory Redis连接工厂
* @return
*/
@Primary
@Bean(name = "redisTemplate")
public RedisTemplate<String, PatientVo> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, PatientVo> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
Jackson2JsonRedisSerializer redisSerializer = new Jackson2JsonRedisSerializer(PatientVo.class);
template.setValueSerializer(redisSerializer);
template.setHashValueSerializer(redisSerializer);
template.afterPropertiesSet();
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
vo类代码:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PatientVo {
private Long patientId;
private Integer queueNum;
private String patientName;
private String patientGender;
}
3. redis排队工具类
这里写的不好的地方在于没有判断捕获异常。
@Component
public class QueueUtil {
@Autowired
private RedisTemplate<String, PatientVo> redisTemplate;
/**
* 入队
* @param roomCode 诊室编码
* @param vo 病人vo对象
*/
public void addPatient2Queue(String roomCode, PatientVo vo) {
redisTemplate.opsForList().rightPush(roomCode, vo);
}
/**
* 队长
* @param roomCode 诊室编码
* @return Long长度
*/
public Long getQueueLength(String roomCode) {
return redisTemplate.opsForList().size(roomCode);
}
// 获取队列数据
public List<PatientVo> getQueueData(String roomCode) {
return redisTemplate.opsForList().range(roomCode, 0, -1);
}
// 某人中途离开队伍
public void leaveQueue(String roomCode, PatientVo patientVo) {
redisTemplate.opsForList().remove(roomCode, 0, patientVo);
}
// 队首离队
public PatientVo headLeaveQueue(String roomCode) {
PatientVo leftPop = redisTemplate.opsForList().leftPop(roomCode);
return leftPop;
}
// 得到某人队列中的位置
public List<Integer> getOnesPosition(String roomCode, PatientVo patientVo) {
List<PatientVo> queueData = getQueueData(roomCode);
int myPositionBeforeNum = queueData.indexOf(patientVo);
int myPosition = myPositionBeforeNum + 1;
int size = queueData.size();
// 当前排队res[0]人,您排在第res[1]位,前面还有res[2]位。
List<Integer> result = new ArrayList<>();
result.add(size);
result.add(myPosition);
result.add(myPositionBeforeNum);
return result;
}
// 插队操作
public void jumpAQueue(String roomCode, PatientVo jumpChecker, PatientVo targetChecker, Integer jumpType) {
// 在target前面插队
if (jumpType.equals(1)) {
redisTemplate.opsForList().leftPush(roomCode, targetChecker, jumpChecker);
}
if (jumpType.equals(2)) {
redisTemplate.opsForList().rightPush(roomCode, targetChecker, jumpChecker);
}
}
}
4. 简单测试
测试代码
@Autowired
private QueueUtil queueUtil;
@GetMapping("/test/qu")
private Result testQueueUtil() {
long start = System.currentTimeMillis();
List<PatientInfo> list = patientInfoService.list();
for (int i = 0; i < 10; i++) {
PatientInfo info = list.get(i);
PatientVo vo = new PatientVo(info.getPatientId(),
info.getQueueNum(),
info.getPatientName(),
info.getPatientSex());
queueUtil.addPatient2Queue("test", vo);
}
List<PatientVo> queueData = queueUtil.getQueueData("test");
// PatientVo jumpVo = new PatientVo(1999L, 2999, "test", "test");
// PatientVo targetVo = new PatientVo(1058L, 2005, "冯秀娟", "女");
// queueUtil.jumpAQueue("test", jumpVo, targetVo, 2);
// List<PatientVo> queueData = queueUtil.getQueueData("test");
long end = System.currentTimeMillis();
log.info("waste time: {}s", (float)(end - start) / 1000);
return Result.success(queueData);
}
测试结果图1:
测试结果图2:
然后测试一下插队功能,在冯秀娟后面插入test
查看RedisManager里的结果:
test成功插队。