一,為什么需要生成唯一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)