背景
我们的异æ¥é˜Ÿåˆ—是Tasktigerï¼Œä¸€ä¸ªèƒŒåŽæœ‰å•†ä¸šå…¬å¸æ”¯æŒçš„å¼€æºé¡¹ç›®ã€‚
之剿˜¯Pyres,一个Resqueçš„Pythonå®žçŽ°ï¼Œå› ä¸ºGithub在使用Resque,Instagram在使用Pyres,我们应该å¯ä»¥ç”¨ä¸€æ®µæ—¶é—´ã€‚
åŽæ¥Pyresé•¿ä¹…ä¸å†ç»´æŠ¤ï¼Œç§¯ç´¯äº†ä¸€äº›bugä»¥åŠæ€§èƒ½é—®é¢˜è®©æˆ‘们ä¸å¾—ä¸é€‰æ‹©æ›´æ¢ä¸€ä¸ªå¼‚æ¥é˜Ÿåˆ—系统,期åˆè€ƒè™‘过Celery,那是一个很æˆç†Ÿå¾ˆå¼ºå¤§çš„é˜Ÿåˆ—ç³»ç»Ÿï¼Œä»¥è‡³äºŽæˆ‘å¾ˆéš¾ä¸€ä¸‹åæžæ¸…å®ƒã€‚
问题
在使用Tasktiger
åŽæœ‰æ—¶ä¼šé‡åˆ°å·²ç»å¤„ç†å®Œæ¯•çš„taskåˆé‡æ–°è¢«enqueue的情况,如果所有task都是幂ç‰çš„(Idempotentï¼‰ï¼Œé‚£è¿™ä¹Ÿä¸æ˜¯é—®é¢˜ï¼Œç¬¬äºŒæ¬¡æ‰§è¡Œtaskä¼šå› ä¸ºä¹‹å‰å¤„ç†è¿‡è€Œä¸è‡³äºŽå†åšä¸€æ¬¡ã€‚ç„¶è€Œå¹¶ä¸æ˜¯æ‰€æœ‰task都完æˆäº†å¹‚ç‰æ”¹é€ ,所以这对于我们æ¥è¯´æ˜¯ä¸ªä¸¥é‡é—®é¢˜ï¼Œä½†å½“æ—¶å¹¶ä¸èƒ½ç¨³å®šé‡çŽ°ã€‚
探索
æ€€ç–‘è¿‡è®¸å¤šåœ°æ–¹ï¼ŒåŽæ¥å‘现在queue redis上执行BGREWRITEAOF
就能é¿å…这个问题,于是把原先一个月执行一次的AOF
é‡å†™æ“ä½œå˜æˆäº†æ¯å¤©æ‰§è¡Œä¸€æ¬¡ï¼Œå¤§æ¦‚在备份数æ®ä¹‹å‰ï¼Œå› 为备份需è¦åœæœºï¼Œå¦‚æžœä¸åœ¨è¿™ä¹‹å‰åšï¼Œå¯èƒ½å¤‡ä»½å¯¼è‡´é‡å¯æœåŠ¡å°±ä¼šè®©å¾ˆå¤šå·²ç»å¤„ç†è¿‡çš„taské‡å¤å¤„ç†ï¼Œä¾‹å¦‚å¯èƒ½ä¼šç»™ä¼šå‘˜å‘多æ¡çŸä¿¡å’Œå¾®ä¿¡æé†’。
å°±åœ¨æ˜¨å¤©ï¼Œå› ä¸ºé‡å¯queue redis导致很多taské‡å¤å¤„ç†ï¼Œè¿™ä¸ªé—®é¢˜ç»ˆç©¶æ²¡æœ‰è¢«è§£å†³ï¼ŒäºŽæ˜¯èŠ±äº†å¤§çº¦2å¤©æ—¶é—´ï¼Œæ‰¾åˆ°äº†å¯¼è‡´é—®é¢˜çš„æ ¹æºã€‚
之å‰çŒœæµ‹æ˜¯ä¸æ˜¯Tasktiger
哪里没写好导致,毕竟这个项目在Githubä¸Šåªæœ‰ä¸åˆ°500个star,而redisæœ‰å‡ ä¸‡ï¼Œå¹¿å¤§ç”¨æˆ·åŸºç¡€æ›´å®¹æ˜“å‘现奇怪bug,而在Tasktiger
上é‡åˆ°æŸä¸ªbugï¼Œä¹Ÿè®¸ä½ å°±æ˜¯ä¸–ç•Œä¸Šç¬¬ä¸€ä¸ªé‡åˆ°çš„。基于这个å‡è®¾æˆ‘看了很多æºä»£ç ,没有找到å¯é è¯æ®ï¼Œè€Œä¸”基于一个简å•事实:一个task从最开始ä¸å˜åœ¨ï¼Œåˆ°å‡ºçŽ°åœ¨Tasktigerä¸ï¼Œåˆ°å¤„ç†å®Œè¢«åˆ 除,如果是Tasktiger
çš„é—®é¢˜ï¼Œé‚£å®ƒä»Žå“ªé‡Œå¾—åˆ°æ•°æ®æž„é€ å‡ºä¸€ä¸ªtaskå‘¢ï¼Ÿéš¾é“æ˜¯redis的问题å—?
queue redis一般都会å¯ç”¨AOF
,所有修改数æ®çš„命令和值被记录到AOF
ä¸ï¼Œç‰redisé‡å¯æ—¶ï¼Œä¼šæ‰§è¡ŒAOF
文件ä¸çš„å‘½ä»¤ï¼Œè¿™æ ·redis就有了和é‡å¯å‰ä¸€æ¨¡ä¸€æ ·çš„æ•°æ®ã€‚在AOF
ä¸è®°å½•ç€æŸä¸ªtaskä»Žå‡ºçŽ°åˆ°æ¶ˆå¤±çš„å…¨è¿‡ç¨‹ï¼Œéš¾é“æ˜¯redis没完æˆloadå°±æŽ¥å—æœåŠ¡äº†ï¼Ÿä»Žæ—¥å¿—ä¸Šçœ‹å¹¶ä¸æ˜¯è¿™æ ·ï¼Œåœ¨æ²¡load完执行æ“作会得到异常。
看了下生产环境的queue状æ€ï¼ŒåŸºæœ¬æ²¡æœ‰æ£åœ¨å¤„ç†çš„taskï¼ŒåœæŽ‰queue redis,下载AOF
文件,在本地让rediså¯åЍ并load这份AOF
,ä¸å¯åЍTasktiger
çš„worker,åªé™é™åœ°çœ‹queue的状æ€ï¼Œå‘现loadå®ŒåŽæœ‰å¾ˆå¤štaskè¢«é‡æ–°æ”¾å›žäº†queueï¼Œæ¤æ—¶å¦‚果有worker,一定会é‡å¤æ‰§è¡Œã€‚
到这里难é“能确定是redis的问题å—?redisçš„AOF
å¹¶ä¸èƒ½è®©redis回到之å‰çš„状æ€ï¼Ÿå¦‚æžœæ˜¯è¿™æ ·çš„è¯redis怎么会被广泛应用?
于是找了一个task,从AOF
ä¸çœ‹çœ‹Tasktiger
是如何处ç†å®ƒçš„。
处ç†è¿‡ç¨‹æ²¡å‘çŽ°å¼‚å¸¸ï¼Œåªæ˜¯Tasktiger
用了很多lua
脚本æ“作数æ®ï¼Œä¾‹å¦‚有个脚本是:当一个keyä¸åœ¨ç»™å®šçš„æŸå‡ ä¸ªzset䏿—¶åˆ 除这个key。这个æ“作ä¸å¤æ‚ï¼Œä½†ä½ æ— æ³•åªç”¨redis完æˆï¼Œå› 为redisåªæ˜¯ä¸€ä¸ªæ•°æ®å˜å‚¨ï¼Œæ— 法处ç†å¤æ‚逻辑,所以åªèƒ½ä½¿ç”¨lua
完æˆï¼Œå½“然很多è¯éƒ½å¯ä»¥å®Œæˆè¿™ä¸ªé€»è¾‘,åªä¸è¿‡redisæ”¯æŒæ‰§è¡Œlua
脚本,算作是对redisæ“作的补充。
å› ä¸ºæœ‰äº†ä¸€ä¸ªå…·ä½“task,查看和它相关的所有æ“作åŽï¼Œå‘çŽ°é™¤éžæ˜¯åœ¨æŸä¸€æ¥lua
脚本未执行时æ‰ä¼šå¯¼è‡´è¿™ä¸ªtask被放在active queueä¸è€Œä¸æ˜¯è¢«ç§»é™¤ã€‚
äºŽæ˜¯æˆ‘æž„é€ äº†ä¸€ä»½AOF
ï¼Œæœ€å¼€å§‹çš„éƒ¨åˆ†æ˜¯ä¸€å †SCRIPT LOAD
指令,把Tasktiger
需è¦çš„脚本都load到redisä¸ï¼Œè¿™æ ·åŽç»æ“作æ‰å¯ä»¥ä½¿ç”¨EVALSHA
ï¼Œè¿™ä»½æž„é€ å¥½çš„AOF
也通过了redis-check-aof
检查,被loadåŽå†æ¬¡æŸ¥çœ‹queue状æ€ï¼Œç«Ÿç„¶æ£å¸¸äº†ï¼Œå’Œåœredis之å‰ä¸€è‡´ï¼çœ‹æ¥é—®é¢˜å‡ºåœ¨æ‰§è¡Œè¿™äº›è„šæœ¬çš„åœ°æ–¹ã€‚
在redis䏿‰§è¡Œlua
脚本有两ç§åšæ³•ï¼Œç¬¬ä¸€ç§æ˜¯ä½¿ç”¨EVAL
,这ç§åšæ³•æ˜¯æŠŠè„šæœ¬å†…å®¹ä¸€å¹¶è´´ä¸Šï¼Œæ¯æ¬¡æ‰§è¡Œéƒ½éœ€è¦è¿™æ ·ï¼Œå¦‚果总是é‡å¤æ‰§è¡ŒæŸä¸ªç‰¹å®šè„šæœ¬å°±ä¼šå› ä¸ºä¼ è¾“äº†å¤§é‡è„šæœ¬å†…å®¹è€Œä½¿å¾—æ•ˆçŽ‡ä¸å¤Ÿé«˜ï¼ŒäºŽæ˜¯æœ‰äº†ç¬¬äºŒç§æ‰§è¡Œæ–¹æ³•––EVALSHA
,使用这ç§éœ€è¦è„šæœ¬çš„SHA1
,当执行SCRIPT LOAD
åŽï¼Œredis返回的内容就是脚本的SHA1
,load过的脚本在手工flush剿ˆ–者redisé‡å¯å‰éƒ½æ˜¯æœ‰æ•ˆçš„。SCRIPT LOAD
在BGREWRITEAOF
åŽå¹¶ä¸ä¼šå˜åœ¨ï¼Œè™½ç„¶ä½ 还是å¯ä»¥é€šè¿‡EVALSHA
调用脚本。
é‚£ä¹ˆè¿™ä¸¤ç§æ‰§è¡Œæ–¹æ³•对于AOF
文件记录内容有差别å—?没有,执行EVALSHA
也会被记录æˆEVAL
ï¼Œå¦‚æžœä½ åœ¨AOF
ä¸å‘现有EVALSHA
那就奇怪了。在生产环境下载的AOF
ä¸ï¼Œæœ‰ä¸å°‘EVALSHA
,为何没有被记录æˆEVAL
ï¼Ÿå› ä¸ºè¿™äº›EVALSHA
并䏿˜¯redis执行的,而是Tasktiger
的pipeline
å¯¼è‡´å‡ºçŽ°å›žæ»šé—®é¢˜çš„åŽŸå› æ˜¯ï¼šBGREWRITEAOFåŽï¼Œç”±äºŽSCRIPT LOAD被去掉,redisé‡å¯åŽå¦‚果有使用EVALSHAè°ƒç”¨è„šæœ¬çš„åœ°æ–¹ä¼šå‡ºé”™ï¼Œå› ä¸ºé‚£ä¸ªè„šæœ¬å¹¶æ²¡æœ‰è¢«redis load
䏿˜¯è¯´EVALSHA
会被翻译æˆEVAL
放在AOF
ä¸å—?是的,ä¸è¿‡Tasktiger
自己实现了一个redis pipeline
,这也是个lua
脚本,在这个lua
è„šæœ¬ä¸æ˜¯éœ€è¦è°ƒç”¨å…¶ä»–lua
脚本的。调用方法包å«ä¸¤ç§ï¼Œå¤§å¤šæ•°éƒ½æ˜¯EVALSHA
, 由于redisç¦æ¢åœ¨luaä¸ä½¿ç”¨redis.call执行EVALSHA调用å¦ä¸€ä¸ªè„šæœ¬ï¼ˆå¹¶ä¸æ˜¯æ‰€æœ‰redis命令都å¯ä»¥åœ¨redis.call使用) ,Tasktiger使用了其他方法æ¥è°ƒç”¨ï¼Œç®—是一ç§hack。
由于redisçš„é™åˆ¶åŠ ä¸ŠTasktiger
çš„hack,导致 在BGREWRITEAOFåŽï¼Œå¦‚果有pipeline脚本的调用å‘生,下次é‡å¯redis会使得之å‰åšè¿‡çš„taskè¢«é‡æ–°æ”¾å›žqueueï¼Œå› ä¸ºç§»é™¤æ“作的脚本还没有load,所以移除失败了,看起æ¥å°±åƒæ˜¯taskè¢«é‡æ–°æ”¾å›žqueue
ç›®å‰æˆ‘们针对这个问题的解决方法是:勤快点BGREWRITEAOF,æ¯å°æ—¶ä¸€æ¬¡ï¼Œé‡å¯redis之å‰å†åšä¸€æ¬¡
Top comments (0)