2024-08-19



package main
 
import (
    "fmt"
    "net"
    "net/http"
)
 
func getClientIP(r *http.Request) string {
    // 尝试从标准HTTP头中获取IP地址
    ip := r.Header.Get("X-Forwarded-For")
    if ip == "" || ip == "unknown" {
        ip = r.Header.Get("X-Real-IP")
    }
    if ip == "" || ip == "unknown" {
        host, _, err := net.SplitHostPort(r.RemoteAddr)
        if err == nil {
            ip = host
        }
    }
    return ip
}
 
func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        ip := getClientIP(r)
        fmt.Fprintf(w, "客户端IP地址: %s\n", ip)
    })
 
    http.ListenAndServe(":8080", nil)
}

这段代码定义了一个getClientIP函数,用于从HTTP请求中获取客户端的IP地址。它首先检查了两个自定义HTTP头X-Forwarded-ForX-Real-IP,如果这些头不存在,则直接从r.RemoteAddr中提取IP地址。在main函数中,它启动了一个简单的HTTP服务器,并定义了一个处理函数,该处理函数使用getClientIP函数来响应客户端的请求,显示其IP地址。

2024-08-19

由于篇幅限制,我们将提供两种语言的核心特性对比,例如并发模型、内存管理、语法风格等。

Go 语言特性:

  • 并发与并行: goroutine 轻量级线程, 通过 channel 进行通信。
  • 内存管理: 自动垃圾回收, 智能指针, 内存安全。
  • 函数式编程: 首类函数, 高阶函数, 匿名函数。
  • 语法特点: 结构体字面量, 多重赋值, 可选逗号等。

Java 语言特性:

  • 类型安全: 强类型语言, 编译时检查。
  • 内存管理: 垃圾回收, 自动内存管理。
  • 并发编程: 同步原语如 synchronized, 并发集合。
  • 面向对象: 类继承, 多态, 封装。

代码示例对比(Go 与 Java):

Go 语言:




package main
 
import "fmt"
 
func main() {
    go func() { // 并发的匿名函数
        fmt.Println("Hello from goroutine!")
    }()
 
    var x, y int = 10, 20 // 多变量声明
    fmt.Println(x, y)
}

Java 语言:




public class Main {
    public static void main(String[] args) {
        new Thread(() -> { // Lambda 表达式作为并发任务
            System.out.println("Hello from thread!");
        }).start();
 
        int x = 10; // 单变量声明
        int y = 20;
        System.out.println(x + " " + y); // 注意字符串拼接
    }
}

这两个简单的例子展示了 Go 和 Java 在表达并发、变量声明以及打印输出方面的语法差异。虽然语言特性有所不同,但它们都是现代编程语言,提供了内存安全、并发支持等优秀特性,适用于不同的应用场景。

2024-08-19

在Go语言中,包的管理主要通过go get, go list, go mod tidy等命令来完成。

  1. 安装包:



go get package-path
  1. 查看包信息:



go list -json package-path
  1. 删除包:



go clean -i package-path

如果你遇到了删除后依然存在的问题,可能是由于以下原因:

  • 包被其他模块依赖,导致无法正常删除。
  • 包被系统缓存,位于$GOPATH/pkg$GOMODCACHE中。

针对这种情况,你可以尝试以下方法:

  1. 删除包缓存:



go clean -modcache
  1. 删除$GOPATH/pkg$GOMODCACHE中的包:



rm -rf $GOPATH/pkg/mod
rm -rf $GOPATH/pkg/cache
  1. 使用go mod tidy清理不再需要的模块和依赖项:



go mod tidy
  1. 重新编译项目:



go build

确保在执行这些操作前,你已经处理好所有依赖关系,避免意外删除其他模块所需的包。

2024-08-19



package main
 
import (
    "fmt"
    "net/http"
    "os"
    "strings"
    "sync"
)
 
var (
    rootURL = "http://example.com"
    wg      sync.WaitGroup
    seen    = make(map[string]bool)
    mu      sync.Mutex
)
 
