赞
踩
环境说明
背景说明
实现思路:采用 RedisTemplate, 执行 lua 脚本。
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置 redisTermplate, 配置 lua 脚本,便于 redisTemplate 执行[^2]
@Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { @Bean @SuppressWarnings(value = { "unchecked", "rawtypes" }) public RedisTemplate<String, Object> redisTemplate1(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); RedisSearchSerializer serializer = new RedisSearchSerializer(Object.class); // 使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); // Hash的key也采用StringRedisSerializer的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } // lua 脚本配置 @Bean public DefaultRedisScript<String> jsonSetScript() { DefaultRedisScript<String> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText("return redis.call('JSON.SET', KEYS[1], '$', ARGV[1]);"); redisScript.setResultType(String.class); return redisScript; } @Bean public DefaultRedisScript<Object> jsonGetScript() { DefaultRedisScript<Object> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText("return redis.call('JSON.GET', KEYS[1]);"); redisScript.setResultType(Object.class); return redisScript; } @Bean public DefaultRedisScript<List> jsonSearchScript() { DefaultRedisScript<List> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText( "local offset = tonumber(ARGV[2])\n" + "local count = tonumber(ARGV[3])\n" + "return redis.call('FT.SEARCH', KEYS[1], ARGV[1], 'return', 0, 'limit', offset, count);"); redisScript.setResultType(List.class); return redisScript; } }
RedisSearchSerializer 序列化配置
import com.alibaba.fastjson2.JSON; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; import java.nio.charset.Charset; /** * Redis使用FastJson序列化 * * @author ruoyi */ public class RedisSearchSerializer<T> implements RedisSerializer<T> { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private Class<T> clazz; public RedisSearchSerializer(Class<T> clazz) { super(); this.clazz = clazz; } @Override public byte[] serialize(T t) throws SerializationException { if (t == null) { return new byte[0]; } if (t instanceof String) { return ((String)t).getBytes(DEFAULT_CHARSET); } return JSON.toJSONString(t).getBytes(DEFAULT_CHARSET); } @Override public T deserialize(byte[] bytes) throws SerializationException { if (bytes == null || bytes.length <= 0) { return null; } String str = new String(bytes, DEFAULT_CHARSET); // 不是 json 也不是 序列化的字符串,那就只能是数字,如果不是数字直接返回 if (!str.startsWith("{") && !str.startsWith("[") && !str.startsWith("\"") && !str.matches("^\\d*$")) { return clazz.cast(str); } return JSON.parseObject(str, clazz); } }
数据实体类
@Data
public class LoginUser {
private String ipaddr;
private String username;
public LoginUser(String ipaddr, String username) {
this.ipaddr = ipaddr;
this.username = username;
}
}
redisService
@Service public class RedisService { @Autowired private RedisScript<String> jsonSetScript; @Autowired private RedisScript<Object> jsonGetScript; @Autowired private RedisScript<List> jsonSearchScript; @Autowired private RedisTemplate<String, Object> redisTemplate1; public LoginUser getLoginUser(String uuid) { String key = RedisKeys.LOGIN_TOKEN_KEY + uuid; JSONObject obj = (JSONObject) redisTemplate1.execute(this.jsonGetScript, Collections.singletonList(key)); if (obj == null) { return null; } return obj.toJavaObject(LoginUser.class); } public void setLoginUser(String uuid, LoginUser loginUser, int expireTime, TimeUnit unit) { String key = RedisKeys.LOGIN_TOKEN_KEY + uuid; redisTemplate1.execute(this.jsonSetScript, Collections.singletonList(key), loginUser); redisCache.expire(key, expireTime, unit); } public Page<String> searchLoginUser(String query, Pageable page) { List list = redisTemplate1.execute( this.jsonSearchScript, Collections.singletonList("login_tokens_idx"), query, page.getOffset(), page.getPageSize()); Long total = (Long) list.get(0); List<String> ids = new ArrayList<>(); for (int i = 1; i < list.size(); i++) { ids.add(((String) list.get(i)).replaceAll(RedisKeys.LOGIN_TOKEN_KEY, "")); } return new PageImpl<>(ids, page, total); } public interface RedisKeys { String LOGIN_TOKEN_KEY = "login_tokens1:"; } }
redis 创建索引[^1], 其中 ipaddr 是 IP 字段,包含 “.” 等特殊字符,所以需要将 索引中的 ipaddr 设置成 tag 类型,才能搜索到[^3]
# 连接redis, 如果使用 redisinsight 则不需要这步
redis-cli -a "password"
# 创建索引
FT.CREATE login_tokens_idx
on JSON prefix 1 "login_tokens1:"
SCHEMA $.ipaddr tag $.username text
import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.TimeUnit; @RunWith(SpringRunner.class) @SpringBootTest(classes = YourApplication.class) @ActiveProfiles("prod-local") public class RedisServiceTest { @Autowired private RedisService redisService; @Test public void testSetAndGetLoginUser() { LoginUser user = new LoginUser("192.168.1.1", "testUser"); redisService.setLoginUser("123456", user, 60, TimeUnit.SECONDS); LoginUser getUser = redisService.getLoginUser("123456"); Assert.assertEquals(user.getIpaddr(), getUser.getIpaddr()); Assert.assertEquals(user.getUsername(), getUser.getUsername()); } @Test public void testDeleteLoginUser() { LoginUser user = new LoginUser("192.168.1.1", "testUser"); redisService.setLoginUser("123456", user, 60, TimeUnit.SECONDS); redisService.deleteLoginUser("123456"); LoginUser getUser = redisService.getLoginUser("123456"); Assert.assertNull(getUser); } @Test public void testSearchLoginUser() { // 添加测试数据 LoginUser user1 = new LoginUser("192.168.1.1", "user1"); LoginUser user2 = new LoginUser("192.168.1.2", "user2"); redisService.setLoginUser("123456", user1, 60, TimeUnit.SECONDS); redisService.setLoginUser("789012", user2, 60, TimeUnit.SECONDS); // 搜索测试 Page<String> page = redisService.searchLoginUser("user*", PageRequest.of(0, 10)); Assert.assertEquals(page.getTotalElements(), 2); Assert.assertEquals(page.getContent().size(), 2); Assert.assertTrue(page.getContent().contains("123456")); Assert.assertTrue(page.getContent().contains("789012")); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。