2017-08-09 56 views
0

我需要建立它響應比HTTP GET多在80毫秒下每秒15000個請求一個REST API /服務器。如果有必要,我可以使用負載平衡器運行多個實例。如何使用數據查找(> 15K req/sec)設計極其快速的python HTTP API?

服務器收到一個包含標準列表(大約20)的請求,需要對它們進行分析並與規則集(大約2000條規則,這些規則對於所有20條標準和最終決定具有不同值)進行比較,決定響應(是或否)。

樣品請求負載:

{"Country" : "DE", 
"ID" : "998423-423432-4234234-234234", 
"Criteria1": "8748r78", 
"Criteria2": "Some String", 
    [...] 
} 

樣的規則集(仍有待決定,但讓我們用一個簡單的設計開始):

+--------+---------+-------------+--------------+ 
| RuleId | Country | Criteria1 | Criteria2 | etc... 
+--------+---------+-------------+--------------+ 
|  1 | UK  | SomeString1 | SomeString3 | 
|  2 | UK  | SomeString1 | SomeString2 | 
|  3 | US  | SomeString4 | * (Wildcard) | 
+--------+---------+-------------+--------------+ 

每個標準可在1到大概在400個不同之間含有值,所有字符串(例如ISO代碼中的GEO)。有些可能是空的,並被視爲通配符。從理論上講,所有20個標準的條目可能具有相同的值,但這是尚未編寫的規則引擎需要解決的主題。

我做了一些研究如何實現這一點:

  1. 爲高吞吐量網絡服務器使用中信高科,根據我的 這不包括japronto這是阿爾法 最快的蟒蛇研究;編輯:有沒有人有關於類似用例的基於python的web服務器+ webframework的性能的經驗?我只讀其通常具有非常簡單的測試用例基準(只是響應一個固定的字符串的請求,因此高數每秒可能請求中的所有基準)
  2. 使用對規則查找sql​​ite3的(在存儲器中);不確定具有20個約束的SQL語句是否足夠快?也許有另一個來比較每個請求規則集20個標準(每 一個是字符串比較) 方式。編輯:感謝一位評論者,我可能會預先計算規則到哈希中,並使用哈希進行查找,因此不需要用於實時查找的數據庫。
  3. 使用redis或其他數據庫來存儲預先計算好的規則(即 是另一個主題),並使其準備好加載到http服務器的每個 實例/ worker中,從而加載sqlite3數據庫。
  4. 也許使用pypy3額外的加速,但我沒有經驗 與pypy

我將主持這在Heroku。

所以,問題是:哪些庫,從而架構將允許那種與Python的速度?

+0

你能舉個例子請求和示例規則?你如何確定應用哪個規則 - 與給定標準最接近的匹配,即笛卡爾距離?規則集是多麼「密集」,即請求與最接近的匹配規則之間的預期最大距離是多少?規則集多久更新一次? –

+1

2000規則是* tiny *。我會使用一些內存中的哈希表。 –

+0

...還注意到Sanic在PyPi上被列爲「pre-alpha」 - 我不確定我是否想在生產中信任它。 –

回答

1

我會認爲

  1. 所有給定的標準是準確的字符串匹配
  2. 所有未指定的標準匹配任何(通配符)
  3. 我們可以拋棄它產生假
  4. 規則可能包含無所有規則匹配任何東西(通配符)
  5. 如果至少有一條規則與所有給定條件匹配,則結果爲真,否則爲False

我們可以建立一個快速查找的一套字典(值)的字典(列)(匹配規則ID):

from collections import namedtuple 

WILDCARD = None 

Rule = namedtuple("Rule", ["Country", "Criteria1", "Criteria2"]) 

rules = [ 
    Rule("UK", "Somestring1", "Somestring3"), 
    Rule("UK", "Somestring1", "Somestring2"), 
    Rule("US", "Somestring4", WILDCARD) 
] 

def build_lookup(rules): 
    columns = Rule._fields 
    # create lookup table (special handling of wildcard entries) 
    lookup = {column: {WILDCARD: set()} for column in columns} 
    # index rules by criteria 
    for id, rule in enumerate(rules): 
     for column, value in zip(columns, rule): 
      if value in lookup[column]: 
       lookup[column][value].add(id) 
      else: 
       lookup[column][value] = {id} 
    return lookup 

rule_lookup = build_lookup(rules) 

在給定的樣本數據,rule_lookup現在包含

{ 
    'Country': {WILDCARD: set(), 'UK': {0, 1}, 'US': {2}}, 
    'Criteria1': {WILDCARD: set(), 'Somestring1': {0, 1}, 'Somestring4': {2}}, 
    'Criteria2': {WILDCARD: {2}, 'Somestring2': {1}, 'Somestring3': {0}} 
} 

那麼我們就可以快速的匹配規則的規則像

def all_matching_rules(criteria): 
    """ 
    criteria is a dict of {column: value} to match 

    Return a set of all rule ids which match criteria 
    """ 
    if criteria: 
     result = empty = set() 
     first = True 
     for column, value in criteria.items(): 
      ids = rule_lookup[column].get(value, empty) | rule_lookup[column][WILDCARD] 
      if first: 
       result = ids 
       first = False 
      else: 
       result &= ids # find intersection of sets 
      # short-circuit evaluation if result is null set 
      if not result: 
       break 
     return result 
    else: 
     # no criteria, return everything 
     return set(range(len(rules))) 

def any_rule_matches(criteria): 
    """ 
    criteria is a dict of {column: value} to match 

    Return True if any rule matches criteria, else False 
    """ 
    if criteria: 
     return bool(all_matching_rules(criteria)) 
    else: 
     return bool(len(rules)) 

它運行像

>>> all_matching_rules({"Country": "UK", "Criteria2": "Somestring8"}) 
set() 

>>> all_matching_rules({"Country": "US", "Criteria2": "Somestring8"}) 
{2} 

>>> any_rule_matches({"Country": "UK", "Criteria2": "Somestring8"}) 
False 

>>> any_rule_matches({"Country": "US", "Criteria2": "Somestring8"}) 
True 

Timeit報道稱,此次運行在我的機器上930ns - 應該是足夠快足夠;-)