常规:Web服务器中的共享全局变量

时间:2019-07-04 14:17:49

标签: go

我在网络服务器上运行了go服务器,并处理了post请求,该请求内部调用了不同的url以使用goroutine获取响应并继续。

我已将整个流程划分为不同的方法。代码草稿。

<style>

    * {
      box-sizing: border-box;
    }

    .header {
      padding: 15px;
    }

    .menu {
      padding: 5px;
      margin: 0;
      padding: 0;
      width: 25%;
      height: 100%;
      background-color: #f1f1f1;
      float: left;
      overflow: auto;}

    .main {
      width: 75%;
      float: left;
      padding: 15px;
      margin-left:25%;
      padding:1px 16px;
      height:1000px;
    }
</style>

    <div class="header">
  <h1>Computer Networks</h1>
</div>
 <hr>
<div class="menu">
    <iframe id="bookpage" name="bookpage" src="Navigation.html"></iframe>
</div>

<div class="main">
  <h1>The Content</h1>
    <iframe id="content01" name="content01" src="courses/DCN/Computer Network Types.html"></iframe>

</div>

上面的代码可以做到:

  1. 全局变量声明,在prepare函数中初始化。
  2. 在go例程callUrls函数中附加值
  3. 在过程函数中读取这些变量

现在我应该将全局声明的变量传递给每个函数调用,以使它们成为本地变量,因为那样一来就不会被共享?(我不愿意这样做)

或者是否有其他方法可以实现相同的目的而无需在调用的函数中添加更多参数。

因为我将在程序中以及go例程函数中使用的其他字符串和整数值也很少。

使它们成为线程安全的正确方法是什么,并且每个请求同时进入端口仅5个代码。

1 个答案:

答案 0 :(得分:0)

请勿使用全局变量,而应使用显式变量并使用函数参数。此外,您在status_codes上存在竞争条件,因为它可以由多个goroutine进行访问而没有任何互斥锁。

在下面查看我的修复程序。

func prepare(w http.ResponseWriter, r *http.Request, name string) {
    var urls []string
    //status_codes is populated by callUris(), so let it return the slice with values
    results, status_codes := callUrls(urls)
    //process() needs status_codes in order to work, so pass the variable explicitely
    process(w, results, status_codes)

}

type Response struct {
    status int
    url    string
    body   string
}

func callUrls(urls []string) []*Response {
    ch := make(chan *Response, len(urls))
    //In order to avoid race condition, let's use a channel
    statusChan := make(chan string, len(urls))
    for _, url := range urls {
        go func(url string) {
            //http post on url,
            //base on status code of url call, add to status code
            //some thing like

            req, err := http.NewRequest("POST", url, bytes.NewBuffer(somePostData))
            req.Header.Set("Content-Type", "application/json")
            req.Close = true

            client := &http.Client{
                Timeout: time.Duration(time.Duration(100) * time.Second),
            }

            response, err := client.Do(req)

            if err != nil {
                statusChan <- "200"
                //do other thing with the response received
            } else {
                statusChan <- "500"

            }

            // return to channel accordingly
            ch <- &Response{200, "url", "response body"}

        }(url)
    }
    var results []*Response
    var status_codes []string
    for !doneRes || !doneStatus { //continue until both slices are filled with values
        select {
        case r := <-ch:
            results = append(results, r)
            if len(results) == len(urls) {
                //Done
                close(ch)      //Not really needed here
                doneRes = true //we are done with results, set the corresponding flag

            }
        case status := <-statusChan:
            status_codes = append(status_codes, status)
            if len(status_codes) == len(urls) {
                //Done
                close(statusChan) //Not really needed here
                doneStatus = true //we are done with statusChan, set the corresponding flag
            }
        }

    }
    return results, status_codes
}

func process(w http.ResponseWriter, results []*Response, status_codes []string) {
    fmt.Println("status", status_codes)
}