如何设计具有数据查找(>15K req/sec)的极快python HTTP API?
我需要构建一个RESTAPI/服务器,它在80毫秒内响应超过每秒15000个HTTP GET请求。如果需要,我可以使用负载平衡器运行多个实例 服务器收到一个包含大约20个条件列表的请求,需要对它们进行解析,并将其与规则集(大约2000个规则)进行比较,这些规则对所有20个条件都有不同的值,最后的决定决定响应是或否 请求有效负载示例:如何设计具有数据查找(>15K req/sec)的极快python HTTP API?,python,heroku,sqlite,webserver,sanic,Python,Heroku,Sqlite,Webserver,Sanic,我需要构建一个RESTAPI/服务器,它在80毫秒内响应超过每秒15000个HTTP GET请求。如果需要,我可以使用负载平衡器运行多个实例 服务器收到一个包含大约20个条件列表的请求,需要对它们进行解析,并将其与规则集(大约2000个规则)进行比较,这些规则对所有20个条件都有不同的值,最后的决定决定响应是或否 请求有效负载示例: {"Country" : "DE", "ID" : "998423-423432-4234234-234234", "Criteria1": "8748r78"
{"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代码中的GEOs。有些可能为空,并被视为通配符。从理论上讲,可能存在所有20个标准都具有相同值的条目,但这是有待编写的规则引擎要解决的问题
我研究了如何实现这一点:
根据我的观点,使用sanic作为Web服务器可以获得高吞吐量
研究这是除japronto之外python最快的,japronto是
阿尔法;编辑:对于类似的用例,是否有人对基于python的webserver+webframework的性能有经验?我只阅读通常有一个非常简单的测试用例的基准测试,它只对一个请求响应一个固定的字符串,因此在所有基准测试中每秒可能有大量的请求
在内存中使用sqlite3进行规则查找;不确定具有20个约束的SQL语句是否足够快?也许还有另一个
将每个请求与规则集进行比较的方法,每个规则集超过20个标准
一个是字符串比较。编辑:多亏了一个注释,我可能会将规则预计算成散列并使用散列进行查找,因此不需要实时查找的数据库。
使用redis或其他数据库存储预先计算的规则
是另一个主题,让他们准备好在每个
http服务器和sqlite3数据库的实例/工作者。
也许可以使用Py3进行额外的加速,但我没有经验
和派比
我会在Heroku主持这个节目
所以问题是:哪些库和体系结构允许python具有这种速度?我假设
所有给定的条件都是精确的字符串匹配
所有未指定的条件都匹配任何通配符
我们可以抛弃所有产生错误的规则
规则可能不包含任何与通配符匹配的内容
如果至少有一条规则与所有给定条件匹配,则结果为True,否则为False
我们可以快速查找集合匹配规则ID的dict值的dict列:
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)
对于给定的示例数据,规则查找现在包含
{
'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——应该足够快了- 你能给我们一个请求示例和规则示例吗?您如何确定应用哪个规则-最接近给定标准,即笛卡尔距离?规则集有多密集,即请求与其最接近的匹配规则之间的预期最大距离是多少?规则集多久更改一次?2000条规则很小。我会使用一些内存哈希表。。。。还要注意的是,Sanic在PyPi上被列为pre-alpha-我还不确定我是否希望在生产中信任它。@HughBothwell添加了示例—还没有代码,只是数据结构的原始开始。我最关心的是整个往返行程的80毫秒截止时间。@CL。非常好的主意,我可以散列所有规则,然后散列请求并获得极快的查找。。