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で統一的に保存される.
使用方法は大体以下の通りで、関数を呼び出す方法を少し調整すればいい(多少は私が偏関数式プログラミングの習慣である):
全体的な操作過程は網状(Mesh)であることを理解することができ,もちろん,(ループ図の有無,DAGの有無)に分解し,ある操作過程がチェーンである.LogicLogはこのチェーンで起こったことを記録し、全体の状況はチェーンのデータを図に統合し、アルゴリズムによって分析、制御する.
以下はV 1バージョンですが、まず基礎的なものを作ります.クラスを初期化するときは、現在のgidが現在のフォルダの下にログがあるかどうかを確認します.
その中のlog関数は実行した情報を記録する責任を負い、結果が規範に合わない場合、記録時に自動的に修正されます.所定の出力(入力)仕様は
以下はlog関数です.
ハローワールド(初めて実行)
もう一つhelloworld(2回目の実行)
次にもう一度やりますが、今回はdebugモードに入り、わざとパラメータを間違えました.
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を含む辞書を出力する必要がある.
これは別の編に置いてある.
何事も間違いを犯す可能性がある限り、必ず間違いを犯す.
マーフィーの法則に関するいくつかの説は参考になる.マスクのSpaceX(数十人の従業員しかいない)はもう一つの良い逆例の参考であり、少なくとも以下の点を説明しています.
既存の有人宇宙船に搭載されている星搭載コンピュータとコントローラの例では、単一コントローラの価格は500万人民元前後、単一コントローラの価格は500万人民元前後、計14システムで、高信頼性を追求するために、システム1+1バックアップごとに、計28コントローラで、コストは計約1.4億人民元である.スペースXの竜飛船主制御システムのチップセットは2.6万元しか使わず、コストの差は5348倍だった.
ヒント:将来は少ない人、低いコストで、トップクラスのことを完成するのに十分です.ある程度はThe Fewer、The Better!
地上に戻って,本論文では,中程度の複雑なシステムの監視とdebug過程を先に解決するために,高信頼性の決定(制御)方法を主キーで構築する方法について議論する.
仮想シーン:
チェーンは大体こうです.
ユーザFlask[pikaメッセージ消費]
1コンテンツ
1.1 Flaskのロジック
完全なサービスを考慮すると、Flask(青写真モード)は少なくともいくつかの部分を備えなければならない.
設計/製造の原則
1まず決定可能な基本コンポーネントを決定し,規則−図に従って組織する.2基本コンポーネントが信頼性が低いと仮定し、追跡、制御、冗長性を増加させる必要がある.3基本コンポーネントを最適化し(基本コンポーネント自体もより基本的なコンポーネントから構成される)、その客観的安定性が6つの9以上に達するまで.4冗長設計と制御方法により、システムの信頼性を十分に向上させる9.
私たちはまず間違いの可能性のある角度から切り込みます(結局眉を焼くのはうるさいです).簡単に言えば、ログのような方法で記録しますが、簡単なログ(サーバほど多くのログが使用されているのはどれくらいですか?)ではありません.
1.2 LogicLog(論理ログ)
これは私が自分で書いたクラスで、いくつかの出発点、あるいは仮定点は以下の通りです.
使用方法は大体以下の通りで、関数を呼び出す方法を少し調整すればいい(多少は私が偏関数式プログラミングの習慣である):
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は有用な情報を多く保存しているが,依存を考慮せず,これまでの関数辞書の内容を引き返し,整理し直すことが分かる.
簡単に言えば、
これは別の編に置いてある.