这是一个必须将并行性引入后端服务器的情况。
我愿意查询N ELB,每个查询5个不同的查询,并将结果发送回网络客户端。
后端是Tornado,根据我在docs多次阅读的内容,过去,如果我使用@ gen.Task或gen,我应该可以并行处理多个任务。协程。
但是,我必须在这里遗漏一些东西,因为我的所有请求(20个,4个elbs * 5个查询)都是一个接一个地处理。
def query_elb(fn, region, elb_name, period, callback):
callback(fn (region, elb_name, period))
class DashboardELBHandler(RequestHandler):
@tornado.gen.coroutine
def get_elb_info(self, region, elb_name, period):
elbReq = yield gen.Task(query_elb, ELBSumRequest, region, elb_name, period)
elb2XX = yield gen.Task(query_elb, ELBBackend2XX, region, elb_name, period)
elb3XX = yield gen.Task(query_elb, ELBBackend3XX, region, elb_name, period)
elb4XX = yield gen.Task(query_elb, ELBBackend4XX, region, elb_name, period)
elb5XX = yield gen.Task(query_elb, ELBBackend5XX, region, elb_name, period)
raise tornado.gen.Return(
[
elbReq,
elb2XX,
elb3XX,
elb4XX,
elb5XX,
]
)
@tornado.web.authenticated
@tornado.web.asynchronous
@tornado.gen.coroutine
def post(self):
ret = []
period = self.get_argument("period", "5m")
cloud_deployment = db.foo.bar.baz()
for region, deployment in cloud_deployment.iteritems():
elb_name = deployment["elb"][0]
res = yield self.get_elb_info(region, elb_name, period)
ret.append(res)
self.push_json(ret)
def ELBQuery(region, elb_name, range_name, metric, statistic, unit):
dimensions = { u"LoadBalancerName": [elb_name] }
(start_stop , period) = calc_range(range_name)
cw = boto.ec2.cloudwatch.connect_to_region(region)
data_points = cw.get_metric_statistics( period, start, stop,
metric, "AWS/ELB", statistic, dimensions, unit)
return data_points
ELBSumRequest = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "RequestCount", "Sum", "Count")
ELBLatency = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "Latency", "Average", "Seconds")
ELBBackend2XX = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "HTTPCode_Backend_2XX", "Sum", "Count")
ELBBackend3XX = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "HTTPCode_Backend_3XX", "Sum", "Count")
ELBBackend4XX = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "HTTPCode_Backend_4XX", "Sum", "Count")
ELBBackend5XX = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "HTTPCode_Backend_5XX", "Sum", "Count")
答案 0 :(得分:3)
问题是ELBQuery
是阻塞函数。如果某个地方没有yield
另一个协程,则协同调度程序无法交错调用。 (这是协同程序的全部意义 - 它们是合作的,而不是先发制人的。)
如果问题类似于calc_range
调用,则可能很容易处理 - 将其分解为较小的片段,其中每个片段都会产生下一个片段,这使得调度器有机会进入每一件。
但最有可能的是,这是阻止的boto调用,并且你的大部分函数都花在等待get_metric_statistics
返回,而没有别的东西可以运行。
那么,你如何解决这个问题?
asyncboto
项目。greenlets
和monkeypatch足够的库依赖项来欺骗它成为异步。这听起来很糟糕,但它实际上可能是最好的解决方案;请参阅Marrying Boto to Tornado。greenlets
和monkeypatch整个stdlib ala gevent
来欺骗boto和龙卷风一起工作,甚至没有意识到它。这听起来像一个可怕的想法;您最好将整个应用移植到gevent
。gevent
。在不知道更多细节的情况下,我建议先看看#2和#4,但我不能保证它们会成为你的最佳答案。