栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

springboot+redis+lua 一般使用总结

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

springboot+redis+lua 一般使用总结

1. redis操作工具类
package com.yl.client.util;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.ConvertingCursor;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@RequiredArgsConstructor(onConstructor_ = {@Autowired})
@Component
public class RedisUtil {
    private final RedisTemplate redisTemplate;
    
    @Value("${spring.redis.keyPrefix}:")
    private String prefix;


    // - - - - - - - - - - - - - - - - - - - - - 公共方法 - - - - - - - - - - - - - - -
	public RedisTemplate getRedisTemplate() {
		return redisTemplate;
	}
	
    
    public boolean expire(String key, long time) {
        return redisTemplate.expire(this.prefix + key, time, TimeUnit.SECONDS);
    }

    
    public long getTtl(String key) {
        return redisTemplate.getExpire(this.prefix + key, TimeUnit.SECONDS);
    }

    
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(this.prefix + key);
    }

    
    public boolean persist(String key) {
        return redisTemplate.boundValueOps(this.prefix + key).persist();
    }

    
    public boolean deleteKey(String key) {
        return redisTemplate.delete(this.prefix + key);
    }

    
    public boolean deleteKeys(Collection keys) {
        List collect = keys.stream().map(key -> this.prefix + key).collect(Collectors.toList());
        return redisTemplate.delete(collect) == collect.size();
    }

    
    public boolean deleteKeyLike(String key) {
        if (StringUtils.isBlank(key)) {
            return false;
        }
        Set keys = redisTemplate.keys(this.prefix + key + "*");
        if (keys.isEmpty()) {
            return false;
        }
        return deleteKeys(keys);
    }

    
    public List getKeys(String key) {
        List list = new ArrayList<>();
        if (StringUtils.isBlank(key)) {
            return list;
        }
        Set keys = redisTemplate.keys(this.prefix + key + "*");
        if (keys.isEmpty()) {
            return list;
        }
        list.addAll(keys);
        return list;
    }

    // - - - - - - - - - - - - - - - - - - - - - String类型 - - - - - - - - - - - - -
    // - - - - - - -

    
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(this.prefix + key);
    }

    
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(this.prefix + key, value);
    }

    
    public void set(String key, Object value, long time) {
        if (time > 0) {
            redisTemplate.opsForValue().set(this.prefix + key, value, time, TimeUnit.SECONDS);
        } else {
            redisTemplate.opsForValue().set(this.prefix + key, value);
        }
    }

    public void set(String prefix, String key, Object value, long time) {
        redisTemplate.opsForValue().set(prefix + key, value, time, TimeUnit.SECONDS);
    }


    
    public void batchSet(Map keyAndValue) {
        redisTemplate.opsForValue().multiSet(keyAndValue);
    }

    
    public void batchSetIfAbsent(Map keyAndValue) {
        redisTemplate.opsForValue().multiSetIfAbsent(keyAndValue);
    }

    
    public Long increment(String key, long number) {
        return redisTemplate.opsForValue().increment(prefix + key, number);
    }

    
    public Long decrement(String key, long number) {
        return redisTemplate.opsForValue().decrement(prefix + key, number);
    }

    // - - - - - - - - - - - - - - - - - - - - - set类型 - - - - - - - - - - - - - - -
    // - - - - -

    
    public Long addSet(String key, Object... value) {
        return redisTemplate.opsForSet().add(this.prefix + key, value);
    }

    
    public Set getSetMembers(String key) {
        return redisTemplate.opsForSet().members(this.prefix + key);
    }

    
    public List getSetRandomMembers(String key, long count) {
        return redisTemplate.opsForSet().randomMembers(this.prefix + key, count);
    }

    
    public Object getSetRandomMember(String key) {
        return redisTemplate.opsForSet().randomMember(this.prefix + key);
    }

    
    public Object popSet(String key) {
        return redisTemplate.opsForSet().pop(this.prefix + key);
    }

    
    public Long setLength(String key) {
        return redisTemplate.opsForSet().size(this.prefix + key);
    }

    
    public boolean sHasKey(String key, Object value) {
        return redisTemplate.opsForSet().isMember(this.prefix + key, value);
    }

    
    public boolean isSetMember(String key, Object obj) {
        return redisTemplate.opsForSet().isMember(this.prefix + key, obj);
    }

    
    public boolean moveSet(String key, String value, String destKey) {
        return redisTemplate.opsForSet().move(this.prefix + key, value, destKey);
    }

    
    public void removeSet(String key, Object... values) {
        redisTemplate.opsForSet().remove(this.prefix + key, values);
    }

    
    public Set differenceSet(String key, String destKey) {
        return redisTemplate.opsForSet().difference(this.prefix + key, destKey);
    }

    // - - - - - - - - - - - - - - - - - - - - - hash类型 - - - - - - - - - - - - - -
    // - - - - - -
    
    public void addHash(String key, Map map) {
        redisTemplate.opsForHash().putAll(this.prefix + key, map);
    }

    
    public void addHash(String key, String key1, Object value) {
        redisTemplate.opsForHash().put(this.prefix + key, key1, value);
    }

    
    public Map getHashEntries(String key) {
        return redisTemplate.opsForHash().entries(this.prefix + key);
    }

    
    public boolean hasHashKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(this.prefix + key, hashKey);
    }

    
    public Object getHashMap(String key, String key2) {
        return redisTemplate.opsForHash().get(this.prefix + key, key2);
    }

    
    public Long deleteHashKey(String key, Object... hashKeys) {
        return redisTemplate.opsForHash().delete(this.prefix + key, hashKeys);
    }

    
    public Long increment(String key, String hashKey, long number) {
        return redisTemplate.opsForHash().increment(this.prefix + key, hashKey, number);
    }

    
    public Double increment(String key, String hashKey, Double number) {
        return redisTemplate.opsForHash().increment(this.prefix + key, hashKey, number);
    }

    
    public Set hashKeys(String key) {
        return redisTemplate.opsForHash().keys(this.prefix + key);
    }

    
    public Long hashSize(String key) {
        return redisTemplate.opsForHash().size(this.prefix + key);
    }

    // - - - - - - - - - - - - - - - - - - - - - list类型 - - - - - - - - - - - - - -
    // - - - - - -

    
    public Object index(String key, long index) {
        return redisTemplate.opsForList().index(this.prefix + key, index);
    }

    
    public List range(String key, long start, long end) {
        return redisTemplate.opsForList().range(this.prefix + key, start, end);
    }

    
    public Long leftPush(String key, Object value) {
        return redisTemplate.opsForList().leftPush(this.prefix + key, value);
    }

    
    public Long leftPush(String key, Object pivot, Object value) {
        return redisTemplate.opsForList().leftPush(this.prefix + key, pivot, value);
    }

    
    public  Long leftPushAll(String key, Collection values) {
        return redisTemplate.opsForList().leftPushAll(this.prefix + key, values.toArray(new Object[values.size()]));
    }


    
    public  Long rightPushAll(String key, Collection values) {
        return redisTemplate.opsForList().rightPushAll(this.prefix + key, values.toArray(new Object[values.size()]));
    }

    
    public Long rightPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(this.prefix + key, value);
    }

    
    public Long rightPushIfPresent(String key, Object value) {
        return redisTemplate.opsForList().rightPushIfPresent(this.prefix + key, value);
    }

    
    public Long listLength(String key) {
        return redisTemplate.opsForList().size(this.prefix + key);
    }

    
    public Object leftPop(String key) {
        return redisTemplate.opsForList().leftPop(this.prefix + key);
    }

    
    public Object leftPop(String key, long time) {
        return redisTemplate.opsForList().leftPop(this.prefix + key, time, TimeUnit.SECONDS);
    }

    
    public Object rightPop(String key) {
        return redisTemplate.opsForList().rightPop(this.prefix + key);
    }

    
    public Object rightPop(String key, long time) {
        return redisTemplate.opsForList().rightPop(this.prefix + key, time, TimeUnit.SECONDS);
    }

    
    public Object rightPopAndLeftPush(String key) {
        return redisTemplate.opsForList().rightPopAndLeftPush(this.prefix + key, this.prefix + key);
    }

    
    public Long remove(String key, Object value) {
        return redisTemplate.opsForList().remove(this.prefix + key, 0, value);
    }

    
    public void setList(String key, long index, Object value) {
        redisTemplate.opsForList().set(this.prefix + key, index, value);
    }

    
    public Long removeByIndex(String key, long index) {
        Long size = redisTemplate.opsForList().size(this.prefix + key);
        if(size >0 && size > index && index >= 0){
            redisTemplate.opsForList().set(this.prefix + key, index, "old-del");
            return redisTemplate.opsForList().remove(this.prefix + key, 0, "old-del");
        }
        return 0L;
    }
	
	
	public Cursor scan(String pattern, int limit) {
		ScanOptions options = ScanOptions.scanOptions().match(pattern).count(limit).build();
		RedisSerializer redisSerializer = (RedisSerializer) redisTemplate.getKeySerializer();
			return (Cursor) redisTemplate.executeWithStickyConnection(new RedisCallback() {
				@Override        
				public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
				Object obj =  new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize);
					try {                //关闭连接,否则连接数量上限之后会一直阻塞                
						redisConnection.close();            
					} catch (Exception e) {                
						throw new BizException("redis关闭异常", e);
					}            
				return obj;       
			}    
		});
	}
	
}
 
