クライアントpythonホットアップデート

4130 ワード

紹介:
ホット・アップデートとは、サービスを継続的に維持する場合に、ソフトウェア・コードの論理または構成データを更新修復することです.ゲームプロジェクトにスクリプト言語が導入されるにつれて、ホット更新技術は徐々に標準化され、私が経験したゲームプロジェクトでは、サービス側でもクライアント側でも、バージョンの更新反復は静的patchと動的patch(ホット更新)をめぐって行われています.クライアントPythonのホットアップデートの処理についてお話しします.
原理:
  • 標準import標準pythonモジュールをインポートし、モジュールをメモリにロードしsysに追加できることを知っています.modulesで.複数のimport同じモジュールは、現在のLocal名前空間に名前をインポートするだけで、1つのモジュールは重複してロードされません.
  • reload関数reload()関数は、インポートしたモジュールを再ロードすることができ、pythonモジュールを熱的に更新できるように見えます.残念なことに、pythonオリジナルのreload関数はゲームのホット更新の問題をはるかに満たすことができません.なぜなら、reloadが再ロードされたモジュールは古いバージョンのモジュールを削除しません.つまり、すでに参照されている古いモジュールは更新できません.同じように古いオブジェクトの参照ができないため、fromを使用します.import ... 方式で参照されるモジュールはreloas(m)を更新することができなくなった後も、classとその派生classのインスタンスオブジェクトは、古いclass定義を使用します.モジュールのロードに失敗した場合,rollbackメカニズムがなく,モジュールを再importする必要があるため,ゲームのシナリオと結びつけて適切なreloadをカスタマイズする必要がある.新しいカスタムreloadの目的は、元のプログラムが終了しない場合に、変更後のコードを動的にロードできるようにすることです.主に以下の2点に達したい:
  • 開発期間の開発効率を向上させる
  • ゲームを再起動することなく緊急BUG
  • を修復する
    実装:
    ホットアップデートでは,作成したオブジェクトに新しいコードの変化をどのように獲得させるか,reload前後でタイプの不一致を生じさせないかが実際のポイントである.functionをリフレッシュすると、class内で定義されたmethodが実現しやすいが、module内で定義された変数をリフレッシュするには、class内で定義された変数、さらに新しく追加されたメンバー変数については、統一的な約束が必要である.したがって,ホット更新の過程で,コード更新とデータ更新の2点を考慮すれば,更新は可能である.次は、新しいreloadがどのような特性を備えているかを示します.
  • 更新コード定義(function/method/static_method/class_method)
  • はデータを更新(コード定義以外のタイプはデータとみなす)
  • である.
  • moduleでreload_を約束moduleインタフェース、classでreload_を約束classインタフェース、この2つのインタフェースでデータの更新を手動で処理し、さらに多くの約束とインタフェースが完了する
  • 関数オブジェクトの内容を置換
    #                      ,              
    def update_function(oldobj, newobj, depth=0):  
        setattr(oldobj, "func_code", newobj.func_code)  
        setattr(oldobj, "func_defaults", newobj.func_defaults)  
        setattr(oldobj, "func_doc", newobj.func_doc)  
    

    クラスの内容を置換
    #            ,            
    def _update_new_style_class(oldobj, newobj, depth):  
        handlers = get_valid_handlers()  
        for k, v in newobj.__dict__.iteritems():  
            #     key    class ,     
            if k not in oldobj.__dict__:  
                setattr(oldobj, k, v)  
                _log("[A] %s : %s"%(k, _S(v)), depth)  
                continue  
            oldv = oldobj.__dict__[k]  
      
            #   key       class   ,    class     
            if type(oldv) != type(v):  
                _log("[RD] %s : %s"%(k, _S(oldv)), depth)  
                continue  
      
            #              
            v_type = type(v)  
            handler = handlers.get(v_type)  
            if handler:  
                _log("[U] %s : %s"%(k, _S(v)), depth)  
                handler(oldv, v, depth + 1)  
                #       oldv   ,     setattr 。  
            else:  
                _log("[RC] %s : %s : %s"%(k, type(oldv), _S(oldv)), depth)  
      
        #      reload_class  ,            
        object_list = gc.get_referrers(oldobj)  
        for obj in object_list:  
            #                  
            if obj.__class__.__name__ != oldobj.__name__:  
                continue  
            if hasattr(obj, "x_reload_class"):  
                obj.x_reload_class()  
    

    staticmethod
    def _update_staticmethod(oldobj, newobj, depth):  
        #   staticmethod  ,   sm.__get__(object)    function    
        oldfunc = oldobj.__get__(object)  
        newfunc = newobj.__get__(object)  
        update_function(oldfunc, newfunc, depth)  
    

    classmethod
    def _update_classmethod(oldobj, newobj, depth):  
        oldfunc = oldobj.__get__(object).im_func  
        newfunc = newobj.__get__(object).im_func  
        update_function(oldfunc, newfunc, depth)  
    

    モジュールの更新も似ていて、いちいち貼らないで、ただ元のreloadの基礎の上で改良を行って、モジュールの熱い更新に対して、また1つのreload_を約束しましたmoduleインタフェースで、データの更新をカスタマイズできます.次に、いくつかの例を追加します.
    def x_reload_class(self):  
        """     ,                  
                           ,                     
                 ,          
        """  
        self._new_var = 5000    #          
        self.init_widget()      #         
    

    現在のホット更新モジュールは開発デバッグで使用されており、いくつかの更新タスクを簡単に完了することができますが、リモートクライアントに更新するには、閉パッケージ、内部ローカル変数など、より多くの規範とインタフェースが必要であり、徐々に学習と改善が必要です.