请求和响应写入器将被覆盖。

huangapple go评论79阅读模式
英文:

Request and ResponseWriter will be overwrite

问题

我用go http包做了一些实验。

看一下下面的代码片段,一个非常简单的http服务器:

package main

import (
	"fmt"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"net/http"
	//"time"
)

type controller struct {
	request  *http.Request
	response http.ResponseWriter
}

func (self *controller) send() {
	fmt.Fprintf(self.response, "Request %p and Response %p \n", self.request, self.response)
}

func (self *controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	fmt.Println("Start controller")
	self.request = r
	self.response = rw

	self.send()
}

func main() {

	router := mux.NewRouter()
	router.Handle("/", &controller{})

	n := negroni.Classic()
	n.UseHandler(router)

	n.Run(":3000")
}

然后我向服务器发送了20次请求:

package main

import (
	"fmt"
	"io"
	"net/http"
	"runtime"
	"time"
)

func main() {

	buffer := make([]byte, 100)
	runtime.GOMAXPROCS(runtime.NumCPU())

	for i := 0; i < 20; i++ {
		go func(z int) {
			//fmt.Println("Request goroutine 1 ", z)
			resp, err := http.Get("http://127.0.0.1:3000")
			if err != nil {
				fmt.Println(err)
			}

			io.ReadFull(resp.Body, buffer)
			fmt.Println(string(buffer))
		}(i)
	}

	time.Sleep(time.Second * 5)

}

我收到了以下响应:

Request 0xc0820201a0 and Response 0xc082007100

Request 0xc082021380 and Response 0xc0820072c0

Request 0xc0820204e0 and Response 0xc082007740

Request 0xc082020dd0 and Response 0xc0820071c0

Request 0xc082020d00 and Response 0xc082007240

Request 0xc082021450 and Response 0xc082007400

Request 0xc082020f70 and Response 0xc082007500

Request 0xc082021110 and Response 0xc082007480

Request 0xc0820212b0 and Response 0xc082007540

Request 0xc082020b60 and Response 0xc0820075c0

Request 0xc082020750 and Response 0xc082007640

Request 0xc082020270 and Response 0xc0820076c0

Request 0xc082020c30 and Response 0xc082007840

Request 0xc082020820 and Response 0xc0820078c0

Request 0xc082020ea0 and Response 0xc082007940

Request 0xc0820211e0 and Response 0xc0820079c0

Request 0xc082021520 and Response 0xc082007a40

Request 0xc0820209c0 and Response 0xc082007ac0

Request 0xc082021040 and Response 0xc082007380

Request 0xc082020a90 and Response 0xc0820077c0

正如你在这里看到的,一切都很好。每个请求和响应都有不同的存储地址。

所以如果我将http服务器更改为:

package main

import (
	"fmt"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"net/http"
	"time"
)

type controller struct {
	request  *http.Request
	response http.ResponseWriter
	counter  int
}

func (self *controller) get() {
	self.counter++
	if self.counter == 7 {
		time.Sleep(time.Second * 4)
	}

	fmt.Printf("Request %p and Response %p \n", self.request, self.response)

}

func (self *controller) send() {
	fmt.Fprintf(self.response, "Request %p and Response %p \n", self.request, self.response)
}

func (self *controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	fmt.Println("Start controller")
	self.request = r
	self.response = rw

	self.get()
	self.send()
}

func main() {

	router := mux.NewRouter()
	router.Handle("/", &controller{})

	n := negroni.Classic()
	n.UseHandler(router)

	n.Run(":3000")
}

正如你在这里看到的,我添加了一个名为get的方法,当计数器达到7时,它将休眠4秒钟。

func (self *controller) get() {
	self.counter++
	if self.counter == 7 {
		time.Sleep(time.Second * 4)
	}

	fmt.Printf("Request %p and Response %p \n", self.request, self.response)

}

作为输出,我得到了:

Request 0xc082021040 and Response 0xc082007100

Request 0xc0820209c0 and Response 0xc082007240

Request 0xc0820211e0 and Response 0xc082007380

Request 0xc082020270 and Response 0xc082007500

Request 0xc082020d00 and Response 0xc082007640

Request 0xc082020f70 and Response 0xc082007740

