【问题标题】:Long Polling, "Global" Button, Broadcast to everyone?长轮询,“全球”按钮,向所有人广播?
【发布时间】:2013-11-17 02:19:57
【问题描述】:

我正在尝试实现一个全局按钮计数器,该计数器会在任何/不同用户单击它时更新。 所以这个想法是,如果一个人点击按钮,我会在我的页面实例上看到计数器更新。

我目前使用长轮询技术,或者我认为是这样,但经过审查,我认为我在将更新“广播”到所有浏览器时出错。

当前的错误是,例如,如果我打开了两个浏览器,并且我不断单击一个浏览器,则我单击按钮的那个浏览器只会更新一半时间。它将得到 1 3 5 等,而其他浏览器显示 2 4 6 等。

在线查看后,我认为这可能与频道和网站上所有浏览器的广播有关。如果有人可以帮助我举例说明我如何将更新发送到所有浏览器,每一次,我都会非常感激。

客户:

<html>
<script language=javascript>

function longpoll(url, callback) {

    var req = new XMLHttpRequest (); 
    req.open ('GET', url, true); 

    req.onreadystatechange = function (aEvt) {
        if (req.readyState == 4) { 
            if (req.status == 200) {
                callback(req.responseText);
                longpoll(url, callback);
            } else {
                alert ("long-poll connection lost");
            }
        }
    };

    req.send(null);
}

function recv(msg) {

    var box = document.getElementById("counter");

    box.innerHTML += msg + "\n";
}
function send() {


    var box = document.getElementById("counter");

  var req = new XMLHttpRequest (); 
    req.open ('POST', "/push?rcpt=", true); 

    req.onreadystatechange = function (aEvt) {
        if (req.readyState == 4) { 
            if (req.status == 200) {
            } else {
                alert ("failed to send!");
            }
        }
  };
  req.send("hi")

  //box.innerHTML += "test" ;  
}
</script>
<body onload="longpoll('/poll', recv);">

<h1> Long-Poll Chat Demo </h1>

<p id="counter"></p>
<button onclick="send()" id="test">Test Button</button>
</body>
</html>

服务器:

package main

import (
    "net/http"
    "log"
    "io"
//  "io/ioutil"
  "strconv"
)

var messages chan string = make(chan string, 100)

var counter = 0

func PushHandler(w http.ResponseWriter, req *http.Request) {

    //body, err := ioutil.ReadAll(req.Body)

    /*if err != nil {
        w.WriteHeader(400)
    }*/
    counter += 1
    messages <- strconv.Itoa(counter)
}


func PollResponse(w http.ResponseWriter, req *http.Request) {

    io.WriteString(w, <-messages)
}

func main() {
    http.Handle("/", http.FileServer(http.Dir("./")))
    http.HandleFunc("/poll", PollResponse)
    http.HandleFunc("/push", PushHandler)
    err := http.ListenAndServe(":8010", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

【问题讨论】:

  • 您能添加一些调试输出并查看推送请求何时通过吗?
  • @Brenden 如果您就您想要的东西给我更多的指导,我很乐意效劳。推送请求通过客户端的send() 函数进行,只要用户按下按钮就会激活该函数
  • 我会添加一些fmt.Println() 电话以确认这些电话何时通过,而不是民意调查响应何时通过。我会在一个小时左右运行本地测试,看看我能得到什么结果(我现在在工作,无法测试你的代码)
  • @Brenden 据我所知,他们正在按预期进行,一个接一个。但是非常感谢你帮助我!对此,我真的非常感激!期待您的回音!
  • 我让它在本地运行,它按预期工作。编号是连续的。我在 Linux/Chromium 上进行测试,你在测试什么?您可以在没有 Javascript 的情况下手动请求轮询和推送 URL 吗?它的工作方式不同吗?

标签: javascript go global-variables long-polling


【解决方案1】:

问题不在于 Go 代码(单独;参见 PS PS),而在于浏览器(Chrome)。向同一个 URL 发出 2 个请求是按顺序发生的,而不是并行发生的。

解决方案 你需要给 longpoll URL 添加一个唯一的时间戳来欺骗浏览器:

req.open ('GET', url+"?"+(new Date().getTime()), true); 

PS - 通过这个问题,我学到了很多关于 Go 通道和互斥锁的知识。谢谢:)

PS PS - James 的回答 (https://stackoverflow.com/a/19803051/143225) 是让服务器端 Go 代码一次处理多个请求的关键,因为 Go 通道被阻塞,这意味着一次只能接收 1 个 goroutine。所以OP的问题的解决方案是前端和后端代码更改的组合。

【讨论】:

  • 无法解释我多么感谢您在聊天中与我一起工作了几个小时,以获得这个答案!大家投票!
  • 问题中的 Go 代码也被破坏了,对吧?仅凭这一点并不能解决问题。
  • @JamesHenstridge 我想知道,但我已经切换到您建议的 Mutex 代码。我想切换回初始代码来验证这一点,但我相信你的答案很关键,因为通道不能被超过 1 个 goroutine 读取。
  • @JamesHenstridge 我已经更新了我的答案,以感谢您对 Go 代码的更改。谢谢。
【解决方案2】:

Go 频道不是多播的。也就是说,如果你有多个从通道读取的 goroutine,当你向通道写入值而不是广播给所有读者时,只有一个会唤醒。

另一种方法是使用条件变量:

var (
    lock sync.Mutex
    cond = sync.NewCond(&lock)
)

在您的 PollResponse 处理程序中,您可以等待条件:

lock.Lock()
cond.Wait()
message := strconv.Itoa(counter)
lock.Unlock()
// write message to response

在您的 PushHandler 处理程序中,您可以广播更改:

lock.Lock()
counter += 1
cond.Broadcast()
lock.Unlock()

【讨论】:

  • 可怕的问题,但我也对 Go 的文档感到沮丧......这些来自哪个包。我尝试过从同步、条件、锁定、同步/条件和同步/锁定导入。虽然我没有做对
  • sync 是包名,所以import "sync" 可以。
  • 在 PollReponse 处理程序中,我是否会以使用 io.WriteString(w, &lt;-messages) 的相同方式将消息写入响应
  • 如果我在您的 cmets 之后添加该行,但实际上是 io.WriteString(w, message),其中 message 是您在解决方案中使用的变量,问题仍然存在
  • 这是我尝试过的版本:paste.ubuntu.com/6368785 -- 我在两个终端窗口中运行 curl http://localhost:8010/poll,在第三个窗口中运行 curl http://localhost:8010/push。前两个窗口一直阻塞,直到我执行第三个窗口中的命令,此时它们都打印了相同的计数器值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-01
相关资源
最近更新 更多