go 的defer 执行顺序

作者: 时间: 2026-08-11 评论: 暂无评论
package main

import "fmt"

// 核心规则4
// return 语句不是原子操作,它分为两步:

//第一步:将返回值(若有命名返回值则直接赋值,若无则创建临时变量)赋值为 return 后面的表达式。

//第二步:执行所有已注册的 defer 函数(后注册先执行)。

//第三步:函数真正返回。

//命名返回值(如 (r int))在函数开始时就已声明并初始化为零值,return 语句会先给这个变量赋值。

// defer 函数的参数在 defer 语句被执行到时就已经求值(值拷贝),而 defer 函数体内部对变量的修改是否影响外部,取决于它捕获的是变量本身(闭包)还是副本(参数)。

func f1() (r int) {
    defer func() {
        r++
    }()
    return 0 //r=0 再执行 defer  r=1
}

func f2() (r int) {
    t := 5
    defer func() {
        t = t + 5
    }()
    return t // 创建临时变量=5  t+5无效
}

func f3() (r int) {
    defer func(r int) {
        r = r + 5
    }(r)
    return 1 // r=1  defer 使用拷贝了一份,使用内部变量
}

func f4() (r int) {
    defer func() {
        r = r + 5
    }()
    return 1 // r=1  defer后=6
}

func main() {
    fmt.Println(f1()) //1
    fmt.Println(f2()) //5
    fmt.Println(f3()) //1
    fmt.Println(f4()) //6

}

GO多态

作者: 时间: 2026-08-11 评论: 暂无评论

1.多态是什么
通俗讲就是 “一个接口,多种形态”。它指的是:同一个行为(方法名),作用于不同的对象时,会产生不同的执行结果。
2.举例子

// 定义一个函数,它接受 A 接口类型
func Process(a A) {
    // 这里不管传入的是谁,直接调用 ShowA
    fmt.Println(a.ShowA()) 
}

func main() {
    w1 := Work{3}   // ShowA 返回 i+10
    w2 := OtherWork{5} // 假设另一个结构体,ShowA 返回 i*2

    Process(w1) // 输出 13
    Process(w2) // 输出 10(假设 OtherWork 的 ShowA 是乘以2)
}

在这个例子里:

Process 函数只关心参数是 A 类型,它根本不知道传入的是 Work 还是 OtherWork。
但运行时,w1 和 w2 各自执行了自己独有的 ShowA 逻辑。

Channel有缓冲和无缓冲有什么区别?

作者: 时间: 2026-05-13 评论: 暂无评论

无缓冲Channel本质是同步通信——发送和接收必须同时准备好,否则阻塞。它是一种“握手”机制,常用于两个goroutine之间做精准的同步信号。有缓冲Channel是异步通信——生产者可以一口气往缓冲区写,消费者可以慢慢读,解耦了生产和消费的节奏。但缓冲区满了依然会阻塞,千万别把缓冲当“存储用”。

示例:

无缓冲:ch := make(chan int),发送方和接收方必须配对才能继续;

有缓冲:ch := make(chan int, 3),可连续发送3次不阻塞,第4次才阻塞。

关于select:select会随机选择一个可执行的case(不是顺序的),这是Go语言故意为之,避免饥饿问题。

golang kafka 使用方法,秒杀案例(segmentio/kafka-go)

作者: 时间: 2025-01-06 评论: 暂无评论
package main

import (
    "context"
    "github.com/segmentio/kafka-go"
    "gotribe-admin/pkg/util"
    "log"
)

func main() {
    type DoOrderEvent struct {
        ShopID int `json:"shop_id"`
        Uid    int `json:"uid"`
    }
    data := DoOrderEvent{
        ShopID: 10086,
        Uid:    44,
    }
    w := &kafka.Writer{
        Addr:        kafka.TCP("localhost:9092"),
        Topic:       "order_event",
        MaxAttempts: 3,
        Async:       true, //异步发送消息 提高效率
        BatchSize:   10,   //批量发送消息
        //WriteTimeout:  3,

        RequiredAcks: kafka.RequireAll, // 表示所有副本节点都确认消息后才返回
        // RequireNone (0)  fire-and-forget, do not wait for acknowledgements from the
        //  RequireOne  (1)  wait for the leader to acknowledge the writes
        //  RequireAll  (-1) wait for the full ISR to acknowledge the writes

        //Completion: ,表示消息发送完成后的回调函数
        AllowAutoTopicCreation: true, //否允许自动创建主题

    }
    err := w.WriteMessages(context.Background(), kafka.Message{
        Key:        []byte(util.UUID()),
        Value:      []byte(util.Struct2Json(data)),
        WriterData: data,
    })
    if err != nil {
        log.Fatal("failed to write messages:", err)
    }

    // 关闭生产者
    if err := w.Close(); err != nil {
        log.Fatal("failed to close writer:", err)
    }

    //启用消费者
    go func() {
        kafka.NewConsumerGroup(kafka.ConsumerGroupConfig{})
        r := kafka.NewReader(kafka.ReaderConfig{
            Brokers:  []string{"localhost:9092"},
            Topic:    "order_event",
            GroupID:  "consumer_order_sec",
            MinBytes: 10e3, // 10KB
            MaxBytes: 10e6, // 10MB
        })
        defer r.Close()
        for {
            //m, err := r.ReadMessage(c) //阻塞获取消息 如果是消费组消费 自动提交偏移量
            m, err := r.FetchMessage(c) //获取消息 如果是消费组消费 不自动提交偏移量  需要手动提交 Consumer Lag
            if err != nil {
                log.Fatal("failed to read message:", err)
            }
            log.Printf("message at offset %d: %s = %s\n", m.Offset, string(m.Key), string(m.Value))
            if err := r.CommitMessages(c, m); err != nil {
                log.Fatal("bad commit message: ", err)
            }

        }

    }()
    select {}

}

go学习笔记2

作者: 时间: 2024-12-25 评论: 暂无评论
var policies []model.RoleAct
policies6 := &[]model.RoleAct{}
fmt.Printf("初始化 policies %+v", policies) //分配了零值+内存地址
fmt.Printf("初始化 policies %+v", policies6) //分配了零值+内存地址

var policies2 *[]model.RoleAct //nil没有被初始化 没有分配内存地址
fmt.Printf("没有初始化 policies2 %+v\n", policies2)
policies2 = &[]model.RoleAct{}
fmt.Printf("初始化 policies2 %+v\n", *policies2) //解引用看到  分配了零值+内存地址
//为什么要强调这个,因为有些方法 是必须给内存地址的,比如
//grom 中find方法必须给内存地址的
list:=&[]model.RoleAct{}
DB.Find(list).Error