分佈式任務調度系統V1目標
初步目標實現,實現任務的下發分配,分佈式任務執行,支持任務分片(在代碼上支持),任務執行記錄。
任務調度系統構思
基於C/S架構實現,基於長連接來管理實現,當前版本的邏輯架構圖如下;
系統主要是通過自有協議進行通信,採用單個長連接來進行數據交互,Server端主要參考早期版本tornado的設計思路,Client端實現主要參考Kazoo的設計思路。
協議
實現自有的協議,在選擇協議時,二進制協議相對而言性能較高,但由於精力所限還是採用的字節流解析,首先將接受的數據解析成字符串,再根據字符串來進一步解析。基本的消息格式如下;
16\r\ntest\nstatus\t200
16:代表test\nstatus\t200的長度
\r\n: 表示頭部長度信息分隔符
test:表示消息的類型
\n: 表示消息類的內容信息
status: 表示消息裏面的key
\t: 表示消息體裏面的分隔符,分割key與value
200: 表示解析出來的key對應的value
根據該消息格式可進一步解析爲;
length\r\ntype\nkey1\tvalue2\tkey2\tvalue2....
根據不同的類型去定義不同的解析方式,並可靈活擴展不同的參數。
Server端
Server端的實現,主要參考了tornado的早期版本的架構實現,採用了單線程IO複用的架構模式來實現服務端較好的處理性能,在早期版本中該模式可以減少部分高併發模式下一些資源競爭的問題。其中也是分層的抽象爲Server層,數據流Stream層,Application處理層,並通過註冊handler的形式,將不同的處理條件註冊到Application中。
Client端
Client端主要參考了Kazoo的架構的實現的思路,借鑑了其實現的基本思路,即開啓單獨的線程來進行事件監聽,來監聽服務端發送過來的數據,並通過條件變量來實現與主線程之間的數據交互。
時序圖
系統演示
首先,開啓一個server端,也可以通過taskserver啓動,會監聽默認的端口4546,ip爲127.0.0.1
(venv) wuzideMacBook-Pro:local_work wuzi$ taskserver
server start listen at 127.0.0.1:4546
此時,編寫兩個文件分別爲task1.py,與task2.py
# task1.py
import sys
sys.path.append("/Users/wuzi/PycharmProjects/distributed_schedule")
from distributed_schedule.tasks import Task, register_task
from distributed_schedule.client import main
import time
class TestTask(Task):
def execute(self, *args, **kwargs):
print("TestTask task {0} {1}".format(args, kwargs))
time.sleep(10)
print("TestTask over ")
class TestDTask(Task):
def execute(self, *args, **kwargs):
print("TestDTask task {0} {1}".format(args, kwargs))
time.sleep(1)
raise
print("TestDTask over ")
register_task(TestTask("test_task"))
register_task(TestDTask("testd_task"))
main()
# task2.py
import sys
sys.path.append("/Users/wuzi/PycharmProjects/distributed_schedule")
from distributed_schedule.tasks import Task, register_task
from distributed_schedule.client import main
import time
class Test2Task(Task):
def execute(self, *args, **kwargs):
print("Test2Task task {0} {1}".format(args, kwargs))
time.sleep(1)
print("Test2Task over ")
class TestD2Task(Task):
def execute(self, *args, **kwargs):
print("TestD2Task task {0} {1}".format(args, kwargs))
time.sleep(1)
print("TestD2Task over ")
register_task(Test2Task("test2_task"))
register_task(TestD2Task("test2d_task"))
main()
此時我們通過類似與api的形式來調用該任務client_api.py
import sys
from distributed_schedule.client import TaskClient, Client
from distributed_schedule.config import set_logging_level
sys.path.append("/Users/wuzi/PycharmProjects/distributed_schedule")
set_logging_level(20)
c = Client("client")
c.start()
tclient = TaskClient(c)
print("start")
res = tclient.submit("test_task", shard=5)
#
print(res)
res = tclient.submit("test_task", mode="all")
print(res)
res = tclient.submit("test2d_task", mode="once")
print(res)
此時我們先啓動task1.py和task2.py
python task1.py --name clienttask1 --role worker
python task2.py --name clienttask2 --role worker
python task1.py --name="clienttask3" --role worker
此時,就有兩個客戶端執行了task1.py中的任務,即test_task,testd_task任務有兩個可以執行的客戶端即clienttask1和clienttask3。此時執行client_api.py文件。
python client_api.py
start
('broad', 'status\t200\t')
('broad', 'status\t200\t')
('broad', 'status\t200\t')
此時終端任務的展示如下;
(venv) wuzideMacBook-Pro:local_work wuzi$ python task1.py --name clienttask1 --role worker
response ok
response ok
TestTask task () {'event_id': '06ac4d14-b1eb-47c7-ab00-535a7587f6eb', 'status': 'doing', 'client_event_id': 'a677403a-a5e2-42b1-b61b-00940388917d', 'item': '0'}
TestTask task () {'event_id': '06ac4d14-b1eb-47c7-ab00-535a7587f6eb', 'status': 'doing', 'client_event_id': 'a677403a-a5e2-42b1-b61b-00940388917d', 'item': '2'}
TestTask task () {'event_id': '06ac4d14-b1eb-47c7-ab00-535a7587f6eb', 'status': 'doing', 'client_event_id': 'a677403a-a5e2-42b1-b61b-00940388917d', 'item': '4'}
TestTask task () {'event_id': '4e8297c1-80ba-4c40-bb84-392849169efa', 'status': 'doing', 'client_event_id': '79d3a627-e5eb-4869-8136-c995b502c1f9'}
TestTask over
TestTask over
TestTask over
TestTask over
(venv) wuzideMacBook-Pro:local_work wuzi$ python task2.py --name clienttask2 --role worker
response ok
response ok
TestD2Task task () {'event_id': 'bd874874-7e44-4ea0-9cfd-0d41024d807a', 'status': 'doing', 'client_event_id': 'eb54c8bf-c979-4f2f-912b-03edbb6c7b14'}
TestD2Task over
(venv) wuzideMacBook-Pro:local_work wuzi$ python task1.py --name="clienttask3" --role worker
response ok
response ok
TestTask task () {'event_id': '06ac4d14-b1eb-47c7-ab00-535a7587f6eb', 'status': 'doing', 'client_event_id': 'e31ccc68-f0dc-47e0-bfa8-cdf4a9de0003', 'item': '1'}
TestTask task () {'event_id': '06ac4d14-b1eb-47c7-ab00-535a7587f6eb', 'status': 'doing', 'client_event_id': 'e31ccc68-f0dc-47e0-bfa8-cdf4a9de0003', 'item': '3'}
TestTask task () {'event_id': '4e8297c1-80ba-4c40-bb84-392849169efa', 'status': 'doing', 'client_event_id': 'd804769d-0844-4cff-aa41-beb254bb84e7'}
TestTask over
TestTask over
TestTask over
從終端輸出結果可以看出,test_task被分片執行了五次,分別爲clienttask1中的分片0,2,4和clienttask3中的分片1,3;在test_task通知所有執行的過程中,clienttask1和clienttask3分別執行了一次,最後調用了一次test2d_task任務,該任務就執行了一次。
總結
本文只是簡單編寫的第一版本的分佈式任務,項目上傳到了github傳送門,由於本人水平有限,編寫過程中會出現不少錯誤與問題,並且代碼設計與編寫並不算規範,希望多多批評。本項目測試用例並沒有編寫完成,所以後續會繼續補上,並且第一版架構相對簡單,存在服務單點問題,並且數據並沒有做持久化,後續會會慢慢規劃上。由於本人才疏學淺,如有錯誤請批評指正。