如何与gorilla mux建立无状态连接?

时间:2018-05-15 13:16:45

标签: go

我的程序运行正常,每次只有一个连接,但没有并发连接。

我需要一个函数呈现所有连接,这个函数将包含我服务中需要的所有数据,而且工作不正常,所以我用下面的简单代码说明:

package main

import (
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "github.com/rs/cors"
    "net/http"
    "reflect"
    "time"
)

var Out struct {
        Code     int             `json:"status"`
        Message  []interface{}   `json:"message"`
}

func Clear(v interface{}) {
    p := reflect.ValueOf(v).Elem()
    p.Set(reflect.Zero(p.Type()))
}

func YourHandler(w http.ResponseWriter, r *http.Request) {
    Clear(&Out.Message)
    Out.Code = 0

    // w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Access-Control-Allow-Headers","Content-Type,access-control-allow-origin, access-control-allow-headers")
    w.WriteHeader(http.StatusOK)

    for i:=0; i<10; i++ {
        Out.Code = Out.Code + 1
        Out.Message = append(Out.Message, "Running...")
        time.Sleep(1000 * time.Millisecond)

        if err := json.NewEncoder(w).Encode(Out)
        err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
        }
    }
}

func main() {
    r := mux.NewRouter()
    r.StrictSlash(true);

    r.HandleFunc("/", YourHandler)

    handler := cors.New(cors.Options{
        AllowedOrigins: []string{"*"},
        AllowCredentials: true,
        Debug: true,
        AllowedHeaders: []string{"Content-Type"},
        AllowedMethods: []string{"GET"},
    }).Handler(r)

    fmt.Println("Working in localhost:5000")
    http.ListenAndServe(":5000", handler)
}

如果你运行这段代码,你每次都不会在一个连接中看到任何错误,但是如果你在另一个tab / browser / etc中同时运行它,由于延迟,状态代码将不会从1到10,但是所有的电话都会被洗牌。

所以我猜这意味着它不是无状态的,我需要它,所以即使同时有300个连接,它也会始终从1到10返回状态代码。

我该怎么办? (正如我所说的,这是一个简单的代码,结构和渲染函数是彼此分开的包和所有数据集合中的)和/或

3 个答案:

答案 0 :(得分:3)

net/http服务器同时调用处理程序。服务器为每个客户端连接创建一个goroutine,并在这些goroutine上调用处理程序。

Gorilla Mux在并发方面是被动的。在调用多路复用器的任何goroutine上,多路复用器调用已注册的应用程序处理程序。

使用sync.Mutex一次将执行限制为一个goroutine:

var mu sync.Mutex

func YourHandler(w http.ResponseWriter, r *http.Request) {
    mu.Lock()
    defer mu.Unlock()

    Clear(&Out.Message)
    Out.Code = 0
    ...

考虑到处理程序中的time.Sleep调用,这不是一个好的解决方案。服务器每10秒最多处理一个请求。

更好的解决方案是将Out声明为处理函数内的局部变量。通过此更改,此处不需要互斥锁或清除Out:

func YourHandler(w http.ResponseWriter, r *http.Request) {

    var Out struct {
        Code     int             `json:"status"`
        Message  []interface{}   `json:"message"`
    }


    // w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    w.Header().Set("Access-Control-Allow-Origin", "*")
    ...

如果无法移动Out的声明,则将值复制到局部变量:

func YourHandler(w http.ResponseWriter, r *http.Request) {
    Out := Out // local Out is copy of package-level Out
    Clear(&Out.Message)
    Out.Code = 0
    ...

答案 1 :(得分:0)

Gorilla Mix使用Go的net / http服务器来处理您的http请求。 Go创建一个Go例程来为每个传入的请求提供服务。如果我正确理解您的问题,您希望Go响应将按照从1到10的顺序排列您的自定义状态代码,因为您希望每个请求按顺序同步进入。如果您熟悉Java,那么例行程序的并行性并不像Java线程那样保证执行顺序。因此,如果为1到10循环中创建的每个请求生成Go例程,则例程将自行执行,而不考虑先前完成的顺序。这些Go例程中的每一个都将在完成后为您的请求提供服务。如果要控制并行处理的这些请求的顺序,但按顺序则可以使用通道。查看此链接以控制每个http请求的10个Go例程之间的同步。 https://gobyexample.com/channel-synchronization

答案 2 :(得分:0)

首先,我要感谢ThunderCat和Ramil的帮助,你的答案给了我一个北方找到正确的答案。

简短的回答是:不要没有无国籍联系,所以我无法做我想要的事。

一旦说到,我认为(基于RFC 7230)它没有的原因是因为:

  • 在传统的Web服务器应用程序中,我们有一个程序来处理连接(Apache,nginx等)并打开一个线程到路由应用程序,而在Go中我们都有相同的应用程序,所以任何全局总是在连接之间共享
  • 在可能像Go这样的语言(打开端口的应用程序并保持监听),如C ++,它们是面向对象的,所以即使公共变量都在一个类中,所以你不会分享它,因为你每次都必须创建一个类的实例。

创建一个线程可以解决问题,但是Go没有它,而是有Goroutines,有关它的更多详细信息:

https://translate.google.com/translate?sl=ko&tl=en&u=https%3A%2F%2Ftech.ssut.me%2F2017%2F08%2F20%2Fgoroutine-vs-threads%2F

经过几天的努力和帮助,我将修复它,将我的结构改为键入并将其置于本地,如下所示:

package main

import (
    "encoding/json"
    "fmt"
    "github.com/gorilla/mux"
    "github.com/rs/cors"
    "net/http"
    "reflect"
    "time"
)

type Out struct {
    Code     int             `json:"status"`
    Message  []interface{}   `json:"message"`
}

func Clear(v interface{}) {
    p := reflect.ValueOf(v).Elem()
    p.Set(reflect.Zero(p.Type()))
}

func YourHandler(w http.ResponseWriter, r *http.Request) {
    localOut := Out{0,nil}

    // w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Access-Control-Allow-Headers","Content-Type,access-control-allow-origin, access-control-allow-headers")
    w.WriteHeader(http.StatusOK)

    for i:=0; i<10; i++ {
        localOut.Code = localOut.Code + 1
        localOut.Message = append(localOut.Message, "Running...")
        time.Sleep(1000 * time.Millisecond)

        if err := json.NewEncoder(w).Encode(localOut)
        err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
        }
    }
}

func main() {
    r := mux.NewRouter()
    r.StrictSlash(true);

    r.HandleFunc("/", YourHandler)

    handler := cors.New(cors.Options{
        AllowedOrigins: []string{"*"},
        AllowCredentials: true,
        Debug: true,
        AllowedHeaders: []string{"X-Session-Token","Content-Type"},
        AllowedMethods: []string{"GET","POST","PUT","DELETE"},
    }).Handler(r)

    fmt.Println("Working in localhost:5000")
    http.ListenAndServe(":5000", handler)
}

当然这需要几周时间,所以现在我把我的应用程序放在nginx后面,现在它按预期工作了。