1

我在 CherryPy 服务器中构建了一个奇怪的情况,我想要一些帮助来了解到底发生了什么,以及为什么。这里是server.py

import cherrypy
import os
import threading

class Root(object):
    def __init__(self):
        self.count = 0;
        self.lock = threading.Lock()

    @cherrypy.expose
    def app(self):
        with self.lock:
            self.count += 1
        return "Call #%d" % (self.count)

if __name__ == '__main__':
    cherrypy.quickstart(Root(), "/", {"/": { "tools.staticdir.on": True,
                                             "tools.staticdir.dir": os.getcwd() } })

这是一个非常简单的应用程序,其中某些状态受互斥体保护。这里是index.html

<!doctype html>
<script src=http://code.jquery.com/jquery-1.8.2.js></script>
<script src=index.js></script>

(HTML 可以这么稀疏 - 请参阅Paul Irish 的演讲)这里是index.js

function request() {
    var cb = function (response) {
        console.log(response);
    };

    $.ajax({
        url: "/app",
        success: cb
    });
}

function go_slow(N, delay) {
    if (N > 0) {
        request();
        window.setTimeout(go_slow, delay, N - 1, delay);
    }
}

function go_fast(N) {
    var i;
    for (i = 0; i < N; i++) {
        request();
    }
}

window.onload = function () {
    //go_slow(100, 0);
    go_fast(100);
};

所有这些文件都应该放在同一个目录中。当我启动 CherryPy 服务器然后访问http://localhost:8080/index.html时,控制台显示短语“Call #1”100 次,CherryPy 日志显示一个对“/app”的 GET 请求。在 Firefox 中,我看到几个连续的控制台消息,显示“#1”到“#100”。Firefox 的行为是我所期望的,因为我发出了 100 个显式 ajax 请求,并且与这些请求关联的函数每次都应该返回不同的结果。

如果不是go_fast()我调用go_slow()- 使用“trampolining”(我认为它被调用)来强制在不同时间从单独的函数调用发出 ajax 请求的变体 - 那么我通过“#100”行为得到“#1” Chrome 和 Firefox。

最后,如果我修改app()CherryPy 服务器中的方法以接受它然后忽略的参数,就像这样(注意i没有出现在其他任何地方的参数):

@cherrypy.expose
def app(self, i):
    with self.lock:
        self.count += 1
    return "Call #%d" % (self.count)

我安排go_fast()函数向"/app/" + i"i循环索引在哪里)发出ajax请求,而不是"/app"通过改变request()go_fast()这样的:

function request(i) {
    var cb = function (response) {
        console.log(response);
    };

    $.ajax({
        url: "/app/" + i,
        success: cb
    });
}

function go_fast(N) {
    var i;
    for (i = 0; i < N; i++) {
        request(i);
    }
}

然后我再次通过“#100”行为获得“#1”。相反,如果我修改要使用的函数,例如"/app/7"用作 URL(即,"/app/" + i在上面的代码段中替换为"/app/7"),我会得到 100 次重复的“Call #1”,如初始示例中所示。

对我来说,它看起来很像某些东西“缓存”了在初始情况下实际发出的唯一一个 ajax 请求的结果(我只看到了“Call #1”100 次)。Chrome 控制台中的“网络”选项卡仅显示接收状态代码“200 OK”的一个 ajax 请求,据我所知,您必须在 CherryPy 中显式启用服务器端缓存。Firefox 在这种情况下给出了预期的行为也令人好奇。

在其他情况下,由于某种原因,这种“缓存”机制被打败了。Trampolining 可能会导致浏览器丢弃 ajax 请求的直接上下文,从而阻止它“记住”先前对同一 URL 的请求,同时设计 URL 要求位置参数只是为每个请求提供一个唯一的 URL,强制发出实际请求,而不是使用任何缓存机制。

谁能具体解释这里发生了什么?这仅仅是关于 Chrome 如何在短时间内处理许多 ajax 请求的一个特点吗?或者这种情况是拥有有状态 ajax 资源的致命问题吗?

4

1 回答 1

0

我认为这是与cache: false客户端选项相结合的竞争条件,请记住您的对象Root在线程池中共享。

准时A您修改self.count并且准时A另一个 X 请求(线程)正在返回您新修改的参数,它们准时A - 1with块上并且没有返回该特定请求的新计数。

当正在修改并退出 with 语句的请求时,另一个已经在返回中,因此确定返回数字的重复或缺失的是cherrypy线程池的处理吞吐量。

于 2013-12-03T19:50:34.867 回答