2024-08-23



package main
 
import (
    "fmt"
    "github.com/Shopify/sarama"
)
 
// 生产者
func NewAsyncProducer(addrs []string, topic string) (sarama.AsyncProducer, error) {
    config := sarama.NewConfig()
    config.Producer.RequiredAcks = sarama.WaitForAll
    config.Producer.Partitioner = sarama.NewRandomPartitioner
    config.Producer.Return.Successes = true
 
    producer, err := sarama.NewAsyncProducer(addrs, config)
    if err != nil {
        return nil, err
    }
 
    // 处理成功投递的消息
    go func(p sarama.AsyncProducer) {
        for suc := range p.Successes() {
            fmt.Printf("消息成功投递到分区 %d 偏移量 %d\n", suc.Partition, suc.Offset)
        }
    }(producer)
 
    return producer, nil
}
 
// 消费者
func NewConsumer(addrs []string, topic string, partition int32) (sarama.Consumer, error) {
    consumer, err := sarama.NewConsumer(addrs, nil)
    if err != nil {
        return nil, err
    }
 
    partitionConsumer, err := consumer.ConsumePartition(topic, partition, sarama.OffsetNewest)
    if err != nil {
        return nil, err
    }
 
    // 处理消息
    go func(pc sarama.PartitionConsumer) {
        for msg := range pc.Messages() {
            fmt.Printf("消息分区 %d 偏移量 %d 值: %s\n", msg.Partition, msg.Offset, string(msg.Value))
        }
    }(partitionConsumer)
 
    return consumer, nil
}
 
func main() {
    // Kafka 集群地址
    addrs := []string{"127.0.0.1:9092"}
    // 主题
    topic := "my_topic"
 
    // 创建异步生产者
    producer, err := NewAsyncProducer(addrs, topic)
    if err != nil {
        panic(err)
    }
 
    // 创建消费者
    consumer, err := NewConsumer(addrs, topic, int32(0))
    if err != nil {
        panic(err)
    }
    defer func() {
        consumer.Close()
    }()
 
    // 生产者发送消息
    msg := &sarama.ProducerMessage{
        Topic: topic,
        Value: sarama.StringEncoder("Hello Kafka!"),
    }
    producer.Input() <- msg
 
    // 等待程序结束
    // 实际应用中,这里可能需要一个优雅退出的逻辑
    select {}
}

这段代码首先定义了一个异步生产者的创建函数NewAsyncProducer,它配置了生产者,并且启动了一个goroutine来处理成功投递的消息。然后定义了一个消费者的创建函数NewConsumer,它启动了一个goroutine来处理接收到的消息。最后,在main函数中创建了生产者和消费者,并发送了一条消息。这个例子展示了如何使用sarama包来连接Kafka并进行消息的生产和消费。

2024-08-23



package main
 
import (
    "fmt"
    "github.com/gorilla/websocket"
    "github.com/kward/rdpgo"
    "log"
)
 
func main() {
    // 创建一个新的rdpgo客户端
    client, err := rdpgo.NewClient("127.0.0.1:3389", "username", "password", nil)
    if err != nil {
        log.Fatalf("创建RDP客户端失败: %s", err)
    }
 
    // 连接到RDP服务器
    err = client.Connect()
    if err != nil {
        log.Fatalf("连接到RDP服务器失败: %s", err)
    }
 
    // 获取rdpgo的Websocket连接
    conn := client.GetConn()
 
    // 模拟一个简单的服务器,接收来自RDP服务器的消息
    for {
        _, message, err := conn.ReadMessage()
        if err != nil {
            log.Println("读取消息时发生错误:", err)
            return
        }
        fmt.Printf("收到消息: %s\n", message)
    }
}

这个代码示例展示了如何使用rdpgo库创建一个RDP客户端并连接到RDP服务器。它还演示了如何使用gorilla/websocket库来处理从RDP服务器接收到的消息。这个例子简单且直接,对于需要在Go语言中实现与RDP协议交互的开发者来说,是一个很好的学习资源。

2024-08-23



package main
 
