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成功插隊。