亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

攔截Redis命令導(dǎo)致的Lua腳本執(zhí)行失敗的問題解決

 更新時(shí)間:2023年06月22日 10:10:04   作者:CodeFox  
本文主要介紹了攔截Redis命令導(dǎo)致的Lua腳本執(zhí)行失敗的問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

大家好,今天分享一個(gè)在使用 redis lua 腳本過程中遇到的一個(gè)問題,問題不難,但是容易踩坑。

lua 腳本使用方式

   // 定義腳本資源
   DefaultRedisScript redisScript = new DefaultRedisScript<>();
   redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(SCRIPT_PATH + scriptName)));
   redisScript.setResultType(List.class);
   // 預(yù)加載腳本(可選)
   redisTemplate.getConnectionFactory().getClusterConnection().scriptLoad(redisScript.getScriptAsString().getBytes());
   // 執(zhí)行腳本
   stringRedisTemplate.execute(redisScript, keys, args);

redis script 相關(guān)命令說明

  • script load:將 lua 腳本加載到 redis 的腳本緩存中,返回該腳本的 sha1 校驗(yàn)和,之后通過 evalsha 命令用此校驗(yàn)和調(diào)用該腳本。
  • evalsha:根據(jù) sha1 執(zhí)行已加載入的 lua 腳本。
  • eval:執(zhí)行一段 lua 腳本代碼,執(zhí)行完后該腳本也會(huì)緩存到 redis 腳本緩存中。
  • script exists:根據(jù) sha1 檢查腳本是否已經(jīng)存在于腳本緩存中。
  • script flush:清空 redis 的腳本緩存,刪除所有已加載的 lua 腳本。
  • script kill:kill 正在執(zhí)行的 lua 腳本。

execute 方法執(zhí)行過程

reidsTemplate 里持有一個(gè) ScriptExecutor,最終的執(zhí)行都代理給 ScriptExecutor,ScriptExecutor 會(huì)通過 evalsha 命令去 redis server 端執(zhí)行腳本。

如果之前已經(jīng)通過 script load 命令預(yù)加載了 lua 腳本,則 evalsha 會(huì)正常執(zhí)行;如果沒有事先加載腳本且第一次執(zhí)行該腳本,則 evalsha 會(huì)返回 "NOSCRIPT No matching script. Please use EVAL." 異常,該異常會(huì)封裝成 RedisSystemException 拋出。

捕獲異常后,判斷如果異常類型是 NonTransientDataAccessException,且異常信息里包含 "NOSCRIPT" 關(guān)鍵詞,則再通過 eval 命令傳遞完整的腳本來執(zhí)行一次,執(zhí)行完之后會(huì)緩存腳本,以后每次調(diào)用只需通過 evalsha 命令傳遞 sha1 即可執(zhí)行。

項(xiàng)目中遇到的問題

負(fù)責(zé)的項(xiàng)目中有一段 lua 腳本用來做短信發(fā)送頻率的限流處理,服務(wù)部署到全新的一套環(huán)境后發(fā)現(xiàn)請求報(bào)錯(cuò) "NOSCRIPT No matching script. Please use EVAL.",根據(jù)上述介紹,該錯(cuò)誤表示 redis server 通過傳遞的 sha1 找不到相應(yīng)的腳本。

該服務(wù)目前做法是沒事先通過 script load 預(yù)加載腳本,是通過懶加載方式,由第一個(gè)請求去做加載操作。因?yàn)樾碌倪@套環(huán)境 redis 集群也是新搭建的,所以肯定是沒緩存此腳本的,但是按照上述分析,第一個(gè)請求 evalsha 失敗后是會(huì)執(zhí)行 eval 的。

所以可以推斷是異常類型不是 NonTransientDataAccessException,或者異常信息里沒有包含 "NOSCRIPT" 關(guān)鍵詞,導(dǎo)致異常直接拋出去了。

經(jīng)過排查發(fā)現(xiàn)是前兩周是接入了 sentinel-redis 流控功能引起的問題。

<dependency>
     <groupId>com.xxx</groupId>
     <artifactId>xxx-sentinel-spring-boot-starter-redis</artifactId>
     <version>${xxx-sentinel.version}</version>
</dependency>

該模塊會(huì)對每一個(gè) redis 命令做攔截,然后通過 sentinel 流控 api 進(jìn)行包裹。

實(shí)際命令是通過 method.invoke() 反射執(zhí)行的。如果執(zhí)行內(nèi)部有異常,會(huì)拋出 InvocationTargetException。

綜上,第一次執(zhí)行 evalsha 命令拋出的 "NOSCRIPT" RedisSystemException 被包裝成了 InvocationTargetException 異常,所以在此判斷直接返回 false,導(dǎo)致異常直接拋出了,并沒有執(zhí)行后續(xù)的 eval 命令。

怎么攔截 redis 命令

我們知道 redis 命令都是通過 RedisConnection 對象執(zhí)行的,RedisConnection 是從 RedisConnectionFactory 中 get 的。

RedisConnectionFactory 一般有 jedis、lettuce 這兩種實(shí)現(xiàn)。