import (
    "fmt"
    "github.com/spf13/cobra"
    "os"
)
 
// 定义一个全局的命令对象
var rootCmd = &cobra.Command{
    Use:   "app",
    Short: "一个简单的Cobra应用程序",
    Long:  `这是一个使用Cobra库构建的命令行应用程序的示例。`,
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("Hello, World!")
    },
}
 
func main() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

这段代码演示了如何使用cobra库创建一个简单的命令行应用程序。在这个例子中,我们定义了一个全局的cobra命令对象rootCmd,并设置了它的使用方法、短描说和长描述。当命令被执行时,它会打印一个问候语。如果执行过程中出现错误,它会将错误信息输出到标准错误并以非零状态退出程序。

2024-08-23

Go的pprof包确实非常有用,它提供了运行时的性能分析功能。然而,在某些情况下,可能会不小心或者因为错误配置导致pprof服务开放在公网上,从而引发安全漏洞。

为了规避这个问题,可以采取以下措施:

  1. 禁用默认的pprof端点:

    默认情况下,Go的net/http/pprof包会在localhost:6060上自动注册web接口。为了安全起见,可以禁止对外暴露这个接口。




import (
    "net/http"
    _ "net/http/pprof" // 引入pprof包,但不导出任何内容
)
 
func main() {
    // 不使用http.ListenAndServe("localhost:6060", nil)
    // 因为pprof包已经默认绑定到了这个地址,但是没有被导出
}
  1. 手动绑定pprof到安全的地址:

    如果你需要使用pprof进行调试,可以手动绑定到一个安全的地址上。




import (
    "net/http"
    "net/http/pprof"
)
 
func main() {
    http.ListenAndServe("127.0.0.1:8081", nil) // 默认服务
    http.ListenAndServe("127.0.0.1:8082", nil) // pprof服务
 
    mux := http.NewServeMux()
    mux.HandleFunc("/debug/pprof/", pprof.Index)
    mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
    mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
    mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
    mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
    mux.Handle("/debug/pprof/block", pprof.Handler("block"))
    mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
    mux.Handle("/debug/pprof/heap", pprof.Handler("heap"))
    mux.Handle("/debug/pprof/mutex", pprof.Handler("mutex"))
    mux.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
 
    go http.ListenAndServe("localhost:8082", mux)
}
  1. 使用防火墙:

    在服务器上设置防火墙规则,仅允许特定的IP地址访问pprof服务。




# 示例iptables规则,仅允许本地访问pprof服务
iptables -A INPUT -p tcp -s 127.0.0.1 --dport 8082 -j ACCEPT
iptables -A INPUT -p tcp -s 0.0.0.0/0 --dport 8082 -j DROP
  1. 使用代理服务器:

    在pprof服务和外部世界之间放置一个代理服务器,代理服务器可以实现访问控制。

总结,要规避pprof引发的安全问题,可以通过禁用默认端点、手动绑定到安全地址、使用防火墙和代理服务器来实现。

2024-08-23

在Go语言中,你可以使用crypto/rsa包来进行RSA加密和解密。以下是一个简单的例子,展示了如何使用公钥进行签名(加密)和如何使用私钥进行验签(解密)。




package main
 
import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "fmt"
)
 
func main() {
    // 生成RSA密钥对
    privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        panic(err)
    }
    publicKey := &privateKey.PublicKey
 
    // 签名
    message := []byte("Hello, third-party API!")
    signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, message)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Signature: %x\n", signature)
 
    // 验签
    err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, message, signature)
    if err != nil {
        panic(err)
    }
    fmt.Println("Verification succeeded!")
}

在这个例子中,我们首先生成了一个RSA密钥对。然后,我们使用私钥对一个消息进行了签名,生成了加密的签名。最后,我们使用公钥验证了这个签名,确保消息的完整性。

请注意,这个例子是为了演示目的而简化的。在实际应用中,密钥的生成和管理应该更加安全,密钥可能会从外部源(例如密钥管理服务)加载,签名和验签的过程可能会更加复杂,涉及到与第三方接口的协议约定。

2024-08-23

