基於Webpy實現服務器策略模型

如今咱們來談一些(黑)科技,但願能給你們一些啓發和幫助。如今我有一個策略文件addition.policypython

load!: addition_delegate.py

await: first_string -> s1
apply: concat_with_time(s1) -> s1

await: second_string -> s2
apply: concat_with_time(s2) -> s2

await: return_result
apply: join_with_linefeed(s1, s2) -> result
yield: result

還有一個委託函數的Python源碼addition_delegate.pygit

# addition_delegate.py

def concat_with_time(s):
    import time

    return str(s) + time.ctime()

def join_with_linefeed(s1, s2):
    return "%s\n%s\n" % (str(s1), str(s2))

這是什麼語法?可是咱們大抵都能明白它想幹什麼:前後獲取兩個字符串,分別將它們和時間拼接在一塊兒,而後在獲取return_result後回傳結果。而後呢?而後咱們有一些Web接口,簡單地用web.py編寫main.pygithub

#!/usr/bin/env python

import web
from policy import resume

class first_str_view:
    def GET(self):
        resume("addition.policy", "first_str",
            value = web.input()["value"], anew = True)
        return ""

class second_str_view:
    def GET(self):
        resume("addition.policy", "second_str",
            value = web.input()["value"])
        return ""

class return_result_view:
    def GET(self):
        return resume("addition.policy", "return_result")

urls = [
    '/first_str/?', first_str_view,
    '/second_str/?', second_str_view,
    '/return_result/?', return_result_view,
]

if __name__ == "__main__":
    app = web.application(urls, globals())
    app.run()

就算沒用過web.py的人都大抵能明白這個結構是什麼意思了,除了那個resume有點不知所謂以外,可是結合上面的那個addition.policy,好像看上去也挺合理,大概就是從acquire處斷開,而後獲得輸入後繼續執行那個policy。如你所料:web

$ ./main.py 9999 &
[1] 19121
http://0.0.0.0:9999/
$ curl "http://localhost:9999/first_str?value=First+Record+"
$ curl "http://localhost:9999/second_str?value=Second+Record+"
$ curl "http://localhost:9999/return_result"
First Record Sat Sep  5 15:59:25 2015
Second Record Sat Sep  5 15:59:28 2015

這樣能夠解決不少問題。好比在用戶更變郵箱的時候,用戶先提交新郵箱,而後還要等等他何時去郵箱裏收驗證郵件,這樣更變郵箱的操做才完成。還有一些更麻煩的操做,整個流程下來,要收幾回輸入,而後才能真正地輸入成功存進數據庫。舉個例子,你能夠簡單地寫一個策略文件,讓它控制整個流程,接口只須要跟用戶打交道就行了:數據庫

load!: email_service
assert!: is_authenticated

await: modify_email -> address
apply: send_verf_email(address)

await: verf_email_recv
apply: save_current_user_info(address)

不得不說這種模型有點像是協程(coroutine),可是不是用它來實現的,畢竟:一次請求完成了整個線程大大小小都結束了哪裏還有協程啊對吧。這也不是WebSocket能解決的:好比收驗證郵件,都在第二個地方鏈接了,哪裏還有Socket可言。這裏針對的狀況是,兩次鏈接之間的時間段是斷開的狀況。(若是非要用設計模式來講,我以爲是一個策略+狀態+解釋器的組合,然而我並不喜歡被設計模式拘束)設計模式

實現思路

主要是在模擬恢復執行的時候能較好地恢復原有上下文,在Python有exec的狀況下,想辦法生成可配合執行Python代碼是一個不錯的選擇。恢復執行有這些步驟:app

  • 解析策略文件curl

  • 從持久存儲設備中反序列化上下文ide

  • 找到斷點應該在哪裏,按照這個位置,執行一些每次都要執行的語句(標 !號)函數

  • 一直執行到下一個await點,退出執行

先看一下resume()函數的一個實現是什麼樣子的:

from policy import policy

import pickle
import os

def resume(pf, await_tag, value = None, anew = False):
    c = context.start_new() if anew else \
        pickle.load(file("context.dump", "rb"))
    p = policy.load(pf)

    p.load_context(c)
    p.provide(await_tag, value)

    ret = None
    
    try:
        ret = p.resume()
    finally:
        os.remove("context.dump")

    if p.is_end():
        os.remove("context.dump")

    return ret

這裏的contextpolicy是我對這個模型的一個實現,能夠看出這二者是分開保存的,Policy幾乎就是一個常量了,硬編碼在一個文件裏。而Context在每一次退出執行的時候都要保存一下,除非已經執行結束了,或者執行出現了錯誤(也至關於執行結束),才把它削除。

Policy-Control已經推上了Github,代碼很短,歡迎各位圍觀:https://github.com/Shihira/policy-control

附:語法清單

digit  := '0' | ... | '9'
underscore := '_'

symbol ::=
    letter | underscore
    { letter | underscore | digit }

command ::= symbol
variable ::= symbol

string ::=
    '"' {
    [ 0x00 | ... | 0x21 | 0x23 | ... | 0x7f | '\n' | '\r' | '\"' | '\\' ]
    } '"'

value ::= string | variable

parameter ::= value
parameter-list ::=
    '('
    [ parameter-list ',' parameter | parameter ]
    ')'

argument ::= symbol [ parameter-list ]
argument-list ::= argument-list argument | argument

command-line ::=
    command [ '!' ] ':'
    argument-list
    [ '->' variable ]

policy ::=
    policy \n command-line | command-line
相關文章
相關標籤/搜索