中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Google是怎么設計Ruby Serverless Runtime的

發布時間:2022-01-12 16:44:08 來源:億速云 閱讀:149 作者:柒染 欄目:云計算

這篇文章跟大家分析一下“Google是怎么設計Ruby Serverless Runtime的”。內容詳細易懂,對“Google是怎么設計Ruby Serverless Runtime的”感興趣的朋友可以跟著小編的思路慢慢深入來閱讀一下,希望閱讀后能夠對大家有所幫助。下面跟著小編一起深入學習“Google是怎么設計Ruby Serverless Runtime的”的知識吧。

一種實現 Ruby Serverless 化的方式

為 Serverless 產品提供 Ruby 支持比您預期的要復雜得多。從最基本的角度來看,語言運行時只是 Ruby 的安裝,并且可以肯定的是,配置 Ruby 鏡像并將其安裝在 VM 上并不難。但是,當您將 “Serverless” 加入其中時,事情會變得更加復雜。Severless 不僅僅是自動維護和擴容。這是對計算資源的完全不同的思考方式,這與過去15年中我們學到的有關部署 Ruby 應用程序的許多知識背道而馳。當 Google Cloud 的 Ruby 團隊承擔為 Cloud Functions 設計 Ruby 運行時的任務時,我們還承擔了一項艱巨的任務,即提出一種 Ruby 方式來實現 Serverless。在堅持我們社區所熟悉的 Ruby 習慣、實踐和工具的同時,我們還必須重新思考如何在幾乎每個層次上進行 web 應用程序開發,從代碼到依賴、持久化、測試等等。

本文將研究我們在設計的五個不同方面的方法:函數語法、并發性和生命周期、測試、依賴項和標準。在每種情況下,我們都將在忠于 Ruby 本色的重要性與擁抱新的 Serverless 范式的愿望之間保持一個平衡。我們非常努力地保持與傳統 Ruby 工作方式的連續性,并且還從 Google Cloud Functions 其他語言運行時中汲取了經驗,并借鑒了其他云提供商的 Serverless 產品所樹立的先例。但是,在少數情況下,我們選擇另辟蹊徑。我們之所以這么做,是因為我們覺得當前的方法要么是濫用了語言功能,要么是誤導和鼓勵了關于 Serverless 應用開發的錯誤想法。

某些決策最終有可能被證明是錯誤的。這就是我現在提供這篇文章的原因。討論我們已經做的事情,并開始討論我們作為 Ruby 社區實踐 Serverless 應用程序開發的方式。好消息是 Ruby 是一種非常靈活的語言,隨著我們的學習和需求的發展,我們將有很多機會適應它。

因此,讓我們看一下我們做出的一些初始設計決策和權衡以及做出這些決策的原因。

函數化 Ruby

“函數即服務”(FaaS)當前是較流行的 Serverless 范式之一。Google Cloud Functions 只是一種實現。許多其他主要的云提供商都擁有自己的 FaaS 產品,并且也有開源實現。

當然,這種想法是使用一種編程模型,該模型不以 Web 服務器為中心,而是以函數為中心:無狀態的代碼片段,它們接受輸入參數并返回結果。這似乎是一個簡單的、幾乎顯而易見的術語變化,但實際上具有深遠的意義。

對 Ruby 而言,面臨的第一個挑戰是,與許多其他編程語言不同,在 Ruby 中函數并不是一等公民。Ruby 首先是一種面向對象的語言。當我們編寫代碼并將其封裝在 def 中時,我們正在編寫一個方法,這是響應發送給對象的消息而運行的代碼。這是一個重要的區別,因為組成方法調用上下文的對象和類不是 Serverless 抽象的一部分。因此,它們的存在會使 Serverless 的應用程序復雜化,甚至在我們編寫應用程序時誤導我們。

例如,某些 FaaS 框架使您可以使用 def 在 Ruby 文件的頂層編寫函數:

def handler(event:, context:)
  "Hello, world!"
end

雖然這段代碼看起來很簡單,但重要的是要記住它實際上做了什么。它將這個“函數”添加為 Object 類的私有方法,Object 類是 Ruby 類層次結構的基類。換句話說,Ruby 虛擬機中的幾乎每個對象都添加了“函數”。(當然,除非應用程序在加載文件時更改了主對象和類上下文,這種技術會帶來其他風險。)在最好的情況下,這打破了封裝和單一職責。在最壞的情況下,它可能會干擾應用程序的功能、依賴關系,甚至是 Ruby 標準庫。這就是為什么這種“頂級”方法在簡單的單文件 Ruby 腳本和 Rakefiles 中很常見,但在大型 Ruby 應用程序中不推薦使用。

Google Ruby 團隊認為這個問題很嚴重,所以我們選擇了一種不同的語法,將函數寫成塊的形式:

require "functions_framework"
FunctionsFramework.http("handler") do |request|
  "Hello, world!"
end