通過 SpringBoot 自動(dòng)裝配裝載進(jìn) Spring 容器的就是具體的 RedisConnectionFactory 實(shí)現(xiàn)。

所以我們可以通過攔截 RedisConnectionFactory 的 getConnection 方法得到 RedisConnection,然后對 RedisConnection 在進(jìn)行一次代理,這樣所有的 redis 命令就都能走到我們自己的攔截器里了。

解決辦法

回到主題,我們要怎么解決這個(gè)問題呢?

  • 使用 lua 腳本最好在服務(wù)啟動(dòng)后通過 script load 做預(yù)加載。
  • 對 redis 命令(不限于)做攔截后,最好返回原始異常。

總結(jié)

  • 該問題還是比較坑的,不好復(fù)現(xiàn),在遷移新環(huán)境之前,一直沒出現(xiàn)過該問題,主要原因是 sentine-redis 包是最近才引入的,不管 dev、test、prod 各環(huán)境 lua 腳本其實(shí)早就已經(jīng)緩存到 redis server 了,走不到第一次加載的邏輯里去。
  • 對各種組件的執(zhí)行流程做攔截、擴(kuò)展前需仔細(xì)看下原有的執(zhí)行流程,是否對異常有特殊處理,最好返回原始異常。

到此這篇關(guān)于攔截Redis命令導(dǎo)致的Lua腳本執(zhí)行失敗的問題解決的文章就介紹到這了,更多相關(guān)redis lua執(zhí)行內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • k8s部署redis遠(yuǎn)程連接的項(xiàng)目實(shí)踐

    k8s部署redis遠(yuǎn)程連接的項(xiàng)目實(shí)踐

    本文主要介紹了k8s部署redis遠(yuǎn)程連接的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-10-10
  • redis實(shí)現(xiàn)多級緩存同步方案詳解

    redis實(shí)現(xiàn)多級緩存同步方案詳解

    這篇文章主要介紹了redis實(shí)現(xiàn)多級緩存同步方案詳解,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-12-12
  • RedisTemplate序列化設(shè)置的流程和具體步驟

    RedisTemplate序列化設(shè)置的流程和具體步驟

    在使用 Redis 作為緩存數(shù)據(jù)庫時(shí),我們通常會(huì)使用 RedisTemplate 來簡化與 Redis 進(jìn)行交互的操作,而其中一個(gè)重要的配置項(xiàng)就是序列化設(shè)置,它決定了數(shù)據(jù)在存儲(chǔ)到 Redis 中時(shí)的格式,本文將介紹如何進(jìn)行 RedisTemplate 的序列化設(shè)置,以及一些常見的序列化方案
    2024-11-11
  • 詳解Redis中的簡單動(dòng)態(tài)字符串和C字符串的區(qū)別

    詳解Redis中的簡單動(dòng)態(tài)字符串和C字符串的區(qū)別

    簡單動(dòng)態(tài)字符串(SDS)和?C?字符串在實(shí)現(xiàn)和特性上存在一些區(qū)別,這些區(qū)別使得?SDS?更適合作為?Redis?中字符串對象的內(nèi)部表示,本文給大家介紹一下Redis中的簡單動(dòng)態(tài)字符串和C字符串的區(qū)別,需要的朋友可以參考下
    2023-12-12
  • 微服務(wù)Spring Boot 整合 Redis 實(shí)現(xiàn)好友關(guān)注功能

    微服務(wù)Spring Boot 整合 Redis 實(shí)現(xiàn)好友關(guān)注功能

    這篇文章主要介紹了微服務(wù)Spring Boot 整合 Redis 實(shí)現(xiàn) 好友關(guān)注,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-12-12
  • Redis數(shù)據(jù)庫安全詳解

    Redis數(shù)據(jù)庫安全詳解

    這篇文章主要為大家介紹了Redis數(shù)據(jù)庫安全詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • websocket+redis動(dòng)態(tài)訂閱和動(dòng)態(tài)取消訂閱的實(shí)現(xiàn)示例

    websocket+redis動(dòng)態(tài)訂閱和動(dòng)態(tài)取消訂閱的實(shí)現(xiàn)示例

    本文主要介紹了websocket+redis動(dòng)態(tài)訂閱和動(dòng)態(tài)取消訂閱,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • Redis通用命令介紹以及key的層級結(jié)構(gòu)講解

    Redis通用命令介紹以及key的層級結(jié)構(gòu)講解

    這篇文章主要介紹了Redis通用命令以及key的層級結(jié)構(gòu),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-12-12
  • 關(guān)于redis的延遲雙刪策略總結(jié)

    關(guān)于redis的延遲雙刪策略總結(jié)

    這篇文章主要介紹了關(guān)于redis的延遲雙刪策略總結(jié),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 一文搞懂阿里云服務(wù)器部署Redis并整合Spring?Boot

    一文搞懂阿里云服務(wù)器部署Redis并整合Spring?Boot

    這篇文章主要介紹了一文搞懂阿里云服務(wù)器部署Redis并整合Spring?Boot,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09

最新評論