Server

package main

import (
    "fmt"
    "net/http"
    // _ "net/http/pprof"

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

func main() {
    gin.SetMode(gin.ReleaseMode)
    r := gin.Default()

    r.GET("/ping", func(c *gin.Context) {

        if c.Request.Header["User-Agent"][0] != "windows" {
            fmt.Println("请求头错误")
            c.JSON(http.StatusBadRequest, gin.H{})
        } else {
            reqIP := c.ClientIP()
            c.JSON(200, gin.H{
                "message": reqIP,
            })
        }
    })
    // go http.ListenAndServe("0.0.0.0:9999", nil)
    r.Run("0.0.0.0:56789")
}

Systemd 管理

vim /usr/lib/systemd/system/returnip.service

[Unit]
Description=Return IP Address

[Service]
Type=simple
Restart=always
RestartSec=5s
ExecStart=/usr/local/go-bin/returnIP
WorkingDirectory=/usr/local/go-bin

[Install]
WantedBy=multi-user.target

开启 pprof

pprof是Go的性能分析工具,在程序运行过程中,可以记录程序的运行信息,可以是CPU使用情况、内存使用情况、goroutine运行情况等,当需要性能调优或者定位Bug时候,这些记录的信息是相当重要。

去掉代码中注释

// _ "net/http/pprof"
// go http.ListenAndServe("0.0.0.0:9999", nil)

访问如下地址:
http://server:9999/debug/pprof/

图片.png

Client

package main

import (
    "encoding/json"
    "io"
    "log"
    "net"
    "net/http"
    "time"

    "github.com/robfig/cron/v3"
)

var ip string

func main() {
    // 运行次数
    num := 0

    // 设置时区
    local, err := time.LoadLocation("Asia/Shanghai")

    // Windows server 2022 服务器无法使用上面代码初始化时区
    // 使用下面代码初始化时区
    if err != nil {
        log.Println("初始化时区错误: ", err.Error())
        local = time.FixedZone("CST", 8*3600)
    }

    interval := cron.New(cron.WithLocation(local), cron.WithSeconds()) // 设置时区并且精度按秒。

    // timeout示例,写法v1的相同:
    // 每12s运行一次:"@every 12s" 或 "*/12 * * * * *"
    // 每分钟的第0s执行一次:"0 */1 * * * *"
    timeout := "*/10 * * * * *"

    // intervalId, err := interval.AddFunc(timeout, func() {
    // 	num++
    // 	log.Println("全局定时器已开启=num=", num)

    // })

    intervalId, err := interval.AddFunc(timeout, httpRequest)

    if err != nil {
        log.Println("全局定时器报错", "\n error=", err, "\n num=", num)
        return
    }

    log.Println("全局定时器已开启,Id=", intervalId)
    interval.Start()

    //关闭着计划任务, 但是不能关闭已经在执行中的任务.
    defer interval.Stop()
    select {} // 阻塞主线程而不退出
}

func httpRequest() {
    url := "http://server:56789/ping"
    request, err := http.NewRequest("GET", url, nil)
    if err != nil {
        log.Panicln(err.Error())
    }
    // 设置请求投
    request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
    request.Header.Add("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")
    request.Header.Add("Connection", "keep-alive")
    // request.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36")
    request.Header.Add("User-Agent", "windows")

    client := http.Client{}
    // Do sends an HTTP request and returns an HTTP response
    // 发起一个HTTP请求,返回一个HTTP响应
    resp, err := client.Do(request)

    if resp.StatusCode != http.StatusOK {
        log.Println("访问错误")
        return
    }

    if err != nil {
        // log.Panicln("请求错误")
        log.Println("请求错误")
        return
    }

    // 关闭 resp,避免内存泄漏
    defer resp.Body.Close()

    var result struct {
        Message string `json:"message"`
    }

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        // log.Panicln("返回结果错误")
        log.Println("返回结果错误")
        return
    }

    err = json.Unmarshal(body, &result)
    if err != nil {
        // log.Panicln("IP 序列化错误")
        log.Println("IP 序列化错误")
        return
    }

    tmpIP := result.Message

    address := net.ParseIP(tmpIP)

    if address == nil {
        // log.Panicln("IP 地址不合法")
        log.Println("IP 地址不合法")
        return
    } else {
        if tmpIP != ip {
            ip = result.Message
            log.Println("IP 已经改变:", result.Message)
        } else {
            return
        }
    }
}