Hydra
是一個輕量級的 NodeJS 庫,用於構建分布式計算應用程序,比如微服務。我們對輕量級的定義是:輕處理外部復雜性和基礎設施依賴 —— 而不是有限的輕處理。
Hydra
聲稱對基礎設施的依賴很輕,這是因為它唯一的外部依賴是 Redis
。
Hydra 利用 Redis 豐富的數據結構來實現重要的微服務所需的功能。
如 presence
(在線狀態)、service discovery
(服務發現)、load balancing
(負載平衡)、messaging
(消息傳遞)、queuing
(隊列)等。
Hydra 到底是什么?
Hydra 是一個 NodeJS 模塊,可以將其導入到 JavaScript Node 應用程序中,以使其具有微服務功能。 Hydra 通過利用 Redis 做到這一點。
在這里,我們看到三個微服務 - 每個微服務都帶有一個連接到 Redis 的 Hydra 模塊。 在這種模式下,大多數服務不會直接與 Redis 通信。而是底層的 Hydra 模塊代理 Redis。
關於此圖的另一點是,Hydra 只是另一個導入的模塊 - 如綠色所示。 Hydra 在底部僅以藍色顯示,以說明其存在和與 Redis 的關系。
Hydra 模塊公開一個 JS 類接口,該接口共有36
個成員函數。
該快照提供了簡化抽象的感覺。成員函數(例如 findService
和 sendMessage
)非常簡單。
Hydra 如何利用 Redis
這張幻燈片顯示了許多重要的微服務問題。每個都是 non-trivial(非平凡的)
微服務所必需的。我們將詳細研究 Hydra
如何使用 Redis
來實現所有這些功能。
請記住,這里的目標是展示如何做到這一點 —— 而不是說每種方法都是您應該如何在自己的服務中實現該特性。一個恰當的例子是,雖然你可以在 Redis 中存儲你的微服務配置數據,或者使用 Redis 作為一個 logger —— 但這並不意味着你應該這樣做。至少,除非你確切地知道你在做什么,以及需要做哪些權衡。
另外,請記住,您也許不需要Hydra。這些功能都是由 Redis 實現的,您當然可以在自己的應用程序中做到這一點。(如:Golang 來一版)
我將向您展示的一個關鍵點是,其中一些特性只有在組合時才能實現。例如,請求(request)和消息路由(message routing)依賴於狀態(presence)、運行狀況(health)、服務發現(service discovery)和負載平衡(load balancing)。
如您所知,這些特性中的每一個都可以使用各種基礎設施工具來解決。然而,Hydra 的一個關鍵目標是簡化構建微服務,同時最小化外部基礎設施需求。在構建可用於生產的服務時,您需要決定需要哪些 Hydra 特性,以及哪些特性將從其他工具獲得。這不是一個非此即彼的命題,而是一個你想要達到的目標和你能多快開始的問題。
就是說,很有趣的是,僅使用 Redis 和您喜歡的編程語言就可以實現所有這些功能。
Key 空間組織
了解 Hydra 如何利用 Redis 的第一步是查看它如何組織對 Redis key 空間的使用。
Hydra 使用的鍵 —— 由 2
到 4
段標簽組成,標簽之間用冒號分隔。段標簽被命名為:前綴(Prefix
)、服務名稱(Service name
)、實例 ID(Instance ID
)和類型(type
)。
前綴段允許過濾 Hydra key 和非 Hydra key。因此,如果你大量使用 Redis,那么能夠過濾特定的 key 是至關重要的。
服務名稱段幫助過濾特定服務類型的 key。例如授權(authorization
)、用戶(user
)或圖像處理(image processing
)服務類型。
實例ID(Instance ID
)段允許過濾唯一服務實例的 key。運行微服務時,通常需要運行一個服務類型的多個實例。每個服務實例都分配有唯一的 ID,並且能夠區分它們是有用的。
最后,還有“類型(Type
)”部分,用於對 key 的用途進行分類。並非每個 key 中都存在所有段。例如,某些 key 中不需要服務名稱(Service name
)和實例ID(instance ID
)。
這是用戶服務(user service
) key 的示例。我們看到前綴 hydra:service
后跟服務名稱,在本例中為 “user-svcs”
。接下來,我們看到唯一的實例ID(unique instance ID
)。最后,我們看到此 key 的類型為 presence(presence
)。 因此,我們說 presence 信息存儲(presence information
)在此 key 地址中。
在前面的描述中,一個令人困惑的地方是,key
由名稱組成,名稱中有2
到4
個段標簽,用冒號分隔。然而,在這里我們看到 hydra:service
也用冒號分隔。當時的想法是,可能還有另一種 hydra:other
類型,service
只是其中之一。
關於消息傳遞還有另一個不一致的地方,稍后我們將對此進行討論。
我們可以輸入 redis-cli
和輸入 Redis 命令來查看各種鍵。在接下來的演示中我們會看到一些例子。
一個關於我們將使用的 redis-cli
示例的快速說明:你將看到 keys
命令的使用 —— 這是為了方便 —— Hydra 內部使用 Redis scan
命令。
所以重述一下 —— Hydra 使用的 key
是按段組織的,這使它們更容易查詢。此外,一致的組織使它們更容易擴展和維護。隨着我們繼續,我們將看到 key
在每個微服務特性的組織中所扮演的角色。讓我們從檢查 presence(examining presence
)開始。
Presence(呈現 type)
在微服務領域中,發現服務、了解服務是否正常以及是否可以路由到該服務的能力至關重要。
這些特性依賴於知道某個特定的服務實例確實存在並可供使用。
對於服務發現(service discovery
)、路由(routing
)和負載平衡(load balancing
)等特性,這也是必需的。
每隔一秒鍾,Hydra 就會更新它的服務 key 的生存時間(TTL
)。
在三秒鍾的時間內這樣做失敗將導致 key 過期,主機應用程序被視為不可用。
在這里我們可以看到使用的 Redis 命令是 “get”
和 “setex”
,它們設置了一個 key 和一個到期時間。
我們可以使用帶有模式匹配項的 “keys”
命令來查詢 presence key。 注意,存在三個 key。這告訴我們存在 “ asset-svcs”
運行的三個實例。
如果我們嘗試檢索其中一個 key 的內容,我們會看到它包含實例ID(instance ID
)。
並對鍵使用 TTL
命令可以向我們顯示,它還有 2
秒鍾的剩余時間。
所以回顧一下。可以使用自動過期的 key 來管理微服務的存在。Hydra 代表主機服務自動更新密鑰。這意味着這不是開發人員做的事情。在3秒內更新 key 失敗將導致服務被視為不可用。這可能意味着服務不健康。
這將我們帶入下一個主題…
Health(健康 type)
能夠監視微服務的運行狀況是另一個重要功能。Hydra 每 5
秒鍾收集並寫入一個健康信息快照。
您可以檢查快照以快速查看單個服務實例的運行狀況。並且,快照可以由監控工具(例如 HydraRouter
儀表板)使用。
這就是健康 key
的樣子。請注意,唯一的新位是標識 key
為關於 health
的 “type”
段。
當我們查看密鑰的內容時,我們看到它包含一個字符串化的 JSON
對象。 在這種情況下,它用於 “project-svcs”
。
將 JSON
解串可以更容易地查看存儲的內容。它包含了很多有用的信息。
因此,可以按服務實例存儲運行狀況(health
)信息。使用包含字符串化的JSON文本的字符串 key 進行管理。 而且這些信息可以通過監視應用程序來使用。
Service Discovery(服務發現)
接下來,讓我們考慮服務發現,這是任何微服務架構的另一個必備功能。
通過名稱發現服務的 IP
和 PORT
位置的能力極大地簡化了通信。其他好處包括不必管理 DNS
條目或創建固定的路由規則。
服務發現信息以一種 “nodes”
的形式存儲在 Redis Hash 中。使用 Hash 可以實現快速的查找。我們使用 Redis 的“hget”
,“hset”
和 “hgetall”
命令來處理 nodes hash。
以下 Redis 操作可用於實現服務發現。
首先是對特定服務類型的查找。
第二個是查找可用實例。第三次查找,允許Hydra檢索有關特定服務實例的信息。
我們可以看到有用的信息,例如服務的版本,instanceID,IP地址和端口,最后還有主機名。在此示例中,主機名也恰好是Docker 容器 ID
。
我們可以使用 Redis “hgetall”
命令檢索有關所有可用實例的信息。這就是 Hydra Router
如何檢索要顯示在其儀表板上的服務列表。
讓我們回顧一下。Hydra 使用 servicename key 段進行查詢,以發現有關服務的各種信息。可以使用 Redis Hash 管理服務詳細信息,該服務可提供快速的服務發現
接下來,讓我們考慮路由。
Routes(路由)
同時路由 HTTP 和消息(例如 Web Socket 或 PubSub )- 要求對 routes
進行驗證。微服務可以發布其 routes
到 Redis。舉個例子,HydraRouter 使用發布的 routes
來實現動態的服務感知路由。
每個服務以 “service:routes” 類型的 key 發布其路由。在這里,我們看到 “asset-svcs” 路由的 key
服務路由存儲在 Set
結構中。非常適合,因為您不想重復輸入路由條目。使用 SADD
和 SMEMBERS
命令。
回到我們的 routes 上。我們可以使用 key
模式拉出路由列表。在這里我們可以看到許多服務的路由。
我們可以使用 “smembers” 命令查看特定路由集的內容。順便說一下,括號中的 [get]
、[post]
和 [put]
位表示 HTTP REST
端點。對於其他消息傳遞傳輸,可以省略括號方法的使用。
讓我們回顧一下。每個服務都會向一個 Redis Set
發布它的路由。訪問一個單獨的路由會顯示該服務的路由條目集合。
路由使用 Set 數據結構存儲在 Redis 中,這避免了重復的路由。發布的路由可用於實現動態的服務感知路由。接下來,讓我們考慮負載平衡。
Load Balancing(負載平衡)
隨着應用程序的增長,您將需要在可用的服務實例之間平衡請求。這是通過使用我們看到的服務呈現(service presence
)和路由(routing
)功能完成的。在應用程序級別,使用 Hydra,這與使用 “makeAPIRequest”
或 “sendMessage”
調用一樣簡單。當 Hydra 使用路由和 presence 信息在可用的目標實例中進行選擇時,就會在這些調用中進行負載平衡。
一個很好的好處是,在路由過程中,如果某個請求在某個特定實例上失敗,Hydra 可以在出現 HTTP 503
服務器不可用錯誤之前重試其他可用實例。
如您所見,負載平衡依賴於其他功能,例如 presence,服務發現和路由。
回顧一下,可以使用我們已經看到的 Presence、服務發現(Service Discovery)和路由(Routing)特性來完成服務之間的負載平衡請求。Redis Strings,Hashes 和 Sets 使這成為可能。整體大於部分之和。
Messaging(消息)
分布式服務強制通過底層網絡彼此通信。
HTTP Rest 調用可能是最常見的,但是 socket 消息傳遞可能更有效。
Hydra 中的消息傳遞是通過 Redis 的 Pub/Sub
通道完成的,而 Redis 通過 socket 連接實現了 Pub/Sub
。
這里有一個例子。Hydra 使用 Redis 的 “subscribe”、“unsubscribe” 和 “publish” 命令。
順便說一句,Hydra router 能夠通過 HTTP 和 WebSocket 接受消息並將其轉換為 pub/sub
消息。
要了解其工作原理,請考慮兩個服務,即 “asset-svcs”
和 “project-svcs”
。
每個服務創建兩個 key,一個使用服務名(service name
),另一個使用服務名(service name
)和實例ID(instance ID
)。
每個服務都監聽兩個 channel
。
在大多數情況下,您並不關心哪個服務實例處理請求。在這些情況下,將使用沒有特定實例ID的通道。
現在,當您需要向特定實例發送消息時,可以使用具有實例ID的通道。 需要特別注意的是,hydra 在負載均衡時會將請求轉換為具有特定實例ID的服務名稱。 這樣可以確保只有一個實例可以處理給定的消息或請求。
我們可以使用 Redis pub/sub channels
命令查看 channel key 列表。注意這里有四個 key。
第一個 key 是 “asset-svcs”
的名稱 —— 由 asset service 的所有實例共享。
接下來,我們將看到三個具有惟一實例id的附加 key。三個服務實例各有一個。
繼續關注消息傳遞。為了確保微服務之間的互操作性,必須標准化共享的通信格式。
通用消息格式是已記錄的基於JSON的格式,其中包括對消息傳遞,路由和排隊的支持。
這些消息作為JSON字符串文本存儲在Redis中。
繼續關注消息傳遞。
為了確保微服務之間的互操作性,必須對共享的通信格式進行標准化。
通用消息格式是一種文檔化的 JSON-based 的格式,包括對消息傳遞(messaging
)、路由(routing
)和隊列(queuing
)的支持。
這些消息作為 JSON 字符串文本存儲在 Redis 中。
下面是一個示例 UMF 消息。
“to”
,“frm”
和 “bdy”
字段是必填字段,服務可以自由地在 “body”
對象中包含自己的自定義字段。
讓我們看看如何在實踐中使用它。
在左邊,“client-svcs”
向 “project-svcs”
發送消息。
注意,這只需要一個 UMF 創建調用和一個發送消息調用,這里用黃色顯示。
在右邊 —— “project-svcs”
偵聽消息並根據需要進行處理。這是使用事件消息偵聽器完成的。
請注意,Hydra
抽象了服務發現(service discovery)、負載平衡(load balancing)、路由和發布/訂閱(pub/sub)等細節。
發送和接收消息只涉及三個成員函數。
在這里稍作停留是值得的。
花幾秒鍾考慮一下使用您最喜歡的堆棧時這個示例會是什么樣子。
讓我們仔細看看。Send message
通過解析消息中的 “to”
字段來確定目標服務名稱。
有了服務名,下一步是檢查可用的實例。
有了目標實例,消息就會被字符串序列化,並通過 Redis 的 “publish”
命令發送。
同樣,我們可以列出Redis中的所有發布/訂閱通道(Pub/Sub Channnel
)。
消息可以通過這些通道發送,並由偵聽器(listeners
)檢索。
因此,只需編寫一些編程代碼,我們就可以使用 Redis 通過組織良好的通道集合(collection of channels
)來路由消息。
總而言之,值得注意的是,由於服務是物理分布的,因此最終需要進行消息傳遞。
Redis 使用其發布/訂閱(pub/sub
)功能啟用消息傳遞。
標准化通信可以實現服務之間的互操作性。
我們還看到,當我們抽象出底層服務發現(service discover
)、負載平衡(load balancing
)、
路由(routing
)和發布/訂閱(pub/sub
)細節時,
在應用程序級別上的通信是多么容易。
接下來,讓我們考慮消息隊列。
Queuing(隊列)
作業(Job
)和消息隊列(message queues
)是許多重要應用程序的另一個重要部分。
Hydra 使用 Redis 為每種服務類型維護動態隊列(dynamic queues
)。
然后,服務實例可以讀取其隊列和處理項目。
隊列消息的內容是UMF消息,遵循用於消息傳遞的相同格式。
同樣,互操作性為王!
Hydra 會為每種服務類型自動創建三個隊列。
- “已接收(
received
)”隊列 - “進行中(
inprogress
)”隊列 - 還有一個“不完整(
incomplete
)”隊列。
因為這些是列表,我們使用 Redis 的 “lpush”
、“rpush”
、“rpoplpush”
和 “lrem”
命令。
下面的圖表顯示了隊列之間的消息流。
在 Redis 中,在隊列之間移動項目是一個原子操作。
所以不管你有多少微服務,它都是安全的。
在下一個左邊的示例中,
對消息進行排隊就像創建一個 UMF 消息並調用 “queueMessage”
來發送它一樣簡單。
右下角的代碼顯示了圖像處理服務通過調用 “getQueuedMessage”
,
然后在處理完消息后調用 “markQueueMessage”
,使消息脫離隊列。很簡單吧?
因此,回顧一下,有時無法期望立即做出回應。
在這種情況下,我們只需要排隊等待后續處理。
Redis List
數據結構可以用作消息隊列。
使用原子操作的 “lpush”
和 “rpoplpush”
之類的命令使此操作可行。
再次在這里,我們看到了使用高級抽象進行基本排隊有多么容易。
Logging(日志記錄)
分布式日志記錄(Distributed logging
)是任何微服務體系結構的另一個重要特征。
但是,如果您知道 Redis
,可能會對將其用作分布式記錄器(distributed logger
)感到震驚。
您可能會理所當然地擔心。但是,您可以將其用作飛行記錄器(flight recorder
)。
僅存儲最嚴重的錯誤,並使用 “lpush”
和 “ltrim”
限制條目的數量。
然后,至少您將有一種快速的方法來檢查微服務可能出了什么問題。
這是 key
的樣子。注意,key
類型為 health:log
。
在這里,我們可以看到 health:log
key 類型實際上是一個 “List”
數據結構。
所以我們可以使用 Redis 的 “lrange”
命令來查看 “imageproc-svcs”
的飛行記錄器(flight recorder)日志。
總結:使用微服務無法登錄數十台或更糟的數百台計算機。
分布式日志記錄絕對是必經之路。
使用 Redis
,您可以構建一個輕量級的記錄器(light-weight logger
)以用作飛行記錄器(flight recorder
)。
使用 Redis List
數據結構以及方便的 “lpush”
和 “ltrim”
命令可以實現此目的。
最后,讓我們考慮配置管理。
Configuration management(配置管理)
管理分布式微服務的配置文件可能具有挑戰性。然而,你甚至可以使用 Redis 來存儲你服務的配置文件。
但這並不理想,得遠離,核心缺點是在 Redis 中存儲配置會使 Redis 有狀態。但這是可以做的。
讓我們看看它是如何工作的。
configs key 類型是一個 hash。
該 hash 的 key 由服務版本和設置為該版本配置數據的值組成。
下面是一個配置示例。
在我們的示例中,我們使用名為 “hydra-cli”
的命令行工具,
它允許我們將配置文件推到特定的服務版本。
所做的一切就是創建一個 hash 條目,其鍵由服務名稱和版本組成,
並將文件內容字符串序列化后(stringified
)作為其值。
記住,你也可以使用 shell 腳本來驅動 redis cli。
我們可以使用 “hget”
命令和配置的版本提取一個特定的版本。
讓我們快速回顧一下,我們了解了 Redis
如何用於存儲應用程序配置文件。
Redis Hash
數據結構允許我們存儲每種服務類型的配置。
每個配置條目均由服務版本標簽索引,並且內容僅指向字符串化的 JSON 配置。
總結
這里分享的是一種大量地使用 JavaScript 和 NodeJS 來利用 Redis 構建分布式應用程序的工程方案。
但是,你完全可以用其他你愛的語言(如:Golang)對 Redis 做同樣的事情。
互相交流
我是為少。微信:uuhells123。公眾號:黑客下午茶。
謝謝點贊支持👍👍👍!