{"id":201841,"date":"2025-05-10T09:42:03","date_gmt":"2025-05-10T01:42:03","guid":{"rendered":"https:\/\/server.hk\/cnblog\/201841\/"},"modified":"2025-05-10T09:42:03","modified_gmt":"2025-05-10T01:42:03","slug":"%e6%80%8e%e4%b9%88%e4%bd%bf%e7%94%a8goredis%e5%ae%9e%e7%8e%b0%e5%b8%b8%e8%a7%81%e9%99%90%e6%b5%81%e7%ae%97%e6%b3%95","status":"publish","type":"post","link":"https:\/\/server.hk\/cnblog\/201841\/","title":{"rendered":"\u600e\u4e48\u4f7f\u7528Go+Redis\u5b9e\u73b0\u5e38\u89c1\u9650\u6d41\u7b97\u6cd5"},"content":{"rendered":"<p><b><\/b> <\/p>\n<h1>\u600e\u4e48\u4f7f\u7528Go+Redis\u5b9e\u73b0\u5e38\u89c1\u9650\u6d41\u7b97\u6cd5<\/h1>\n<p><span style=\"cursor: pointer\"><i><\/i>\u6536\u85cf<\/span> <\/p>\n<p>\u5728\u6570\u636e\u5e93\u5b9e\u6218\u5f00\u53d1\u7684\u8fc7\u7a0b\u4e2d\uff0c\u6211\u4eec\u7ecf\u5e38\u4f1a\u9047\u5230\u4e00\u4e9b\u8fd9\u6837\u90a3\u6837\u7684\u95ee\u9898\uff0c\u7136\u540e\u8981\u5361\u597d\u534a\u5929\uff0c\u7b49\u95ee\u9898\u89e3\u51b3\u4e86\u624d\u53d1\u73b0\u539f\u6765\u4e00\u4e9b\u7ec6\u8282\u77e5\u8bc6\u70b9\u8fd8\u662f\u6ca1\u6709\u638c\u63e1\u597d\u3002\u4eca\u5929golang\u5b66\u4e60\u7f51\u5c31\u6574\u7406\u5206\u4eab\u300a\u600e\u4e48\u4f7f\u7528Go+Redis\u5b9e\u73b0\u5e38\u89c1\u9650\u6d41\u7b97\u6cd5\u300b\uff0c\u804a\u804a\uff0c\u5e0c\u671b\u53ef\u4ee5\u5e2e\u52a9\u5230\u6b63\u5728\u52aa\u529b\u8d5a\u94b1\u7684\u4f60\u3002<\/p>\n<ul>\n<ul><\/ul>\n<\/ul>\n<h3>\u56fa\u5b9a\u7a97\u53e3<\/h3>\n<p>\u4f7f\u7528Redis\u5b9e\u73b0\u56fa\u5b9a\u7a97\u53e3\u6bd4\u8f83\u7b80\u5355\uff0c\u4e3b\u8981\u662f\u7531\u4e8e\u56fa\u5b9a\u7a97\u53e3\u540c\u65f6\u53ea\u4f1a\u5b58\u5728\u4e00\u4e2a\u7a97\u53e3\uff0c\u6240\u4ee5\u6211\u4eec\u53ef\u4ee5\u5728\u7b2c\u4e00\u6b21\u8fdb\u5165\u7a97\u53e3\u65f6\u4f7f\u7528<code>pexpire<\/code>\u547d\u4ee4\u8bbe\u7f6e\u8fc7\u671f\u65f6\u95f4\u4e3a\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\uff0c\u8fd9\u6837\u7a97\u53e3\u4f1a\u968f\u8fc7\u671f\u65f6\u95f4\u800c\u5931\u6548\uff0c\u540c\u65f6\u6211\u4eec\u4f7f\u7528<code>incr<\/code>\u547d\u4ee4\u589e\u52a0\u7a97\u53e3\u8ba1\u6570\u3002<\/p>\n<p>\u56e0\u4e3a\u6211\u4eec\u9700\u8981\u5728<code>counter==1<\/code>\u7684\u65f6\u5019\u8bbe\u7f6e\u7a97\u53e3\u7684\u8fc7\u671f\u65f6\u95f4\uff0c\u4e3a\u4e86\u4fdd\u8bc1\u539f\u5b50\u6027\uff0c\u6211\u4eec\u4f7f\u7528\u7b80\u5355\u7684<code>Lua<\/code>\u811a\u672c\u5b9e\u73b0\u3002<\/p>\n<pre>const&nbsp;fixedWindowLimiterTryAcquireRedisScript&nbsp;=&nbsp;`\n--&nbsp;ARGV[1]:&nbsp;\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\n--&nbsp;ARGV[2]:&nbsp;\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\n\nlocal&nbsp;window&nbsp;=&nbsp;tonumber(ARGV[1])\nlocal&nbsp;limit&nbsp;=&nbsp;tonumber(ARGV[2])\n\n--&nbsp;\u83b7\u53d6\u539f\u59cb\u503c\nlocal&nbsp;counter&nbsp;=&nbsp;tonumber(redis.call(\"get\",&nbsp;KEYS[1]))\nif&nbsp;counter&nbsp;==&nbsp;nil&nbsp;then&nbsp;\n&nbsp;&nbsp;&nbsp;counter&nbsp;=&nbsp;0\nend\n--&nbsp;\u82e5\u5230\u8fbe\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u8bf7\u6c42\u5931\u8d25\nif&nbsp;counter&nbsp;&gt;=&nbsp;limit&nbsp;then\n&nbsp;&nbsp;&nbsp;return&nbsp;0\nend\n--&nbsp;\u7a97\u53e3\u503c+1\nredis.call(\"incr\",&nbsp;KEYS[1])\nif&nbsp;counter&nbsp;==&nbsp;0&nbsp;then\n&nbsp;&nbsp;&nbsp;&nbsp;redis.call(\"pexpire\",&nbsp;KEYS[1],&nbsp;window)\nend\nreturn&nbsp;1\n`<\/pre>\n<pre>package&nbsp;redis\n\nimport&nbsp;(\n&nbsp;&nbsp;&nbsp;\"context\"\n&nbsp;&nbsp;&nbsp;\"errors\"\n&nbsp;&nbsp;&nbsp;\"github.com\/go-redis\/redis\/v8\"\n&nbsp;&nbsp;&nbsp;\"time\"\n)\n\n\/\/&nbsp;FixedWindowLimiter&nbsp;\u56fa\u5b9a\u7a97\u53e3\u9650\u6d41\u5668\ntype&nbsp;FixedWindowLimiter&nbsp;struct&nbsp;{\n&nbsp;&nbsp;&nbsp;limit&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\n&nbsp;&nbsp;&nbsp;window&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\n&nbsp;&nbsp;&nbsp;client&nbsp;*redis.Client&nbsp;\/\/&nbsp;Redis\u5ba2\u6237\u7aef\n&nbsp;&nbsp;&nbsp;script&nbsp;*redis.Script&nbsp;\/\/&nbsp;TryAcquire\u811a\u672c\n}\n\nfunc&nbsp;NewFixedWindowLimiter(client&nbsp;*redis.Client,&nbsp;limit&nbsp;int,&nbsp;window&nbsp;time.Duration)&nbsp;(*FixedWindowLimiter,&nbsp;error)&nbsp;{\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;redis\u8fc7\u671f\u65f6\u95f4\u7cbe\u5ea6\u6700\u5927\u5230\u6beb\u79d2\uff0c\u56e0\u6b64\u7a97\u53e3\u5fc5\u987b\u80fd\u88ab\u6beb\u79d2\u6574\u9664\n&nbsp;&nbsp;&nbsp;if&nbsp;window%time.Millisecond&nbsp;!=&nbsp;0&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;nil,&nbsp;errors.New(\"the&nbsp;window&nbsp;uint&nbsp;must&nbsp;not&nbsp;be&nbsp;less&nbsp;than&nbsp;millisecond\")\n&nbsp;&nbsp;&nbsp;}\n\n&nbsp;&nbsp;&nbsp;return&nbsp;&amp;FixedWindowLimiter{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;limit:&nbsp;&nbsp;limit,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window:&nbsp;int(window&nbsp;\/&nbsp;time.Millisecond),\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client:&nbsp;client,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;script:&nbsp;redis.NewScript(fixedWindowLimiterTryAcquireRedisScript),\n&nbsp;&nbsp;&nbsp;},&nbsp;nil\n}\n\nfunc&nbsp;(l&nbsp;*FixedWindowLimiter)&nbsp;TryAcquire(ctx&nbsp;context.Context,&nbsp;resource&nbsp;string)&nbsp;error&nbsp;{\n&nbsp;&nbsp;&nbsp;success,&nbsp;err&nbsp;:=&nbsp;l.script.Run(ctx,&nbsp;l.client,&nbsp;[]string{resource},&nbsp;l.window,&nbsp;l.limit).Bool()\n&nbsp;&nbsp;&nbsp;if&nbsp;err&nbsp;!=&nbsp;nil&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;err\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u82e5\u5230\u8fbe\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u8bf7\u6c42\u5931\u8d25\n&nbsp;&nbsp;&nbsp;if&nbsp;!success&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ErrAcquireFailed\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;return&nbsp;nil\n}<\/pre>\n<h3>\u6ed1\u52a8\u7a97\u53e3<\/h3>\n<h4>hash\u5b9e\u73b0<\/h4>\n<p>\u6211\u4eec\u4f7f\u7528Redis\u7684<code>hash<\/code>\u5b58\u50a8\u6bcf\u4e2a\u5c0f\u7a97\u53e3\u7684\u8ba1\u6570\uff0c\u6bcf\u6b21\u8bf7\u6c42\u4f1a\u628a\u6240\u6709<code>\u6709\u6548\u7a97\u53e3<\/code>\u7684\u8ba1\u6570\u7d2f\u52a0\u5230<code>count<\/code>\uff0c\u4f7f\u7528<code>hdel<\/code>\u5220\u9664\u5931\u6548\u7a97\u53e3\uff0c\u6700\u540e\u5224\u65ad\u7a97\u53e3\u7684\u603b\u8ba1\u6570\u662f\u5426\u5927\u4e8e\u4e0a\u9650\u3002<\/p>\n<p>\u6211\u4eec\u57fa\u672c\u4e0a\u628a\u6240\u6709\u7684\u903b\u8f91\u90fd\u653e\u5230Lua\u811a\u672c\u91cc\u9762\uff0c\u5176\u4e2d\u5927\u5934\u662f\u5bf9<code>hash<\/code>\u7684\u904d\u5386\uff0c\u65f6\u95f4\u590d\u6742\u5ea6\u662fO(N)\uff0cN\u662f\u5c0f\u7a97\u53e3\u6570\u91cf\uff0c\u6240\u4ee5\u5c0f\u7a97\u53e3\u6570\u91cf\u6700\u597d\u4e0d\u8981\u592a\u591a\u3002<\/p>\n<pre>const&nbsp;slidingWindowLimiterTryAcquireRedisScriptHashImpl&nbsp;=&nbsp;`\n--&nbsp;ARGV[1]:&nbsp;\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\n--&nbsp;ARGV[2]:&nbsp;\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\n--&nbsp;ARGV[3]:&nbsp;\u5f53\u524d\u5c0f\u7a97\u53e3\u503c\n--&nbsp;ARGV[4]:&nbsp;\u8d77\u59cb\u5c0f\u7a97\u53e3\u503c\n\nlocal&nbsp;window&nbsp;=&nbsp;tonumber(ARGV[1])\nlocal&nbsp;limit&nbsp;=&nbsp;tonumber(ARGV[2])\nlocal&nbsp;currentSmallWindow&nbsp;=&nbsp;tonumber(ARGV[3])\nlocal&nbsp;startSmallWindow&nbsp;=&nbsp;tonumber(ARGV[4])\n\n--&nbsp;\u8ba1\u7b97\u5f53\u524d\u7a97\u53e3\u7684\u8bf7\u6c42\u603b\u6570\nlocal&nbsp;counters&nbsp;=&nbsp;redis.call(\"hgetall\",&nbsp;KEYS[1])\nlocal&nbsp;count&nbsp;=&nbsp;0\nfor&nbsp;i&nbsp;=&nbsp;1,&nbsp;#(counters)&nbsp;\/&nbsp;2&nbsp;do&nbsp;\n&nbsp;&nbsp;&nbsp;local&nbsp;smallWindow&nbsp;=&nbsp;tonumber(counters[i&nbsp;*&nbsp;2&nbsp;-&nbsp;1])\n&nbsp;&nbsp;&nbsp;local&nbsp;counter&nbsp;=&nbsp;tonumber(counters[i&nbsp;*&nbsp;2])\n&nbsp;&nbsp;&nbsp;if&nbsp;smallWindow&nbsp;&lt;&nbsp;startSmallWindow&nbsp;then\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.call(\"hdel\",&nbsp;KEYS[1],&nbsp;smallWindow)\n&nbsp;&nbsp;&nbsp;else&nbsp;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count&nbsp;=&nbsp;count&nbsp;+&nbsp;counter\n&nbsp;&nbsp;&nbsp;end\nend\n\n--&nbsp;\u82e5\u5230\u8fbe\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u8bf7\u6c42\u5931\u8d25\nif&nbsp;count&nbsp;&gt;=&nbsp;limit&nbsp;then\n&nbsp;&nbsp;&nbsp;return&nbsp;0\nend\n\n--&nbsp;\u82e5\u6ca1\u5230\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u5f53\u524d\u5c0f\u7a97\u53e3\u8ba1\u6570\u5668+1\uff0c\u8bf7\u6c42\u6210\u529f\nredis.call(\"hincrby\",&nbsp;KEYS[1],&nbsp;currentSmallWindow,&nbsp;1)\nredis.call(\"pexpire\",&nbsp;KEYS[1],&nbsp;window)\nreturn&nbsp;1\n`<\/pre>\n<pre>package&nbsp;redis\n\nimport&nbsp;(\n&nbsp;&nbsp;&nbsp;\"context\"\n&nbsp;&nbsp;&nbsp;\"errors\"\n&nbsp;&nbsp;&nbsp;\"github.com\/go-redis\/redis\/v8\"\n&nbsp;&nbsp;&nbsp;\"time\"\n)\n\n\/\/&nbsp;SlidingWindowLimiter&nbsp;\u6ed1\u52a8\u7a97\u53e3\u9650\u6d41\u5668\ntype&nbsp;SlidingWindowLimiter&nbsp;struct&nbsp;{\n&nbsp;&nbsp;&nbsp;limit&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\n&nbsp;&nbsp;&nbsp;window&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\n&nbsp;&nbsp;&nbsp;smallWindow&nbsp;&nbsp;int64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u5c0f\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\n&nbsp;&nbsp;&nbsp;smallWindows&nbsp;int64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u5c0f\u7a97\u53e3\u6570\u91cf\n&nbsp;&nbsp;&nbsp;client&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*redis.Client&nbsp;\/\/&nbsp;Redis\u5ba2\u6237\u7aef\n&nbsp;&nbsp;&nbsp;script&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*redis.Script&nbsp;\/\/&nbsp;TryAcquire\u811a\u672c\n}\n\nfunc&nbsp;NewSlidingWindowLimiter(client&nbsp;*redis.Client,&nbsp;limit&nbsp;int,&nbsp;window,&nbsp;smallWindow&nbsp;time.Duration)&nbsp;(\n&nbsp;&nbsp;&nbsp;*SlidingWindowLimiter,&nbsp;error)&nbsp;{\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;redis\u8fc7\u671f\u65f6\u95f4\u7cbe\u5ea6\u6700\u5927\u5230\u6beb\u79d2\uff0c\u56e0\u6b64\u7a97\u53e3\u5fc5\u987b\u80fd\u88ab\u6beb\u79d2\u6574\u9664\n&nbsp;&nbsp;&nbsp;if&nbsp;window%time.Millisecond&nbsp;!=&nbsp;0&nbsp;||&nbsp;smallWindow%time.Millisecond&nbsp;!=&nbsp;0&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;nil,&nbsp;errors.New(\"the&nbsp;window&nbsp;uint&nbsp;must&nbsp;not&nbsp;be&nbsp;less&nbsp;than&nbsp;millisecond\")\n&nbsp;&nbsp;&nbsp;}\n\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u7a97\u53e3\u65f6\u95f4\u5fc5\u987b\u80fd\u591f\u88ab\u5c0f\u7a97\u53e3\u65f6\u95f4\u6574\u9664\n&nbsp;&nbsp;&nbsp;if&nbsp;window%smallWindow&nbsp;!=&nbsp;0&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;nil,&nbsp;errors.New(\"window&nbsp;cannot&nbsp;be&nbsp;split&nbsp;by&nbsp;integers\")\n&nbsp;&nbsp;&nbsp;}\n\n&nbsp;&nbsp;&nbsp;return&nbsp;&amp;SlidingWindowLimiter{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;limit:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;limit,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int64(window&nbsp;\/&nbsp;time.Millisecond),\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;smallWindow:&nbsp;&nbsp;int64(smallWindow&nbsp;\/&nbsp;time.Millisecond),\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;smallWindows:&nbsp;int64(window&nbsp;\/&nbsp;smallWindow),\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;script:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.NewScript(slidingWindowLimiterTryAcquireRedisScriptHashImpl),\n&nbsp;&nbsp;&nbsp;},&nbsp;nil\n}\n\nfunc&nbsp;(l&nbsp;*SlidingWindowLimiter)&nbsp;TryAcquire(ctx&nbsp;context.Context,&nbsp;resource&nbsp;string)&nbsp;error&nbsp;{\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u83b7\u53d6\u5f53\u524d\u5c0f\u7a97\u53e3\u503c\n&nbsp;&nbsp;&nbsp;currentSmallWindow&nbsp;:=&nbsp;time.Now().UnixMilli()&nbsp;\/&nbsp;l.smallWindow&nbsp;*&nbsp;l.smallWindow\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u83b7\u53d6\u8d77\u59cb\u5c0f\u7a97\u53e3\u503c\n&nbsp;&nbsp;&nbsp;startSmallWindow&nbsp;:=&nbsp;currentSmallWindow&nbsp;-&nbsp;l.smallWindow*(l.smallWindows-1)\n\n&nbsp;&nbsp;&nbsp;success,&nbsp;err&nbsp;:=&nbsp;l.script.Run(\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctx,&nbsp;l.client,&nbsp;[]string{resource},&nbsp;l.window,&nbsp;l.limit,&nbsp;currentSmallWindow,&nbsp;startSmallWindow).Bool()\n&nbsp;&nbsp;&nbsp;if&nbsp;err&nbsp;!=&nbsp;nil&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;err\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u82e5\u5230\u8fbe\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u8bf7\u6c42\u5931\u8d25\n&nbsp;&nbsp;&nbsp;if&nbsp;!success&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ErrAcquireFailed\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;return&nbsp;nil\n}<\/pre>\n<h4>list\u5b9e\u73b0<\/h4>\n<p>\u5982\u679c\u5c0f\u7a97\u53e3\u6570\u91cf\u7279\u522b\u591a\uff0c\u53ef\u4ee5\u4f7f\u7528<code>list<\/code>\u4f18\u5316\u65f6\u95f4\u590d\u6742\u5ea6\uff0clist\u7684\u7ed3\u6784\u662f\uff1a<\/p>\n<p><code>[counter, smallWindow1, count1, smallWindow2, count2, smallWindow3, count3...]<\/code><\/p>\n<p>\u4e5f\u5c31\u662f\u6211\u4eec\u4f7f\u7528list\u7684\u7b2c\u4e00\u4e2a\u5143\u7d20\u5b58\u50a8\u8ba1\u6570\u5668\uff0c\u6bcf\u4e2a\u7a97\u53e3\u7528\u4e24\u4e2a\u5143\u7d20\u8868\u793a\uff0c\u7b2c\u4e00\u4e2a\u5143\u7d20\u8868\u793a\u5c0f\u7a97\u53e3\u503c\uff0c\u7b2c\u4e8c\u4e2a\u5143\u7d20\u8868\u793a\u8fd9\u4e2a\u5c0f\u7a97\u53e3\u7684\u8ba1\u6570\u3002\u4e0d\u76f4\u63a5\u628a\u5c0f\u7a97\u53e3\u503c\u548c\u8ba1\u6570\u653e\u5230\u4e00\u4e2a\u5143\u7d20\u91cc\u662f\u56e0\u4e3aRedis Lua\u811a\u672c\u91cc\u6ca1\u6709\u5206\u5272\u5b57\u7b26\u4e32\u7684\u51fd\u6570\u3002<\/p>\n<p>\u5177\u4f53\u64cd\u4f5c\u6d41\u7a0b\uff1a<\/p>\n<p>1.\u83b7\u53d6list\u957f\u5ea6<\/p>\n<p>2.\u5982\u679c\u957f\u5ea6\u662f0\uff0c\u8bbe\u7f6ecounter\uff0c\u957f\u5ea6+1<\/p>\n<p>3.\u5982\u679c\u957f\u5ea6\u5927\u4e8e1\uff0c\u83b7\u53d6\u7b2c\u4e8c\u7b2c\u4e09\u4e2a\u5143\u7d20<\/p>\n<p>\u5982\u679c\u8be5\u503c\u5c0f\u4e8e\u8d77\u59cb\u5c0f\u7a97\u53e3\u503c\uff0ccounter-\u7b2c\u4e09\u4e2a\u5143\u7d20\u7684\u503c\uff0c\u5220\u9664\u7b2c\u4e8c\u7b2c\u4e09\u4e2a\u5143\u7d20\uff0c\u957f\u5ea6-2<\/p>\n<p>4.\u5982\u679ccounter\u5927\u4e8e\u7b49\u4e8elimit\uff0c\u8bf7\u6c42\u5931\u8d25<\/p>\n<p>5.\u5982\u679c\u957f\u5ea6\u5927\u4e8e1\uff0c\u83b7\u53d6\u5012\u6570\u7b2c\u4e8c\u7b2c\u4e00\u4e2a\u5143\u7d20<\/p>\n<ul>\n<li>\n<p>\u5982\u679c\u5012\u6570\u7b2c\u4e8c\u4e2a\u5143\u7d20\u5c0f\u7a97\u53e3\u503c\u5927\u4e8e\u7b49\u4e8e\u5f53\u524d\u5c0f\u7a97\u53e3\u503c\uff0c\u8868\u793a\u5f53\u524d\u8bf7\u6c42\u56e0\u4e3a\u7f51\u7edc\u5ef6\u8fdf\u7684\u95ee\u9898\uff0c\u5230\u8fbe\u670d\u52a1\u5668\u7684\u65f6\u5019\uff0c\u7a97\u53e3\u5df2\u7ecf\u8fc7\u65f6\u4e86\uff0c\u628a\u5012\u6570\u7b2c\u4e8c\u4e2a\u5143\u7d20\u5f53\u6210\u5f53\u524d\u5c0f\u7a97\u53e3\uff08\u56e0\u4e3a\u5b83\u66f4\u65b0\uff09\uff0c\u5012\u6570\u7b2c\u4e00\u4e2a\u5143\u7d20\u503c+1<\/p>\n<\/li>\n<li>\n<p>\u5426\u5219\uff0c\u6dfb\u52a0\u65b0\u7684\u7a97\u53e3\u503c\uff0c\u6dfb\u52a0\u65b0\u7684\u8ba1\u6570\uff081\uff09\uff0c\u66f4\u65b0\u8fc7\u671f\u65f6\u95f4<\/p>\n<\/li>\n<\/ul>\n<p>6.\u5426\u5219\uff0c\u6dfb\u52a0\u65b0\u7684\u7a97\u53e3\u503c\uff0c\u6dfb\u52a0\u65b0\u7684\u8ba1\u6570\uff081\uff09\uff0c\u66f4\u65b0\u8fc7\u671f\u65f6\u95f4<\/p>\n<p>7.counter + 1<\/p>\n<p>8.\u8fd4\u56de\u6210\u529f<\/p>\n<pre>const&nbsp;slidingWindowLimiterTryAcquireRedisScriptListImpl&nbsp;=&nbsp;`\n--&nbsp;ARGV[1]:&nbsp;\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\n--&nbsp;ARGV[2]:&nbsp;\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\n--&nbsp;ARGV[3]:&nbsp;\u5f53\u524d\u5c0f\u7a97\u53e3\u503c\n--&nbsp;ARGV[4]:&nbsp;\u8d77\u59cb\u5c0f\u7a97\u53e3\u503c\n\nlocal&nbsp;window&nbsp;=&nbsp;tonumber(ARGV[1])\nlocal&nbsp;limit&nbsp;=&nbsp;tonumber(ARGV[2])\nlocal&nbsp;currentSmallWindow&nbsp;=&nbsp;tonumber(ARGV[3])\nlocal&nbsp;startSmallWindow&nbsp;=&nbsp;tonumber(ARGV[4])\n\n--&nbsp;\u83b7\u53d6list\u957f\u5ea6\nlocal&nbsp;len&nbsp;=&nbsp;redis.call(\"llen\",&nbsp;KEYS[1])\n--&nbsp;\u5982\u679c\u957f\u5ea6\u662f0\uff0c\u8bbe\u7f6ecounter\uff0c\u957f\u5ea6+1\nlocal&nbsp;counter&nbsp;=&nbsp;0\nif&nbsp;len&nbsp;==&nbsp;0&nbsp;then&nbsp;\n&nbsp;&nbsp;&nbsp;redis.call(\"rpush\",&nbsp;KEYS[1],&nbsp;0)\n&nbsp;&nbsp;&nbsp;redis.call(\"pexpire\",&nbsp;KEYS[1],&nbsp;window)\n&nbsp;&nbsp;&nbsp;len&nbsp;=&nbsp;len&nbsp;+&nbsp;1\nelse\n&nbsp;&nbsp;&nbsp;--&nbsp;\u5982\u679c\u957f\u5ea6\u5927\u4e8e1\uff0c\u83b7\u53d6\u7b2c\u4e8c\u7b2c\u4e2a\u5143\u7d20\n&nbsp;&nbsp;&nbsp;local&nbsp;smallWindow1&nbsp;=&nbsp;tonumber(redis.call(\"lindex\",&nbsp;KEYS[1],&nbsp;1))\n&nbsp;&nbsp;&nbsp;counter&nbsp;=&nbsp;tonumber(redis.call(\"lindex\",&nbsp;KEYS[1],&nbsp;0))\n&nbsp;&nbsp;&nbsp;--&nbsp;\u5982\u679c\u8be5\u503c\u5c0f\u4e8e\u8d77\u59cb\u5c0f\u7a97\u53e3\u503c\n&nbsp;&nbsp;&nbsp;if&nbsp;smallWindow1&nbsp;&lt;&nbsp;startSmallWindow&nbsp;then&nbsp;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local&nbsp;count1&nbsp;=&nbsp;redis.call(\"lindex\",&nbsp;KEYS[1],&nbsp;2)\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;counter-\u7b2c\u4e09\u4e2a\u5143\u7d20\u7684\u503c\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;counter&nbsp;=&nbsp;counter&nbsp;-&nbsp;count1\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;\u957f\u5ea6-2\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;len&nbsp;=&nbsp;len&nbsp;-&nbsp;2\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;\u5220\u9664\u7b2c\u4e8c\u7b2c\u4e09\u4e2a\u5143\u7d20\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.call(\"lrem\",&nbsp;KEYS[1],&nbsp;1,&nbsp;smallWindow1)\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.call(\"lrem\",&nbsp;KEYS[1],&nbsp;1,&nbsp;count1)\n&nbsp;&nbsp;&nbsp;end\nend\n\n--&nbsp;\u82e5\u5230\u8fbe\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u8bf7\u6c42\u5931\u8d25\nif&nbsp;counter&nbsp;&gt;=&nbsp;limit&nbsp;then&nbsp;\n&nbsp;&nbsp;&nbsp;return&nbsp;0\nend&nbsp;\n\n--&nbsp;\u5982\u679c\u957f\u5ea6\u5927\u4e8e1\uff0c\u83b7\u53d6\u5012\u6570\u7b2c\u4e8c\u7b2c\u4e00\u4e2a\u5143\u7d20\nif&nbsp;len&nbsp;&gt;&nbsp;1&nbsp;then\n&nbsp;&nbsp;&nbsp;local&nbsp;smallWindown&nbsp;=&nbsp;tonumber(redis.call(\"lindex\",&nbsp;KEYS[1],&nbsp;-2))\n&nbsp;&nbsp;&nbsp;--&nbsp;\u5982\u679c\u5012\u6570\u7b2c\u4e8c\u4e2a\u5143\u7d20\u5c0f\u7a97\u53e3\u503c\u5927\u4e8e\u7b49\u4e8e\u5f53\u524d\u5c0f\u7a97\u53e3\u503c\n&nbsp;&nbsp;&nbsp;if&nbsp;smallWindown&nbsp;&gt;=&nbsp;currentSmallWindow&nbsp;then\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;\u628a\u5012\u6570\u7b2c\u4e8c\u4e2a\u5143\u7d20\u5f53\u6210\u5f53\u524d\u5c0f\u7a97\u53e3\uff08\u56e0\u4e3a\u5b83\u66f4\u65b0\uff09\uff0c\u5012\u6570\u7b2c\u4e00\u4e2a\u5143\u7d20\u503c+1\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local&nbsp;countn&nbsp;=&nbsp;redis.call(\"lindex\",&nbsp;KEYS[1],&nbsp;-1)\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.call(\"lset\",&nbsp;KEYS[1],&nbsp;-1,&nbsp;countn&nbsp;+&nbsp;1)\n&nbsp;&nbsp;&nbsp;else&nbsp;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--&nbsp;\u5426\u5219\uff0c\u6dfb\u52a0\u65b0\u7684\u7a97\u53e3\u503c\uff0c\u6dfb\u52a0\u65b0\u7684\u8ba1\u6570\uff081\uff09\uff0c\u66f4\u65b0\u8fc7\u671f\u65f6\u95f4\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.call(\"rpush\",&nbsp;KEYS[1],&nbsp;currentSmallWindow,&nbsp;1)\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.call(\"pexpire\",&nbsp;KEYS[1],&nbsp;window)\n&nbsp;&nbsp;&nbsp;end\nelse&nbsp;\n&nbsp;&nbsp;&nbsp;--&nbsp;\u5426\u5219\uff0c\u6dfb\u52a0\u65b0\u7684\u7a97\u53e3\u503c\uff0c\u6dfb\u52a0\u65b0\u7684\u8ba1\u6570\uff081\uff09\uff0c\u66f4\u65b0\u8fc7\u671f\u65f6\u95f4\n&nbsp;&nbsp;&nbsp;redis.call(\"rpush\",&nbsp;KEYS[1],&nbsp;currentSmallWindow,&nbsp;1)\n&nbsp;&nbsp;&nbsp;redis.call(\"pexpire\",&nbsp;KEYS[1],&nbsp;window)\nend&nbsp;\n\n--&nbsp;counter&nbsp;+&nbsp;1\u5e76\u66f4\u65b0\nredis.call(\"lset\",&nbsp;KEYS[1],&nbsp;0,&nbsp;counter&nbsp;+&nbsp;1)\nreturn&nbsp;1\n`<\/pre>\n<p>\u7b97\u6cd5\u90fd\u662f\u64cd\u4f5c<code>list<\/code>\u5934\u90e8\u6216\u8005\u5c3e\u90e8\uff0c\u6240\u4ee5\u65f6\u95f4\u590d\u6742\u5ea6\u63a5\u8fd1O(1)<\/p>\n<h3>\u6f0f\u6876\u7b97\u6cd5<\/h3>\n<p>\u6f0f\u6876\u9700\u8981\u4fdd\u5b58\u5f53\u524d\u6c34\u4f4d\u548c\u4e0a\u6b21\u653e\u6c34\u65f6\u95f4\uff0c\u56e0\u6b64\u6211\u4eec\u4f7f\u7528<code>hash<\/code>\u6765\u4fdd\u5b58\u8fd9\u4e24\u4e2a\u503c\u3002<\/p>\n<pre>const&nbsp;leakyBucketLimiterTryAcquireRedisScript&nbsp;=&nbsp;`\n--&nbsp;ARGV[1]:&nbsp;\u6700\u9ad8\u6c34\u4f4d\n--&nbsp;ARGV[2]:&nbsp;\u6c34\u6d41\u901f\u5ea6\/\u79d2\n--&nbsp;ARGV[3]:&nbsp;\u5f53\u524d\u65f6\u95f4\uff08\u79d2\uff09\n\nlocal&nbsp;peakLevel&nbsp;=&nbsp;tonumber(ARGV[1])\nlocal&nbsp;currentVelocity&nbsp;=&nbsp;tonumber(ARGV[2])\nlocal&nbsp;now&nbsp;=&nbsp;tonumber(ARGV[3])\n\nlocal&nbsp;lastTime&nbsp;=&nbsp;tonumber(redis.call(\"hget\",&nbsp;KEYS[1],&nbsp;\"lastTime\"))\nlocal&nbsp;currentLevel&nbsp;=&nbsp;tonumber(redis.call(\"hget\",&nbsp;KEYS[1],&nbsp;\"currentLevel\"))\n--&nbsp;\u521d\u59cb\u5316\nif&nbsp;lastTime&nbsp;==&nbsp;nil&nbsp;then&nbsp;\n&nbsp;&nbsp;&nbsp;lastTime&nbsp;=&nbsp;now\n&nbsp;&nbsp;&nbsp;currentLevel&nbsp;=&nbsp;0\n&nbsp;&nbsp;&nbsp;redis.call(\"hmset\",&nbsp;KEYS[1],&nbsp;\"currentLevel\",&nbsp;currentLevel,&nbsp;\"lastTime\",&nbsp;lastTime)\nend&nbsp;\n\n--&nbsp;\u5c1d\u8bd5\u653e\u6c34\n--&nbsp;\u8ddd\u79bb\u4e0a\u6b21\u653e\u6c34\u7684\u65f6\u95f4\nlocal&nbsp;interval&nbsp;=&nbsp;now&nbsp;-&nbsp;lastTime\nif&nbsp;interval&nbsp;&gt;&nbsp;0&nbsp;then\n&nbsp;&nbsp;&nbsp;--&nbsp;\u5f53\u524d\u6c34\u4f4d-\u8ddd\u79bb\u4e0a\u6b21\u653e\u6c34\u7684\u65f6\u95f4(\u79d2)*\u6c34\u6d41\u901f\u5ea6\n&nbsp;&nbsp;&nbsp;local&nbsp;newLevel&nbsp;=&nbsp;currentLevel&nbsp;-&nbsp;interval&nbsp;*&nbsp;currentVelocity\n&nbsp;&nbsp;&nbsp;if&nbsp;newLevel&nbsp;&lt;&nbsp;0&nbsp;then&nbsp;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;newLevel&nbsp;=&nbsp;0\n&nbsp;&nbsp;&nbsp;end&nbsp;\n&nbsp;&nbsp;&nbsp;currentLevel&nbsp;=&nbsp;newLevel\n&nbsp;&nbsp;&nbsp;redis.call(\"hmset\",&nbsp;KEYS[1],&nbsp;\"currentLevel\",&nbsp;newLevel,&nbsp;\"lastTime\",&nbsp;now)\nend\n\n--&nbsp;\u82e5\u5230\u8fbe\u6700\u9ad8\u6c34\u4f4d\uff0c\u8bf7\u6c42\u5931\u8d25\nif&nbsp;currentLevel&nbsp;&gt;=&nbsp;peakLevel&nbsp;then\n&nbsp;&nbsp;&nbsp;return&nbsp;0\nend\n--&nbsp;\u82e5\u6ca1\u6709\u5230\u8fbe\u6700\u9ad8\u6c34\u4f4d\uff0c\u5f53\u524d\u6c34\u4f4d+1\uff0c\u8bf7\u6c42\u6210\u529f\nredis.call(\"hincrby\",&nbsp;KEYS[1],&nbsp;\"currentLevel\",&nbsp;1)\nredis.call(\"expire\",&nbsp;KEYS[1],&nbsp;peakLevel&nbsp;\/&nbsp;currentVelocity)\nreturn&nbsp;1\n`<\/pre>\n<pre>package&nbsp;redis\n\nimport&nbsp;(\n&nbsp;&nbsp;&nbsp;\"context\"\n&nbsp;&nbsp;&nbsp;\"github.com\/go-redis\/redis\/v8\"\n&nbsp;&nbsp;&nbsp;\"time\"\n)\n\n\/\/&nbsp;LeakyBucketLimiter&nbsp;\u6f0f\u6876\u9650\u6d41\u5668\ntype&nbsp;LeakyBucketLimiter&nbsp;struct&nbsp;{\n&nbsp;&nbsp;&nbsp;peakLevel&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u6700\u9ad8\u6c34\u4f4d\n&nbsp;&nbsp;&nbsp;currentVelocity&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u6c34\u6d41\u901f\u5ea6\/\u79d2\n&nbsp;&nbsp;&nbsp;client&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*redis.Client&nbsp;\/\/&nbsp;Redis\u5ba2\u6237\u7aef\n&nbsp;&nbsp;&nbsp;script&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*redis.Script&nbsp;\/\/&nbsp;TryAcquire\u811a\u672c\n}\n\nfunc&nbsp;NewLeakyBucketLimiter(client&nbsp;*redis.Client,&nbsp;peakLevel,&nbsp;currentVelocity&nbsp;int)&nbsp;*LeakyBucketLimiter&nbsp;{\n&nbsp;&nbsp;&nbsp;return&nbsp;&amp;LeakyBucketLimiter{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;peakLevel:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;peakLevel,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;currentVelocity:&nbsp;currentVelocity,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;script:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.NewScript(leakyBucketLimiterTryAcquireRedisScript),\n&nbsp;&nbsp;&nbsp;}\n}\n\nfunc&nbsp;(l&nbsp;*LeakyBucketLimiter)&nbsp;TryAcquire(ctx&nbsp;context.Context,&nbsp;resource&nbsp;string)&nbsp;error&nbsp;{\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u5f53\u524d\u65f6\u95f4\n&nbsp;&nbsp;&nbsp;now&nbsp;:=&nbsp;time.Now().Unix()\n&nbsp;&nbsp;&nbsp;success,&nbsp;err&nbsp;:=&nbsp;l.script.Run(ctx,&nbsp;l.client,&nbsp;[]string{resource},&nbsp;l.peakLevel,&nbsp;l.currentVelocity,&nbsp;now).Bool()\n&nbsp;&nbsp;&nbsp;if&nbsp;err&nbsp;!=&nbsp;nil&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;err\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u82e5\u5230\u8fbe\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u8bf7\u6c42\u5931\u8d25\n&nbsp;&nbsp;&nbsp;if&nbsp;!success&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ErrAcquireFailed\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;return&nbsp;nil\n}<\/pre>\n<h3>\u4ee4\u724c\u6876<\/h3>\n<p>\u4ee4\u724c\u6876\u53ef\u4ee5\u770b\u4f5c\u662f\u6f0f\u6876\u7684\u76f8\u53cd\u7b97\u6cd5\uff0c\u5b83\u4eec\u4e00\u4e2a\u662f\u628a\u6c34\u5012\u8fdb\u6876\u91cc\uff0c\u4e00\u4e2a\u662f\u4ece\u6876\u91cc\u83b7\u53d6\u4ee4\u724c\u3002<\/p>\n<pre>const&nbsp;tokenBucketLimiterTryAcquireRedisScript&nbsp;=&nbsp;`\n--&nbsp;ARGV[1]:&nbsp;\u5bb9\u91cf\n--&nbsp;ARGV[2]:&nbsp;\u53d1\u653e\u4ee4\u724c\u901f\u7387\/\u79d2\n--&nbsp;ARGV[3]:&nbsp;\u5f53\u524d\u65f6\u95f4\uff08\u79d2\uff09\n\nlocal&nbsp;capacity&nbsp;=&nbsp;tonumber(ARGV[1])\nlocal&nbsp;rate&nbsp;=&nbsp;tonumber(ARGV[2])\nlocal&nbsp;now&nbsp;=&nbsp;tonumber(ARGV[3])\n\nlocal&nbsp;lastTime&nbsp;=&nbsp;tonumber(redis.call(\"hget\",&nbsp;KEYS[1],&nbsp;\"lastTime\"))\nlocal&nbsp;currentTokens&nbsp;=&nbsp;tonumber(redis.call(\"hget\",&nbsp;KEYS[1],&nbsp;\"currentTokens\"))\n--&nbsp;\u521d\u59cb\u5316\nif&nbsp;lastTime&nbsp;==&nbsp;nil&nbsp;then&nbsp;\n&nbsp;&nbsp;&nbsp;lastTime&nbsp;=&nbsp;now\n&nbsp;&nbsp;&nbsp;currentTokens&nbsp;=&nbsp;capacity\n&nbsp;&nbsp;&nbsp;redis.call(\"hmset\",&nbsp;KEYS[1],&nbsp;\"currentTokens\",&nbsp;currentTokens,&nbsp;\"lastTime\",&nbsp;lastTime)\nend&nbsp;\n\n--&nbsp;\u5c1d\u8bd5\u53d1\u653e\u4ee4\u724c\n--&nbsp;\u8ddd\u79bb\u4e0a\u6b21\u53d1\u653e\u4ee4\u724c\u7684\u65f6\u95f4\nlocal&nbsp;interval&nbsp;=&nbsp;now&nbsp;-&nbsp;lastTime\nif&nbsp;interval&nbsp;&gt;&nbsp;0&nbsp;then\n&nbsp;&nbsp;&nbsp;--&nbsp;\u5f53\u524d\u4ee4\u724c\u6570\u91cf+\u8ddd\u79bb\u4e0a\u6b21\u53d1\u653e\u4ee4\u724c\u7684\u65f6\u95f4(\u79d2)*\u53d1\u653e\u4ee4\u724c\u901f\u7387\n&nbsp;&nbsp;&nbsp;local&nbsp;newTokens&nbsp;=&nbsp;currentTokens&nbsp;+&nbsp;interval&nbsp;*&nbsp;rate\n&nbsp;&nbsp;&nbsp;if&nbsp;newTokens&nbsp;&gt;&nbsp;capacity&nbsp;then&nbsp;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;newTokens&nbsp;=&nbsp;capacity\n&nbsp;&nbsp;&nbsp;end&nbsp;\n&nbsp;&nbsp;&nbsp;currentTokens&nbsp;=&nbsp;newTokens\n&nbsp;&nbsp;&nbsp;redis.call(\"hmset\",&nbsp;KEYS[1],&nbsp;\"currentTokens\",&nbsp;newTokens,&nbsp;\"lastTime\",&nbsp;now)\nend\n\n--&nbsp;\u5982\u679c\u6ca1\u6709\u4ee4\u724c\uff0c\u8bf7\u6c42\u5931\u8d25\nif&nbsp;currentTokens&nbsp;==&nbsp;0&nbsp;then\n&nbsp;&nbsp;&nbsp;return&nbsp;0\nend\n--&nbsp;\u679c\u6709\u4ee4\u724c\uff0c\u5f53\u524d\u4ee4\u724c-1\uff0c\u8bf7\u6c42\u6210\u529f\nredis.call(\"hincrby\",&nbsp;KEYS[1],&nbsp;\"currentTokens\",&nbsp;-1)\nredis.call(\"expire\",&nbsp;KEYS[1],&nbsp;capacity&nbsp;\/&nbsp;rate)\nreturn&nbsp;1\n`<\/pre>\n<pre>package&nbsp;redis\n\nimport&nbsp;(\n&nbsp;&nbsp;&nbsp;\"context\"\n&nbsp;&nbsp;&nbsp;\"github.com\/go-redis\/redis\/v8\"\n&nbsp;&nbsp;&nbsp;\"time\"\n)\n\n\/\/&nbsp;TokenBucketLimiter&nbsp;\u4ee4\u724c\u6876\u9650\u6d41\u5668\ntype&nbsp;TokenBucketLimiter&nbsp;struct&nbsp;{\n&nbsp;&nbsp;&nbsp;capacity&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u5bb9\u91cf\n&nbsp;&nbsp;&nbsp;rate&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u53d1\u653e\u4ee4\u724c\u901f\u7387\/\u79d2\n&nbsp;&nbsp;&nbsp;client&nbsp;&nbsp;&nbsp;*redis.Client&nbsp;\/\/&nbsp;Redis\u5ba2\u6237\u7aef\n&nbsp;&nbsp;&nbsp;script&nbsp;&nbsp;&nbsp;*redis.Script&nbsp;\/\/&nbsp;TryAcquire\u811a\u672c\n}\n\nfunc&nbsp;NewTokenBucketLimiter(client&nbsp;*redis.Client,&nbsp;capacity,&nbsp;rate&nbsp;int)&nbsp;*TokenBucketLimiter&nbsp;{\n&nbsp;&nbsp;&nbsp;return&nbsp;&amp;TokenBucketLimiter{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;capacity:&nbsp;capacity,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rate:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rate,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client:&nbsp;&nbsp;&nbsp;client,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;script:&nbsp;&nbsp;&nbsp;redis.NewScript(tokenBucketLimiterTryAcquireRedisScript),\n&nbsp;&nbsp;&nbsp;}\n}\n\nfunc&nbsp;(l&nbsp;*TokenBucketLimiter)&nbsp;TryAcquire(ctx&nbsp;context.Context,&nbsp;resource&nbsp;string)&nbsp;error&nbsp;{\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u5f53\u524d\u65f6\u95f4\n&nbsp;&nbsp;&nbsp;now&nbsp;:=&nbsp;time.Now().Unix()\n&nbsp;&nbsp;&nbsp;success,&nbsp;err&nbsp;:=&nbsp;l.script.Run(ctx,&nbsp;l.client,&nbsp;[]string{resource},&nbsp;l.capacity,&nbsp;l.rate,&nbsp;now).Bool()\n&nbsp;&nbsp;&nbsp;if&nbsp;err&nbsp;!=&nbsp;nil&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;err\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u82e5\u5230\u8fbe\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u8bf7\u6c42\u5931\u8d25\n&nbsp;&nbsp;&nbsp;if&nbsp;!success&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;ErrAcquireFailed\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;return&nbsp;nil\n}<\/pre>\n<h3>\u6ed1\u52a8\u65e5\u5fd7<\/h3>\n<p>\u7b97\u6cd5\u6d41\u7a0b\u4e0e\u6ed1\u52a8\u7a97\u53e3\u76f8\u540c\uff0c\u53ea\u662f\u5b83\u53ef\u4ee5\u6307\u5b9a\u591a\u4e2a\u7b56\u7565\uff0c\u540c\u65f6\u5728\u8bf7\u6c42\u5931\u8d25\u7684\u65f6\u5019\uff0c\u9700\u8981\u901a\u77e5\u8c03\u7528\u65b9\u662f\u88ab\u54ea\u4e2a\u7b56\u7565\u6240\u62e6\u622a\u3002<\/p>\n<pre>const&nbsp;slidingLogLimiterTryAcquireRedisScriptHashImpl&nbsp;=&nbsp;`\n--&nbsp;ARGV[1]:&nbsp;\u5f53\u524d\u5c0f\u7a97\u53e3\u503c\n--&nbsp;ARGV[2]:&nbsp;\u7b2c\u4e00\u4e2a\u7b56\u7565\u7684\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\n--&nbsp;ARGV[i&nbsp;*&nbsp;2&nbsp;+&nbsp;1]:&nbsp;\u6bcf\u4e2a\u7b56\u7565\u7684\u8d77\u59cb\u5c0f\u7a97\u53e3\u503c\n--&nbsp;ARGV[i&nbsp;*&nbsp;2&nbsp;+&nbsp;2]:&nbsp;\u6bcf\u4e2a\u7b56\u7565\u7684\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\n\nlocal&nbsp;currentSmallWindow&nbsp;=&nbsp;tonumber(ARGV[1])\n--&nbsp;\u7b2c\u4e00\u4e2a\u7b56\u7565\u7684\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\nlocal&nbsp;window&nbsp;=&nbsp;tonumber(ARGV[2])\n--&nbsp;\u7b2c\u4e00\u4e2a\u7b56\u7565\u7684\u8d77\u59cb\u5c0f\u7a97\u53e3\u503c\nlocal&nbsp;startSmallWindow&nbsp;=&nbsp;tonumber(ARGV[3])\nlocal&nbsp;strategiesLen&nbsp;=&nbsp;#(ARGV)&nbsp;\/&nbsp;2&nbsp;-&nbsp;1\n\n--&nbsp;\u8ba1\u7b97\u6bcf\u4e2a\u7b56\u7565\u5f53\u524d\u7a97\u53e3\u7684\u8bf7\u6c42\u603b\u6570\nlocal&nbsp;counters&nbsp;=&nbsp;redis.call(\"hgetall\",&nbsp;KEYS[1])\nlocal&nbsp;counts&nbsp;=&nbsp;{}\n--&nbsp;\u521d\u59cb\u5316counts\nfor&nbsp;j&nbsp;=&nbsp;1,&nbsp;strategiesLen&nbsp;do\n&nbsp;&nbsp;&nbsp;counts[j]&nbsp;=&nbsp;0\nend\n\nfor&nbsp;i&nbsp;=&nbsp;1,&nbsp;#(counters)&nbsp;\/&nbsp;2&nbsp;do&nbsp;\n&nbsp;&nbsp;&nbsp;local&nbsp;smallWindow&nbsp;=&nbsp;tonumber(counters[i&nbsp;*&nbsp;2&nbsp;-&nbsp;1])\n&nbsp;&nbsp;&nbsp;local&nbsp;counter&nbsp;=&nbsp;tonumber(counters[i&nbsp;*&nbsp;2])\n&nbsp;&nbsp;&nbsp;if&nbsp;smallWindow&nbsp;&lt;&nbsp;startSmallWindow&nbsp;then\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.call(\"hdel\",&nbsp;KEYS[1],&nbsp;smallWindow)\n&nbsp;&nbsp;&nbsp;else&nbsp;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;j&nbsp;=&nbsp;1,&nbsp;strategiesLen&nbsp;do\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;smallWindow&nbsp;&gt;=&nbsp;tonumber(ARGV[j&nbsp;*&nbsp;2&nbsp;+&nbsp;1])&nbsp;then\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;counts[j]&nbsp;=&nbsp;counts[j]&nbsp;+&nbsp;counter\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end\n&nbsp;&nbsp;&nbsp;end\nend\n\n--&nbsp;\u82e5\u5230\u8fbe\u5bf9\u5e94\u7b56\u7565\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u8bf7\u6c42\u5931\u8d25\uff0c\u8fd4\u56de\u8fdd\u80cc\u7684\u7b56\u7565\u4e0b\u6807\nfor&nbsp;i&nbsp;=&nbsp;1,&nbsp;strategiesLen&nbsp;do\n&nbsp;&nbsp;&nbsp;if&nbsp;counts[i]&nbsp;&gt;=&nbsp;tonumber(ARGV[i&nbsp;*&nbsp;2&nbsp;+&nbsp;2])&nbsp;then\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;i&nbsp;-&nbsp;1\n&nbsp;&nbsp;&nbsp;end\nend\n\n--&nbsp;\u82e5\u6ca1\u5230\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u5f53\u524d\u5c0f\u7a97\u53e3\u8ba1\u6570\u5668+1\uff0c\u8bf7\u6c42\u6210\u529f\nredis.call(\"hincrby\",&nbsp;KEYS[1],&nbsp;currentSmallWindow,&nbsp;1)\nredis.call(\"pexpire\",&nbsp;KEYS[1],&nbsp;window)\nreturn&nbsp;-1\n`<\/pre>\n<pre>package&nbsp;redis\n\nimport&nbsp;(\n&nbsp;&nbsp;&nbsp;\"context\"\n&nbsp;&nbsp;&nbsp;\"errors\"\n&nbsp;&nbsp;&nbsp;\"fmt\"\n&nbsp;&nbsp;&nbsp;\"github.com\/go-redis\/redis\/v8\"\n&nbsp;&nbsp;&nbsp;\"sort\"\n&nbsp;&nbsp;&nbsp;\"time\"\n)\n\n\/\/&nbsp;ViolationStrategyError&nbsp;\u8fdd\u80cc\u7b56\u7565\u9519\u8bef\ntype&nbsp;ViolationStrategyError&nbsp;struct&nbsp;{\n&nbsp;&nbsp;&nbsp;Limit&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\n&nbsp;&nbsp;&nbsp;Window&nbsp;time.Duration&nbsp;\/\/&nbsp;\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\n}\n\nfunc&nbsp;(e&nbsp;*ViolationStrategyError)&nbsp;Error()&nbsp;string&nbsp;{\n&nbsp;&nbsp;&nbsp;return&nbsp;fmt.Sprintf(\"violation&nbsp;strategy&nbsp;that&nbsp;limit&nbsp;=&nbsp;%d&nbsp;and&nbsp;window&nbsp;=&nbsp;%d\",&nbsp;e.Limit,&nbsp;e.Window)\n}\n\n\/\/&nbsp;SlidingLogLimiterStrategy&nbsp;\u6ed1\u52a8\u65e5\u5fd7\u9650\u6d41\u5668\u7684\u7b56\u7565\ntype&nbsp;SlidingLogLimiterStrategy&nbsp;struct&nbsp;{\n&nbsp;&nbsp;&nbsp;limit&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\n&nbsp;&nbsp;&nbsp;window&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int64&nbsp;\/\/&nbsp;\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\n&nbsp;&nbsp;&nbsp;smallWindows&nbsp;int64&nbsp;\/\/&nbsp;\u5c0f\u7a97\u53e3\u6570\u91cf\n}\n\nfunc&nbsp;NewSlidingLogLimiterStrategy(limit&nbsp;int,&nbsp;window&nbsp;time.Duration)&nbsp;*SlidingLogLimiterStrategy&nbsp;{\n&nbsp;&nbsp;&nbsp;return&nbsp;&amp;SlidingLogLimiterStrategy{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;limit:&nbsp;&nbsp;limit,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;window:&nbsp;int64(window),\n&nbsp;&nbsp;&nbsp;}\n}\n\n\/\/&nbsp;SlidingLogLimiter&nbsp;\u6ed1\u52a8\u65e5\u5fd7\u9650\u6d41\u5668\ntype&nbsp;SlidingLogLimiter&nbsp;struct&nbsp;{\n&nbsp;&nbsp;&nbsp;strategies&nbsp;&nbsp;[]*SlidingLogLimiterStrategy&nbsp;\/\/&nbsp;\u6ed1\u52a8\u65e5\u5fd7\u9650\u6d41\u5668\u7b56\u7565\u5217\u8868\n&nbsp;&nbsp;&nbsp;smallWindow&nbsp;int64&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u5c0f\u7a97\u53e3\u65f6\u95f4\u5927\u5c0f\n&nbsp;&nbsp;&nbsp;client&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*redis.Client&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;Redis\u5ba2\u6237\u7aef\n&nbsp;&nbsp;&nbsp;script&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*redis.Script&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;TryAcquire\u811a\u672c\n}\n\nfunc&nbsp;NewSlidingLogLimiter(client&nbsp;*redis.Client,&nbsp;smallWindow&nbsp;time.Duration,&nbsp;strategies&nbsp;...*SlidingLogLimiterStrategy)&nbsp;(\n&nbsp;&nbsp;&nbsp;*SlidingLogLimiter,&nbsp;error)&nbsp;{\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u590d\u5236\u7b56\u7565\u907f\u514d\u88ab\u4fee\u6539\n&nbsp;&nbsp;&nbsp;strategies&nbsp;=&nbsp;append(make([]*SlidingLogLimiterStrategy,&nbsp;0,&nbsp;len(strategies)),&nbsp;strategies...)\n\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u4e0d\u80fd\u4e0d\u8bbe\u7f6e\u7b56\u7565\n&nbsp;&nbsp;&nbsp;if&nbsp;len(strategies)&nbsp;==&nbsp;0&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;nil,&nbsp;errors.New(\"must&nbsp;be&nbsp;set&nbsp;strategies\")\n&nbsp;&nbsp;&nbsp;}\n\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;redis\u8fc7\u671f\u65f6\u95f4\u7cbe\u5ea6\u6700\u5927\u5230\u6beb\u79d2\uff0c\u56e0\u6b64\u7a97\u53e3\u5fc5\u987b\u80fd\u88ab\u6beb\u79d2\u6574\u9664\n&nbsp;&nbsp;&nbsp;if&nbsp;smallWindow%time.Millisecond&nbsp;!=&nbsp;0&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;nil,&nbsp;errors.New(\"the&nbsp;window&nbsp;uint&nbsp;must&nbsp;not&nbsp;be&nbsp;less&nbsp;than&nbsp;millisecond\")\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;smallWindow&nbsp;=&nbsp;smallWindow&nbsp;\/&nbsp;time.Millisecond\n&nbsp;&nbsp;&nbsp;for&nbsp;_,&nbsp;strategy&nbsp;:=&nbsp;range&nbsp;strategies&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;strategy.window%int64(time.Millisecond)&nbsp;!=&nbsp;0&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;nil,&nbsp;errors.New(\"the&nbsp;window&nbsp;uint&nbsp;must&nbsp;not&nbsp;be&nbsp;less&nbsp;than&nbsp;millisecond\")\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strategy.window&nbsp;=&nbsp;strategy.window&nbsp;\/&nbsp;int64(time.Millisecond)\n&nbsp;&nbsp;&nbsp;}\n\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u6392\u5e8f\u7b56\u7565\uff0c\u7a97\u53e3\u65f6\u95f4\u5927\u7684\u6392\u524d\u9762\uff0c\u76f8\u540c\u7a97\u53e3\u4e0a\u9650\u5927\u7684\u6392\u524d\u9762\n&nbsp;&nbsp;&nbsp;sort.Slice(strategies,&nbsp;func(i,&nbsp;j&nbsp;int)&nbsp;bool&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a,&nbsp;b&nbsp;:=&nbsp;strategies[i],&nbsp;strategies[j]\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;a.window&nbsp;==&nbsp;b.window&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;a.limit&nbsp;&gt;&nbsp;b.limit\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;a.window&nbsp;&gt;&nbsp;b.window\n&nbsp;&nbsp;&nbsp;})\n\n&nbsp;&nbsp;&nbsp;for&nbsp;i,&nbsp;strategy&nbsp;:=&nbsp;range&nbsp;strategies&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u968f\u7740\u7a97\u53e3\u65f6\u95f4\u53d8\u5c0f\uff0c\u7a97\u53e3\u4e0a\u9650\u4e5f\u5e94\u8be5\u53d8\u5c0f\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;i&nbsp;&gt;&nbsp;0&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;strategy.limit&nbsp;&gt;=&nbsp;strategies[i-1].limit&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;nil,&nbsp;errors.New(\"the&nbsp;smaller&nbsp;window&nbsp;should&nbsp;be&nbsp;the&nbsp;smaller&nbsp;limit\")\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u7a97\u53e3\u65f6\u95f4\u5fc5\u987b\u80fd\u591f\u88ab\u5c0f\u7a97\u53e3\u65f6\u95f4\u6574\u9664\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;strategy.window%int64(smallWindow)&nbsp;!=&nbsp;0&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;nil,&nbsp;errors.New(\"window&nbsp;cannot&nbsp;be&nbsp;split&nbsp;by&nbsp;integers\")\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strategy.smallWindows&nbsp;=&nbsp;strategy.window&nbsp;\/&nbsp;int64(smallWindow)\n&nbsp;&nbsp;&nbsp;}\n\n&nbsp;&nbsp;&nbsp;return&nbsp;&amp;SlidingLogLimiter{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strategies:&nbsp;&nbsp;strategies,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;smallWindow:&nbsp;int64(smallWindow),\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;script:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;redis.NewScript(slidingLogLimiterTryAcquireRedisScriptHashImpl),\n&nbsp;&nbsp;&nbsp;},&nbsp;nil\n}\n\nfunc&nbsp;(l&nbsp;*SlidingLogLimiter)&nbsp;TryAcquire(ctx&nbsp;context.Context,&nbsp;resource&nbsp;string)&nbsp;error&nbsp;{\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u83b7\u53d6\u5f53\u524d\u5c0f\u7a97\u53e3\u503c\n&nbsp;&nbsp;&nbsp;currentSmallWindow&nbsp;:=&nbsp;time.Now().UnixMilli()&nbsp;\/&nbsp;l.smallWindow&nbsp;*&nbsp;l.smallWindow\n&nbsp;&nbsp;&nbsp;args&nbsp;:=&nbsp;make([]interface{},&nbsp;len(l.strategies)*2+2)\n&nbsp;&nbsp;&nbsp;args[0]&nbsp;=&nbsp;currentSmallWindow\n&nbsp;&nbsp;&nbsp;args[1]&nbsp;=&nbsp;l.strategies[0].window\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u83b7\u53d6\u6bcf\u4e2a\u7b56\u7565\u7684\u8d77\u59cb\u5c0f\u7a97\u53e3\u503c\n&nbsp;&nbsp;&nbsp;for&nbsp;i,&nbsp;strategy&nbsp;:=&nbsp;range&nbsp;l.strategies&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;args[i*2+2]&nbsp;=&nbsp;currentSmallWindow&nbsp;-&nbsp;l.smallWindow*(strategy.smallWindows-1)\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;args[i*2+3]&nbsp;=&nbsp;strategy.limit\n&nbsp;&nbsp;&nbsp;}\n\n&nbsp;&nbsp;&nbsp;index,&nbsp;err&nbsp;:=&nbsp;l.script.Run(\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctx,&nbsp;l.client,&nbsp;[]string{resource},&nbsp;args...).Int()\n&nbsp;&nbsp;&nbsp;if&nbsp;err&nbsp;!=&nbsp;nil&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;err\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;\/\/&nbsp;\u82e5\u5230\u8fbe\u7a97\u53e3\u8bf7\u6c42\u4e0a\u9650\uff0c\u8bf7\u6c42\u5931\u8d25\n&nbsp;&nbsp;&nbsp;if&nbsp;index&nbsp;!=&nbsp;-1&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;&amp;ViolationStrategyError{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Limit:&nbsp;&nbsp;l.strategies[index].limit,\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Window:&nbsp;time.Duration(l.strategies[index].window),\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;return&nbsp;nil\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u600e\u4e48\u4f7f\u7528Go+Redis\u5b9e\u73b0\u5e38\u89c1&#46;&#46;&#46;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[101],"tags":[],"class_list":["post-201841","post","type-post","status-publish","format-standard","hentry","category-database"],"_links":{"self":[{"href":"https:\/\/server.hk\/cnblog\/wp-json\/wp\/v2\/posts\/201841","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/server.hk\/cnblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/server.hk\/cnblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/server.hk\/cnblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/server.hk\/cnblog\/wp-json\/wp\/v2\/comments?post=201841"}],"version-history":[{"count":0,"href":"https:\/\/server.hk\/cnblog\/wp-json\/wp\/v2\/posts\/201841\/revisions"}],"wp:attachment":[{"href":"https:\/\/server.hk\/cnblog\/wp-json\/wp\/v2\/media?parent=201841"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/server.hk\/cnblog\/wp-json\/wp\/v2\/categories?post=201841"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/server.hk\/cnblog\/wp-json\/wp\/v2\/tags?post=201841"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}