spring boot:redis+lua实现顺序自增的唯一id发号器(spring boot 2.3.1)

一,为什么需要生成唯一id(发号器)?

1,在分布式和微服务系统中,

  生成唯一id相对困难,

  常用的方式:

   uuid不具备可读性,作为主键存储时性能也不够好,

   mysql的主键,在分库时使用不够方便,高并发时性能没有保障

   所以在这里我们演示使用redis+lua生成唯一id

2,使用redis性能虽好,但仍然要考虑单点故障问题,

   这里建议在生产环境中使用主从+哨兵或集群方式

  

说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest

         对应的源码可以访问这里获取: https://github.com/liuhongdi/

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,本演示项目的相关信息

1,项目地址:

https://github.com/liuhongdi/redisuniqueid

2,项目原理:

利用redis中lua脚本的原子性,避免产生重复id的问题

3,项目结构:

 

三,lua代码说明

id.lua

local id_key = 'id_key_'..KEYS[1]
local current = redis.call('get',id_key)
if current == false then
    redis.call('set',id_key,1)
    return '1'
end
--redis.log(redis.LOG_NOTICE,' current:'..current..':')
local result =  tonumber(current)+1
--redis.log(redis.LOG_NOTICE,' result:'..result..':')
redis.call('set',id_key,result)
return tostring(result)

说明:

id_key变量作为存储的kv对的key

如果变量不存在,设置id_key值为1并返回

如果变量存在,值加1后返回

注意转为字符串形式后返回,方便java代码接收

四,java代码说明

RedisLuaUtil.java

@Service
public class RedisLuaUtil {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private static final Logger logger = LogManager.getLogger("bussniesslog");
    /*
    run a lua script
    luaFileName: lua file name,no path
    keyList: list for redis key
    return:lua return value,type is string
    */
    public String runLuaScript(String luaFileName,List<String> keyList) {
        DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/"+luaFileName)));
        redisScript.setResultType(String.class);

        String argsone = "none";
        //String result = stringRedisTemplate.execute(redisScript, keyList,argsone);
        String result = "";
        try {
            result = stringRedisTemplate.execute(redisScript, keyList,argsone);
        } catch (Exception e) {
            logger.error("发生异常",e);
            throw new ServiceException(ResponseCode.LUA_ERROR.getMsg());
        }

        return result;
    }
}

说明:功能用来运行resource目录下的lua脚本

IdServiceImpl.java

@Service
public class IdServiceImpl implements IdService {

    @Resource
    private RedisLuaUtil redisLuaUtil;

    /*
    * 调用lua得到唯一id
    * 返回:唯一的自增id,字符串形式
    * */
    @Override
    public String getId(String idType) {
        List<String> keyList = new ArrayList();
        keyList.add(idType);
        String res = redisLuaUtil.runLuaScript("id.lua",keyList);
        System.out.println("-----res:"+res);
        return res;
    }
}

说明:得到自增id的service

五,测试发号器效果

1,从redis删除已创建的key

127.0.0.1:6379> del id_key_formtoken
(integer) 1
127.0.0.1:6379> get id_key_formtoken
(nil)

2,用ab发起测试

#-c:20个并发

#-n:共20个请求

[liuhongdi@localhost ~]$ ab -c 20 -n 20 http://127.0.0.1:8080/order/getid

3,查看输出效果

-----res:1
-----res:2
-----res:3
-----res:4
-----res:5
-----res:6
-----res:8
-----res:7
-----res:9
-----res:10
-----res:11
-----res:12
-----res:13
-----res:14
-----res:15
-----res:16
-----res:17
-----res:20
-----res:19
-----res:18

并发情况下,仍然正确的按自增的顺序生成id

六,查看spring boot的版本

  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _    
( ( )\___ | '_ | '_| | '_ / _` |    
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.1.RELEASE)
原文地址:https://www.cnblogs.com/architectforest/p/13177918.html