Request 0xc082020680 and Response 0xc082007840

Request 0xc082020820 and Response 0xc082007940

Request 0xc082020b60 and Response 0xc082007a40

Request 0xc082021380 and Response 0xc0820071c0

Request 0xc082021110 and Response 0xc0820072c0

Request 0xc0820208f0 and Response 0xc082007400

Request 0xc082020a90 and Response 0xc082007540

Request 0xc082020340 and Response 0xc0820076c0

Request 0xc082020750 and Response 0xc0820075c0

Request 0xc082020dd0 and Response 0xc0820077c0

Request 0xc0820212b0 and Response 0xc0820078c0

Request 0xc0820201a0 and Response 0xc0820079c0

Request 0xc082020ea0 and Response 0xc082007ac0

Request 0xc082020ea0 and Response 0xc082007ac0

正如你所看到的,最后两行的地址是相同的,这意味着在第7个请求时,请求和响应的地址将被覆盖,因为响应被延迟了。

我的问题是,当一个请求需要进行密集计算时,例如调整图像大小,在调整大小过程中另一个请求进来,图像调整过程中的请求和响应将像上面那样被覆盖。然后响应将发送给最后一个请求的客户端。在我看来,这是错误的,对吗?

我知道go为每个请求创建一个新的goroutine。

英文:

I experiment a little bit with go http package.

Look at the following code snippet, a very simple http server:

package main

import (
	&quot;fmt&quot;
	&quot;github.com/codegangsta/negroni&quot;
	&quot;github.com/gorilla/mux&quot;
	&quot;net/http&quot;
	//&quot;time&quot;
)

type controller struct {
	request  *http.Request
	response http.ResponseWriter
}

func (self *controller) send() {
	fmt.Fprintf(self.response, &quot;Request %p and Response %p \n&quot;, self.request, self.response)
}

func (self *controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	fmt.Println(&quot;Start controller&quot;)
	self.request = r
	self.response = rw

	self.send()
}

func main() {

	router := mux.NewRouter()
	router.Handle(&quot;/&quot;, &amp;controller{})

	n := negroni.Classic()
	n.UseHandler(router)

	n.Run(&quot;:3000&quot;)
}

and I make 20 times request to server:

package main

import (
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;net/http&quot;
	&quot;runtime&quot;
	&quot;time&quot;
)

func main() {

	buffer := make([]byte, 100)
	runtime.GOMAXPROCS(runtime.NumCPU())

	for i := 0; i &lt; 20; i++ {
		go func(z int) {
			//fmt.Println(&quot;Request goroutine 1 &quot;, z)
			resp, err := http.Get(&quot;http://127.0.0.1:3000&quot;)
			if err != nil {
				fmt.Println(err)
			}

			io.ReadFull(resp.Body, buffer)
			fmt.Println(string(buffer))
		}(i)
	}

	time.Sleep(time.Second * 5)

}

I received the following response:

Request 0xc0820201a0 and Response 0xc082007100

Request 0xc082021380 and Response 0xc0820072c0

Request 0xc0820204e0 and Response 0xc082007740

Request 0xc082020dd0 and Response 0xc0820071c0

Request 0xc082020d00 and Response 0xc082007240

Request 0xc082021450 and Response 0xc082007400

Request 0xc082020f70 and Response 0xc082007500

Request 0xc082021110 and Response 0xc082007480

Request 0xc0820212b0 and Response 0xc082007540

Request 0xc082020b60 and Response 0xc0820075c0

Request 0xc082020750 and Response 0xc082007640

Request 0xc082020270 and Response 0xc0820076c0

Request 0xc082020c30 and Response 0xc082007840

Request 0xc082020820 and Response 0xc0820078c0

Request 0xc082020ea0 and Response 0xc082007940

Request 0xc0820211e0 and Response 0xc0820079c0

Request 0xc082021520 and Response 0xc082007a40

Request 0xc0820209c0 and Response 0xc082007ac0

Request 0xc082021040 and Response 0xc082007380

Request 0xc082020a90 and Response 0xc0820077c0

As you can see here, everything is fine. Every request and response have different storage address.

So if I change the http server to:

package main

import (
	&quot;fmt&quot;
	&quot;github.com/codegangsta/negroni&quot;
	&quot;github.com/gorilla/mux&quot;
	&quot;net/http&quot;
	&quot;time&quot;
)

type controller struct {
	request  *http.Request
	response http.ResponseWriter
	counter  int
}


func (self *controller) get() {
	self.counter++
	if self.counter == 7 {
		time.Sleep(time.Second * 4)
	}

	fmt.Printf(&quot;Request %p and Response %p \n&quot;, self.request, self.response)

}

func (self *controller) send() {
	fmt.Fprintf(self.response, &quot;Request %p and Response %p \n&quot;, self.request, self.response)
}

func (self *controller) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	fmt.Println(&quot;Start controller&quot;)
	self.request = r
	self.response = rw

	self.get()
	self.send()
}

