Javascript “长轮询”;“全球”;按钮,向所有人广播?

Javascript “长轮询”;“全球”;按钮,向所有人广播?,javascript,go,global-variables,long-polling,Javascript,Go,Global Variables,Long Polling,我正在尝试实现一个全局按钮计数器,当任何/不同的用户单击它时,它会更新。 因此,如果一个人单击按钮,我会在我的页面实例上看到计数器更新 我目前使用的是长轮询技术,或者我认为是这样,但经过审查,我认为我在向所有浏览器“广播”更新时出现了一个错误 当前的错误是,例如,如果我打开了两个浏览器,并且我连续单击一个浏览器,那么我单击按钮的浏览器只会更新一半的时间。它将获得1 3 5等,而另一个浏览器显示2 4 6等 在网上查看之后,我认为这可能与频道和向网站上所有浏览器的广播有关。如果有人能帮我举一个例子

我正在尝试实现一个全局按钮计数器,当任何/不同的用户单击它时,它会更新。 因此,如果一个人单击按钮,我会在我的页面实例上看到计数器更新

我目前使用的是长轮询技术,或者我认为是这样,但经过审查,我认为我在向所有浏览器“广播”更新时出现了一个错误

当前的错误是,例如,如果我打开了两个浏览器,并且我连续单击一个浏览器,那么我单击按钮的浏览器只会更新一半的时间。它将获得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>

函数longpoll(url,回调){
var req=新的XMLHttpRequest();
请求打开('GET',url,true);
req.onreadystatechange=功能(aEvt){
如果(req.readyState==4){
如果(请求状态==200){
回调(请求响应文本);
longpoll(url,回调);
}否则{
警报(“长轮询连接丢失”);
}
}
};
请求发送(空);
}
函数recv(msg){
变量框=document.getElementById(“计数器”);
box.innerHTML+=msg+“\n”;
}
函数send(){
变量框=document.getElementById(“计数器”);
var req=新的XMLHttpRequest();
请求打开('POST',“/push?rcpt=”,true);
req.onreadystatechange=功能(aEvt){
如果(req.readyState==4){
如果(请求状态==200){
}否则{
警报(“发送失败!”);
}
}
};
请求发送(“hi”)
//box.innerHTML+=“测试”;
}
长轮询聊天演示

测试按钮
服务器:

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)
    }
}
主程序包
进口(
“net/http”
“日志”
“io”
//“io/ioutil”
“strconv”
)
var消息chan string=make(chan string,100)
变量计数器=0
func PushHandler(带http.ResponseWriter,req*http.Request){
//body,err:=ioutil.ReadAll(req.body)
/*如果错误!=零{
w、 文书主任(400)
}*/
计数器+=1

消息Go频道不是多播的。也就是说,如果您有多个Goroutine从该频道读取,那么当您向该频道写入值时,只有一个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代码(单独;请参见PS),而在于浏览器(Chrome)。对同一URL发出两个请求是顺序发生的,而不是并行发生的

解决方案 您需要向longpoll URL添加唯一的时间戳以欺骗浏览器:

req.open ('GET', url+"?"+(new Date().getTime()), true); 
PS-通过这个问题,我学到了很多关于Go频道和互斥的知识。谢谢:)


PS-詹姆斯的回答()是让服务器端Go代码一次处理多个请求的关键,因为Go通道被阻塞,这意味着一次只能接收一个goroutine。因此OP问题的解决方案是前端和后端代码更改的组合。

您能添加一些调试输出并查看推送请求何时通过吗?@Brenden如果你能给我更多关于你想要什么的指导,我很乐意为你效劳。推送请求通过客户端的
send()
函数完成,每当用户按下按钮,就会激活它。我会添加一些
fmt.Println()
呼叫以确认这些呼叫何时通过,而不是轮询响应何时通过。我将在一个小时左右运行本地测试,看看能得到什么结果(我目前正在工作,无法测试您的代码)@据我所知,Brenden正在按照预期一个接一个地进行测试。但非常感谢你的帮助!我真的很感激!我期待着收到你的回音!我在本地运行了它,它按预期工作。数字是连续的。我正在Linux/Chromium上测试,你在用什么进行测试?你能手动吗ally request poll and push URL而不使用Javascript?它的工作方式是否不同?这个问题很糟糕,但我也对Go的文档感到失望…这些文档来自哪个软件包。我尝试过从sync、cond、lock导入,然后从sync/cond导入,以及从sync/lock导入。虽然包名是
sync
,但我没有理解正确,所以
import“sync”
就可以了。在pollResponse处理程序中,我是否会以同样的方式将消息写入Response,使用
io.WriteString(w,如果我在您的注释后添加该行,但实际上是io.WriteString(w,message),当message是您在解决方案中使用的变量时,问题仍然存在,这是我尝试的版本:--I run
curlhttp://localhost:8010/poll
在两个终端窗口中,并运行
curlhttp://localhost:8010/push
在第三个窗口中。前两个窗口被阻塞,直到我在第三个窗口中执行命令,在我的意思是,他们都打印了相同的计数器值。无法解释我多么感谢你在聊天中与我一起工作几个小时来得到这个答案!大家都起来投票!问题中的Go代码也被破坏了,对吗?单凭这一点不能解决问题。@JamesHenstridge我很想知道,但我已经切换到互斥代码给你了建议。我想切换回初始代码以验证这一点,但我相信您的答案是关键的,因为频道不能被超过1个goroutine读取。@JamesHenstridge我已更新了我的答案,为您更改Go代码提供了依据。谢谢。