這提供了一種類似于 Ruby 的方式來定義函數而無需修改 Object 基類。它還有一些附帶好處:

  • 名稱(在這種情況下為 “handler”)只是一個字符串參數。它不必是合法的 Ruby 方法名稱,也不必擔心它與 Ruby 關鍵字沖突。

  • 塊比方法具有更多的傳統詞法作用域,因此其行為與其他語言中的函數更相似。

  • 塊語法使管理函數定義更加容易。例如,可以干凈地“undefine”函數,這對于測試很重要。

當然,需要權衡取舍。其中:

  • 語法稍微有些冗長。

  • 它需要一個庫來提供用于將函數定義為塊的接口。(這里,Ruby 通過使用 Functions Framework 庫跟隨了 Cloud Functions 的其他語言運行時。)

我們認為,為了實現正確區分函數的目標,這些權衡是值得的。

共享或不共享

并發性是很難的。這是 Serverless 設計(特別是函數即服務)的一個關鍵觀察點:我們生活在一個并發的世界中,我們需要各種方法來應對。函數范式通過堅持函數不共享狀態(除非通過外部持久化系統,如隊列或數據庫)來解決并發性問題。這實際上是我們選擇使用塊語法而不是方法語法的另一個原因。方法隱含對象,對象以實例變量的形式攜帶狀態,這些狀態在無狀態 FaaS 環境中可能無法正常工作。回避方法是一種微妙但有效的語法方法,可以阻止我們知道的存在問題的實踐。

也就是說,如果需要共享資源,比如數據庫連接池,該怎么辦?何時初始化這些資源,如何訪問它們?

為此,Ruby 運行時支持啟動函數,這些函數可以初始化資源并將它們傳遞給函數調用方。重要的是,啟動函數可以創建資源,而普通函數只能讀取它們。

require "functions_framework"


# Use an on_startup block to initialize a shared client and store it in
# the global shared data.
FunctionsFramework.on_startup do
  require "google/cloud/storage"
  set_global :storage_client, Google::Cloud::Storage.new
end

# The shared storage_client can be accessed by all function invocations
# via the global shared data.
FunctionsFramework.http "storage_example">

注意,我們選擇了定義特殊方法 global 和 set_global 來與全局資源交互。順便說一下,這些不是 Object 上的方法,而是作為函數上下文使用的特定類上的方法。同樣,我們可以使用更傳統的習慣用法,如 Ruby 全局變量,甚至構造函數和實例變量,將信息從啟動代碼傳遞給函數調用方。然而,這些語法可能傳遞了錯誤的東西。我們不是在普通的 Ruby 類和方法中編寫共享數據是正常的,而是在 Serverless 的函數中編寫共享數據是危險的(即使可能的話),我們認為語法上強調區別是很重要的。這些特殊方法是經過深思熟慮的設計決策,以防止在并發存在時出現危險的實踐。

測試為首

強大的測試文化是 Ruby 社區的核心。流行的框架,如 Rails,承認了這一點,并通過提供測試工具和腳手架作為框架的一部分來鼓勵主動測試,Google Cloud Functions 的 Ruby 運行時也遵循了這一點,為 Serverless 的函數提供了測試工具。

FaaS 范式實際上非常適合測試。函數本質上是容易測試的,只需傳入參數并對結果進行斷言即可。特別是,您不需要啟動 web 服務器來運行測試,因為 web 服務器不是抽象的一部分。Ruby 運行時提供了一個 helper方 法模塊,用于創建作為輸入使用的 HTTP 請求和云事件對象,除此之外,大多數測試都非常容易編寫。

然而,我們遇到的主要測試挑戰之一與測試初始化代碼有關。確實,這是 Google Ruby團隊成員在使用其他框架(包括 Rails)時遇到的一個問題:很難測試應用程序的初始化過程,因為框架的初始化通常發生在測試之外,在它們運行之前。因此,我們設計了一種測試方法來隔離函數的整個生命周期,包括初始化。這允許我們在測試中運行初始化,甚至重復它多次,允許不同方面的測試:

require "minitest/autorun"
require "functions_framework/testing"


class MyTest < Minitest::Test
  # Include testing helper methods
  include FunctionsFramework::Testing


  def test_startup_tasks
    # Run the lifecycle, and test the startup tasks in isolation.
    load_temporary "app.rb" do
      globals = run_startup_tasks "storage_example"
      assert_kind_of Google::Cloud::Storage, globals[:storage_client]
    end
  end


  def test_storage_request
    # Rerun the entire lifecycle, including the startup tasks, and
    # test a function call.
    load_temporary "app.rb" do
      request = make_get_request "https://example.com/foo"
      response = call_http "storage_example", request
      assert_equal 200, response.status
    end
  end
end

load_temporary 方法在沙箱中加載函數定義,將它們及其初始化與其他測試運行隔離開來。該方法和其他 helper 方法定義在 FunctionsFramework::Testing 模塊中,可以包含在 minitest 或 rspec 測試中。

到目前為止,我們只為 Ruby 運行時提供了基本的測試工具,我希望隨著用戶開發更多的應用程序和識別出更多常見的測試模式,我們會在工具集中大量增加這些工具。但我堅信測試工具是任何庫的重要組成部分,特別是那些聲稱是框架或運行時的庫,所以它是我們設計的核心部分。

