Golang/Gin

Goroutines inside a middleware

Tongchun 2019. 3. 14. 17:55

Server 개발 시 비동기로 처리해야 할 때가 있습니다. 저 같은 경우 redis에 쓰기 처리를 할때는 보통 비동기로 처리하기도 합니다.

go에서 비동기 처리 할 때는 goroutine을 사용합니다.

https://github.com/gin-gonic/gin#goroutines-inside-a-middleware


goroutine을 사용할 때 중요한 부분은 gin의 원본 context를 사용하면 안되고 읽기 전용으로 복제해서 사용해야 합니다.

When starting new Goroutines inside a middleware or handler, you SHOULD NOT use the original context inside it, you have to use a read-only copy.


바로 코드를 확인하면 아래와 같습니다.

package main

import (
	"log"
	"time"

	"github.com/gin-gonic/gin"
)

func setupRouter() *gin.Engine {
	// Disable Console Color
	// gin.DisableConsoleColor()
	r := gin.Default()

	r.GET("/long_async", longAsync)
	r.GET("/long_sync", longSync)

	return r
}

func longAsync(c *gin.Context) {

	// create copy to be used inside the goroutine
	cCp := c.Copy()

	go func() {
		// simulate a long task with time.Sleep(). 5 seconds
		time.Sleep(5 * time.Second)

		// note that you are using the copied context "cCp", IMPORTANT
		log.Println("Done! in path " + cCp.Request.URL.Path)
	}()

}

func longSync(c *gin.Context) {

	// simulate a long task with time.Sleep(). 5 seconds
	time.Sleep(5 * time.Second)

	// since we are NOT using a goroutine, we do not have to copy the context
	log.Println("Done! in path " + c.Request.URL.Path)
}

func main() {
	r := setupRouter()
	// Listen and Server in 0.0.0.0:8080
	r.Run(":8080")
}

goroutine을 사용한 async 처리와 그렇지 않은 sync 처리를 비교할 수 있습니다.

실행하고 두 route를 호출하면 아래와 같은 결과가 나옵니다.

$ go run example/server.go 
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /long_async               --> main.longAsync (3 handlers)
[GIN-debug] GET    /long_sync                --> main.longSync (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2019/03/14 - 15:51:58 | 404 |         876ns |             ::1 | GET      /login-asinc
[GIN] 2019/03/14 - 15:52:10 | 200 |       3.486µs |             ::1 | GET      /long_async
2019/03/14 15:52:15 Done! in path /long_async
2019/03/14 15:53:05 Done! in path /long_sync
[GIN] 2019/03/14 - 15:53:05 | 200 |   5.00426148s |             ::1 | GET      /long_sync

/long_async를 호출하면 아래와 같이 15:52:10초에 response가 리턴되고 groutine은 그보다 5초 후인 15:52:15에 종료됩니다.

[GIN] 2019/03/14 - 15:52:10 | 200 |       3.486µs |             ::1 | GET      /long_async

2019/03/14 15:52:15 Done! in path /long_async

/long_sync의 경우 5초를 기다린 다음 response가 리턴됩니다.

2019/03/14 15:53:05 Done! in path /long_sync

[GIN] 2019/03/14 - 15:53:05 | 200 |   5.00426148s |             ::1 | GET      /long_sync

비동기 처리에 사용하면 좋습니다.