c语言编程笔录

首页 >   > 笔记大全

笔记大全

怎么在Golang中优雅地关闭HTTP服务器

更新时间:2023-10-26

前言:

在使用Golang编写HTTP服务器时,我们需要确保服务器在关闭的时候能够优雅地停止,这样可以确保正在处理的请求能够完成而不会丢失数据或造成服务器异常。本文将介绍如何在Golang中优雅地关闭HTTP服务器。

第一段:使用http.Shutdown方法

Golang标准库中的http.Server结构体提供了Shutdown方法,可以用于优雅地关闭HTTP服务器。当我们收到关闭信号时,可以调用该方法来停止服务器的监听并等待所有正在处理的请求处理完毕。

package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	// 创建一个http.Server实例
	server := &http.Server{
		Addr:    ":8080",
		Handler: nil, // 自定义处理逻辑
	}

	// 启动HTTP服务器
	go func() {
		err := server.ListenAndServe()
		if err != nil && err != http.ErrServerClosed {
			log.Fatal("HTTP 服务器启动失败:", err)
		}
	}()

	// 监听系统信号
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	// 等待关闭信号
	<-signalChan
	fmt.Println("收到关闭信号,开始关闭服务器...")

	// 关闭HTTP服务器
	err := server.Shutdown(nil)
	if err != nil {
		log.Fatal("HTTP 服务器关闭失败:", err)
	}

	fmt.Println("服务器已关闭")
}

第二段:使用context实现超时控制

除了使用Shutdown方法,我们还可以使用context包实现超时控制,确保在关闭HTTP服务器时等待请求的最长时间。

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func main() {
	// 创建一个http.Server实例
	server := &http.Server{
		Addr:    ":8080",
		Handler: nil, // 自定义处理逻辑
	}

	// 启动HTTP服务器
	go func() {
		err := server.ListenAndServe()
		if err != nil && err != http.ErrServerClosed {
			log.Fatal("HTTP 服务器启动失败:", err)
		}
	}()

	// 监听系统信号
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	// 等待关闭信号
	<-signalChan
	fmt.Println("收到关闭信号,开始关闭服务器...")

	// 创建一个超时上下文
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// 关闭HTTP服务器
	err := server.Shutdown(ctx)
	if err != nil {
		log.Fatal("HTTP 服务器关闭失败:", err)
	}

	fmt.Println("服务器已关闭")
}

第三段:处理未完成的请求

在关闭HTTP服务器时,可能还有一些请求正在处理中,为了确保这些请求能够顺利完成,我们可以使用sync.WaitGroup来等待这些请求的处理完毕。

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"
)

func main() {
	// 创建一个http.Server实例
	server := &http.Server{
		Addr:    ":8080",
		Handler: nil, // 自定义处理逻辑
	}

	// 启动HTTP服务器
	go func() {
		err := server.ListenAndServe()
		if err != nil && err != http.ErrServerClosed {
			log.Fatal("HTTP 服务器启动失败:", err)
		}
	}()

	// 监听系统信号
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	// 声明一个等待组
	var wg sync.WaitGroup

	// 用来表示服务器是否已关闭
	var serverClosed bool

	go func() {
		// 等待关闭信号
		<-signalChan
		fmt.Println("收到关闭信号,开始关闭服务器...")

		// 设定服务器已关闭标志
		serverClosed = true

		// 创建一个超时上下文
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()

		// 关闭HTTP服务器
		err := server.Shutdown(ctx)
		if err != nil {
			log.Fatal("HTTP 服务器关闭失败:", err)
		}

		fmt.Println("服务器已关闭")
	}()

	// 处理请求
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// 如果服务器已关闭,则返回503 Service Unavailable
		if serverClosed {
			http.Error(w, "服务器已关闭", http.StatusServiceUnavailable)
			return
		}

		// 处理请求...

		// 完成请求处理前,增加等待组的计数
		wg.Add(1)
		defer wg.Done()

		// 处理请求...
	})

	// 等待所有请求处理完毕
	wg.Wait()
}

总结:

在Golang中优雅地关闭HTTP服务器是编写高质量服务器的重要部分。本文介绍了两种常见的方法,一种是使用http.Shutdown方法来优雅地关闭服务器,另一种是使用context包来实现超时控制。我们还讨论了如何处理未完成的请求,以确保这些请求能够顺利完成。通过使用这些方法,我们可以确保服务器在关闭时不丢失数据,并保持服务器的稳定性和可靠性。