2. lua工具类 
package com.yl.demo.util;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import java.util.List;


@Slf4j
@Component
public class LuaUtil {
    @Autowired
    private RedisTemplate redisTemplate;

    
    public String getSha1(Resource scriptResource) {
        DefaultRedisScript redisScript = new DefaultRedisScript();
        redisScript.setLocation(scriptResource);
        //将脚本缓存到服务端并返回摘要信息
        String sha1 = redisScript.getSha1();
        log.info("script load {}, 返回sha1===>{}", scriptResource.getFilename(), sha1);
        return sha1;
    }

    
    public RedisScript getRedisScript(Resource scriptResource) {
        DefaultRedisScript redisScript = new DefaultRedisScript();
        redisScript.setLocation(scriptResource);
        String sha1 = redisScript.getSha1();
        log.info("script load {}, 返回sha1===>{}", scriptResource.getFilename(), sha1);
        return redisScript;
    }

    
    public String execute(Resource scriptResource, List paramList, List argList) {
        DefaultRedisScript redisScript = new DefaultRedisScript();
        // 返回值类型必须指定,一般设置为String,lua脚本中返回值一般也为字符串(或者json字符串,指定序列化方式,可转为Javabean)
        redisScript.setResultType(String.class);
        redisScript.setLocation(scriptResource);
        //第3个参数 resultSerializer 也指定为字符串(不是jsonRedisSerializer),处理完结果由Java解析字符串处理具体类型
        return redisTemplate.execute(redisScript, redisTemplate.getStringSerializer(), redisTemplate.getStringSerializer(), paramList, argList.toArray());
    }

}

 
3. springboot整合redids依赖和配置 

	org.springframework.data
	spring-data-redis
	2.3.4.RELEASE

配置

@SpringBootApplication
public class RedisConfig {
    private final StringRedisSerializer srs = new StringRedisSerializer();
    private final Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

    
	@Bean
	public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		template.setHashKeySerializer(srs);
		template.setHashValueSerializer(srs);
		template.setKeySerializer(srs);
		template.setValueSerializer(jsonRedisSerializer);
		return template;
	}
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}
 

集群配置

spring:
  redis:
    cluster:
      nodes:
        - 192.168.1.236:7001
        - 192.168.1.236:7002
        - 192.168.1.236:7003
        - 192.168.1.244:7004
        - 192.168.1.244:7005
        - 192.168.1.244:7006
      max-redirects: 3  # 获取失败 最大重定向次数
	  timeout: 6000  # 连接超时时长(毫秒)
	password:    # 密码(默认为空)
	keyPrefix: projectName  #项目名编号
	lettuce:
		pool:
		  max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
		  max-idle: 10    # 连接池中的最大空闲连接
		  max-wait: -1   # 连接池最大阻塞等待时间(使用负值表示没有限制)
		  min-idle:  5     # 连接池中的最小空闲连接

本地单机配置

spring:
  redis:
    database: 0
    host: localhost
    port: 6379
	#Redis服务器连接密码(默认为空)
    password: 123456
    timeout: 5000
	# 自定义,redis key 前缀
    keyPrefix: app:
    lettuce:
      pool:
        # 连接池最大连接数(使用负值表示没有限制) 默认为8
        max-active: 8
        # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认为-1
        max-wait: -1
        # 连接池中的最大空闲连接 默认为8
        max-idle: 8
        # 连接池中的最小空闲连接 默认为 0
        min-idle: 2
