package main
import (
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)
var (
g errgroup.Group
)
func route01() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"error": "wellcome server 01",
},
)
})
return e
}
func router02() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"error": "wellcome server02",
},
)
})
return e
}
func main() {
server01 := &http.Server{
Addr: ":8080",
Handler: route01(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server02 := &http.Server{
Addr: ":8081",
Handler: router02(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
g.Go(func() error {
return server01.ListenAndServe()
})
g.Go(func() error {
return server02.ListenAndServe()
})
if err := g.Wait(); err != nil {
log.Fatal("the err is ", err)
}
}
轻量级:goroutine 是非常轻量级的,它的创建和切换的开销都非常小。一个 Go 程序可以同时运行数十万甚至数百万个 goroutine,而在大多数操作系统上,同时运行这么多的线程是不可能的。
内存占用:每个 goroutine 的堆栈大小开始时非常小(只有几 KB),并且可以根据需要增长和缩小,而线程的堆栈大小通常是固定的,通常为几 MB。
调度:goroutine 的调度是由 Go 运行时进行的,而不是由操作系统进行的。这意味着 Go 运行时可以更精细地控制 goroutine 的执行,例如,当一个 goroutine 在等待 I/O 操作时,Go 运行时可以运行其他 goroutine。
并发编程模型:Go 语言提供了 channel 和 select 语句,使得在 goroutine 之间进行通信和同步变得非常简单。这是一种不同于传统的共享内存并发模型的并发编程模型,它更易于理解和使用。