func crawl(url string, depth int, fetcher Fetcher) {
    if depth <= 0 {
        return
    }
    body, urls, err := fetcher.Fetch(url)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("found: %s %q\n", url, body)
    for _, u := range urls {
        if strings.HasPrefix(u, "http") {
            u = strings.TrimPrefix(u, "/")
        }
        if !seen[u] {
            seen[u] = true
            wg.Add(1)
            go crawl(u, depth-1, fetcher)
        }
    }
    wg.Done()
}
 
type Fetcher interface {
    Fetch(url string) (body string, urls []string, err error)
}
 
func (f *httpFetcher) Fetch(url string) (string, []string, error) {
    resp, err := http.Get(url)
    if err != nil {
        return "", nil, err
    }
    if resp.StatusCode != http.StatusOK {
        return "", nil, fmt.Errorf("bad status code: %s", resp.Status)
    }
    defer resp.Body.Close()
    bodyBytes, err := os.ReadAll(resp.Body)
    if err != nil {
        return "", nil, fmt.Errorf("os read error: %v", err)
    }
    bodyStr := string(bodyBytes)
    urls, err := extractUrls(bodyStr)
    if err != nil {
        return "", nil, err
    }
    return bodyStr, urls, nil
}
 
func extractUrls(s string) ([]string, error) {
    // 这里应该实现一个正则表达式来提取URLs
    return []string{}, nil
}
 
type httpFetcher struct{}
 
func main() {
    wg.Add(1)
    go crawl(rootURL, 4, &httpFetcher{})
    wg.Wait()
}

这个代码实例提供了一个简化的网络爬虫实现,使用Go语言编写。它定义了一个crawl函数,该函数递归地访问网页,并通过Fetcher接口来获取页面内容和页面中的链接,以便进一步爬取。httpFetcher结构体实现了Fetcher接口,通过HTTP协议获取网页内容。这个例子省略了URL提取的部分,你需要根据实际情况实现一个能够从网页内容中提取URLs的算法。

2024-08-19



package main
 
import (
    "fmt"
    "net/http"
    "time"
 
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)
 
func main() {
    e := echo.New()
 
    // 注册中间件以记录请求的处理时间
    e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
        Skipper: func(c echo.Context) bool {
            // 跳过对 /metrics 路径的日志记录
            return c.Path() == "/metrics"
        },
        Format: `{"time":"${time_rfc3339}","id":"${id}","remote_ip":"${remote_ip}","host":"${host}",` +
            `"method":"${method}","uri":"${uri}","status":${status},"error":"${error}","latency":${latency},` +
            `"latency_human":"${latency_human}","bytes_in":${bytes_in},"bytes_out":${bytes_out}}` + "\n",
    }))
 
    // 注册一个路由处理函数
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })
 
    // 启动服务器
    server := &http.Server{
        Addr:         ":8080",
        Handler:      e,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
    }
 
    e.Logger.Fatal(server.ListenAndServe())
}

这段代码使用了Echo web框架来创建一个简单的HTTP服务器,并使用middleware.LoggerWithConfig中间件记录请求的处理时间。它展示了如何配置中间件来跳过对特定路径(例如/metrics)的日志记录,并自定义日志格式。这是一个实践中的例子,展示了如何将日志记录集成到Web应用程序中。

2024-08-19



package main
 
import (
    "fmt"
    "github.com/juju/ratelimit"
    "time"
)
 
// 令牌桶限流器示例
func main() {
    // 创建一个每秒填充5个令牌的令牌桶限流器
    limiter := ratelimit.NewBucket(5, 1*time.Second)
 
    // 尝试获取令牌
    for i := 0; i < 10; i++ {
        time.Sleep(200 * time.Millisecond) // 为了更好地观察,在获取令牌前等待200毫秒
        if limiter.TakeAvailable(1) {
            fmt.Printf("Request allowed: %d\n", i)
        } else {
            fmt.Printf("Request rejected: %d\n", i)
        }
    }
}

这段代码演示了如何使用ratelimit库中的NewBucket函数创建一个令牌桶限流器,并通过TakeAvailable方法尝试获取令牌。在每次循环中,程序会等待200毫秒,然后尝试从桶中获取一个令牌。如果获取成功,则允许请求,否则拒绝请求。这种方式可以帮助我们控制数据流量和防止峰值流量打垮系统。

2024-08-19



package main
 
import (
    "fmt"
    "sync"
)
 
// 使用sync.Once实现单例模式
type singleton struct{}
 
var once sync.Once
var instance *singleton
 
func GetInstance() *singleton {
    return instance
}
 
func init() {
    instance = &singleton{}
}
 
func (s *singleton) Initialize() {
    // 初始化代码
    fmt.Println("Initialized singleton")
}
 
func main() {
    // 安全地调用初始化方法,只会执行一次
    once.Do(GetInstance().Initialize)
    // 程序其他部分...
}

这段代码展示了如何使用sync.Once来确保单例模式中的初始化代码只执行一次,即便面临多个goroutine同时调用。这是一个在Go中实现单例模式的简单方法,同时也演示了sync.Once的用法。

2024-08-19

在CentOS 7上使用yum安装Go语言环境的步骤如下:

  1. 首先,你需要添加Go的官方仓库。创建一个新的repo文件:



sudo tee /etc/yum.repos.d/golang.repo<<EOF
[golang]
name=Go Repository
baseurl=https://mirrors.aliyun.com/golang/yum/el7/
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/golang/yum/el7/gpgkey
EOF
  1. 导入Go的公钥:



sudo rpm --import https://mirrors.aliyun.com/golang/yum/el7/gpgkey
  1. 接下来,你可以使用yum安装Go语言:



sudo yum install golang
  1. 安装完成后,你可以检查Go的版本确认安装成功:



go version
  1. 设置环境变量。编辑你的shell配置文件(比如~/.bash_profile~/.bashrc),添加以下内容:



export GOROOT=/usr/lib/golang
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
  1. 更新你的shell配置使其生效:



source ~/.bash_profile
# 或者
source ~/.bashrc

完成以上步骤后,Go语言应该已经成功安装并配置在你的CentOS 7系统上。

2024-08-19



package main
 
import (
    "context"
    "fmt"
    "log"
    "time"
 
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)
 
func main() {
    clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
    client, err := mongo.Connect(context.Background(), clientOptions)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(context.Background())
 
    collection := client.Database("testdb").Collection("testcollection")
 
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
 
    // 插入文档
    insertResult, err := collection.InsertOne(ctx, bson.D{{"name", "John Doe"}, {"age", 30}})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("文档插入成功,ID: %v\n", insertResult.InsertedID)
 
    // 查询单个文档
    var result bson.M
    err = collection.FindOne(ctx, bson.D{{"name", "John Doe"}}).Decode(&result)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("查询结果: %v\n", result)
 
    // 更新文档
    updateResult, err := collection.UpdateOne(ctx, bson.D{{"name", "John Doe"}}, bson.D{{"$set", bson.D{{"age", 31}}}})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("文档更新成功,影响行数: %v\n", updateResult.MatchedCount)
 
    // 删除文档
    deleteResult, err := collection.DeleteOne(ctx, bson.D{{"name", "John Doe"}})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("文档删除成功,删除行数: %v\n", deleteResult.DeletedCount)
}

这段代码展示了如何使用MongoDB的Go语言驱动程序来连接到MongoDB实例,对一个集合进行插入、查询、更新和删除操作。代码简洁,注重于展示核心功能,并包含了错误处理。

2024-08-19

Golang的GMP模型是指Go程序运行时的内存管理、垃圾回收和并发调度。

  1. 内存管理:

    • 使用Tcmalloc替代glibc的malloc实现,减少内存分配的锁竞争。
    • 对象分配器,用于高效管理小对象。
    • 三色标记清除垃圾回收算法。
  2. 调度:

    • M: OS线程,负责执行Go代码。
    • P: 代表处理器,负责调度M和管理任务队列。
    • G: Go程,即Go语言中的goroutine。

GMP模型调度流程示例代码:




// 创建一个新的Goroutine
go func() {
    // 你的代码
}()
 
// 创建P
p := newP()
 
// 创建M
m := newM()
 
// 将新创建的M和P绑定
newm(m, p)
 
// 执行Goroutine
execute(gp)

在实际的Golang运行时中,这些操作都是隐式进行的,无需手动干预。开发者只需编写Go代码并让Go运行时负责管理内存和调度goroutine即可。