以下是一个简化的代码实例,展示了如何在Go中使用Gin框架和Vue 3创建一个简单的博客系统。

Go (Gin + GORM):




package main
 
import (
    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/binding"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)
 
type Post struct {
    gorm.Model
    Title   string
    Content string
}
 
func main() {
    r := gin.Default()
    db, err := gorm.Open(sqlite.Open("blog.db"), &gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }
    db.AutoMigrate(&Post{})
 
    // 创建博客
    r.POST("/posts", func(c *gin.Context) {
        var post Post
        if err := c.ShouldBindBodyWith(&post, binding.JSON); err != nil {
            c.JSON(400, gin.H{"error": err.Error()})
            return
        }
        db.Create(&post)
        c.JSON(200, post)
    })
 
    // 获取博客列表
    r.GET("/posts", func(c *gin.Context) {
        var posts []Post
        db.Find(&posts)
        c.JSON(200, posts)
    })
 
    r.Run()
}

Vue 3 (Vue CLI + Axios):




<template>
  <div>
    <input v-model="post.title" placeholder="Title" />
    <textarea v-model="post.content" placeholder="Content"></textarea>
    <button @click="createPost">Create Post</button>
  </div>
</template>
 
<script>
import axios from 'axios';
 
export default {
  data() {
    return {
      post: {
        title: '',
        content: ''
      }
    };
  },
  methods: {
    async createPost() {
      try {
        const response = await axios.post('http://localhost:8080/posts', this.post);
        console.log(response.data);
      } catch (error) {
        console.error(error);
      }
    }
  }
};
</script>

这个简单的例子展示了如何使用Gin和GORM在Go端创建RESTful API,以及如何在Vue 3中使用Axios与该API进行交互。这个系统可以扩展,添加更多的功能,比如用户认证、分页、搜索等。

2024-08-23



package main
 
import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"
)
 
// 优雅地重启服务的示例函数
func gracefulRestart(ctx context.Context) error {
    // 创建系统信号监听器,监听中断(interrupt)信号
    stop := make(chan os.Signal, 1)
    signal.Notify(stop, syscall.SIGINT) // 当按下Ctrl+C时,会发送SIGINT信号
 
    // 创建一个超时上下文,当服务在2s内没有正常关闭时会强制退出
    var cancel context.CancelFunc
    ctx, cancel = context.WithTimeout(ctx, 2*time.Second)
    defer cancel()
 
    // 阻塞等待接收信号
    <-stop
 
    // 在这里可以添加代码来关闭外部资源(如数据库连接、清理文件等)
    // 这里只是简单打印信息
    fmt.Println("服务正在优雅地关闭...")
 
    // 返回nil表示不需要进行错误处理,因为优雅关闭不应该有错误返回
    return nil
}
 
func main() {
    // 创建一个空的上下文
    ctx := context.Background()
 
    // 调用优雅重启函数
    if err := gracefulRestart(ctx); err != nil {
        fmt.Println("优雅关闭失败:", err)
        os.Exit(1)
    }
 
    fmt.Println("优雅关闭成功")
    os.Exit(0)
}

这段代码演示了如何在Go语言中实现一个简单的优雅重启逻辑。它创建了一个监听系统中断信号(如Ctrl+C)的goroutine,并设置了一个超时上下文,以确定优雅关闭的最后期限。在实际应用中,你可以在gracefulRestart函数中添加关闭数据库连接、清理资源等逻辑。

2024-08-23



package main
 
import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)
 
// 假设我们有一个API的请求结构体
type ApiRequest struct {
    Field1 string `json:"field1"`
    Field2 int    `json:"field2"`
}
 
// 假设我们有一个API的响应结构体
type ApiResponse struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
}
 
func main() {
    // 创建API请求对象
    request := ApiRequest{
        Field1: "value1",
        Field2: 123,
    }
 
    // 序列化请求对象为JSON格式
    requestBody, err := json.Marshal(request)
    if err != nil {
        panic(err)
    }
 
    // 发起POST请求到第三方API
    resp, err := http.Post("https://api.example.com/endpoint", "application/json", bytes.NewBuffer(requestBody))
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
 
    // 读取响应体
    responseBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }
 
    // 反序列化响应体到响应对象
    var response ApiResponse
    err = json.Unmarshal(responseBody, &response)
    if err != nil {
        panic(err)
    }
 
    // 输出API响应结果
    fmt.Printf("API Response: %+v\n", response)
}

这段代码展示了如何在Go中创建一个HTTP POST请求,发送JSON格式的数据到第三方API,并处理返回的JSON响应。代码中包含了错误处理,确保在发生任何错误时程序不会崩溃。

2024-08-23



package main
 
import (
    "fmt"
    "time"
)
 
func worker(id int, c chan int) {
    for n := range c {
        fmt.Printf("Worker %d received %d\n", id, n)
        time.Sleep(time.Second)
    }
}
 
func main() {
    const numWorkers = 3
    const numMessages = 5
 
    workers := make([]chan int, numWorkers)
 
    for i := range workers {
        workers[i] = make(chan int)
        go worker(i, workers[i])
    }
 
    for i := 0; i < numMessages; i++ {
        time.Sleep(time.Second)
        fmt.Printf("Master sending %d\n", i)
        for _, worker := range workers {
            worker <- i
        }
    }
 
    for _, worker := range workers {
        close(worker)
    }
 
    fmt.Scanln() // 阻塞主goroutine,防止程序立即退出
}

这段代码修复了原始代码中的问题,并提供了一个简单的Go程序示例,它创建了一个工作池,并向每个工作程序平均分配了任务。每个工作程序在自己的goroutine中运行,并且主goroutine会等待用户输入来防止程序立即退出。这个例子展示了Go语言中协程、通道和工作池模式的基本使用方法。

2024-08-23



# 安装并配置内网穿透工具 - 以 `frp` 为例
# 下载 frp 到你的 Windows 系统
frp_windows_amd64.zip (https://github.com/fatedier/frp/releases)
# 解压缩
 
# 配置 frps.ini
[common]
bind_port = 7000
 
# 启动 frps
./frps -c frps.ini
 
# 在你的 Linux 服务器上安装 frpc
# 下载 frp 到你的 Linux 服务器
frp_linux_amd64.zip (https://github.com/fatedier/frp/releases)
# 解压缩
 
# 配置 frpc.ini
[common]
server_addr = <你的 Windows IP>
server_port = 7000
 
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000
 
[tunnel]
type = tcp
remote_port = 7001
local_ip = 127.0.0.1
local_port = 6333
 
# 启动 frpc
./frpc -c frpc.ini
 
# 在 GoLand 中配置 SSH 远程开发环境
# 打开 GoLand,选择 `File` > `Settings` > `Build, Execution, Deployment` > `Deployment`
# 点击 `+` 添加一个新的部署配置,选择 `SFTP`
# 填写你的服务器信息,用户名、主机、私钥等
# 在 `Mappings` 中配置本地目录与远程服务器目录的映射
# 保存配置并测试连接
 
# 配置端口转发,以便于 GoLand 可以通过内网穿透连接到你的开发环境
# 在 frpc.ini 中添加如下配置
[goland_debug_server]
type = tcp
local_ip = 127.0.0.1
local_port = 6333
remote_port = 6333
 
# 重启 frpc 使配置生效
 
# 在 GoLand 中配置远程解释器
# 选择 `File` > `Settings` > `Languages & Frameworks` > `Go` > `Go Modules (vgo)` 或 `Go SDK`
# 在 `SSH Interpreter` 中配置你的 SSH 连接信息,选择你刚才创建的部署配置
# 保存设置并重新启动 GoLand
 
# 现在你可以在 GoLand 中远程编辑、编译和调试 Linux 服务器上的 Go 代码了

这个例子展示了如何使用内网穿透工具 frp 将你的本地开发环境与远程的 Linux 服务器连接起来,并在 GoLand 中配置相关设置以实现远程开发和调试。这样可以在没有公网 IP 的情况下进行开发工作,特别适合家庭、办公室网络环境或在教育、个人项目中使用。