4. redis和lua问题
问题1  org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Invalid UTF-8 start byte 0x9d
 at [Source: (byte[])"李四"; line: 1, column: 3]; nested exception is com.fasterxml.jackson.core.JsonParseException: Invalid UTF-8 start byte 0x9d
 at [Source: (byte[])"李四"; line: 1, column: 3]
这个问题是添加key value 时使用命令行导致,获取的时通过API,不能读取
 
问题2 redisTemplate  leftPushAll(K key, V… values) 或者 leftPushAll(K key, Collection values) 以数组或者collection方式添加 注意:不能是ArrayList
redisTemplate.opsForList().leftPushAll(key3, t1.toArray());

redisTemplate.opsForList().leftPushAll(list); //点击进去走的是 V...values 方法  坑  api里面的leftPushAll(Collection list)用不了
改造为
	public void leftPushAll(String key, Collection values) { //传入集合  会自动指定自定义类型
		redisTemplate.opsForList().leftPushAll(key, values.toArray[new Object[values.size()]]);
	}
或者
	public void leftPushAll(String key, V... values) {  //传入数组  会自动指定自定义类型
		redisTemplate.opsForList().leftPushAll(key, values);
	}

问题3. ERR Error running script (call to f_bb65bb83b2f8a61cac728df1b1af12175dd208a7): @user_script:9: @user_script: 9: Lua redis() command arguments must be strings or integers

问题4. @enable_strict_lua:15: user_script:1: Script attempted to access nonexistent global variable 'require' 
redis中调用 lua脚本
require在Redis中不可用,库已预加载     第一行 local cjson = require "cjson"  不需要写

问题5. hash tag  mset在集群模式设置值时错误 CROSSSLOT Keys in request don’t hash to the same slot
HashTag机制可以影响key被分配到的slot,从而可以使用那些被限制在slot中操作。
HashTag即是用{}包裹key的一个子串,如{user:}1, {user:}2。
解决方案HashTag
HashTag即是用{}包裹key的一个子串,如{user:}1, {user:}2

问题6. Redis遇到的问题 SerializationException:Could not read JSON: Could not resolve type id 'com.cmbchina.entity.RedPacketDetailBean' as a subtype of java.lang.Object: no such class found 解决办法
原因:A服务存储对象到redis中时候会对应有一个全路径类名限定。在通过token进行取对象值并强制转换的时候,如果接收对象的全路径名称和redis中保存的不一样的话就会转换失败报错。
解决办法:
方法一:
获取并转换接收redis中的对象时将接收对象的全路径与redis中保持一致。
RewardEmp rewardEmp = (RewardEmp)redisTemplate.execute(redisScript, new StringRedisSerializer(), getRedisSerializer(), paramList, argList.toArray());
方法二 
从源头上解决,将保存对象的方式,换成其他方式  com.alibaba.fastjson.JSONObject
5. lua脚本

抢红包脚本

-- local cjson = require "cjson"

local prefix = KEYS[1]
local redpackKey = KEYS[2]
local activityId = KEYS[3]
local disRedpackKey = KEYS[4]
local username = KEYS[5]
local redpackObj = redis.call('rpop', prefix..redpackKey..activityId)
local ttl = redis.call('ttl', prefix..redpackKey..activityId)


if(redpackObj ~= nil) then
-- username 添加到对象中
  local obj = cjson.decode(redpackObj)
  obj[2]['username']=username
  local jsonObj = cjson.encode(obj)
  redis.call('lpush', prefix..disRedpackKey..activityId, jsonObj)
  redis.call('expire', prefix..disRedpackKey..activityId, ttl)
  return jsonObj
else
  return nil
end

获取id的lua脚本

local key = KEYS[1]
local id = redis.call('get', key)
if(id == false) 
then
  redis.call('set', key, 1)
  return 1
elseif(tonumber(id) >= 999999)
then
  redis.call('set', key, 1)
  return '1'
else
  return tostring(redis.call('incr', key))
end

抽奖的lua脚本

-- 实际也就是轮询获取随机数返回前端展示滚动数据,谁中奖前端再请求一次后台获取一次随机数

-- 从候选人列表中随机取一个,谁中奖Java中可以从list中移除这个元素
local prefix = KEYS[1]
local empKey = KEYS[2]
local length = redis.call('llen', prefix..empKey)
local index = math.random(length) -1
local obj = redis.call('lindex', prefix..empKey, index)
return obj
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号