用於排隊叫號系統的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成功插隊。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM