{
  "version": "https://jsonfeed.org/version/1", 
  "title": "Ruby", 
  "description": "A dynamic, interpreted, open source programming language with a focus on simplicity and productivity.", 
  "home_page_url": "https://www.v2ex.com/go/ruby", 
  "feed_url": "https://www.v2ex.com/feed/ruby.json", 
  "icon": "https://cdn.v2ex.com/navatar/0504/9e90/367_large.png?m=1579009612", 
  "favicon": "https://cdn.v2ex.com/navatar/0504/9e90/367_normal.png?m=1579009612", 
  "items": [
    {
      "author": {
        "url": "https://www.v2ex.com/member/mizuhashi", 
        "name": "mizuhashi", 
        "avatar": "https://cdn.v2ex.com/avatar/8854/0e78/157347_large.png?m=1774621158"
      }, 
      "url": "https://www.v2ex.com/t/1195605", 
      "date_modified": "2026-03-03T23:15:50+00:00", 
      "content_html": "<p><a href=\"https://identity.foundation/didcomm-messaging/spec/v2.1/\" rel=\"nofollow\">DIDComm</a>\u662f\u4e00\u500b\u57fa\u65bc W3C DID \uff08\u53bb\u4e2d\u5fc3\u5316 ID \uff09\u7684\u901a\u4fe1\u5354\u8b70\u3002\u5982\u679c\u4e00\u500b\u670d\u52d9\u5668\u60f3\u548c\u53e6\u4e00\u500b\u5be6\u73fe\u4e86 DIDComm \u7684\u670d\u52d9\u5668\u901a\u4fe1\uff0c\u5b83\u6703\u9700\u8981\u5728/.well-known/did.json \u63d0\u4f9b\u4e00\u500b DID \u6587\u6a94\uff0c\u6587\u6a94\u88cf\u5305\u542b\u4e86\u516c\u9470\u4ee5\u53ca DIDComm \u7684 service endpoint \u3002\u6211\u7684 demo \u670d\u52d9\u5668\u7684 DID \u6587\u6a94\u9577\u9019\u6a23\uff1a <a href=\"https://dc.mbkr.ca/.well-known/did.json\" rel=\"nofollow\">https://dc.mbkr.ca/.well-known/did.json</a> \u3002</p>\n<p>\u64c1\u6709\u4e86\u9019\u500b\u6587\u6a94\u4e4b\u5f8c\uff0c<code>did:web:<a href=\"http://dc.mbkr.ca\" rel=\"nofollow\">dc.mbkr.ca</a></code> \u9019\u500b DID \u5c31\u6703\u5c0d\u61c9\u6211\u7684\u670d\u52d9\u5668\uff0c\u4f60\u53ea\u9700\u8981\u9019\u500b id \u5c31\u53ef\u4ee5\u7d66\u6211\u767c\u4fe1\u606f\uff0c\u767c\u4fe1\u65b9\u6703\u6839\u64da\u9019\u500b id \u89e3\u6790\u5230 DID \u6587\u6a94\uff0c\u7372\u53d6\u516c\u9470\u69cb\u5efa\u4fe1\u606f\uff0c\u4e26\u63a8\u5230 service endpoint \u4e0a\u3002</p>\n<p>\u6211\u4e5f\u505a\u4e86\u4e00\u500b\u516c\u958b\u7684 demo \u670d\u52d9\u5668\uff0c<a href=\"https://dc-public.mbkr.ca/\" rel=\"nofollow\">https://dc-public.mbkr.ca/</a> \uff0c\u4f60\u53ef\u4ee5\u7528<code>public</code>\u505a\u5bc6\u78bc\u767b\u9304\u3002</p>\n<p>Demo \u7684 repo: <a href=\"https://github.com/onyxblade/didcomm-rails-demo\" rel=\"nofollow\">https://github.com/onyxblade/didcomm-rails-demo</a></p>\n<p>\u5be6\u73fe\u4e0a\uff0c\u6211\u672c\u4f86\u662f\u8b93 AI \u751f\u6210\u4e86\u4e00\u500b Ruby \u7248\u672c\u7684 DIDComm \u5be6\u73fe\uff0c\u4e0d\u904e\u5bc6\u78bc\u5b78\u7684\u5167\u5bb9\u5f88\u591a\uff0c\u6211\u6700\u5f8c\u9084\u662f\u6c7a\u5b9a\u76f4\u63a5\u7528<a href=\"https://github.com/sicpa-dlab/didcomm-rust\" rel=\"nofollow\">didcomm-rust</a>\u9019\u500b\u6a19\u6e96\u7684\u53c3\u8003\u5be6\u73fe\u3002\u9019\u500b Rust \u7684\u5be6\u73fe\u53ef\u4ee5\u8f38\u51fa wasm \uff0c\u4f46 wasmtime-rb \u597d\u50cf\u9084\u6c92\u6210\u719f\u5230\u80fd\u76f4\u63a5\u7528\uff0c\u6240\u4ee5\u6700\u7d42\u6211\u505a\u4e86\u4e00\u500b HTTP \u670d\u52d9\u5668<a href=\"https://github.com/onyxblade/didcomm-http\" rel=\"nofollow\">didcomm-http</a>\uff0c\u4f86\u628a wasm \u63a5\u53e3\u5305\u88dd\u6210 HTTP API \u3002</p>\n<p>Happy Hacking!</p>\n", 
      "date_published": "2026-03-03T23:15:27+00:00", 
      "title": "\u4e00\u500b\u7528 DIDComm \u5be6\u73fe\u670d\u52d9\u5668\u9593\u901a\u4fe1\u7684 Rails demo", 
      "id": "https://www.v2ex.com/t/1195605"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Kauruus", 
        "name": "Kauruus", 
        "avatar": "https://cdn.v2ex.com/avatar/1c6c/41b9/328201_large.png?m=1531401751"
      }, 
      "url": "https://www.v2ex.com/t/1094035", 
      "title": "RubyConf China 2024 \u53c2\u4f1a\u62a5\u544a - Day 1", 
      "id": "https://www.v2ex.com/t/1094035", 
      "date_published": "2024-11-30T17:15:22+00:00", 
      "content_html": "<h2>Rails + Next.js\uff1a\u4e00\u79cd\u63d0\u5347\u5f00\u53d1\u8005\u548c\u7528\u6237\u4f53\u9a8c\u7684\u5f00\u53d1\u67b6\u6784\u8303\u5f0f</h2>\n<p>Rails \u5199\u540e\u7aef\u975e\u5e38\u723d\uff0c\u4f46\u662f\u5199\u524d\u7aef\u5c31\u5f88\u75db\u82e6\uff0c\u4e8e\u662f\u62c9\u4e0a\u4f53\u9a8c\u66f4\u597d\u7684 Next.js \uff0c\u76f4\u63a5\u6709\u5b8c\u5584\u7684\u7ec4\u4ef6\u3001IDE \u548c\u793e\u533a\u652f\u6301\uff0c\u7ec4\u5408\u6210 Next.js \u4ee3\u7406 API \u8bf7\u6c42\u5230 Rails \u7684\u67b6\u6784\u3002</p>\n<p>\u6f14\u8bb2\u8005\u8fd8\u63d0\u5230\u4e00\u4e2a\u597d\u5904\uff0c\u5c31\u662f Rails \u57fa\u4e8e cookie \u7684 session \u7ba1\u7406\u975e\u5e38\u5b8c\u5584\uff0c\u901a\u8fc7\u8fd9\u4e2a\u67b6\u6784\u5c31\u53ef\u4ee5\u91cd\u7528\u8fd9\u90e8\u5206\uff0c\u800c\u4e0d\u9700\u8981\u641e\u4ec0\u4e48 JWT token \u3002\u4f46\u662f\u4ece\u6f14\u8bb2\u91cc\u770b\uff0cNext.js \u548c Rails \u4e4b\u95f4\u4ea4\u4e92\u4e5f\u6ca1\u7279\u522b\u65b9\u4fbf\uff0c\u4f8b\u5982 Rails \u8981\u5173\u6389 CSRF token \u4e4b\u7c7b\u7684\u4e1c\u897f\uff0c\u8fd9\u53cd\u800c\u53ef\u80fd\u88ab\u5229\u7528\u3002</p>\n<p>\u8fd9\u7c7b\u6d89\u53ca\u524d\u540e\u7aef\u5f00\u53d1\u7684\u95ee\u9898\uff0c\u80af\u5b9a\u53c8\u626f\u51fa\u524d\u540e\u7aef\u5206\u79bb\u3001\u56e2\u961f\u642d\u5efa\u7684\u95ee\u9898\u3002\u6211\u81ea\u5df1\u5f88\u4e45\u6ca1\u5199\u524d\u7aef\u4e86\uff0c\u6240\u6709\u662f\u504f\u5411\u4e8e\u5206\u5f00\u7684\uff0c\u4f46\u662f\u5f88\u591a Rails KOL \u90fd\u559c\u6b22\u5168\u6808\u7684\u611f\u89c9\u3002</p>\n<h2>\u5341\u500d\u6027\u80fd\u63d0\u5347\uff0c\u539f\u751f Ruby \u5f02\u6b65\u7f16\u7a0b\u673a\u5236\u7684\u601d\u8003\u4e0e\u5e94\u7528</h2>\n<p>\u8fd9\u662f\u6211\u633a\u671f\u5f85\u7684\u4e00\u4e2a\u4e3b\u9898\uff0c\u6211\u8fd8\u505a\u4e86\u529f\u8bfe\uff0c\u53bb\u770b\u4e86 Fiber scheduler, evt, async \uff0c\u6d4b\u8bd5\u4e86\u7528 falcon \u8dd1 Rails \u7684\u6027\u80fd\u3002</p>\n<p>\u6f14\u8bb2\u8005\u5f15\u5165\u5f02\u6b65\u7684\u539f\u56e0\u662f\u56e0\u4e3a LLM API \u54cd\u5e94\u6162\uff0c\u6240\u6709\u5982\u679c\u4e0d\u5f02\u6b65\uff0c\u90a3\u53d1\u4e2a API \u8bf7\u6c42\u5c31\u8981\u963b\u585e\u4e00\u4e2a\u7ebf\u7a0b\uff0c\u6240\u4ee5\u5f02\u6b65\u52bf\u5728\u5fc5\u884c\uff0c\u4e3e\u7684\u4f8b\u5b50\u4e5f\u4e3b\u8981\u662f\u7528 Fiber \u505a\u5f02\u6b65 API \u8bf7\u6c42\uff0c\u4f46\u6ca1\u7ec6\u8bb2\uff08\u6216\u8005\u672c\u6765\u5c31\u4e0d\u503c\u4e00\u8bb2\u5427\uff09\u3002\u6f14\u8bb2\u8005\u79f0 LLM \u5e26\u6765\u4e86\u7f16\u7a0b\u8303\u5f0f\u7684\u8f6c\u6362\uff0c\u5176\u4e2d\u5305\u62ec\u4ece CPU \u5bc6\u96c6\u578b\u53d8\u6210\u4e86 IO \u5ef6\u8fdf\u5f0f\uff0c\u8fd9\u5c31\u6709\u70b9\u5f3a\u884c\u4e86\u3002\u540e\u9762\u5c31\u662f\u7b80\u5355\u7684 Fiber \u4ecb\u7ecd\uff0c\u548c async/await, promise, goroutine \u7684\u5bf9\u6bd4\uff0c\u8fc7\u4e8e\u7b80\u5355\uff0c\u5c31\u5217\u8868\u683c\u8bb2\u4e2a\u6982\u5ff5\u3002</p>\n<p>\u6211\u662f\u633a\u5931\u671b\u7684\u3002\u6807\u9898\u53f7\u79f0\u5341\u500d\u6027\u80fd\u63d0\u5347\uff0c\u6211\u5e0c\u671b\u662f\u6709\u6570\u636e\u652f\u6491\u7684\uff0c\u4f46\u662f\u6ca1\u6709\u3002\u5bf9\u673a\u5236\u7684\u601d\u8003\uff0c\u6211\u4ee5\u4e3a\u4f1a\u8bb2 Fiber \u548c Fiber scheduler \u7684\u539f\u7406\uff0c\u4f46\u8fd9\u4e9b\u90fd\u7b80\u5355\u5e26\u8fc7\u4e86\u3002\u800c\u5e94\u7528\u4e5f\u53ea\u6709 API \u8bf7\u6c42\u5f02\u6b65\u5316\uff08\u8fd8\u5f88\u592a\u5feb\u7ffb\u8fc7\u7684 concurrency gem \u4f8b\u5b50\uff0c\u770b\u4e0d\u6e05\u554a\uff09\u3002</p>\n<p>\u5c31\u5f53\u629b\u7816\u5f15\u7389\u5427\uff0c\u6700\u540e\u547c\u5401\u5927\u5bb6\u6765\u5171\u5efa Ruby \u7684 LLM \u548c\u5f02\u6b65\u751f\u6001\u3002</p>\n<h2>SDB: a New Ruby Stack Profiling Tool</h2>\n<p>\u4e00\u4e2a\u65b0\u7684 stack profiler \uff0c\u5728 ruby \u8fdb\u7a0b\u5185\u901a\u8fc7\u4e00\u4e2a\u72ec\u7acb\u7ebf\u7a0b\u626b\u63cf ruby \u7684\u8c03\u7528\u6808\uff0c\u4f46\u4e0d\u83b7\u53d6 GVL \u6240\u4ee5\u57fa\u672c\u4e0d\u5f71\u54cd\u5e94\u7528\u6027\u80fd\u3002\u6709 data race \u7684\u98ce\u9669\uff0c\u4f46\u4e00\u70b9\u9519\u8bef\u4e0d\u5f71\u54cd\u6027\u80fd\u5206\u6790\u7684\u76ee\u6807\u3002\u51fd\u6570\u5730\u5740\u548c\u6587\u4ef6\u540d/\u51fd\u6570\u540d\u7684\u5173\u7cfb\u662f\u901a\u8fc7 ebpf \u7684 uprobe/uretprobe \u8bb0\u5f55\u7684\uff0c\u53ea\u5728\u65b9\u6cd5\u88ab\u5b9a\u4e49\u7684\u65f6\u5019\u4f1a\u8bb0\u5f55,\u4e0d\u7528\u91cd\u590d\u89e3\u6790\u3002</p>\n<p>\u8fd9\u4e2a\u6f14\u8bb2\u7684\u51c6\u5907\u5c31\u975e\u5e38\u597d\uff0c\u63d0\u51fa\u95ee\u9898\uff0c\u5206\u6790\u73b0\u6709\u65b9\u6848\u7684\u7f3a\u70b9\uff0c\u63d0\u51fa\u6539\u8fdb\u8bbe\u8ba1\u548c\u76ee\u6807\uff0c\u901a\u8fc7\u6027\u80fd\u5bf9\u6bd4\u8bc1\u660e\u8bbe\u8ba1\u7684\u53ef\u884c\u6027\u3002</p>\n<h2>\u4e0b\u4e00\u4e2a\u5341\u5e74\u7684 Modern Monolith</h2>\n<p>\u8fd9\u91cc\u7684 monolith \uff0c\u662f\u6307\u524d\u540e\u7aef\u653e\u4e00\u8d77\uff0c\u6240\u4ee5\u548c\u7b2c\u4e00\u4e2a\u4e3b\u9898\u662f\u7c7b\u4f3c\u7684\uff0c\u5c31\u662f\u4e3a\u4e86\u66f4\u597d\u5730\u5728 Rails \u9879\u76ee\u91cc\u5199\u524d\u7aef\u4ee3\u7801\u3002</p>\n<p>\u901a\u8fc7\u4e00\u4e2a Todo list \u7684\u5e94\u7528\uff0c\u5206\u522b\u6f14\u793a Hotwire, ViewComponent, Inertia.js \u7684\u65b9\u6848\u3002\u4f5c\u4e3a demo \u6211\u89c9\u5f97\u633a\u5931\u8d25\u7684\uff0c\u6ca1\u6709\u771f\u5b9e\u6f14\u793a\uff0c\u53ea\u6709\u622a\u56fe\uff0c\u89c6\u56fe\u4ee3\u7801\u4e5f\u590d\u6742\uff0c\u5c5e\u4e8e\u4e0d\u61c2\u7684\u5b8c\u5168\u5c31\u8ddf\u4e0d\u4e0a\u7684\u7c7b\u578b\uff0c\u5927\u90e8\u5206\u53ea\u662f\u6d6a\u8d39\u65f6\u95f4\uff0c\u8fd8\u597d Inertia.js \u662f\u8bb2\u6e05\u695a\u539f\u7406\u7684\u3002</p>\n<p>\u6700\u540e\u4e5f\u6709\u89c2\u4f17\u95ee\u4e86 monolith \u7684\u5b9a\u4e49\u95ee\u9898\uff0c\u5728\u4e00\u4e2a Raisl \u5e94\u7528\u91cc\u6709\u591a\u4e2a\u9886\u57df\u7684\uff08\u6ca1\u62c6\u6210\u5fae\u670d\u52a1\uff09\uff0c\u4f46\u505a\u4e86\u524d\u540e\u7aef\u5206\u79bb\u8fd8\u7b97\u4e0d\u7b97 monolith \u3002\u6f14\u8bb2\u8005\u8bf4\u662f\u4f60\u5e94\u7528\u4e1a\u52a1\u903b\u8f91\u81a8\u80c0\uff0c\u8fd9\u90fd\u4e0d\u80fd\u7b97\u662f\u6280\u672f\u95ee\u9898\u4e86\uff08\u5927\u610f\u5982\u6b64\uff09\u3002</p>\n<h2>Metasploit \u4e0e Ruby</h2>\n<p>\u975e\u5e38\u201c\u5c0f\u4f17\u201c\u7684\u4e3b\u9898\uff0c\u6f14\u8bb2\u8005\u4e5f\u505a\u4e86\u5f88\u591a\u51c6\u5907\uff0cPPT \u585e\u4e86\u5f88\u591a\u5185\u5bb9\uff0c,Metasploit \u5386\u53f2\uff0c\u67b6\u6784\uff0c\u73af\u5883\uff0cTruffleRuby \u4ec0\u4e48\u90fd\u6709\u3002\u4f46\u662f\u6f14\u8bb2\u6548\u679c\u4e0d\u592a\u597d\uff0c\u8bed\u901f\u98de\u5feb\uff0c\u6211\u53ea\u80fd\u542c\u4e2a\u5927\u6982\uff0c\u611f\u89c9\u5185\u5bb9\u90fd\u662f\u873b\u8713\u70b9\u6c34\uff0c\u7ed3\u679c\u5c31\u662f\u4ec0\u4e48\u90fd\u6ca1\u5b66\u5230\u3002</p>\n<h2>\u6784\u5efa\u57fa\u4e8e LLM \u7684\u8bed\u97f3\u673a\u5668\u4eba</h2>\n<p>Demo \u6548\u679c\u975e\u5e38\u597d\uff0c\u4e5f\u5206\u4eab\u4e86\u5f88\u591a\u5de5\u7a0b\u4e0a\u7684\u5b9e\u8df5\uff0c\u5982\u5206\u5c42\u7684\u67b6\u6784\uff08 ASR + TTS, turn-taking, LLM)\uff0c\u72b6\u6001\u7684\u7ba1\u7406\uff0c\u6d41\u5f0f\u6027\u80fd\u4f18\u5316\uff0cprompt \uff0ctool calling \uff0c\u5bf9\u751f\u6210\u5185\u5bb9\u7684\u4f18\u5316\u2026\u2026\u5177\u4f53\u8fd8\u662f\u770b\u56de\u653e\u5427\u3002</p>\n<p>\u6fc0\u8d77\u4e86\u6211\u5bf9 LLM \u5f00\u53d1\u7684\u5174\u8da3\u3002</p>\n<p>\u800c\u4e14\u770b\u4ee3\u7801\u5176\u5b9e\u662f Go \u5b9e\u73b0\u7684\uff0c\u6ca1\u6709 API \u963b\u585e\u7ebf\u7a0b\u7684\u95ee\u9898\u3002\u72b6\u6001\u673a\uff08\u6f14\u8bb2\u8005\u628a\u5b83\u53eb Routine \uff09\u7684\u5b9e\u73b0\u91cc\uff0c\u6bcf\u5404\u72b6\u6001\u662f\u4e00\u4e2a\u65b9\u6cd5\uff0c\u6bcf 50ms \u6267\u884c\u4e00\u6b21\uff0c\u63a5\u53d7\u53c2\u6570\uff08\u4f8b\u5982 LLM \u7684\u8f93\u51fa\uff09\u505a\u903b\u8f91\u5224\u65ad\uff0c\u901a\u8fc7\u8fd4\u56de\u4e0b\u4e00\u4e2a Routine \u5b9e\u73b0\u72b6\u6001\u8fc1\u79fb\u3002\u6709\u624b\u5199 coroutine \u7684\u611f\u89c9\u3002 </p>\n<h2>A.I-Generated Interactive Narrative Design in Ruby</h2>\n<p>\u7528 AI \u751f\u6210\u6e38\u620f\u5267\u672c\uff0cdemo \u4e5f\u5f88\u6709\u8da3\uff0c\u4f46\u662f\u4e3a\u4e86\u505a\u51fa\u8fd9\u4e2a\u6548\u679c\u505a\u4e86\u597d\u591a\u5de5\u4f5c\uff0c\u90fd\u4e0d\u6b62\u662f LLM \u7684\u8303\u7574\u7684\u3002</p>\n<p>\u4f8b\u5982\u5b9e\u73b0\u4e86\u4e00\u4e2a DSL \uff08\u6700\u540e\u662f Blockly \u7684\u5f62\u5f0f\uff09\u6765\u5199\u5267\u672c\u3002\u7528 Logica \u6765\u9a8c\u8bc1 Storylet \u7684\u903b\u8f91\u6b63\u786e\u6027\uff0c\u8fd8\u80fd\u8bb2\u4e00\u9636\u903b\u8f91\uff0c\u4e8c\u9636\u903b\u8f91\u3002\u8ba9\u6211\u610f\u5916\u7684\u662f Logica \u751f\u6210 SQL \u7ed9 PG \u67e5\u8be2\u5c45\u7136\u6bd4 Prolog \u66f4\u5feb\uff0c\u539f\u56e0\u4e4b\u4e00\u662f\u51cf\u5c11\u4e86\u52a0\u8f7d\u6570\u636e\u5230 Prolog \u7684\u8fc7\u7a0b\uff0cProlog \u6709\u6ca1\u6709\u7c7b\u4f3c\u7684 image \u6216\u8005\u6570\u636e\u5e93\u7684\u6301\u4e45\u5316\u529f\u80fd\uff1f</p>\n<p>\u8fd8\u6709\u767d\u5b66:)</p>\n<p>\u6700\u540e\u89c2\u4f17\u8ba8\u8bba\u7684\u662f\u751f\u6210\u5267\u60c5\u7684\u6e38\u620f\u53ef\u80fd\u9047\u5230\u7684\u95ee\u9898\uff0c\u800c\u4e14\u771f\u7684\u6709\u5e02\u573a\u5417\uff1f</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/coool", 
        "name": "coool", 
        "avatar": "https://cdn.v2ex.com/gravatar/dd999e8cb14d94112f57c94fb5654906?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/1087235", 
      "title": "\u6709\u5bf9 Ruby \u611f\u5174\u8da3\u7684\u5417\uff1f", 
      "id": "https://www.v2ex.com/t/1087235", 
      "date_published": "2024-11-06T12:53:45+00:00", 
      "content_html": "<p>\u4e2d\u6587\u751a\u81f3\u5df2\u7ecf\u5f88\u96be\u627e\u5230\u5173\u4e8e Rails 6 \u7684\u6587\u7ae0\u6216\u4e66\u7c4d\u4e86\uff0c\u82f1\u6587\u8d44\u6599\u5374\u5c42\u51fa\u4e0d\u7a77\uff1a</p>\n<ul>\n<li>PragProg \u5df2\u51fa\u7248\u548c\u5f85\u51fa\u7248\u7684\u65b0\u4e66\u6709\u5f88\u591a\u672c</li>\n<li>Sustainable Web Development \u4e5f\u66f4\u65b0\u5230\u4e86 Rails 7.1 </li>\n<li>\u751a\u81f3 Apress \u90fd\u51fa\u4e86\u4e00\u672c Hands-on Test-Driven Development</li>\n<li>... </li>\n</ul>\n<p>\u7531\u4e8e\u4e00\u76f4\u5bf9 Ruby \u5f88\u611f\u5174\u8da3\uff0c\u4e0a\u9762\u7684\u4e66\u57fa\u672c\u4e0a\u90fd\u770b\u4e86\u4e00\u904d\u4ee5\u4e0a\uff0c\u611f\u89c9\u5b66\u5230\u4e86\u5f88\u591a\u77e5\u8bc6\uff0c\u4f46\u5374\u6ca1\u6709\u505a\u51fa\u4ec0\u4e48\u5e94\u7528 \uff08\u4e3b\u8981\u8fd8\u662f\u53d7\u9650\u4e8e\u60f3\u6cd5\u3001\u8bbe\u8ba1\u7b49\u80fd\u529b\uff09\uff0c\u4e8e\u662f\u60f3\u7740\u4e0d\u59a8\u628a\u5b66\u5230\u7684\u77e5\u8bc6\u603b\u7ed3\u51fa\u6765\uff0c\u4e00\u65b9\u9762\u662f\u4e3a\u4e86\u81ea\u5df1\u5de9\u56fa\uff0c\u53e6\u4e00\u65b9\u9762\u4e5f\u5e0c\u671b\u80fd\u5bf9 Ruby \u611f\u5174\u8da3\u7684\u65b0\u4eba\u6709\u6240\u5e2e\u52a9\uff0c\u6240\u4ee5\u521b\u5efa\u4e86\u4e00\u4e2a Ruby \u6280\u672f\u516c\u4f17\u53f7 \u201cRuby \u672d\u8bb0\u201d\uff0c\u53d1\u4e86\u7b2c\u4e00\u7bc7\u6587\u7ae0 <a href=\"https://mp.weixin.qq.com/s/Y2Hiv2UvO3LV2PkDP2DzoA\" rel=\"nofollow\">Rails \u5982\u4f55\u5904\u7406\u9759\u6001\u8d44\u6e90</a>\u3002</p>\n<p>\u5728 Ruby China \u4e5f\u53d1\u4e86<a href=\"https://ruby-china.org/topics/43935\" rel=\"nofollow\">\u4e00\u7bc7\u5e16\u5b50</a>.</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/1053200", 
      "title": "MRuby Devkit \u4e00\u4e2a\u7b80\u5355\u7684\u811a\u624b\u67b6\uff0c\u5e2e\u52a9\u4f60\u50cf Go \u4e00\u6837\u628a Ruby \u7f16\u8bd1\u6210\u53ef\u6267\u884c\u4e8c\u8fdb\u5236\u6587\u4ef6", 
      "id": "https://www.v2ex.com/t/1053200", 
      "date_published": "2024-06-27T13:06:25+00:00", 
      "content_html": "<p>\u9879\u76ee\u5730\u5740\uff1a <a href=\"https://github.com/Mark24Code/mruby-devkit\" rel=\"nofollow\">https://github.com/Mark24Code/mruby-devkit</a></p>\n<p>MRuby Devkit \u662f\u4e00\u4e2a\u5f00\u7bb1\u5373\u7528\u7684\u811a\u624b\u67b6\u3002 \u57fa\u4e8e MRuby \u5c06\u4f60\u7684 Ruby \u4ee3\u7801\u6253\u5305\u6210 \u4e8c\u8fdb\u5236\u53ef\u6267\u884c\u6587\u4ef6\u3002</p>\n<p>\u65b9\u4fbf\u5f00\u53d1\u7c7b\u4f3c\u4e8e Golang \u7684\u4e8c\u8fdb\u5236\u53ef\u6267\u884c\u6587\u4ef6\u3002</p>\n<blockquote>\n<p>\u2014\u2014 \u7075\u611f\u6765\u81ea\u4e8e Golang \u53ef\u4ee5\u7f16\u8bd1\u4e3a\u4e8c\u8fdb\u5236\u53ef\u6267\u884c\u6587\u4ef6\u7684\u8ff7\u4eba\u7279\u6027\u3002</p>\n</blockquote>\n<p>\u63a5\u53d7\u7b80\u5355\u7684\u7ea6\u5b9a\uff0c\u4e13\u6ce8\u4e8e\u7f16\u5199 Ruby \u4ee3\u7801\u3002\u8f7b\u677e\u5730 build \u6210\u4e8c\u8fdb\u5236\u53ef\u6267\u884c\u6587\u4ef6\u3002</p>\n<h1>\u4f7f\u7528\u8bbe\u8ba1</h1>\n<h2>0. \u7f16\u5199\u7a0b\u5e8f</h2>\n<p><code>src</code> \u4e0b\u7f16\u5199 ruby \u7a0b\u5e8f</p>\n<h2>1. \u8fd0\u884c\u7a0b\u5e8f</h2>\n<blockquote>\n<p>\u6a21\u4eff golang \u7684 go run</p>\n</blockquote>\n<p><code>rake run</code></p>\n<h2>2. \u7f16\u8bd1\u5f53\u524d\u7a0b\u5e8f\uff08\u9ed8\u8ba4\u4f7f\u7528\u5f53\u524d\u8ba1\u7b97\u673a\u5e73\u53f0\uff09</h2>\n<blockquote>\n<p>\u6a21\u4eff golang \u7684 go build</p>\n</blockquote>\n<p><code>rake build</code></p>\n<h2>3.\u4ea4\u53c9\u7f16\u8bd1\u7684\u5305</h2>\n<p>\u501f\u52a9 Github Action \u7f16\u8bd1\u4e0d\u540c\u5e73\u53f0\u7684\u53ef\u6267\u884c\u4e8c\u8fdb\u5236\u6587\u4ef6\u3002</p>\n<ul>\n<li>\u53ef\u4ee5 fork \u4ed3\u5e93\u5728  Github Action \u8fd0\u884c\u7ed3\u679c\u4e0b\u53ef\u4ee5\u770b\u5230\u6784\u5efa\u4ea7\u7269\u3002</li>\n</ul>\n<p>\u66f4\u591a\u8bf7\u67e5\u770b <a href=\"https://github.com/Mark24Code/mruby-devkit\" rel=\"nofollow\">https://github.com/Mark24Code/mruby-devkit</a></p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/1044459", 
      "title": "\u4f7f\u7528 Ruby-build \u5728 MacOS \u4e0a \u7f16\u8bd1 Portable Ruby", 
      "id": "https://www.v2ex.com/t/1044459", 
      "date_published": "2024-05-27T11:56:40+00:00", 
      "content_html": "<p>RubyChina \u8ba8\u8bba\uff1a <a href=\"https://ruby-china.org/topics/43710\" rel=\"nofollow\">https://ruby-china.org/topics/43710</a></p>\n<p><a href=\"https://mark24code.github.io/ruby/2024/05/27/%E4%BD%BF%E7%94%A8Ruby-build-%E5%9C%A8-MacOS%E4%B8%8A-%E7%BC%96%E8%AF%91-Portable-ruby.html\" rel=\"nofollow\">\u6211\u7684 Blog</a></p>\n<p>\u5927\u5bb6\u597d\uff0c\u6211\u662f Mark24 \u3002</p>\n<p>\u5206\u4eab\u4e0b\u6211\u7684\u7b14\u8bb0\uff0c\u4f7f\u7528 Ruby-build \u5728 MacOS \u4e0a \u7f16\u8bd1 Portable ruby</p>\n<p>\u8bbe\u60f3\u4e00\u4e0b\uff0c\u5982\u679c ruby \u53ef\u4ee5\u53d8\u6210 portable \u7684\uff0c\u653e\u5728 U \u76d8\u4e0a\u5c31\u53ef\u4ee5\u5e26\u8d70\uff0c\u4f20\u8f93\u5230\u4efb\u4f55\u4e00\u53f0\u7535\u8111\u4e0a\u5c31\u53ef\u4ee5\u6267\u884c\u3002</p>\n<p>Portable Ruby + \u4f60\u7684 Ruby \u4ee3\u7801   \u7684 zip \u5305\uff0c\u5c31\u50cf\u4e00\u4e2a\u884c\u8d70\u7684\u72ec\u7acb\u8f6f\u4ef6\u3002\u5c31\u50cf Go \u6253\u5305\u7684\u4e00\u6837\u3002</p>\n<p>\u4f60\u8fd8\u53ef\u4ee5\u628a\u4ed6\u4eec\u585e\u5165 \u4e00\u4e9b\u58f3\u8f6f\u4ef6\u91cc\u3002\u5c31\u50cf Electron \u90a3\u6837\u8fd0\u884c\uff08\u5185\u90e8\u662f\u4e2a\u6d4f\u89c8\u5668\uff09\u3002</p>\n<p>\u5f53\u7136 Ruby \u793e\u533a\u66fe\u7ecf\u6709\u5f88\u591a\u65b9\u6848 Traveling Ruby \u3001Ruby Packer \uff0c \u90fd\u7528\u5404\u81ea\u7684\u65b9\u5f0f\u5b9e\u73b0\u7c7b\u4f3c\u7684\u6548\u679c\uff0c\u4e0d\u8fc7\u90fd\u4e0d\u7ef4\u62a4\u4e86\u3002</p>\n<p>\u4e0b\u9762\u7528\u4e00\u4e2a\u7b80\u5355\u7684\u65b9\u6cd5\u6765\u5236\u4f5c Portable Ruby \u3002</p>\n<hr/>\n<p>\u622a\u6b62 2024-05-27 \u6700\u65b0\u7248\u672c\u662f 3.3.1 \u3002\n\u6bcf\u4e2a\u7248\u672c\u56e0\u4e3a\u7279\u6027\u7684\u4e0d\u540c\u6784\u5efa\u662f\u4e00\u4e2a\u52a8\u6001\u7684\u8fc7\u7a0b\u3002\u5c31\u4ee5 3.3.1 \u4e3a\u4f8b\u3002</p>\n<p>\u8fc7\u7a0b\u5077\u61d2\uff0c\u5efa\u7acb\u5728 ruby-build( <a href=\"https://github.com/rbenv/ruby-build\" rel=\"nofollow\">https://github.com/rbenv/ruby-build</a>) \u7684\u57fa\u7840\u4e0a\u3002</p>\n<p>\u4e0d\u8bba\u662f asdf \u3001rvm \u2026\u2026 \u4ed6\u4eec\u7684\u80cc\u540e\u90fd\u662f ruby-build \u4e00\u4e2a\u65b9\u4fbf\u5b89\u88c5\u7684 standalone \u7684\u5de5\u5177\u3002ruby-build \u89e3\u51b3\u4e86\u5927\u90e8\u5206\u7684\u95ee\u9898\uff0c\u6211\u4eec\u53ea\u9700\u8981\u627e\u5230\u5408\u9002\u7684\u6784\u5efa\u53c2\u6570\u3002</p>\n<h2>\u4e00\u3001\u524d\u7f6e\u4f9d\u8d56</h2>\n<p>1.\u5b89\u88c5 Mac \u7684\u57fa\u7840\u5de5\u5177\u96c6</p>\n<p>\u7ec8\u7aef\u8f93\u5165 <code>xcode-select --install</code></p>\n<p>2.\u5b89\u88c5\u4e0a homebrew</p>\n<p><a href=\"https://brew.sh/\" rel=\"nofollow\">https://brew.sh/</a></p>\n<p>\u83b7\u5f97 \u7c7b\u4f3c\u4e8e Linux \u4e0a\u7684\u5305\u7ba1\u7406\u5de5\u5177</p>\n<p>3.\u5b89\u88c5 Ruby \u7f16\u8bd1\u9700\u8981\u7684\u524d\u7f6e\u4f9d\u8d56</p>\n<pre><code># \u5b89\u88c5\u524d\u7f6e\u4f9d\u8d56\n# ruby-build \u662f\u5b89\u88c5\u5de5\u5177\n# openssl@3 readline libyaml gmp \u662f\u5fc5\u8981\u7684\u4f9d\u8d56\n# rust \u662f YJIT \u5fc5\u8981\u7684\u4f9d\u8d56\uff0c\u4e0d\u88c5\u5c31\u4e0d\u4f1a\u6784\u5efa YJIT \u529f\u80fd\n\nbrew install ruby-build openssl@3 readline libyaml gmp rust\n</code></pre>\n<h2>\u4e8c\u3001\u7f16\u8bd1</h2>\n<p>0.\u77e5\u8bc6\u70b9</p>\n<pre><code>C \u8bed\u8a00\uff08 CRuby \u662f C \u8bed\u8a00\u9879\u76ee\uff09\u7f16\u8bd1\u4e00\u822c\u5206\u4e3a 3 \u4e2a\u57fa\u672c\u8fc7\u7a0b\n\n1 \uff09\u9884\u5904\u7406\uff1a\u5904\u7406\u4e00\u4e9b\u524d\u7f6e\u7684\u5b8f\u66ff\u6362\n2 \uff09\u7f16\u8bd1\uff1a\u628a .c \u4ee3\u7801\u6587\u4ef6\u7ffb\u8bd1\u6210 .o \u673a\u5668\u7801\u6587\u4ef6\u76ee\u6807\u6587\u4ef6\n3 \uff09\u94fe\u63a5\uff1a\u628a .o \u6587\u4ef6\u548c\u7cfb\u7edf\u7684\u5e95\u5c42\u5e93\uff08\u6bd4\u5982\u6807\u51c6\u8f93\u5165\u8f93\u51fa\uff09\u6b63\u786e\u7684\u5173\u8054\u8d77\u6765\u3002\u751f\u6210\u53ef\u6267\u884c\u6587\u4ef6\n\n\u94fe\u63a5\u8fd9\u90e8\uff0c\u6709\u4e24\u4e2a\u57fa\u672c\u7684\u5b9e\u73b0\n\n1 \uff09\u9759\u6001\u94fe\u63a5\n2 \uff09\u52a8\u6001\u94fe\u63a5\n\n\u9759\u6001\u94fe\u63a5\u6bd4\u8f83\u7b80\u5355\uff0c\u5c31\u662f\u628a\u6240\u6709\u7528\u5230\u7684\u4ee3\u7801\u6253\u5305\u6210\u4e00\u4e2a\u6574\u4f53\u3002\u8f6f\u4ef6\u5c31\u50cf\u4e00\u4e2a exe \u6587\u4ef6\uff0c\u5e26\u5230\u54ea\u513f\u90fd\u53ef\u4ee5\u6267\u884c\u3002\n\u4f18\u70b9\u5c31\u662f\uff0c\u968f\u5904\u6267\u884c\u3002\u7f3a\u70b9\u5c31\u662f\u4f53\u79ef\u5927\uff0c\u66f4\u65b0\u56f0\u96be\uff0c\u6bd4\u5982\u4f60\u4f9d\u8d56\u7684\u7cfb\u7edf\u90e8\u5206\u6709\u5b89\u5168\u7f3a\u9677\u3002\u4f60\u5fc5\u987b\u6574\u4f53\u66ff\u6362\u3002\n\n\u52a8\u6001\u94fe\u63a5\uff0c\u5c31\u662f\u8f6f\u4ef6\u628a\u7528\u5230\u516c\u5171\u90e8\u5206\uff08\u7cfb\u7edf\u3001\u4e0a\u6e38 lib \uff09\u7684\u90e8\u5206\uff0c\u6307\u4ed6\u4eec\u7684\u52a8\u6001\u5e93\uff08 linux \u662f so \u6587\u4ef6\uff0cwindows \u662f dll \u6587\u4ef6\uff0cmac \u91cc\u662f dylib \u6587\u4ef6\uff09\u3002\n\u4f18\u70b9\uff1a\u4f53\u79ef\u5c0f\uff0c \u5982\u679c\u516c\u5171\u90e8\u5206\u6709\u5b89\u5168\u6f0f\u6d1e\uff0c\u7cfb\u7edf\u66f4\u65b0\uff0c\u53ea\u9700\u8981\u66f4\u65b0\u52a8\u6001\u94fe\u63a5\u5e93\u6587\u4ef6\uff0c\u6240\u6709\u5f15\u7528\u7684\u8f6f\u4ef6\u90fd\u4f1a\u83b7\u5f97\u66f4\u65b0\u3002\n\u7f3a\u70b9\uff1a\u9664\u4e86\u65e0\u6cd5 portable \uff0c\u8f6f\u4ef6\u8fd0\u884c\u7684\u524d\u63d0\u662f\u7cfb\u7edf\u62e5\u6709\u76f8\u5e94\u7684 \u5e93\u3002\n\n\u52a8\u6001\u94fe\u63a5\u662f\u5e38\u6001\uff0c\u4e0d\u8bba\u662f Linux \u3001MacOS \u3001Windows \u3002\u52a8\u6001\u94fe\u63a5\u7684\u5b9e\u8df5\u8fd9\u4e48\u591a\u5e74\u8fd0\u884c\u7684\u4e00\u76f4\u5f88\u597d\u3002\u901a\u5e38\u5e93\u90fd\u662f\u6309\u7167\u52a8\u6001\u94fe\u63a5\u5e93\u65b9\u5411\u6765\u8bbe\u8ba1\u7684\u3002\u6ca1\u6709\u63d0\u4f9b\u9759\u6001\u5e93\u3002\n\nMacOS \u8fd8\u7981\u6b62\u7cfb\u7edf\u52a8\u6001\u5e93\u8fdb\u884c \u9759\u6001\u94fe\u63a5\u3002\n</code></pre>\n<ol>\n<li>\u6700\u7b80\u5355\u7684\u7f16\u8bd1</li>\n</ol>\n<p>\u5173\u952e\u53c2\u6570\uff1a</p>\n<ul>\n<li><code>$HOME/portable-ruby</code> \u662f\u4f60\u5b58\u653e\u7684\u76ee\u5f55</li>\n<li><code>--enable-load-relative</code> \u5730\u5740\u662f\u76f8\u5bf9\u76ee\u5f55\uff0c\u8fd9\u5bf9\u6211\u4eec\u79fb\u52a8\u5f88\u91cd\u8981</li>\n<li><code>--with-static-linked-ext</code> \u9759\u6001\u94fe\u63a5</li>\n</ul>\n<pre><code>RUBY_CONFIGURE_OPTS=\"--enable-load-relative --with-static-linked-ext\" ruby-build 3.2.2 $HOME/portable-ruby\n</code></pre>\n<p>2.\u4e00\u4e9b\u4f18\u5316\u9009\u9879</p>\n<p>\u53ef\u4ee5\u53c2\u8003 <a href=\"https://github.com/rbenv/ruby-build\" rel=\"nofollow\">https://github.com/rbenv/ruby-build</a></p>\n<p>\u989d\u5916\u7684\u9009\u9879</p>\n<ul>\n<li><code>--with-out-ext=win32,win32ole</code> \u53bb\u6389 MacOS \u4e0a\u4e0d\u9700\u8981\u7684\u62d3\u5c55</li>\n<li><code>--disable-install-doc </code> \u5173\u95ed\u6587\u6863\uff0c\u51cf\u5c0f\u4f53\u79ef</li>\n<li><code>--disable-install-rdoc</code></li>\n<li><code>--disable-dependency-tracking</code></li>\n</ul>\n<pre><code>RUBY_CONFIGURE_OPTS=\"--enable-load-relative --with-static-linked-ext --with-out-ext=win32,win32ole --disable-install-doc --disable-install-rdoc --disable-dependency-tracking \" ruby-build 3.2.2 $HOME/portable-ruby\n</code></pre>\n<p>ruby-build \u80fd\u505a\u7684\u66f4\u591a\uff0c\u6bd4\u5982\u652f\u6301\u4ea4\u53c9\u7f16\u8bd1</p>\n<h2>\u4e09\u3001Portable Ruby</h2>\n<p>\u7f16\u8bd1\u6b63\u786e\u5b8c\u6210\uff0c\u4f60\u5e94\u8be5\u83b7\u5f97\u4e86 portable ruby</p>\n<p>\u5728\u62e5\u6709 \u4f9d\u8d56\u5e93\u7684\u7535\u8111\u4e0a\uff08\u5bf9\uff0c\u6211\u4eec\u524d\u9762\u89e3\u91ca\u4e86\uff0c\u7cfb\u7edf\u90e8\u5206\u662f\u7981\u6b62 \u9759\u6001\u94fe\u63a5\u7684\uff09\u3002</p>\n<p>\u4f60\u7684\u53ef\u4ee5\u628a\u4f60\u7684 ruby \u4ee3\u7801 + portable ruby \u653e\u5728\u4e00\u4e2a\u6587\u4ef6\u5939\u91cc\u3002 \u7528 \u4e00\u4e2a shell \u811a\u672c\uff0c\u901a\u8fc7\u76f8\u5bf9\u8def\u5f84\u8fde\u63a5\u8d77\u6765\u6267\u884c\u3002</p>\n<p>\u6bd4\u5982\u8fd9\u6837</p>\n<pre><code class=\"language-shell\">#!/usr/bin/env bash\n./portable-ruby/bin/ruby ./main.rb\n</code></pre>\n<p>\u67d0\u79cd\u610f\u4e49\u4e0a\uff0cPortable Ruby + Ruby Script \u548c Go \u3001Crystal \u6253\u5305\u7684\u53ef\u6267\u884c\u6587\u4ef6\uff0c\u662f\u4e00\u6837\u7684\u3002\u5c31\u662f\u5927\u4e86\u4e00\u70b9 :D</p>\n<p><a href=\"https://mark24code.github.io/ruby/2024/05/27/%E4%BD%BF%E7%94%A8Ruby-build-%E5%9C%A8-MacOS%E4%B8%8A-%E7%BC%96%E8%AF%91-Portable-ruby.html\" rel=\"nofollow\">\u6211\u7684 Blog</a></p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/981372", 
      "title": "[\u7ffb\u8bd1] Async Ruby\uff08\u5f02\u6b65 Ruby\uff09", 
      "id": "https://www.v2ex.com/t/981372", 
      "date_published": "2023-10-12T08:21:26+00:00", 
      "content_html": "<ul>\n<li>\u539f\u6587\u4f5c\u8005\uff1a<a href=\"https://brunosutic.com/\" rel=\"nofollow\">Bruno Sutic</a></li>\n<li>\u539f\u6587\u94fe\u63a5: <a href=\"https://brunosutic.com/blog/async-ruby\" rel=\"nofollow\">\u300a Async Ruby \u300b</a></li>\n<li>\u539f\u6587\u65f6\u95f4\uff1a2021 \u5e74 10 \u6708 30 \u65e5</li>\n<li>\u539f\u6587\u8ba8\u8bba\uff1a<a href=\"https://news.ycombinator.com/item?id=29049881\" rel=\"nofollow\">Hacker News \u8ba8\u8bba</a></li>\n<li>\u8bd1\u8005\uff1a<a href=\"https://github.com/Mark24Code\" rel=\"nofollow\">Mark24</a></li>\n<li>\u8bd1\u8005 Email\uff1a <a href=\"mailto:mark.zhangyoung@gmail.com\">mark.zhangyoung@gmail.com</a></li>\n<li>\u8bd1\u6587\u94fe\u63a5\uff1a <a href=\"https://mark24code.github.io/ruby/2023/10/12/Async-Ruby.html\" rel=\"nofollow\">https://mark24code.github.io/ruby/2023/10/12/Async-Ruby.html</a></li>\n</ul>\n<p>Ruby \u5df2\u7ecf\u6709\u4e86\u5f02\u6b65\u5b9e\u73b0\uff01</p>\n<p>\u5b83\u73b0\u5728\u5c31\u53ef\u4f7f\u7528\uff0c\u5df2\u7ecf\u505a\u597d\u4e86\u6295\u5165\u751f\u4ea7\u7684\u51c6\u5907\uff0c\u800c\u4e14\u5b83\u53ef\u80fd\u662f\u8fc7\u53bb\u5341\u5e74\u751a\u81f3\u66f4\u4e45\u65f6\u95f4\u91cc Ruby \u53d1\u751f\u7684\u6700\u4ee4\u4eba\u632f\u594b\u7684\u4e8b\u60c5\u3002</p>\n<p>Async Ruby \u7ed9\u8fd9\u95e8\u8bed\u8a00\u6dfb\u52a0\u4e86\u65b0\u7684\u5e76\u53d1\u7279\u6027\uff1b\u4f60\u53ef\u4ee5\u5c06\u5176\u89c6\u4e3a\u201c\u6ca1\u6709\u4efb\u4f55\u7f3a\u70b9\u7684\u7ebf\u7a0b\u201d\u3002\u5b83\u5df2\u7ecf\u5728\u915d\u917f\u4e86\u51e0\u5e74\uff0c\u4e5f\u7ec8\n\u4e8e\u5728 Ruby 3.0 \u4e2d\u51c6\u5907\u597d\u8fdb\u5165\u4e3b\u6d41\u3002</p>\n<p>\u5728\u8fd9\u7bc7\u6587\u7ae0\u4e2d\uff0c\u6211\u5e0c\u671b\u5411\u4f60\u5c55\u793a\u5f02\u6b65 Ruby \u7684\u6240\u6709\u529b\u91cf\u3001\u53ef\u6269\u5c55\u6027\u548c\u9b54\u529b\u3002<strong>\u5982\u679c\u4f60\u70ed\u7231 Ruby \uff0c\u90a3\u8fd9\u5e94\u8be5\u4f1a\u8ba9\u4f60\u975e\u5e38\u6fc0\u52a8\uff01</strong></p>\n<h3>Async gem</h3>\n<p>\u4ec0\u4e48\u662f Async Ruby \uff1f</p>\n<p>\u9996\u5148\uff0c<a href=\"https://github.com/socketry/async\" rel=\"nofollow\">Async \u53ea\u662f\u4e00\u4e2a gem</a>\uff0c\u53ef\u4ee5\u901a\u8fc7 <code>gem install async</code> \u8fdb\u884c\u5b89\u88c5\u3002\u8fd9\u662f\u4e00\u4e2a\u76f8\u5f53\u7279\u6b8a\u7684 gem \uff0c\u56e0\u4e3a <strong>Matz( Ruby \u7684\u521b\u59cb\u4eba) \u8bf7\u5b83\u52a0\u5165 Ruby \u7684\u6807\u51c6\u5e93</strong>\uff0c\u4f46\u9080\u8bf7\u8fd8\u672a\u88ab\u63a5\u53d7\u3002</p>\n<p>Async Ruby \u662f\u7531 <a href=\"https://github.com/ioquatix\" rel=\"nofollow\">Samuel Williams</a> \u521b\u5efa\u7684\uff0c\u4ed6\u4e5f\u662f\u4e00\u4e2a Ruby \u6838\u5fc3\u8d21\u732e\u8005\u3002Samuel \u8fd8\u5b9e\u73b0\u4e86 Fiber Scheduler \uff08\u7ea4\u7a0b\u8c03\u5ea6\u5668\uff09\uff0c\u8fd9\u662f Ruby 3.0 \u7684\u4e00\u4e2a\u91cd\u8981\u7279\u6027\u3002\u5b83\u662f\"\u5e93\u65e0\u5173\u7684\"\uff0c\u672a\u6765\u53ef\u80fd\u6709\u5176\u4ed6\u7528\u9014\uff0c\u4f46\u76ee\u524d\uff0c<strong>\u7ea4\u7a0b\u8c03\u5ea6\u5668\u7684\u4e3b\u8981\u76ee\u7684\u662f\u4f7f async gem \u4e0e Ruby \u65e0\u7f1d\u96c6\u6210</strong>\u3002</p>\n<p>\u5e76\u4e0d\u662f\u5f88\u591a gem \u80fd\u5f97\u5230\u4ed6\u4eec\u81ea\u5b9a\u4e49\u7684 Ruby \u96c6\u6210\uff0c\u4f46\u8fd9\u4e2a\u662f\u503c\u5f97\u7684\uff01</p>\n<p>\u6240\u6709\u8fd9\u4e9b\u90fd\u544a\u8bc9\u4f60\uff0c<code>async</code> \u4e0d\u662f\"\u53ea\u662f\u5916\u9762\u7684\u53e6\u4e00\u4e2a gem\"\u3002<strong>Ruby \u6838\u5fc3\u56e2\u961f\uff0c\u5305\u62ec Matz \u672c\u4eba\uff0c\u90fd\u5728\u652f\u6301\u8fd9\u4e2a gem \uff0c\u5e0c\u671b\u5b83\u80fd\u6210\u529f\u3002</strong></p>\n<h3>Async \u751f\u6001</h3>\n<p>Async \u8fd8\u662f\u4e00\u4e2a gem \u751f\u6001\u7cfb\u7edf\uff0c\u8fd9\u4e9b gem \u80fd\u5f88\u597d\u5730\u4e00\u8d77\u5de5\u4f5c\u3002\u4ee5\u4e0b\u662f\u4e00\u4e9b\u6700\u6709\u7528\u7684\u4f8b\u5b50\uff1a</p>\n<ul>\n<li><code>async-http</code> \u662f\u4e00\u4e2a\u529f\u80fd\u4e30\u5bcc\u7684 HTTP \u5ba2\u6237\u7aef</li>\n<li><code>falcon</code> \u662f\u56f4\u7ed5 Async \u6838\u5fc3\u6784\u5efa\u7684 HTTP \u670d\u52a1\u5668</li>\n<li><code>async-await</code> \u662f Async \u7684\u8bed\u6cd5\u7cd6</li>\n<li><code>async-redis</code> \u662f Redis \u5ba2\u6237\u7aef</li>\n<li>...\u4ee5\u53ca\u8bb8\u591a\u5176\u4ed6\u7684</li>\n</ul>\n<p>\u867d\u7136\u4e0a\u9762\u5217\u51fa\u7684\u6bcf\u4e00\u4e2a gem \u90fd\u63d0\u4f9b\u4e86\u4e00\u4e9b\u6709\u7528\u7684\u4e1c\u897f\uff0c\u4f46\u4e8b\u5b9e\u662f\u4f60\u53ea\u9700\u8981\u6838\u5fc3 async gem \u5c31\u53ef\u4ee5\u83b7\u53d6\u5b83\u7684\u5927\u90e8\u5206\u597d\u5904\u3002</p>\n<h3>\u5f02\u6b65\u6a21\u578b\uff08 Asynchronous paradigm \uff09</h3>\n<p>Asynchronous programming \uff08\u5f02\u6b65\u7f16\u7a0b\uff09\uff0c\uff08\u5728\u4efb\u4f55\u8bed\u8a00\u4e2d\uff0c\u5305\u62ec Ruby \uff09\u5141\u8bb8\u540c\u65f6\u8fd0\u884c\u8bb8\u591a\u4e8b\u60c5\u3002\u8fd9\u6700\u5e38\u89c1\u7684\u662f\u591a\u4e2a\u7f51\u7edc I/O \u64cd\u4f5c\uff08\u5982 HTTP \u8bf7\u6c42\uff09\uff0c\u56e0\u4e3a\u5728\u8fd9\u65b9\u9762 <code>async</code> \u662f\u6700\u6709\u6548\u7684\u3002</p>\n<p>\u591a\u4efb\u52a1\u64cd\u4f5c\u7ecf\u5e38\u5e26\u6765\u6df7\u4e71\uff1a\u201c\u56de\u8c03\u5730\u72f1\uff08 callback hell \uff09\u201d\uff0c\u201cPromise hell \uff08 Promise \u5730\u72f1\uff09\u201d\uff0c\u4e43\u81f3 \"async-await hell \uff08 async-await \u5730\u72f1\uff09\" \u662f\u5176\u4ed6\u8bed\u8a00\u4e2d <code>async</code> \u63a5\u53e3\u7684\u4f17\u6240\u5468\u77e5\u7684\u7f3a\u70b9\u3002</p>\n<p>\u4f46 Ruby \u662f\u4e0d\u540c\u7684\u3002<strong>\u7531\u4e8e\u5176\u8d85\u7fa4\u7684\u8bbe\u8ba1\uff0cAsync Ruby \u4e0d\u53d7\u4efb\u4f55\u8fd9\u4e9b *-\u5730\u72f1\u7684\u56f0\u6270\u3002\u5b83\u5141\u8bb8\u7f16\u5199\u51fa\u4ee4\u4eba\u60ca\u559c\u7684\u5e72\u51c0\u3001\u7b80\u5355\u4e14\u6709\u5e8f\u7684\u4ee3\u7801\u3002</strong>\u5b83\u662f\u4e00\u4e2a\u50cf Ruby \u4e00\u6837\u4f18\u96c5\u7684 async \u5b9e\u73b0\u3002</p>\n<p>\u6ce8\u610f\uff1aAsync \u4e0d\u80fd\u7ed5\u8fc7 Ruby \u7684\u5168\u5c40\u89e3\u91ca\u5668\u9501\uff08 GIL \uff09\u3002</p>\n<hr/>\n<p>\u8bd1\u8005\u6ce8\uff1a</p>\n<ul>\n<li>Async gem \u4ee5\u53ca Fiber Scheduler \u90fd\u662f\u5de5\u4f5c\u5728\u5f53\u524d\u4e3b\u7ebf\u7a0b\u3002\u4ed6\u4eec\u53d7\u5230 GIL \u7ea6\u675f\u3002</li>\n<li>\u4e0d\u53d7 GIL \u7ea6\u675f\u53c2\u8003 <a href=\"https://docs.ruby-lang.org/en/master/ractor_md.html\" rel=\"nofollow\">Ractor</a>\uff0cRactor \u88ab\u8bbe\u8ba1\u7528\u6765\u63d0\u4f9b Ruby \u7684\u5e76\u884c\u6267\u884c\u529f\u80fd\uff0c\u800c\u4e0d\u9700\u8981\u8003\u8651\u7ebf\u7a0b\u5b89\u5168\u95ee\u9898\u3002</li>\n</ul>\n<hr/>\n<h3>\u540c\u6b65\u793a\u4f8b\uff08 Synchronous example \uff09</h3>\n<p>\u8ba9\u6211\u4eec\u4ece\u4e00\u4e2a\u7b80\u5355\u7684\u4f8b\u5b50\u5f00\u59cb\uff1a</p>\n<pre><code class=\"language-ruby\">require \"open-uri\"\n\nstart = Time.now\n\nURI.open(\"https://httpbin.org/delay/1.6\")\nURI.open(\"https://httpbin.org/delay/1.6\")\n\nputs \"Duration: #{Time.now - start}\"\n</code></pre>\n<p>\u4e0a\u8ff0\u4ee3\u7801\u6b63\u5728\u53d1\u8d77\u4e24\u4e2a HTTP \u8bf7\u6c42\u3002\u5355\u4e2a HTTP \u8bf7\u6c42\u7684\u603b\u6301\u7eed\u65f6\u95f4\u4e3a 2 \u79d2\uff0c\u5305\u62ec\uff1a</p>\n<ul>\n<li>\u5927\u7ea6 0.2 \u79d2\u7684\u7f51\u7edc\u5ef6\u8fdf\u5728\u8fdb\u884c\u8bf7\u6c42\u65f6</li>\n<li>1.6 \u79d2\u7684\u670d\u52a1\u5668\u5904\u7406\u65f6\u95f4</li>\n<li>\u5927\u7ea6 0.2 \u79d2\u7684\u7f51\u7edc\u5ef6\u8fdf\u5728\u63a5\u6536\u54cd\u5e94\u65f6</li>\n</ul>\n<p>\u8ba9\u6211\u4eec\u8fd0\u884c\u8fd9\u4e2a\u793a\u4f8b\uff1a</p>\n<p>\u6301\u7eed\u65f6\u95f4\uff1a4.010390391</p>\n<p>\u5982\u9884\u671f\uff0c\u7a0b\u5e8f\u9700\u8981 2 x 2 \u79d2 = 4 \u79d2\u624d\u80fd\u5b8c\u6210\u3002</p>\n<p>\u8fd9\u6bb5\u4ee3\u7801\u8fd8\u4e0d\u9519\uff0c\u4f46\u5b83\u8fd0\u884c\u901f\u5ea6\u6162\u3002\u5bf9\u4e8e\u8fd9\u4e24\u4e2a\u8bf7\u6c42\uff0c\u6267\u884c\u8fc7\u7a0b\u5927\u6982\u50cf\u8fd9\u6837\uff1a</p>\n<ul>\n<li>\u89e6\u53d1\u4e00\u4e2a HTTP \uff08\u8d85\u6587\u672c\u4f20\u8f93\u534f\u8bae\uff09\u8bf7\u6c42</li>\n<li>\u7b49\u5f85 2 \u79d2\u4ee5\u83b7\u53d6\u54cd\u5e94</li>\n</ul>\n<p>\u95ee\u9898\u5728\u4e8e\u7a0b\u5e8f\u5728\u5927\u90e8\u5206\u65f6\u95f4\u91cc\u90fd\u5904\u4e8e\u7b49\u5f85\u72b6\u6001\uff1b 2 \u79d2\u949f\uff08\u5bf9\u4e8e\u8ba1\u7b97\u673a\uff09\u5c31\u50cf\u6c38\u6052\u3002</p>\n<h3>Threads \uff08\u7ebf\u7a0b\uff09</h3>\n<p>\u63d0\u9ad8\u591a\u4e2a\u7f51\u7edc\u8bf7\u6c42\u901f\u5ea6\u7684\u5e38\u7528\u65b9\u6cd5\u662f\u4f7f\u7528\u7ebf\u7a0b\u3002\u4ee5\u4e0b\u662f\u4e00\u4e2a\u793a\u4f8b\uff1a</p>\n<pre><code class=\"language-ruby\">require \"open-uri\"\n\n@counter = 0\n\nstart = Time.now\n\n1.upto(2).map {\n  Thread.new do\n    URI.open(\"https://httpbin.org/delay/1.6\")\n\n    @counter += 1\n  end\n}.each(&amp;:join)\n\nputs \"Duration: #{Time.now - start}\"\n</code></pre>\n<p>\u4ee3\u7801\u7684\u8f93\u51fa\u662f\uff1a</p>\n<p>\u6301\u7eed\u65f6\u95f4: 2.055751087</p>\n<p>\u6211\u4eec\u5c06\u6267\u884c\u65f6\u95f4\u7f29\u77ed\u5230 2 \u79d2\u949f\uff0c\u8fd9\u8868\u660e\u8bf7\u6c42\u5728\u540c\u65f6\u8fd0\u884c\u3002\u90a3\u4e48\uff0c\u95ee\u9898\u89e3\u51b3\u4e86\u5417\uff1f</p>\n<p>\u597d\u5427\uff0c\u522b\u8fc7\u4e8e\u7740\u6025\uff1a<strong>\u5982\u679c\u4f60\u505a\u8fc7\u4efb\u4f55\u771f\u5b9e\u4e16\u754c\u7684\u7ebf\u7a0b\u7f16\u7a0b\uff0c\u4f60\u4f1a\u77e5\u9053\u7ebf\u7a0b\u5f88\u96be\u3002\u771f\u7684\uff0c\u975e\u5e38\u96be\u3002</strong></p>\n<p>\u5982\u679c\u4f60\u6253\u7b97\u505a\u4efb\u4f55\u4e25\u8083\u7684\u7ebf\u7a0b\u5de5\u4f5c\uff0c\u4f60\u6700\u597d\u4e60\u60ef\u4f7f\u7528\u4e92\u65a5\uff08 mutexes \uff09\uff0c\u6761\u4ef6\u53d8\u91cf\uff08 condition variables \uff09\uff0c\u5904\u7406\u8bed\u8a00\u7ea7\u7684\u7ade\u6001\u6761\u4ef6\uff08 race conditions \uff09...\u751a\u81f3\u6211\u4eec\u7684\u7b80\u5355\u793a\u4f8b\u5728 @<a href=\"/member/counter\">counter</a> += 1 \u8fd9\u4e00\u884c\u5c31\u6709\u4e00\u4e2a\u7ade\u6001\u6761\u4ef6\u9519\u8bef\uff01</p>\n<p>\u7ebf\u7a0b\u662f\u56f0\u96be\u7684\uff0c\u5e76\u4e14\u6beb\u65e0\u7591\u95ee\u4e0b\u9762\u7684\u58f0\u660e\u5728 Ruby \u793e\u533a\u4e00\u76f4\u88ab\u4e0d\u65ad\u63d0\u53ca\uff1a</p>\n<pre><code>  I regret adding threads.\n\n                    \u2014 Matz\n</code></pre>\n<h2>Async \u4f8b\u5b50</h2>\n<p>\u9274\u4e8e\u6240\u6709\u7684\u7ebf\u7a0b\u590d\u6742\u6027\uff0cRuby \u793e\u533a\u65e9\u5c31\u5e94\u8be5\u6709\u4e00\u4e2a\u66f4\u597d\u7684\u5e76\u53d1\u6a21\u5f0f\u3002\u6709\u4e86 Async Ruby \uff0c\u6211\u4eec\u7ec8\u4e8e\u6709\u4e86\u4e00\u79cd\u3002</p>\n<h3>async-http</h3>\n<p>\u8ba9\u6211\u4eec\u770b\u770b\u4f7f\u7528 Async Ruby \u6765\u8fdb\u884c\u4e24\u6b21 HTTP \u8bf7\u6c42\u7684\u540c\u6837\u7684\u4f8b\u5b50\uff1a</p>\n<pre><code class=\"language-ruby\">require \"async\"\nrequire \"async/http/internet\"\n\nstart = Time.now\n\nAsync do |task|\n  http_client = Async::HTTP::Internet.new\n\n  task.async do\n    http_client.get(\"https://httpbin.org/delay/1.6\")\n  end\n\n  task.async do\n    http_client.get(\"https://httpbin.org/delay/1.6\")\n  end\nend\n\nputs \"Duration: #{Time.now - start}\"\n\n</code></pre>\n<p>\u793a\u4f8b\u7684\u8f93\u51fa\u662f\uff1a</p>\n<p>\u6301\u7eed\u65f6\u95f4\uff1a1.996420725</p>\n<p>\u770b\u770b\u603b\u8fd0\u884c\u65f6\u95f4\uff0c\u6211\u4eec\u53ef\u4ee5\u770b\u51fa\u8bf7\u6c42\u662f\u540c\u65f6\u8fd0\u884c\u7684\u3002</p>\n<p>\u8fd9\u4e2a\u4f8b\u5b50\u663e\u793a\u4e86 Async Ruby \u7a0b\u5e8f\u7684\u4e00\u822c\u7ed3\u6784\uff1a</p>\n<ul>\n<li>\u4f60\u603b\u662f\u4ece\u4e00\u4e2a\u4f20\u9012\u4efb\u52a1\u7684 <code>Async</code> \u5757\u5f00\u59cb\u3002</li>\n<li>\u8fd9\u4e2a\u4e3b\u4efb\u52a1\u901a\u5e38\u7528\u4e8e\u7528 <code>task.async</code> \u751f\u6210\u66f4\u591a\u7684 <code>Async</code> \u4efb\u52a1\u3002</li>\n<li>\u8fd9\u4e9b\u4efb\u52a1\u76f8\u4e92\u5e76\u53d1\u8fd0\u884c\uff0c\u4e5f\u4e0e\u4e3b\u4efb\u52a1\u5e76\u53d1\u8fd0\u884c\u3002</li>\n</ul>\n<p>\u4e00\u65e6\u4f60\u4e60\u60ef\u4e86\uff0c\u4f60\u4f1a\u53d1\u73b0\u8fd9\u4e2a\u7ed3\u6784\u5b9e\u9645\u4e0a\u975e\u5e38\u6574\u6d01\u3002</p>\n<h3>URI.open</h3>\n<p>\u524d\u4e00\u4e2a\u4f8b\u5b50\u4e2d\u53ef\u4ee5\u88ab\u8ba4\u4e3a\u662f\u4e00\u4e2a\u7f3a\u70b9\u7684\u4e8b\u60c5\u662f\uff0c\u5b83\u4f7f\u7528\u4e86 <code>async-http</code>\uff0c\u4e00\u4e2a\u5177\u6709\u5f02\u6b65\u7279\u6027\u7684 HTTP \u5ba2\u6237\u7aef\u3002\u6211\u4eec\u5927\u591a\u6570\u4eba\u6709\u81ea\u5df1\u559c\u6b22\u7684 Ruby HTTP \u5ba2\u6237\u7aef\uff0c\u6211\u4eec\u4e0d\u60f3\u518d\u82b1\u65f6\u95f4\u53bb\u5b66\u4e60\u53e6\u4e00\u4e2a HTTP \u5e93\u7684\u8be6\u7ec6\u60c5\u51b5\u3002\n\u8ba9\u6211\u4eec\u770b\u6536\u540c\u6837\u7684\u4f8b\u5b50\uff0c\u53ea\u662f\u8fd9\u6b21\u4f7f\u7528 URI.open\uff1a</p>\n<pre><code class=\"language-ruby\">require \"async\"\nrequire \"open-uri\"\n\nstart = Time.now\n\nAsync do |task|\n  task.async do\n    URI.open(\"https://httpbin.org/delay/1.6\")\n  end\n\n  task.async do\n    URI.open(\"https://httpbin.org/delay/1.6\")\n  end\nend\n\nputs \"Duration: #{Time.now - start}\"\n</code></pre>\n<p>\u4e0e\u524d\u4e00\u4e2a\u4f8b\u5b50\u7684\u552f\u4e00\u533a\u522b\u662f\uff0c\u6211\u4eec\u7528 Ruby \u7684\u6807\u51c6\u5e93\u4e2d\u7684\u65b9\u6cd5 <code>URI.open</code> \u66ff\u6362\u4e86 <code>async-http</code>\u3002</p>\n<p>\u793a\u4f8b\u7684\u8f93\u51fa\u662f\uff1a</p>\n<p>\u6301\u7eed\u65f6\u95f4\uff1a2.030451785</p>\n<p>\u8fd9\u4e2a\u6301\u7eed\u65f6\u95f4\u663e\u793a\u4e86\u4e24\u4e2a\u8bf7\u6c42\u662f\u5e76\u884c\u8fd0\u884c\u7684\uff0c\u6240\u4ee5\u6211\u4eec\u8ba4\u4e3a <code>URI.open</code> \u662f\u5f02\u6b65\u8fd0\u884c\u7684\uff01</p>\n<p>\u8fd9\u4e00\u5207\u771f\u7684\u5f88\u597d\u3002\u6211\u4eec\u4e0d\u4ec5\u4e0d\u9700\u8981\u5fcd\u53d7\u7ebf\u7a0b\u53ca\u5176\u590d\u6742\u6027\uff0c\u800c\u4e14\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528 Ruby \u7684\u6807\u51c6 <code>URI.open</code> \u6765\u8fd0\u884c\u8bf7\u6c42\uff0c</p>\n<p>\u65e0\u8bba\u662f\u5728 <code>Async</code> \u5757\u7684\u5916\u90e8\u8fd8\u662f\u5185\u90e8\u3002\u8fd9\u65e0\u7591\u53ef\u4ee5\u4e3a\u6211\u4eec\u63d0\u4f9b\u4e00\u4e9b\u65b9\u4fbf\u7684\u4ee3\u7801\u91cd\u7528\u3002</p>\n<h3>\u5176\u4ed6 HTTP clients</h3>\n<p>\u867d\u7136 <code>URI.open</code> \u662f\u666e\u901a\u7684 Ruby \uff0c\u4f46\u53ef\u80fd\u5e76\u4e0d\u662f\u4f60\u559c\u6b22\u7684\u8fdb\u884c HTTP \u8bf7\u6c42\u7684\u65b9\u5f0f\u3002\u800c\u4e14\uff0c\u4f60\u4e5f\u4e0d\u7ecf\u5e38\u770b\u5230\u5b83\u88ab\u7528\u4e8e\"serious work(\u6b63\u5f0f\u7684\u5de5\u4f5c)\"\u3002</p>\n<p>\u4f60\u53ef\u80fd\u6709\u4f60\u81ea\u5df1\u559c\u6b22\u7684 HTTP  gem \uff0c\u4f60\u53ef\u80fd\u4f1a\u95ee \"\u5b83\u80fd\u5728 Async \u4e2d\u5de5\u4f5c\u5417\"\uff1f\u4e3a\u4e86\u627e\u51fa\u7b54\u6848\uff0c\u8fd9\u91cc\u6709\u4e00\u4e2a\u4f7f\u7528 <code>HTTParty</code>\uff08\u4e00\u79cd\u77e5\u540d\u7684 HTTP \u5ba2\u6237\u7aef\uff09\u7684\u4f8b\u5b50\u3002</p>\n<pre><code class=\"language-ruby\">require \"async\"\nrequire \"open-uri\"\nrequire \"httparty\"\n\nstart = Time.now\n\nAsync do |task|\n  task.async do\n    URI.open(\"https://httpbin.org/delay/1.6\")\n  end\n\n  task.async do\n    HTTParty.get(\"https://httpbin.org/delay/1.6\")\n  end\nend\n\nputs \"Duration: #{Time.now - start}\"\n</code></pre>\n<p>\u5728\u8fd9\u4e2a\u4f8b\u5b50\u4e2d\uff0c\u6211\u4eec\u5728\u4e00\u8d77\u8fd0\u884c\u4e86 <code>URI.open</code> \u548c <code>HTTParty</code>\uff0c\u8fd9\u5b8c\u5168\u6ca1\u95ee\u9898\u3002</p>\n<p>\u8f93\u51fa\u662f\uff1a</p>\n<p>\u6301\u7eed\u65f6\u95f4\uff1a2.010069566</p>\n<p>\u5b83\u8fd0\u884c\u7684\u65f6\u95f4\u7a0d\u5fae\u8d85\u8fc7\u4e86 2 \u79d2\uff0c\u8fd9\u8868\u660e\u4e24\u4e2a\u8bf7\u6c42\u662f\u5e76\u53d1\u8fd0\u884c\u7684\uff08\u540c\u65f6\u8fdb\u884c\uff09\u3002\n\u8fd9\u91cc\u7684\u8981\u70b9\u662f\uff1a<strong>\u4f60\u53ef\u4ee5\u5728\u4e00\u4e2a Async \u4e0a\u4e0b\u6587\u4e2d\u8fd0\u884c\u4efb\u4f55 HTTP \u5ba2\u6237\u7aef\uff0c\u5b83\u5c06\u4f1a\u5f02\u6b65\u8fd0\u884c\u3002Async Ruby \u5b8c\u5168\u652f\u6301\u4efb\u4f55\u73b0\u6709\u7684 HTTP gem \uff01</strong></p>\n<h2>\u9ad8\u7ea7\u4f8b\u5b50</h2>\n<p>\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u6211\u4eec\u53ea\u770b\u5230 Async Ruby \u7528\u5404\u79cd HTTP \u5ba2\u6237\u7aef\u8fdb\u884c\u8bf7\u6c42\u3002\u8ba9\u6211\u4eec\u63ed\u793a Async \u5728 Ruby 3 \u4e2d\u7684\u5168\u90e8\u80fd\u529b\u3002</p>\n<pre><code class=\"language-ruby\">require \"async\"\nrequire \"open-uri\"\nrequire \"httparty\"\nrequire \"redis\"\nrequire \"net/ssh\"\nrequire \"sequel\"\n\nDB = Sequel.postgres\nSequel.extension(:fiber_concurrency)\nstart = Time.now\n\nAsync do |task|\n  task.async do\n    URI.open(\"https://httpbin.org/delay/1.6\")\n  end\n\n  task.async do\n    HTTParty.get(\"https://httpbin.org/delay/1.6\")\n  end\n\n  task.async do\n    Redis.new.blpop(\"abc123\", 2)\n  end\n\n  task.async do\n    Net::SSH.start(\"164.90.237.21\").exec!(\"sleep 1\")\n  end\n\n  task.async do\n    DB.run(\"SELECT pg_sleep(2)\")\n  end\n\n  task.async do\n    sleep 2\n  end\n\n  task.async do\n    `sleep 2`\n  end\nend\n\nputs \"Duration: #{Time.now - start}\"\n</code></pre>\n<p>\u6211\u4eec\u6269\u5c55\u4e86\u5305\u542b URI.open \u548c HTTParty \u7684\u524d\u4e00\u4e2a\u4f8b\u5b50\uff0c\u589e\u52a0\u4e86\u4e94\u4e2a\u9644\u52a0\u64cd\u4f5c\uff1a</p>\n<ul>\n<li>Redis \u8bf7\u6c42</li>\n<li>\u4f7f\u7528 net-ssh gem \u8fdb\u884c\u7684 SSH \u8fde\u63a5</li>\n<li>\u4f7f\u7528 sequel gem \u8fdb\u884c\u7684\u6570\u636e\u5e93\u67e5\u8be2</li>\n<li>Ruby \u7684 sleep \u65b9\u6cd5</li>\n<li>\u8fd0\u884c sleep \u53ef\u6267\u884c\u6587\u4ef6\u7684\u7cfb\u7edf\u547d\u4ee4\u3002</li>\n</ul>\n<p>\u8fd9\u4e2a\u4f8b\u5b50\u4e2d\u7684\u6240\u6709\u64cd\u4f5c\u4e5f\u9700\u8981\u6070\u597d 2 \u79d2\u624d\u80fd\u8fd0\u884c\u3002</p>\n<p>\u4ee5\u4e0b\u662f\u793a\u4f8b\u8f93\u51fa\uff1a</p>\n<p>\u6301\u7eed\u65f6\u95f4\uff1a2.083171146</p>\n<p>\u6211\u4eec\u5f97\u5230\u7684\u8f93\u51fa\u7ed3\u679c\u548c\u4e4b\u524d\u4e00\u6837\uff0c\u8fd9\u8868\u660e\u6240\u6709\u7684\u64cd\u4f5c\u90fd\u662f\u5e76\u53d1\u8fd0\u884c\u7684\u3002\u54c7\uff0c\u8fd9\u6709\u5f88\u591a\u4e0d\u540c\u7684 gem \u53ef\u4ee5\u5f02\u6b65\u8fd0\u884c\uff01</p>\n<p>\u91cd\u70b9\uff1a<strong>\u4efb\u4f55\u963b\u585e\u64cd\u4f5c\uff08 Ruby \u89e3\u91ca\u5668\u7b49\u5f85\u7684\u65b9\u6cd5\uff09\u90fd\u4e0e Async \u517c\u5bb9\uff0c\u5e76\u5c06\u5728 Ruby 3.0 \u548c\u66f4\u9ad8\u7248\u672c\u7684 Async \u4ee3\u7801\u5757\u4e2d\u5f02\u6b65\u5de5\u4f5c\u3002</strong></p>\n<p>\u6027\u80fd\u770b\u8d77\u6765\u5f88\u597d\uff1a7 x 2 = 14 \u79d2\uff0c\u4f46\u793a\u4f8b\u5728 2 \u79d2\u5185\u5b8c\u6210 \u2013 \u5f88\u5bb9\u6613\u5f97\u5230 7 \u500d\u7684\u63d0\u5347\u3002</p>\n<h3>Fiber Scheduler \uff08\u7ea4\u7a0b\u8c03\u5ea6\u5668\uff09</h3>\n<p>\u8ba9\u6211\u4eec\u82b1\u4e00\u70b9\u65f6\u95f4\u6765\u53cd\u601d\u4e00\u4e9b\u91cd\u8981\u7684\u4e8b\u60c5\u3002\u8fd9\u4e2a\u4f8b\u5b50\u4e2d\u7684\u6240\u6709\u64cd\u4f5c\uff08\u4f8b\u5982\uff0cURI.open \uff0cRedis \uff0csleep \uff09\u90fd\u4f1a\u6839\u636e\u4e0a\u4e0b\u6587\u7684\u4e0d\u540c\u800c\u8868\u73b0\u4e0d\u540c\uff1a</p>\n<ul>\n<li>\u540c\u6b65\u6267\u884c:\n\u64cd\u4f5c\u9ed8\u8ba4\u540c\u6b65\u6267\u884c\u3002\u6574\u4e2a Ruby \u7a0b\u5e8f\uff08\u6216\u8005\u66f4\u5177\u4f53\u7684\u8bf4\uff0c\u5f53\u524d\u7684\u7ebf\u7a0b\uff09\u4f1a\u7b49\u5f85\u4e00\u4e2a\u64cd\u4f5c\u5b8c\u6210\u540e\u624d\u4f1a\u8fdb\u884c\u4e0b\u4e00\u4e2a\u64cd\u4f5c\u3002</li>\n<li>\u5f02\u6b65\u6267\u884c:\n\u5f53\u64cd\u4f5c\u5305\u88f9\u5728\u4e00\u4e2a Async \u5757\u4e2d\u65f6\uff0c\u64cd\u4f5c\u4f1a\u5f02\u6b65\u5730\u6267\u884c\u3002\u7531\u6b64\uff0c\u591a\u4e2a HTTP \u6216\u7f51\u7edc\u8bf7\u6c42\u53ef\u4ee5\u540c\u65f6\u8fd0\u884c\u3002</li>\n</ul>\n<p>\u4f46\u662f\uff0c\u4f8b\u5982\uff0c<code>HTTParty</code> \u6216 <code>sleep</code> \u65b9\u6cd5\u5982\u4f55\u80fd\u540c\u6b65\u548c\u5f02\u6b65\u540c\u65f6\u5b58\u5728\u5462\uff1f Async \u5e93\u662f\u5426\u5bf9\u6240\u6709\u8fd9\u4e9b gems \u548c\u5185\u90e8 Ruby \u65b9\u6cd5\u8fdb\u884c\u4e86\u7334\u5b50\u8865\u4e01\uff1f\n\u8fd9\u79cd\u9b54\u672f\u662f\u7531\u4e8e <code>Fiber Scheduler</code>\u3002\u8fd9\u662f Ruby 3.0 \u7684\u4e00\u4e2a\u7279\u6027\uff0c\u4f7f\u5f97 <code>async</code> \u80fd\u591f\u5f88\u597d\u5730\u4e0e\u73b0\u6709\u7684 Ruby gems \u548c\u65b9\u6cd5\u96c6\u6210 - \u4e0d\u9700\u8981\u4efb\u4f55 hack \u6216 \u7334\u5b50\u8865\u4e01(Monkey patch) \uff01</p>\n<p><a href=\"https://brunosutic.com/blog/ruby-fiber-scheduler\" rel=\"nofollow\">Fiber Scheduler \u4e5f\u53ef\u4ee5\u5355\u72ec\u4f7f\u7528</a> (<a href=\"https://mark24code.github.io/ruby/2023/10/12/Ruby-Fiber-Scheduler.html\" rel=\"nofollow\">\u94fe\u63a5\u8bd1\u6587</a>)\uff01\u7528\u8fd9\u79cd\u65b9\u5f0f\uff0c\u53ea\u9700\u8981\u51e0\u4e2a\u5185\u7f6e\u7684 Ruby \u65b9\u6cd5\u5c31\u80fd\u542f\u7528\u5f02\u6b65\u7f16\u7a0b\u3002</p>\n<p>\u5982\u4f60\u6240\u60f3\uff0cFiber Scheduler \u89e6\u53ca\u7684\u4ee3\u7801\u8303\u56f4\u975e\u5e38\u5e7f\uff1a\u5b83\u662f Ruby \u5f53\u524d\u6240\u6709\u7684\u963b\u585e API \uff01\u8fd9\u7edd\u4e0d\u4ec5\u4ec5\u662f\u4e00\u4e2a\u5c0f\u529f\u80fd\u3002</p>\n<h2>\u6269\u5c55\u4f8b\u5b50</h2>\n<p>\u8ba9\u6211\u4eec\u63d0\u9ad8\u6548\u7387\uff0c\u5e76\u5c55\u793a\u4e00\u4e2a Async Ruby \u64c5\u957f\u7684\u53e6\u4e00\u65b9\u9762\uff1a\u6269\u5c55(scaling)\u3002</p>\n<pre><code class=\"language-ruby\">require \"async\"\nrequire \"async/http/internet\"\nrequire \"redis\"\nrequire \"sequel\"\n\nDB = Sequel.postgres(max_connections: 1000)\nSequel.extension(:fiber_concurrency)\n# Warming up redis clients\nredis_clients = 1.upto(1000).map { Redis.new.tap(&amp;:ping) }\n\nstart = Time.now\n\nAsync do |task|\n  http_client = Async::HTTP::Internet.new\n\n  1000.times do |i|\n    task.async do\n      http_client.get(\"https://httpbin.org/delay/1.6\")\n    end\n\n    task.async do\n      redis_clients[i].blpop(\"abc123\", 2)\n    end\n\n    task.async do\n      DB.run(\"SELECT pg_sleep(2)\")\n    end\n\n    task.async do\n      sleep 2\n    end\n\n    task.async do\n      `sleep 2`\n    end\n  end\nend\n\nputs \"Duration: #{Time.now - start}s\"\n</code></pre>\n<p>\u6b64\u4f8b\u5b50\u57fa\u4e8e\u4e4b\u524d\u7684\u90a3\u4e2a\u4f8b\u5b50\uff0c\u53ea\u662f\u505a\u4e86\u4e00\u4e9b\u6539\u52a8\uff1a</p>\n<ul>\n<li>\u5728 <code>Async</code> \u533a\u5757\u4e2d\u7684\u6240\u6709\u5185\u5bb9\u90fd\u4f1a\u88ab\u91cd\u590d <code>1000.times</code> (\u8fd0\u884c 1000 \u6b21)\uff0c\u8fd9\u5c06\u5e76\u53d1\u64cd\u4f5c\u7684\u6570\u91cf\u589e\u52a0\u5230\u4e86 5000 \u3002</li>\n<li>\u51fa\u4e8e\u6027\u80fd\u8003\u8651\uff0c\u6211\u4eec\u5c06 <code>URI.open</code> \u548c <code>HTTParty</code> \u66ff\u6362\u4e3a\u4e86 <code>async-http</code> HTTP <code>\u5ba2\u6237\u7aef\u3002async-http</code> \u53ef\u4ee5\u4e0e HTTP2 \u4e00\u8d77\u5de5\u4f5c\uff0c\u5f53\u8fdb\u884c\u5927\u91cf\u8bf7\u6c42\u65f6\uff0c\u5b83\u7684\u901f\u5ea6\u8981\u5feb\u5f97\u591a\u3002</li>\n<li>SSH \u64cd\u4f5c\u88ab\u79fb\u9664\u4e86\uff0c\u56e0\u4e3a\u6211\u627e\u4e0d\u5230\u4e00\u79cd\u6b63\u786e\u7684\u914d\u7f6e\u65b9\u6cd5\u53ef\u4ee5\u4f7f\u5176\u9ad8\u6548\u5730\u5de5\u4f5c\u3002</li>\n</ul>\n<p>\u5c31\u50cf\u4e4b\u524d\u4e00\u6837\uff0c\u6bcf\u4e2a\u72ec\u7acb\u64cd\u4f5c\u90fd\u9700\u8981 2 \u79d2\u624d\u80fd\u6267\u884c\u3002\u5176\u8f93\u51fa\u4e3a\uff1a</p>\n<p>\u6301\u7eed\u65f6\u95f4: 13.672289712</p>\n<p>\u8fd9\u8868\u660e\u7d2f\u79ef\u8fd0\u884c\u65f6\u95f4\u4e3a 10,000 \u79d2\u7684 5,000 \u4e2a\u64cd\u4f5c\u4ec5\u5728 13.6 \u79d2\u5185\u5c31\u5b8c\u6210\u4e86\uff01</p>\n<p>\u8fd9\u4e2a\u6301\u7eed\u65f6\u95f4\u6bd4\u524d\u9762\u7684\u4f8b\u5b50\uff08 2 \u79d2\uff09\u8981\u957f\uff0c\u8fd9\u662f\u56e0\u4e3a\u521b\u5efa\u8fd9\u4e48\u591a\u7f51\u7edc\u8fde\u63a5\u7684\u5f00\u9500\u3002</p>\n<p>\u6211\u4eec\u51e0\u4e4e\u6ca1\u6709\u8fdb\u884c\u6027\u80fd\u8c03\u4f18\uff08\u4f8b\u5982\uff0c\u8c03\u6574\u5783\u573e\u6536\u96c6\uff0c\u5185\u5b58\u5206\u914d\u7b49\uff09\uff0c<strong>\u4f46\u6211\u4eec\u4ecd\u7136\u5b9e\u73b0\u4e86 730 \u500d\u7684\u201c\u52a0\u901f\u201d\uff0c\u5728\u6211\u770b\u6765\uff0c\u8fd9\u662f\u4e00\u4e2a\u76f8\u5f53\u4ee4\u4eba\u5370\u8c61\u6df1\u523b\u7684\u7ed3\u679c\uff01</strong></p>\n<h3>\u6269\u5bb9\u9650\u5236(Scaling limits)</h3>\n<p>\u6700\u597d\u7684\u90e8\u5206\u662f\uff1a\u6211\u4eec\u53ea\u662f\u521d\u6b65\u63a2\u7d22\u4e86\u4f7f\u7528 Async Ruby \u6240\u80fd\u505a\u5230\u7684\u4e8b\u60c5\u3002</p>\n<p>\u867d\u7136\u7ebf\u7a0b\uff08 Threads \uff09\u7684\u6700\u5927\u6570\u91cf\u662f 2048 \uff08\u81f3\u5c11\u5728\u6211\u7684\u673a\u5668\u4e0a\u662f\u8fd9\u6837\uff09\uff0c<strong>\u4f46\u662f Async tasks \u7684\u4e0a\u9650\u6570\u91cf\u662f\u767e\u4e07\u7ea7\u522b\u7684\uff01</strong></p>\n<p>\u4f60\u771f\u7684\u53ef\u4ee5\u540c\u65f6\u8fd0\u884c\u767e\u4e07\u4e2a\u5f02\u6b65\u64cd\u4f5c\u5417\uff1f\u662f\u7684\uff0c\u4f60\u53ef\u4ee5 - \u5df2\u7ecf\u6709\u4e9b\u7528\u6237\u505a\u5230\u4e86\u3002</p>\n<p>Async \u771f\u7684\u4e3a Ruby \u6253\u5f00\u4e86\u65b0\u5c40\u9762\uff1a\u60f3\u8c61\u4e00\u4e0b\u4e00\u4e2a HTTP \u670d\u52a1\u5668\u5904\u7406\u6210\u5343\u4e0a\u4e07\u7684\u5ba2\u6237\uff0c\u6216\u8005\u540c\u4e00\u65f6\u95f4\u5904\u7406\u6210\u767e\u4e0a\u5343\u7684 websocket \u8fde\u63a5 ... \u8fd9\u90fd\u662f\u53ef\u80fd\u7684\uff01</p>\n<h2>\u7ed3\u8bba</h2>\n<p><strong>Async Ruby \u7ecf\u8fc7\u4e86\u6f2b\u957f\u800c\u795e\u79d8\u7684\u5f00\u53d1\u671f\uff0c\u4f46\u73b0\u5728\u5b83\u7a33\u5b9a\u4e14\u5df2\u7ecf\u51c6\u5907\u597d\u6295\u5165\u751f\u4ea7\u3002</strong>\u5df2\u7ecf\u6709\u4e00\u4e9b\u516c\u53f8\u5728\u751f\u4ea7\u73af\u5883\u4e0b\u8fd0\u884c\u5b83\u5e76\u4ece\u4e2d\u53d7\u76ca\u3002\u8981\u5f00\u59cb\u4f7f\u7528\u5b83\uff0c\u4f60\u53ef\u4ee5\u53bb <a href=\"https://github.com/socketry/async\" rel=\"nofollow\">Async \u7684\u4ed3\u5e93</a>\u770b\u770b\u3002</p>\n<p>\u552f\u4e00\u7684\u6ce8\u610f\u70b9\u662f\uff0c\u5b83\u4e0d\u80fd\u548c Ruby on Rails \u4e00\u8d77\u5de5\u4f5c\uff0c\u56e0\u4e3a <code>ActiveRecord</code> \u4e0d\u652f\u6301 <code>Async</code> gem \u3002\u4f46\u5982\u679c\u4e0d\u6d89\u53ca\u5230 <code>ActiveRecord</code>\uff0c\u4f60\u4ecd\u7136\u53ef\u4ee5\u5728 Rails \u4e2d\u4f7f\u7528\u5b83\u3002</p>\n<p>Async \u7684\u6700\u5927\u4f18\u52bf\u5728\u4e8e\u6269\u5c55\u7f51\u7edc I/O \u64cd\u4f5c\uff0c\u6bd4\u5982\u8fdb\u884c\u6216\u63a5\u6536 HTTP \u8bf7\u6c42\u3002\u5bf9\u4e8e CPU \u5bc6\u96c6\u578b\u7684\u5de5\u4f5c\u8d1f\u8f7d\uff0c\u7ebf\u7a0b\u662f\u66f4\u597d\u7684\u9009\u62e9\uff0c\u4f46\u81f3\u5c11\u6211\u4eec\u4e0d\u518d\u9700\u8981\u628a\u4ed6\u4eec\u7528\u4e8e\u6240\u6709\u4e8b\u60c5\u3002</p>\n<p>Async Ruby \u975e\u5e38\u5f3a\u5927\uff0c\u53ef\u6269\u5c55\u6027\u6781\u9ad8\u3002\u5b83\u662f\u4e00\u4e2a\u6e38\u620f\u89c4\u5219\u6539\u53d8\u8005\uff0c\u6211\u5e0c\u671b\u8fd9\u7bc7\u6587\u7ae0\u80fd\u8bc1\u660e\u8fd9\u4e00\u70b9\u3002Async \u6539\u53d8\u4e86 Ruby \u7684\u53ef\u80fd\u6027\uff0c\u5e76\u4e14\u5f53\u6211\u4eec\u6240\u6709\u4eba\u5f00\u59cb\u66f4\u591a\u5730\u201c\u5f02\u6b65\u201d\u601d\u8003\u65f6\uff0c\u5b83\u5c06\u5bf9 Ruby \u793e\u533a\u4ea7\u751f\u91cd\u5927\u5f71\u54cd\u3002</p>\n<p>\u6700\u597d\u7684\u4e00\u70b9\u662f\uff0c\u5b83\u4e0d\u4f1a\u4f7f\u4efb\u4f55\u73b0\u6709\u7684\u4ee3\u7801\u53d8\u5f97\u8fc7\u65f6\u3002<strong>\u5c31\u50cf Ruby \u672c\u8eab\u4e00\u6837\uff0cAsync \u8bbe\u8ba1\u5f97\u5f88\u7f8e\uff0c\u4f7f\u7528\u8d77\u6765\u4e5f\u5f88\u6109\u5feb\u3002</strong></p>\n<p>\u5e0c\u671b\u4f60\u5728\u4f7f\u7528 Async Ruby \u65f6\u7f16\u7a0b\u6109\u5feb\uff01</p>\n<p>Happy hacking with Async Ruby!</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/981356", 
      "title": "[\u7ffb\u8bd1] Ruby Fiber Scheduler", 
      "id": "https://www.v2ex.com/t/981356", 
      "date_published": "2023-10-12T07:46:08+00:00", 
      "content_html": "<ul>\n<li>\u539f\u6587\u4f5c\u8005\uff1a<a href=\"https://brunosutic.com/\" rel=\"nofollow\">Bruno Sutic</a></li>\n<li>\u539f\u6587\u94fe\u63a5: <a href=\"https://brunosutic.com/blog/ruby-fiber-scheduler\" rel=\"nofollow\">\u300a Ruby Fiber Scheduler \u300b</a></li>\n<li>\u539f\u6587\u65f6\u95f4\uff1a2022 \u5e74 2 \u6708 25 \u65e5</li>\n<li>\u539f\u6587\u8ba8\u8bba\uff1a<a href=\"https://news.ycombinator.com/item?id=30488227\" rel=\"nofollow\">Hacker News \u8ba8\u8bba</a></li>\n<li>\u8bd1\u8005\uff1a<a href=\"https://github.com/Mark24Code\" rel=\"nofollow\">Mark24</a></li>\n<li>\u8bd1\u8005 Email\uff1a <a href=\"mailto:mark.zhangyoung@gmail.com\">mark.zhangyoung@gmail.com</a></li>\n<li>\u8bd1\u6587\u94fe\u63a5\uff1a <a href=\"https://mark24code.github.io/ruby/2023/10/12/Ruby-Fiber-Scheduler.html\" rel=\"nofollow\">https://mark24code.github.io/ruby/2023/10/12/Ruby-Fiber-Scheduler.html</a></li>\n</ul>\n<p>Fiber Scheduler(\u7ea4\u7a0b\u8c03\u5ea6\u5668)\u5728 Ruby \u4e2d\u5b9e\u73b0\u5f02\u6b65\u7f16\u7a0b\u3002\u8be5\u529f\u80fd\u662f Ruby 3.0 \u7684\u4e00\u5927\u589e\u5f3a\u529f\u80fd\uff0c\u5e76\u4e14\u4e5f\u662f\u4f18\u79c0\u7684 async gem \u7684\u6838\u5fc3\u7ec4\u4ef6\u4e4b\u4e00\u3002\n\u6700\u68d2\u7684\u4e00\u70b9\u662f\uff0c\u4f60\u5e76\u4e0d\u9700\u8981\u4e00\u4e2a\u5b8c\u6574\u7684\u6846\u67b6\u5c31\u80fd\u5f00\u59cb\uff01<strong>\u53ea\u9700\u4f7f\u7528\u4e00\u5bf9\u5185\u7f6e\u7684 Ruby \u65b9\u6cd5\uff0c\u5c31\u80fd\u72ec\u7acb\u5730\u5b9e\u73b0\u7ea4\u7a0b\u8c03\u5ea6\u5668\u5e76\u4eab\u53d7\u5230\u5f02\u6b65\u7f16\u7a0b\u7684\u597d\u5904\u3002</strong></p>\n<p>\u7ea4\u7a0b\u8c03\u5ea6\u5668\u4e3b\u8981\u5305\u62ec\u4e24\u90e8\u5206\uff1a</p>\n<ul>\n<li>\n<p>Fiber Scheduler interface \uff08\u7ea4\u7a0b\u8c03\u5ea6\u5668\u63a5\u53e3\uff09\n\u8fd9\u662f\u4e00\u5957\u5185\u7f6e\u4e8e\u7f16\u7a0b\u8bed\u8a00\u4e2d\u7684\u963b\u585e\u64cd\u4f5c\u94a9\u5b50\u3002\u94a9\u5b50\u5b9e\u73b0\u88ab\u59d4\u6258\u7ed9 <code>Fiber.scheduler</code> \u5bf9\u8c61\u3002</p>\n</li>\n<li>\n<p>Fiber Scheduler implementation \uff08\u7ea4\u7a0b\u8c03\u5ea6\u5668\u7684\u5b9e\u73b0\uff09\n\u5b9e\u73b0\u4e86\u5f02\u6b65\u884c\u4e3a\u3002\u8fd9\u662f\u4e00\u4e2a\u9700\u8981\u7a0b\u5e8f\u5458\u663e\u5f0f\u8bbe\u7f6e\u7684\u5bf9\u8c61\uff0c\u56e0\u4e3a Ruby \u4e0d\u63d0\u4f9b\u9ed8\u8ba4\u7684 Fiber Scheduler \uff08\u7ea4\u7a0b\u8c03\u5ea6\u5668\uff09\u5b9e\u73b0\u3002</p>\n</li>\n</ul>\n<p>\u975e\u5e38\u611f\u8c22 Samuel Williams \uff01\u4ed6\u662f Ruby \u7684\u6838\u5fc3\u5f00\u53d1\u8005\uff0c\u8bbe\u8ba1\u5e76\u5b9e\u73b0\u4e86\u7ea4\u7a0b\u8c03\u5ea6\u5668\u8fd9\u4e00\u529f\u80fd\u5e76\u6574\u5408\u5230\u4e86\u8bed\u8a00\u4e2d\u3002</p>\n<h2>Fiber Scheduler interface \uff08\u7ea4\u7a0b\u8c03\u5ea6\u5668\u63a5\u53e3\uff09</h2>\n<p>Fiber Scheduler \uff08\u7ea4\u7a0b\u8c03\u5ea6\u5668\uff09\u63a5\u53e3\u662f\u4e00\u5957\u963b\u585e\u64cd\u4f5c\u7684\u94a9\u5b50\uff0c\u5b83\u5141\u8bb8\u5728\u963b\u585e\u64cd\u4f5c\u53d1\u751f\u65f6\u63d2\u5165\u5f02\u6b65\u884c\u4e3a\u3002\u5b83\u50cf\u662f\u5e26\u6709\u53cd\u8f6c\u7684\u56de\u8c03\uff1a\u5f53\u5f02\u6b65\u56de\u8c03\u88ab\u6267\u884c\u65f6\uff0c\u4e3b\u963b\u585e\u65b9\u6cd5\u4e0d\u4f1a\u8fd0\u884c\u3002\n\u8fd9\u4e9b\u94a9\u5b50\u5728 <a href=\"https://docs.ruby-lang.org/en/3.1/Fiber/SchedulerInterface.html\" rel=\"nofollow\">Fiber::SchedulerInterface</a> \u7c7b\u4e2d\u6709\u6587\u6863\u8bb0\u5f55\u3002\u8fd9\u4e2a Ruby \u529f\u80fd\u80cc\u540e\u7684\u4e00\u4e9b\u4e3b\u8981\u601d\u60f3\u5305\u62ec\uff1a</p>\n<ul>\n<li>\u94a9\u5b50\u662f\u4f4e\u5c42\u7ea7\u7684\u3002\u8fd9\u5bfc\u81f4\u4e86\u5c11\u91cf\u7684\u94a9\u5b50\uff0c\u6bcf\u4e2a\u94a9\u5b50\u5904\u7406\u8bb8\u591a\u9ad8\u5c42\u7ea7\u65b9\u6cd5\u7684\u884c\u4e3a\u3002\u4f8b\u5982\uff0c<code>#address_resolve</code> \u94a9\u5b50\u8d1f\u8d23\u5904\u7406\u5927\u7ea6 20 \u4e2a\u65b9\u6cd5\u3002</li>\n<li>\u94a9\u5b50\u53ea\u5728 <code>Fiber.scheduler</code> \u5bf9\u8c61\u8bbe\u7f6e\u540e\u624d\u4f1a\u5de5\u4f5c\uff0c\u94a9\u5b50\u7684\u5b9e\u73b0\u88ab\u59d4\u6258\u7ed9\u8be5\u5bf9\u8c61\u3002</li>\n<li>\u94a9\u5b50\u7684\u884c\u4e3a\u5e94\u8be5\u662f\u5f02\u6b65\u7684\u3002</li>\n</ul>\n<h3>Hook implementation (\u94a9\u5b50\u5b9e\u73b0)</h3>\n<p>\u8ba9\u6211\u4eec\u770b\u4e00\u4e2a\u793a\u4f8b\uff0c\u663e\u793a\u5982\u4f55\u5b9e\u73b0 <code>Kernel#sleep</code> \u94a9\u5b50\u3002\u5728\u5b9e\u8df5\u4e2d\uff0c\u6240\u6709\u7684\u94a9\u5b50\u90fd\u662f\u7528 C \u8bed\u8a00\u7f16\u5199\u7684\uff0c\u4f46\u4e3a\u4e86\u6e05\u6670\u8d77\u89c1\uff0c\u8fd9\u91cc\u4f7f\u7528\u4e86 Ruby \u4f2a\u4ee3\u7801\u3002</p>\n<pre><code class=\"language-ruby\">module Kernel\n  def sleep(duration = nil)\n    if Fiber.scheduler\n      Fiber.scheduler.kernel_sleep(duration)\n    else\n      synchronous_sleep(duration)\n    end\n  end\nend\n</code></pre>\n<p>\u4ee5\u4e0a\u4ee3\u7801\u7684\u9605\u8bfb\u65b9\u5f0f\u5982\u4e0b\uff1a</p>\n<ul>\n<li>\u5982\u679c\u8bbe\u7f6e\u4e86 <code>Fiber.scheduler</code> \u5bf9\u8c61 - \u8fd0\u884c\u5176 <code>#kernel_sleep</code> \u65b9\u6cd5\u3002<code>#kernel_sleep</code> \u5e94\u8be5\u5f02\u6b65\u8fd0\u884c <code>sleep</code>\u3002</li>\n<li>\u5426\u5219\uff0c\u6267\u884c\u5e38\u89c4\u7684 <code>synchronous_sleep</code>\uff0c\u5b83\u4f1a\u963b\u585e\u5f53\u524d\u7ebf\u7a0b\u76f4\u5230 <code>sleep</code> \u5b8c\u6210\u3002</li>\n</ul>\n<p>\u5176\u4ed6\u7684\u94a9\u5b50\u7684\u5de5\u4f5c\u65b9\u5f0f\u4e5f\u7c7b\u4f3c\u3002</p>\n<h3>Blocking operations \uff08\u963b\u585e\u64cd\u4f5c\uff09</h3>\n<p>\u5df2\u7ecf\u591a\u6b21\u63d0\u5230\u4e86\"Blocking operations \uff08\u963b\u585e\u64cd\u4f5c\uff09\"\u8fd9\u4e2a\u6982\u5ff5\uff0c\u4f46\u5b83\u5230\u5e95\u662f\u4ec0\u4e48\u610f\u601d\u5462\uff1f<strong>\u963b\u585e\u64cd\u4f5c\u662f\u6307\u4efb\u4f55 Ruby \u8fdb\u7a0b\uff08\u66f4\u5177\u4f53\u5730\u8bf4\uff1a\u5f53\u524d\u7ebf\u7a0b\uff09\u6700\u7ec8\u4f1a\u7b49\u5f85\u7684\u64cd\u4f5c\u3002</strong>\u4e00\u4e2a\u66f4\u5177\u63cf\u8ff0\u6027\u7684\u540d\u79f0\u662f\u201cwaiting operations \uff08\u7b49\u5f85\u64cd\u4f5c\uff09\u201d\u3002\n\u4e00\u4e9b\u4f8b\u5b50\u5982\u4e0b\uff1a</p>\n<ul>\n<li><code>sleep</code> \u65b9\u6cd5\u3002</li>\n<li>I/O \u64cd\u4f5c\u5982 <code>URI.open(\"<a href=\"https://brunosutic.com\" rel=\"nofollow\">https://brunosutic.com</a>\")</code>\u3002</li>\n<li>\u7cfb\u7edf\u547d\u4ee4\uff0c\u4f8b\u5982 <code>curl <a href=\"https://www.ruby-lang.org\" rel=\"nofollow\">https://www.ruby-lang.org</a></code>\u3002</li>\n<li>\u901a\u8fc7 <code>Thread#join</code> \u7b49\u5f85\u7ebf\u7a0b\u7ed3\u675f\u3002</li>\n</ul>\n<p>\u4f5c\u4e3a\u4e00\u4e2a\u53cd\u4f8b\uff0c\u4ee5\u4e0b\u4ee3\u7801\u7247\u6bb5\u9700\u8981\u4e00\u6bb5\u65f6\u95f4\u624d\u80fd\u5b8c\u6210\uff0c\u4f46\u4e0d\u5305\u542b\u963b\u585e\u64cd\u4f5c\uff1a</p>\n<pre><code class=\"language-ruby\">def fibonacci(n)\n  return n if [0, 1].include? n\n\n  fibonacci(n - 1) + fibonacci(n - 2)\nend\n\nfibonacci(100)\n</code></pre>\n<p>\u83b7\u53d6 <code>fibonacci(100)</code> \u7684\u7ed3\u679c\u9700\u8981\u7b49\u5f85\u5f88\u957f\u65f6\u95f4\uff0c\u4f46\u53ea\u6709\u7a0b\u5e8f\u5458\u5728\u7b49\u5f85\uff01\u6574\u4e2a\u65f6\u95f4 Ruby \u89e3\u91ca\u5668\u90fd\u5728\u5de5\u4f5c\uff0c\u540e\u53f0\u8fdb\u884c\u8ba1\u7b97\u3002\u4e00\u4e2a\u7b80\u5355\u7684\u6590\u6ce2\u90a3\u5951\u5b9e\u73b0\u5e76\u4e0d\u5305\u542b\u963b\u585e\u64cd\u4f5c\u3002</p>\n<p>\u53d1\u5c55\u5bf9\u963b\u585e\u64cd\u4f5c\u662f\u4ec0\u4e48\uff08\u548c\u4e0d\u662f\u4ec0\u4e48\uff09\u7684\u76f4\u89c9\u662f\u503c\u5f97\u7684\uff0c<strong>\u56e0\u4e3a\u5f02\u6b65\u7f16\u7a0b\u7684\u6574\u4e2a\u76ee\u6807\u5c31\u662f\u540c\u65f6\u7b49\u5f85\u591a\u4e2a\u963b\u585e\u64cd\u4f5c</strong>\u3002</p>\n<h2>Fiber Scheduler implementation \uff08\u7ea4\u7a0b\u8c03\u5ea6\u5668\u5b9e\u73b0\uff09</h2>\n<p>\u7ea4\u7a0b\u8c03\u5ea6\u5668\u5b9e\u73b0\u662f Fiber Scheduler \u529f\u80fd\u7684\u7b2c\u4e8c\u5927\u90e8\u5206\u3002</p>\n<p>\u5982\u679c\u4f60\u60f3\u5728 Ruby \u4e2d\u542f\u7528\u5f02\u6b65\u884c\u4e3a\uff0c\u4f60\u9700\u8981\u4e3a\u5f53\u524d\u7ebf\u7a0b\u8bbe\u7f6e\u4e00\u4e2a <code>Fiber Scheduler</code> \u5bf9\u8c61\u3002\u8fd9\u662f\u901a\u8fc7 <code>Fiber.set_scheduler(scheduler)</code> \u65b9\u6cd5\u5b8c\u6210\u7684\u3002\u5b9e\u73b0\u901a\u5e38\u662f\u4e00\u4e2a\u5b9a\u4e49\u4e86\u6240\u6709 <code>Fiber::SchedulerInterface</code> \u65b9\u6cd5\u7684\u7c7b\u3002</p>\n<p><strong>Ruby \u4e0d\u63d0\u4f9b\u9ed8\u8ba4\u7684 <code>Fiber Scheduler</code> \u7c7b\uff0c\u4e5f\u6ca1\u6709\u53ef\u4ee5\u7528\u4e8e\u6b64\u76ee\u7684\u7684\u5bf9\u8c61</strong>\u3002\u8fd9\u770b\u8d77\u6765\u4e0d\u5bfb\u5e38\uff0c\u4f46\u5b9e\u9645\u4e0a\u4e0d\u5c06 <code>Fiber Scheduler</code> \u5b9e\u73b0\u5305\u542b\u5728\u8bed\u8a00\u4e2d\u662f\u4e00\u4e2a\u597d\u7684\u957f\u671f\u51b3\u5b9a\u3002\u6700\u597d\u5c06\u8fd9\u79cd\u76f8\u5bf9\u5feb\u901f\u6f14\u53d8\u7684\u5173\u6ce8\u70b9\u7559\u5728 Ruby \u6838\u5fc3\u4e4b\u5916\u3002\n\u4ece\u5934\u5f00\u59cb\u7f16\u5199 <code>Fiber Scheduler</code> \u7c7b\u662f\u4e00\u9879\u590d\u6742\u7684\u4efb\u52a1\uff0c\u6240\u4ee5\u6700\u597d\u4f7f\u7528\u73b0\u6709\u7684\u89e3\u51b3\u65b9\u6848\u3002\u5b9e\u73b0\u7684\u5217\u8868\uff0c\u5b83\u4eec\u7684\u4e3b\u8981\u533a\u522b\u548c\u63a8\u8350\u53ef\u4ee5\u5728 <a href=\"https://github.com/bruno-/fiber_scheduler_list\" rel=\"nofollow\">Fiber Scheduler List</a> \u9879\u76ee\u4e2d\u627e\u5230\u3002</p>\n<h3>\u4e3e\u4e2a\u4f8b\u5b50</h3>\n<p>\u8ba9\u6211\u4eec\u6765\u770b\u770b\u4ec5\u4f7f\u7528 <code>Fiber Scheduler</code> \u53ef\u4ee5\u505a\u4ec0\u4e48\u3002\n\u6240\u6709\u793a\u4f8b\u90fd\u4f7f\u7528 Ruby 3.1 \u548c\u6765\u81ea <a href=\"https://github.com/bruno-/fiber_scheduler\" rel=\"nofollow\">fiber_scheduler</a> gem \u7684 <code>FiberScheduler</code> \u7c7b\uff0c\u8fd9\u4e2a gem \u7531\u6211\u7ef4\u62a4\u3002\u8fd9\u4e2a gem \u5bf9\u4e8e\u793a\u4f8b\u6765\u8bf4\u4e0d\u662f\u4e00\u4e2a\u786c\u6027\u4f9d\u8d56\u9879\uff0c\u56e0\u4e3a\u5982\u679c\u5c06\u4ee5\u4e0b\u4ee3\u7801\u7247\u6bb5\u4e2d\u7684 <code>FiberScheduler</code> \u66ff\u6362\u4e3a\u53e6\u4e00\u4e2a <code>Fiber Scheduler</code> \u7c7b\uff0c\u6bcf\u4e2a\u4ee3\u7801\u7247\u6bb5\u4ecd\u7136\u5e94\u8be5\u53ef\u4ee5\u5de5\u4f5c\u3002</p>\n<h4>\u57fa\u672c\u793a\u4f8b</h4>\n<p>\u8fd9\u91cc\u6709\u4e00\u4e2a\u7b80\u5355\u7684\u793a\u4f8b\uff1a</p>\n<pre><code class=\"language-ruby\">require \"fiber_scheduler\"\nrequire \"open-uri\"\n\nFiber.set_scheduler(FiberScheduler.new)\n\nFiber.schedule do\n  URI.open(\"https://httpbin.org/delay/2\")\nend\n\nFiber.schedule do\n  URI.open(\"https://httpbin.org/delay/2\")\nend\n</code></pre>\n<p>\u4e0a\u9762\u7684\u4ee3\u7801\u521b\u5efa\u4e86\u4e24\u4e2a\u7ea4\u7a0b\uff0c\u6bcf\u4e2a\u7ea4\u7a0b\u90fd\u8fdb\u884c\u4e00\u6b21 HTTP \u8bf7\u6c42\u3002\u8fd9\u4e9b\u8bf7\u6c42\u5e76\u884c\u8fd0\u884c\uff0c\u6574\u4e2a\u7a0b\u5e8f\u5728 2 \u79d2\u5185\u5b8c\u6210\u3002</p>\n<ul>\n<li>\n<p><code>Fiber.set_scheduler(FiberScheduler.new)</code>\n\u5728\u5f53\u524d\u7ebf\u7a0b\u4e2d\u8bbe\u7f6e\u4e00\u4e2a <code>Fiber Scheduler</code>\uff0c\u8fd9\u4f7f\u5f97 <code>Fiber.schedule</code> \u65b9\u6cd5\u53ef\u4ee5\u5de5\u4f5c\uff0c\u4e14 fiber \u53ef\u4ee5\u5f02\u6b65\u884c\u4e3a\u3002</p>\n</li>\n<li>\n<p><code>Fiber.schedule { ... }</code>\n\u8fd9\u662f\u4e00\u4e2a\u5185\u7f6e\u7684 Ruby \u65b9\u6cd5\uff0c\u7528\u4e8e\u542f\u52a8\u65b0\u7684\u5f02\u6b65 fiber \u3002</p>\n</li>\n</ul>\n<p><strong>\u8fd9\u4e2a\u793a\u4f8b\u4ec5\u4f7f\u7528\u4e86\u6807\u51c6\u7684 Ruby \u65b9\u6cd5 - <code>Fiber.set_scheduler</code> \u548c <code>Fiber.schedule</code> \u81ea Ruby 3.0 \u7248\u672c\u4ee5\u6765\u5c31\u4e00\u76f4\u53ef\u7528\u3002</strong></p>\n<h4>\u9ad8\u7ea7\u4f8b\u5b50</h4>\n<p>\u6211\u4eec\u6765\u770b\u770b\u8fd0\u884c\u591a\u79cd\u4e0d\u540c\u64cd\u4f5c\u662f\u4ec0\u4e48\u6837\u5b50\u7684\uff1a</p>\n<pre><code class=\"language-ruby\">require \"fiber_scheduler\"\nrequire \"httparty\"\nrequire \"open-uri\"\nrequire \"redis\"\nrequire \"sequel\"\n\nDB = Sequel.postgres\nSequel.extension(:fiber_concurrency)\n\nFiber.set_scheduler(FiberScheduler.new)\n\nFiber.schedule do\n  URI.open(\"https://httpbin.org/delay/2\")\nend\n\nFiber.schedule do\n  # Use any HTTP library\n  HTTParty.get(\"https://httpbin.org/delay/2\")\nend\n\nFiber.schedule do\n  # Works with any TCP protocol library\n  Redis.new.blpop(\"abc123\", 2)\nend\n\nFiber.schedule do\n  # Make database queries\n  DB.run(\"SELECT pg_sleep(2)\")\nend\n\nFiber.schedule do\n  sleep 2\nend\n\nFiber.schedule do\n  # Run system commands\n  `sleep 2`\nend\n</code></pre>\n<p>\u5982\u679c\u6211\u4eec\u987a\u5e8f\u8fd0\u884c\u8fd9\u4e2a\u7a0b\u5e8f\uff0c\u5b83\u5927\u7ea6\u9700\u8981 12 \u79d2\u624d\u80fd\u5b8c\u6210\u3002\u4f46\u662f\u7531\u4e8e\u8fd9\u4e9b\u64cd\u4f5c\u662f\u5e76\u884c\u8fd0\u884c\u7684\uff0c\u6240\u4ee5\u603b\u7684\u8fd0\u884c\u65f6\u95f4\u4ec5\u4ec5\u8d85\u8fc7 2 \u79d2\u3002\n\u4f60\u5e76\u4e0d\u4ec5\u9650\u4e8e\u53d1\u8d77 HTTP \u8bf7\u6c42\u3002<strong>\u4efb\u4f55\u5185\u7f6e\u5728 Ruby \u4e2d\u6216\u7531\u5916\u90e8 gem \u5b9e\u73b0\u7684\u963b\u585e\u64cd\u4f5c\u90fd\u53ef\u4ee5\u5de5\u4f5c\uff01</strong></p>\n<h4>\u6269\u5c55\u793a\u4f8b</h4>\n<p>\u8fd9\u662f\u4e00\u4e2a\u7b80\u5355\u7684\uff0c\u663e\u7136\u662f\u4eba\u4e3a\u523b\u610f\u7684\u793a\u4f8b\uff0c\u540c\u65f6\u8fd0\u884c\u4e00\u4e07\u4e2a\u64cd\u4f5c\u3002</p>\n<pre><code class=\"language-ruby\">require \"fiber_scheduler\"\n\nFiber.set_scheduler(FiberScheduler.new)\n\n10_000.times do\n  Fiber.schedule do\n    sleep 2\n  end\nend\n</code></pre>\n<p>\u4e0a\u8ff0\u4ee3\u7801\u7684\u5b8c\u6210\u65f6\u95f4\u7565\u8d85\u8fc7 2 \u79d2\u3002</p>\n<p>\u7531\u4e8e\u5176\u4f4e\u5f00\u9500\uff0c<code>sleep</code> \u65b9\u6cd5\u88ab\u9009\u62e9\u7528\u4e8e\u6269\u5c55\u793a\u4f8b\u3002\u5982\u679c\u6211\u4eec\u4f7f\u7528\u7f51\u7edc\u8bf7\u6c42\uff0c\u7531\u4e8e\u9700\u8981\u5efa\u7acb\u6570\u5343\u4e2a\u8fde\u63a5\u5e76\u8fdb\u884c SSL \u63e1\u624b\u7b49\uff0c\u6267\u884c\u65f6\u95f4\u5c06\u4f1a\u66f4\u957f\u3002</p>\n<p>\u5f02\u6b65\u7f16\u7a0b\u7684\u4e3b\u8981\u4f18\u52bf\u4e4b\u4e00\u662f\u80fd\u591f\u540c\u65f6\u7b49\u5f85\u8bb8\u591a\u963b\u585e\u64cd\u4f5c\u3002<strong>\u963b\u585e\u64cd\u4f5c\u6570\u91cf\u7684\u589e\u52a0\u5c06\u589e\u52a0\u8fd9\u79cd\u4f18\u52bf\u3002\u5e78\u8fd0\u7684\u662f\uff0c\u8fd0\u884c\u5927\u91cf\u534f\u7a0b(fibers)\u975e\u5e38\u7b80\u5355\u3002</strong></p>\n<h2>\u7ed3\u8bba</h2>\n<p><strong>Ruby \u53ea\u9700\u8981\u4e00\u4e2a\u7ea4\u7a0b\u8c03\u5ea6\u5668\uff08 Fiber Scheduler \uff09\u548c\u4e00\u4e9b\u5185\u7f6e\u65b9\u6cd5\u5c31\u53ef\u4ee5\u5f02\u6b65\u5de5\u4f5c - \u4e0d\u9700\u8981\u4efb\u4f55\u6846\u67b6\uff01</strong></p>\n<p>\u4f7f\u5176\u5de5\u4f5c\u5f88\u5bb9\u6613\u3002\u9009\u62e9\u4e00\u4e2a\u7ea4\u7a0b\u8c03\u5ea6\u5668\uff08 Fiber Scheduler \uff09\u5b9e\u73b0\uff0c\u7136\u540e\u4f7f\u7528\u4ee5\u4e0b\u8fd9\u4e9b\u65b9\u6cd5\uff1a</p>\n<ul>\n<li><code>Fiber.set_scheduler(scheduler)</code>\u4e3a\u5f53\u524d\u7ebf\u7a0b\u8bbe\u7f6e\u4e00\u4e2a\u7ea4\u7a0b\u8c03\u5ea6\u5668\uff08 Fiber Scheduler \uff09\uff0c\u4f7f\u963b\u585e\u64cd\u4f5c\u80fd\u591f\u5f02\u6b65\u6267\u884c\u3002</li>\n<li><code>Fiber.schedule { ... }</code> \u542f\u52a8\u4e00\u4e2a\u65b0\u7684\u7ea4\u7a0b\uff0c\u8be5\u7ea4\u7a0b\u4e0e\u5176\u4ed6\u7ea4\u7a0b\u5e76\u53d1\u8fd0\u884c\u3002</li>\n</ul>\n<p>\u4e00\u65e6\u4f60\u5f00\u59cb\u8fd0\u884c\uff0c<strong>\u4f60\u53ef\u4ee5\u901a\u8fc7\u5c06\u5b83\u5305\u88c5\u5728\u4e00\u4e2a <code>Fiber.schedule</code> \u5757\u4e2d\u6765\u4f7f\u4efb\u4f55\u4ee3\u7801\u5f02\u6b65\u5316</strong>\u3002</p>\n<pre><code class=\"language-ruby\">Fiber.schedule do\n  SynchronousCode.run\nend\n</code></pre>\n<p>\u6574\u4e2a\u5e93\u53ef\u4ee5\u8f7b\u677e\u5730\u4f7f\u7528\u8fd9\u79cd\u65b9\u6cd5\u8f6c\u6362\u4e3a\u5f02\u6b65\uff0c\u800c\u4e14\u5f80\u5f80\u4e0d\u9700\u8981\u6bd4\u8fd9\u91cc\u5c55\u793a\u7684\u66f4\u591a\u52aa\u529b\u3002</p>\n<p><strong>\u5f02\u6b65\u7f16\u7a0b\u7684\u91cd\u5927\u597d\u5904\u662f\u5e76\u884c\u5316\u963b\u585e/\u7b49\u5f85\u64cd\u4f5c\u4ee5\u51cf\u5c11\u7a0b\u5e8f\u8fd0\u884c\u65f6\u95f4\u3002</strong>\u8fd9\u901a\u5e38\u610f\u5473\u7740\u5728\u5355\u4e2a CPU \u4e0a\u8fd0\u884c\u66f4\u591a\u7684\u64cd\u4f5c\uff0c\u6216\u8005\u66f4\u597d\u5730\uff0c\u5728\u4f60\u7684 Web \u670d\u52a1\u5668\u4e0a\u5904\u7406\u66f4\u591a\u7684\u8bf7\u6c42\u3002</p>\n<p>\u795d\u4f60\u4f7f\u7528\u7ea4\u7a0b\u8c03\u5ea6\u5668\uff08 Fiber Scheduler \uff09\u6109\u5feb\uff01</p>\n<p>Happy hacking with Fiber Scheduler!</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/905233", 
      "title": "Ruby 3.2.0 \u53d1\u5e03", 
      "id": "https://www.v2ex.com/t/905233", 
      "date_published": "2022-12-28T12:56:22+00:00", 
      "content_html": "<p><a href=\"https://www.ruby-lang.org/en/news/2022/12/25/ruby-3-2-0-released/\" rel=\"nofollow\">https://www.ruby-lang.org/en/news/2022/12/25/ruby-3-2-0-released/</a></p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Livid", 
        "name": "Livid", 
        "avatar": "https://cdn.v2ex.com/avatar/c4ca/4238/1_large.png?m=1775624785"
      }, 
      "url": "https://www.v2ex.com/t/902116", 
      "title": "Ruby on Mac", 
      "id": "https://www.v2ex.com/t/902116", 
      "date_published": "2022-12-13T02:28:46+00:00", 
      "content_html": "<a target=\"_blank\" href=\"https://www.rubyonmac.dev/\" rel=\"nofollow noopener\">https://www.rubyonmac.dev/</a><br /><br />\u89e3\u51b3 Ruby \u5f00\u53d1\u73af\u5883\u5728 macOS \u4e0a\u5404\u79cd\u5b89\u88c5\u95ee\u9898\u7684\u65b9\u6848\u3002"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/872259", 
      "title": "\u7528 100 \u884c Ruby \u4ee3\u7801\u6a21\u62df JavaScript \u7684 Eventloop", 
      "id": "https://www.v2ex.com/t/872259", 
      "date_published": "2022-08-11T11:04:15+00:00", 
      "content_html": "<h2>\u524d\u8a00</h2>\n<p>\u5927\u5bb6\u597d\uff0c\u6211\u662f Mark24</p>\n<ul>\n<li>\u4ee3\u7801\u4ed3\u5e93: <a href=\"https://github.com/Mark24Code/rb_simulate_eventloop\" rel=\"nofollow\">Mark24Code/rb_simulate_eventloop</a></li>\n<li>[\u672c\u6587\u535a\u5ba2\u5730\u5740] ( <a href=\"https://mark24code.github.io/ruby/2022/08/11/%E7%94%A8100%E8%A1%8CRuby%E4%BB%A3%E7%A0%81%E6%A8%A1%E6%8B%9FJavaScript%E7%9A%84Eventloop.html?source=v2ex\" rel=\"nofollow\">https://mark24code.github.io/ruby/2022/08/11/%E7%94%A8100%E8%A1%8CRuby%E4%BB%A3%E7%A0%81%E6%A8%A1%E6%8B%9FJavaScript%E7%9A%84Eventloop.html?source=v2ex</a>)</li>\n<li><a href=\"https://ruby-china.org/topics/42590\" rel=\"nofollow\">RubyChina \u540c\u8bdd\u9898\u8ba8\u8bba</a></li>\n</ul>\n<h2>\u80cc\u666f</h2>\n<p>\u6211\u4eec\u90fd\u77e5\u9053 JavaScript \u662f\u5355\u7ebf\u7a0b\u7684\u3002</p>\n<p>\u4eca\u5929\u770b\u5230\u4e00\u4e2a\u6709\u8da3\u7684<a href=\"https://www.v2ex.com/t/871848#reply95\" rel=\"nofollow\">\u5e16\u5b50 www.v2ex.com/t/871848</a>\uff0c\u4e3b\u8981\u662f\u4e89\u8bba JavaScript \u7684\u4f18\u7f3a\u70b9\u3002\u6211\u770b\u5230\u8fd9\u4e2a\u8bc4\u8bba\u89c9\u5f97\u5f88\u6709\u610f\u601d\uff1a</p>\n<pre><code>@qrobot:\n\n....\u7701\u7565....\n\n\n\u591a\u7ebf\u7a0b\u4e0b\u4f1a\u6d88\u8017\u4ee5\u4e0b\u8d44\u6e90\n\n1. \u5207\u6362\u9875\u8868\u5168\u5c40\u76ee\u5f55\n2. \u5207\u6362\u5185\u6838\u6001\u5806\u6808\n3. \u5207\u6362\u786c\u4ef6\u4e0a\u4e0b\u6587\uff08\u8fdb\u7a0b\u6062\u590d\u524d\uff0c\u5fc5\u987b\u88c5\u5165\u5bc4\u5b58\u5668\u7684\u6570\u636e\u7edf\u79f0\u4e3a\u786c\u4ef6\u4e0a\u4e0b\u6587\uff09\nip(instruction pointer)\uff1a\u6307\u5411\u5f53\u524d\u6267\u884c\u6307\u4ee4\u7684\u4e0b\u4e00\u6761\u6307\u4ee4\nbp(base pointer): \u7528\u4e8e\u5b58\u653e\u6267\u884c\u4e2d\u7684\u51fd\u6570\u5bf9\u5e94\u7684\u6808\u5e27\u7684\u6808\u5e95\u5730\u5740\nsp(stack poinger): \u7528\u4e8e\u5b58\u653e\u6267\u884c\u4e2d\u7684\u51fd\u6570\u5bf9\u5e94\u7684\u6808\u5e27\u7684\u6808\u9876\u5730\u5740\ncr3:\u9875\u76ee\u5f55\u57fa\u5740\u5bc4\u5b58\u5668\uff0c\u4fdd\u5b58\u9875\u76ee\u5f55\u8868\u7684\u7269\u7406\u5730\u5740\n......\n\n4. \u5237\u65b0 TLB\n5. \u7cfb\u7edf\u8c03\u5ea6\u5668\u7684\u4ee3\u7801\u6267\u884c\n\n....\u7701\u7565.....\n\n</code></pre>\n<p>\u8fd9\u4f4d\u540c\u5b66\u5217\u4e3e\u4e86\u591a\u7ebf\u7a0b\u5207\u6362\u7684\u65f6\u5019\u53d1\u751f\u4e86\u4ec0\u4e48\u3002\n\u8fd9\u6837\u7ed9\u4e86\u4e00\u79cd\u5f88\u76f4\u89c2\u7684\u611f\u53d7\uff0c\u5c31\u662f\u591a\u7ebf\u7a0b\u5207\u6362\u7684\u65f6\u5019\u53d1\u751f\u4e86\u5f88\u591a\u4e8b\u60c5\uff0c\u5b9e\u9645\u4e0a\u4f1a\u6bd4\u5355\u7ebf\u7a0b\uff08\u53ea\u9700\u8981\u5207\u6362\u51fd\u6570\u4e0a\u4e0b\u6587\uff09\u8981\u6d88\u8017\u70b9\u66f4\u591a\u7684\u8d44\u6e90\u3002</p>\n<p>\u5b9e\u9645\u4e0a\u51e1\u662f\u4ea4\u4e92\u7684\u8f6f\u4ef6\uff0c\u6700\u7ec8\u90fd\u662f \u5355\u7ebf\u7a0b\u6a21\u578b + \u4e8b\u4ef6\u9a71\u52a8\u8f85\u52a9\u3002</p>\n<p>\u4ece\u719f\u6089\u7684\u6d4f\u89c8\u5668\u3001\u6e38\u620f\u3001\u5e94\u7528\u7a0b\u5e8f\u2026\u2026\u90fd\u662f\u5982\u6b64\u3002</p>\n<p>\u4e5f\u6709\u591a\u7ebf\u7a0b\u5b9e\u73b0\u7684\u3002\u8fd9\u91cc<a href=\"https://news.ycombinator.com/item?id=10490627\" rel=\"nofollow\">Multithreaded toolkits: A failed dream? (2004) </a> \u6709\u5f88\u591a\u8ba8\u8bba\u3002</p>\n<p>\u5b9e\u9645\u4e0a\u5355\u7ebf\u7a0b\u6a21\u578b\u662f\u6700\u540e\u7684\u80dc\u51fa\u8005\u3002</p>\n<p>JavaScript \u5185\u90e8\u5355\u7ebf\u7a0b\u5904\u7406\u4efb\u52a1\uff0c\u4e3b\u8981\u662f\u6709\u4e00\u4e2a EventLoop \u5355\u7ebf\u7a0b\u7684\u5faa\u73af\u5b9e\u73b0\u3002</p>\n<p>\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7 JavaScript \u7684\u8868\u73b0\uff0c\u53cd\u63a8\u5b9e\u73b0\u4e00\u4e0b EventLoop \u3002</p>\n<h1>EventLoop \u5b9e\u73b0</h1>\n<h3>JavaScript \u7684\u884c\u4e3a</h3>\n<p>\u6211\u4eec\u77e5\u9053 <code>setTimeout</code> \u5728 JavaScript \u4e2d\u7528\u6765\u63a8\u8fdf\u4efb\u52a1\u3002\u5b9e\u9645\u4e0a\u81ea\u4ece Promise \u51fa\u73b0\u4e4b\u540e\uff0c\u6e10\u6e10\u6709\u4e24\u4e2a\u6982\u5ff5\u51fa\u73b0\u5728\u5927\u5bb6\u7684\u89c6\u91ce\u91cc\u3002</p>\n<ul>\n<li>Macrotask(\u5b8f\u4efb\u52a1\uff09</li>\n<li>Microtask \uff08\u5fae\u4efb\u52a1\uff09</li>\n</ul>\n<p>setTimeout \u5c5e\u4e8e\u5b8f\u4efb\u52a1\uff0c\u800c promise \u7684 then \u56de\u8c03\u5c5e\u4e8e\u5fae\u4efb\u52a1\u3002</p>\n<p>\u8fd8\u6709\u4e00\u4e2a\u5c31\u662f JavaScript \u5728\u7b2c\u4e00\u6b21\u540c\u6b65\u6267\u884c\u4ee3\u7801\u7684\u65f6\u5019\uff0c\u662f\u5b8f\u4efb\u52a1\u3002</p>\n<p>EventLoop \u7684\u8868\u73b0\u662f\uff0c\u9664\u4e86\u7b2c\u4e00\u6b21\u6267\u884c\u7ed3\u675f\u4e4b\u540e\uff0c\u5982\u679c\u6709\u66f4\u9ad8\u4f18\u5148\u7ea7\u7684 \u5fae\u4efb\u52a1\u603b\u662f\u5148\u6267\u884c\u5fae\u4efb\u52a1\uff0c\u7136\u540e\u518d\u6267\u884c\u5b8f\u4efb\u52a1\u3002</p>\n<p>setTimeout \u662f\u4e00\u4e2a\u5b9a\u65f6\u5668\uff0c\u5f88\u7279\u522b\u7684\u662f\u4ed6\u5728\u4f1a\u5728\u8ba1\u65f6\u5668\u7ebf\u7a0b\u5de5\u4f5c\uff0c\u8fd0\u884c\u65f6\u95f4\u4e4b\u540e\uff0c\u56de\u8c03\u51fd\u6570\u4f1a\u88ab\u63d2\u5165\u5230 \u5b8f\u4efb\u52a1\u4e2d\u6267\u884c\u3002\u8ba1\u65f6\u5668\u7ebf\u7a0b\u5176\u5b9e\u4e0d\u662f JavaScript \u865a\u62df\u7684\u4e00\u90e8\u5206\uff0c\u4ed6\u662f\u6d4f\u89c8\u5668\u7684\u90e8\u5206\u3002</p>\n<h3>Ruby \u6a21\u62df</h3>\n<p>JavaScript \u662f\u5355\u7ebf\u7a0b\u7684\u3002Ruby \u662f\u652f\u6301\u591a\u7ebf\u7a0b\u7684\u3002\u6211\u4eec\u53ef\u4ee5\u7528 Ruby \u6a21\u62df\u4e00\u4e2a \u5355\u7ebf\u7a0b\u7684\u6838\u5fc3\uff0c\u548c\u5355\u72ec\u7684\u8ba1\u65f6\u5668\u7ebf\u7a0b\uff0c\u8fd9\u90fd\u662f\u5f88\u8f7b\u677e\u7684\u4e8b\u60c5\u3002</p>\n<p>\u5176\u5b9e\u6211\u4eec\u542c\u5230\u4e86\u8fd9\u4e2a\u884c\u4e3a \u2014\u2014 \u82b1 1 \u5206\u949f\u5927\u6982\u80fd\u60f3\u5230\uff0cEventLoop \u7684\u5de5\u4f5c\u6a21\u578b</p>\n<ul>\n<li>\u9996\u5148\u4ed6\u662f\u4e00\u4e2a\u4e3b\u5faa\u73af\uff0c\u8fd9\u6837\u624d\u80fd\u6240\u8c13\u7684\u5355\u7ebf\u7a0b</li>\n<li>\u5176\u6b21\uff0c\u65e2\u7136\u6709\u4e24\u79cd\u4efb\u52a1\uff0c\u5e94\u8be5\u662f\u4e24\u79cd\u961f\u5217</li>\n<li>\u518d\u8005\uff0c\u5982\u679c\u7b2c\u4e00\u6b21\u540c\u6b65\u4ee3\u7801\u662f\u5b8f\u4efb\u52a1\uff0c\u591a\u534a\u53ef\u4ee5\u4ee3\u7801\u4efb\u52a1\u4e5f\u4e22\u5230\u961f\u5217\u91cc</li>\n</ul>\n<p>\u6211\u4eec\u53ef\u4ee5\u7528\u6570\u7ec4\u5f53\u505a \u961f\u5217\u3002\u4f46\u662f \u7531\u4e8e\u5b58\u5728\u65f6\u95f4\u7ebf\u7a0b\uff0c\u8fd8\u5f97\u7528 Thread#Queue \u6709\u4fdd\u969c\u4e00\u70b9\u3002</p>\n<p>\u5927\u6982\u7684\u6a21\u578b\u53ef\u4ee5\u753b\u51fa\u6765\uff0c\u60f3\u8fd9\u4e2a\u6837: </p>\n<h3>Eventloop Model</h3>\n<pre><code>\uff08 start)\n   |\n  init (e.g create TimerThread )\n   |\n  sync task (e.g read &amp; run code) \n   |                                        \n   |\n ------------------&gt;\n|                  |                                    -------------\n|               macro_task  ---  add timer task --&gt;    | TimerThread |\n|   (Eventloop)    |       &lt;--  insertjob result ---    -------------\n|                  |\n|               micro_task\n|                  |\n|                  |\n &lt;-----------------   \n   |\n   |\n  (end)\n\n</code></pre>\n<ul>\n<li>\u5b8c\u6574\u7684\u4ee3\u7801\u4ed3\u5e93 <a href=\"https://github.com/Mark24Code/rb_simulate_eventloop\" rel=\"nofollow\">Mark24Code/rb_simulate_eventloop</a></li>\n</ul>\n<p>\u7136\u540e\u6211\u4eec\u5927\u6982\u7528 100 \u884c\u4e0d\u5230\u5c31\u53ef\u4ee5\u5b9e\u73b0\u5982\u4e0b:</p>\n<h4>\u9700\u8981\u8bf4\u660e\u7684\u662f:</h4>\n<ol>\n<li>\n<p>settimeout \u4e0d\u8981\u7528\u6bcf\u4e00\u4e2a\u65b0\u7684\u7ebf\u7a0b\u6765\u6a21\u62df\uff0c\u56e0\u4e3a\u4e00\u65e6\u591a\u7ebf\u7a0b\uff0c\u6d89\u53ca\u5230\u62a2\u5360\u5f0f\u56de\u8c03\uff0c\u5176\u5b9e\u8fd4\u56de\u7684\u65f6\u95f4\u4e0d\u786e\u5b9a\u3002\u4f60\u7684\u7ed3\u679c\u662f\u4e0d\u7a33\u5b9a\u7684\u3002\n\u6211\u4eec\u9700\u8981\u5355\u72ec\u5b9e\u73b0\u4e00\u4e2a\u8ba1\u65f6\u5668\u7ebf\u7a0b\u3002</p>\n</li>\n<li>\n<p>\u6211\u4eec\u901a\u8fc7\u884c\u4e3a\u5c01\u88c5\uff0c\u628a\u4e24\u8fb9\u51fd\u6570\u5199\u6cd5\u5bf9\u7167\uff0c\u8fd9\u6837\u53ef\u4ee5\u590d\u5236</p>\n</li>\n</ol>\n<p>\u8fd0\u884c\u770b\u7ed3\u679c</p>\n<p><img alt=\"result_example\" class=\"embedded_image\" loading=\"lazy\" referrerpolicy=\"no-referrer\" rel=\"noreferrer\" src=\"https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/60bb8e9548554970ab35219e17dbeae0%7Etplv-k3u1fbpfcp-zoom-1.image\"/></p>\n<h2>\u5177\u4f53\u5b9e\u73b0</h2>\n<pre><code class=\"language-ruby\"># https://github.com/Mark24Code/rb_simulate_eventloop\nrequire 'thread'\n\nclass EventLoop\n  \n  attr_accessor :macro_queue, :micro_queue\n  def initialize\n    @running = true\n    \n    @macro_queue = Queue.new\n    @micro_queue = Queue.new\n\n    @time_thr_task_queue = Queue.new\n\n    @timer = Timer.new(@time_thr_task_queue, @macro_queue)\n\n    # \u8ba1\u65f6\u7ebf\u7a0b\uff0c\u662f\u4e00\u4e2a\u540c\u6b65\u961f\u5217\n    # \u4f1a\u628a\u5b9a\u65f6\u4efb\u52a1\u7ed3\u679c\u585e\u56de\u5b8f\u961f\u5217\n    @timer_thx = Thread.new do\n      @timer.run\n    end\n  end\n\n\n  def before_loop_sync_tasks\n    # do sth setting\n    @first_task.call\n  end\n\n  def task(&amp;block)\n    # \u8fd9\u91cc\u653e\u7f6e\u7b2c\u4e00\u6b21\u540c\u6b65\u4efb\u52a1\n    # \n    # \u5916\u90e8\u4e66\u5199\u7684\u4ee3\u7801\uff0c\u6a21\u62df\u8bfb\u53d6 js\n    # \u63d0\u4f9b\u5185\u90e8\u7684 api\n    @first_task = -&gt; () { instance_eval(&amp;block) }\n  end\n\n  def after_loop\n    puts \"[after_loop] eventloop is quit :D\"\n  end\n\n  def macro_queue_works\n    while !@macro_queue.empty?\n      job = @macro_queue.shift\n      job.call\n    end\n  end\n\n  def micro_queue_works\n    while !@micro_queue.empty?\n      job = @micro_queue.shift\n      job.call\n    end\n  end\n\n  def start\n    begin\n      before_loop_sync_tasks\n\n      while @running\n\n        macro_queue_works\n\n        micro_queue_works\n\n        # avoid CPU 100%\n        sleep 0.1\n      end\n    ensure\n      after_loop\n    end\n  end\n\n  # dsl public api\n  # inner api\n  def macro_task(&amp;block)\n    @macro_queue.push(block)\n  end\n\n  def micro_task(&amp;block)\n    @micro_queue.push(block)\n  end\n\n  def settimeout(time, &amp;block)\n    # \u6a21\u62df\u5b9a\u65f6\u5668\u7ebf\u7a0b\n    if time == 0\n      time = 0.1\n    end\n\n    # \u65b9\u6848 1: \u7528\u72ec\u7acb\u5206\u6563\u7684\u7ebf\u7a0b\u6a21\u62df\u5b58\u5728\u95ee\u9898\n    # \u62a2\u5360\u7684\u8fd4\u56de\u987a\u5e8f\u4e0d\u662f\u56fa\u5b9a\u7684\n    # t = Thread.new do\n    #   sleep time\n    #   @micro_queue.push(block)\n    # end\n    ## !!! \u8fd9\u91cc\u4e00\u5b9a\u4e0d\u80fd\u963b\u585e\uff0c\u4e00\u65e6\u963b\u585e\u5c31\u4e0d\u662f\u5355\u7ebf\u7a0b\u6a21\u578b\n    ## \u6709\u5916\u5faa\u73af\u63a7\u5236\u4e0d\u4f1a\u7ed3\u675f\n    # t.join\n\n    # \u65b9\u6848 2: \u65f6\u95f4\u7ebf\u7a0b\u4e5f\u9700\u8981\u5355\u72ec\u6a21\u62df\n    # \u5efa\u7acb\u4e00\u4e2a\u65f6\u95f4\u4efb\u52a1\n    @time_thr_task_queue.push({\n      sleep_time: Time.now.to_i + time,\n      job: -&gt; () { @micro_queue.push(block) }\n    })\n\n  end\nend\n\nclass Timer\n  def initialize(task_queue, macro_queue)\n    @task_queue = task_queue\n    @macro_queue = macro_queue\n  end\n  def run\n    while (task = @task_queue.shift)\n      sleep_time = task[:sleep_time]\n      if sleep_time &gt;= Time.now.to_i\n        @macro_queue.push(task[:job])\n      else\n        @task_queue.push(task)\n      end\n    end\n  end\nend\n</code></pre>\n<h1>\u603b\u7ed3</h1>\n<p>\u9009\u62e9\u5355\u7ebf\u7a0b\u7684\u539f\u56e0\u662f\u56e0\u4e3a</p>\n<ul>\n<li>\u7ed3\u679c\u8fd0\u884c\u7684\u66f4\u5feb</li>\n<li>\u65e0\u4e0a\u4e0b\u6587\u8d1f\u62c5</li>\n<li>\u4efb\u52a1\u961f\u5217\u6e05\u6670\u800c\u53c8\u7b80\u5355</li>\n<li>\u975e IO \u5bc6\u7ea7\u4efb\u52a1\uff0c\u53ef\u4ee5\u8dd1\u6ee1 CPU</li>\n</ul>\n<p>Nginx \u3001Redis \u5185\u90e8\u4e5f\u5b9e\u73b0\u4e86\u5355\u7ebf\u7a0b\u6a21\u578b\uff0c\u6765\u5e94\u5bf9\u5927\u91cf\u7684\u8bf7\u6c42\uff0c\u63d0\u9ad8\u5e76\u53d1\u3002</p>\n<p>\u73b0\u5728\u6211\u4eec\u5927\u6982\u77e5\u9053\u4e86\uff0c\u6d4f\u89c8\u5668\u3001\u5e94\u7528\u3001app \u3001\u56fe\u5f62\u754c\u9762\u3001\u6e38\u620f\u2026\u2026</p>\n<p>\u4ed6\u4eec\u7684\u80cc\u540e\u5927\u6982\u662f\u4ec0\u4e48\u6837\u5b50\u3002 \u7834\u9664\u795e\u79d8\u611f +1 :D</p>\n<ul>\n<li>[\u672c\u6587\u535a\u5ba2\u5730\u5740] ( <a href=\"https://mark24code.github.io/ruby/2022/08/11/%E7%94%A8100%E8%A1%8CRuby%E4%BB%A3%E7%A0%81%E6%A8%A1%E6%8B%9FJavaScript%E7%9A%84Eventloop.html?source=v2ex\" rel=\"nofollow\">https://mark24code.github.io/ruby/2022/08/11/%E7%94%A8100%E8%A1%8CRuby%E4%BB%A3%E7%A0%81%E6%A8%A1%E6%8B%9FJavaScript%E7%9A%84Eventloop.html?source=v2ex</a>)</li>\n</ul>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/868773", 
      "title": "\u7ba1\u7aa5\u8821\u6d4b\u4ece\u601d\u8003\u6e38\u620f\u5230\u5b9e\u73b0 2048", 
      "id": "https://www.v2ex.com/t/868773", 
      "date_published": "2022-07-26T05:42:29+00:00", 
      "content_html": "<p><a href=\"https://mark24code.github.io/ruby/2022/07/26/%E8%BF%99%E5%BE%97%E4%BB%8E2048%E8%AF%B4%E8%B5%B7.html\" rel=\"nofollow\">\u6211\u7684\u535a\u5ba2</a></p>\n<h1>\u524d\u8a00</h1>\n<p>\u672c\u6587\u6bd4\u8f83\u5570\u55e6\uff0c\u66f4\u503e\u5411\u4e8e\u662f\u81ea\u8a00\u81ea\u8bed\u3002\u4e0d\u8fc7\u6211\u5199\u5b8c\u56de\u987e\uff0c\u8fd9\u66f4\u50cf\u662f\u8fd9\u6bb5\u65f6\u95f4\uff0c\u81ea\u7531\u601d\u8003\u7684\u603b\u7ed3 :P</p>\n<p>\u4e0d\u8fc7\u6211\u4e0d\u662f\u6e38\u620f\u9886\u57df\u7684\u4eba\uff0c\u8fd9\u90e8\u5206\u90fd\u662f\u4e1a\u4f59\u6478\u9c7c\u601d\u8003\u7684\u8bb0\u5f55\uff0c\u5982\u679c\u6709\u52d8\u8bef\uff0c\u8bf7\u4e0e\u6211\u8054\u7cfb\uff0c\u975e\u5e38\u4e50\u610f\u4ea4\u6d41\u3002</p>\n<p>\u6587\u7ae0\u53ef\u80fd\u9700\u8981 30 \u5206\u949f\u3002</p>\n<p>\u4e3b\u8981\u6d89\u53ca\u7684\u4e3b\u9898\uff1a</p>\n<ul>\n<li>\u6e38\u620f\u4e4b\u96be</li>\n<li>\u6e38\u620f\u57fa\u672c\u6784\u6210</li>\n<li>\u6e38\u620f\u5f15\u64ce</li>\n<li>\u6e38\u620f\u4e0e\u4ea4\u4e92\u7a0b\u5e8f</li>\n<li>\u6846\u67b6\u548c\u5e93\u601d\u8003</li>\n<li>\u8bed\u8a00\u662f\u5426\u662f\u6e38\u620f\u7684\u74f6\u9888</li>\n<li>\u53cc\u7f13\u51b2\u6a21\u5f0f</li>\n<li>\u7ebf\u7a0b\u548c\u534f\u7a0b\u7684\u8ba8\u8bba</li>\n<li>\u7ebf\u7a0b\u961f\u5217&amp;\u4e2d\u65ad</li>\n</ul>\n<p>\u4f7f\u7528 Ruby \u5b9e\u73b0 demo \u3002</p>\n<h1>rb2048</h1>\n<ul>\n<li>\u9879\u76ee\u5730\u5740: <a href=\"https://github.com/Mark24Code/rb2048\" rel=\"nofollow\">rb2048</a></li>\n<li>gem \u5730\u5740: <a href=\"https://gems.ruby-china.com/gems/rb2048\" rel=\"nofollow\">rb2048</a></li>\n</ul>\n<p>\u9879\u76ee\u5b89\u88c5: <code>gem install rb2048</code></p>\n<h4>\u8fdb\u5165\u6e38\u620f</h4>\n<p>\u5e2e\u52a9\u4fe1\u606f\uff1a <code>rb2048 --help</code></p>\n<pre><code class=\"language-ruby\">Usage: rb2048 [options]\n        --version                    verison\n        --size SIZE                  Size of board: 4-10\n        --level LEVEL                Hard Level 2-5\n</code></pre>\n<p>\u5f00\u59cb\u6e38\u620f <code>rb2048</code></p>\n<pre><code>           -- Ruby 2048 --\n\n -------------------------------------\n |    16  |    16  |     2  |    16  |\n -------------------------------------\n |     0  |     0  |     0  |     0  |\n -------------------------------------\n |     0  |     0  |     0  |     2  |\n -------------------------------------\n |     0  |     0  |     0  |     0  |\n -------------------------------------\n\n Score: 16              You:UP\n\n\n\n Control: W(\u2191) A(\u2190) S(\u2193) D(\u2192) Q(quit) R(Restart)\n</code></pre>\n<p>\u5347\u7ea7\u96be\u5ea6 <code>rb2048 --size=10 --level=5</code></p>\n<pre><code>                            -- Ruby 2048 --\n\n -----------------------------------------------------------------------\n |   8  |  16  |   0  |   0  |   0  |   0  |   0  |   2  |   0  |   0  |\n -----------------------------------------------------------------------\n |   0  |  16  |   0  |  16  |   0  |   8  |   0  |   0  |   0  |   0  |\n -----------------------------------------------------------------------\n |   0  |   0  |   0  |   2  |   0  |   0  |   0  |   0  |  16  |   8  |\n -----------------------------------------------------------------------\n |   0  |  16  |   0  |   8  |   0  |   0  |   0  |   0  |   0  |   2  |\n -----------------------------------------------------------------------\n |   0  |   0  |   0  |   0  |   0  |   0  |   0  |   0  |   0  |   0  |\n -----------------------------------------------------------------------\n |   0  |   8  |   8  |   0  |   0  |   0  |   0  |   0  |   0  |   0  |\n -----------------------------------------------------------------------\n |   8  |   0  |   0  |   0  |   0  |   4  |   0  |   0  |   0  |   0  |\n -----------------------------------------------------------------------\n |   0  |   0  |   0  |   0  |   0  |   0  |   0  |   0  |   0  |   0  |\n -----------------------------------------------------------------------\n |   0  |   0  |   0  |   4  |   0  |   0  |   0  |   0  |   0  |   0  |\n -----------------------------------------------------------------------\n |   0  |   4  |   0  |   0  |   4  |   8  |   0  |   0  |   0  |  16  |\n -----------------------------------------------------------------------\n\n Score: 0\n\n\n\n Control: W(\u2191) A(\u2190) S(\u2193) D(\u2192) Q(quit) R(Restart)\n</code></pre>\n<h1>\u80cc\u666f</h1>\n<p>\u6211\u89c9\u5f97\u547d\u4ee4\u884c\u7684\u7a0b\u5e8f\u6bd4\u8f83\u8d5b\u535a\u670b\u514b\uff0c\u4e00\u76f4\u60f3\u505a\u4e2a\u547d\u4ee4\u884c\u7684\u4ea4\u4e92\u7a0b\u5e8f\u3002\n\u76ee\u524d\u5728\u6e38\u620f\u516c\u53f8\uff0c\u867d\u7136\u6211\u4e0d\u662f\u6e38\u620f\u5de5\u7a0b\u5e08\uff0c\u4f46\u662f\u63a5\u89e6\u4e86\u4e00\u4e9b\u6e38\u620f\u884c\u4e1a\u7684\u4f18\u79c0\u5c0f\u4f19\u4f34\uff0c\u6211\u4e5f\u5fcd\u4e0d\u4f4f\u601d\u8003\u5173\u4e8e\u6e38\u620f\u7684\u4e3b\u9898\u3002</p>\n<p>\u6211\u60f3\u505a\u7684\u547d\u4ee4\u884c\u4ea4\u4e92\u5f0f\u7a0b\u5e8f\uff0c\u5176\u5b9e\u548c\u6e38\u620f\u7684\u601d\u60f3\u5185\u6838\u662f\u4e00\u81f4\u7684\u3002\u4e00\u62cd\u5373\u5408\u3002</p>\n<p>\u6211\u4ee5\u524d\u505a\u8fc7\u4e00\u70b9\u70b9\u7814\u7a76\u3002\u8bb0\u5f55\u4e86\u4e00\u4e9b\u7b14\u8bb0\u3002\u5173\u4e8e Ruby \u4e2d\u5982\u4f55\u5b9e\u73b0\u4ea4\u4e92\u5f0f\u547d\u4ee4\u884c\u7a0b\u5e8f\u3002\n\u672c\u6587\u4e5f\u662f\u5efa\u7acb\u5728\u8fd9\u4e2a\u57fa\u7840\u4e4b\u4e0a\u3002</p>\n<ul>\n<li><a href=\"https://mark24code.github.io/cli/tui/2022/03/01/%E5%91%BD%E4%BB%A4%E8%A1%8C%E7%95%8C%E9%9D%A2TUI&amp;CLI%E7%9B%B8%E5%85%B3%E6%94%B6%E9%9B%86.html\" rel=\"nofollow\">\u547d\u4ee4\u884c\u754c\u9762 TUI&amp;CLI \u76f8\u5173\u6536\u96c6</a> </li>\n<li><a href=\"https://mark24code.github.io/%E6%B8%B8%E6%88%8F/2022/03/01/%E5%91%BD%E4%BB%A4%E8%A1%8C%E4%B8%8E%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E%E5%88%9D%E6%8E%A2.html\" rel=\"nofollow\">\u547d\u4ee4\u884c\u4e0e\u6e38\u620f\u5f15\u64ce\u521d\u63a2</a></li>\n</ul>\n<p>\u7528\u6700\u7b80\u5355\u7684\u65b9\u5f0f\u5b9e\u73b0\u4e86\u4e00\u4e2a [\u8d2a\u5403\u86c7]</p>\n<ul>\n<li>Github: <a href=\"https://github.com/Mark24Code/snakes\" rel=\"nofollow\">https://github.com/Mark24Code/snakes</a></li>\n<li>Gem: <a href=\"https://gems.ruby-china.com/gems/snakes\" rel=\"nofollow\">https://gems.ruby-china.com/gems/snakes</a></li>\n</ul>\n<h1>rb2048 \u5fc3\u8def\u5386\u7a0b</h1>\n<h2>rb2048 \u4eae\u70b9</h2>\n<p>rb2048 \u6709\u8da3\u7684\u5730\u65b9\u5728\u4e8e\uff0c\u5728\u8bbe\u8ba1\u7684\u65f6\u5019\uff0c\u6ca1\u6709\u7b80\u5355\u5b9e\u73b0\u4e86\u4e4b\u3002\u6bd5\u7adf\u6709\u592a\u591a 2048 \u4e86\uff0c\u4e0d\u5dee\u8fd9\u4e00\u4e2a\u3002</p>\n<p>\u5bf9\u4e8e\u6211\u4e0d\u662f\u5b8c\u6210\u4e00\u4e2a\u4efb\u52a1\u3002\u7531\u4e8e\u6700\u8fd1\u4e24\u5929\u5173\u6ce8\u4e8e\u7ebf\u7a0b\u7684\u4f7f\u7528\uff0c\u4e8e\u662f\u6211\u628a\u7ebf\u7a0b\u65b9\u9762\u7684\u4f7f\u7528\u52a0\u5165\u5230 rb2048 \u3002\u8fd9\u7b97\u662f\u4e00\u4e2a\u5b9e\u9a8c\u6027\u7684\u4f8b\u5b50\u3002\u9a8c\u8bc1\u6211\u7684\u60f3\u6cd5\uff1a</p>\n<p>rb2048 \u5c06:</p>\n<ul>\n<li>\u7528\u6237 I/O</li>\n<li>\u6e38\u620f\u6570\u636e\u8ba1\u7b97</li>\n<li>\u6e38\u620f\u6e32\u67d3</li>\n</ul>\n<p>\u8fd9\u4e09\u90e8\u5206\u5206\u522b\u7528\u5355\u72ec\u7684\u7ebf\u7a0b\u5b9e\u73b0\uff0c\u7528\u961f\u5217\u901a\u4fe1\u3002\u9ebb\u96c0\u867d\u5c0f\uff0c\u4e94\u810f\u4ff1\u5168\u3002\u867d\u7136\u7c97\u7cd9\uff0c\u4f46\u662f\u4ee3\u8868\u4e86\u6e38\u620f\u5f15\u64ce\u5178\u578b\u7684\u8bbe\u8ba1\u601d\u8def\u3002\n\uff08\u867d\u7136\u6211\u4e86\u89e3\u7684\u4e0d\u591a\uff09</p>\n<h2>\u8ba4\u77e5\u53d8\u5316</h2>\n<p>\u7b80\u5355\u8bf4\u8bf4\u6211\u6700\u8fd1\u7684\u601d\u8003\u5427:</p>\n<p>1 \uff09\u5bf9\u4e8e\u8ba1\u7b97\u673a\u4e0d\u540c\u9886\u57df\u8ba4\u8bc6\u53d1\u751f\u4e86\u53d8\u5316</p>\n<p>\u4ee5\u524d\u4f1a\u89c9\u5f97\uff1a\u6e38\u620f\u662f\u6e38\u620f\uff0cweb \u662f web \uff0c\u8bed\u8a00\u662f\u8bed\u8a00\uff0c\u5143\u7f16\u7a0b\u5c31\u662f\u5143\u7f16\u7a0b\u2026\u2026\u4e5f\u8bb8\u8fd8\u6709\u5f88\u591a\u6982\u5ff5\uff0c\u4f46\u662f\u6e10\u6e10\u73b0\u5728\u89c9\u5f97\u65e0\u975e\u662f\u4e00\u4ef6\u4e8b \u2014\u2014 \u7f16\u7a0b\u7f62\u4e86\u3002</p>\n<p>\u968f\u7740\u770b\u5230\u601d\u8003\u7684\u4e1c\u897f\u9010\u6e10\u53d8\u591a\uff0c\u5f88\u591a\u8ba1\u7b97\u673a\u9886\u57df\u7684\u95ee\u9898\uff0c\u5728\u6211\u7684\u89d2\u5ea6\u89c9\u5f97\u90fd\u4e00\u6837\u3002</p>\n<p>2 \uff09\u7b2c\u4e00\u6027\u539f\u7406 + \u4ea4\u6d41\uff0c\u5411\u5185\u4e60\u5f97</p>\n<p>\u8fd9\u6b21\u6478\u7740\u77f3\u5934\u8fc7\u6cb3\uff0c\u6bd4\u8f83\u65b0\u5947\u7684\u4f53\u9a8c\u5c31\u662f\uff0c\u4ece\u5f53\u521d\u4e00\u4e2a\u60f3\u6cd5\u5230\u539f\u7406\u7684\u8ba8\u8bba\u5230\u6700\u540e\u5b9e\u73b0\u3002\u4e3b\u8981\u662f\u601d\u8003\u63a8\u7406\uff0c\u8fd8\u6709\u548c\u4f18\u79c0\u7684\u540c\u4e8b\u7684\u804a\u5929\u4e2d\u4e60\u5f97 \uff08\u8fd9\u91cc\u611f\u8c22 @\u8c37\u795e)\u3002</p>\n<ul>\n<li>\u523b\u610f\u5b66\u4e60 VS \u5185\u5728\u4e60\u5f97</li>\n</ul>\n<p>\u73b0\u5b9e\u4e2d\u6709\u5f88\u591a\u6e38\u620f\u5f15\u64ce\u3002\u4ed6\u4eec\u4e5f\u8bb8\u5185\u6709\u4e7e\u5764\uff0c\u4e0d\u8fc7\u5176\u5b9e\u662f\u5426\u7814\u7a76\u4ed6\u4eec\u4e5f\u4e0d\u91cd\u8981\u3002</p>\n<p>\u6211\u4e5f\u4e0d\u5728\u4e4e\u522b\u4eba\u7684\u5b9e\u73b0\uff0c\u6216\u8005\u66f4\u597d\u5730\u5b9e\u73b0\uff0c\u662f\u5426\u6709\u5b9e\u73b0\u8fc7\u4e86\u53ef\u4ee5\u53c2\u8003\u3002\u5176\u5b9e\u6ca1\u4ec0\u4e48\u53ef\u53c2\u8003\u7684\u3002\u53ea\u8981\u6211\u4eec\u81ea\u5df1\u60f3\u660e\u767d\u4e86\uff0c\u522b\u5fd8\u4e86\u6211\u4eec\u4e0a\u9762\u8bf4\u7684\uff0c\u4ed6\u4eec\u90fd\u662f\u4e00\u4ef6\u4e8b \u2014\u2014 \u7f16\u7a0b\u7f62\u4e86\u3002\n\u5f53\u6211\u4eec\u9762\u4e34\u65b0\u95ee\u9898\uff0c\u6211\u4eec\u4e5f\u4f1a\u52a0\u5f3a\u6211\u4eec\u7684 \u201c\u5f15\u64ce\u201d\u3002\u4ece\u601d\u60f3\u4e0a\uff0c\u4ed6\u4eec\u662f\u5e73\u7b49\u7684\u3002:P</p>\n<p>\u53ef\u80fd\u4e0e\u4ee5\u524d\u5411\u5916\u6c42\u77e5\uff0c\u73b0\u5728\u4f1a\u989d\u5916\u7684\u5411\u5185\u601d\u8003\u3002\u6bd4\u8f83\u795e\u5947\u7684\u4f53\u9a8c\u662f\uff0c\u4e00\u4e9b\u4e1c\u897f\u542c\u4e2a\u5927\u6982\uff0c\u4e5f\u80fd\u76f2\u731c\u4e2a\u4e03\u516b\u5206\u3002</p>\n<h1>\u4ece\u6e38\u620f\u5f00\u59cb\u804a\u5427</h1>\n<h2>\u6e38\u620f\u4e4b\u96be</h2>\n<p>\u5176\u5b9e 2048 \u6ca1\u5565\u597d\u804a\uff0c\u5199 2048 \u7684\u80cc\u540e\u662f\u5bf9\u6e38\u620f\u7684\u4e00\u4e9b\u601d\u8003\u3002</p>\n<p>\u5176\u5b9e\u6e38\u620f\u662f\u4e00\u4e2a\u6bd4\u8f83\u7279\u522b\u7684\u5b58\u5728\u3002\u4ed6\u662f\u4e00\u79cd\u6bd4\u8f83\u7279\u6b8a\u7684\u7a0b\u5e8f\uff0c\u7279\u6b8a\u5728\u54ea\u513f\u5462\uff1f</p>\n<p>1 \uff09\u4ed6\u662f\u6301\u7eed\u4ea4\u4e92\u7a0b\u5e8f</p>\n<p>\u4e0d\u540c\u4e8e\u7b80\u5355\u7684\u811a\u672c\uff0c\u8dd1\u5b8c\u7ed3\u675f\u3002\u6216\u8005\u4f20\u9012\u4e00\u4e2a\u521d\u59cb\u53c2\u6570\uff0c\u5c31\u50cf\u51fd\u6570\u4e00\u6837\u8fd0\u884c\u5b8c\u7ed3\u675f\u3002</p>\n<p>\u4ed6\u662f\u4e00\u4e2a\u6301\u7eed\u4ea4\u4e92\u7684\u8fc7\u7a0b\uff0c\u968f\u7740\u65f6\u95f4\u7d2f\u8ba1\u6e38\u620f\u7684\u65b9\u65b9\u9762\u9762\u90fd\u5728\u53d8\u5316\u3002</p>\n<p>2 \uff09\u591a\u9762\u5e73\u8861</p>\n<p>\u4e0d\u540c\u4e8e\u4f60\u5199\u4e00\u6bb5 function \u5c31\u7ed3\u675f\u4e86\u3002\u6e38\u620f\u8981\u5728\u8fd0\u884c\u7684\u751f\u547d\u5468\u671f\u91cc\uff1a</p>\n<ul>\n<li>\u7528\u6237\u4ea4\u4e92\u4e8b\u4ef6</li>\n<li>\u6e38\u620f\u6570\u636e\u8ba1\u7b97</li>\n<li>\u6e32\u67d3\u89c6\u56fe</li>\n</ul>\n<p>\u5728\u81f3\u5c11\u8fd9\u4e09\u4e2a\u65b9\u9762\u4e92\u76f8\u4f5c\u7528\u3002</p>\n<p>\u8fd8\u53ef\u80fd\u6709:</p>\n<ul>\n<li>\u7f51\u7edc</li>\n<li>\u8c03\u5ea6</li>\n<li>\u786c\u4ef6 CPU \u3001GPU \u52a0\u901f\u6e32\u67d3</li>\n<li>AI</li>\n<li>\u8d44\u6e90\u751f\u6210</li>\n<li>\u6570\u636e\u91c7\u96c6</li>\n<li>\u5404\u79cd\u4f18\u5316\u6280\u672f</li>\n</ul>\n<p>\u5176\u4ed6\u5468\u8fb9\u5e76\u4e0d\u5c55\u5f00</p>\n<p>3 \uff09\u7a33\u5b9a\u7684\u5e27\u7387</p>\n<p>\u5982\u679c\u662f 60HZ \u7684\u6e38\u620f\uff0c\u5fc5\u987b\u5728 16.6ms \u5185\u5b8c\u6210\u52a8\u4f5c\u8fdb\u884c\u5237\u65b0\u3002</p>\n<p>\u8fd9\u4e5f\u4e0d\u662f\u666e\u901a\u4e1a\u52a1\u811a\u672c\u3001\u7a0b\u5e8f\u4e00\u76f4\u8dd1\u81ea\u5df1\u7684\u7ebf\u6027\u903b\u8f91\u5c31\u7b97\u4e86\uff0c\u6839\u672c\u4e0d\u5173\u5fc3\u65f6\u95f4\u3002</p>\n<p>4 \uff09\u5bc6\u96c6\u5bf9\u8c61\u8ba1\u7b97</p>\n<p>\u7b80\u5355\u7684\u6e38\u620f\u8fd8\u597d\uff0c\u4f20\u7edf\u7684\u6a21\u5f0f\u662f\u9762\u5411\u5bf9\u8c61\u5efa\u6a21\uff0c\u4e00\u5207\u770b\u8d77\u6765\u8fd8\u7b97\u81ea\u7136\u3002</p>\n<p>\u4f46\u662f\u4e5f\u51fa\u73b0\u4e86\u4e07\u4eba\u540c\u53f0\u7684\u6e38\u620f\uff0c\u8fd9\u91cc\u4f20\u7edf\u7684\u7f16\u7a0b\u6a21\u5f0f\u5df2\u7ecf\u6ee1\u8db3\u4e0d\u4e86\u6e38\u620f\u5bf9\u8c61\u7684\u904d\u5386\u4e86\uff0c\u5f88\u5feb\u4f1a\u8fbe\u5230\u6027\u80fd\u74f6\u9888\u3002</p>\n<p>\u8fd9\u51e0\u5e74\uff0c\u51fa\u73b0\u4e86 ECS \u67b6\u6784\uff08 Entity-Component-System \uff09\u3002</p>\n<blockquote>\n<p><a href=\"https://blog.codingnow.com/2017/06/overwatch_ecs.html\" rel=\"nofollow\">\u6d45\u8c08\u300a\u5b88\u671b\u5148\u950b\u300b\u4e2d\u7684 ECS \u6784\u67b6</a></p>\n</blockquote>\n<p>\u5c0f\u7ed3\uff1a</p>\n<p>\u5176\u5b9e\u8fd8\u6709\u5404\u79cd\u53d1\u6563\u3002\u5982\u4f55\u4f7f\u7528 CPU \u3001GPU \u52a0\u901f\u6e32\u67d3\uff0c\u8fd9\u5c31\u4e0d\u518d\u63d0\u4e86\u3002</p>\n<p>\u6e38\u620f\u662f\u4e00\u4e2a\u975e\u5e38\u7279\u6b8a\u7684\u5b58\u5728\uff0c\u5b83\u610f\u5473\u7740\u5bc6\u96c6\u578b\u8ba1\u7b97\u3001\u5bc6\u96c6\u578b IO \u6df7\u5408\u51fa\u73b0\u7684\u573a\u666f\u3002\u6211\u7406\u89e3\u662f\u6bd4 Web \u590d\u6742\u5728\u53e6\u4e00\u4e2a\u7ef4\u5ea6\u4e0a\u3002</p>\n<p>\u6e38\u620f\u6d89\u53ca\u5230 \u7f16\u7a0b\u67b6\u6784\u3001\u7f51\u7edc\u3001\u56fe\u5f62\u5b66\u3001\u7f8e\u672f\u8bbe\u8ba1\u3001\u8d44\u6e90\u52a0\u8f7d\u2026\u2026 \u8bf8\u591a\u4e30\u5bcc\u7684\u8bdd\u9898\u3002</p>\n<p>\u8fd9\u4e9b\u5c31\u4e0d\u662f\u6211\u8fd9\u4e2a\u95e8\u5916\u6c49\u9760\u7ba1\u7aa5\u8821\u6d4b\u80fd\u591f\u8bf4\u5f97\u6e05\u7684\u3002\u6211\u4eca\u5929\u53ef\u4ee5\u53ea\u8c08\u8c08\u6211\u5bf9\u6e38\u620f\u7684\u7406\u89e3\u548c\u8ba4\u8bc6\uff0c\u4ee5\u53ca\u6784\u5efa 2048 \u7684\u601d\u8003\u3002</p>\n<h2>\u6e38\u620f\u57fa\u672c\u6784\u6210</h2>\n<p>\u5176\u5b9e\u4e00\u4e2a\u57fa\u672c\u6e38\u620f\u53ef\u4ee5\u7528\u5982\u4e0b\u4ee3\u7801\u63cf\u8ff0\uff1a</p>\n<pre><code class=\"language-ruby\">loop do\n  IOEvent\n  UpdateGameData\n  Render\nend\n</code></pre>\n<p>\u6e38\u620f\u5904\u5728\u4e00\u4e2a\u4e3b\u5faa\u73af\u4e2d\uff0c\u6211\u4eec\u4f9d\u6b21\u8981\u5904\u7406\u7528\u6237\u8f93\u5165\u4e8b\u4ef6\uff0c\u6839\u636e\u7528\u6237\u8f93\u5165\u4e8b\u4ef6\u8fdb\u884c\u6e38\u620f\u6a21\u578b\u7684\u53d8\u5316\uff0c\u6700\u540e\u518d\u628a\u6570\u636e\u6e32\u67d3\u5728\u5c4f\u5e55\u4e0a\u3002</p>\n<p>\u8fd9\u662f\u4e00\u4e2a\u5355\u7ebf\u7a0b\uff0c\u4e3b\u5faa\u73af\u7684\u4f8b\u5b50\u3002</p>\n<p>\u73b0\u5b9e\u4e2d\u6bcf\u4e2a\u90e8\u5206\u90fd\u53ef\u4ee5\u989d\u5916\u53d8\u5f97\u590d\u6742\u3002\u4e5f\u53ef\u4ee5\u7528\u7ebf\u7a0b\u5355\u72ec\u5b9e\u73b0\u3002\u4e00\u5207\u770b\u9700\u6c42\u3002</p>\n<h3>\u6e38\u620f\u4e0e\u4ea4\u4e92\u5e94\u7528\u7a0b\u5e8f</h3>\n<p>\u4f60\u4f1a\u53d1\u73b0\u6e38\u620f\u5c31\u662f\u4ea4\u4e92\u7a0b\u5e8f\u3002</p>\n<p>\u4e0a\u9762\u7684\u4e09\u90e8\u5206\uff0c\u4f60\u4e5f\u53ef\u4ee5\u548c MVC \u5f3a\u884c\u626f\u5728\u4e00\u8d77\u3002</p>\n<ul>\n<li>M \u5c31\u662f Model \u6e38\u620f\u6570\u636e</li>\n<li>V \u5c31\u662f View  \u8d1f\u8d23\u6e32\u67d3\u89c6\u56fe</li>\n<li>C \u5c31\u662f Controler \u53ef\u4ee5\u5bf9\u5e94\u4e8b\u4ef6\u63a7\u5236</li>\n</ul>\n<p>MVC \u7684\u5178\u578b\u7a0b\u5e8f\uff0c\u9664\u4e86\u684c\u9762\u8f6f\u4ef6\uff0cWeb \u4e5f\u7b97\u662f\uff0cApp \u4e5f\u7b97\u3002</p>\n<p>\u770b\u4f3c\u662f\u5728\u8bf4\u6e38\u620f\uff0c\u5b9e\u9645\u4e0a\u4ed6\u4eec\u662f\u4e00\u56de\u4e8b\u3002</p>\n<h2>\u6e38\u620f\u5f15\u64ce\u7684\u79d8\u5bc6</h2>\n<p>\u6e38\u620f\u5f15\u64ce\u5176\u5b9e\u5c31\u662f\u6846\u67b6\uff0c\u5f88\u4f69\u670d\u4ed6\u4eec\u4f1a\u8d77\u540d\u5b57\u3002</p>\n<p>\u6846\u67b6\u3001\u5f15\u64ce\u5176\u5b9e\u662f\u4e00\u4e2a\u4e1c\u897f\uff0c\u4ed6\u4eec\u7684\u7279\u5f81\u5c31\u662f\u4e00\u4e2a\u534a\u6210\u54c1\u7684\u8f6f\u4ef6\u3002</p>\n<pre><code class=\"language-ruby\">loop do\n  IOEvent\n  UpdateGameData\n  Render\nend\n</code></pre>\n<p>\u6bd4\u5982\u8fd9\u4e2a\u6e38\u620f\u5faa\u73af\uff0c\u5982\u679c\u6211\u4eec\u5c01\u88c5\u4e86\u4e3b\u5faa\u73af\uff0c\u5c01\u88c5\u4e86\u4e8b\u4ef6\u5bf9\u8c61\u3002\u5bf9\u5916\u66b4\u9732\u4e86\u4e00\u4e9b\u751f\u547d\u5468\u671f\u3002\n\u8fd9\u79cd\u534a\u6210\u54c1\u8f6f\u4ef6\u5c31\u662f \u6240\u8c13\u7684\u6846\u67b6\uff0c\u5728\u6e38\u620f\u9886\u57df\u5c31\u662f\u5f15\u64ce\u3002</p>\n<p>\u4f5c\u4e3a\u4e0b\u6e38\uff0c\u6e38\u620f\u5f15\u64ce /\u6846\u67b6\u7684\u4f7f\u7528\u8005\u6765\u8bf4\uff0c\u6211\u4eec\u5199\u7684\u7a0b\u5e8f\u5c31\u50cf\u586b\u7a7a\u4e00\u6837\u548c\u4e3b\u5faa\u73af\u5de5\u4f5c\u5728\u4e00\u8d77\u3002</p>\n<h3>\u4e3b\u5faa\u73af\u51b3\u5b9a\u4e86\u4ec0\u4e48\u662f\u6846\u67b6\u3001\u4ec0\u4e48\u662f\u5e93</h3>\n<p>\u6240\u4ee5\u6211\u4e2a\u4eba\u89c9\u5f97\uff0c\u51b3\u5b9a\u4e86\u4ec0\u4e48\u662f \u6846\u67b6 Framework \u548c \u5e93 Library \u7684\u672c\u8d28\u533a\u522b\u662f \u2014\u2014 \u4e3b\u5faa\u73af\u3002</p>\n<p>\u5f53\u4f60\u7684\u7a0b\u5e8f\u662f\u4e00\u79cd\u53ef\u88ab\u8c03\u7528\u7684\u72b6\u6001\uff0c\u90a3\u4e48\u57fa\u672c\u4e0a\u4f60\u7684\u7a0b\u5e8f\u53ef\u4ee5\u770b\u6210\u4e00\u4e2a lib\n\u5f53\u4f60\u7684\u7a0b\u5e8f\u5982\u679c\u62e5\u6709\u4e86\u4e3b\u5faa\u73af\u7684\u72b6\u6001\uff0c\u57fa\u672c\u5ba3\u544a\u4e86\u4e0d\u53ef\u88ab\u76f4\u63a5\u8c03\u7528\u3002\u90a3\u4e48\u5b83\u5176\u5b9e\u662f\u4e00\u4e2a Framework \u4e86\u3002\u9664\u4e86\u5404\u79cd Pattern \u5f88\u5c11\u89c1\u5230\u4e3b\u5faa\u73af\u7684 lib \u5c55\u793a\uff0c\u4e0d\u5b58\u5728\u7684\u539f\u56e0\u662f\u56e0\u4e3a\u62e5\u6709\u4e3b\u5faa\u73af\u7684\u7a0b\u5e8f\uff0c\u4e00\u822c\u4ee5\u5177\u4f53\u7684\u8f6f\u4ef6\u5f62\u6001\u51fa\u6765\uff1a</p>\n<ol>\n<li>\u67d0\u79cd\u8bed\u8a00\uff0c\u6bd4\u5982 \u81ea\u5e26\u8c03\u5ea6\u7684 golang \u3001\u81ea\u5e26 EventLoop \u7684 JavaScript \u5f15\u64ce V8</li>\n<li>\u67d0\u79cd\u6846\u67b6\uff0c\u6bd4\u5982 Web \u6846\u67b6\u81ea\u5e26\u76d1\u542c\u5faa\u73af</li>\n<li>\u67d0\u79cd\u5f15\u64ce\uff0c\u6bd4\u5982 \u6e38\u620f\u5f15\u64ce</li>\n</ol>\n<p>Framework \u5f0f\u7684\u7a0b\u5e8f\uff0c\u4f60\u7684\u5de5\u4f5c\u4efb\u52a1\u5c31\u4f1a\u8f6c\u5411\u719f\u6089\u8fd9\u4e2a\u7a0b\u5e8f\u66b4\u9732\u7684\u5bf9\u8c61\uff0c\u671f\u5f85\u4f60\u7684\u7a0b\u5e8f\u548c\u4e3b\u5faa\u73af\u80fd\u4e00\u8d77\u5de5\u4f5c\u3002</p>\n<h2>\u7f16\u7a0b\u8bed\u8a00\u4f1a\u662f\u6e38\u620f\u7684\u74f6\u9888\u4e48\uff1f</h2>\n<p>\u6211\u4eec\u518d\u6765\u804a\u804a\u6e38\u620f\u5f15\u64ce\u548c\u7f16\u7a0b\u8bed\u8a00\u3002</p>\n<p>Unity \u7684\u80cc\u540e\u662f C# \u652f\u6491\uff1b\u865a\u5e7b\u5f15\u64ce\u7684\u80cc\u540e\u662f C++\u3002\u4ed6\u4eec\u91c7\u7528\u4e86\u66f4\u5e95\u5c42\u7684\u8bed\u8a00\u3002\u90a3\u4e48\u95ee\u9898\u6765\u4e86\uff0c\u7f16\u7a0b\u8bed\u8a00\u4f1a\u6210\u4e3a\u5236\u7ea6\u6e38\u620f\u7684\u74f6\u9888\u4e48\uff1f</p>\n<p>\u8fd9\u4e5f\u662f\u6211\u81ea\u5df1\u601d\u8003\u7684\u4e00\u4e2a\u95ee\u9898\u3002</p>\n<p>\u6211\u4eec\u53ef\u80fd\u4f1a\u5f88\u7c97\u66b4\u5730\u89c9\u5f97 \u52a8\u6001\u8bed\u8a00\u666e\u904d\u6162\uff0c\u5f53\u7136\u662f\u8d8a\u63a5\u8fd1\u5e95\u5c42\u8d8a\u597d\u3002\u5176\u5b9e\u6211\u66f4\u60f3\u77e5\u9053\uff0c\u5982\u6b64\u8fd9\u6837\u9009\u62e9\u7684\u6807\u51c6\u5728\u54ea\u513f\uff1f</p>\n<p>\u5176\u5b9e\u6211\u4eec\u53ef\u4ee5\u601d\u8003\u4e0b\uff0c\u8fd9\u4e2a\u7ed3\u8bba\u4e0d\u96be\u83b7\u5f97\u3002</p>\n<h3>\u52a8\u6001\u8bed\u8a00\u771f\u7684\u6162\u4e48\uff1f</h3>\n<p>\u5176\u5b9e\u52a8\u6001\u8bed\u8a00\u5728\u6267\u884c\u4e00\u4e2a\u547d\u4ee4\u7684\u65f6\u5019\uff0cRuby \u8fd9\u79cd\u6700\u540e C \u5b9e\u73b0\uff1b Golang \u6700\u540e\u4e5f\u843d\u5728 C \uff08 Golang \u5b9e\u73b0\u81ea\u4e3e\u4e4b\u540e\uff0c\u90a3\u5c31\u7528\u6c47\u7f16\u601d\u8003\u5427\uff09\u3002\u5176\u5b9e\u4ed6\u4eec\u5728\u6267\u884c\u4e00\u4e2a\u5177\u4f53\u64cd\u4f5c\u7684\u65f6\u5019\uff0c\u6570\u91cf\u7ea7\u4e00\u81f4\u7684\u3002</p>\n<p>\u4ed6\u4eec\u5176\u5b9e\u5dee\u4e0d\u591a\u3002</p>\n<p>\u901f\u5ea6\u5dee\u8ddd\u5728\u54ea\u513f\u5462\uff1f</p>\n<p>1 \uff09\u8f7d\u5165\u73af\u5883</p>\n<p>C \u3001Golang \u8fd9\u79cd\u53ef\u4ee5\u6253\u5305\u6210\u4e8c\u8fdb\u5236\u7684\u8bed\u8a00\u3002\u4ed6\u7f16\u8bd1\u9636\u6bb5\u4f1a\u628a\u9700\u8981\u6267\u884c\u7684\u4ee3\u7801\u7f16\u8bd1\u6210\u4e8c\u8fdb\u5236\u3002</p>\n<p>\u6240\u4ee5\u6267\u884c\u7684\u65f6\u5019\u8f7d\u5165\u7684\u662f\u6240\u9700\u8981\u7528\u5230\u7684\u90e8\u5206\u529f\u80fd\u3002</p>\n<p>Python \u3001Ruby \u8fd9\u79cd\u5176\u5b9e \u4e8c\u8fdb\u5236\u662f\u8bed\u8a00\u7684\u89e3\u91ca\u5668\u3002\u8fd0\u884c\u7684\u65f6\u5019\u66f4\u591a\u7684\u65f6\u95f4\u82b1\u8d39\u5728\u52a0\u8f7d\u89e3\u91ca\u5668\u3002</p>\n<p>\u4e0d\u8fc7\uff0c\u5f53\u4f60\u7684\u7a0b\u5e8f\u590d\u6742\u5230\u6d89\u53ca\u5927\u91cf IO \u3001\u57fa\u7840\u5e93\u7684\u65f6\u5019\uff0cGolang \u7684\u6253\u5305\u7ed3\u679c\u4f1a\u8d8b\u5411\u4e8e\u63a5\u8fd1\u4e00\u4e2a\u89e3\u91ca\u5668\u7684\u5927\u5c0f\uff0c\u6bd4\u5982 Ruby \u5dee\u4e0d\u591a\u5728 30M \u5de6\u53f3\u3002</p>\n<p>\u6211\u66fe\u7ecf\u6bd4\u8f83\u8fc7\uff1a</p>\n<p>Golang \u7684\u4e00\u4e2a\u9879\u76ee\u547d\u4ee4\u884c\u7f16\u8f91\u5668 <a href=\"https://github.com/zyedidia/micro\" rel=\"nofollow\">micro</a> \u3001Ruby \u7684\u4e00\u4e2a\u9879\u76ee\u547d\u4ee4\u884c\u7f16\u8f91\u5668 <a href=\"https://www.ruby-toolbox.com/projects/diakonos\" rel=\"nofollow\">diakonos</a></p>\n<p>micro \u8fd0\u884c\u5185\u5b58 16M \uff0c\u4e5f\u5c31\u662f\u4ed6\u672c\u5730\u5927\u5c0f\uff1b diakonos \u8fd0\u884c\u5185\u5b58 30M \uff0c\u4e5f\u5c31\u662f Ruby \u89e3\u91ca\u5668\u5dee\u4e0d\u591a\u7684\u5927\u5c0f\u3002ruby \u4ee3\u7801\u4f1a\u6267\u884c\u624d\u52a0\u8f7d\uff0c\u6240\u4ee5\u53ef\u4ee5\u5ffd\u7565\u4e0d\u8ba1\u3002</p>\n<p>\u6700\u5927\u7684\u5dee\u8ddd\uff0c\u5728\u4e8e 30-16 \u7684\u8f7d\u5165\u901f\u5ea6\u5dee\uff0c\u8fd9\u4e2a\u91cf\u7ea7\u662f\u4e0d\u540c\u7684\u3002</p>\n<p>2 \uff09\u8bed\u8a00\u6784\u4ef6</p>\n<p>C \u8bed\u8a00\u5c31\u50cf\u662f\u4e00\u4e2a\u9ad8\u7ea7\u4e00\u70b9\u7684\u6c47\u7f16\u3002C \u7684\u89d2\u5ea6\u4e00\u5207\u90fd\u9700\u8981\u624b\u52a8\u7ba1\u7406\u3002\u90a3\u4e48\u5176\u5b9e\u5bf9\u4e8e\u5e95\u5c42\u8bed\u8a00\uff0c\u66f4\u73b0\u5b9e\u4e00\u70b9\u7684\u662f\u4f1a\u81ea\u5df1\u624b\u52a8\u5b9e\u73b0\u6570\u636e\u7ed3\u6784\u3002</p>\n<p>Ruby \u8fd9\u79cd\u52a8\u6001\u8bed\u8a00\uff0c\u5185\u90e8\u9ed8\u8ba4\u4f1a\u6709\u4e00\u4e2a\u6570\u636e\u7ed3\u6784\u3002</p>\n<p>\u4e3e\u4e2a\u4f8b\u5b50\uff1a</p>\n<p>\u6bd4\u5982 <code>a = \"GAME\"</code></p>\n<p>C \u8bed\u8a00\u5b9e\u9645\u4e0a\u53ea\u4f1a\u624b\u52a8\u521b\u5efa  \"GAME\" \u56db\u4e2a\u5b57\u7b26</p>\n<p>Python \u5e95\u5c42\u53ef\u80fd\u521b\u5efa\u4e00\u4e2a 20 \u5b57\u7b26\u957f\u5ea6\u7684\u6570\u7ec4\u3002\u5b58 GAME \u3002\u4e5f\u6709\u597d\u5904\uff0c\u53ef\u4ee5\u4e0d\u5b9a\u957f\u652f\u6301\u52a8\u6001\u6269\u5bb9\u3002</p>\n<p>\u5728\u751f\u6210\u8bed\u8a00\u6784\u5efa\u7684\u65f6\u5019\u5b58\u5728\u901f\u5ea6\u5dee\u3002\n\u52a8\u6001\u8bed\u8a00\u7b49\u4e8e\u591a\u521b\u5efa\u4e86\u5f88\u591a\u8bed\u8a00\u5728\u5185\u5b58\u91cc\u7684\u89e3\u6784\u3002</p>\n<p>3 \uff09\u89e3\u6790\u65f6\u95f4</p>\n<p>\u4e8c\u8fdb\u5236\u7684\u6587\u4ef6\uff0c\u76f4\u63a5\u8f7d\u5165\u5185\u5b58\u6267\u884c\u3002</p>\n<p>\u52a8\u6001\u8bed\u8a00\u6709\u4e00\u4e2a\u89e3\u6790\u7684\u8fc7\u7a0b\u3002\u5f53\u7136\uff0c\u4e5f\u6709\u4f18\u5316\u7a7a\u95f4\uff0c\u6211\u4eec\u53ef\u4ee5\u63d0\u524d\u7f16\u8bd1\u52a8\u6001\u8bed\u8a00\u4e3a\u865a\u62df\u673a\u5b57\u8282\u7801\u3002\u8fd9\u6837\u5c31\u83b7\u5f97\u4e86 \u5bf9\u4e8e\u89e3\u91ca\u5668\u662f\u4e8c\u8fdb\u5236\u7c7b\u4f3c\u7684\u4e1c\u897f\u3002</p>\n<p>4 \uff09 GC \u65f6\u95f4</p>\n<p>\u548c C \u8bed\u8a00\u76f8\u6bd4\uff0cPython \u3001Ruby \u81ea\u5e26 GC \u3002</p>\n<p>\u4ed6\u4eec\u5b58\u5728\u4e00\u4e2a \u5fc5\u987b GC \u6682\u505c\u7684\u90a3\u4e48\u4e00\u4e2a\u95ee\u9898\u3002C \u8bed\u8a00\u7684\u7b56\u7565\u662f\u624b\u52a8\u56de\u6536\u3002</p>\n<h3>\u53cc\u7f13\u51b2\u6a21\u5f0f</h3>\n<p>\u6211\u4eec\u597d\u50cf\u5217\u4e3e\u4e86\u4e00\u5927\u5806 \u52a8\u6001\u8bed\u8a00\u7684\u7f3a\u70b9\u4f3c\u7684\u3002\u5b9e\u9645\u4e0a\u81ea\u52a8\u7ba1\u7406\u7684\u6570\u636e\u7ed3\u6784\u3001\u81ea\u5e26 GC \u3001\u53ef\u4ee5\u52a8\u6001\u7684\u7f16\u8bd1\u6267\u884c\u2026\u2026 \u8fd9\u4e9b\u90fd\u662f\u52a8\u6001\u8bed\u8a00\u7684\u7f3a\u70b9\u3002</p>\n<p>\u867d\u7136\u4ed8\u51fa\u4e86\u4e9b\u8bb8\u65f6\u95f4\u7684\u4ee3\u4ef7\u3002\u53ea\u8981\u6211\u4eec\u4e0d\u6ee5\u7528\u8bed\u8a00\u6784\u4ef6 \u548c \u7279\u522b\u70c2\u7684\u7b97\u6cd5\uff0c\u771f\u662f\u5de7\u5999\u7684\u63a5\u8fd1\u5e95\u5c42\u9ad8\u6548\u7684\u5b9e\u73b0\u3002</p>\n<p>\u5176\u5b9e\u6211\u60f3\u8bf4\uff0c\u52a8\u6001\u8bed\u8a00\u81f3\u5c11\u5728\u76ee\u6807\u4e0a\u4e0d\u662f\u7279\u522b\u5927\u7684\u74f6\u9888\u3002</p>\n<p>Java \u4e5f\u6709\u6e38\u620f\u7684\u4f8b\u5b50\uff1b C# \u4e5f\u662f\u81ea\u5e26 GC \u3002GC \u4e0d\u4f1a\u662f\u74f6\u9888\u3002</p>\n<p>\u8bed\u8a00\u7684\u901f\u5ea6\u4e0d\u4f1a\u7edd\u5bf9\u610f\u4e49\u4e0a\u6210\u4e3a\u4e00\u4e2a\u6e38\u620f\u7ec4\u6210\u7684\u963b\u788d\u3002</p>\n<p>EVE \u8fd9\u6837\u7684\u5927\u578b\u6e38\u620f\uff0c\u5185\u90e8\u4f7f\u7528\u4e86 \u5de8\u6162\u7684 Python \u5c31\u53ef\u4ee5\u8bf4\u660e\u95ee\u9898\u3002</p>\n<p>\u4e4b\u6240\u4ee5\u8bed\u8a00\u4e0d\u4e00\u5b9a\u6784\u6210\u62d6\u6162\u6e38\u620f\u7684\u539f\u56e0\uff0c\u8fd8\u6709\u4e00\u4e2a\u5c31\u662f\u6e38\u620f\u548c\u5c4f\u5e55\u7684\u5237\u65b0\u673a\u5236 \u2014\u2014 \u53cc\u7f13\u51b2\u6a21\u5f0f\u3002</p>\n<p>\u5176\u5b9e\u53ef\u4ee5\u7406\u89e3\u4e3a\u4e00\u4e2a \u5185\u5b58\u7a7a\u95f4\uff0c\u6211\u4eec\u79f0\u4e4b\u4e3a Buffer \u3002\u6211\u4eec\u6709\u4e24\u4e2a Buffer \uff0c\u5206\u522b\u53eb A Buffer \u3001B Buffer \u3002</p>\n<p>\u663e\u793a\u5668\u5148\u4ece A Buffer \u4e2d\u8bfb\u53d6\u6570\u636e\u6e32\u67d3\u5c4f\u5e55\u3002\u6211\u4eec\u7a0b\u5e8f\u5199\u5165 B Buffer \uff0c\u7b49\u6211\u4eec\u771f\u7684\u5199\u5b8c\u4e86\uff0c\u53ef\u6162\u6216\u8005\u5feb\uff0c\u4f46\u662f\u65e0\u6240\u8c13\uff0c\u53cd\u6b63\u5c4f\u5e55\u8fd9\u65f6\u5019\u5728\u7a33\u5b9a\u7684\u8bfb\u53d6 A Buffer \u5185\u5bb9\u3002\u6211\u4eec\u8ba1\u7b97\u5b8c\u6bd5\uff0cB Buffer \u4e2d\u5199\u5165\u4e86\u6211\u4eec\u60f3\u8981\u7684\u4e1c\u897f\uff0c\u8fd9\u65f6\u5019\u53ea\u8981\u628a\u663e\u793a\u5668\u8bfb\u53d6\u7684\u6307\u9488\u6307\u5411 B Buffer \uff0c\u4e0b\u6b21\u5c4f\u5e55\u5c31\u4f1a\u83b7\u5f97\u6211\u4eec\u60f3\u8981\u7684\u753b\u9762\u3002\u8fd9\u5c31\u662f\u53cc\u7f13\u51b2\u6a21\u5f0f\u3002\u7531\u4e8e\u5b58\u5728\u53cc\u7f13\u51b2\u89e3\u6784\uff0c\u7b97\u5feb\u548c\u5feb\u6162\uff0c\u81f3\u5c11\u4e0d\u4f1a\u6210\u4e3a\u753b\u9762\u6495\u88c2\u7684\u539f\u56e0\u3002</p>\n<blockquote>\n<p>rb2048 \u4f7f\u7528\u4e86 Curses \u5e93\u6765\u7ed8\u5236\u754c\u9762\uff0c\u800c Curses \u5185\u90e8\u4f7f\u7528\u4e86\u53cc\u7f13\u51b2\u6a21\u5f0f\u3002</p>\n</blockquote>\n<h1>\u7ebf\u7a0b\u548c\u534f\u7a0b\u7684\u8ba8\u8bba</h1>\n<p>\u6211\u4eec\u81ea\u5df1\u7814\u7a76\u4e86\u4e24\u5929\u7ebf\u7a0b\u548c\u961f\u5217\u3002\u4e3b\u8981\u662f Ruby \u7684\u5b9e\u73b0\u3002</p>\n<p>\u8fd9\u91cc\u4e0d\u6559\u7ebf\u7a0b\u548c\u534f\u7a0b\uff0c\u53ea\u8bb0\u5f55\u6211\u89c9\u5f97\u597d\u73a9\u7684\u4ea4\u6d41\u7ed3\u679c\u3002</p>\n<h2>Ruby \u7ebf\u7a0b\u7684\u95ee\u9898</h2>\n<p>\u7f3a\u70b9\uff1a</p>\n<p>Ruby \u5b58\u5728\u7ebf\u7a0b\u9501\uff0c\u8fd9\u5bfc\u81f4\u6bcf\u4e00\u65f6\u523b\u53ea\u80fd\u8fd0\u884c\u4e00\u4e2a\u7ebf\u7a0b\u3002\u7ebf\u7a0b\u5c31\u50cf\u80cc\u540e\u867d\u7136\u6709\u5f88\u591a\u5de5\u4eba\uff0c\u4f46\u662f\u53ea\u80fd\u4ea4\u66ff\u7684\u4e00\u4eba\u4e00\u9524\u5b50\u3002</p>\n<p>\u8fd9\u80cc\u540e\u7684\u539f\u56e0\u5728\u4e8e Ruby \u8003\u8651\u5b89\u5168\u66f4\u591a\u4e00\u70b9 \u2014\u2014 \u7ebf\u7a0b\u5b89\u5168\u3002</p>\n<p>\u8fd9\u6837\u7684\u591a\u7ebf\u7a0b\u65e0\u6cd5\u5229\u7528 CPU \u591a\u6838\u5fc3\u5e76\u884c\u7684\u7279\u70b9\u3002\u5e0c\u671b\u5229\u7528\u591a\u6838\u7684\uff0c\u53ef\u4ee5\u53bb\u7528 JRuby \uff0c\u56e0\u4e3a Java \u5e95\u5c42\u6ca1\u6709\u52a0\u9501\u3002</p>\n<p>Ruby3 \u4e2d\u4e5f\u6709\u4e86\u65e0\u9501\u7ebf\u7a0b\u7684\u66ff\u4ee3\u54c1  Ractor \u4e5f\u53ef\u4ee5\u4e86\u89e3\u4e0b\u3002</p>\n<p>CRuby \u5982\u679c\u60f3\u5229\u7528\u591a\u6838\u5fc3\u53ef\u4ee5\u4f7f\u7528\u8fdb\u7a0b\u66ff\u4ee3\u7ebf\u7a0b\u3002\u5982\u679c\u8bbe\u8ba1\u5f97\u5f53\uff0c\u5176\u5b9e\u5dee\u4e0d\u591a\u3002Ruby \u91cc\u9762 Webserver \u6709\u540d\u6c14\u7684 Puma \u91c7\u7528\u7684\u5c31\u662f\u591a\u8fdb\u7a0b\u5b9e\u73b0\u3002</p>\n<p>\u4f18\u70b9\uff1a</p>\n<p>\u52a0\u4e0a\u9501\u6700\u5927\u597d\u5904\u662f\u7ebf\u7a0b\u5b89\u5168\uff0c\u4f60\u53ef\u4ee5\u81ea\u7531\u7684\u7f16\u7801\uff0cRuby \u5e2e\u4f60\u52a0\u9501\u3002\u8fd9\u6837\u591a\u7ebf\u7a0b\u8bbf\u95ee\u53d8\u91cf\u7684\u65f6\u5019\uff0c\u4e0d\u4f1a\u51fa\u9519\u3002</p>\n<p>\u4f46\u662f\u4f60\u9000\u51fa\u6765\u60f3\uff0c\u53cd\u6b63\u4f60\u81ea\u5df1\u4e5f\u8981\u52a0\u9501\u554a\uff0c\u8c01\u52a0\u4e0d\u662f\u52a0\u3002Ruby \u9ed8\u8ba4\u7684\u7ebf\u7a0b\u5176\u5b9e\u4e66\u5199\u8d77\u6765\u975e\u5e38\u53cb\u597d\u3002</p>\n<h2>\u8fdb\u7a0b\u3001\u7ebf\u7a0b\u3001\u534f\u7a0b \u50bb\u50bb\u5206\u4e0d\u6e05\u695a</h2>\n<p>\u6211\u89c9\u5f97\u518d\u8fd9\u6837\u4ecb\u7ecd\u8fd9\u4e09\u4e2a\u6982\u5ff5\uff0c\u8fd9\u6587\u7ae0\u592a\u5197\u957f\u4e86\u3002</p>\n<p>\u76f4\u63a5\u8bf4\u7ed3\u8bba\u5427\uff0c\u76f4\u89c2\u4e0a\uff0c\u8fd9\u4e09\u8005\u5b58\u5728\u91cf\u7ea7\u5dee\uff0c\u4e0d\u4ec5\u4f53\u73b0\u5728\u7a7a\u95f4\u8d44\u6e90\uff0c\u65f6\u95f4\u8d44\u6e90\u90fd\u5dee\u4e0d\u591a\u3002</p>\n<p>\u8fdb\u7a0b &gt;&gt; \u7ebf\u7a0b &gt;&gt; \u534f\u7a0b</p>\n<p>\u6bd4\u5982\u4e00\u53f0\u673a\u5668 4G \u5185\u5b58\uff1a</p>\n<p>\u53ef\u80fd\u53ea\u80fd\u5b9e\u9645\u751f\u6210\u51e0\u767e\u4e2a\u8fdb\u7a0b\u5c31\u4e0d\u592a\u884c\u4e86\u3002\n\u540c\u6837\uff0c\u53ef\u4ee5\u751f\u6210\u51e0\u5343\u4e2a\u7ebf\u7a0b\uff0c\u5c31\u52a8\u4e0d\u4e86\u4e86\u3002\n\u534f\u7a0b\u53ef\u4ee5\u751f\u6210\u51e0\u5341\u4e07\u4e2a\u3002</p>\n<p>\u4ed6\u4eec\u5927\u6982\u5c31\u662f\u8fd9\u4e2a\u5dee\u8ddd(\u6709\u66f4\u597d\u6570\u636e\u652f\u6301\u7684\uff0c\u8bf7\u8054\u7cfb\u6211)\u3002</p>\n<p>\u4ed6\u4eec\u5207\u6362\u4e0a\u4e0b\u6587\u7684\u65f6\u95f4\u4e5f\u9075\u5faa\u8fd9\u4e2a\u6bd4\u8f83\u5173\u7cfb\u3002</p>\n<p>\u6240\u4ee5\u6211\u4eec\u4e00\u822c\u7684\u7b56\u7565\uff0c\u5c3d\u91cf\u591a\u7528\u534f\u7a0b&amp;\u7ebf\u7a0b\uff0c\u5c11\u7528\u8fdb\u7a0b\u3002</p>\n<p>\u5982\u679c\u4efb\u52a1\u72ec\u7acb\u8fd0\u884c\u8fd8\u597d\uff0c\u5c31\u6015\u5f7c\u6b64\u8fd8\u8981\u901a\u4fe1\uff0c\u51fa\u73b0\u4e92\u76f8\u7b49\u5f85\u7684\u5c40\u9762\u3002</p>\n<p>\u7ebf\u7a0b\u5177\u6709 CPU \u4eb2\u548c\u6027\uff08\u4e00\u822c\u8bed\u8a00\u6765\u8bb2\uff09\u3002</p>\n<p>\u6bd4\u5982 Golang \u7684 M:N \u6a21\u578b\uff0c\u4e3b\u5f20 \u5148\u751f\u6210 M \u4e2a\u7ebf\u7a0b\uff0cM \u662f\u673a\u5668 CPU \u6838\u5fc3\u6570\uff0c\u7136\u540e\u518d\u5728 M \u4e2a\u7ebf\u7a0b\u4e4b\u95f4\u8c03\u5ea6\u5b9e\u9645\u4ea7\u751f\u7684 N \u4e2a\u4efb\u52a1\u3002</p>\n<p>\u6bd4\u5982 Nginx \u7684\u914d\u7f6e\u4e5f\u4e3b\u5f20 \u914d\u7f6e\u7ebf\u7a0b\u6838\u5fc3\u6570\u548c CPU \u6838\u5fc3\u6570\u4e00\u81f4\u3002</p>\n<h3>\u4ec0\u4e48\u65f6\u5019\u7528\u7ebf\u7a0b\u3001\u4ec0\u4e48\u65f6\u5019\u7528\u534f\u7a0b\uff1f</h3>\n<p>\u7ebf\u7a0b\u3001\u534f\u7a0b\u4ea7\u751f\u7684\u539f\u56e0\u662f\u4ec0\u4e48\uff1f</p>\n<p>\u5176\u5b9e\u8fd8\u662f\u4e3a\u4e86\u8c03\u5ea6\u3002</p>\n<p>\u7ebf\u7a0b\u662f\u7ec6\u5206\u8fdb\u7a0b\u4e0b\u5171\u4eab\u5185\u5b58\u7684\u573a\u666f\uff1b\u534f\u7a0b\u662f\u4e3a\u4e86\u7ec6\u5316\u8c03\u5ea6\u3002</p>\n<p>\u56e0\u4e3a\u8fdb\u7a0b\u3001\u7ebf\u7a0b\u672c\u8d28\u4e0a\u662f\u64cd\u4f5c\u7cfb\u7edf\u5728\u8c03\u5ea6\u3002\u64cd\u4f5c\u7cfb\u7edf\u5e76\u4e0d\u6e05\u695a\u4ec0\u4e48\u65f6\u5019\u5e94\u8be5\u8c03\u5ea6\u3002\u53ea\u80fd\u91c7\u7528\u5404\u79cd\u4f18\u5148\u8ba1\u7b97\u6cd5\u3001\u5e73\u5747\u7b97\u6cd5\u3002\u518d\u600e\u4e48\u7b97\uff0c\u4e5f\u662f\u76f2\u4eba\u6478\u8c61\u7f62\u4e86\u3002</p>\n<p>\u534f\u7a0b\u7ed9\u4e86\u7a0b\u5e8f\u5458\u4e00\u4e2a\u53e3\u5b50\uff0c\u4f60\u53ef\u4ee5\u7528 \u534f\u7a0b\u5728 \u6d89\u53ca\u963b\u585e\u90e8\u5206\u8fdb\u884c\u8ba9\u51fa\u63a7\u5236\u6743\u3002</p>\n<p>\u7b80\u800c\u8a00\u4e4b\uff0c\u7ecf\u9a8c\u4e4b\u8c08\uff1a</p>\n<p>\u6d89\u53ca\u5230 \u8ba1\u7b97\u5bc6\u96c6\u578b \u8bf7\u7528\u7ebf\u7a0b\u3002</p>\n<p>\u5982\u679c\u6d89\u53ca\u5230 IO \u963b\u585e\u5bc6\u96c6\uff0c\u8bf7\u7528\u534f\u7a0b\u3002</p>\n<p>\u6211\u4eec\u7684\u76ee\u7684\u4e0d\u662f\u4e3a\u4e86\u7528\u800c\u7528\uff0c\u800c\u662f\u4f7f\u7528\u8c03\u5ea6\uff0c\u63d0\u9ad8\u6211\u4eec\u4ee3\u7801\u6267\u884c\u7684\u6548\u7387\uff0c\u51cf\u5c11\u7b49\u5f85\u3002</p>\n<h3>\u786c\u4ef6\u4e2d\u65ad</h3>\n<p>\u5982\u679c\u8bf4\u5176\u5b9e\u6ca1\u6709 if-else\\switch\\while \uff0c\u8ba1\u7b97\u673a\u5668\u5176\u5b9e\u53ea\u6709  goto \u3002</p>\n<p>\u5982\u679c\u4f60\u770b\u8fc7\u6c47\u7f16\uff0c\u5927\u6982\u7406\u89e3\u6211\u662f\u4ec0\u4e48\u610f\u601d\u3002</p>\n<p>\u540c\u6837\uff0c\u8ba1\u7b97\u673a\u91cc\u8fdb\u7a0b\u3001\u7ebf\u7a0b\u3001\u534f\u7a0b\u80cc\u540e\u8c03\u5ea6\u7684\u79d8\u5bc6\uff0c\u90fd\u6765\u81ea\u4e8e CPU \u7684\u786c\u4ef6\u4e2d\u65ad\u529f\u80fd\u3002</p>\n<p>\u53ea\u4e0d\u8fc7\u662f\u4e0a\u4e0b\u6587\u5feb\u901f\u5207\u6362\uff0c\u5207\u6362\u4e0a\u4e0b\u6587\u591a\u548c\u5c11\u7f62\u4e86\u3002</p>\n<h1>2048 \u7684\u5b9e\u73b0</h1>\n<p>\u5176\u5b9e 2048 \u7684\u5173\u952e\u5c31\u662f\u76f8\u90bb\u5143\u7d20\u5408\u5e76\uff0c\u5b9e\u73b0\u8fd9\u4e48\u4e00\u4e2a\u7b97\u6cd5\uff0c\u53cd\u590d\u6267\u884c\u5230\u65e0\u5143\u7d20\u53ef\u4ee5\u7ee7\u7eed\u5408\u5e76\u3002\u518d\u628a\u8fd9\u4e2a\u5e94\u7528\u5230 x\\y \u65b9\u5411\u6240\u6709\u884c\u5217\u5c31\u597d\u4e86\u3002</p>\n<h2>\u5177\u4f53\u7ebf\u7a0b</h2>\n<p>\u76ee\u524d\u5b9e\u73b0\u6210\u901a\u8fc7\u961f\u5217\u6765\u5b9e\u73b0\u901a\u4fe1\uff1a</p>\n<p>IO \u7ebf\u7a0b\uff0c\u7528\u6237\u4ea7\u751f\u4e00\u4e2a\u8f93\u5165\uff0c\u8fdb\u5165\u4e8b\u4ef6\u961f\u5217\u3002\n\u6e38\u620f\u8bfb\u53d6\u4e8b\u4ef6\u961f\u5217\uff0c\u5f00\u59cb\u8ba1\u7b97\u6e38\u620f\u6570\u636e\uff0c\u628a\u7ed3\u679c\u585e\u5165\u6e32\u67d3\u961f\u5217\u3002\n\u6e32\u67d3\u7ebf\u7a0b\uff0c\u8bfb\u53d6\u6e32\u67d3\u961f\u5217\u6570\u636e\u8fdb\u884c\u6e32\u67d3\u3002</p>\n<h2>\u540e\u7eed\u8ba8\u8bba</h2>\n<p>\u6211\u548c\u540c\u4e8b\u4ea4\u6d41\u4e86\u4e00\u4e0b\uff0c\u5c31 2048 \u800c\u8a00\u5176\u5b9e\u53ef\u4ee5\u5f88\u591a\u65b9\u5f0f\u505a\uff1a</p>\n<ol>\n<li>\u5982\u679c\u662f\u961f\u5217\u4f9d\u8d56\u5f0f</li>\n</ol>\n<p>\u6211\u4eec\u7b49\u4e8e\u505a\u51fa\u4e00\u4e2a pipline \u7684\u65b9\u5f0f\u4e86</p>\n<ol>\n<li>\u6211\u4eec\u4e5f\u53ef\u4ee5\u89e3\u5f00\u961f\u5217\u963b\u585e</li>\n</ol>\n<p>\u771f\u6b63\u7684\u81ea\u7531\u6e32\u67d3\u3002\u867d\u7136 2048 \u770b\u4e0d\u51fa\u6548\u679c</p>\n<h2>\u961f\u5217\u8ffd\u8d76\u95ee\u9898</h2>\n<p>\u7528\u6237\u4e0d\u65ad\u5730\u6572\u51fb\uff0c\u4ea7\u751f\u65f6\u95f4\uff0c\u5982\u679c\u961f\u5217\u91cc\u4e00\u81f4\u4ea7\u751f\u6570\u636e\uff0c\u90a3\u4e0d\u662f\u6e32\u67d3\u6c38\u8fdc\u8ffd\u4e0d\u4e0a\uff1f</p>\n<p>\u591a\u7ebf\u7a0b\u961f\u5217\u9700\u8981\u601d\u8003 \u751f\u4ea7\u8005\u3001\u6d88\u8d39\u8005\u6a21\u578b\uff0c\u9700\u8981\u8bbe\u8ba1\u5339\u914d\u7684\u65b9\u5f0f\u3002</p>\n<p>\u89e3\u51b3\u65b9\u6cd5</p>\n<p>1 \uff09\u63a7\u5236\u751f\u4ea7\u9891\u7387\uff0c\u751f\u4ea7\u548c\u6d88\u8017\u76f8\u62b5\u6d88</p>\n<p>\u4e8b\u4ef6\u91c7\u6837\u3001\u6e32\u67d3 \u53ef\u4ee5\u4fdd\u6301\u4e00\u4e2a\u9891\u7387</p>\n<p>2 \uff09\u4e0d\u63a7\u5236\u751f\u4ea7\uff0c\u4f46\u662f\u8df3\u8fc7\u751f\u4ea7</p>\n<p>\u4e8b\u4ef6\u91c7\u6837\uff0c\u53ef\u4ee5\u643a\u5e26\u65f6\u95f4\u6233\u3002</p>\n<p>\u5982\u679c\u6e32\u67d3\u7684\u65f6\u5019\uff0c\u6bcf\u6b21\u65f6\u95f4\u8d85\u65f6\uff0c\u8df3\u8fc7\u5173\u952e\u5e27\u3002</p>\n<p>\u5f53\u7136\u8fd9\u4e9b\u90fd\u662f\u5f88\u7ec6\u5316\u7684\u95ee\u9898\u4e86\u3002</p>\n<h1>\u603b\u7ed3</h1>\n<p>\u6211\u503e\u5411\u4e8e\u7814\u7a76\u4e00\u4e2a\u4e1c\u897f\uff0c\u601d\u8003\u4ed6\u7684\u5168\u90e8\uff0c\u5bfb\u627e\u6700\u4f73\u7684\u8def\u5f84\u3002\n\u8fd9\u4e9b\u90fd\u662f\u6478\u9c7c\u7ed3\u679c\uff0c\u7b80\u5355\u5206\u4eab\u4e0b\u3002\u66f4\u6df1\u7684\u611f\u53d7\u8fd8\u9700\u8981\u5b9e\u8df5\u548c\u4ea4\u6d41\u3002</p>\n<h1>\u540e\u7eed</h1>\n<p>\u4e0a\u6587\u63d0\u5230\u6e38\u620f\u91cc\u9762\u6700\u65b0\u6d41\u884c ECS \u67b6\u6784\u3002ECS \u629b\u5f03\u4e86\u9762\u5411\u5bf9\u8c61\u7684\u601d\u60f3\uff0c\u628a\u540c\u7c7b\u6570\u636e\u6446\u653e\u5728\u4e00\u8d77\uff0c\u4eb2\u548c CPU \u8fd0\u884c\u673a\u5236\uff0c\u65b9\u4fbf\u5927\u89c4\u6a21\u5c5e\u6027\u904d\u5386\u3002</p>\n<p>ECS \u5e94\u8be5\u5982\u4f55\u7528 Ruby \u5b9e\u73b0\u5462\uff1f </p>\n<p><a href=\"https://mark24code.github.io/ruby/2022/07/26/%E8%BF%99%E5%BE%97%E4%BB%8E2048%E8%AF%B4%E8%B5%B7.html\" rel=\"nofollow\">\u6211\u7684\u535a\u5ba2</a></p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/868255", 
      "title": "\u7528 Ruby \u8bb2\u4ece\u521b\u4e1a\u5230 996 \u516c\u53f8\u7684\u6545\u4e8b(\u620f\u8bf4 master-worker \u6a21\u5f0f)", 
      "id": "https://www.v2ex.com/t/868255", 
      "date_published": "2022-07-23T13:31:33+00:00", 
      "content_html": "<h1>\u524d\u8a00</h1>\n<p>\u9605\u8bfb\u5927\u6982\u9700\u8981 20 \u5206\u949f\u3002</p>\n<p>\u5047\u8bbe\u4f60\u5e0c\u671b\u4e86\u89e3 \u7ebf\u7a0b\u3001\u7ebf\u7a0b\u6c60\u3001\u96c6\u7fa4\u6a21\u5f0f /Master-Worker \u6a21\u5f0f\u3001\u8c03\u5ea6\u5668\u3002</p>\n<p>\u9700\u8981\u4e86\u89e3 Ruby \u57fa\u672c\u7684\u7528\u6cd5\u548c\u9762\u5411\u5bf9\u8c61\u601d\u60f3\u3002</p>\n<p>\u672c\u6587\u620f\u8bf4\uff0c\u65e0\u987b\u4e25\u8083\u5bf9\u5f85\u3002\u52ff\u5bf9\u53f7\u5165\u5ea7\u3002\u4e2a\u4eba\u4e5f\u6ca1\u6709\u4e25\u8083\u89c2\u70b9\u3002\u4e2a\u4eba\u89c2\u70b9\u548c\u6240\u6709\u4eba\u6ca1\u6709\u5173\u7cfb\u3002</p>\n<p><a href=\"https://mark24code.github.io/ruby/2022/07/23/%E7%94%A8ruby%E6%9D%A5%E5%86%99%E4%B8%80%E4%B8%AA996%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F(master-worker).html\" rel=\"nofollow\">\u672c\u6587\u535a\u5ba2\u5730\u5740</a></p>\n<h2>\u5b8c\u6574\u4ee3\u7801\u793a\u4f8b</h2>\n<p><a href=\"https://github.com/Mark24Code/rb-master-worker-demo\" rel=\"nofollow\">github:rb-master-worker-demo</a></p>\n<h1>Master Worker \u6a21\u5f0f</h1>\n<p>MasterWorker \u6a21\u5f0f\uff0c\u4e5f\u6709\u7ffb\u8bd1\u6210\u4f5c\u96c6\u7fa4\u6a21\u5f0f\u3001\u4e5f\u53eb Master-Slave \u6a21\u5f0f\u3002</p>\n<blockquote>\n<p>Git \u4e0d\u8bb8\u4f7f\u7528 master \u4e86\uff0c\u6362\u6210\u4e86 main \uff0cMaster/Slave \u5177\u6709\u653f\u6cbb\u4e0d\u6b63\u786e\u7684\u6b67\u89c6\u8272\u5f69\u3002\u4e0d\u8fc7\u8fd9\u4e0d\u91cd\u8981\u4e86\u3002\u5176\u5b9e\u8fd9\u4e2a\u540d\u5b57\u5f88\u80fd\u8868\u8fbe\u8fd9\u4e2a\u6a21\u5f0f\u7684\u7279\u70b9\u3002</p>\n</blockquote>\n<p>\u4e3b\u8981\u601d\u60f3\u5c31\u662f\u7531\u4e00\u4e2a Master \u62bd\u8c61\u5bf9\u8c61\u6765\u8c03\u5ea6 Worker \u5bf9\u8c61\u6765\u5de5\u4f5c\u3002</p>\n<h2>Ruby \u6587\u5b66\u7f16\u7a0b\uff0c\u7528\u4ee3\u7801\u8bb2\u6545\u4e8b</h2>\n<p>\u5176\u5b9e\u8fd9\u4e5f\u975e\u5e38\u50cf\u73b0\u5b9e\u4e2d\u7684\u5de5\u4f5c\u6a21\u578b\u3002Ruby \u5929\u751f\u9762\u5411\u5bf9\u8c61\uff0c\u8868\u8fbe\u7684\u6587\u5b66\u6027\uff0c\u6211\u4eec\u53ef\u4ee5\u5f88\u65b9\u4fbf\u7684\u6765\u4f7f\u7528\u4ee3\u7801\u6a21\u62df\u8fd9\u79cd\u73b0\u5b9e\u60c5\u51b5\u3002\n\u6211\u4eec\u6765\u7528 Ruby \u6a21\u62df\u4e0b\u73b0\u5b9e\u4e2d\u8fd9\u79cd\u60c5\u51b5\uff0c\u987a\u4fbf\u5b66\u4e0b\u5982\u4f55\u5b9e\u73b0\u8fd9\u4e2a\u6a21\u5f0f\u3002</p>\n<h2>\u7ea6\u5b9a</h2>\n<p>\u4f1a\u51fa\u73b0\u51e0\u4e2a\u7c7b\uff1a</p>\n<ul>\n<li>Master \u4ee3\u8868 \u201c\u9886\u5bfc\u201d\uff0c\u4e0d\u5e72\u6d3b\uff0c\u4e3b\u8981\u5de5\u4f5c\u4efb\u52a1\u662f\u5206\u914d\u4efb\u52a1\uff0c\u8fd9\u662f Master \u7c7b\u7684\u7279\u5f81\u3002</li>\n<li>Worker \u4ee3\u8868 \u201c\u6253\u5de5\u4eba\u201d\uff0c\u5de5\u4f5c\u548c\u521b\u9020\u4ef7\u503c\u7684\u4e3b\u4f53\u3002\u4e3b\u8981\u4efb\u52a1\u5c31\u662f\u5e72\u6d3b\u3002</li>\n<li>Workshop \u4ee3\u8868 \u201c\u516c\u53f8\u201d\uff0c\u4e3b\u8981\u662f\u8d1f\u8d23\u63a5\u5355\u3002</li>\n</ul>\n<p>\u6545\u4e8b\u7684\u601d\u8def\uff1a</p>\n<p>\u6211\u4eec\u81ea\u5df1\u662f\u5ba2\u6237\uff0c\u628a\u201c\u4efb\u52a1\u201d\u8ba2\u5355\u4ea4\u7ed9\u201c\u516c\u53f8\u201d\uff0c\u8fd9\u4e9b\u4efb\u52a1\u4f1a\u8f6c\u4ea4\u7ed9\u201c\u9886\u5bfc\u201d\u624b\u4e2d\uff0c\u7136\u540e\u201c\u9886\u5bfc\u201d\u4f1a\u6392\u671f\uff0c\u628a\u5de5\u4f5c\u5e03\u7f6e\u7ed9\u201c\u6253\u5de5\u4eba\u201d\u3002\u6700\u7ec8\u201c\u6253\u5de5\u4eba\u201d\u4e50\u6b64\u4e0d\u75b2\u7684\u5b8c\u6210\u4efb\u52a1\u3002</p>\n<h2>\u5b9e\u73b0 \u6253\u5de5\u4eba Worker \u7c7b</h2>\n<h3>step1 \u7ed9\u5458\u5de5\u5de5\u53f7</h3>\n<p>\u9996\u5148\u6211\u4eec\u5efa\u7acb\u4e00\u4e2a Worker \u7c7b\uff0c\u6211\u4eec\u7ed9\u4ed6\u4e00\u4e2a\u540d\u5b57\u5c5e\u6027\u3002<code>attr</code> \u66b4\u9732\u51fa <code>name</code> \u5c5e\u6027\u3002</p>\n<pre><code class=\"language-ruby\"># Workshop.rb\n\nclass Worker\n  attr :name\n  def initialize(name)\n    @name = \"worker@#{name}\"\n  end\nend\n</code></pre>\n<p>\u6211\u4eec\u91c7\u7528 TDD \u65b9\u5f0f\u6765\u9010\u6b65\u5b9e\u73b0\u6211\u4eec\u7684\u60f3\u6cd5\uff1a</p>\n<pre><code class=\"language-ruby\">#Workshop_test.rb\nrequire 'minitest/autorun'\nrequire_relative '../lib/Workshop'\n\ndescribe Worker do\n  it \"check worker name\" do\n    w = Worker.new(\"ruby01\")\n    assert_equal w.name, \"worker@ruby01\"\n  end\nend\n</code></pre>\n<p>\u5f88\u5feb\uff0c\u6211\u4eec\u77e5\u9053\u8fd9\u540d\u6253\u5de5\u4eba\u4ed6\u53eb \u201cruby01\u201d \u5458\u5de5\u3002</p>\n<h2>step2 \u7ed9\u5458\u5de5 KPI/OKR</h2>\n<p>\u6211\u4eec\u4e0d\u5e0c\u671b\u6253\u5de5\u4eba\u6bcf\u6b21\u53ea\u80fd\u505a\u4e00\u4ef6\u4e8b\uff0c\u4f60\u5fc5\u987b\u5f97\u63a8\u7740\u4ed6\u624d\u80fd\u5de5\u4f5c\u3002\u4ed6\u6700\u597d\u5b66\u4f1a\u201c\u6210\u957f\u201d\u4f1a\u81ea\u5df1\u52aa\u529b\u7684\u5de5\u4f5c\u3002\n\u5176\u5b9e\u5c31\u662f\u4e00\u5806\u4efb\u52a1\uff0c\u6211\u4eec\u5e0c\u671b\u4ed6\u4eec\u4e00\u76f4\u5fd9\u3002\u7ed9\u4ed6 N \u4ef6\u4e8b\u60c5\uff0c\u4ed6\u4e00\u4e2a\u4e00\u4e2a\u81ea\u5df1\u505a\u3002\n\u6211\u4eec\u8981\u7ed9\u4ed6\u4e00\u4e2a\u76ee\u6807\uff0c\u4e5f\u5c31\u662f KPI \u6216\u8005 OKR \u968f\u4fbf\u5427\uff0c\u5b9e\u9645\u4e0a\u8fd9\u662f\u4e00\u4e2a\u961f\u5217\u5bf9\u5427\u3002\u6211\u4eec\u7528\u961f\u5217\u5b9e\u73b0\u3002</p>\n<pre><code class=\"language-ruby\">require 'thread'\n\nclass Worker\n  attr :name\n  def initialize(name)\n    @name = \"worker@#{name}\"\n    @queue = Queue.new\n    @thr = Thread.new { perfom }\n  end\n\n  def &lt;&lt;(job)\n    @queue.push(job)\n  end\n\n  def join\n    @thr.join\n  end\n\n  def perfom\n    while (job = @queue.deq)\n      break if job == :done\n      puts \"worker@#{name}: job:#{job}\"\n      job.call\n    end\n  end\n\n  def size\n    @queue.size\n  end\nend\n</code></pre>\n<p>\u73b0\u5728\u6253\u5de5\u4eba\u53d8\u5f97\u5145\u5b9e\u4e86\u8bb8\u591a\uff0c\u4ed6\u81ea\u4ece\u6765\u4e86\u516c\u53f8\u57f9\u8bad\u4e4b\u540e\uff0c\u5c31\u62e5\u6709\u4e86\u5f88\u591a\u5c5e\u6027\u548c\u65b9\u6cd5\u3002</p>\n<ul>\n<li>\u5c5e\u6027\u8bf4\u660e\uff1a</li>\n</ul>\n<p><code>@queue</code> \u5c31\u662f\u4ed6\u7684 OKR \u6e05\u5355\uff0c\u4ed6\u5fc5\u987b\u5b8c\u6210\u6240\u6709\u7684\u5de5\u4f5c\u4efb\u52a1\u3002</p>\n<p><code>@thr</code> \u610f\u601d\u662f thread \u7f29\u5199\uff0c\u8fd9\u91cc\u662f\u4f1a\u4f7f\u7528\u4e00\u4e2a\u7ebf\u7a0b\u6765\u8c03\u7528 <code>perform</code> \u6211\u4eec\u5728\u7528\u7ebf\u7a0b\u6a21\u62df\u6253\u5de5\u4eba\u5e72\u6d3b\u8fd9\u4ef6\u4e8b\u3002\u53ef\u4ee5\u7406\u89e3\u4e3a <code>@thr</code> \u5c31\u662f\u6253\u5de5\u4eba\u7684\u7075\u9b42\u3002</p>\n<ul>\n<li>\u65b9\u6cd5\u8bf4\u660e\uff1a</li>\n</ul>\n<p><code>&lt;&lt;</code> \u662f\u4e00\u4e2a push \u65b9\u6cd5\u7684\u8bed\u6cd5\u7cd6\uff0c\u5c31\u7ed9\u7ed9\u81ea\u5df1\u7684 OKR \u91cc\u6dfb\u52a0\u4efb\u52a1\u3002</p>\n<p><code>perform</code>\n\u53ef\u80fd\u8981\u8bf4\u4e0b perform \u65b9\u6cd5, \u8fd9\u91cc\u662f \u201c\u8fd0\u884c\u201d\u7684\u610f\u601d\u54c8\uff0c\u4e0d\u662f\u201c\u8868\u6f14\u201d :P \u3002\n\u6253\u5de5\u4eba\u600e\u4e48\u5e72\u6d3b\u5462\uff1f\u8fd9\u5f97\u8bf4\u9053\u8bf4\u9053\u3002\u6211\u4eec\u5f97\u6307\u5bfc\u4ed6\u5982\u4f55\u201c\u6210\u957f\u201d\u3002</p>\n<p>\u6211\u4eec\u524d\u9762\u8bf4\u4e86 <code>@queue</code> \u5c31\u662f\u4ed6\u7684 OKR, \u4ed6\u5fc5\u987b\u4ece\u81ea\u5df1\u7684 OKR \u4e2d\u53d6\u51fa\u4efb\u52a1\u7136\u540e\u6267\u884c\u3002\u8fd9\u91cc\u6211\u7528\u4e86 <code>job.call</code>\u3002\n\u6697\u793a\uff0c\u8fd9\u5fc5\u987b\u662f\u4e00\u4e2a callable \u5bf9\u8c61\uff0c\u5728 ruby \u91cc\u4e5f\u5c31\u662f\u62e5\u6709 <code>call</code> \u65b9\u6cd5\u7684\u5bf9\u8c61\u3002\u53ef\u4ee5\u662f lambda \u3001\u6216\u8005\u5b9e\u73b0 call \u7684\u3002\n\u8fd9\u4e5f\u5f88\u5408\u7406\uff0c\u9700\u6c42\u5fc5\u987b\u80fd\u505a\u624d\u4f1a\u505a\u3002\u6ca1\u6cd5\u505a\u7684\u9700\u6c42\uff0c\u505a\u4e0d\u4e86\u5c31\u662f\u505a\u4e0d\u4e86\u3002</p>\n<p>\u4f46\u662f\u5982\u679c\u7ed9\u4e86\u4e00\u4e2a <code>:done</code> \u53e6\u8bf4\u3002\u5faa\u73af\u4f1a\u7ed3\u675f\uff0c\u8fd9\u4e2a\u7ebf\u7a0b\u4f1a\u6d88\u5931\u3002(\u88c1\u5458\u4e86 :P)</p>\n<pre><code class=\"language-ruby\">  def perfom\n    while (job = @queue.deq)\n      break if job == :done\n      puts \"worker@#{name}: job:#{job}\"\n      job.call\n    end\n  end\n</code></pre>\n<blockquote>\n<p>\u5176\u5b9e Queue \u8fd9\u4e2a\u5bf9\u8c61\u5f88\u6709\u610f\u601d\uff0cRuby \u505a\u4e86\u4e00\u4e9b\u5de5\u4f5c\u3002Queue \u5728\u7a7a\u7684\u65f6\u5019\uff0c\u865a\u62df\u673a\u4f1a\u8ba9\u7ebf\u7a0b\u8fdb\u5165\u7761\u7720\u7b49\u5f85\u3002\u5982\u679c\u961f\u5217\u91cc\u6709\u4efb\u52a1\uff0c\u5c31\u4f1a\u7ee7\u7eed\u5de5\u4f5c\u3002Ruby \u5f88\u8d34\u5fc3\uff0c\u679c\u7136\u662f\u7a0b\u5e8f\u5458\u7684\u597d\u670b\u53cb\u554a\u3002 \u5176\u5b9e\u6211\u4e0d\u77e5\u9053\u5176\u4ed6\u8bed\u8a00\u4ec0\u4e48\u6837\uff0c\u61d2\u5f97\u67e5\u4e86\u3002</p>\n</blockquote>\n<p><code>join</code> \u65b9\u6cd5\u662f\u4e00\u4e2a Thread \u7684\u7ebf\u7a0b\u65b9\u6cd5\uff0c\u4e3b\u8981\u7684\u4f5c\u7528\u662f\u544a\u8bc9\u4e3b\u7ebf\u7a0b\u4f60\u8981\u7b49\u5f85\u6bcf\u4e00\u4e2a\u5b50\u7ebf\u7a0b\uff08\u81ea\u5df1\uff09\u7684\u5b8c\u6210\u3002\u5982\u679c\u4e0d\u5199\u8fd9\u53e5\uff0c\u4e3b\u7ebf\u7a0b\u5982\u679c\u6bd4\u6240\u6709\u5b50\u7ebf\u7a0b\u63d0\u524d\u7ed3\u675f\u3002\u90a3\u4e48\u5b50\u7ebf\u7a0b\u4f1a\u88ab\u5168\u90e8\u5173\u95ed\u3002\u7b80\u800c\u8a00\u4e4b <code>join</code> \u5c31\u662f\u540c\u6b65\u7b49\u5f85\u7ebf\u7a0b\u7ed3\u679c\u3002</p>\n<p>\u8ba9\u6211\u4eec\u6765\u770b\u770b TDD:</p>\n<p>\u6211\u4eec\u53ef\u4ee5\u52a0\u4e00\u6bb5\u9a8c\u8bc1\u5de5\u53f7 ruby02 \u7684\u6253\u5de5\u4eba\u662f\u4e0d\u662f\u5982\u671f\u7684\u5b8c\u6210\u4e86\u5de5\u4f5c\u3002</p>\n<pre><code class=\"language-ruby\"># ....\n  it \"check worekr do sth job\" do\n    w = Worker.new(\"ruby02\")\n\n    finished = []\n    w &lt;&lt; lambda { puts \"do job 1\"; finished.push \"job1\"}\n    w &lt;&lt; lambda { puts \"do job 2\"; finished.push \"job2\"}\n    w &lt;&lt; :done\n    w.join\n\n    assert_equal finished, [\"job1\",\"job2\"]\n  end\n\n# ....\n</code></pre>\n<p>\u5176\u5b9e\u5230\u8fd9\u91cc\uff0c\u4e00\u4e2a\u5408\u683c\u7684\u6253\u5de5\u4eba\u5c31\u6253\u9020\u5b8c\u6bd5\u4e86\u3002\u6253\u5de5\u4eba\u5f88\u7b80\u5355\uff0c\u53ea\u8981\u5403\u82e6\u8010\u52b3\uff0c\u4e00\u5207\u90fd OK \u3002\n\u4e0b\u9762\u6211\u4eec\u8981\u5b9e\u73b0\u4e0b Workshop \u516c\u53f8\u7c7b\u3002</p>\n<h2>\u5b9e\u73b0 \u516c\u53f8 Workshop \u7c7b</h2>\n<h3>\u5728\u6b64\u4e4b\u524d\uff0c\u6211\u4eec\u5148\u5b9e\u73b0\uff1a\u521b\u4e1a\u516c\u53f8 MiniWorkshop \u7c7b</h3>\n<p>\u5176\u5b9e\u6211\u6253\u7b97\u8fc7\u6e21\u4e0b\uff0c\u9996\u5148\u5b9e\u73b0\u4e00\u4e2a \u201c\u521b\u4e1a\u516c\u53f8\u201d <code>MiniWorkshop</code>\u3002\n\u521b\u4e1a\u516c\u53f8\u521a\u8d77\u6b65\uff0c\u4e00\u822c\u662f\u53ea\u6709\u201c\u6253\u5de5\u4eba\u201d\uff0c\u6ca1\u6709\u771f\u6b63\u610f\u4e49\u4e0a\u7684\u4e2d\u5c42\u51fa\u73b0\u3002\n\u8fd9\u4e00\u65f6\u671f\u975e\u5e38\u7b80\u5355\uff0c\u4f0a\u7538\u56ed\u65f6\u671f\u3002\u6709\u6d3b\u5927\u5bb6\u4e00\u8d77\u5e72\uff0c\u5927\u5bb6\u90fd\u662f\u5144\u5f1f\u3002</p>\n<pre><code class=\"language-ruby\">class MiniWorkshop\n  def initialize(count)\n    @worker_count = count # \u6253\u5de5\u4eba\u6570\u91cf\n    @workers = @worker_count.times.map do |i| # \u6839\u636e\u6570\u91cf\u751f\u6210(\u62db\u8058)\u6253\u5de5\u4eba\n      Worker.new(i) # \u7ed9\u4e2a\u5de5\u53f7\n    end\n  end\n\n  # \u521d\u521b\u516c\u53f8\u5206\u914d\u4efb\u52a1\n  def &lt;&lt;(job)\n    if job == :done\n      @workers.map {|m| m &lt;&lt; job}\n    else\n      # \u968f\u673a\u9009\u62e9\u4e00\u4e2a\u6253\u5de5\u4eba\uff0c\u63a5\u6d3b\n      @workers.sample &lt;&lt; job\n    end\n  end\n\n  def join\n    @workers.map {|m| m.join}\n  end\nend\n</code></pre>\n<p>\u8fd9\u91cc\u53ef\u80fd\u8bf4\u4e0b</p>\n<pre><code class=\"language-ruby\">  def &lt;&lt;(job)\n    if job == :done\n      @workers.map {|m| m &lt;&lt; job}\n    else\n      # \u968f\u673a\u9009\u62e9\u4e00\u4e2a\u6253\u5de5\u4eba\uff0c\u63a5\u6d3b\n      @workers.sample &lt;&lt; job\n    end\n  end\n</code></pre>\n<p>\u8fd9\u91cc\u5e72\u6d3b\u7684\u6a21\u5f0f\u53ef\u80fd\u4e0d\u597d\uff0c\u56e0\u4e3a\u6211\u4eec\u7adf\u7136 <code>Array#sample</code> \u65b9\u5f0f\u3002\u8fd9\u662f\u4e00\u4e2a\u968f\u673a\u65b9\u6cd5\u3002\u968f\u673a\u9009\u62e9\u4e00\u4e2a\u3002\n\u770b\u4f3c\u4e0d\u5408\u7406\uff0c\u5b9e\u9645\u4e0a\u4e5f\u5408\u60c5\u5408\u7406\u3002</p>\n<p>\u521b\u4e1a\u516c\u53f8\u521d\u671f\u867d\u7136\u662f\u8349\u6839\uff0c\u53ef\u662f\u5927\u5bb6\u54ea\u4e2a\u4e0d\u662f\u5927\u4f6c\u3002\u6240\u4ee5\u6d3b\u6765\u4e86\u8c01\u90fd\u884c\uff0c\u95ee\u9898\u4e0d\u5927\u3002</p>\n<p>\u6ca1\u4e8b\u6211\u4eec\u540e\u9762\u518d\u6539\u8fdb\u597d\u4e86\u3002</p>\n<p>TDD:</p>\n<p>\u6211\u4eec\u7684\u5355\u5143\u6d4b\u8bd5\u5176\u5b9e\u63cf\u8ff0\u4e86\u4e00\u4e2a\u6545\u4e8b\u3002\u4e00\u5bb6\u521b\u4e1a\u516c\u53f8\uff0c\u53ea\u6709 2 \u4e2a\u4eba\u3002\u63a5\u5230\u4e86\u4e00\u4e2a\u8ba2\u5355\u662f 4 \u4e2a\u5de5\u4f5c\u5185\u5bb9\u3002</p>\n<pre><code class=\"language-ruby\"># ...\n  it \"check MiniWorkshop work\" do\n    ws = MiniWorkshop.new(2)\n\n    finished = []\n    ws &lt;&lt; lambda { puts \"job1\"; finished.push \"job1\"}\n    ws &lt;&lt; lambda { puts \"job2\"; finished.push \"job2\"}\n    ws &lt;&lt; lambda { puts \"job3\"; finished.push \"job3\"}\n    ws &lt;&lt; lambda { puts \"job4\"; finished.push \"job4\"}\n    ws &lt;&lt; :done\n\n    ws.join\n\n    assert_equal finished.size, 4\n  end\n# ...\n</code></pre>\n<p>\u6211\u4eec\u56de\u8fc7\u5934\u518d\u770b <code>MiniWorkshop</code> \u7c7b\uff0c\u521d\u59cb\u5316\u7684\u65f6\u5019\u521b\u5efa\u4e86\u4e24\u4e2a\u5458\u5de5\u3002\u4efb\u52a1\u6765\u4e86\u5c31\u968f\u673a\u5206\u914d\u7ed9\u4e00\u4e2a\u5458\u5de5\u3002\n\u5f88\u7b26\u5408\u5c0f\u4f5c\u574a\u7684\u6a21\u5f0f\u3002</p>\n<h2>\u5b9e\u73b0\u4e0a\u5e02\u516c\u53f8</h2>\n<p>\u516c\u53f8\u53d8\u5927\u4e86\uff0c\u5c31\u4e0d\u6b62 2 \u4e2a\u5458\u5de5\u4e86\u3002\u53ef\u80fd\u56db\u4e94\u767e\u53f7\uff0c\u968f\u673a\u4ea4\u7ed9\u4e00\u4e2a\u5458\u5de5\uff0c\u4e0d\u73b0\u5b9e\u3002\u4e2d\u5c42\u7ba1\u7406\u51fa\u73b0\u3002\u4e2d\u5c42\u51fa\u73b0\u610f\u5473\u7740\u6211\u4eec\u516c\u53f8\u7684\u7c7b\u4e5f\u8981\u8fdb\u884c\u6539\u53d8\uff0c\u516c\u53f8\u9700\u8981\u6539\u9769\u3002</p>\n<p>\u6211\u4eec\u5148\u5b9e\u73b0\u4e00\u4e2a\u6539\u9769\u4e4b\u540e\u7684 Workshop \u516c\u53f8\u7c7b\u3002</p>\n<pre><code class=\"language-ruby\">class Workshop\n  def initialize(count, master_name)\n    @worker_count = count\n    @workers = @worker_count.times.map do |i|\n      Worker.new(i)\n    end\n    @master = Master.new(@workers) # \u65b0\u589e\u89d2\u8272\n  end\n\n  def &lt;&lt;(job)\n    if job == :done\n      @workers.map {|m| m &lt;&lt; job}\n    else\n      @master.assign(job) # master \u5206\u914d\u4efb\u52a1\n    end\n  end\n\n  def join\n    @workers.map {|m| m.join}\n  end\nend\n</code></pre>\n<p>\u53ef\u4ee5\u770b\u5230\uff0c\u6211\u4eec\u5728\u521d\u59cb\u5316\u51fd\u6570\u91cc\u65b0\u589e\u4e86 <code>@master</code> \u4ed6\u63a5\u53d7 <code>@workers</code> \u4f5c\u4e3a\u53c2\u6570\u3002\u6bd5\u7adf\u9886\u5bfc\u8981\u70b9\u5175\u554a\u3002</p>\n<p><code>&lt;&lt;</code>\u65b9\u6cd5\u4e5f\u8fdb\u884c\u4e86\u6539\u8fdb\uff0c\u7531\u4ee5\u524d\u7684 \u76f4\u63a5\u8ba9 @workers \u63a5\u6536\u4efb\u52a1\uff0c\u53d8\u6210 <code>@master.assign</code> \u5206\u914d\u4efb\u52a1\u3002</p>\n<p>\u8ba9\u6211\u4eec\u6765\u770b\u4e0b Master \u7c7b</p>\n<pre><code class=\"language-ruby\">class Master\n  def initialize(workers)\n    @workers = workers\n  end\n\n  def assign(job)\n    @workers.sort{|a,b| a.size &lt;=&gt; b.size}.first &lt;&lt; job\n  end\nend\n</code></pre>\n<p>\u5176\u5b9e\u4e5f\u4e0d\u590d\u6742\u3002\u6211\u4eec\u4fdd\u6301\u4e86 @workers \u7684\u6307\u9488\uff0c <code>assign</code> \u65b9\u6cd5\u66f4\u50cf\u662f\u628a\u4ee5\u524d\u5206\u914d\u7684\u903b\u8f91\u63a5\u8fc7\u6765\u5b9e\u73b0\u4e86\u4e00\u904d\u3002</p>\n<p>\u8fd9\u6b21\u6211\u4eec\u6539\u4e86\u5206\u914d\u4efb\u52a1\u7684\u65b9\u5f0f\uff0c\u6211\u4eec\u8981\u6839\u636e <code>Worker#size</code> \u5fd9\u788c\u7a0b\u5ea6\u6765\u5206\u914d\u4efb\u52a1\u3002</p>\n<p>\u6bd5\u7adf\u561b\uff0c\u9886\u5bfc\u6709\u4e2a\u65b9\u6cd5\u8bba\uff0c\u4f1a\u6bd4\u5c0f\u4f5c\u574a\u9ad8\u7ea7\u5f88\u591a\u3002</p>\n<h2>\u591a\u91cd\u9886\u5bfc</h2>\n<p>\u4e00\u4e2a\u9886\u5bfc\u5c31\u8db3\u591f\u4e86\u4e48\uff1f\u4e0d\u3002</p>\n<p>\u73b0\u5b9e\u4e2d\u6211\u4eec\u89c1\u8fc7\u5f62\u5f62\u8272\u8272\u7684\u9886\u5bfc\uff0c\u6709\u7684\u662f\u81ea\u5df1\u57f9\u517b\uff0c\u6709\u7684\u662f\u7559\u8fc7\u6d0b\uff0c\u6709\u7684\u662f\u5927\u5382\u7a7a\u964d\u3002\u4ed6\u4eec\u62e5\u6709\u4e0d\u540c\u7684\u201c\u65b9\u6cd5\u8bba\u201d\uff0c\u4e5f\u5c31\u662f <code>Master#assign</code> \u7684\u65b9\u5f0f\u53ef\u80fd\u4e0d\u540c\u3002</p>\n<p>\u6211\u4eec\u7ed9\u516c\u53f8\u518d\u52a0\u4e24\u4e2a\u9886\u5bfc\u3002</p>\n<h3>\u65e0\u9650\u65b9\u6cd5\u8bba</h3>\n<p>996ICU \u9886\u5bfc\uff1a</p>\n<p>\u6211\u4eec\u4f7f\u7528\u4e86 <code>Array#cycle</code> \u7684\u65b9\u5f0f\uff0c\u8fd9\u662f\u4e00\u4e2a\u8fed\u4ee3\u5668\u3002\u6bd4\u5982 <code>[1,2,3].cycle</code> \u6bcf\u6b21 <code>.next</code> \u4f1a\u4ea7\u751f <code>1 \u30012 \u30013 \u30011 \u30012 \u30013 \u30011 \u30012 \u30013 .....</code> \u65e0\u9650\u8f6e\u8bad\u3002</p>\n<p>\u8fd9\u4e2a\u65b9\u6cd5\u8bba\u5c31\u662f 996 \u65b9\u6cd5\u8bba\uff0c\u53ea\u8981\u5e72\u4e0d\u6b7b\u5c31\u5f80\u6b7b\u91cc\u5e72\u3002\u4eba\u6d77\u6218\u672f\uff0c\u628a\u4eba\u8f6e\u756a\u586b\u4e0a\u3002</p>\n<pre><code class=\"language-ruby\">class ICU996Master\n  def initialize(workers)\n    @current_worker = workers.cycle # \u8fed\u4ee3\u5668\n  end\n\n  def assign(job)\n    @current_worker.next &lt;&lt; job\n  end\nend\n</code></pre>\n<h3>\u5206\u7ec4\u4efb\u52a1\u65b9\u6cd5\u8bba</h3>\n<p>\u7b49\u6211\u4eec\u7684\u516c\u53f8\u53d8\u5927\u4e86\uff0c\u6211\u4eec\u7684\u4e1a\u52a1\u4e5f\u4f1a\u53d8\u5f97\u4e30\u5bcc\uff0c\u4efb\u52a1\u4e0d\u662f\u90a3\u4e48\u5355\u4e00\u3002\u5f88\u591a\u5de5\u4f5c\u8981\u6dfb\u52a0\u4e0a\u7ec4\u522b group_id \uff0c\u5206\u95e8\u522b\u7c7b\u7684\u4ea4\u7ed9\u4e0d\u540c\u5de5\u79cd\u7684\u6253\u5de5\u4eba\uff0c\u6bd4\u5982 \u5f00\u53d1\u3001\u4ea7\u54c1\u3001\u6d4b\u8bd5\u3001\u8bbe\u8ba1\u3001\u8fd0\u8425\u3002</p>\n<pre><code class=\"language-ruby\">\nclass GroupMaster\n  GROUPS = [:group1, :group2, :group3]\n\n  def initialize(workers)\n    @workers = {}\n    workers_per_group = workers.length / GROUPS.size\n    workers.each_slice(workers_per_group).each_with_index do |slice, index|\n      group_id = GROUPS[index]\n      @workers[group_id] = slice\n    end\n  end\n\n  def assign(job)\n    worker = @workers[job.group].sort_by(&amp;:size).first\n    worker &lt;&lt; job\n  end\nend\n</code></pre>\n<p>\u7136\u540e\u6211\u4eec\u53ef\u4ee5\u628a\u4e0d\u540c\u98ce\u683c\u7684\u9886\u5bfc\u73ed\u5b50\u96c6\u4e2d\u8d77\u6765</p>\n<pre><code class=\"language-ruby\">Masters = {\n  normal: NormalMaster,\n  ICU996: ICU996Master,\n  group: GroupMaster\n}\n</code></pre>\n<p>\u6211\u4eec\u6539\u9020\u4e0b <code>Workshop</code> \u6bd5\u7adf\u8fd9\u4e2a\u8bcd\u662f\u4e00\u4e2a \u5de5\u4f5c\u5ba4\u7684\u610f\u601d\uff0c\u5176\u5b9e\u662f\u4e2a\u5c0f\u90e8\u95e8\u3002</p>\n<p>\u6211\u4eec\u6539\u9020\u4e4b\u540e\uff0c\u6211\u4eec\u7684\u5c0f\u90e8\u95e8\u53ef\u4ee5\u6309\u7167\u98ce\u683c\u4e0d\u540c\u7684\u9886\u5bfc\u8fdb\u884c\u5206\u6d3e\u5de5\u4f5c\u3002</p>\n<pre><code class=\"language-ruby\">class Workshop\n  def initialize(count, master_name) # \u65b0\u589e master_name \u6307\u5b9a\n    @worker_count = count\n    @workers = @worker_count.times.map do |i|\n      Worker.new(i)\n    end\n    # \u5339\u914d master\n    @master = Masters[master_name].new(@workers)\n  end\n\n  def &lt;&lt;(job)\n    if job == :done\n      @workers.map {|m| m &lt;&lt; job}\n    else\n      @master.assign(job)\n    end\n  end\n\n  def join\n    @workers.map {|m| m.join}\n  end\nend\n</code></pre>\n<p>\u6211\u4eec\u6765\u770b\u770b\u4e0d\u540c\u90e8\u95e8\u7684 TDD</p>\n<pre><code class=\"language-ruby\">  it \"check Workshop@ normal master\" do\n    ws = Workshop.new(4, :normal)\n\n    finished = []\n    ws &lt;&lt; lambda { puts \"job1\"; finished.push \"job1\"}\n    ws &lt;&lt; lambda { puts \"job2\"; finished.push \"job2\"}\n    ws &lt;&lt; lambda { puts \"job3\"; finished.push \"job3\"}\n    ws &lt;&lt; lambda { puts \"job4\"; finished.push \"job4\"}\n    ws &lt;&lt; :done\n\n    ws.join\n\n    assert_equal finished.size, 4\n  end\n\n  it \"check Workshop@ ICU996 master\" do\n    ws = Workshop.new(4, :ICU996)\n\n    finished = []\n    ws &lt;&lt; lambda { puts \"job1\"; finished.push \"job1\"}\n    ws &lt;&lt; lambda { puts \"job2\"; finished.push \"job2\"}\n    ws &lt;&lt; lambda { puts \"job3\"; finished.push \"job3\"}\n    ws &lt;&lt; lambda { puts \"job4\"; finished.push \"job4\"}\n    ws &lt;&lt; :done\n\n    ws.join\n\n    assert_equal finished.size, 4\n  end\n\n  it \"check Workshop@ group master\" do\n    ws = Workshop.new(4, :group)\n\n    class GroupJob\n      def initialize(group_id, &amp;b)\n        @group_id = group_id\n        @blk = b\n      end\n\n      # \u4efb\u52a1\u5206\u7ec4\n      def group\n        \"group#{@group_id}\".to_sym\n      end\n\n      def call\n\n        @blk.call(@group_id)\n      end\n    end\n\n    finished = []\n    ws &lt;&lt; GroupJob.new(1) { |group_id| finished.push(group_id)}\n    ws &lt;&lt; GroupJob.new(2) { |group_id| finished.push(group_id)}\n    ws &lt;&lt; GroupJob.new(3) { |group_id| finished.push(group_id)}\n    ws &lt;&lt; GroupJob.new(1) { |group_id| finished.push(group_id)}\n    ws &lt;&lt; :done\n\n    ws.join\n\n    assert_equal finished.size, 4\n  end\n\n</code></pre>\n<h1>\u603b\u7ed3 Master-Worker \u6a21\u5f0f</h1>\n<p>\u597d\u5427\uff0c\u620f\u8bf4\u4e0d\u662f\u80e1\u8bf4\uff0c\u6539\u7f16\u4e0d\u662f\u4e71\u7f16\u3002</p>\n<p>\u6211\u4eec\u4ece\u73b0\u5b9e\u7684\u6545\u4e8b\u4e2d\u8d70\u51fa\u6765\u3002</p>\n<ul>\n<li>\u8c03\u5ea6\u5668(Scheduler)</li>\n</ul>\n<p>\u5176\u5b9e\u5728\u8fd9\u91cc Master \u7c7b\uff0c\u53ef\u80fd\u4f1a\u88ab\u53eb\u505a <code>Scheduler</code> \u5373\u8c03\u5ea6\u5668\u3002\u5185\u90e8\u7684\u65b9\u6cd5\u4e3b\u8981\u662f\u4f7f\u7528\u4e0d\u540c\u7684\u7b56\u7565\u6765\u5206\u914d\u4efb\u52a1\u3002</p>\n<p>\u800c\u4e0d\u540c\u7684 Master \u5b9e\u73b0\u7684 assign \u65b9\u6cd5\u5c31\u662f \u8c03\u5ea6\u7b56\u7565\u3002</p>\n<ul>\n<li>\u7ebf\u7a0b\u6c60(Thread Pool)</li>\n</ul>\n<p>Workshop \u5176\u5b9e \u6301\u6709 <code>@workers</code>\uff0c\u4e5f\u5c31\u662f\u8bf4\u6c47\u805a\u4e86\u5b9e\u9645\u5de5\u4f5c\u7ebf\u7a0b\u7684\u5bf9\u8c61\u3002\u4ed6\u4eec\u53ef\u80fd\u4f1a\u6709\u53e6\u4e00\u4e2a\u540d\u5b57 \u2014\u2014 \u7ebf\u7a0b\u6c60\uff08 Thread Pool)</p>\n<p>\u6545\u4e8b\u8bb2\u5b8c\u4e86\uff0c\u4f60\u6709\u6ca1\u6709\u5b66\u4f1a\u5462\uff1f :D</p>\n<h1>\u793a\u4f8b\u4ee3\u7801\uff1a</h1>\n<ul>\n<li><a href=\"https://github.com/Mark24Code/rb-master-worker-demo\" rel=\"nofollow\">rb-master-worker-demo</a></li>\n</ul>\n<h1>\u53c2\u8003\u8d44\u6599\uff1a</h1>\n<ul>\n<li><a href=\"https://devdocs.io/ruby%7E3.1/thread\" rel=\"nofollow\">Ruby3 Doc: Thread</a></li>\n<li><a href=\"https://devdocs.io/ruby%7E3.1/thread/queue\" rel=\"nofollow\">Ruby3 Doc: Thread#Queue</a></li>\n<li><a href=\"https://hspazio.github.io/2017/worker-pool/\" rel=\"nofollow\">worker-pool</a></li>\n</ul>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/833872", 
      "title": "Sinatra \u6e90\u7801\u5206\u6790 (\u4e00):set \u7cfb\u7edf\u5de5\u4f5c\u539f\u7406", 
      "id": "https://www.v2ex.com/t/833872", 
      "date_published": "2022-02-14T14:59:28+00:00", 
      "content_html": "<p><a href=\"https://mark24code.github.io/ruby/sinatra/2022/02/14/Sinatra%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90(%E4%B8%80)-set%E7%B3%BB%E7%BB%9F%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.html\" rel=\"nofollow\">\u6211\u7684 BLOG</a></p>\n<h1>\u524d\u8a00</h1>\n<p>\u5927\u5bb6\u597d\uff0c\u6211\u662f MARK24 \u3002\u53ef\u4ee5\u53eb\u6211 MARK \u3002\u8fd9\u662f\u6211\u7814\u7a76 Sinatra \u7684\u7b14\u8bb0\u3002</p>\n<p>\u9605\u8bfb\u8fc7\u7a0b\u5927\u7ea6 10 \u5206\u949f\u3002</p>\n<p>\u57fa\u4e8e Sinatra 2.1.0 \u8fdb\u884c\u8ba8\u8bba</p>\n<p><a href=\"https://github.com/Mark24Code/sinatra-code-review\" rel=\"nofollow\">Sinatra v2.1.0 Fork \u4ee3\u7801\u5730\u5740</a></p>\n<h1>Sinatra set \u4ecb\u7ecd</h1>\n<p>set \u7cfb\u7edf\u53ef\u4ee5\u8ba9 Sinatra \u5728\u81ea\u8eab\u81ea\u7531\u7684\u5b9a\u4e49 \u8bbe\u7f6e\u76f8\u5173\u7684\u53d8\u91cf\u3002</p>\n<p>\u6bd4\u5982\u5b9a\u4e49\u6a21\u677f\u6240\u5728\uff1a</p>\n<pre><code class=\"language-ruby\">set :views, settings.root + '/templates'\n</code></pre>\n<p>\u5b9a\u4e49 session secret\uff1a</p>\n<pre><code class=\"language-ruby\">set :session_secret, ENV.fetch('SESSION_SECRET') { SecureRandom.hex(64) }\n</code></pre>\n<p>\u7b49\u7b49\uff0c\u975e\u5e38\u81ea\u7531\u4e14\u7075\u6d3b\u3002</p>\n<p>set \u7cfb\u7edf\u8fd9\u90e8\u5206\u7684\u6e90\u7801\u6070\u5de7\u662f\u53ef\u4ee5\u7b80\u5355\u4fee\u6539\u4e4b\u540e\u72ec\u7acb\u5de5\u4f5c\u7684\u3002\u6458\u8981\u5982\u4e0b\uff1a</p>\n<pre><code class=\"language-ruby\"># https://github.com/Mark24Code/sinatra-code-review/blob/master/lib/sinatra/base.rb#L1267\n\n\ndef define_singleton(name, content = Proc.new)\n  singleton_class.class_eval do\n    undef_method(name) if method_defined? name\n    String === content ? class_eval(\"def #{name}() #{content}; end\") : define_method(name, &amp;content)\n  end\nend\n\n\ndef set(option, value = (not_set = true), ignore_setter = false, &amp;block)\n  raise ArgumentError if block and !not_set\n  value, not_set = block, false if block\n\n  if not_set\n    raise ArgumentError unless option.respond_to?(:each)\n    option.each { |k,v| set(k, v) }\n    return self\n  end\n\n  if respond_to?(\"#{option}=\") and not ignore_setter\n    return __send__(\"#{option}=\", value)\n  end\n\n  setter = proc { |val| set option, val, true }\n  getter = proc { value }\n\n  case value\n  when Proc\n    getter = value\n  when Symbol, Integer, FalseClass, TrueClass, NilClass\n    getter = value.inspect\n  when Hash\n    setter = proc do |val|\n      val = value.merge val if Hash === val\n      set option, val, true\n    end\n  end\n\n  define_singleton(\"#{option}=\", setter)\n  define_singleton(option, getter)\n  # \u539f\u59cb\u4ee3\u7801\u653e\u5728\u4e00\u4e2a\u7c7b\u4e2d\uff0c \u5982\u679c\u6211\u4eec\u60f3\u653e\u5728\u5355\u6587\u4ef6\u6267\u884c\uff0c\u9700\u8981 \u6539\u5199\u4e3a `self.class.method_defined?` \u8c03\u7528\u5230\u65b9\u6cd5\n  # define_singleton(\"#{option}?\", \"!!#{option}\") unless method_defined? \"#{option}?\"\n  define_singleton(\"#{option}?\", \"!!#{option}\") unless self.class.method_defined? \"#{option}?\"\n  self\nend\n</code></pre>\n<h1>set \u6e90\u7801\u5206\u6790</h1>\n<p>Sinatra \u5185\u90e8\u5b9e\u73b0\u4e86\u4e00\u5957 \u914d\u7f6e\u7cfb\u7edf\uff0c\u57fa\u4e8e\u4e00\u4e2a DSL \u8bed\u6cd5 set \u3002 \u8fd9\u662f Sinatra Class \u90e8\u5206\u521d\u59cb\u5316\u4e4b\u540e\u552f\u4e00\u7684\u521d\u59cb\u5316\u7684 DSL \u3002Sinatra \u6ca1\u6709\u505a\u5f88\u591a\u590d\u6742\u7684\u524d\u7f6e\u5de5\u4f5c\u3002</p>\n<p>\u4e00\u81f4\u5f88\u8ba9\u6211\u7591\u60d1\u7684\u662f\uff0c\u8fd9\u91cc\u7684 getter \u3001setter \u3002\u6211\u4eec\u4f20\u7edf\u7406\u89e3\u7684 \u662f\u8fd9\u6837\u5de5\u4f5c\u7684\uff1a</p>\n<pre><code class=\"language-ruby\">\nclass Sample\n  def initialize()\n    @name\n  end\n\n  # getter\n  def name\n    @name\n  end\n\n  # setter\n  def name=(new_name)\n    @name = new_name\n  end\nend\n</code></pre>\n<p>\u4f46\u662f Sinatra \u8fd9\u91cc\u4f3c\u4e4e\u662f\u4e00\u4e2a\u5faa\u73af\u4e00\u6837\u7684\uff0c\u4f60\u4f1a\u53d1\u73b0\u4ed6\u7684 setter \u6c38\u8fdc\u5728\u8c03\u7528 set \u8fd9\u662f\u4e3a\u4ec0\u4e48\u5462\uff1f\u6211\u4e00\u5ea6\u975e\u5e38\u8ff7\u60d1\u3002</p>\n<pre><code class=\"language-ruby\">setter = proc { |val| set option, val, true }\n</code></pre>\n<p>\u6211\u521a\u5f00\u59cb\u8fdb\u5165\u8fd9\u6bb5\u662f\u767e\u601d\u4e0d\u5f97\u5176\u89e3\u3002\u4f46\u4e8b\u5b9e\u8bc1\u660e\u6211\u683c\u5c40\u5c0f\u4e86\u3002\u8fd9\u90e8\u5206\u5176\u5b9e\u6839\u672c\u4e0d\u662f\u4f20\u7edf\u7684 setter, \u6211\u4eec\u89c2\u5bdf\u4f20\u7edf\u7684 setter \u4ed6\u7684\u95ee\u9898\u662f\u5fc5\u987b\u8981\u4ee5\u4e00\u4e2a\u5b9e\u4f8b\u53d8\u91cf\u4e3a\u4f9d\u6258\u3002\u6240\u4ee5\u4ed6\u624d\u5fc5\u987b\u5199\u6210\u8fd9\u6837\u3002\n\u5982\u679c\u662f\u4e0b\u9762\u8fd9\u6837\u5462\uff1f</p>\n<pre><code class=\"language-ruby\">class Sample\n\n  # getter\n  def name\n    \"new value\"\n  end\n\n  # setter\n  def name=(new_name)\n    # setter \u7684\u903b\u8f91\uff0c\u5c31\u662f\u8986\u76d6\u5f0f\u5b9a\u4e49\u4e00\u4e2a \u65b0\u7684 \u76f4\u63a5\u8fd4\u56de\u65b0\u503c\u7684 getter\n    re_define_name_getter(new_name)\n  end\nend\n</code></pre>\n<p>\u76f4\u63a5\u4f2a\u4ee3\u7801\uff0c\u6211\u4eec\u6bcf\u6b21\u8c03\u7528 setter \uff0csetter \u7684\u4efb\u52a1\u4e0d\u662f\u53bb\u4fee\u6539\u4e00\u4e2a \u4e2d\u95f4\u503c\uff0c\u800c\u662f\u6bcf\u6b21\u53bb\u91cd\u65b0\u5b9a\u4e49 \u65b0\u7684 getter \u65b9\u6cd5\uff0c\u5b9a\u4e49\u7684\u65f6\u5019\u5c31\u585e\u5165\u65b0\u7684\u503c\u3002</p>\n<p>\u8fd9\u6837\u4f9d\u7136\u4fdd\u6301\u4e86 getter \u7684\u529f\u80fd\uff01\u8c41\u7136\u5f00\u6717\uff01</p>\n<p>Sinatra \u7684 set \u7cfb\u7edf\u5c31\u662f\u8fd9\u6837\u5de5\u4f5c\u7684\uff0c\u4e0d\u8bba\u662f set \u51fd\u6570\u5b9a\u4e49\u672c\u8eab\uff0c\u8fd8\u662f set \u5185\u90e8\u8c03\u7528\u7684 set \uff0c\u8fd8\u662f\u7528\u6237\u6700\u7ec8\u5728\u5916\u90e8\u4e66\u5199 <code>set xxx, new_value</code> \u6700\u7ec8\u6b8a\u9014\u540c\u5f52\u7684\u8fdb\u5165 <code>set option, value, true</code> \u7136\u540e\u90fd\u4f1a\u8d70\u5230\u6700\u540e\u4e00\u90e8\u5206\uff0c\u91cd\u65b0\u5b9a\u4e49\u4e09\u4e2a\u65b9\u6cd5\u3002</p>\n<pre><code class=\"language-ruby\">  define_singleton(\"#{option}=\", setter)\n  define_singleton(option, getter)\n  define_singleton(\"#{option}?\", \"!!#{option}\") unless method_defined? \"#{option}?\"\n</code></pre>\n<p>setter \u65b9\u6cd5\u7684\u4f5c\u7528\u5c31\u662f\u8c03\u7528 set \u81ea\u8eab\uff0c\u8fd9\u6837\u53ea\u8981\u88ab\u8c03\u7528\uff0c\u65f6\u95f4\u4e0a\u53ef\u4ee5\u5b8c\u6210\u4e86\u4e00\u79cd\u5faa\u73af\u3002\u95ed\u73af\u8c03\u7528\uff08\u539f\u8c05\u6211\u7528\u4e86\u95ed\u73af\u8fd9\u4e2a\u8bcd :P \uff09\ngetter \u65b9\u6cd5 \u662f\u4ee5\u65b0\u7684\u503c\u76f4\u63a5\u8fd4\u56de\uff0crespond_to \u65b9\u6cd5\u540c\u7406\uff0c\u4ee5\u65b0\u503c\u8ba1\u7b97\u8fd4\u56de\u3002</p>\n<h1>\u8865\u5145</h1>\n<p>1.\u5b9a\u4e49\u5904\u6709\u8da3\u7684\u5199\u6cd5 <code>value = (not_set = true)</code></p>\n<pre><code class=\"language-ruby\">def set(option, value = (not_set = true), ignore_setter = false, &amp;block)\n  # ....\nend\n</code></pre>\n<p>\u53ef\u4ee5\u901a\u8fc7\u5b9e\u9a8c\u8bc1\u5b9e\u8fd9\u79cd\u5199\u6cd5\u7684\u7279\u70b9\u662f\uff1a</p>\n<p>\u5982\u679c value \u8d4b\u503c \u6bd4\u5982\u662f 99 \uff0c\u90a3\u4e48  value = 99, not_set = nil</p>\n<p>\u5982\u679c value \u6ca1\u6709\u8d4b\u503c\uff0c \u90a3\u4e48 value = not_set = true </p>\n<p>\u8fd9\u91cc\u4e3b\u8981\u662f\u6ca1\u6709\u8d4b\u503c\uff0cnot_set \u5f00\u59cb\u53d1\u6325\u903b\u8f91\u4e0a\u7684\u4f5c\u7528\u3002</p>\n<p><a href=\"https://mark24code.github.io/ruby/sinatra/2022/02/14/Sinatra%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90(%E4%B8%80)-set%E7%B3%BB%E7%BB%9F%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.html\" rel=\"nofollow\">\u6211\u7684 BLOG</a></p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/822114", 
      "title": "\u300aRuby \u8bbe\u8ba1\u6a21\u5f0f\u300b\u7b14\u8bb0", 
      "id": "https://www.v2ex.com/t/822114", 
      "date_published": "2021-12-14T05:54:52+00:00", 
      "content_html": "<p>\u7b97\u662f\u79c1\u4eba\u7b14\u8bb0\u3002\u4f46\u662f\u8bb0\u5f55\u4e0b\u4e5f\u8bb8\u9700\u8981\u4eba\u53ef\u4ee5\u83b7\u5f97\u6709\u4ef7\u503c\u7684\u4fe1\u606f\u3002</p>\n<p>\u8fd9\u672c\u4e66\u7edd\u7248\u4e86</p>\n<p><a href=\"https://mark24code.github.io/%E7%AC%94%E8%AE%B0/2021/12/02/%E7%AC%94%E8%AE%B0-Ruby%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html\" rel=\"nofollow\">Ruby \u8bbe\u8ba1\u6a21\u5f0f\u7b14\u8bb0</a></p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/820700", 
      "title": "Ruby \u6807\u51c6\u5e93\u6709\u8da3\u90e8\u5206\u6458\u8981", 
      "id": "https://www.v2ex.com/t/820700", 
      "date_published": "2021-12-07T11:07:04+00:00", 
      "content_html": "<h2>\u6211\u7684 BLOG <a href=\"https://mark24code.github.io/ruby/2021/12/07/Ruby%E6%A0%87%E5%87%86%E5%BA%93%E6%9C%89%E8%B6%A3%E9%83%A8%E5%88%86%E6%91%98%E8%A6%81.html\" rel=\"nofollow\">https://mark24code.github.io/ruby/2021/12/07/Ruby%E6%A0%87%E5%87%86%E5%BA%93%E6%9C%89%E8%B6%A3%E9%83%A8%E5%88%86%E6%91%98%E8%A6%81.html</a></h2>\n<h1>\u6458\u8981</h1>\n<ul>\n<li><a href=\"https://devdocs.io/ruby%7E2.7/etc\" rel=\"nofollow\">etc</a> \u7528\u6765\u83b7\u5f97 <code>/etc</code> \u4e0b\u9762\u4fe1\u606f\u3002\u6bd4\u5982\u7cfb\u7edf\u767b\u5f55\u7528\u6237\uff0c\u53ef\u4ee5\u505a\u4e00\u4e2a\u7cfb\u7edf\u7c98\u5408\u6027\u8f83\u9ad8\u7684\u7a0b\u5e8f\u6765\u4f7f\u7528\u3002</li>\n<li><a href=\"https://devdocs.io/ruby%7E2.7/enumerable\" rel=\"nofollow\">enumerable</a> \u628a\u4f60\u7684 class \u53d8\u6210\u8fed\u4ee3\u5668</li>\n<li><a href=\"https://devdocs.io/ruby%7E2.7/objectspace\" rel=\"nofollow\">objectspace</a> \u53ef\u4ee5\u8fd4\u56de class \u5b9e\u4f8b\u7edf\u8ba1\u4fe1\u606f\uff0csize \u7b49\uff0c\u53ef\u4ee5\u4f5c\u4e3a\u626b\u63cf\u3001performance \u3001\u7edf\u8ba1\u4f7f\u7528\u3002</li>\n<li><a href=\"https://devdocs.io/ruby%7E2.7/observable\" rel=\"nofollow\">observable</a> \u628a\u4f60\u7684 class \u53d8\u6210\u53d1\u5e03\u8ba2\u9605\u6a21\u5f0f</li>\n<li><a href=\"https://devdocs.io/ruby%7E2.7/marshal\" rel=\"nofollow\">marshal</a> \u628a\u7a0b\u5e8f\u5bf9\u8c61\u5b57\u8282\u6301\u4e45\u5316\uff0c\u6216\u8005\u8fd8\u539f\u3002\u9002\u5408\u505a\u5728\u5185\u5b58\u4e2d\u7f13\u5b58\u5bf9\u8c61\u3002\u6bd4\u5982 \u547d\u4ee4\u6a21\u5f0f\u6808\u91cc\u64a4\u9500\u7684\u5bf9\u8c61\u3002</li>\n<li><a href=\"https://devdocs.io/ruby%7E2.7/pathname\" rel=\"nofollow\">pathname</a> \u6709\u91ce\u5fc3\u7684 module \u5c01\u88c5\u4e86\u8def\u5f84\u7684\u64cd\u4f5c\uff0c\u65b9\u4fbf\u76ee\u5f55\u6587\u4ef6\u64cd\u4f5c</li>\n<li><a href=\"https://devdocs.io/ruby%7E2.7/io#method-i-eof\" rel=\"nofollow\">IO#eof?</a> <code>eof?</code>\u662f\u4e00\u4e2a\u5916\u90e8\u8fed\u4ee3\u5668\u53ef\u4ee5\u7528\u7684\u65b9\u6cd5\uff0c\u5916\u90e8\u8fed\u4ee3\u5668\u66f4\u65b9\u4fbf\u63a7\u5236\u3002\u5176\u4ed6<code>eof?</code> \u540c\u7406\u3002</li>\n<li><a href=\"https://devdocs.io/ruby%7E2.7/mutex\" rel=\"nofollow\">mutex</a> \u591a\u7ebf\u7a0b\u4e2d\u63d0\u4f9b\u9501\u540c\u6b65</li>\n<li><a href=\"https://devdocs.io/ruby%7E2.7/drb\" rel=\"nofollow\">drb</a> \u8ba9 Ruby \u652f\u6301\u5206\u5e03\u5f0f\u7a0b\u5e8f\uff0c\u901a\u8fc7 TCP/IP \u5c06\u7a0b\u5e8f\u5404\u90e8\u5206\u5408\u5e76\u5728\u4e00\u8d77\u3002</li>\n</ul>\n<p>\u672a\u5b8c\u5f85\u7eed</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/807831", 
      "title": "\u5e76\u884c\u5e76\u53d1\u8fdb\u7a0b\u7ebf\u7a0b\u534f\u7a0b GIL \u6982\u5ff5\u7b80\u660e\u89e3\u91ca\u7b14\u8bb0\uff08Ruby \u4e3e\u4f8b\uff09", 
      "id": "https://www.v2ex.com/t/807831", 
      "date_published": "2021-10-14T08:05:35+00:00", 
      "content_html": "<p><a href=\"https://www.v2ex.com/t/807569\" rel=\"nofollow\">\u5e76\u884c\u5e76\u53d1\u8fdb\u7a0b\u7ebf\u7a0b\u534f\u7a0b GIL \u6982\u5ff5\u7b80\u660e\u89e3\u91ca\u7b14\u8bb0</a></p>\n<p>\u6700\u5148\u53d1\u5728 Ruby \u8282\u70b9\uff0c\u56e0\u4e3a\u4f8b\u5b50\u91cc\u4e3b\u8981\u4e3e\u4e86 Ruby</p>\n<p>\u7531\u4e8e GIL \u7684\u95ee\u9898\uff0c\u5bf9\u4e8e Python \u4e5f\u6709\u540c\u6837\u7684\u610f\u4e49\uff0c\u6240\u4ee5\u53d1\u5728\u4e86 Python \u8282\u70b9\uff0c\u770b\u7684\u4eba\u591a\u4e00\u70b9</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/801866", 
      "title": "Sinatra \u7684 app \u6a21\u677f\uff0c\u63d0\u4f9b\u4e00\u4e9b\u80f6\u6c34\u4ee3\u7801\u652f\u6301\u7c7b\u4f3c Rails \u7684\u4f53\u9a8c", 
      "id": "https://www.v2ex.com/t/801866", 
      "date_published": "2021-09-14T15:02:45+00:00", 
      "content_html": "<h3>Sinatra \u7684 app \u6a21\u677f\uff0c\u63d0\u4f9b\u4e00\u4e9b\u80f6\u6c34\u4ee3\u7801\u652f\u6301\u7c7b\u4f3c Rails \u7684\u4f53\u9a8c</h3>\n<p>\u5982\u679c\u4f60\u60f3\u7075\u6d3b\u7684\u5f00\u5c55\u5de5\u4f5c\uff0c\u53c8\u89c9\u5f97 Rails \u8fc7\u4e8e\u5e9e\u5927\uff08\u6bd4\u5982 Rails6+ \u643a\u5e26\u4e00\u4e2a Node \uff09\u3001\u6587\u6863\u8981\u8bfb\u5f88\u4e45\uff0c\u6b63\u5728\u72b9\u8c6b\u5f53\u4e2d\u3002</p>\n<p>\u4f60\u6070\u5de7\u77e5\u9053 Sinatra \u7684\u5b58\u5728\uff0c15 \u5206\u949f\u8bfb\u5b8c\u7684 <code>Sinatra/README</code> \u53c8\u89c9\u5f97\u81ea\u5df1\u884c\u4e86\uff0c\u53ef\u662f Sinatra \u4f3c\u4e4e\u592a\u7b80\u5355\u4e86\uff0c\u4f60\u60f3\u8981\u662f Sinatra \u6709 MVC \u548c\u5f00\u7bb1\u5373\u7528\u7684 ORM \u5c31\u597d\u4e86\u3002</p>\n<p>\u8fd9\u662f\u6700\u8fd1\u505a\u4e00\u4e2a\u7b80\u5355\u540e\u7aef\u9879\u76ee\u7684\u6c89\u6dc0\uff0c\u53ef\u4ee5\u4f5c\u4e3a\u4e00\u4e2a\u7b80\u5355\u7684\u8d77\u70b9\u3002</p>\n<p>\u4e00\u5207\u57fa\u4e8e Sinatra+Rack\uff0c\u7528\u4e00\u4e9b\u80f6\u6c34\u4ee3\u7801 \u628a  Rack/Sinatra + \u914d\u7f6e + \u6587\u4ef6\u76ee\u5f55 \u8054\u7cfb\u5728\u4e00\u8d77\u5f00\u59cb\u5de5\u4f5c\u3002\u5bb9\u6613\u66f4\u6539\uff0c\u7b80\u5355\u660e\u4e86\u3002</p>\n<p>\u5206\u4eab\u4e00\u4e0b\uff0c\u53ef\u80fd\u4e0d\u591f\u6210\u719f\uff0c\u6b22\u8fce\u78b0\u649e\uff0c\u8ba9\u6211\u53ef\u4ee5\u5b66\u4e60\u66f4\u591a~</p>\n<p>github\uff1a <a href=\"https://github.com/Mark24Code/sinatra-app-template\" rel=\"nofollow\">https://github.com/Mark24Code/sinatra-app-template</a></p>\n<p>geeknote\uff1a <a href=\"https://geeknote.net/mark24/posts/283\" rel=\"nofollow\">https://geeknote.net/mark24/posts/283</a></p>\n<p>RubyChina: <a href=\"https://ruby-china.org/topics/41685\" rel=\"nofollow\">https://ruby-china.org/topics/41685</a></p>\n<h1>Sinatra App Template</h1>\n<p>Lightweight web framework codebase. Just clone and develop on it.</p>\n<p>Tech component: Rack+Sinatra+Sequel and default use Postgresql database.</p>\n<p>Add rails-like migration command line helpers.</p>\n<h2>Openbox Features</h2>\n<h3>Apps</h3>\n<ul>\n<li>&lt;input checked=\"\" disabled=\"\" type=\"checkbox\"&gt; Multi Env Configuration</li>\n<li>&lt;input checked=\"\" disabled=\"\" type=\"checkbox\"&gt; Multi router DSL base on Rack</li>\n<li>&lt;input checked=\"\" disabled=\"\" type=\"checkbox\"&gt; CORS support</li>\n<li>&lt;input checked=\"\" disabled=\"\" type=\"checkbox\"&gt; Hot reload</li>\n<li>&lt;input checked=\"\" disabled=\"\" type=\"checkbox\"&gt; Custom logger</li>\n<li>&lt;input checked=\"\" disabled=\"\" type=\"checkbox\"&gt; ORM base on Sequel'</li>\n</ul>\n<h3>Tasks</h3>\n<ul>\n<li>&lt;input checked=\"\" disabled=\"\" type=\"checkbox\"&gt; Rails-like migration helpers</li>\n<li>&lt;input checked=\"\" disabled=\"\" type=\"checkbox\"&gt; Test</li>\n<li>&lt;input checked=\"\" disabled=\"\" type=\"checkbox\"&gt; Seed</li>\n</ul>\n<h3>CI&amp;CD</h3>\n<ul>\n<li>&lt;input checked=\"\" disabled=\"\" type=\"checkbox\"&gt; Dockerfile</li>\n</ul>\n<h2>Find helpful rake tasks</h2>\n<p><code>rake</code> or  <code>rake -T</code></p>\n<h2>Run server &amp; develop</h2>\n<p><code>rake server:run</code></p>\n<h2>Production Server &amp; deploy</h2>\n<p><code>APP_ENV=production  bundle exec rake server:run</code></p>\n<p>you can also use docker</p>\n<p><code>docker built -t &lt;what your docker image label&gt;  .</code></p>\n<h2>Custom server &amp; database</h2>\n<p>You can use DSL to config <code>Key:Value</code> , then you application just use.</p>\n<pre><code class=\"language-ruby\">Config::Default.configure do\n  set :app_env, ENV.fetch('APP_ENV'){ 'development' }\n  set :bind, ENV.fetch('HOST') { '0.0.0.0' }\n  set :port, ENV.fetch('PORT') { 3000 }\n  set :secrets, ENV.fetch('SECRETS') { 'YOU CANNOT GUESS ME' }\n  set :max_threads, ENV.fetch('MAX_THREADS') { 5 }\n\n  set :database_url, ENV['DATABASE_URL']\nend\n\nConfig::Development.configure do \n  set :database_url, 'ENV['DATABASE_URL']'\nend\n\nConfig::Test.configure do \n  set :database_url, ENV['DATABASE_URL']\nend\n\nConfig::Production.configure do \n  # set :database_url, ENV['DATABASE_URL']\nend\n</code></pre>\n<p>They have an inheritance relationship</p>\n<pre><code>Development &lt; Default\nTest &lt; Default\nProduction &lt; Default\n</code></pre>\n<p>In your code, just use  <code>Config</code> directly. <code>core/bootstrap</code>  do a work that loaded all necessery mods before your code.</p>\n<pre><code class=\"language-Ruby\">Config.current  # current env configuration\n\nConfig::Development.database_url\n\nConfig::Development\n\nConfig::Development.database_url\n</code></pre>\n<p>You can also create your own <code>Config</code>  for your single Application:</p>\n<pre><code class=\"language-ruby\">class MyConfig &lt; Config::Base\n\nend\n\nMyConfig.configure do \n  # set :database_url, ENV['DATABASE_URL']\nend\n\n</code></pre>\n<h2>Mount different Sinatra web application</h2>\n<p>Edit <code><a href=\"http://config.ru\" rel=\"nofollow\">config.ru</a></code></p>\n<p>Lark also is Rack application. We can use Rack middlewares.</p>\n<pre><code class=\"language-ruby\">require_relative './cores/bootstrap'\nBootstrap.rack \n\n# you can load Rack middleware here\n\n# mount applications\nrequire 'controllers/root_controller'\n# routers(handy config)\nmap '/' do\n  run RootController \nend\n</code></pre>\n<h1>Base</h1>\n<p><code>bases</code> directory are use for Application Base Class.</p>\n<p>You can make different Configured Sinatra Application class here, then your application/controller just inherit the Base Class to create Application.</p>\n<p>It will share Config, and make less code.</p>\n<pre><code class=\"language-ruby\"># Sinatra Doc http://sinatrarb.com/intro.html\nrequire 'sinatra/base'\nrequire 'json'\n\nclass BaseController &lt; Sinatra::Base\n  # Inject config\n\n  # Config &amp; register Sinatra Extensions\n\n  # Rewrite Views dir\n  settings.views = File.expand_path(File.join($PROJECT_DIR, 'views'))\n\n  configure :development do\n    require 'sinatra/reloader'\n    register Sinatra::Reloader\n  end\n\n  # mount Sinatra Helpers\n\n  # mount Sinatra middlewares\n\nend\n\n\n# Share Configuration\n\nclass MyPageServer &lt; BaseController\nend\n\nclass MyApiServer &lt; BaseController\nend\n\n</code></pre>\n<h1>ORM &amp; Tools</h1>\n<p>Provide rails-like rake task help you build app quickly.</p>\n<pre><code>rake db:check                   # Checking for current migrations\nrake db:connect                 # Connect database\nrake db:console                 # Database Console\nrake db:create[database_name]   # Create database\nrake db:create_migration[name]  # Create a migration\nrake db:drop[database_name]     # Drop database\nrake db:ls                      # List database tables\nrake db:migrate[version]        # Run migrations\nrake db:rollback[version]       # Rollback to migration\nrake db:version                 # Prints current schema version\nrake list                       # List all tasks\nrake seed:all                   # Seed: run all seeds\nrake seed:run[seed_name]        # Seed: run seed\nrake server:run                 # Run server\nrake test                       # Run tests\n</code></pre>\n<h1>Project Structure</h1>\n<pre><code>.\n\u251c\u2500\u2500 Dockerfile # Common Dockerfile\n\u251c\u2500\u2500 Gemfile\n\u251c\u2500\u2500 Gemfile.lock\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 Rakefile # Rake Task Index File.\n\u251c\u2500\u2500 bases # Base configured class. You can make different BaseClasses then reuse them.\n\u2502   \u2514\u2500\u2500 base_controller.rb # You contoller can inherit it or write yourself.\n\u251c\u2500\u2500 config.ru # Application index. You can mount controllers and routes here.\n\u251c\u2500\u2500 configs # You can make different configs for applications\n\u2502   \u2514\u2500\u2500 config.rb # Base config\n\u251c\u2500\u2500 controllers \n\u2502   \u2514\u2500\u2500 root_controller.rb\n\u251c\u2500\u2500 cores # Inject ENVS and autoloads files, make MVC works\n\u2502   \u251c\u2500\u2500 01_config.rb # Names can controller mount order\n\u2502   \u2514\u2500\u2500 bootstrap.rb\n\u251c\u2500\u2500 dbs # You can make multi database here\n\u2502   \u251c\u2500\u2500 default_db.rb # default database connect instance\n\u2502   \u2514\u2500\u2500 migrations # save database migrations\n\u251c\u2500\u2500 docs\n\u2502   \u2514\u2500\u2500 good.feature\n\u251c\u2500\u2500 log # Directory for save logs by default\n\u2502   \u2514\u2500\u2500 development.log\n\u251c\u2500\u2500 loggers # Loggers for application\n\u2502   \u2514\u2500\u2500 default_logger.rb\n\u251c\u2500\u2500 public # Public resources\n\u2502   \u2514\u2500\u2500 favicon.svg\n\u251c\u2500\u2500 seeds # Seeds\n\u251c\u2500\u2500 tasks # Rake helpful tasks \n\u2502   \u251c\u2500\u2500 db_task.rb\n\u2502   \u251c\u2500\u2500 seed_task.rb\n\u2502   \u251c\u2500\u2500 server_task.rb\n\u2502   \u2514\u2500\u2500 test_task.rb\n\u251c\u2500\u2500 tests # Test cases\n\u2502   \u2514\u2500\u2500 test_demo.rb\n\u2514\u2500\u2500 views # views template\n    \u251c\u2500\u2500 base.erb\n    \u2514\u2500\u2500 root.erb\n\n</code></pre>\n<h1>Bootstrap &amp; Load orders</h1>\n<h2>For Rake</h2>\n<pre><code>require_relative './cores/bootstrap'\nBootstrap.rake\n</code></pre>\n<p>It will auto load files make sure rake task can work.</p>\n<p>In rake we can use <code>Config.current</code> to read configuration.</p>\n<p><code>DB</code> also available.</p>\n<h2>For Rack/Applications</h2>\n<p>In the same way</p>\n<pre><code>require_relative './cores/bootstrap'\nBootstrap.rack\n# OR\n# Bootstrap.apps\n</code></pre>\n<p>It will autoload all dep mods. Share with a context.</p>\n<h2>Change load orders</h2>\n<p><code>cores/bootstrap.rb</code>  defines different load orders, you can change.</p>\n<p>In anther way, you can change filename to e.g  <code>00_before_all.rb</code> \u3001<code>01_first_load.rb</code> to control mods load order.</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Mark24", 
        "name": "Mark24", 
        "avatar": "https://cdn.v2ex.com/avatar/edea/c9b0/62847_large.png?m=1723538556"
      }, 
      "url": "https://www.v2ex.com/t/792532", 
      "date_modified": "2021-07-29T10:53:00+00:00", 
      "content_html": "<p>\u6709\u66f4\u597d\u7684\u65b9\u6cd5\u53ef\u4ee5\u544a\u8bc9\u6211\uff0c\u6211\u6700\u65b0\u5728\u5b66\u4e60 Ruby</p>\n<p>\u6700\u65b0\u7684\u4fee\u6539\u4f1a\u66f4\u65b0\u5728 BLOG</p>\n<p><a href=\"https://mark24code.github.io/ruby/2021/07/29/Ruby%E7%9A%84%E6%96%B9%E6%B3%95%E6%9F%A5%E6%89%BE%E5%86%8D%E5%BE%80%E5%89%8D%E4%B8%80%E6%AD%A5.html\" rel=\"nofollow\">\u6211\u7684\u535a\u5ba2</a></p>\n<p><a href=\"https://ruby-china.org/topics/41505\" rel=\"nofollow\">RubyChina \u8ba8\u8bba\u5e16</a> </p>\n<hr/>\n<h1>\u80cc\u666f</h1>\n<p>\u300a Ruby \u5143\u7f16\u7a0b\uff08\u7b2c\u4e8c\u7248\uff09\u300b 5.4 \u8282 \u5355\u4ef6\u7c7b \u5728 Page125 \u8fd9\u9875\uff0c\u8bb2\u4e86\u4e00\u79cd\u60c5\u51b5\uff1a</p>\n<pre><code class=\"language-ruby\">\nclass C\n  def a_method\n    'C#a_method()'\n  end\nend\n\n\n\nclass C\n  class &lt;&lt; self\n    def a_class_method\n      '#C.a_class_method() #singleton'\n    end\n  end\nend\n\nclass D &lt; C;end\n\nobj = D.new\n\n\nD.a_class_method # =&gt; '#C.a_class_method() #singleton'\n</code></pre>\n<p>D.a_class_method  \u4ed6\u662f\u5982\u4f55\u67e5\u627e\u7684\u5462\uff1f</p>\n<p>\u672c\u6587\u5c31\u662f\u5bfb\u627e\u8fd9\u4e2a\u7684\u7b54\u6848\u3002\u8bb2\u7684\u662f Ruby \u7684\u65b9\u6cd5\u67e5\u627e\u518d\u5f80\u524d\u8d70\u4e00\u6b65\u3002</p>\n<p>\u4e3a\u4e86\u8bf4\u660e\u8fd9\u4e2a\u95ee\u9898\uff0c\u5148\u8981\u5570\u55e6\u7684\u505a\u4e00\u4e9b\u94fa\u57ab\u3002</p>\n<p>\u4e0b\u6587\u4e2d\uff0c\u6b64\u4e66\u7b80\u79f0\u4e3a\u300a\u5143\u7f16\u7a0b\u300b</p>\n<h1>\u4e00\u3001Ruby \u7684\u7ee7\u627f\u7ed3\u6784</h1>\n<p><img alt=\"\" class=\"embedded_image\" loading=\"lazy\" referrerpolicy=\"no-referrer\" rel=\"noreferrer\" src=\"https://l.ruby-china.com/photo/Mark24/f255ac04-b557-4b93-bc51-041d9d769f5c.png%21large\"/></p>\n<p>\u8fd9\u5f20\u56fe\u5b9e\u5728\u603b\u7ed3\u7684\u592a\u7f8e\u4e3d\u4e86\u3002\u5148\u653e\u5728\u8fd9\u91cc\u3002\u56fe\u7247\u7684\u51fa\u5904\uff0c\u53ef\u4ee5\u53c2\u8003\u6587\u672b\u3002</p>\n<h1>\u4e8c\u3001Ruby \u4e00\u822c\u65b9\u6cd5\u67e5\u627e\u89c4\u5219</h1>\n<p>\u300a\u5143\u7f16\u7a0b\u300b\u91cc\u9762\u91cc\u9762\u603b\u7ed3\u4e86 Ruby \u7684\u67e5\u627e\u89c4\u5219\uff1a</p>\n<blockquote>\n<p>\u201c\u5411\u53f3\u4e00\u6b65\uff0c\u7136\u540e\u5411\u4e0a\u67e5\u627e\u201d\u3002</p>\n</blockquote>\n<p>\u610f\u601d\u5c31\u662f\uff0c\u5411\u53f3\u5bfb\u627e\u4ed6\u7684\u7236\uff0c\u7136\u540e\u5f00\u59cb\u5f80\u4e0a\u5bfb\u627e\u7ee7\u627f\u5173\u7cfb\uff0c\u901a\u8fc7\u8fd9\u79cd\u65b9\u5f0f\u67e5\u627e\u65b9\u6cd5\u3002</p>\n<p>\u6bd4\u5982\u4ee5\u4e0b\u4ee3\u7801</p>\n<pre><code class=\"language-ruby\">class C\n  def a_method\n    'C#a_method()'\n  end\nend\n\n\nclass D &lt; C;end\n\nobj = D.new\nobj.a_method\n</code></pre>\n<p>obj \u5982\u4f55\u67e5\u627e a_method \u65b9\u6cd5\u5462\uff1f</p>\n<p><img alt=\"\" class=\"embedded_image\" loading=\"lazy\" referrerpolicy=\"no-referrer\" rel=\"noreferrer\" src=\"https://l.ruby-china.com/photo/Mark24/2229f594-96d4-4bb8-b8cf-219d09742522.jpeg%21large\"/></p>\n<p>\u5982\u679c\u6211\u4eec\u7ed9 obj \u5bf9\u8c61\u6dfb\u52a0\u5355\u4f8b\u7c7b\uff0c\u4ed6\u4f1a\u5982\u4f55\u67e5\u627e\u5462\uff1f</p>\n<pre><code class=\"language-ruby\">\nclass C\n  def a_method\n    'C#a_method()'\n  end\nend\n\n\nclass D &lt; C;end\n\nobj = D.new\n\n# \u5b9a\u4e49\u5355\u4f8b\u7c7b\n\nclass &lt;&lt; obj\n  def a_singleton_method\n    \"obj#a_singleton_method\"\n  end\nend\n\nobj.a_singleton_method \n\n</code></pre>\n<p>obj.a_singleton_method  \u4f1a\u5982\u4f55\u67e5\u627e\u65b9\u6cd5\u5462\uff1f</p>\n<p><img alt=\"\" class=\"embedded_image\" loading=\"lazy\" referrerpolicy=\"no-referrer\" rel=\"noreferrer\" src=\"https://l.ruby-china.com/photo/Mark24/37d0a87c-f8ca-4aa8-884b-b84db0ccc493.jpeg%21large\"/></p>\n<p>\u53ef\u4ee5\u901a\u8fc7\u4e00\u4e0b\u65b9\u5f0f\u68c0\u9a8c</p>\n<pre><code class=\"language-ruby\">\nobj.singleton_class.superclass # =&gt; D\n\n</code></pre>\n<p>\u4ed6\u4f1a\u6309\u7167\u5982\u56fe\u7684\u65b9\u5f0f\uff0c\u5176\u5b9e\u5b9e\u4f8b\u5bf9\u8c61\u521b\u9020\u4e86\u4e00\u4e2a \u5355\u4f8b\u7c7b \u53ef\u4ee5\u6807\u8bb0\u4e3a #obj\uff0c\u7528#\u8868\u793a\u5355\u4f8b\u7c7b\u3002\n#obj \u4f1a\u51fa\u73b0\u5728\u5bf9\u8c61\u548c\u771f\u6b63\u7684\u7c7b\u4e2d\u95f4\u3002</p>\n<p>\u6211\u4eec\u4e5f\u80fd\u7528\u4e0a\u9762 </p>\n<blockquote>\n<p>\u201c\u5411\u53f3\u4e00\u6b65\uff0c\u7136\u540e\u5411\u4e0a\u67e5\u627e\u201d\u3002</p>\n</blockquote>\n<p>\u6765\u6307\u5bfc\u6211\u4eec\u67e5\u627e\uff0c\u53ea\u4e0d\u8fc7\u5bf9\u8c61\u5b58\u5728\u4e00\u4e2a\u5355\u4f8b\u7c7b\u7f62\u4e86\u3002</p>\n<h1>\u4e09\u3001\u65b0\u7684\u95ee\u9898\u51fa\u73b0</h1>\n<p>\u4f46\u662f\u95ee\u9898\u6765\u4e86\uff0c\u56de\u5230\u6587\u7ae0\u7684\u6700\u5f00\u5934\u3002</p>\n<pre><code class=\"language-ruby\">\nclass C\n  def a_method\n    'C#a_method()'\n  end\nend\n\n\n\nclass C\n  class &lt;&lt; self\n    def a_class_method\n      '#C.a_class_method() #singleton'\n    end\n  end\nend\n\nclass D &lt; C;end\n\nobj = D.new\n\n\nD.a_class_method # =&gt; '#C.a_class_method() #singleton'\n</code></pre>\n<p>\u8fd9\u4e2a\u4f8b\u5b50\u3002\u5728\u7c7b C \u4e0a\u5b9a\u4e49\u4e86\u5355\u4f8b\u65b9\u6cd5\uff0c\u5e76\u4e14\u6211\u4eec\u6307\u5bfc\u6240\u6709\u4e1c\u897f\u5728 Ruby \u91cc\u90fd\u662f\u5bf9\u8c61\uff0c\u90fd\u53ef\u4ee5\u5b9a\u4e49\u5355\u4f8b\u65b9\u6cd5\u3002</p>\n<p>\u8fd9\u5c31\u662f\u6587\u7ae0\u5f00\u5934\u6700\u5148\u7684\u56fe\u7247\u3002\u6240\u6709\u7684\u7c7b\u90fd\u53ef\u4ee5\u5b9a\u4e49\u5355\u4f8b\u7c7b\u3002\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0cD.a_class_method \u5e94\u8be5\u5982\u4f55\u67e5\u627e\u5462\uff1f</p>\n<blockquote>\n<p>\u201c\u5411\u53f3\u4e00\u6b65\uff0c\u7136\u540e\u5411\u4e0a\u67e5\u627e\u201d\u3002</p>\n</blockquote>\n<p>\u4f3c\u4e4e\u5e2e\u4e0d\u4e86\u6211\u4eec\u4e86\u3002\u56e0\u4e3a\u6211\u4eec\u9762\u4e34\u4e00\u4e2a\u95ee\u9898\uff0c\u8ba9\u6211\u6765\u63cf\u8ff0\u4e0b\uff1a</p>\n<p>\u6211\u4eec\u628a D \u5f53\u505a\u4e00\u4e2a\u5bf9\u8c61\uff0c\u5f00\u59cb\u5bfb\u627e\u4ed6\u7684\u65b9\u6cd5\u3002</p>\n<p><img alt=\"\" class=\"embedded_image\" loading=\"lazy\" referrerpolicy=\"no-referrer\" rel=\"noreferrer\" src=\"https://l.ruby-china.com/photo/Mark24/5aa37d39-2815-4838-903d-7413982be067.png%21large\"/></p>\n<p>\u62ff\u8fd9\u5e45\u56fe\u505a\u4f8b\u5b50:</p>\n<p>Dog \u5f00\u59cb\u5bfb\u627e\u5b9a\u4e49\u7684\u65b9\u6cd5\uff0c\u5411\u53f3\u4e00\u6b65\uff0c\u8fdb\u5165\u81ea\u5df1\u7684 \u5355\u4f8b\u7c7b #Dog\uff0c\u7136\u540e\u5e94\u8be5\u505a\u4ec0\u4e48\uff0c\u9009\u62e9\u5411\u4e0a\u4e48\uff1f\u662f\u8d70 \u4ed6\u7684\u7236\u7c7b Class\uff0c\u8fd8\u662f \u5e94\u8be5\u5f80 \u5355\u4f8b\u7c7b\u7684\u7ee7\u627f\u94fe\u5f80\u4e0a\u627e\u5462\uff1f</p>\n<p>\u300a\u5143\u7f16\u7a0b\u300b\u6587\u672b\u7684\u51e0\u53e5\u8bdd,\u4f3c\u4e4e\u5728\u6697\u793a\u9ec4\u8272\u8fd9\u6761\u7ebf\u7684\u5bfb\u627e\u65b9\u5411\uff0c\u4f46\u662f\u4f5c\u8005\u5e76\u6ca1\u6709\u771f\u6b63\u8bf4\u6e05\u695a\uff1a</p>\n<p><img alt=\"\" class=\"embedded_image\" loading=\"lazy\" referrerpolicy=\"no-referrer\" rel=\"noreferrer\" src=\"https://l.ruby-china.com/photo/Mark24/2f72104e-8835-4bde-a722-afc0fa7a7810.png%21large\"/></p>\n<h1>\u4e09\u3001\u5bfb\u627e\u7b54\u6848</h1>\n<p>\u6211\u5148\u653e\u51fa\u7b54\u6848\uff0c\u5982\u4e0b\u56fe\u6240\u793a\uff1a</p>\n<p><img alt=\"\" class=\"embedded_image\" loading=\"lazy\" referrerpolicy=\"no-referrer\" rel=\"noreferrer\" src=\"https://l.ruby-china.com/photo/Mark24/1ccdcb33-7370-4e11-813e-179d8afe6047.jpeg%21large\"/></p>\n<p>\u5bf9\u8c61\u7684\u65b9\u6cd5\uff0c\u9075\u5faa</p>\n<blockquote>\n<p>\u201c\u5411\u53f3\u4e00\u6b65\uff0c\u7136\u540e\u5411\u4e0a\u67e5\u627e\u201d\u3002</p>\n</blockquote>\n<p>\u7c7b\u65b9\u6cd5\u7684\u67e5\u627e\u662f\u6211\u4eec\u5173\u5fc3\u7684\uff0c\u53ef\u4ee5\u770b\u5230\u5b9e\u9645\u7ed3\u679c\u662f\uff0c\u5b83\u6cbf\u7740\u7ee7\u627f\u7684\u5355\u4f8b\u7c7b\u4e00\u8def\u5411\u4e0a\uff0c\u7136\u540e\u518d\u8fdb\u5165\u7236\u7c7b\u3002</p>\n<p>\u5bfb\u627e\u8fd9\u4e2a\u7b54\u6848\u7684\u8fc7\u7a0b\u4e2d\uff0c\u6211\u770b\u4e86\u633a\u591a\u8d44\u6599\u548c\u6587\u5b57,\u8fd8\u6709\u95ee\u4e00\u4e9b Ruby \u65b9\u9762\u7684\u670b\u53cb\u90fd\u6ca1\u6709\u771f\u6b63\u5206\u6790\u5230\u8fd9\u4e00\u6b65\u3002</p>\n<p>\u6211\u6700\u540e\u662f\u600e\u4e48\u627e\u5230\u7b54\u6848\u7684\u5462\uff1f \u8fd9\u5c31\u5f97\u501f\u52a9 Ruby \u81ea\u8eab\u5b8c\u5584\u7684\u81ea\u7701\u673a\u5236\u3002\uff08\u5410\u69fd\uff0c\u5176\u4ed6\u8bed\u8a00\u53ef\u80fd\u90fd\u6ca1\u6709\u5b9e\u73b0\u7684\u90a3\u4e48\u7ec6\u81f4\uff09\u3002</p>\n<p>\u5176\u5b9e Ruby \u81ea\u8eab\u7684\u5f88\u591a\u5c5e\u6027\u90fd\u7ed1\u5b9a\u5728\u81ea\u8eab\u4e86\uff0c\u76f4\u63a5\u5411 Ruby \u95ee\u7b54\u6848\u5c31\u597d\u4e86</p>\n<pre><code class=\"language-ruby\">\nclass C\n  def a_method\n    'C#a_method()'\n  end\nend\n\n\n\nclass C\n  class &lt;&lt; self\n    def a_class_method\n      '#C.a_class_method() #singleton'\n    end\n  end\nend\n\nclass D &lt; C;end\n\nobj = D.new\n\n\nD.a_class_method # =&gt; '#C.a_class_method() #singleton'\n</code></pre>\n<p>\u6211\u4eec\u77e5\u9053 obj.ancestors \u53ef\u4ee5\u6253\u5370\u7ee7\u627f\u5173\u7cfb\uff0c\u4f46\u662f\u8fd9\u4e2a\u5f88\u9057\u61be\u7684\u662f\u5b83\u4e0d\u4f1a\u6253\u5370 \u5355\u4f8b\u7c7b\u3002</p>\n<p>\u5355\u4f8b\u7c7b\u5b9e\u9645\u4e0a\u662f\u4e00\u4e2a\u9690\u85cf\u7684\u5b58\u5728\u3002\u8fd9\u4e5f\u5c31\u662f\u7814\u7a76\u8fd9\u4e2a\u95ee\u9898\u5f88\u96be\u5f97\u5730\u65b9\uff0c\u56e0\u4e3a\u9690\u85cf\uff0c\u4f3c\u4e4e\u53ea\u80fd\u901a\u8fc7\u6e90\u7801\u548c\u5916\u90e8\u8d44\u6599\u53bb\u67e5\u770b\u3002</p>\n<p>\u5b9e\u9645\u4e0a\u6211\u4eec\u662f\u53ef\u4ee5\u62ff\u5230 obj.singleton_class \u7684\uff0c\u7136\u540e\u6211\u4eec\u524d\u9762\u5206\u6790\u4e86\u4e00\u4e9b\u7ed3\u8bba\uff0c\u5927\u81f4\u7ed9\u51fa\u4e86\u4e00\u4e2a\u5bf9\u8c61\u7684\u7ee7\u627f\u6a21\u578b\u3002</p>\n<pre><code class=\"language-ruby\">obj.singleton_class.ancestors\n\n# =&gt; [#&lt;Class:#&lt;D:0x00007feae092efc8&gt;&gt;, D, C, Object, Kernel, BasicObject]\n</code></pre>\n<p>\u5c31\u53ef\u4ee5\u6253\u5370\u51fa\uff0c\u5bf9\u8c61\u67e5\u627e\u7684\u987a\u5e8f\u3002</p>\n<p>\u540c\u7406\uff0c\u6211\u4eec\u60f3\u8981\u77e5\u9053 D \u7684\u65b9\u6cd5\u7684\u67e5\u627e\u987a\u5e8f</p>\n<pre><code class=\"language-ruby\">D.singleton_class.ancestors\n\n# =&gt; [#&lt;Class:D&gt;, #&lt;Class:C&gt;, #&lt;Class:Object&gt;, #&lt;Class:BasicObject&gt;, Class, Module, Object, Kernel, BasicObject]\n</code></pre>\n<p>\u8fd9\u4e2a\u5176\u5b9e\u5c31\u662f D \u67e5\u627e\u65b9\u6cd5\u7684\u987a\u5e8f\uff0c\u53ef\u4ee5\u770b\u5230\uff0c\u4ed6\u5148\u662f\u628a\u6240\u6709\u7684\u5355\u4f8b\u7c7b\u8d70\u4e86\u4e00\u904d\uff0c\u7136\u540e\u5f00\u59cb\u8fdb\u5165\u81ea\u5df1\u7684\u7236\u3002</p>\n<h1>\u56db\u3001\u603b\u7ed3</h1>\n<p>\u6700\u540e\uff0c\u8fd9\u53e5\u8bdd</p>\n<blockquote>\n<p>\u201c\u5411\u53f3\u4e00\u6b65\uff0c\u7136\u540e\u5411\u4e0a\u67e5\u627e\u201d\u3002</p>\n</blockquote>\n<p>\u6709\u4e86\u65b0\u5185\u6db5\uff0c \u5411\u53f3\u4e00\u6b65\u7684\u8fc7\u7a0b\u4e2d\uff0c\u4f18\u5148\u7684\u8d70\u5355\u4f8b\u7c7b\uff08\u5982\u679c\u6709\u7684\u8bdd\uff09\u4ee5\u53ca\u5355\u4f8b\u7684\u7ee7\u627f\uff0c\u7ed3\u675f\u540e\uff0c\u5f00\u59cb\u8fdb\u5165\u81ea\u5df1\u771f\u6b63\u7684\u7236\uff0c\u5373\u5411\u4e0a\u5728\u7ee7\u627f\u5173\u7cfb\u4e2d\u5bfb\u627e\u3002</p>\n<p>\u5355\u4f8b\u7c7b\u4e5f\u53ef\u4ee5\u770b\u6210\u662f\u4e00\u79cd\u5916\u6302\u65b9\u6cd5\uff08\u6bd4\u55bb\u4e0d\u4e25\u8c28\u4f46\u662f\u5f88\u597d\u7406\u89e3\uff09\uff0c\u5148\u5728\u5916\u6302\u65b9\u6cd5\u91cc\u627e\uff0c\u4e5f\u53ef\u4ee5\u987a\u7740\u5916\u6302\u7ee7\u627f\u94fe\u627e\u3002\u627e\u4e0d\u5230\u518d\u5230\u7ee7\u627f\u5173\u7cfb\u91cc\u9762\u627e\u3002</p>\n<h1>\u9898\u5916\u8bdd</h1>\n<p>\u6709\u4eba\u53ef\u80fd\u4f1a\u95ee\uff0c\u4e3a\u5565\u7ee7\u627f\u4f53\u7cfb\u8981\u641e\u5f97\u90a3\u4e48\u590d\u6742\uff1f</p>\n<p>\u501f\u7528 \u300a\u5143\u7f16\u7a0b\u300b\u91cc\u9762\u7684\u4e00\u53e5</p>\n<blockquote>\n<p>\u8fd9\u6837\u4f60\u5c31\u53ef\u4ee5\u5728 D \u4e2d \u8c03\u7528 C \u7684\u65b9\u6cd5\u4e86\u3002</p>\n</blockquote>\n<p>\u628a\u5bf9\u8c61\u7a7f\u6210\u94fe\u8868\uff0c\u7136\u540e\u76f8\u5f53\u4e8e\u4f60\u53ef\u4ee5\u62e5\u6709\u548c\u590d\u7528\u8fd9\u4e2a\u94fe\u6761\u4e0a\u6240\u6709\u7684\u65b9\u6cd5\u3002</p>\n<p>\u5143\u7f16\u7a0b\u7684\u4e00\u90e8\u5206\u601d\u60f3\u4e5f\u5c31\u662f\u52a8\u6001\u7684\u4fee\u6539\u3001\u521b\u9020\u3001\u8f6c\u53d1\u65b9\u6cd5\u3002\u8fd8\u6709 \u300a\u5143\u7f16\u7a0b\u300b\u91cc\u9762\u63d0\u5230\u7684 \u201c\u81ea\u7531\u65b9\u6cd5\u201d\u6211\u7684\u7406\u89e3\u5c31\u50cf\u662f\u628a\u7ee7\u627f\u94fe\u4e2d\u67d0\u4e9b\u65b9\u6cd5\u590d\u5236\uff0c\u7136\u540e\u7c98\u8d34\u5230\u5f53\u524d\u5bf9\u8c61\u6267\u884c\uff0c\u5728\u7ee7\u627f\u94fe\u4e0a\u8df3\u8dc3\u6267\u884c\u65b9\u6cd5\u2026\u2026</p>\n<p>\u8fd9\u4e00\u5207\u90fd\u662f\u4e3a\u4e86\u6781\u5927\u5730\u81ea\u7531\u3002</p>\n<p>\u6211\u4ee5\u524d\u4e00\u81f4\u4e0d\u592a\u7406\u89e3\u201cRuby \u662f\u5feb\u4e50\u4f18\u5148\u201d\u8fd9\u53e5\u8bdd\u662f\u4ec0\u4e48\u610f\u601d\uff0c\u73b0\u5728\u6211\u7684\u7406\u89e3\u2014\u2014\u8fd9\u79cd\u5feb\u4e50\u5c31\u662f\u81ea\u7531\uff0c\u62e5\u6709\u81ea\u7531\u7684\u5feb\u4e50\u3002</p>\n<h1>\u5176\u4ed6</h1>\n<p>\u56fe\u7247\u6765\u81ea\u6587\u7ae0</p>\n<ul>\n<li><a href=\"https://draveness.me/metaprogramming/\" rel=\"nofollow\">\u8c08\u5143\u7f16\u7a0b\u4e0e\u8868\u8fbe\u80fd\u529b</a></li>\n</ul>\n<p>\u5b89\u5229\u4e00\u6ce2\u4f5c\u8005\u56fe\u7247\u914d\u8272</p>\n<ul>\n<li><a href=\"https://draveness.me/sketch-and-sketch/\" rel=\"nofollow\">\u6280\u672f\u6587\u7ae0\u914d\u56fe\u6307\u5357</a></li>\n</ul>\n<h1>\u53c2\u8003</h1>\n<p>\u4e66\u7c4d\u63a8\u8350 <a href=\"https://book.douban.com/subject/26575429/\" rel=\"nofollow\">\u300a Ruby \u5143\u7f16\u7a0b\uff08\u7b2c 2 \u7248)\u300b</a></p>\n<h1>BLOG</h1>\n<p>\u6709\u66f4\u597d\u7684\u65b9\u6cd5\u53ef\u4ee5\u544a\u8bc9\u6211\uff0c\u6211\u6700\u65b0\u5728\u5b66\u4e60 Ruby</p>\n<p>\u6700\u65b0\u7684\u4fee\u6539\u4f1a\u66f4\u65b0\u5728 BLOG</p>\n<p><a href=\"https://mark24code.github.io/ruby/2021/07/29/Ruby%E7%9A%84%E6%96%B9%E6%B3%95%E6%9F%A5%E6%89%BE%E5%86%8D%E5%BE%80%E5%89%8D%E4%B8%80%E6%AD%A5.html\" rel=\"nofollow\">\u6211\u7684\u535a\u5ba2</a></p>\n<p><a href=\"https://ruby-china.org/topics/41505\" rel=\"nofollow\">RubyChina \u8ba8\u8bba\u5e16</a></p>\n", 
      "date_published": "2021-07-29T10:51:58+00:00", 
      "title": "Ruby \u7684\u65b9\u6cd5\u67e5\u627e\u518d\u5f80\u524d\u4e00\u6b65", 
      "id": "https://www.v2ex.com/t/792532"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/XisucksYi", 
        "name": "XisucksYi", 
        "avatar": "https://cdn.v2ex.com/avatar/96e0/fcf5/467854_large.png?m=1624599525"
      }, 
      "url": "https://www.v2ex.com/t/780713", 
      "title": "\u6709\u5728\u6df1\u5733\u81f3\u7b80\u5929\u6210\u7684\u5417\uff1f \u6c42\u52a0\u5fae\u4fe1", 
      "id": "https://www.v2ex.com/t/780713", 
      "date_published": "2021-06-01T14:00:32+00:00", 
      "content_html": "<p>\u6211\u5fae\u4fe1\u662f Lebum1995</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/putaozhenhaochi", 
        "name": "putaozhenhaochi", 
        "avatar": "https://cdn.v2ex.com/avatar/aa58/d4b1/365763_large.png?m=1755822585"
      }, 
      "url": "https://www.v2ex.com/t/738885", 
      "title": "Ruby 3.0.0 Released", 
      "id": "https://www.v2ex.com/t/738885", 
      "date_published": "2020-12-25T06:10:52+00:00", 
      "content_html": "Ruby 3.0.0 Released<br /><br /> [Ruby 3.0.0 Released]: <a target=\"_blank\" href=\"https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/\" rel=\"nofollow noopener\">https://www.ruby-lang.org/en/news/2020/12/25/ruby-3-0-0-released/</a>"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Nonebodysam9", 
        "name": "Nonebodysam9", 
        "avatar": "https://cdn.v2ex.com/avatar/f4d5/e752/298047_large.png?m=1708314645"
      }, 
      "url": "https://www.v2ex.com/t/697379", 
      "title": "\u65b0\u5efa\u4e86\u4e2a ruby \u4e2d\u6587\u8ba8\u8bba\u7fa4", 
      "id": "https://www.v2ex.com/t/697379", 
      "date_published": "2020-08-11T07:04:08+00:00", 
      "content_html": "discord \u7684\uff0c\u7528\u8d77\u6765\u611f\u89c9\u4e0d\u9519<br /><br />\u9080\u8bf7\u94fe\u63a5 <a target=\"_blank\" href=\"https://discord.gg/jQ9yQ9g\" rel=\"nofollow noopener\">https://discord.gg/jQ9yQ9g</a>"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/celadevra", 
        "name": "celadevra", 
        "avatar": "https://cdn.v2ex.com/avatar/467e/7933/21569_large.png?m=1587781884"
      }, 
      "url": "https://www.v2ex.com/t/691065", 
      "title": "Language Hopping\uff0c Ruby \u4e3a\u4ec0\u4e48\u4e0d\u706b\uff0c\u4e00\u70b9\u778e\u60f3", 
      "id": "https://www.v2ex.com/t/691065", 
      "date_published": "2020-07-18T02:12:01+00:00", 
      "content_html": "<p>\u5728\u6280\u672f\u6808\u95f4\u8df3\u6765\u8df3\u53bb\uff0c\u8bf4\u8d77\u6765\u4e5f\u662f\u8fc1\u5c31\u81ea\u5df1\u7684 ego \u800c\u4e0d\u8003\u8651<a href=\"https://expoundite.net/toot/f568247a-5564-4cc3-8b23-ccb34f4e53d6/\" rel=\"nofollow\">\u89e3\u51b3\u5b9e\u9645\u95ee\u9898</a>\u7684\u5fc3\u6001\u4f5c\u795f\u3002\u8001\u662f\u9009\u62e9\u65b0\u7684\u8bed\u8a00\u3001\u6846\u67b6\u5c31\u66f4\u662f\u5982\u6b64\u3002</p>\n<p>\u8fd9\u80cc\u540e\u7684\u52a8\u529b\u4e00\u90e8\u5206\u662f\u88ab\u666f\u4ef0\u548c\u5c0a\u91cd\u7684\u5fc3\u7406\u9700\u8981\uff08\u522b\u4eba\u90fd\u4e0d\u4f1a\u7528\u7684\u6280\u672f\u6211\u662f\u5927\u725b\uff09\uff0c\u53e6\u4e00\u90e8\u5206\u5219\u662f\u5bf9\u67d0\u9879\u6280\u672f\u7684\u793e\u7fa4\u5f62\u6210\u4e86\u4e00\u4e2a\u56fe\u50cf\uff0c\u5e0c\u671b\u522b\u4eba\u7528\u8fd9\u4e2a\u56fe\u50cf\u6765\u63cf\u8ff0\u81ea\u5df1\u548c\u8ba4\u540c\u81ea\u5df1\u3002</p>\n<p>\u867d\u7136\u8fd9\u79cd\u5fc3\u6001\u4e5f\u8ba9\u6211\u83b7\u5f97\u4e86\u4e00\u4e9b\u6709\u8da3\u548c\u6709\u7528\u7684\u7ecf\u9a8c\uff0c\u6bd4\u5982 Emacs\uff0c\u4f46\u603b\u5730\u8bf4\u6765\u662f\u8ba9\u6211\u6d6a\u8d39\u4e86\u592a\u591a\u7684\u65f6\u95f4\u5728\u8fc7\u5404\u79cd tutorial \u548c\u5199 hello world \u4e0a\u3002Language hopping \u7684\u7ed3\u679c\u5c31\u662f\u59cb\u7ec8\u65e0\u6cd5\u6df1\u5165\u94bb\u7814\u4e00\u4e2a\u4e1c\u897f\uff0c\u4e5f\u5c31\u4e0d\u77e5\u9053\u5b83\u80fd\u5e72\u4ec0\u4e48\uff0c\u5b83\u7684\u5c40\u9650\u5728\u54ea\u91cc\u3002</p>\n<p>\u8fd9\u6837\u770b\u6765\uff0cEmacs \u7684\u5389\u5bb3\u4e4b\u5904\u5728\u4e8e\u201c\u4ece\u5165\u95e8\u5230\u7cbe\u901a\u201d\uff08\u7cbe\u901a\u662f\u4e2a\u5883\u754c\uff0c\u8fd9\u8f88\u5b50\u90fd\u4e0d\u53ef\u80fd\u7cbe\u901a\u7684\uff09\u63d0\u4f9b\u4e86\u4e00\u6761\u8fde\u7eed\u800c\u65e0\u7a77\u5c3d\u7684\u5b66\u4e60\u9014\u5f84\uff0c\u53ea\u8981\u662f\u548c\u6587\u672c\u6709\u5173\u7684\u95ee\u9898\uff0c\u90fd\u53ef\u4ee5\u5c1d\u8bd5\u7740\u7528\u5b83\u53bb\u89e3\u51b3\uff0c\u5e76\u4e14\u5728\u8fd9\u4e2a\u8fc7\u7a0b\u4e2d\u53ef\u80fd\u53c8\u5b66\u5230\u4e86\u4e00\u70b9\u65b0\u4e1c\u897f\u3002Python \u4e5f\u662f\u7c7b\u4f3c\uff0c\u5165\u95e8\u95e8\u69db\u4f4e\uff0c\u5929\u82b1\u677f\u51e0\u4e4e\u65e0\u9650\u9ad8\uff0c\u4f46\u4e2d\u95f4\u6bcf\u4e00\u5c42\u90fd\u53ef\u4ee5\u63a2\u7d22\u3002</p>\n<p>\u800c Ruby \u7684\u95ee\u9898\u5927\u6982\u5c31\u662f\u8fd9\u4e48\u591a\u5e74 Rails \u72ec\u9886\u98ce\u9a9a\uff0c\u57fa\u672c\u4e0a\u8981\u6c42\u5b66\u4e60\u8005\u5199\u5b8c hello world \u548c fizzbuzz \u4e4b\u540e\u5c31\u5f00\u59cb\u7814\u7a76\u5143\u7f16\u7a0b\u3001DSL \u548c Rails \u7684\u5404\u79cd\u4ee4\u4eba\u773c\u82b1\u7f2d\u4e71\u7684\u5b9e\u73b0\u6280\u5de7\uff0c\u7f3a\u4e4f\u66f4\u5e7f\u6cdb\u7684\u95ee\u9898\u57df\u548c\u8fde\u7eed\u7684\u5b66\u4e60\u8def\u5f84\u3002</p>\n<p>\u4f5c\u4e3a\u6bd4\u8f83\u65b0\u7684\u8bed\u8a00\uff0cRacket \u548c Roku \u4e5f\u6709\u7c7b\u4f3c\u7684\u95ee\u9898\uff08\u4e0d\u77e5\u9053\u4e3a\u4ec0\u4e48\u6211\u5bf9 R \u5f00\u5934\u7684\u8bed\u8a00\u6bd4\u8f83\u611f\u5174\u8da3\uff09\u3002\u4e0d\u8fc7\u6211\u8fd8\u662f\u60f3\u5b66\u597d Ruby \u53bb\u7ed9 DHH \u6253\u5de5\u7684\uff0c\u5c3d\u7ba1\u53ea\u4f1a fizzbuzz \uff08\u54ed</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/fxjson", 
        "name": "fxjson", 
        "avatar": "https://cdn.v2ex.com/gravatar/3699e3a269b5b051942d2e082a8b2ab2?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/682185", 
      "date_modified": "2020-06-16T18:15:47+00:00", 
      "content_html": "<p>\u73a9\u8fc7\u4e00\u6bb5\u65f6\u95f4\u7684 ror, \u611f\u89c9\u633a\u723d\uff0c\u7279\u522b\u662f\u547d\u4ee4\u884c\u5de5\u5177\uff0c\u73b0\u5728\u601d\u60f3\u88ab php,python \u6284\u7684\u5dee\u4e0d\u591a\u4e86\uff0c\u770b\u770b ruby china \u793e\u533a\uff0c\u8fd8\u6709 v2 \u7684  ruby \u8282\u70b9\uff0c\u5f80\u65e5\u4e0d\u518d\uff0c\u597d\u53ef\u60dc\uff0c\u5e0c\u671b ruby3*3 \u80fd\u8d77\u6765\u5427</p>\n", 
      "date_published": "2020-06-16T14:09:01+00:00", 
      "title": "ruby,\u4e00\u4ee3\u4f18\u79c0\u8bed\u8a00\u5c31\u8fd9\u6837\u9668\u843d\u4e86\u5417\uff1f", 
      "id": "https://www.v2ex.com/t/682185"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/yykrlc", 
        "name": "yykrlc", 
        "avatar": "https://cdn.v2ex.com/avatar/43f1/ddfc/236979_large.png?m=1498269608"
      }, 
      "url": "https://www.v2ex.com/t/681993", 
      "title": "ruby \u7684 \"a\" \u548c :a \u80fd\u8f6c\u6362\u5417", 
      "id": "https://www.v2ex.com/t/681993", 
      "date_published": "2020-06-16T03:52:34+00:00", 
      "content_html": "ruby \u7684 x = {\"a\": 1 \"b\": 2}<br />y = 'b'<br />&gt; x[y]<br />=&gt; nil <br />&gt; x[:b]<br />=&gt; 2<br />\u600e\u4e48\u529e\uff0c\u6211\u7684\u53d8\u91cf\u662f\u5b57\u7b26\u4e32"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/freeduke", 
        "name": "freeduke", 
        "avatar": "https://cdn.v2ex.com/gravatar/f1b41b728e2a66968a7b641af3d295a8?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/674972", 
      "date_modified": "2020-05-24T10:59:44+00:00", 
      "content_html": "\u670d\u52a1\u5668\u5728\u56fd\u5185\uff0c\u817e\u8baf\u4e91 1C 2G\uff0ccentOS7\uff0c<br />\u8bf7\u8054\u7cfb\u6211\u7684 yduke # <a target=\"_blank\" href=\"http://qq.com\" rel=\"nofollow noopener\">qq.com</a>", 
      "date_published": "2020-05-24T10:59:09+00:00", 
      "title": "\u6c42\u5927\u4f6c\u5e2e\u5b89\u88c5\u4e00\u4e2a mastodon\uff0c\u6709\u507f\uff01", 
      "id": "https://www.v2ex.com/t/674972"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/ColinChang", 
        "name": "ColinChang", 
        "avatar": "https://cdn.v2ex.com/gravatar/c855c9f9f87fa23c43efd537baf4710b?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/651562", 
      "date_modified": "2020-03-10T08:36:28+00:00", 
      "content_html": "\u6211\u7279\u522b\u559c\u6b22\u7528 notepad ssh \u5230\u5b66\u6821 linux \u670d\u52a1\u5668\u64b8\u4ee3\u7801<br />\u6700\u8fd1\u5199\u811a\u672c\u8bed\u8a00\uff08 perl,ruby \uff09\u7684\u65f6\u5019\uff0c\u7ecf\u5e38\u4f1a\u62a5\u5404\u79cd\u7a00\u5947\u53e4\u602a\u7684\u9519\u3002\u603b\u7ed3\u4e00\u4e0b\u5fc3\u5f97\u3002<br /><br /><br /><a target=\"_blank\" href=\"https://i.imgur.com/F92nbns.png\" rel=\"nofollow noopener\" target=\"_blank\"><img src=\"https://i.imgur.com/F92nbns.png\" class=\"embedded_image\" rel=\"noreferrer\"></a>[/img]<br />\u770b\u8d77\u6765\u5f88\u6b63\u5e38\uff0c\u4f46\u662f\u9519\u8bef\u63d0\u793a\u627e\u4e0d\u5230\u6587\u4ef6\uff0c\u6240\u4ee5\u6000\u7591\u662f\u7b2c\u4e00\u884c\u6ca1\u6709\u6210\u529f\u8c03\u7528 ruby\u3002<br />\u8bd5\u8bd5\u7528 ruby \u624b\u52a8\u8fd0\u884c\u811a\u672c\uff0c\u679c\u7136\u63d0\u793a shellbang \u6709\u4e00\u4e2a\\r\uff0c\u53ef\u80fd\u4f1a\u9020\u6210\u95ee\u9898\u3002<br /><br />[img]<a target=\"_blank\" href=\"https://i.imgur.com/L1lCz5Z.png\" rel=\"nofollow noopener\" target=\"_blank\"><img src=\"https://i.imgur.com/L1lCz5Z.png\" class=\"embedded_image\" rel=\"noreferrer\"></a>[/img]<br />\u8f6c\u6210 16 \u8fdb\u5236\u770b\u4e0b\uff0c\u67e5\u4e86\u4e0b ASCII \u5b57\u7b26 16 \u8fdb\u5236\u7684\u4ee3\u7801\uff0c\u679c\u7136\u4e00\u5806 CR \u7b26 \\r 0x0d,windows \u7cfb\u7edf\u7684\u6362\u884c\u662f\\r\\n\uff0c\u5148\u56de\u8f66(carriage return)\uff0c\u518d\u6362\u884c(line feed)\uff0c\u53ef\u4ee5\u8ffd\u6eaf\u5230\u6253\u5b57\u673a\u65f6\u4ee3\u3002\u800c linux \u76f4\u63a5\\n \u5c31\u6362\u884c\u4e86<br /><br />[img]<a target=\"_blank\" href=\"https://i.imgur.com/rpxlZYm.png\" rel=\"nofollow noopener\" target=\"_blank\"><img src=\"https://i.imgur.com/rpxlZYm.png\" class=\"embedded_image\" rel=\"noreferrer\"></a>[/img]<br />\u6700\u7b80\u5355\u65b9\u6cd5\uff0c\u5199\u4e2a\u811a\u672c\u4e8c\u8fdb\u5236\u6253\u5f00\u7a0b\u5e8f\u6587\u672c\uff0c\u5220\u9664\u6240\u6709 0d\u3002\u8bb0\u5f97\u53bb\u5e74\u4e0a socket \u7f16\u7a0b\u7684\u65f6\u5019\uff0c\u8001\u5e08\u7b2c\u4e00\u4e2a\u6559\u7684\u7a0b\u5e8f\u5c31\u662f\u5728 linux \u548c windows \u4e4b\u95f4\u8f6c\u6362\u6587\u672c\uff0c\u5c31\u662f\u8fd9\u6837\u5b9e\u73b0\u7684<br /><br />\u4f46\u662f\u8fd9\u6837\u7684\u8bdd\uff0c\u6bcf\u6b21\u5199\u4e24\u884c\u60f3\u6d4b\u8bd5\u7684\u65f6\u5019\uff0c\u5c31\u5f97\u624b\u52a8\u8fd0\u884c\u4e0b\u8f6c\u6362\u7a0b\u5e8f\uff0c\u989d\u5916\u589e\u52a0\u4e86\u8d1f\u62c5\uff08\u6211\u4fdd\u5b58 ctrl S \u90fd\u61d2\u5f97\u6309\uff0c\u7528\u7684\u662f notepad++\u7684\u7a97\u53e3\u5931\u53bb\u7126\u70b9\u81ea\u52a8\u4fdd\u5b58\u3002\u3002\u3002\uff09<br /><br />\u4f46\u6211\u53c8\u4e0d\u60f3\u7528 ssh \u5ba2\u6237\u7aef\u7684\u5783\u573e\u7f16\u8f91\u5668\uff0c\u4e8e\u662f\u5c06\u5c31\u4e00\u4e0b\uff0c\u624b\u52a8\u518d linux \u91cc\u7528 vim \u6572\u7b2c\u4e00\u884c\uff0c\u5269\u4e0b\u7684 notepad++ ssh \u5c31\u53ef\u4ee5\u7ee7\u7eed\u5199\u4e86<br /><br /><br />\u8fd8\u6709\u4ec0\u4e48\u5751\uff0c\u60f3\u60f3\u7ee7\u7eed\u7801\u5b57", 
      "date_published": "2020-03-10T08:28:08+00:00", 
      "title": "notepad++ ssh \u4e00\u4e9b\u4e2a\u5c0f\u5751", 
      "id": "https://www.v2ex.com/t/651562"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/junho", 
        "name": "junho", 
        "avatar": "https://cdn.v2ex.com/gravatar/14296d63a2229450e57bc3a7eaba685a?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/602859", 
      "title": "\u6709\u4ec0\u4e48\u597d\u7684\u65b9\u6cd5/IDE \u9605\u8bfb Gem \u5e93\u7684\u6e90\u7801\uff1f", 
      "id": "https://www.v2ex.com/t/602859", 
      "date_published": "2019-09-21T08:36:08+00:00", 
      "content_html": "\u5e73\u65f6\u5f00\u53d1\u4f7f\u7528 CocoaPod \u6bd4\u8f83\u591a\uff0c\u6253\u7b97\u770b\u4e0b\u6e90\u7801\u4ee5\u4fbf\u66f4\u597d\u5730\u8f85\u52a9\u5f00\u53d1\u3002<br />\u5df2\u7ecf\u5b66\u4e86 Ruby \u7684\u4e00\u4e9b\u57fa\u7840\u77e5\u8bc6\uff0c\u4f46\u662f\u9605\u8bfb\u8fc7\u7a0b\u4e2d\u6ca1\u6709 IDE \u5de5\u5177\uff0c\u4e0d\u80fd\u8df3\u8f6c\u67e5\u770b\u65b9\u6cd5\u8c03\u7528\u5565\u7684\u5f88\u4e0d\u65b9\u4fbf<br />\u8bf7\u95ee\u4e0b\u6709\u6ca1\u6709\u4ec0\u4e48 IDE\uff0c\u53ef\u4ee5\u628a\u6574\u4e2a\u5de5\u7a0b\u5bfc\u8fdb\u53bb\u65b9\u4fbf\u770b\u6e90\u7801\u7684\uff1f"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Kronos", 
        "name": "Kronos", 
        "avatar": "https://cdn.v2ex.com/gravatar/9e648736233d9b00e52d53278ae40f14?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/596019", 
      "title": "\u95ee\u4e00\u4e2a ruby \u7684\u95ee\u9898", 
      "id": "https://www.v2ex.com/t/596019", 
      "date_published": "2019-08-28T13:12:18+00:00", 
      "content_html": "<p>ruby \u4e2d\u7684&gt;&gt;\u4ec0\u4e48\u610f\u601d\uff1f 2880000000 &gt;&gt; 10 \u8ba1\u7b97\u7684\u7ed3\u679c\u662f\u591a\u5c11\uff1f\n\u4e4b\u524d\u4e5f\u6ca1\u6709\u63a5\u89e6\u8fc7 ruby\uff0c\u4eca\u5929\u770b\u4e86\u4e00\u4e2a ruby \u5904\u7406\u6570\u636e\u7684\u811a\u672c\uff0c\u524d\u9762\u770b\u5f97\u7565\u61c2\uff0c\u5230&gt;&gt;\u8fd9\u5c31\u5361\u4f4f\u4e86 \u8001\u54e5\u4eec\uff0c\u5e2e\u5e2e\u5fd9\u89e3\u91ca\u4e00\u4e0b\uff1a\uff09</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/chinesestudio", 
        "name": "chinesestudio", 
        "avatar": "https://cdn.v2ex.com/avatar/9191/2c04/371581_large.png?m=1556562307"
      }, 
      "url": "https://www.v2ex.com/t/594942", 
      "title": "\u7528 discourse \u642d\u4e86\u4e2a\u8bba\u575b \u6c42\u6d4b\u8bd5", 
      "id": "https://www.v2ex.com/t/594942", 
      "date_published": "2019-08-25T09:19:50+00:00", 
      "content_html": "<p>\u5927\u5bb6\u53ef\u4ee5\u6765\u6d4b\u8bd5\u611f\u53d7\u4e00\u4e0b</p>\n<p>\u5305\u62ec\u6709\u5584\u7684\u538b\u6d4b</p>\n<p>\u8bba\u575b\u6ca1\u5565\u5185\u5bb9</p>\n<p>\u7a0b\u5e8f\u733f\u4e5f\u4e0d\u662f\u76ee\u6807\u7528\u6237</p>\n<p>\u6240\u4ee5\u522b\u8ba4\u4e3a\u662f\u63a8\u5e7f\u5c31\u884c</p>\n<p><a href=\"https://forums.taosky.com\" rel=\"nofollow\">https://forums.taosky.com</a></p>\n<p>\u8fdc\u7a0b cloudflare nginx frps</p>\n<p>\u672c\u5730 frpc discourse</p>\n<p>\u6d4b\u8bd5\u9636\u6bb5</p>\n<p>\u53ea\u6c42\u80fd\u7528 \u901f\u5ea6\u5c31\u522b\u6307\u671b\u4e86\u592a\u5feb</p>\n<p>\u4e2d\u6587\u5206\u8bcd\u8fd8\u662f\u6ca1\u5b8c\u5584</p>\n<p>\u5bfc\u81f4\u641c\u7d22\u548c\u654f\u611f\u8bcd\u8fc7\u6ee4\u6548\u679c\u4e00\u822c</p>\n<p>\u5176\u4ed6\u95ee\u9898\u6162\u6162\u53d1\u73b0\u89e3\u51b3</p>\n<p>\u5148\u8c22\u5566</p>\n<p>discourse \u7684\u95ee\u9898\u4e5f\u53ef\u4ee5\u95ee\u6211</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/jfry", 
        "name": "jfry", 
        "avatar": "https://cdn.v2ex.com/gravatar/55b09f9dd3b9f2d95c3fc520155298fc?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/594596", 
      "date_modified": "2019-08-23T11:05:08+00:00", 
      "content_html": "<p>\u5df2\u7ecf\u5b9e\u73b0\u4e86\u4e00\u4e9b\u57fa\u672c\u7684\u529f\u80fd\u4e86\uff0c\u4f46\u662f\u8fd8\u6709\u5f88\u591a\u5f85\u5b8c\u5584\u548c\u6539\u8fdb\u7684\u5730\u65b9\uff0c\u627e\u611f\u5174\u8da3\u7684\u5c0f\u4f19\u4f34\u4e00\u8d77\u6765\u5f00\u53d1~</p>\n<p>\u4e00\u4e2a\u6bd4\u8f83\u5f53\u524d\u63d0\u4ea4\u548c\u4e0a\u6b21\u63d0\u4ea4\u4fee\u6539\u7684\u4ee3\u7801\u7684\u8986\u76d6\u7387\u7684\u4f8b\u5b50\uff1a</p>\n<pre><code class=\"language-bash\">$ incrcov HEAD^ HEAD\n\n+----------------+-----------+-------------+---------------+---------------+--------------+\n| Path           | Method    | Total Lines | Covered Lines | Coverage Rate | Missed Lines |\n+----------------+-----------+-------------+---------------+---------------+--------------+\n| app/demo2.rb:6 | say_world | 2           | 1             | 50.0%         | 7            |\n+----------------+-----------+-------------+---------------+---------------+--------------+\nOverall incremental test coverage: 75.0%\nNumber of updated methods: 2\nNumber of low test coverage(&lt;90%) methods: 1\n</code></pre>\n<p>\u9879\u76ee\u5730\u5740\uff1a <a href=\"https://github.com/toaco/incrcov\" rel=\"nofollow\">https://github.com/toaco/incrcov</a></p>\n", 
      "date_published": "2019-08-23T11:02:15+00:00", 
      "title": "\u627e\u5c0f\u4f19\u4f34\u4e00\u8d77\u5f00\u53d1\u548c\u5b8c\u5584\u4e00\u4e2a Ruby \u589e\u91cf\u4ee3\u7801\u8986\u76d6\u7387\u7edf\u8ba1\u5de5\u5177", 
      "id": "https://www.v2ex.com/t/594596"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/ycz0926", 
        "name": "ycz0926", 
        "avatar": "https://cdn.v2ex.com/avatar/8736/3191/212962_large.png?m=1568379868"
      }, 
      "url": "https://www.v2ex.com/t/581441", 
      "title": "\u90fd\u5728\u8bf4\u6291\u90c1\uff0c\u6211\u4eec\u5b66\u4e00\u4e0b ruby \u5427\uff0c\u4e00\u95e8\u8ba9\u4f60\u5feb\u4e50\u7684\u8bed\u8a00", 
      "id": "https://www.v2ex.com/t/581441", 
      "date_published": "2019-07-09T11:01:33+00:00", 
      "content_html": "<p><a href=\"https://rubymonk.com/\" rel=\"nofollow\">https://rubymonk.com/</a></p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/TangMonk", 
        "name": "TangMonk", 
        "avatar": "https://cdn.v2ex.com/avatar/c5f1/ae9e/55793_large.png?m=1644466105"
      }, 
      "url": "https://www.v2ex.com/t/579758", 
      "title": "ruby2.6 \u7684 jit \u5927\u5bb6\u611f\u89c9\u600e\u4e48\u6837\uff1f", 
      "id": "https://www.v2ex.com/t/579758", 
      "date_published": "2019-07-03T12:18:58+00:00", 
      "content_html": ""
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/hujianxin", 
        "name": "hujianxin", 
        "avatar": "https://cdn.v2ex.com/avatar/9024/ae2d/97887_large.png?m=1555229655"
      }, 
      "url": "https://www.v2ex.com/t/510051", 
      "title": "\u5728 one-liner \u6587\u672c\u5904\u7406\u65b9\u9762\uff0c ruby \u6bd4 perl \u8981\u900a\u8272\u5417\uff1f", 
      "id": "https://www.v2ex.com/t/510051", 
      "date_published": "2018-11-21T07:29:13+00:00", 
      "content_html": "<p>\u4e4b\u524d\u6ca1\u63a5\u89e6\u8fc7 ruby\uff0c\u6240\u4ee5\u8fc7\u6765\u8bf7\u6559\u5927\u4f6c\uff0c\u8c22\u8c22</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/scottverne", 
        "name": "scottverne", 
        "avatar": "https://cdn.v2ex.com/gravatar/0b00a68b843a931c6b1c225f8ea17a22?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/505120", 
      "title": "\u4f7f\u7528 ruby \u5199\u4e86\u4e00\u4e2a\u5468\u62a5\u7cfb\u7edf", 
      "id": "https://www.v2ex.com/t/505120", 
      "date_published": "2018-11-06T10:48:21+00:00", 
      "content_html": "<p>\u5927\u5bb6\u53ef\u4ee5\u4f53\u9a8c <a href=\"http://www.weeklyreport.info/scottverne/2018/11/1\" rel=\"nofollow\">http://www.weeklyreport.info/scottverne/2018/11/1</a></p>\n<p>\u4f7f\u7528 sinatra \u6846\u67b6\u5f00\u53d1\u7684\u3002</p>\n<p><img src=\"http://www.weeklyreport.info/upload/2018/11/6/Screenshot%20from%202018-11-06%2005-45-15.png\" alt=\"\"></p>\n<p>github \u5728 <a href=\"https://github.com/weeklyreportinfo/weeklyreportinfo\" rel=\"nofollow\">https://github.com/weeklyreportinfo/weeklyreportinfo</a></p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/asensio", 
        "name": "asensio", 
        "avatar": "https://cdn.v2ex.com/avatar/f413/558b/253904_large.png?m=1702800353"
      }, 
      "url": "https://www.v2ex.com/t/489122", 
      "title": "ruby \u7684 devkit \u5b89\u88c5", 
      "id": "https://www.v2ex.com/t/489122", 
      "date_published": "2018-09-13T14:11:54+00:00", 
      "content_html": "<p>\u5b89\u88c5 Ruby \u65f6\uff0c\u5b98\u7f51( <a href=\"https://rubyinstaller.org/downloads/)\u7ed9\u4e86\" rel=\"nofollow\">https://rubyinstaller.org/downloads/)\u7ed9\u4e86</a> WITH DEVKIT \u7684 ruby \u5b89\u88c5\u5305\uff0c \u53ef\u662f\u6211\u5168\u7a0b\u5b89\u88c5\u5b8c\u540e\uff0c\u8fd8\u662f\u53d1\u73b0 Devkit \u6ca1\u6709\u88ab\u5b89\u88c5\u554a\uff1f</p>\n<p>\u201c WITH DEVKIT \u201d\u662f\u5565\u610f\u601d\u554a\uff1f</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/SimbaPeng", 
        "name": "SimbaPeng", 
        "avatar": "https://cdn.v2ex.com/gravatar/644f3f438e1280eb32e78f1aa72929f6?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/421095", 
      "title": "\u603b\u542c\u4eba\u8bf4 ruby \u4ee3\u7801\u5199\u8d77\u6765\u5f88\u723d\uff0c\u6709\u591a\u723d\uff1f\u600e\u4e48\u4e2a\u723d\u6cd5\uff1f", 
      "id": "https://www.v2ex.com/t/421095", 
      "date_published": "2018-01-08T08:39:29+00:00", 
      "content_html": ""
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/iosGong", 
        "name": "iosGong", 
        "avatar": "https://cdn.v2ex.com/gravatar/47669605f2679cbcda084ae35bb53696?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/389303", 
      "title": "RubyGems \u6ce8\u518c\u4e0d\u4e86\u7684\uff1f", 
      "id": "https://www.v2ex.com/t/389303", 
      "date_published": "2017-09-08T13:46:32+00:00", 
      "content_html": "<p>\u6ce8\u518c\u4e4b\u540e\uff0c\u9a8c\u8bc1\u90ae\u7bb1\uff0c\u5728\u767b\u5f55\u9875\u8fd8\u662f\u63d0\u9192\u9a8c\u8bc1\u90ae\u7bb1\uff0c\u4ec0\u4e48\u9b3c\uff1f\uff1f\uff1f</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/syhsyh9696", 
        "name": "syhsyh9696", 
        "avatar": "https://cdn.v2ex.com/avatar/49d9/209e/149313_large.png?m=1657100198"
      }, 
      "url": "https://www.v2ex.com/t/345083", 
      "title": "Ruby \u7834\u73af\u65b9\u6cd5\u4e3a\u4ec0\u4e48\u6bd4\u975e\u7834\u574f\u65b9\u6cd5\u6162\u4e00\u4e9b\uff1f", 
      "id": "https://www.v2ex.com/t/345083", 
      "date_published": "2017-03-05T08:24:33+00:00", 
      "content_html": "<p>\u505a leetcode \u7684\u65f6\u5019\u6211\u53d1\u73b0</p>\n<p>nums.sort! \u660e\u663e\u8981\u6bd4 nums = nums.sort \u8981\u6162 10ms</p>\n<p>\u8fd9\u662f\u4e3a\u4ec0\u4e48\u5462\uff1f sort!\u7684\u64cd\u4f5c\u66f4\u591a\uff1f\u867d\u7136 sort!\u5199\u8d77\u6765\u683c\u5916\u7684\u65b9\u4fbf\uff0c\u4f46\u662f\u5403\u4e86\u5f88\u591a\u65f6\u95f4\u7684\u4e8f\u3002</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/cosmosz", 
        "name": "cosmosz", 
        "avatar": "https://cdn.v2ex.com/gravatar/bfda0b5cb0c413c429d2851ba8dbcf7f?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/338415", 
      "title": "Ruby Conference 2017 AU", 
      "id": "https://www.v2ex.com/t/338415", 
      "date_published": "2017-02-06T04:37:29+00:00", 
      "content_html": "<p>\u8bf7\u95ee\u6709\u4eba\u53bb\u561b\uff1f</p>\n<p>\u6211\u5728\u6089\u5c3c\uff0c Ruby On Rails Backend \u5f00\u53d1\u3002</p>\n<p>\u6709\u5174\u8da3\u7684\u53ef\u4ee5\u4e00\u8d77\u4ea4\u6d41\u4e0b\u3002</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/Gem", 
        "name": "Gem", 
        "avatar": "https://cdn.v2ex.com/gravatar/4506e57d77d4bba333eaa2c9ba89551c?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/331493", 
      "date_modified": "2016-12-31T13:51:22+00:00", 
      "content_html": "\u73b0\u5728 ruby \u4e3b\u8981\u5728 web \u5f00\u53d1\u65b9\u9762\uff0c python \u4e0d\u4f46\u5728 web \u65b9\u9762\uff0c\u5728\u5176\u4ed6\u65b9\u9762\u4e5f\u591a\u6709\u5efa\u6811\uff0c\u5982\u679c\u8bf4\u6027\u80fd\u65b9\u9762\uff0c\u90fd\u662f\u811a\u672c\u8bed\u8a00\uff0c ruby \u4e5f\u6ca1\u5dee\u591a\u5c11\u5427\uff1f\u5728\u5199\u6cd5\u65b9\u9762\uff0c\uff08\u7eaf OOP + FP \u5473\u9053\uff09\uff0c ruby \u5199\u8d77\u6765\uff0c\u4e2a\u4eba\u611f\u89c9\u8fd8\u662f\u8981\u6bd4 python \u6d41\u7545\u4e00\u4e9b\u7684\u3002", 
      "date_published": "2016-12-31T12:58:16+00:00", 
      "title": "ruby \u8fd9\u4e48\u4f18\u96c5\u7684\u8bed\u8a00\uff0c\u600e\u4e48\u6ca1\u6709\u50cf python \u8fd9\u6837\u6d41\u884c\u5f00\u6765\uff1f", 
      "id": "https://www.v2ex.com/t/331493"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/rockllei", 
        "name": "rockllei", 
        "avatar": "https://cdn.v2ex.com/avatar/4916/ba26/40887_large.png?m=1482224375"
      }, 
      "url": "https://www.v2ex.com/t/313785", 
      "title": "Ruby \u4e2d Singleton \u65b9\u6cd5\u7684\u53e6\u4e00\u79cd\u7406\u89e3\u65b9\u5f0f", 
      "id": "https://www.v2ex.com/t/313785", 
      "date_published": "2016-10-19T02:34:21+00:00", 
      "content_html": "<p>Ruby \u4e2d\u7684 Singleton \u65b9\u6cd5\u662f Ruby \u4e2d\u4e00\u4e2a\u91cd\u8981\u7684\u77e5\u8bc6\u70b9\uff0c\u4e5f\u662f\u505a Ruby \u5143\u7f16\u7a0b\u7684\u4e00\u4e2a\u91cd\u8981\u6280\u5de7\u3002\n\u7b80\u5355\u7684\u8bf4\u5c31\u662f\u5728 Ruby \u4e2d\u4efb\u4f55\u81ea\u5b9a\u4e49\u7c7b\u90fd\u662f Class \u8fd9\u4e2a\u7c7b\u7684\u4e00\u4e2a\u5b9e\u4f8b\uff0c\u6240\u4ee5 Singleton \u65b9\u6cd5\u5176\u5b9e\u5c31\u662f\u4e00\u4e2a\u5b9e\u4f8b\u6240\u62e5\u6709\u7684\u65b9\u6cd5\uff0c\u6bd4\u5982:</p>\n<pre><code>a = \"hi\"\n\ndef a.you\n  \"hi,  you\"\nend\n\na.you # =&gt; hi, you\n\n\"hello\".you # =&gt; undefined method 'you'\n</code></pre>\n<p>\u4e0a\u9762\u7684 a.you \u65b9\u6cd5\u5c31\u662f a \u8fd9\u4e2a\u5b57\u7b26\u4e32\u5b9e\u4f8b\u7684 Singleton Method \u3002</p>\n<p>\u540c\u6837\u5982\u679c\u62ff\u6211\u4eec\u6700\u719f\u6089\u7684\u7c7b\u4e3e\u4f8b\u5b50\u7684\u8bdd\u5c31\u662f\uff1a</p>\n<pre><code>class A\n  def self.hi\n    'hi A'\n  end\nend\n\nA.hi # =&gt; hi A\n</code></pre>\n<p>\u4e0a\u9762\u7684\u5b9a\u4e49\u65b9\u6cd5\u6240\u6709\u7684\u540c\u5b66\u5e94\u8be5\u90fd\u77e5\u9053\uff0c\u5176\u5b9e\u5c31\u662f\u7c7b\u65b9\u6cd5\u7684\u5b9a\u4e49\uff0c\u5728 Ruby \u4e2d\u7c7b\u65b9\u6cd5\u5176\u5b9e\u5c31\u662f\u7c7b\u7684 Singleton Method \uff0c\u5c31\u50cf\u4e0a\u9762\u8bf4\u7684\u56e0\u4e3a A \u4e5f\u662f Class \u8fd9\u4e2a\u7c7b\u7684\u4e00\u4e2a\u5b9e\u4f8b\uff08\u901a\u8fc7 A.class \u5c31\u53ef\u4ee5\u77e5\u9053\uff09\uff0c\u6240\u4ee5\uff0c\u5176\u5b9e\u4e0a\u9762\u7684\u65b9\u6cd5\u4e5f\u53ef\u4ee5\u8fd9\u6837\u5b9a\u4e49\uff1a</p>\n<pre><code>class A\nend\n\ndef A.hi\n  'hi A'\nend\n\nA.hi # =&gt; hi A\n</code></pre>\n<p>\u8fd9\u6837\u7684\u8bdd\uff0c\u5c31\u548c\u4e0a\u9762\u5b57\u7b26\u4e32 a \u7684\u6548\u679c\u4e00\u6837\uff0c\u5c31\u66f4\u5bb9\u7406\u89e3\u4e86\u3002\n\u6240\u4ee5\u5173\u952e\u662f\u7406\u89e3\u6211\u4eec\u6240\u5b9a\u4e49\u7684\u7c7b\u4e5f\u662f Class \u8fd9\u4e2a\u7c7b\u7684\u4e00\u4e2a\u5b9e\u4f8b\uff0c\u8fd9\u662f\u5173\u952e\u3002</p>\n<hr>\n<p>\u5176\u5b9e\u6211\u60f3\u8bf4\u7684\uff0c\u4e0b\u9762\u624d\u662f\u5173\u952e\uff0c</p>\n<p>\u86cb\u4eba\u7f51 <a href=\"http://eggman.tv\" rel=\"nofollow\">http://eggman.tv</a> \u7684\u6700\u65b0\u7cfb\u5217\u5927\u8bfe\u7a0b&lt;Ruby \u5143\u7f16\u7a0b=\"\"&gt;\u5df2\u7ecf\u5f00\u5751\u4e86\uff0c\u4ece\u4eca\u5929\u5f00\u59cb\u4f1a\u9646\u7eed\u66f4\u65b0\uff0c\u7b2c\u4e00\u8282\u89c6\u9891\u8bfe\u4ef6&lt;Ruby \u5143\u7f16\u7a0b\u7684\u4ecb\u7ecd\u548c\u4f7f\u7528\u573a\u666f=\"\"&gt;\u5df2\u7ecf\u653e\u51fa\uff0c\u514d\u8d39\u7684\uff0c <a href=\"http://eggman.tv/c/s-ruby-meta-programming\" rel=\"nofollow\">http://eggman.tv/c/s-ruby-meta-programming</a> \uff0c\u8ba1\u5212\u6bcf\u5468\u66f4\u65b0\u4e24\u671f\uff0c\u4e0a\u9762\u8bb2\u8ff0\u7684\u662f\u8bfe\u4ef6\u4e00\u90e8\u5206\u5185\u5bb9\uff0c\u6211\u4eec\u8ba1\u5212\u4f1a\u5728\u6574\u4e2a\u8bfe\u4ef6\u4e2d\u4e3a\u5927\u5bb6\u8bb2\u8ff0\uff1a</p>\n<ul>\n<li>Ruby \u57fa\u7840\u7c7b\u7ed3\u6784</li>\n<li>Module Mixin \u8bbe\u8ba1\u6a21\u5f0f</li>\n<li>instance_eval \u548c class_eval \u7684\u4f7f\u7528\uff0c\u4ee5\u53ca*_eval \u7c7b\u548c*_exec \u7c7b\u7684\u533a\u522b</li>\n<li>Singleton \u65b9\u6cd5\u7684\u539f\u7406\u548c\u5404\u79cd\u4f7f\u7528\u573a\u666f</li>\n<li>ActiveSuppot::Concern \u7684\u5b9e\u73b0\u539f\u7406</li>\n<li>define_method &amp; method_missing</li>\n<li>Block \u7684\u8fdb\u9636\u4f7f\u7528</li>\n</ul>\n<p>\u7b49\u7b49\u4f17\u591a\u6df1\u5165\u5185\u5bb9\u7684\u8bb2\u89e3\uff0c\u5f53\u7136\u8fd8\u5305\u62ec\u4f17\u591a\u7684\u5b9e\u4f8b\u6f14\u793a\u548c\u5982\u4f55\u4f7f\u7528\u8fd9\u4e9b\u9ad8\u7ea7\u6280\u5de7\uff0c\u6b22\u8fce\u5404\u4f4d\u5927\u54e5\u5927\u5ac2\u524d\u6765\u652f\u6301\u3002</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/xinali", 
        "name": "xinali", 
        "avatar": "https://cdn.v2ex.com/avatar/cc6e/581c/111322_large.png?m=1711442898"
      }, 
      "url": "https://www.v2ex.com/t/306641", 
      "title": "\u9700\u8981\u5b66\u4e60 ruby\uff0c\u60f3\u95ee\u73b0\u5728 ruby \u7684\u4e3b\u6d41\u7248\u672c\u662f\u591a\u5c11\uff1f", 
      "id": "https://www.v2ex.com/t/306641", 
      "date_published": "2016-09-16T11:54:20+00:00", 
      "content_html": "<p>\u8ddf python \u5dee\u4e0d\u591a\uff0c\u6bd4\u5982 python \u73b0\u5728\u7684\u4e3b\u6d41\u7248\u672c\u662f 2.7 \uff0c\u4e3b\u8981\u7684\u5de5\u5177\u548c\u5e93\u4e5f\u90fd\u96c6\u4e2d\u5728 2.7</p>\n<p>\u90a3\u4e48 ruby \u7684\u4e3b\u6d41\u7248\u672c\u5462\uff0c\u4e3b\u8981\u662f\u4e3b\u6d41\u5e93\u554a\uff0c\u5de5\u5177\u4ec0\u4e48\u7684\u6240\u7528\u7684\u7248\u672c</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/popu111", 
        "name": "popu111", 
        "avatar": "https://cdn.v2ex.com/avatar/ffdd/1df8/59927_large.png?m=1491693344"
      }, 
      "url": "https://www.v2ex.com/t/290915", 
      "title": "Ruby \u7684\u5185\u5b58\u5360\u7528\u6781\u9650\uff1f", 
      "id": "https://www.v2ex.com/t/290915", 
      "date_published": "2016-07-07T08:42:12+00:00", 
      "content_html": "<p>\u6700\u8fd1\u6709\u4e9b\u95f2\u8bd5\u7740\u4f5c\u4e2a\u6b7b\uff0c\u5728 irb \u91cc\u6267\u884c<code>r = (1..99999999999999).to_a</code></p>\n<p>\u672c\u6765\u4ee5\u4e3a\u5f88\u5feb\u5c31\u80fd\u628a\u5185\u5b58\u5360\u5230 2G \u6765\u7740\uff0c\u7ed3\u679c\u624d 500M \u5c31\u62a5\u9519\u4e86</p>\n<p><img alt=\"Image 1.jpg\" src=\"https://ooo.0o0.ooo/2016/07/07/577e16fbd23b6.jpg\"></p>\n<p><img alt=\"Image 2.jpg\" src=\"https://ooo.0o0.ooo/2016/07/07/577e17b048bf5.jpg\"></p>\n<p>\uff08\u6709\u4e09\u4e2a\u8fdb\u7a0b\uff0c\u662f\u6211\u540e\u6765\u53c8\u5f00\u4e86\u4fe9\uff0c\u4f46\u662f\u8fd9\u6b21 700M \u624d\u62a5\u9519\uff09</p>\n<p>\u6240\u4ee5\u73b0\u5728\u95ee\u9898\u6765\u4e86\uff0c\u8fd9\u5230\u5e95\u5565\u60c5\u51b5\uff1f</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/shyling", 
        "name": "shyling", 
        "avatar": "https://cdn.v2ex.com/avatar/c129/6c1a/142212_large.png?m=1455339718"
      }, 
      "url": "https://www.v2ex.com/t/285989", 
      "title": "Ruby \u5199\u4e86\u4e00\u4e2a\u547d\u4ee4\u884c\u706b\u8f66\u7968\u67e5\u770b\u5668", 
      "id": "https://www.v2ex.com/t/285989", 
      "date_published": "2016-06-15T10:31:04+00:00", 
      "content_html": "<p>\u6700\u8fd1\u8ff7\u4e0a\u4e86 Ruby \u5450\uff0c\u5728 Python \u533a\u770b\u5230\u4e86<a href=\"http://v2ex.com/t/284909\" rel=\"nofollow\">http://v2ex.com/t/284909</a>\n\u987a\u624b\u4e5f\u7528 Ruby \u5199\u4e86\u4e00\u4e2a\uff0c\u53c2\u8003\u4e86\u90e8\u5206\u4ee3\u7801\u3002</p>\n<p>\u5730\u5740:<a href=\"https://github.com/lingmm/tickets\" rel=\"nofollow\">github</a></p>\n<p><img alt=\"Tickets\" src=\"http://res.shyling.com/ruby-tickets.png\"></p>\n<p>\u987a\u4fbf\u589e\u52a0\u4e86\u66f4\u65b0 stations.dat \u7684\u529f\u80fd</p>\n<p>\u4e0d\u662f\u5f88\u4f1a\u73a9 rubygems \uff0c\u5c31\u4e0d\u8003\u8651\u4e0a\u4f20\u4ec0\u4e48\u7684\u4e86</p>\n<p>\u8bdd\u8bf4\uff0c\u7f16\u7801\u95ee\u9898\u8fd8\u662f\u5f88\u9ebb\u70e6\u554a=-=\n\u5728 windows \u4e0a ARGV \u91cc\u9ed8\u8ba4\u662f GBK \u7f16\u7801\uff0c\u6211\u5728 ssh \u91cc\u7528 arch \u65f6 ARGV \u662f ASCII_8BIT,\u5728\u7535\u8111\u4e0a\u7684 arch \u4e0a\u662f utf-8 \uff0c ARGV \u7684\u5185\u5bb9\u9ed8\u8ba4\u8fd8\u662f frozen \u7684\u3002 Rubinius/JRuby \u4ec0\u4e48\u7684\u8fd8\u6728\u6709\u6d4b\u8bd5</p>\n<p>\u6c42 star \u554a\u6c42 star,\u987a\u4fbf\u5982\u679c\u90a3\u91cc\u5199\u7684\u4e0d\u597d\u7684\u8bdd\u6c42\u6307\u70b9\u3002</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/mortonnex", 
        "name": "mortonnex", 
        "avatar": "https://cdn.v2ex.com/avatar/67b8/6995/156857_large.png?m=1461910632"
      }, 
      "url": "https://www.v2ex.com/t/270657", 
      "title": "\u6709\u8c01\u7528\u8fc7 Ruby \u5199\u7684\u6bd4\u7279\u5e01\u4ea4\u6613\u5e73\u53f0 Peatio,admin \u9762\u677f\u8fdb\u4e0d\u53bb,\u6c42\u6559\u554a\u554a\u554a\u554a\u554a\u554a\u554a", 
      "id": "https://www.v2ex.com/t/270657", 
      "date_published": "2016-04-13T03:05:03+00:00", 
      "content_html": "<p><a href=\"https://github.com/peatio/peatio\" rel=\"nofollow\">https://github.com/peatio/peatio</a></p>\n<p>\u6309\u7167 readme \u5b89\u88c5\u5230\u6211\u7684 Mac \u4e0a,\u4f7f\u7528\u4ed6\u7ed9\u7684 admin \u5e10\u53f7\u767b\u5f55,\u7ed3\u679c\n\u8fdb\u4e0d\u53bb admin \u7684\u9762\u677f</p>\n<p>\u6c42\u89e3\u7b54,\u6709\u94dc\u5e01\u9001\u4e0a~~</p>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/EchoWhale", 
        "name": "EchoWhale", 
        "avatar": "https://cdn.v2ex.com/avatar/ec55/4ba4/162040_large.png?m=1735781838"
      }, 
      "url": "https://www.v2ex.com/t/267309", 
      "title": "\u548c\u9694\u58c1\u7684 python \u6bd4\u8d77\u6765\uff0c\u8fd9\u8282\u70b9\u597d\u51b7\u6e05", 
      "id": "https://www.v2ex.com/t/267309", 
      "date_published": "2016-03-30T04:17:05+00:00", 
      "content_html": ""
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/rockuw", 
        "name": "rockuw", 
        "avatar": "https://cdn.v2ex.com/gravatar/f0874d863bc2249e2cc43ea4f21f4c5c?s=73&d=retro"
      }, 
      "url": "https://www.v2ex.com/t/263451", 
      "title": "\u963f\u91cc\u4e91 OSS Ruby SDK \u5f00\u53d1\u624b\u8bb0", 
      "id": "https://www.v2ex.com/t/263451", 
      "date_published": "2016-03-14T11:52:57+00:00", 
      "content_html": "<p><a target=\"_blank\" rel=\"nofollow\" href=\"https://www.aliyun.com/product/oss\">\u963f\u91cc\u4e91 OSS</a>\u662f\u4e00\u4e2a\u5bf9\u8c61\u5b58\u50a8\u670d\u52a1\uff0c\u7c7b\u4f3c\u4e8e Amazon \u7684 S3 \u3002\u76f8\u6bd4\u4e8e\u81ea\u5efa\u5b58\u50a8\uff0c\u4f7f\u7528 OSS \u80fd\u591f\u8fc5\u901f\u5e2e\u60a8\u641e\u5b9a\u5b58\u50a8\uff0c\u5e76\u4e14\u5927\u5927\u5730\u8282\u7701\u6210\u672c\uff0c\u63d0\u5347\u53ef\u9760\u6027\u548c\u5b89\u5168\u6027\u3002</p>\n\n<p>\u73b0\u5728\uff0c\u559c\u6b22 Ruby \u7684\u540c\u5b66\u7ec8\u4e8e\u53ef\u4ee5\u4f18\u96c5\u5730\u4f7f\u7528 OSS \u4e86\u3002\u5148\u770b\u770b Rubyist \u5982\u4f55\u4e0a\u4f20\u548c\u4e0b\u8f7d\u6587\u4ef6\u5427\uff1a</p>\n<div class=\"highlight\"><pre>require &#39;aliyun/oss&#39;\nbucket = Aliyun::OSS::Client.new(\n  endpoint: &#39;http://oss-cn-hangzhou.aliyuncs.com&#39;,\n  access_key_id: &#39;xxx&#39;,\n  access_key_secret: &#39;yyy&#39;).get_bucket(&#39;bucket&#39;)\n\nbucket.put_object(&#39;ruby&#39;) { |s| s &lt;&lt; &#39;hello world&#39; }\nbucket.get_object(&#39;ruby&#39;) { |c| puts c }\n\nbucket.put_object(&#39;rails&#39;, :file =&gt; &#39;/tmp/x&#39;)\nbucket.get_object(&#39;rails&#39;, :file =&gt; &#39;/tmp/y&#39;)\n</pre></div>\n\n<p>\u662f\u4e0d\u662f so easy \uff1f OSS SDK for Ruby \u652f\u6301 OSS \u76ee\u524d\u7edd\u5927\u90e8\u5206\u529f\u80fd\uff0c\u4e3b\u8981\u7684 highlights \u5305\u62ec\uff1a</p>\n\n<ul>\n<li>\u987e\u540d\u601d\u4e49\u7684\u63a5\u53e3 put_object/get_object/list_buckets/list_objects</li>\n<li>block \u98ce\u683c\u7684\u4ee3\u7801</li>\n<li>iterator \u5f62\u5f0f\u7684\u5217\u8868</li>\n<li>\u6d41\u5f0f\u4e0a\u4f20 /\u4e0b\u8f7d</li>\n<li>\u65ad\u70b9\u7eed\u4f20\u4e0a\u4f20 /\u4e0b\u8f7d</li>\n</ul>\n\n<p>\u4e0b\u9762\u6211\u4eec\u968f\u4fbf\u6311\u51e0\u4e2a\u6765\u804a\u4e00\u804a\uff1a</p>\n\n<h3>1. iterator \u5f62\u5f0f\u7684\u5217\u8868</h3>\n\n<p>OSS \u7528\u6237\u5728\u4e00\u4e2a bucket \u4e0b\u53ef\u80fd\u6709\u6210\u5343\u4e0a\u4e07\u7684 objects \uff0c\u6240\u4ee5 OSS \u4e0d\u4f1a\u5c06 object \u5217\u8868\u4e00\u6b21\u6027\u5168\u90e8\u8fd4\u56de\uff0c\u6bcf\u6b21\u6700\u591a\u8fd4\u56de 1000 \u6761\u3002\u5982\u679c\u6211\u8981\u5217\u51fa\u524d 1001 \u4e2a object \u600e\u4e48\u529e\uff1f\u4e00\u822c\u6765\u8bf4\u7528\u6237\u53ef\u80fd\u8981\u8c03\u7528\u4e24\u6b21\u63a5\u53e3\u3002\u4f46\u662f\u5728 ruby \u4e2d\u4f60\u53ea\u9700\u8981\u4e00\u884c\uff1a</p>\n<div class=\"highlight\"><pre>bucket.list_objects.take(1001)\n</pre></div>\n\n<p>\u518d\u6bd4\u5982\u7528\u6237\u8981\u627e files/\u4e0b\u6587\u4ef6\u540d\u5305\u542b&#39;ruby&#39;\u7684\u6587\u4ef6\uff1a</p>\n<div class=\"highlight\"><pre>bucket.list_objects(prefix: &#39;files/&#39;).find { |x| x.key.include?(&#39;ruby&#39;) }\n</pre></div>\n\n<p>\u662f\u4e0d\u662f\u975e\u5e38\u65b9\u4fbf\uff1f</p>\n\n<h3>2. \u6d41\u5f0f\u4e0a\u4f20</h3>\n\n<p>\u6d41\u5f0f\u4e0a\u4f20\u5141\u8bb8\u7528\u6237\u52a8\u6001\u5730\u4e00\u8fb9\u751f\u6210\u5185\u5bb9\uff0c\u4e00\u8fb9\u4e0a\u4f20\u5230 OSS \uff0c\u4e0a\u4f20\u7684\u6570\u636e\u53ef\u4ee5 generated on the fly \u3002\u5982\u4e0b\u9762\u7684\u4f8b\u5b50\uff1a</p>\n<div class=\"highlight\"><pre>bucket.put_object(&#39;numbers&#39;) do |stream|\n  (1..1_000_000).each { |i| stream &lt;&lt; i &lt;&lt; &quot;\\n&quot; }\nend\n</pre></div>\n\n<p><strong>\u6ce8\u610f\uff1a\u8fd9\u91cc\u5e76\u4e0d\u662f\u8981 100 \u4e07\u4e2a\u6570\u5b57\u90fd\u751f\u6210\u5b8c\u540e\u518d\u53d1\u9001\uff0c\u800c\u662f\u4e00\u8fb9\u751f\u6210\u4e00\u8fb9\u53d1\u9001\u7684\u3002</strong> </p>\n\n<p>\u8981\u505a\u5230\u4e0a\u9762\u7684\u6548\u679c\u5e76\u4e0d\u5bb9\u6613\uff0c\u8bd5\u60f3\u4e00\u4e0b\uff0c<code>put_object</code>\u8981\u5982\u4f55\u8c03\u7528\u6240\u63a5\u53d7\u7684 block \u53c2\u6570\uff1f\u5982\u679c\u8c03\u7528\u90a3\u4e48 100 \u4e07\u4e2a\u6570\u5b57\u90fd\u4e00\u53e3\u6c14\u751f\u6210\u5b8c\u4e86\uff0c\u6240\u4ee5\uff0c<strong>\u5982\u4f55\u80fd\u505a\u5230\u5bf9\u4e00\u4e2a\u51fd\u6570\u8c03\u7528\u4e00\u534a\uff1f</strong> \u7b54\u6848\u5c31\u662f\u4f7f\u7528<code>Fiber</code>\u3002</p>\n<div class=\"highlight\"><pre>def hello\n  puts &#39;hello&#39;\n  Fiber.yield\n  puts &#39;world&#39;\nend\n\ndef world\n  fiber = Fiber.new { hello }\n\n  puts &#39;first&#39;\n  fiber.resume # puts &#39;hello&#39;\n  puts &#39;second&#39;\n  fiber.resume # puts &#39;world&#39;\nend\n\nworld\n</pre></div>\n\n<p>\u7c7b\u4f3c\u4e0a\u9762\u7684\u4ee3\u7801\uff0c<code>stream#&lt;&lt;</code>\u5185\u6bcf\u63a5\u53d7\u4e00\u90e8\u5206\u5185\u5bb9\uff0c\u4f1a\u5c06\u81ea\u5df1 yield \u51fa\u53bb\uff0c\u8fd9\u6837\u5df2\u7ecf\u63a5\u53d7\u7684\u5185\u5bb9\u5c31\u53ef\u4ee5\u7acb\u5373\u53d1\u9001\u51fa\u53bb\u3002\u6709\u5174\u8da3\u53ef\u4ee5\u67e5\u770b<a target=\"_blank\" rel=\"nofollow\" href=\"https://github.com/aliyun/aliyun-oss-ruby-sdk/blob/master/lib/aliyun/oss/http.rb#L45\">SDK \u4ee3\u7801</a>\u3002</p>\n\n<h3>3. \u65ad\u70b9\u4e0a\u4f20</h3>\n\n<p>\u5728\u4e0a\u4f20\u5927\u6587\u4ef6\u65f6\u5982\u679c\u4e2d\u9014\u5931\u8d25\u4e86\uff0c\u8981\u91cd\u65b0\u4e0a\u4f20\u662f\u4e0d\u662f\u5f88\u6cae\u4e27\uff1f\u6709\u4e86\u65ad\u70b9\u4e0a\u4f20\uff0c\u4e2d\u9014\u5931\u8d25\u540e\u53ef\u4ee5\u63a5\u7740\u4e0a\u6b21\u7684\u8fdb\u5ea6\u7ee7\u7eed\u4e0a\u4f20\u3002\u5728 ruby \u4e2d\u53ea\u9700\u8981\uff1a</p>\n<div class=\"highlight\"><pre>bucket.resumable_upload(&#39;object_key&#39;, &#39;local_file&#39;)\n</pre></div>\n\n<p>\u65ad\u70b9\u4e0a\u4f20\u4f9d\u8d56\u4e8e OSS \u63d0\u4f9b\u7684 multipart \u529f\u80fd\uff0c\u7c7b\u4f3c\u4e8e\u4e00\u4e2a\u4e8b\u52a1\uff1a</p>\n\n<ul>\n<li>\u5f00\u542f\u4e00\u4e2a multipart \u4e0a\u4f20 (begin transaction)</li>\n<li>\u5c06\u6587\u4ef6\u5206\u6210\u591a\u4e2a part \u5206\u522b\u4e0a\u4f20 (do modify)</li>\n<li>\u5b8c\u6210\u6b64\u6b21 multipart \u4e0a\u4f20 (commit transaction)</li>\n</ul>\n\n<p>\u53ea\u6709\u6700\u540e\u4e00\u6b65\u6210\u529f\u540e\u6587\u4ef6\u624d\u7b97\u4e0a\u4f20\u6210\u529f\uff0c\u5728\u6b64\u4e4b\u524d\u6587\u4ef6\u5bf9\u7528\u6237\u662f\u4e0d\u53ef\u89c1\u7684\u3002</p>\n\n<p>\u6587\u4ef6\u88ab\u4e2d\u65ad\u540e\u5982\u4f55\u6062\u590d\u4e0a\u4f20\uff1f\u5982\u4f55\u77e5\u9053\u54ea\u4e9b parts \u5df2\u7ecf\u4e0a\u4f20\u6210\u529f\uff1f\u8fd9\u9700\u8981\u8bb0\u5f55\u4e8b\u52a1\u7684\u72b6\u6001\u4fe1\u606f\u3002 SDK \u7684\u505a\u6cd5\u662f\u5c06\u8fd9\u4e9b\u4fe1\u606f\uff08\u79f0\u4e3a checkpoint \uff09\u4fdd\u5b58\u5728\u4e00\u4e2a\u672c\u5730\u7684 json \u6587\u4ef6\u4e2d\u3002\u6bcf\u4e0a\u4f20\u5b8c\u4e00\u4e2a part \u5c31\u66f4\u65b0\u4e00\u6b21 checkpoint \u3002\u6062\u590d\u4e0a\u4f20\u65f6\u4ece checkpoint \u6587\u4ef6\u4e2d\u6062\u590d\u4e0a\u4f20\u7684\u8fdb\u5ea6\u3002</p>\n\n<p>\u53e6\u5916\u65ad\u70b9\u4e0a\u4f20 /\u4e0b\u8f7d\u4e2d\u4e5f\u5229\u7528\u591a\u7ebf\u7a0b\u5b9e\u73b0\u52a0\u901f\uff0c\u5148\u770b\u7ed3\u679c\uff1a</p>\n<div class=\"highlight\"><pre>$ruby tests/test_large_file.rb -n test_large_file_1gb                                                                                                                                                     \nRun options: -n test_large_file_1gb --seed 7587\n\n# Running:\n\n                                       user     system      total        real\nUpload with put_object:           20.810000   1.880000  22.690000 ( 62.843336)\nUpload with resumable_upload:     28.720000   9.740000  38.460000 ( 33.963555)\nDownload with get_object:         17.300000   4.550000  21.850000 ( 47.132476)\nDownload with resumable_download:  23.260000   9.530000  32.790000 ( 31.883211)\n</pre></div>\n\n<p>Ruby \u6216\u8005 Python \u7684\u591a\u7ebf\u7a0b\u4e00\u76f4\u662f\u4e2a\u201c\u8ff7\u201d\uff0c\u4f46\u662f\u5bf9\u4e8e\u8fd9\u79cd IO \u591a\u573a\u666f\uff0c\u7528\u591a\u7ebf\u7a0b\u6548\u679c\u8fd8\u662f\u5f88\u660e\u663e\u7684\u3002\u56e0\u4e3a\u8fdb\u884c IO \u7684\u6807\u51c6\u5e93\u51fd\u6570\u5728\u9700\u8981\u7b49\u5f85 IO \u65f6\u4f1a\u5c06\u5f53\u524d\u7ebf\u7a0b\u5207\u51fa\u53bb\u3002\u53c2\u8003\uff1a <a target=\"_blank\" rel=\"nofollow\" href=\"http://yehudakatz.com/2010/08/14/threads-in-ruby-enough-already/\">http://yehudakatz.com/2010/08/14/threads-in-ruby-enough-already/</a></p>\n\n<h3>\u4e00\u4e2a\u7b80\u5355\u7684 demo</h3>\n\n<p>\u501f\u52a9 rails \uff0c\u53ef\u4ee5\u5728 15 \u5206\u949f\u5185\u642d\u5efa\u4e00\u4e2a<a target=\"_blank\" rel=\"nofollow\" href=\"https://github.com/rockuw/oss-manager\">oss \u6587\u4ef6\u7ba1\u7406\u5668</a>\uff0c\u53ef\u4ee5\u67e5\u770b /\u4e0a\u4f20 /\u4e0b\u8f7d\u6587\u4ef6\uff0c\u6548\u679c\u56fe\u5982\u4e0b\uff1a</p>\n\n<p><img src=\"http://img3.tbcdn.cn/L1/461/1/320b7e22b5de82f44aa39a9720000e2827e1f738.png\" alt=\"QQ20151202_0\"></p>\n\n<h3>\u66f4\u591a</h3>\n\n<ul>\n<li>github \u5730\u5740\uff08\u6b22\u8fce fork/start/pr \uff09\uff1a <a target=\"_blank\" rel=\"nofollow\" href=\"https://github.com/aliyun/aliyun-oss-ruby-sdk\">https://github.com/aliyun/aliyun-oss-ruby-sdk</a></li>\n<li>SDK \u6587\u6863\uff1a <a target=\"_blank\" rel=\"nofollow\" href=\"http://help.aliyun.com/document_detail/oss/sdk/ruby-sdk/install.html\">http://help.aliyun.com/document_detail/oss/sdk/ruby-sdk/install.html</a></li>\n<li>API \u6587\u6863\u5730\u5740\uff1a <a target=\"_blank\" rel=\"nofollow\" href=\"http://www.rubydoc.info/gems/aliyun-sdk/\">http://www.rubydoc.info/gems/aliyun-sdk/</a></li>\n<li>ChangeLog \uff1a <a target=\"_blank\" rel=\"nofollow\" href=\"https://github.com/aliyun/aliyun-oss-ruby-sdk/blob/master/CHANGELOG.md\">https://github.com/aliyun/aliyun-oss-ruby-sdk/blob/master/CHANGELOG.md</a></li>\n<li>oss-manager: <a target=\"_blank\" rel=\"nofollow\" href=\"https://github.com/rockuw/oss-manager\">https://github.com/rockuw/oss-manager</a></li>\n</ul>\n"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/puttin", 
        "name": "puttin", 
        "avatar": "https://cdn.v2ex.com/avatar/5308/7acd/36935_large.png?m=1381394137"
      }, 
      "url": "https://www.v2ex.com/t/261840", 
      "date_modified": "2016-03-08T02:13:21+00:00", 
      "content_html": "<ol>\n<li>\u6709\u522b\u7684\u53ef\u9009\u9879\u5417?<br></li>\n<li><del>\u6709\u4ec0\u4e48\u5185\u5e55</del></li>\n</ol>\n", 
      "date_published": "2016-03-08T02:12:51+00:00", 
      "title": "\u6dd8\u5b9d RubyGems \u955c\u50cf\u6709\u6bb5\u65f6\u95f4\u6ca1\u540c\u6b65\u4e86", 
      "id": "https://www.v2ex.com/t/261840"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/wentian", 
        "name": "wentian", 
        "avatar": "https://cdn.v2ex.com/avatar/27cf/6860/159783_large.png?m=1485186322"
      }, 
      "url": "https://www.v2ex.com/t/261031", 
      "title": "\u4e3a\u4ec0\u4e48 Ruby \u7ed9\u4eba\u4e00\u79cd\u300c\u53ea\u6709 Rails\u300d\u7684\u5370\u8c61?", 
      "id": "https://www.v2ex.com/t/261031", 
      "date_published": "2016-03-04T02:42:13+00:00", 
      "content_html": "\u8fd9\u5f53\u7136\u662f\u504f\u89c1, \u4f46\u662f\u4e8b\u5b9e\u662f...\r<br />\u6709\u65f6\u5019\u751a\u81f3\u6709\u4e00\u79cd\u300c\u65e0 Rails \u4e0d Ruby \u300d\u7684\u611f\u89c9"
    }, 
    {
      "author": {
        "url": "https://www.v2ex.com/member/ming2281", 
        "name": "ming2281", 
        "avatar": "https://cdn.v2ex.com/avatar/fa0e/cdc7/77292_large.png?m=1463581866"
      }, 
      "url": "https://www.v2ex.com/t/257497", 
      "title": "Ruby \u4e2d\u6709\u7c7b\u4f3c\u300cPython \u4e2d\u7684 IPython\u300d\u8fd9\u79cd\u300c\u65b9\u4fbf\u4f7f\u7528 Ruby \u89e3\u91ca\u5668\u300d\u7684\u795e\u5668\u5417?", 
      "id": "https://www.v2ex.com/t/257497", 
      "date_published": "2016-02-18T14:28:03+00:00", 
      "content_html": ""
    }
  ]
}