0

問題はこれです: Web サーバーがあります。ページの読み込みにゴルーチンを使用すると有益であると考えたので、goroutine として loadPage 関数を呼び出すことにしました。ただし、これを行うと、サーバーはエラーなしで動作を停止します。空白の白いページが印刷されます。問題は関数自体にあるに違いありません - 何かがゴルーチンと競合しています。

関連する機能は次のとおりです。

func loadPage(w http.ResponseWriter, path string) {
   s := GetFileContent(path)
   w.Header().Add("Content-Type", getHeader(path))
   w.Header().Add("Content-Length", GetContentLength(path))
   fmt.Fprint(w, s)
}
func GetFileContent(path string) string {
   cont, err := ioutil.ReadFile(path)
   e(err)
   aob := len(cont)
   s := string(cont[:aob])
   return s
}


func GetFileContent(path string) string {
   cont, err := ioutil.ReadFile(path)
   e(err)
   aob := len(cont)
   s := string(cont[:aob])
   return s
}

func getHeader(path string) string {
   images := []string{".jpg", ".jpeg", ".gif", ".png"}
   readable := []string{".htm", ".html", ".php", ".asp", ".js", ".css"}
   if ArrayContainsSuffix(images, path) {
      return "image/jpeg"
   }
   if ArrayContainsSuffix(readable, path) {
      return "text/html"
   }
   return "file/downloadable"
}


func ArrayContainsSuffix(arr []string, c string) bool {
   length := len(arr)
   for i := 0; i < length; i++ {
      s := arr[i]
      if strings.HasSuffix(c, s) {
         return true
      }
   }
return false
}
4

2 に答える 2

2

これが発生する理由は、 「 loadPage」を呼び出す HandlerFunc がリクエストと同期して呼び出されるためです。go ルーチンで呼び出すと、実際には Handler がすぐに返され、応答がすぐに送信されます。そのため、空白のページが表示されます。

これはserver.goで確認できます(1096 行目):

serverHandler{c.server}.ServeHTTP(w, w.req)
if c.hijacked() {
    return
}
w.finishRequest()

ServeHTTP関数はハンドラを呼び出し、返されるとすぐに「finishRequest」を呼び出します。そのため、ハンドラー関数は、要求を満たしたい限りブロックする必要があります。

go ルーチンを使用しても、実際にはページが速くなるわけではありません。フィリップが提案するように、1 つのゴー ルーチンをチャネルと同期させることも、この場合には役に立ちません。

問題の根本は実際にはioutil.ReadFile、ファイル全体を送信する前にメモリにバッファリングすることです。

ファイルをストリーミングしたい場合は、os.Open. io.Copyファイルのコンテンツをブラウザにストリーミングするために使用できます。ブラウザは、チャンク エンコーディングを使用します。

それは次のようになります。

f, err := os.Open(path)
if err != nil {
    http.Error(w, "Not Found", http.StatusNotFound)
    return
}
n, err := io.Copy(w, f)
if n == 0 && err != nil {
    http.Error(w, "Error", http.StatusInternalServerError)
    return
}

何らかの理由で複数の go ルーチンで作業を行う必要がある場合は、 を参照してくださいsync.WaitGroup。チャネルも機能します。

ファイルを提供するだけの場合は、FileServerServeFileなど、これに最適化された他のオプションがあります。

于 2013-08-02T20:52:08.277 に答える
0

In the typical web framework implementations in Go, the route handlers are invoked as Goroutines. I.e. at some point the web framework will say go loadPage(...).

So if you call a Go routine from inside loadPage, you have two levels of Goroutines.

The Go scheduler is really lazy and will not execute the second level if it's not forced to. So you need to enforce it through synchronization events. E.g. by using channels or the sync package. Example:

func loadPage(w http.ResponseWriter, path string) {
  s := make(chan string)
  go GetFileContent(path, s)
  fmt.Fprint(w, <-s)
}

The Go documentation says this:

If the effects of a goroutine must be observed by another goroutine, use a synchronization mechanism such as a lock or channel communication to establish a relative ordering.

Why is this actually a smart thing to do? In larger projects you may deal with a large number of Goroutines that need to be coordinated somehow efficiently. So why call a Goroutine if it's output is used nowhere? A fun fact: I/O operations like fmt.Printf do trigger synchronization events too.

于 2013-08-02T20:26:00.843 に答える