func main() {

	router := mux.NewRouter()
	router.Handle(&quot;/&quot;, &amp;controller{})

	n := negroni.Classic()
	n.UseHandler(router)

	n.Run(&quot;:3000&quot;)
}

As you can see here I've add the method get and when counter reach number 7, it will sleep for 4 second.

func (self *controller) get() {
	self.counter++
	if self.counter == 7 {
		time.Sleep(time.Second * 4)
	}

	fmt.Printf(&quot;Request %p and Response %p \n&quot;, self.request, self.response)

}

As output I've got

Request 0xc082021040 and Response 0xc082007100

Request 0xc0820209c0 and Response 0xc082007240

Request 0xc0820211e0 and Response 0xc082007380

Request 0xc082020270 and Response 0xc082007500

Request 0xc082020d00 and Response 0xc082007640

Request 0xc082020f70 and Response 0xc082007740

Request 0xc082020680 and Response 0xc082007840

Request 0xc082020820 and Response 0xc082007940

Request 0xc082020b60 and Response 0xc082007a40

Request 0xc082021380 and Response 0xc0820071c0

Request 0xc082021110 and Response 0xc0820072c0

Request 0xc0820208f0 and Response 0xc082007400

Request 0xc082020a90 and Response 0xc082007540

Request 0xc082020340 and Response 0xc0820076c0

Request 0xc082020750 and Response 0xc0820075c0

Request 0xc082020dd0 and Response 0xc0820077c0

Request 0xc0820212b0 and Response 0xc0820078c0

Request 0xc0820201a0 and Response 0xc0820079c0

Request 0xc082020ea0 and Response 0xc082007ac0

Request 0xc082020ea0 and Response 0xc082007ac0

As you can see the address from the last two lines is the same, that means by request 7 the address of request and response will be overwrite, because the response is delay.

My question is, when a request have to process intensive calculation, for example resizing image and during the resizing an other request comes in, the request and response from image resizing process will be overwrite like above. Then the response will go to the last requested client. In my opinion, thats wrong right?

I know that go create for every request a new goroutine.

答案1

得分: 5

你正在介绍一个数据竞争:

router.Handle("/", &controller{})

如你所见,controller 类型只有一个实例。

self.request = r
self.response = rw

在这里,你正在修改你的结构体。但是 ServeHTTP() 方法将会从多个 goroutine 中调用。

一个选项是创建一个处理程序,如下所示:

router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    c := &controller{request: r, response: w}
    c.send()
    ...
})

如果你使用 -race 标志运行代码,它会警告你这个问题。参见 http://blog.golang.org/race-detector

另外一件事:不要将接收器命名为 self。这不符合惯例。它会使你的代码难以阅读。self 是什么?给它一个合适的名字。

英文:

You are introducing a data race:

router.Handle(&quot;/&quot;, &amp;controller{})

As you can see there is only one instance of the controller type.

self.request = r
self.response = rw

Here you are modifying your struct. But the ServeHTTP() method will be called from multiple goroutines.

An option would be to create a handler like so

router.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
    c := &amp;controller{request: r, response: w)
    c.send()
    ...
})

If you would run your code with the -race flag, it would warn you about this. See also http://blog.golang.org/race-detector

Another thing: don't name your receiver self. This is not idiomatic. It makes your code unreadable. What is self? Give it an appropriate name.

huangapple
  • 本文由 发表于 2014年11月27日 00:10:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/27153811.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定