英文:
Simple Go web server, not seeing responses in the client
问题
我正在学习Go,并且正在编写一个简单的Web服务器,该服务器使用通道来限制并发请求的数量。服务器会在控制台打印日志条目,显示它正在接收和处理请求,但是客户端浏览器没有显示任何输出。我尝试添加了对响应写入器的刷新,但没有起作用。
作为一个新手,我错过了什么?感谢任何提示和指导。
以下是代码:
package main
import (
"fmt"
"html"
"net/http"
"time"
)
// 定义一个与请求通道一起使用的类型
type clientRequest struct {
r *http.Request
w http.ResponseWriter
}
const (
MaxRequests int = 10
)
// 请求通道,用于限制正在处理的并发请求的数量
var reqChannel chan *clientRequest
func init() {
reqChannel = make(chan *clientRequest, MaxRequests)
}
func main() {
// 创建服务器的处理程序
var ServeMux = http.NewServeMux()
ServeMux.HandleFunc("/", serveHandler)
// 启动请求处理程序池,所有处理程序都从同一个通道读取
for i := 0; i < MaxRequests; i++ {
go processRequest(i)
}
// 创建服务器对象
s := &http.Server{
Addr: ":8080",
Handler: ServeMux, // 要调用的处理程序,如果为nil,则使用http.DefaultServeMux
ReadTimeout: 10 * time.Second, // 请求读取超时的最大持续时间
WriteTimeout: 10 * time.Second, // 响应写入超时的最大持续时间
MaxHeaderBytes: 1 << 20, // 请求头的最大大小,1048576字节
}
// 启动服务器
err := s.ListenAndServe()
if err != nil {
fmt.Println("服务器启动失败:", err)
}
}
func serveHandler(w http.ResponseWriter, r *http.Request) {
var newRequest = new(clientRequest)
newRequest.r = r
newRequest.w = w
reqChannel <- newRequest // 将新请求发送到请求通道
fmt.Printf("已发送请求到reqChannel,URL为:%q\n", html.EscapeString(r.URL.Path))
}
func processRequest(instanceNbr int) {
fmt.Printf("处理程序已启动,实例编号:%d\n", instanceNbr)
for theRequest := range reqChannel { // 从通道接收请求,直到通道关闭
fmt.Printf("从reqChannel接收到请求,URL为:%q\n", html.EscapeString(theRequest.r.URL.Path))
// xxx 这部分不起作用:
fmt.Fprintf(theRequest.w, "处理程序实例 #%d:URL为 %q", instanceNbr, html.EscapeString(theRequest.r.URL.Path))
if f, ok := theRequest.w.(http.Flusher); ok {
f.Flush()
}
}
}
希望对你有帮助!
英文:
I'm learning Go, and am writing a simple web server that uses a channel to limit the number of concurrent requests. The server prints log entries at the console that show it's receiving the requests and processing them, however the client browser doesn't show any output. I've tried adding a flush of the response writer, that didn't help.
As a noob, what am I missing? Thanks for any tips/pointers.
Here's the code:
package main
import (
"fmt"
"html"
"net/http"
"time"
)
// define a type to be used with our request channel
type clientRequest struct {
r *http.Request
w http.ResponseWriter
}
const (
MaxRequests int = 10
)
// the request channel, to limit the number of simultaneous requests being processed
var reqChannel chan *clientRequest
func init() {
reqChannel = make(chan *clientRequest, MaxRequests)
}
func main() {
// create the server's handler
var ServeMux = http.NewServeMux()
ServeMux.HandleFunc("/", serveHandler)
// start pool of request handlers, all reading from the same channel
for i := 0; i < MaxRequests; i++ {
go processRequest(i)
}
// create the server object
s := &http.Server{
Addr: ":8080",
Handler: ServeMux, // handler to invoke, http.DefaultServeMux if nil
ReadTimeout: 10 * time.Second, // maximum duration before timing out read of the request
WriteTimeout: 10 * time.Second, // maximum duration before timing out write of the response
MaxHeaderBytes: 1 << 20, // maximum size of request headers, 1048576 bytes
}
// start the server
err := s.ListenAndServe()
if err != nil {
fmt.Println("Server failed to start: ", err)
}
}
func serveHandler(w http.ResponseWriter, r *http.Request) {
var newRequest = new(clientRequest)
newRequest.r = r
newRequest.w = w
reqChannel <- newRequest // send the new request to the request channel
fmt.Printf("Sent request to reqChannel for URL: %q\n", html.EscapeString(r.URL.Path))
}
func processRequest(instanceNbr int) {
fmt.Printf("processRequest started for instance #%d\n", instanceNbr)
for theRequest := range reqChannel { // receive requests from the channel until it is closed
fmt.Printf("Got request from reqChannel for URL: %q\n", html.EscapeString(theRequest.r.URL.Path))
// xxx this isn't working:
fmt.Fprintf(theRequest.w, "processRequest instance #%d: URL is %q", instanceNbr, html.EscapeString(theRequest.r.URL.Path))
if f, ok := theRequest.w.(http.Flusher); ok {
f.Flush()
}
}
}
答案1
得分: 3
服务器在serveHandler
返回时关闭响应。
一种修复方法是阻塞serveHandler
直到请求被处理。在下面的代码中,工作线程关闭done
来表示请求已完成。处理程序等待done
关闭。
type clientRequest struct {
r *http.Request
w http.ResponseWriter
done chan struct{} // <-- 添加这一行
}
func serveHandler(w http.ResponseWriter, r *http.Request) {
var newRequest = new(clientRequest)
newRequest.r = r
newRequest.w = w
newRequest.done = make(chan struct{})
reqChannel <- newRequest // 将新请求发送到请求通道
fmt.Printf("Sent request to reqChannel for URL: %q\n", html.EscapeString(r.URL.Path))
<-newRequest.done // 等待工作线程完成
}
func processRequest(instanceNbr int) {
fmt.Printf("processRequest started for instance #%d\n", instanceNbr)
for theRequest := range reqChannel { // 从通道接收请求,直到通道关闭
fmt.Printf("Got request from reqChannel for URL: %q\n", html.EscapeString(theRequest.r.URL.Path))
fmt.Fprintf(theRequest.w, "processRequest instance #%d: URL is %q", instanceNbr, html.EscapeString(theRequest.r.URL.Path))
if f, ok := theRequest.w.(http.Flusher); ok {
f.Flush()
}
close(theRequest.done) // 通知处理程序请求已完成
}
}
如果目标是限制活动处理程序的数量,可以使用通道作为计数信号量来限制活动处理程序的数量:
var reqChannel = make(chan struct{}, MaxRequests)
func serveHandler(w http.ResponseWriter, r *http.Request) {
reqChannel <- struct{}{}
// 处理请求
<-reqChannel
}
请注意,服务器在每个连接的goroutine中运行处理程序。
更简单的方法是编写一个处理程序。大多数服务器不需要限制请求处理程序的并发性。
英文:
The server closes the response when serveHandler
returns.
One fix is to block serveHandler
until the request is processed. In the following code, the worker closes done
to signal that the request is complete. The handler waits for done
to close.
type clientRequest struct {
r *http.Request
w http.ResponseWriter
done chan struct{} // <-- add this line
}
func serveHandler(w http.ResponseWriter, r *http.Request) {
var newRequest = new(clientRequest)
newRequest.r = r
newRequest.w = w
newRequest.done = make(chan struct{})
reqChannel <- newRequest // send the new request to the request channel
fmt.Printf("Sent request to reqChannel for URL: %q\n", html.EscapeString(r.URL.Path))
<-newRequest.done // wait for worker goroutine to complete
}
func processRequest(instanceNbr int) {
fmt.Printf("processRequest started for instance #%d\n", instanceNbr)
for theRequest := range reqChannel { // receive requests from the channel until it is closed
fmt.Printf("Got request from reqChannel for URL: %q\n", html.EscapeString(theRequest.r.URL.Path))
fmt.Fprintf(theRequest.w, "processRequest instance #%d: URL is %q", instanceNbr, html.EscapeString(theRequest.r.URL.Path))
if f, ok := theRequest.w.(http.Flusher); ok {
f.Flush()
}
close(theRequest.done) // signal handler that request is complete
}
}
If the goal is to limit the number of active handlers, then you can use a channel as a counting semaphore to limit the number of active handler goroutines:
var reqChannel = make(chan struct{}, MaxRequests)
func serveHandler(w http.ResponseWriter, r *http.Request) {
reqChannel <- struct{}{}
// handle the request
<-reqChannel
}
Note that the server runs handlers in a per connection goroutine.
Even simpler is to just write a handler. Most servers do not need to limit request handler concurrency.
答案2
得分: 2
你的答案在这段 net/http 代码的部分 中:
// HTTP 不能同时有多个活动请求。[*]
// 在服务器回复此请求之前,它无法读取另一个请求,
// 所以我们可以在此 goroutine 中运行处理程序。
// [*] 不完全正确:HTTP 管线化。即使它们的响应需要串行化,我们也可以让它们并行处理。
serverHandler{c.server}.ServeHTTP(w, w.req)
if c.hijacked() {
return
}
w.finishRequest()
在 ServeHTTP
返回之后,请求就完成了。
所以你有几个解决方案:
-
放弃你的工作模式,在
serveHandler
中完成任务。 -
等待请求完全处理完再结束
serveHandler
,可以使用类似下面的方法:
(在我的本地测试过)
type clientRequest struct {
r *http.Request
w http.ResponseWriter
done chan struct{}
}
func serveHandler(w http.ResponseWriter, r *http.Request) {
var newRequest = new(clientRequest)
newRequest.r = r
newRequest.w = w
newRequest.done = make(chan struct{})
reqChannel <- newRequest // 将新请求发送到请求通道
fmt.Printf("Sent request to reqChannel for URL: %q\n", html.EscapeString(r.URL.Path))
<-newRequest.done // 等待工作程序完成
}
func processRequest(instanceNbr int) {
fmt.Printf("processRequest started for instance #%d\n", instanceNbr)
for theRequest := range reqChannel { // 从通道接收请求,直到通道关闭
fmt.Printf("Got request from reqChannel for URL: %q\n", html.EscapeString(theRequest.r.URL.Path))
// xxx 这个不起作用:
fmt.Fprintf(theRequest.w, "processRequest instance #%d: URL is %q", instanceNbr, html.EscapeString(theRequest.r.URL.Path))
if f, ok := theRequest.w.(http.Flusher); ok {
f.Flush()
}
theRequest.done <- struct{}{}
}
}
英文:
Your answer is in this part of the net/http code :
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
serverHandler{c.server}.ServeHTTP(w, w.req)
if c.hijacked() {
return
}
w.finishRequest()
After ServeHTTP
returns, the request is finished.
So you have a few solutions :
-
drop your worker pattern and do the job in
serveHandler
-
wait for the request to be fully processed before finishing
serveHandler
, with something like this:
(tested on my local)
type clientRequest struct {
r *http.Request
w http.ResponseWriter
done chan struct{}
}
func serveHandler(w http.ResponseWriter, r *http.Request) {
var newRequest = new(clientRequest)
newRequest.r = r
newRequest.w = w
newRequest.done = make(chan struct{})
reqChannel <- newRequest // send the new request to the request channel
fmt.Printf("Sent request to reqChannel for URL: %q\n", html.EscapeString(r.URL.Path))
<-newRequest.done // wait for the worker to finish
}
func processRequest(instanceNbr int) {
fmt.Printf("processRequest started for instance #%d\n", instanceNbr)
for theRequest := range reqChannel { // receive requests from the channel until it is closed
fmt.Printf("Got request from reqChannel for URL: %q\n", html.EscapeString(theRequest.r.URL.Path))
// xxx this isn't working:
fmt.Fprintf(theRequest.w, "processRequest instance #%d: URL is %q", instanceNbr, html.EscapeString(theRequest.r.URL.Path))
if f, ok := theRequest.w.(http.Flusher); ok {
f.Flush()
}
theRequest.done <- struct{}{}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论