Ray 框架提供 HTTP 和 Python 兩種方式呼叫微服務。HTTP 方式可讓外部服務透過 URL 引數與微服務互動,例如實作溫度轉換功能,其回傳結果以 JSON 格式呈現。Python 直接呼叫則需先取得微服務的控制程式碼,再透過模擬 HTTP 請求的方式進行呼叫,兩種方式的執行結果相同。為提升 Python 呼叫的便利性,可為佈署類別新增方法,避免手動建構 Request 物件,簡化呼叫流程。Ray 提供同步和非同步控制程式碼,可透過 sync 引數設定。同步控制程式碼預設回傳 Ray ObjectRef,非同步控制程式碼則需搭配 await 使用。佈署功能可透過 @serve.deployment 註解擴充,例如設定複製數量和資源需求。FastAPI 提供更便捷的 HTTP 佈署方式,可靈活定義 API 路由和處理器。此外,Ray 也支援佈署組合,可將多個微服務整合為完整應用系統。文章也探討了 Ray 的自動擴充套件機制,其與 Kubernetes 不同,Ray 會建立更多節點並重新分配佈署,以更有效利用資源。當請求量超過每秒三千個時,建議擴充套件 HTTP 進入點並佈署負載平衡器。資源需求可透過 @serve.deployment 中的 ray_actor_options 引數指定,路由字首則可使用 route_prefix 引數設定。FastAPI 與 Ray Serve 的整合可簡化 API 呼叫,並透過不同的 URL 路徑對應不同的方法。最後,文章也介紹了 Canary Deployment 策略,透過逐步引入新版本以降低風險,並說明如何在 Ray 中實作此策略。
利用Ray進行微服務佈署與呼叫
HTTP呼叫及Python直接呼叫
在Ray中,微服務的佈署可以透過HTTP和Python直接呼叫來進行。這裡我們將探討這兩種方式的具體實作和應用。
URL引數與外部服務
首先,我們使用URL引數(查詢字串)來指定引數。由於服務是透過HTTP公開的,因此請求者可以在Ray之外的任何地方執行程式碼。例如,我們可以透過HTTP呼叫微服務來進行溫度轉換。
以下是HTTP呼叫的結果範例:
{
"Fahrenheit temperature": 212.0
}
{
"Celsius temperature": 37.77777777777778
}
{
"Unknown conversion code": "CC"
}
Python直接呼叫
除了HTTP呼叫,我們還可以直接使用Python來呼叫微服務。這需要取得到微服務的控制程式碼,然後使用它來進行呼叫。以下是具體的實作方法:
from starlette.requests import Request
handle = serve.get_deployment('Converter').get_handle()
print(ray.get(handle.remote(Request(
{"type": "http", "query_string": b"temp=100.0&type=CF"}))))
print(ray.get(handle.remote(Request(
{"type": "http", "query_string": b"temp=100.0&type=FC"}))))
print(ray.get(handle.remote(Request(
{"type": "http", "query_string": b"temp=100.0&type=CC"}))))
內容解密:
在這段程式碼中,我們手動建立Starlette請求,並指定請求型別和查詢字串。這樣可以模擬HTTP請求,從而實作對微服務的呼叫。這段程式碼的執行結果與HTTP呼叫的結果相同,展示了兩種呼叫方式的等效性。
增強Python呼叫的便利性
儘管上面的方法可以工作,但最佳實踐是為Python呼叫實作額外的方法,以避免在Python呼叫中使用Request物件。以下是擴充套件初始佈署以支援Python呼叫的範例:
@serve.deployment
class Converter:
def __call__(self, request):
if request.query_params["type"] == 'CF' :
return {"Fahrenheit temperature":
9.0/5.0 * float(request.query_params["temp"]) + 32.0}
elif request.query_params["type"] == 'FC' :
return {"Celsius temperature":
(float(request.query_params["temp"]) - 32.0) * 5.0/9.0 }
else:
return {"Unknown conversion code" : request.query_params["type"]}
def celcius_fahrenheit(self, temp):
return 9.0/5.0 * temp + 32.0
def fahrenheit_celcius(self, temp):
return (temp - 32.0) * 5.0/9.0
Converter.deploy()
# 列出當前佈署
print(serve.list_deployments())
內容解密:
這段程式碼中,我們為Converter類別增加了兩個新方法:celcius_fahrenheit 和 fahrenheit_celcius。這些方法允許我們更方便地進行溫度轉換操作,而不需要手動構建Request物件。
呼叫新增方法
有了這些新方法後,我們可以更簡單地進行Python呼叫:
print(ray.get(handle.celcius_fahrenheit.remote(100.0)))
print(ray.get(handle.fahrenheit_celcius.remote(100.0)))
內容解密:
這段程式碼展示瞭如何使用新增的方法來進行溫度轉換。與之前手動構建Request物件不同,這裡的請求型別是隱含的——它由方法名稱決定。這種方式更加直觀且易於使用。
同步與非同步控制程式碼
Ray提供了同步和非同步控制程式碼。我們可以透過設定sync引數來指定控制程式碼型別:
- 預設情況下,控制程式碼是同步的,
handle.remote傳回一個Ray ObjectRef。 - 若設定
sync=False,則建立非同步控制程式碼。在此情況下,呼叫需要使用await來取得Ray ObjectRef。需要在Pythonasyncio事件迴圈中執行。
增強佈署功能
除了HTTP和直接Python呼叫外,我們還可以透過Kafka等其他方式來呼叫佈署。以下是增強佈署功能的一些選項:
新增引數至註解
@serve.deployment註解可以接受多個引數,最常見的是複製數量和資源需求。例如:
@serve.deployment(num_replicas=3)
class Converter:
def __init__(self):
from uuid import uuid4
self.id = str(uuid4())
def __call__(self, request):
if request.query_params["type"] == 'CF' :
return {"Deployment": self.id, "Fahrenheit temperature":
9.0/5.0 * float(request.query_params["temp"]) + 32.0}
elif request.query_params["type"] == 'FC' :
return {"Deployment": self.id, "Celsius temperature":
(float(request.query_params["temp"]) - 32.0) * 5.0/9.0 }
else:
return {"Unknown conversion code" : request.query_params["type"]}
內容解密:
這段程式碼展示瞭如何透過設定num_replicas引數來增加複製數量。這樣可以提高佈署的可擴充套件性和負載平衡能力。
FastAPI HTTP佈署
FastAPI提供了一種便捷的方式來建立HTTP佈署。這種方式允許我們更靈活地定義API路由和處理器。
佈署組合
我們還可以透過組合多個佈署來實作更複雜的功能。例如,我們可以將多個微服務組合在一起,形成一個完整的應用系統。
總結來說,Ray提供了豐富且靈活的佈署選項,使得我們可以根據具體需求進行定製化組態和擴充套件。希望這些內容能夠幫助你更好地理解和應用Ray中的微服務架構。
微服務架構與 Ray 的自動擴充套件
在現代軟體開發中,微服務架構已成為一種主流設計模式。它透過將應用程式拆分為多個獨立的服務來提高系統的可擴充套件性和維護性。而 Ray 作為一個強大的分散式計算框架,提供了豐富的工具來實作微服務的佈署和管理。本文將探討如何在 Ray 中實作微服務架構,並深入瞭解其自動擴充套件機制。
溫度轉換器的佈署
首先,我們來看一個簡單的例子:溫度轉換器。這個服務可以將攝氏溫度轉換為華氏溫度,反之亦然。以下是使用 Ray Serve 佈署這個溫度轉換器的程式碼:
import ray
from ray import serve
import requests
@serve.deployment
class Converter:
def celcius_fahrenheit(self, temp):
return {"Fahrenheit temperature": 9.0/5.0 * float(temp) + 32.0}
def fahrenheit_celcius(self, temp):
return {"Celsius temperature": (float(temp) - 32.0) * 5.0/9.0}
def __call__(self, request):
temp = request.query_params["temp"]
conversion_type = request.query_params["type"]
if conversion_type == "cf":
return self.celcius_fahrenheit(temp)
elif conversion_type == "fc":
return self.fahrenheit_celcius(temp)
else:
return {"Deployment": self.id, "Unknown conversion code": conversion_type}
Converter.deploy()
# list current deployments
print(serve.list_deployments())
內容解密:
在上述程式碼中,我們定義了一個名為 Converter 的類別,並使用 @serve.deployment 裝飾器來將其標記為一個可佈署的服務。這個類別包含兩個方法:celcius_fahrenheit 和 fahrenheit_celcius,分別用於將攝氏溫度轉換為華氏溫度和將華氏溫度轉換為攝氏溫度。
在 __call__ 方法中,我們從請求引數中提取溫度值和轉換型別,並根據轉換型別選擇相應的方法進行計算。如果轉換型別無效,則傳回一個包含未知轉換程式碼的錯誤訊息。
最後,我們使用 Converter.deploy() 方法來佈署這個服務,並列出當前所有已佈署的服務。
自動擴充套件機制
Ray 的自動擴充套件機制與 Kubernetes 的方式不同。Ray 不會僅僅建立新的例項來處理負載增加,而是會建立更多的 Ray 節點並重新分配佈署。這種方式可以更有效地利用資源。
當你的佈署開始超過每秒三千個請求時,你應該考慮擴充套件 HTTP 進入點到 Ray。預設情況下,HTTP 伺服器僅在頭節點上啟動,但你可以透過以下方式在每個節點上啟動 HTTP 伺服器:
serve.start(http_options={"location": "EveryNode"})
如果你擴充套件了 HTTP 進入點的數量,你還需要佈署一個負載平衡器來分配流量。
資源需求
你可以在 @serve.deployment 中指定特定的資源需求。例如,如果你需要兩個 CPU 和一個 GPU,可以這樣指定:
@serve.deployment(ray_actor_options={"num_cpus": 2, "num_gpus": 1})
class Converter:
# class definition
路由字首
你還可以使用 route_prefix 引數來指定 HTTP 請求的字首。這樣可以使 URL 更加直觀和易於管理:
@serve.deployment(route_prefix="/converter")
class Converter:
# class definition
FastAPI 的整合
為了使 API 呼叫更加便捷,我們可以使用 FastAPI 與 Ray Serve 的整合來實作多個 HTTP API。以下是修改後的程式碼:
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@serve.deployment(route_prefix="/converter")
@serve.ingress(app)
class Converter:
@app.get("/cf")
def celcius_fahrenheit(self, temp: float):
return {"Fahrenheit temperature": 9.0/5.0 * temp + 32.0}
@app.get("/fc")
def fahrenheit_celcius(self, temp: float):
return {"Celsius temperature": (temp - 32.0) * 5.0/9.0}
Converter.deploy()
uvicorn.run(app, host="0.0.0.0", port=8000)
內容解密:
在此程式碼中,我們使用 FastAPI 建立了一個名為 app 的應用程式例項。然後我們在 Converter 類別中定義了兩個新的方法:celcius_fahrenheit 和 fahrenheit_celcius。這兩個方法分別對應於不同的 URL 路徑 /cf 和 /fc。
這樣做的好處是使 API 呼叫更加直觀和便捷。例如,你可以透過以下方式呼叫這些 API:
print(requests.get("http://127.0.0.1:8000/converter/cf?temp=100.0").text)
print(requests.get("http://127.0.0.1:8000/converter/fc?temp=100.0").text)
Canary Deployment
Canary Deployment 是一種常見的佈署策略,透過逐步引入新版本來減少風險。Ray 支援這種策略透過佈署組合來實作。
首先,我們定義兩個基本佈署:
@serve.deployment
def version_one(data):
return {"result": "version1"}
version_one.deploy()
@serve.deployment
def version_two(data):
return {"result": "version2"}
version_two.deploy()
然後我們建立一個 Canary Deployment:
@serve.deployment(route_prefix="/versioned")
class Canary:
def __init__(self, canary_percent):
from random import random
self.version_one = version_one.get_handle()
self.version_two = version_two.get_handle()
self.canary_percent = canary_percent
def __call__(self, request):
if random() < self.canary_percent:
return self.version_two.remote(request)
else:
return self.version_one.remote(request)
Canary.deploy(canary_percent=0.2)
Canary Deployment 的邏輯解密:
Canary Deployment 的核心邏輯是根據隨機數決定是否將請求路由到新版本或舊版本。在 __init__ 方法中,我們初始化兩個處理程式:version_one 和 version_two。然後在 __call__ 方法中,根據隨機數與給定比例 canary_percent 的比較結果來選擇路由目標。
