WebAssembly + Dapr = 下一代雲原生運行時?

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1a/1aea0256432f9dd47ba774b11f34bc6a.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者 | 易立來源 | ","attrs":{}},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/Cp5R-6tfG8Js3MpdTbcD1w","title":"","type":null},"content":[{"type":"text","text":"阿里巴巴雲原生公衆號","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雲計算已經成爲了支撐數字經濟發展的關鍵基礎設施。雲計算基礎設施也在持續進化,從 IaaS,到容器即服務(CaaS),再到 Serverless 容器和函數 PaaS (fPaaS 或者 FaaS),新的計算形態相繼出現。以容器和 Serverless 爲代表的雲原生技術正在重塑整個應用生命週期。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c6/c6bdc8604ac4b79fcf98bc2b78be9bca.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Gartner 分析報告中,雲計算基礎設施的發展路徑,也是雲原生特質逐漸增強的過程。其具體表現在:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"模塊化越來越高","attrs":{}},{"type":"text","text":"- 更加細粒度的計算單元,如容器和 Serverless 函數,更加適於微服務架構的應用交付,可以更加充分利用雲的能力,提升架構敏捷性。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"可編程性越來越高","attrs":{}},{"type":"text","text":"- 可以通過聲明式 API 和策略進行實現自動化管理與運維,可以通過 Immutable Infrastructure (不可變基礎設施)進一步提升分佈式應用運維的確定性。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"彈性效率越來越高","attrs":{}},{"type":"text","text":"- VM 可以實現分鐘級擴容;容器與 Serverless 容器可以實現秒級擴容;藉助調度優化,函數可以做到毫秒級擴容。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"韌性越來越高","attrs":{}},{"type":"text","text":"- Kubernetes 提供了強大自動化編排能力,提升應用系統自愈性。而 Serverless 進一步將穩定性、可伸縮性和安全等系統級別複雜性下沉到基礎設施,開發者只需關注自身業務應用邏輯,進一步釋放了生產力,提升系統的可恢復能力。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分佈式雲則是雲計算髮展的另外一個重要趨勢,公有云的服務可以拓展到不同的物理位置,讓計算進一步貼近客戶。分佈式雲讓客戶享受雲計算的便利的同時,也可以滿足對計算實時性和安全合規的訴求。這也推動了企業應用架構的變化 - 應用要能夠在不同的環境進行部署、遷移,以最優化的方式提供服務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進一步隨着移動互聯網,AI 與 IoT 等新技術的湧現,無處不在的計算已經成爲現實。與此同時,這也在催生算力的多樣性,X86 架構一統天下的時代已經過去,ARM/RISC-V 等芯片新勢力不但稱雄移動通信和嵌入式設備領域,也在向邊緣計算和數據中心市場發起進攻。開發者甚至需要讓應用支持不同的 CPU 體系架構,比如我們可以將一個圖像識別應用部署在邊緣或者 IoT 等不同環境、不同體系架構的設備之上運行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在分佈式雲、邊緣計算、雲端一體等新的雲計算場景下,下一代雲原生應用運行時將具備什麼樣的特點?","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"下一代雲原生應用運行時","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1. 無處不在的計算催生下一代可移植、高性能、輕量化的安全沙箱","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"容器應用採用自包含的打包方式 -- 容器鏡像,它包含了應用代碼和依賴的系統組件,可以實現應用與基礎設施解耦,讓應用可以在公共雲、專有云等不同的運行環境以一致的方式進行部署、運維,簡化了彈性和遷移。此外 Docker 鏡像規範支持多架構(Multi-Arch)鏡像,可以簡化不同 CPU 體系架構(如 x86, ARM 等)的應用鏡像的構建與分發。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"函數應用只包含用於事件響應的代碼包,這將應用交付格式從原生二進制文件提升到了高級語言層面。這也給應用的可移植性帶來了更大的想象空間,理論上甚至可以屏蔽執行環境 CPU 體系架構的差異。比如對於不依賴本地代碼的 Python/NodeJS 等腳本或者 Java 應用,無需修改就可以在 x86 或者 ARM 等不同 CPU 架構上運行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而理想很豐滿,現實很骨感,可移植性和廠商鎖定是函數 PaaS 發展的攔路虎。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很多腳本代碼依然需要通過調用原生代碼來實現數據處理和調用中間件(如數據庫驅動),但是編譯原生代碼需要構建環境與目標執行環境一致才能保障兼容性。比如 AWS Lambda / 阿里雲函數計算都要求二進制原生代碼依賴指定的內核和 libc 版本。因此,越來越多的函數 PaaS 服務支持容器鏡像作爲載體,來簡化函數應用打包和依賴管理。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"函數應用通常依賴後端服務(BaaS, Backend as a Service)實現數據訪問與計算處理等能力,由於 BaaS 不存在任何標準,這樣很難將在 AWS Lambda 上開發的函數應用移植到阿里雲的函數計算服務。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Serverless 計算中,現有的主流技術是利用沙箱容器技術,如 AWS Firecraker 或者阿里雲沙箱容器,來實現強隔離的安全執行環境,但是也帶來更大的資源消耗。雖然現在阿里雲沙箱容器經過優化可以實現 300ms 的冷啓動速度,接近 Docker 這樣的 OS 容器啓動速度,但是還無法滿足函數 PaaS 毫秒級的啓動要求,目前需要通過的調度策略,預留一定的 standby 實例纔可以滿足,但是這樣也引入了更多的資源消耗。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebAssembly(WASM) 是一個新的 W3C 規範,是一個通用、開放、高效、安全的底層虛擬機抽象。它的設計初衷是爲了解決JavaScript的性能問題,使得 Web 應用有接近本機原生應用的性能。可以將現有編程語言應用,如 C/C++, Rust 等,編譯成爲 WASM 的字節碼,運行在瀏覽器中的一個沙箱環境中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WASM 讓應用開發技術與運行時環境解耦,極大促進了代碼複用。Mozilla 更在 2019 年推出了 WebAssembly System Interface(WASI),它提供類似 POSIX 這樣的標準 API 來標準化 WebAssembly 與系統資源的交互抽象,比如文件系統訪問,內存管理等。WASI 的出現拓展了 WASM 的應用場景,可以讓其作爲一個虛擬機運行各種類型的服務端應用。WASM/WASI 爲應用的可移植性帶來全新的希望,爲了進一步推動 WebAssembly 生態發展,Mozilla、Fastly、英特爾和紅帽公司攜手成立了字節碼聯盟(Bytecode Alliance),共同領導 WASI 標準、 WebAssembly 運行時、工具等工作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WebAssembly 所具備的的安全、可移植、高效率,輕量化的特點,爲應用沙箱的發展帶來了全新的思路。WASM 可以輕鬆實現毫秒級冷啓動時間和極低的資源消耗。同時 WASM 字節碼比原生機器碼有更高的安全級別。此外,WASI 實現了細粒度基於能力的安全模型,遵循最小權限原則。在執行過程中,WASI 應用只能訪問由依賴注入指明的確切資源集,這種方式與傳統粗粒度的操作系統級隔離相比,進一步收斂了安全攻擊面。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正因如此,WASM/WASI 得到了 Serverless、IoT/邊緣計算等社區的廣泛關注。Fastly、Cloudflare 等廠商相繼發佈了基於 WebAssembly 技術實現了更加輕量化的 Serverless 服務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而 WebAssembly 在服務器端的應用之路依然佈滿荊棘。首先 WASI 的能力還在非常早期的狀態,一些關鍵能力依然缺失,首當其衝的就是缺乏標準化的網絡訪問能力:","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/WebAssembly/WASI/issues/315","title":"","type":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"https://github.com/WebAssembly/WASI/issues/315","attrs":{}}]},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前 WASI 應用僅能做一些計算類任務,基本無法實現分佈式應用,也無法調用多樣性的後端服務和 Redis、MySQL、Kafka 等應用中間件。這大大限制了 WASI 的應用場景。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當理想撞上現實,頭破血流還是絕處逢生?","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2. 下一代可移植應用運行時加速編程界面上移,應用基礎設施能力下沉","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Dapr 是微軟開源的面向雲原生應用的分佈式應用運行時,目標使所有開發人員能夠使用任何語言和任何框架輕鬆地構建彈性的、事件驅動的、可移植的微服務應用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d5/d589ccdc5aca7a299b0c202b094127a8.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Dapr 實現了一系列構建高性能、可伸縮、高可用的分佈式應用的設計模式,比如提供了服務發現和服務調用能力,也實現了一個簡單、一致的編程模型來支持事件驅動應用架構。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外 Dapr 通過基礎設施屏蔽了應用訪問後端服務的技術細節,如資源綁定、安全管理,可觀測性等等。這個對 Serverless 應用非常重要,一方面將開發和部署進行了解耦,讓開發者和運維團隊可以通過關注點分離簡化系統複雜性;一方面,可以將短生命週期、無狀態的 Serverless 應用邏輯,與數據庫連接池管理這樣的長期運行,有狀態的中間件訪問能力進行解耦,提升了 Serverless 應用的可伸縮性和運行效率。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"“Any language, any framework, anywhere” 是 Dapr 的重要設計目標。Dapr 通過在應用和後端服務之間,通過 Sidecar 方式提供一個抽象層,並通過標準化的 HTTP/gRPC API 實現了應用的可移植性,和後端服務的可替換性。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"走向詩和遠方","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/16/16fccc349a552753ea957ca06604e74b.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以將 WebAssembly 和 Dapr 相結合,來實現可移植、強隔離、輕量化的微服務應用架構。Dapr sidecar 與 WASM 虛擬機部署在一起。WASI 應用通過 HTTP/gRPC 訪問本地的 Dapr 服務端點,由 Dapr 代理連接各種後端服務或者實現服務間通信。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣的架構設計讓 WASI 應用的安全邊界非常清晰,符合 WASI 安全模型,WASI 應用只能通過 Dapr sidecar 實現外部服務訪問。同時在這個架構中,只有 WASM 虛擬機和 Dapr 作爲可信的環境依賴以原生機器碼運行。而應用是可移植的 WASM 字節碼,大大提升了架構的可移植性和安全性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"來自微軟 Deis Labs 的 Radu Matei,最近提供了一個實驗性項目可以爲 WASI 添加 HTTP 支持。詳見:","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/WebAssembly/WASI/issues/315","title":"","type":null},"content":[{"type":"text","text":"_https://deislabs.io/posts/wasi-experimental-http/ _","attrs":{}}]},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在此基礎上,我們來構建一個最小原型,驗證 WebAssembly 與 Dapr 相結合的技術可行性。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1. Dapr 環境準備","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們首先按照 ","attrs":{}},{"type":"link","attrs":{"href":"https://docs.dapr.io/getting-started/","title":"","type":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"https://docs.dapr.io/getting-started/","attrs":{}}]},{"type":"text","text":" 的流程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"$ dapr init\n⌛ Making the jump to hyperspace...\n✅ Downloading binaries and setting up components...\n✅ Downloaded binaries and completed components set up.\nℹ️ daprd binary has been installed to /Users/yili/.dapr/bin.\nℹ️ dapr_placement container is running.\nℹ️ dapr_redis container is running.\nℹ️ dapr_zipkin container is running.\nℹ️ Use `docker ps` to check running containers.\n✅ Success! Dapr is up and running. To get started, go here: https://aka.ms/dapr-getting-started\n\n\n$ dapr run --app-id myapp --dapr-http-port 3500\nWARNING: no application command found.\nℹ️ Starting Dapr with id myapp. HTTP Port: 3500. gRPC Port: 63734\nℹ️ Checking if Dapr sidecar is listening on HTTP port 3500\n...\nℹ️ Checking if Dapr sidecar is listening on GRPC port 63734\nℹ️ Dapr sidecar is up and running.\n✅ You're up and running! Dapr logs will appear here.\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2. 利用 Redis 作爲 WASI 應用的狀態存儲","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們下面利用 Dapr 的 Get Started 的例子,利用 Redis 作爲 WASI 應用的狀態存儲。具體邏輯如下圖。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7a/7af36f379a31c3eb169efe86fe58ab77.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"注:下面的應用需要 Rust 和 AssemblyScript 環境配置,請大家自行完成。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在 Radu 項目的基礎上 fork 了一個版本,首先來下載代碼,並進行構建。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"$ git clone https://github.com/denverdino/wasi-experimental-http\n$ cd wasi-experimental-http\n$ cargo build\n...\n Finished dev [unoptimized + debuginfo] target(s) in 3m 02s\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們利用 AssemblyScript 來實現了這個測試應用,測試代碼如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"$ cat tests/dapr/index.ts\n// @ts-ignore\nimport { Console } from \"as-wasi\";\nimport { DaprClient, StateItem } from \"./dapr\";\nimport { JSON } from \"assemblyscript-json\";\n\n\nConsole.log(\"Testing Dapr API ....\")\n\nlet dapr = new DaprClient()\ndapr.saveState(\"statestore\", \"weapon\", JSON.Value.String(\"Death Star\"))\n\nlet o = JSON.Value.Object()\no.set(\"name\", \"Tatooine\")\no.set(\"test\", 123)\nlet item = new StateItem(\"planets\", o)\nlet items: StateItem[] = [item]\ndapr.saveBulkState(\"statestore\", items)\n\nlet testObj = dapr.getState(\"statestore\", \"planets\")\nlet testStr = dapr.getState(\"statestore\", \"weapon\")\n\nif (testStr.toString() == \"Death Star\" && testObj.isObj && ((testObj).getInteger(\"test\")).valueOf() == 123) {\n Console.log(\"Test successfully!\")\n} else {\n Console.log(\"Test failed!\")\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼邏輯非常簡單,就是創建一個 Dapr 客戶端,然後通過 REST API,進行 Dapr 的狀態管理。我們可以快速驗證一下。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"$ cargo run\n Finished dev [unoptimized + debuginfo] target(s) in 0.19s\n Running `target/debug/wasi-experimental-http-wasmtime-sample`\nTesting Dapr API ....\nPOST http://127.0.0.1:3500/v1.0/state/statestore with [{\"key\":\"weapon\",\"value\":\"Death Star\"}]\nPOST http://127.0.0.1:3500/v1.0/state/statestore with [{\"key\":\"planets\",\"value\":{\"name\":\"Tatooine\",\"test\":123}}]\nGET http://127.0.0.1:3500/v1.0/state/statestore/planets\nGET http://127.0.0.1:3500/v1.0/state/statestore/weapon\nTest successfully!\nmodule instantiation time: 333.16637ms\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3. 關鍵要點分析","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"wasi-experimental-http 項目在 Wasmtime (來自 Bytecode Alliance 的一個 WASM 實現)虛擬機上實現了擴展,支持在 WASI 應用中,訪問 HTTP 服務。它還提供了一個 AssemblyScript 的 HTTP Client 實現。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"wasi-experimental-http 項目:","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/deislabs/wasi-experimental-http/","title":"","type":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"https://github.com/deislabs/wasi-experimental-http/","attrs":{}}]},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在此之上,我們爲 AssemblyScript 提供一個 Dapr 的封裝,可以參見:","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/denverdino/wasi-experimental-http/blob/main/tests/dapr/dapr.ts","title":"","type":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"https://github.com/denverdino/wasi-experimental-http/blob/main/tests/dapr/dapr.ts","attrs":{}}]},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"// @ts-ignore\nimport { Console } from \"as-wasi\";\nimport { Method, RequestBuilder, Response } from \"../../crates/as\";\n\nimport { JSONEncoder, JSON } from \"assemblyscript-json\";\n\nexport class StateItem {\n key: string\n value: JSON.Value\n etag: string | null\n metadata: Map | null\n\n constructor(key: string, value: JSON.Value) {\n this.key = key\n this.value = value\n this.etag = null\n this.metadata = null\n }\n}\n\n...\n\nexport class DaprClient {\n port: i32\n address: string\n\n constructor() {\n this.address = \"127.0.0.1\"\n this.port = 3500\n }\n\n stateURL(storeName: string): string {\n return \"http://\" + this.address + \":\" + this.port.toString() + \"/v1.0/state/\" + storeName\n }\n\n saveState(storeName: string, key: string, value: JSON.Value): boolean {\n let item = new StateItem(key, value)\n let items: StateItem[] = [item]\n return this.saveBulkState(storeName, items)\n }\n\n saveBulkState(storeName: string, items: StateItem[]): boolean {\n // Handle field\n let encoder = new JSONEncoder();\n\n // Construct necessary object\n encoder.pushArray(null);\n for (let i = 0, len = items.length; i < len; i++) {\n let item = items[i]\n encoder.pushObject(null);\n encoder.setString(\"key\", item.key)\n encodeValue(encoder, \"value\", item.value)\n if (item.etag != null) {\n encoder.setString(\"etag\", item.etag)\n }\n encoder.popObject()\n };\n encoder.popArray();\n // Or get serialized data as string\n let jsonString = encoder.toString();\n let url = this.stateURL(storeName);\n Console.log(\"POST \" + url + \" with \" + jsonString);\n let res = new RequestBuilder(url)\n .method(Method.POST)\n .header(\"Content-Type\", \"application/json\")\n .body(String.UTF8.encode(jsonString))\n .send();\n let ok = res.status.toString() == \"200\"\n res.close();\n return ok\n }\n\n getState(storeName: string, key: string): JSON.Value {\n let url = this.stateURL(storeName) + \"/\" + key;\n Console.log(\"GET \" + url);\n let res = new RequestBuilder(url)\n .method(Method.GET)\n .send();\n let ok = res.status.toString() == \"200\"\n let result = new JSON.Null()\n if (ok) {\n let body = res.bodyReadAll();\n result = JSON.parse(body)\n }\n res.close();\n return result\n }\n};\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"測試應用的 main 函數,會創建一個 Wasmtime 運行時環境,併爲其添加爲 HTTP 擴展,並加載執行測試應用的 WASM 字節碼:","attrs":{}},{"type":"link","attrs":{"href":"https://github.com/denverdino/wasi-experimental-http/blob/main/src/main.rs","title":"","type":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"https://github.com/denverdino/wasi-experimental-http/blob/main/src/main.rs","attrs":{}}]},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"fn main() {\n let allowed_domains = Some(vec![\n \"http://127.0.0.1:3500\".to_string(),\n ]);\n let module = \"tests/dapr/build/optimized.wasm\";\n create_instance(module.to_string(), allowed_domains.clone()).unwrap();\n}\n\n/// Create a Wasmtime::Instance from a compiled module and\n/// link the WASI imports.\nfn create_instance(\n filename: String,\n allowed_domains: Option>,\n) -> Result {\n let start = Instant::now();\n let store = Store::default();\n let mut linker = Linker::new(&store);\n\n let ctx = WasiCtxBuilder::new()\n .inherit_stdin()\n .inherit_stdout()\n .inherit_stderr()\n .build()?;\n\n let wasi = Wasi::new(&store, ctx);\n wasi.add_to_linker(&mut linker)?;\n // Link `wasi_experimental_http`\n let http = HttpCtx::new(allowed_domains, None)?;\n http.add_to_linker(&mut linker)?;\n\n let module = wasmtime::Module::from_file(store.engine(), filename)?;\n\n let instance = linker.instantiate(&module)?;\n let duration = start.elapsed();\n println!(\"module instantiation time: {:#?}\", duration);\n Ok(instance)\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"道阻且長,行則將至","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"WASM/WASI 爲輕量化、可移植、缺省安全的應用運行時提供了良好的基礎,在區塊鏈等領域 WebAssembly 已經得到了廣泛的應用。然而,對於通用性的服務器端應用,WASM/WASI 的差距還非常明顯。由於 berkeley socket 這樣標準化的網絡編程接口的缺失,只能通過擴展 WASM 虛擬機的方式來進行補齊。此外 WASM 的多線程能力還沒有被標準化,目前的 HTTP 調用採用阻塞式同步調用,還無法實現高效和穩定的網絡通信。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外,另外 WASM/WASI 的一個短板就是開發效率和生態建設。目前而言,雖然衆多的編程語言已經逐漸開始提供 WebAssembly 的支持,但是對於普通開發者而言,AssemblyScript 這樣的腳本語言是更加合適的選擇。AssemblyScript 複用了 TypeScript 的語法,與 Rust/C++ 相比,大大降低了學習曲線,也提供了非常好的 IDE 工具體驗,如 VS Code 等。但是與 TypeScripty 通過翻譯成爲 JavaScript 執行不同,AssemblyScript 應用會被編譯成 WASM 字節碼執行。AssemblyScript 本質上是一個靜態類型的編譯型語言,本質上與 JS/TS 這樣的動態類型的解釋型語言非常不同。二者在語法上也有一些不同,比如目前 AssemblyScript 缺少對閉包 (closure) 和正則表達式 (Regex) 等常用功能支持,這讓開發 WASM 應用還是有一定的技術門檻。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外與 NPM 強大的生態相比,AssemblyScript 社區也很年輕。很多功能都需要從頭構建,比如對 JSON 的序列化與反序列化,我們選擇了 ","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"https://github.com/nearprotocol/assemblyscript-json","attrs":{}},{"type":"text","text":" ,但是其易用性和性能與成熟的 JSON 類庫還有一定差距。當然我們也看到 AssemblyScript 的快速成長,以及越來越多的開發者開始貢獻 AssemblyScript 代碼庫,比如 regex 支持等等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Dapr 的出現爲 WASM/WASI 開發通用的分佈式應用,尤其是爲可移植的、Serverless 化的應用帶來另外一縷曙光。然而 Dapr 也並非完美:API 標準化在提升對後端服務可移植性的同時也阻礙了對差異化能力的支持。Sidecar 架構在提升靈活性的同時增加了部署和管理複雜性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作爲一個理性樂觀派,任何技術都有其青澀的時代,期待社區的共同努力讓計算無處不在、創新觸手可及的理想成爲現實。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b7/b7fea0353fc32a8f6f3be4eb306e5622.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章