可依賴的運行時

大多數重要的 Ruby 應用程序都需要第三方 gems。對于使用 Google Cloud Functions 的 Ruby 應用程序,我們至少需要一個 gem,即 functions_framework,它提供了編寫函數的 Ruby 接口。您可能還需要其他 gems 來處理數據、進行身份驗證并與其他服務集成等等。依賴項管理是任何運行時框架的關鍵部分。

我們圍繞依賴項管理做出了幾個設計決策。而第一個也是最重要的就是擁抱 Bundler。

我知道這聽起來有點無聊。現在大多數 Ruby 應用程序都在使用 Bundler,而且很少有替代方案,很少有廣泛使用的。但我們實際上更進一步,將 Bundler 深入到我們的基礎架構中,要求應用程序使用它來處理云函數。我們這么做是因為,確切地知道應用將如何管理它的依賴關系將允許我們實現一些重要的優化。

Google是怎么設計Ruby Serverless Runtime的

對于一個好的 FaaS 系統來說,部署和冷啟動的速度至關重要。在 serverless 的世界中,您的代碼可能會快速連續地更新、部署和拆除許多次,因此消除瓶頸(如解析和安裝依賴項)是至關重要的。因為我們為依賴項管理標準化了一個系統,所以我們能夠主動地緩存依賴項。我們認為,實現這樣的緩存所帶來的性能提升,以及 Rubygems.org 基礎架構所減少的負載,遠遠超過了不能使用 Bundler 的替代方案所帶來的靈活性降低。

Google Cloud Functions 的 Ruby 運行時的另一個特性,或者可能是怪癖,是如果 gem lockfile 丟失或不一致,部署將失敗。我們需要這個 Gemfile.lock 在部署時存在。這是執行最佳實踐的另一個決策。如果在部署期間重新解析了鎖文件,那么您的構建可能是不可重復的,并且您可能沒有針對測試時使用的相同依賴項運行。我們通過要求一個最新的 Gemfile.lock 來避免這個問題。同樣,我們能夠強制執行這一點,因為我們需要使用 Bundler。

新舊標準

最后,好的設計依賴于標準和現有技術。為了在 Ruby 中定義健壯的函數,我們不得不進行一些創新,但在表示函數參數時,已經有現成的庫或新興標準可供遵循。

例如,在近期內,許多函數將響應 web hook,并需要關于傳入 HTTP 請求的信息。設計一個表示 HTTP 請求的類并不困難,但是 Ruby 社區已經有了用于這類事情的標準 API: Rack。我們采用 Rack 請求類作為事件參數,并支持標準的 Rack 響應作為返回值。 require “functions_framework”

FunctionsFramework.http "http_example">

這不僅提供了一個熟悉的 API,而且還使它易于與其他基于 Rack 的庫集成。例如,很容易將 Sinatra 應用程序置于云函數之上,因為它們都能支持 Rack。

從長遠來看,我們越來越希望函數即服務(Faas)能夠作為事件系統中的一個組件。基于事件的架構正在迅速普及,經常圍繞事件隊列,如 Apache Kafka。事件體系結構的一個關鍵元素是描述事件本身的標準方法,事件發送方、代理、傳輸和使用者都理解這種標準。

Google Cloud Functions 支持 CNCF CloudEvents,這是一個描述和交付事件的新興標準。除了 HTTP 請求之外,云函數還可以接收 CloudEvent 形式的數據,運行時甚至會在調用函數時將一些遺留事件類型轉換為 CloudEvent。

require "functions_framework"


FunctionsFramework.cloud_event "my_handler" do |event|
  # event is a CloudEvent object defined by the cloud_events gem
  logger.info "I received a CloudEvent of type #{event.type}!"
end

為了在 Ruby 中支持 CloudEvent,Google Ruby 團隊與 CNCF Serverless 工作組密切合作,甚至自愿接管了用于 CloudEvent 的Ruby SDK 的開發。這是一項繁重的工作,但我們認為能夠使用官方的、標準的 Ruby 接口至關重要,即使我們必須自己實現它。

關于Google是怎么設計Ruby Serverless Runtime的就分享到這里啦,希望上述內容能夠讓大家有所提升。如果想要學習更多知識,請大家多多留意小編的更新。謝謝大家關注一下億速云網站!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

泾川县| 雷州市| 梅河口市| 镇远县| 苍溪县| 谷城县| 南陵县| 连南| 织金县| 鲁山县| 永康市| 新竹县| 玉环县| 兴义市| 昆明市| 新巴尔虎右旗| 名山县| 白沙| 阜城县| 屏南县| 犍为县| 邛崃市| 荥经县| 松潘县| 定日县| 潼关县| 两当县| 吴堡县| 安阳县| 宁河县| 和政县| 阳朔县| 霍林郭勒市| 平泉县| 北海市| 青阳县| 南宫市| 得荣县| 定日县| 苏尼特左旗| 丰顺县|