Web 服务,直接重启会导致重启时间段的请求丢失,一个比较简便的优化方式是使用:greuse
地址:https://github.com/gogf/greuse
这个包工作的原理基于 SO_REUSEADDR and SO_REUSEPORT
可以同时启动多个程序来监听相同端口,当不同来路的请求到达后,内核会把请求“均衡负载”到多个实例上。
不中断的更新服务的流程:两个运行中的实例,先停止一个,替换程序包,启动新的实例,再重启旧的实例,完成更新。
关于 Socket 端口复用的介绍文章很多,摘录一些知识点:
From: 🔗socket 端口复用 SO_REUSEPORT 与 SO_REUSEADDR
- 内核层面实现负载均衡。
- 负载均衡算法:使用
(remote_ip, remote_port, local_ip, local_port)
来进行哈希,因此可以保证同一个client的包可以路由到同一个进程。- 有新连接建立时,内核只会唤醒一个进程来accept,并且保证唤醒的均衡性。
- 安全性:第一个进程必须 enable 了这个选项之后,后续的进程才可以通过 enable 这个选项将socket绑定到同一个端口上。
- 安全性:绑定到同一个端口的进程的 Effective user id 必须一致。
基础示例
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!\n")
})
http.ListenAndServe(":8080", nil)
}
使用 Greuse 改造
package main
import (
"fmt"
"net/http"
"os"
"github.com/gogf/greuse"
)
func main() {
listener, err := greuse.Listen("tcp", ":8881")
if err != nil {
panic(err)
}
defer listener.Close()
server := &http.Server{}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "gid: %d, pid: %d\n", os.Getgid(), os.Getpid())
})
panic(server.Serve(listener))
}
Iris 框架基础示例
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.New()
root := app.Party("/")
{
root.Use(iris.Compression)
root.Get("/", func(ctx iris.Context) {
ctx.Writef("Hello World!\n")
})
}
app.Listen(":8080")
}
Iris 框架使用 Greuse 改造
package main
import (
"fmt"
"github.com/gogf/greuse"
"github.com/kataras/iris/v12"
)
func main() {
app := iris.New()
root := app.Party("/")
{
root.Use(iris.Compression)
root.Get("/", func(ctx iris.Context) {
fmt.Fprintf(ctx, "gid: %d, pid: %d\n", os.Getgid(), os.Getpid())
})
}
listener, err := greuse.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
if err := app.Run(iris.Listener(listener)); err != nil {
fmt.Println("Failed to start web server")
}
}
参考: