Raft源碼分析(一) - State

時光粒子源碼

分佈式一致性/分佈式存儲等開源技術探討, 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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章