Pythonフルスタックシリーズ16-論理フルフロー制御


説明
何事も間違いを犯す可能性がある限り、必ず間違いを犯す.
マーフィーの法則に関するいくつかの説は参考になる.マスクのSpaceX(数十人の従業員しかいない)はもう一つの良い逆例の参考であり、少なくとも以下の点を説明しています.
  • 宇宙飛行という複雑な状況でも何らかの方法(アルゴリズム)で極めて高い信頼性(6つの9?)に達する可能性がある.
  • 自分で作ったものは、時間が少し長くなるかもしれませんが、本当に無限の可能性を持っています.

  • 既存の有人宇宙船に搭載されている星搭載コンピュータとコントローラの例では、単一コントローラの価格は500万人民元前後、単一コントローラの価格は500万人民元前後、計14システムで、高信頼性を追求するために、システム1+1バックアップごとに、計28コントローラで、コストは計約1.4億人民元である.スペースXの竜飛船主制御システムのチップセットは2.6万元しか使わず、コストの差は5348倍だった.
    ヒント:将来は少ない人、低いコストで、トップクラスのことを完成するのに十分です.ある程度はThe Fewer、The Better!
    地上に戻って,本論文では,中程度の複雑なシステムの監視とdebug過程を先に解決するために,高信頼性の決定(制御)方法を主キーで構築する方法について議論する.
    仮想シーン:
  • には、Flaskを実行し、ネットワークサービスを提供するクラウドサーバがあります.
  • クラウドサーバにはRabbitMQがあり、メッセージサービスを提供し、FlaskはRabbitMQと通信します.
  • ローカルサーバ上にメッセージ消費者があり、RabbitMQと通信し、間接的にFlaskにサービスを提供する.

  • チェーンは大体こうです.
    ユーザFlask[pikaメッセージ消費]
    1コンテンツ
    1.1 Flaskのロジック
    完全なサービスを考慮すると、Flask(青写真モード)は少なくともいくつかの部分を備えなければならない.
  • 1ユーザー登録と認証
  • 2(メール)メッセージインタラクション
  • 3アプリケーション権限
  • 4データ(ライブラリ)アクセス
  • 5応用ロジック
  • その中で応用ロジックはその中で最も重要で、最も柔軟な部分であるべきで、今回はこれだけを議論します.アプリケーションには、次のような内容が含まれます.
  • 1 flaskサービス関連.たとえば、サービスは、ユーザーのステータス、メールおよびメッセージの送信、ファイルパス、データベース接続(ORMオブジェクト)、およびいくつかの構成を提供します.
  • 2汎用関数パッケージ.例えばPandas,reなど.
  • 3には、関数パッケージがあります.たとえばDataManipulationでは、ファイルの読み取りのみを行うなど、浅い論理のパケットもありますが、エラーが発生した場合は簡単に発見できます.一部は深い論理階層を有しており、この部分は過去には通常面倒な
  • であった.
  • 4論理処理およびジャンプ.ビュー関数の内容の中には、比較的はっきりしているものもあれば、先端に埋め込まれたハイパーチェーンもあります.この部分は、従来は
  • を調整する必要がありました.
    設計/製造の原則
    1まず決定可能な基本コンポーネントを決定し,規則−図に従って組織する.2基本コンポーネントが信頼性が低いと仮定し、追跡、制御、冗長性を増加させる必要がある.3基本コンポーネントを最適化し(基本コンポーネント自体もより基本的なコンポーネントから構成される)、その客観的安定性が6つの9以上に達するまで.4冗長設計と制御方法により、システムの信頼性を十分に向上させる9.
    私たちはまず間違いの可能性のある角度から切り込みます(結局眉を焼くのはうるさいです).簡単に言えば、ログのような方法で記録しますが、簡単なログ(サーバほど多くのログが使用されているのはどれくらいですか?)ではありません.
    1.2 LogicLog(論理ログ)
    これは私が自分で書いたクラスで、いくつかの出発点、あるいは仮定点は以下の通りです.
  • メインプログラム(ll_mainstream)とサブプログラム(ll_funcstream).あるユーザがリンクをクリックしたなど、1つのメインフローといくつかのサブフローがあると仮定し、この作業を完了するために実行されるいくつかのビュー関数がメインプログラムである(views 1->view 2...).各ビュー関数には、サブルーチン(func 1->func 2)という処理プロセスがいくつかあります.
  • デバッグモードとバッチモード.バッチ・モードはデータを保持せず、関数のパスのみを記録します(失敗した場合はエラー).デバッグモードはinput,outputの具体的なデータを記録します.
  • グローバル番号(gid)とパス(gpath).グローバル番号はユーザのidとも考えられ、gpathはファイルを保存する具体的な経路であり、すべてpythonを使用するためpklで統一的に保存される.

  • 使用方法は大体以下の通りで、関数を呼び出す方法を少し調整すればいい(多少は私が偏関数式プログラミングの習慣である):
    some_log = LogicLog(xxx,xxx) 
    -> res1 = some_log.log(func1, args1, kwargs1) 
    -> ... 
    -> some_log.save()
    

    全体的な操作過程は網状(Mesh)であることを理解することができ,もちろん,(ループ図の有無,DAGの有無)に分解し,ある操作過程がチェーンである.LogicLogはこのチェーンで起こったことを記録し、全体の状況はチェーンのデータを図に統合し、アルゴリズムによって分析、制御する.
    以下はV 1バージョンですが、まず基礎的なものを作ります.クラスを初期化するときは、現在のgidが現在のフォルダの下にログがあるかどうかを確認します.
    class LogicLog():
        def __init__(self, gid, gpath='./', debug=False, keep_max=10, logname='ll_mainstream', logmode='justerror'):
            self.gid = str(gid).replace(' ', '').replace('_', '')
            self.logfile_name = logname + '_' + \
                str(gid).replace(' ', '').replace('_', '')
            self.logfile_path = amend_path_slash(gpath)
            #     LogicLog  
            log_dict = tryload_pkl(self.logfile_name, cur_path=self.logfile_path)
            if log_dict is None:
                self.log_dict = {
         }
                self.log_dict['loglist'] = []
                self.log_dict['logsummary'] = {
         }
                #     debug  (    )
                self.log_dict['last_debug_data_dict'] = None
                #       debug  
                self.log_dict['last_debug_wrong_data_dict'] = None
            else:
                self.log_dict = log_dict
            #   keep_max    
            self.log_dict['loglist'] = self.log_dict['loglist'][-keep_max:]
    
            #     -    
            self.step_dict = {
         }
            #     
            self.is_debug = debug
    

    その中のlog関数は実行した情報を記録する責任を負い、結果が規範に合わない場合、記録時に自動的に修正されます.所定の出力(入力)仕様は
    {
         'status':True/False,'msg', 'meta':{
         }, 'data':{
         } }
    

    以下はlog関数です.
        def log(self, f, *args, **kwargs):
            #      I       
            print('it works')
            print('function name', f.__name__)
            print('args', args)
            print('kwargs', kwargs)
            cur_timestamp = int(time.time())
            cur_gid = self.gid
    
            # II     
            try:
                res = f(*args, **kwargs)
            except:
                res = None
    
            cur_finished_timestamp = int(time.time())
            # III          
            #        {'status':True/False,'msg', 'meta'{}, 'data' }
            # new_res     json dumps     
            try:
                if res.get('status'):
                    is_formatted_result = True
                else:
                    is_formatted_result = False
            except:
                is_formatted_result = False
            #       status  ,          ,  ,      ,    (data)     json    ,            
            if not is_formatted_result:
                new_res = {
         }
                if res:
                    new_res['status'] = True
                    new_res['msg'] = 'ok'
                    new_res['meta'] = {
         }
                    # new_res['data'] = res
    
                else:
                    new_res['status'] = False
                    new_res['msg'] = 'error'
                    new_res['meta'] = {
         }
    
                    # new_res['data'] = res
            else:
                new_res = res
            #             (  /  )   
            if new_res.get('status'):
                if self.log_dict['logsummary'].get(f.__name__) is None:
                    self.log_dict['logsummary'][f.__name__] = {
         }
                    self.log_dict['logsummary'][f.__name__]['success'] = 1
                    self.log_dict['logsummary'][f.__name__]['error'] = 0
                else:
                    self.log_dict['logsummary'][f.__name__]['success'] += 1
            else:
                if self.log_dict['logsummary'].get(f.__name__) is None:
                    self.log_dict['logsummary'][f.__name__] = {
         }
                    self.log_dict['logsummary'][f.__name__]['success'] = 0
                    self.log_dict['logsummary'][f.__name__]['error'] = 1
                else:
                    self.log_dict['logsummary'][f.__name__]['error'] += 1
            #       
            new_res['meta']['ts'] = cur_timestamp
            new_res['meta']['tsdone'] = cur_finished_timestamp
            new_res['meta']['gid'] = cur_gid
            new_res['meta']['function'] = f.__name__
    
            #          step_dict
            #      
            if self.step_dict:
                new_id = int(get_current_max_code(
                    self.step_dict, key_or_num='num')) + 1
            else:
                new_id = 1
    
            #        ,            
            if self.is_debug:
                new_res['data'] = {
         }
                new_res['data']['input'] = {
         }
                new_res['data']['input']['args'] = pickle.dumps(args)
                new_res['data']['input']['kwargs'] = pickle.dumps(kwargs)
                new_res['data']['output'] = pickle.dumps(res)
            #         
            new_step = 'step' + str(new_id)
            self.step_dict[new_step] = new_res
            #  debug    
            self.log_dict['last_debug_data_dict'] = new_res
            if not new_res['status']:
                self.log_dict['last_debug_wrong_data_dict'] = new_res
            return res
    	#   get_last_wrong_data    
        def get_last_wrong_data(self):
            if self.log_dict['last_debug_wrong_data_dict'] is not None:
                res = copy.deepcopy(self.log_dict['last_debug_wrong_data_dict'])
                res['data']['input']['args'] = pickle.loads(res['data']['input']['args'] )
                res['data']['input']['kwargs'] = pickle.loads(res['data']['input']['kwargs'] )
                res['data']['output'] = pickle.loads(res['data']['output'] )
            else:
                dm.color_print('      ')
                res = None 
            return res
    

    ハローワールド(初めて実行)
    import DataManipulation as dm 
    import pandas as pd 
    
    gid= 3 
    
    ll = dm.LogicLog(gid)
    
    def hello(x):
        print(x)
        return True
    
    x1 = ll.log(hello, 'world')
    
    ll.save()
    --->
    In [3]:  x1 = ll.log(hello, 'world')                                             
    it works
    function name hello
    args ('world',)
    kwargs {
         }
    world
    
    In [4]: ll.save()                                                                      
    data save to pickle:  ./ll_mainstream_3.pkl
    
    In [5]: ll.step_dict                                                            
    Out[5]: 
    {
         'step1': {
         'status': True,
      'msg': 'ok',
      'meta': {
         'ts': 1599288576, 'gid': '3', 'function': 'hello'}}}
    

    もう一つhelloworld(2回目の実行)
    # ---    
    ll = dm.LogicLog(gid)
    
    def hello1(x):
        dm.color_print('hello1: %s' % str(x))
        return True
    
    x1 = ll.log(hello,'world1')
    x2 = ll.log(hello1,'world2')
    
    ll.save()
    ---   
    it works
    function name hello
    args ('world1',)
    kwargs {
         }
    world1
    it works
    function name hello1
    args ('world2',)
    kwargs {
         }
    hello1: world2
    data save to pickle:  ./ll_mainstream_3.pkl
    
    #      ll -          
    ll.log_dict['logsummary']
    
    In [11]: ll.log_dict['logsummary']                                              
    Out[11]: {
         'hello': {
         'success': 2, 'error': 0}, 'hello1': {
         'success': 1, 'error': 0}}
    #      ll -        
    ll.log_dict['loglist']
    
    In [12]: ll.log_dict['loglist'] 
        ...:                                                                        
    Out[12]: 
    [{
         'step1': {
         'status': True,
       'msg': 'ok',
       'meta': {
         'ts': 1599288576, 'gid': '3', 'function': 'hello'}}},
     {
         'step1': {
         'status': True,
       'msg': 'ok',
       'meta': {
         'ts': 1599291067, 'gid': '3', 'function': 'hello'}},
      'step2': {
         'status': True,
       'msg': 'ok',
       'meta': {
         'ts': 1599291067, 'gid': '3', 'function': 'hello1'}}}]
    #           
    In [13]: len(ll.log_dict['loglist'])                                                                       
    Out[13]: 2
    
    #        hello
    In [14]: ll.log_dict['loglist'][0] 
        ...:                                                                        
    Out[14]: 
    {
         'step1': {
         'status': True,
      'msg': 'ok',
      'meta': {
         'ts': 1599288576, 'gid': '3', 'function': 'hello'}}}
    #         hello,hello1
    In [15]: ll.log_dict['loglist'][1] 
        ...:                                                                        
    Out[15]: 
    {
         'step1': {
         'status': True,
      'msg': 'ok',
      'meta': {
         'ts': 1599291067, 'gid': '3', 'function': 'hello'}},
     'step2': {
         'status': True,
      'msg': 'ok',
      'meta': {
         'ts': 1599291067, 'gid': '3', 'function': 'hello1'}}}
    

    次にもう一度やりますが、今回はdebugモードに入り、わざとパラメータを間違えました.
    ll = LogicLog(gid, debug=True)
    x1 = ll.log(hello, x1='i am wrong')
    x2 = ll.log(hello1,('world3'))
    
    ll.save()
    
    ---         hello     (status:false)
    In [32]: ll.log_dict['loglist']                                                 
    Out[32]: 
    [{
         'step1': {
         'status': True,
       'msg': 'ok',
       'meta': {
         'ts': 1599293551,
        'tsdone': 1599293551,
        'gid': '3',
        'function': 'hello'}}},
     {
         'step1': {
         'status': True,
       'msg': 'ok',
       'meta': {
         'ts': 1599293564,
        'tsdone': 1599293564,
        'gid': '3',
        'function': 'hello'}},
      'step2': {
         'status': True,
       'msg': 'ok',
       'meta': {
         'ts': 1599293564,
        'tsdone': 1599293564,
        'gid': '3',
        'function': 'hello1'}}},
     {
         'step1': {
         'status': False,
       'msg': 'error',
       'meta': {
         'ts': 1599293594,
        'tsdone': 1599293594,
        'gid': '3',
        'function': 'hello'},
       'data': {
         'input': {
         'args': b'\x80\x03).',
         'kwargs': b'\x80\x03}q\x00X\x02\x00\x00\x00x1q\x01X
    \x00\x00\x00i am wrongq\x02s.'
    }, 'output': b'\x80\x03N.'}}, 'step2': { 'status': True, 'msg': 'ok', 'meta': { 'ts': 1599293594, 'tsdone': 1599293594, 'gid': '3', 'function': 'hello1'}, 'data': { 'input': { 'args': b'\x80\x03X\x06\x00\x00\x00world3q\x00\x85q\x01.', 'kwargs': b'\x80\x03}q\x00.'}, 'output': b'\x80\x03\x88.'}}}] # ---- In [33]: len(ll.log_dict['loglist']) Out[33]: 3 # --- In [34]: ll.last_debug_wrong_data_dict Out[34]: { 'status': False, 'msg': 'error', 'meta': { 'ts': 1599293594, 'tsdone': 1599293594, 'gid': '3', 'function': 'hello'}, 'data': { 'input': { 'args': b'\x80\x03).', 'kwargs': b'\x80\x03}q\x00X\x02\x00\x00\x00x1q\x01X
    \x00\x00\x00i am wrongq\x02s.'
    }, 'output': b'\x80\x03N.'}} # --- In [43]: ll = LogicLog(gid, debug=True) In [44]: ll.get_last_wrong_data() Out[44]: { 'status': False, 'msg': 'error', 'meta': { 'ts': 1599295468, 'tsdone': 1599295468, 'gid': '3', 'function': 'hello'}, 'data': { 'input': { 'args': (), 'kwargs': { 'x1': 'i am wrong'}}, 'output': None}}

    2チェーン関数(関数辞書)
    LogicLogは有用な情報を多く保存しているが,依存を考慮せず,これまでの関数辞書の内容を引き返し,整理し直すことが分かる.
    簡単に言えば、
  • 1関数は、関数辞書(Funcdict)によって管理されます.各項目(LogicLogのようにgidによって一意に指定されている)または大きな汎用関数辞書を作成することもできます.この辞書は主にキー値によって操作する関数を関連付けています.
  • 2辞書(Inputdict)とパラメータ辞書(Paramdict)を入力します.入力辞書は、ユーザが入力した部分(例えば、ピクチャマトリクスを入力する)であり、パラメータ辞書は、アルゴリズム制御に関連する部分(例えば、学習率)である.パラメータ辞書はディスクを先に保存することもでき、ユーザーの入力辞書は先に保存されず、将来メタデータが保存されます.たとえば、ユーザが入力した100列のデータフレームは、その内容ではなく記録されます.
  • 3ユーザは、使用時に依存を知っている.辞書リストで操作を接続すると、デフォルトの仮定はallです(つまり、各ステップの実行には前のステップが必要です).ここはLogicLogと合わせると完璧です.
  • 4ブランチ指定.さらに,ユーザはmsgによってどのように分岐するかを指定することができ,ループ実行時に毎回次にどこに行くかを判断する.例えば、True、msg 1は関数1を指し、True、msg 2は関数2を指す.これは,ユーザが関数出力時に前提とした仕様に従ってstatus,msg,meta,dataを含む辞書を出力する必要がある.

  • これは別の編に置いてある.