2017-08-09 48 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 - 应该是足够快足够;-)