用于排队叫号系统的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:
图1
测试结果图2:
image2
然后测试一下插队功能,在冯秀娟后面插入test
查看RedisManager里的结果:
image3
test成功插队。

posted @ 2022-02-25 11:52  cee_nil  阅读(1543)  评论(1编辑  收藏  举报