時光粒子源碼
分佈式一致性/分佈式存儲等開源技術探討, GitHub:https://timequark.github.io/
State 是 raft 的核心類,封裝了框架性的性重要操作,持有Node (server.py)、Log、FileStorage、StateMachine對象;以及 Role 角色,完成 Leader/Follower/Candidate 之間的轉換。
BaseRole的構造方法,將傳入State對象。
State 是上下文,將網絡、Role轉換、Log、FileStorage、StateMachine串聯起來。
以下是State類圖:
(可以下載保存圖片看大圖~~~)
可以看出,State 是整個系統中的核心引擎類,銜接了Network、Node、Role(包括Leader/Follower/Candidate)、Log、StateMachine、FileStorage 等各個對象之間的聯繫。
State源碼分析
state.py
class State:
"""Abstraction layer between Server & Raft State and Storage/Log & Raft State"""
# <Leader object> if state is leader
# <state_id> if state is follower
# <None> if leader is not chosen yet
# Roel爲 Leader 時,leader 即 Leader 自身
# Role爲 Follower/Candidate 時,leader 爲 Leader 的 id
# 注意:
# Leader 選舉成功後,如果還有 Candidate (即可能出現的 split vote 所導致的多個 Candidate 共存),
# Candidate 收到 Leader 的 append_entries 後,自動轉成 Follower 角色
leader = None
# Await this future for election ending
# 異步通知:阻塞在Leader上的動作
leader_future = None
# Node id that's waiting until it becomes leader and corresponding future
# 異步通知:等待指定節點變爲 Leader
wait_until_leader_id = None
wait_until_leader_future = None
def __init__(self, server):
self.server = server
self.id = self._get_id(server.host, server.port)
self.__class__.loop = self.server.loop
# storage, log, state_machine, role
# 均在 State 中實例化
self.storage = FileStorage(self.id)
self.log = Log(self.id)
self.state_machine = StateMachine(self.id)
# Leader/Follower/Candidate構造方法傳入 State
self.role = Follower(self)
# Role啓動
def start(self):
self.role.start()
# Role停止
def stop(self):
self.role.stop()
# 見@leader_required的註解實現
@classmethod
@leader_required
async def get_value(cls, name):
return cls.leader.state_machine[name]
# 見@leader_required的註解實現
@classmethod
@leader_required
async def set_value(cls, name, value):
await cls.leader.execute_command({name: value})
# 發送消息
def send(self, data, destination):
return self.server.send(data, destination)
# 廣播消息
def broadcast(self, data):
"""Sends request to all cluster excluding itself"""
return self.server.broadcast(data)
# 數據傳遞流程:
# UDPProtocl.datagram_received -> Node.request_handler -> State.request_handler
# 根據type,調用相應的 on_reveive_*** 方法(on_receive_*** 方法在Leader/Follower/Candidate各自有不同的實現)
def request_handler(self, data):
getattr(self.role, 'on_receive_{}'.format(data['type']))(data)
@staticmethod
def _get_id(host, port):
return '{}:{}'.format(host, port)
def get_sender_id(self, sender):
return self._get_id(*sender)
@property
def cluster(self):
return [self._get_id(*address) for address in self.server.cluster]
# 過半判定
def is_majority(self, count):
return count > (self.server.cluster_count // 2)
# Role 轉成 Candidate
def to_candidate(self):
self._change_role(Candidate)
self.set_leader(None)
# Role 轉成 Leader
def to_leader(self):
self._change_role(Leader)
self.set_leader(self.role)
# 回調on_leader監聽方法
if asyncio.iscoroutinefunction(config.on_leader):
asyncio.ensure_future(config.on_leader())
else:
config.on_leader()
# Role 轉成 Follower
def to_follower(self):
self._change_role(Follower)
self.set_leader(None)
# 回調on_follower監聽方法
if asyncio.iscoroutinefunction(config.on_follower):
asyncio.ensure_future(config.on_follower())
else:
config.on_follower()
def set_leader(self, leader):
'''
We can have a look at description in Class State. Like the following part:
# <Leader object> if state is leader
# <state_id> if state is follower
# <None> if leader is not chosen yet
leader = None
'''
cls = self.__class__
cls.leader = leader
# 異步通知 cls.leader_future
if cls.leader and cls.leader_future and not cls.leader_future.done():
# We release the future when leader is elected
cls.leader_future.set_result(cls.leader)
# 異步通知 cls.wait_until_leader_future
if cls.wait_until_leader_id and (
cls.wait_until_leader_future and not cls.wait_until_leader_future.done()
) and cls.get_leader() == cls.wait_until_leader_id:
# We release the future when specific node becomes a leader
cls.wait_until_leader_future.set_result(cls.leader)
def _change_role(self, new_role):
self.role.stop()
self.role = new_role(self)
self.role.start()
# 獲取 Leader id
@classmethod
def get_leader(cls):
if isinstance(cls.leader, Leader):
return cls.leader.id
return cls.leader
# 等待選舉成功
@classmethod
async def wait_for_election_success(cls):
"""Await this function if your cluster must have a leader"""
if cls.leader is None:
cls.leader_future = asyncio.Future(loop=cls.loop)
await cls.leader_future
# 等待指定節點成爲 Leader
@classmethod
async def wait_until_leader(cls, node_id):
"""Await this function if you want to do nothing until node_id becomes a leader"""
if node_id is None:
raise ValueError('Node id can not be None!')
if cls.get_leader() != node_id:
cls.wait_until_leader_id = node_id
cls.wait_until_leader_future = asyncio.Future(loop=cls.loop)
await cls.wait_until_leader_future
cls.wait_until_leader_id = None
cls.wait_until_leader_future = None