分类 Go 中的文章

记一次kafka-go使用踩坑(多个partition只会写入一个partition)

kafka-go地址: https://github.com/segmentio/kafka-go 注意:本文解决的问题不是因为kafka配置造成的,是因为代码写得有问题。 今天查看公司某kafka group消费情况的时候,发现该group消费的topic大多数的message写入了同一个partition,先从配置问题查找,后来又去看公司包装的kafka的库的源码查找,看起来都没啥问题。 并且,该问题就出现在一个服务里面,很是让人迷惑。该服务和其他服务最大的不同在于,它每一次都只会发送一个message给kafka。 先看看,kafka-go的demo: 1 2 3 4 5 6 7 8 9 10 11 w := kafka.NewWriter(kafka.WriterConfig{ Brokers: []string{"localhost:9092"}, Topic: "topic-A", Balancer: &kafka.LeastBytes{}, }) 第一个看起来没啥问题,我发现公司代码也是照抄的,继续往NewWriter这个函数里面看,会发现这东西居然是起了一个协程,然后再去查看LeastBytes这个Balancer,他的Balance方法是依赖结构体中字段的状态的,每一次发送的时候都去创建一个新的Balancer,你不翻车谁翻车啊。 因为代码是每次都发送一个message,每次都是一个新的Balancer,所以Balancer每次只需Balance的状态都是一致,所以会造成只写入一个partition中。 总结一下,这个问题可大可小,数据量不大,程序抗得住当然没什么,但是总有一天会扛不住。所以奉劝一句,写完代码记得认认真真测试,程序跑得起来不代表正确。……

阅读全文

golang自动调用结构体中内嵌字段的Init()函数

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 package main import ( "fmt" "reflect" ) type initial interface { Init() } func triggerInitials(c interface{}) { rv := reflect.……

阅读全文

Goroutines和线程对比

栈不同 线程:每一个OS线程都有一个固定大小的内存块(一般会是2MB)来做栈,这个栈会用来存储当前正在被调用或挂起(指在调用其它函数时)的函数的内部变量。固定大小的栈对于更复杂或者更深层次的递归函数调用来说显然是不够的。修改固定的大小可以提升空间的利用率允许创建更多的线程,并且可以允许更深的递归调用,不过这两者是没法同时兼备的。 goroutine:一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB。一个goroutine的栈,和操作系统线程一样,会保存其活跃或挂起的函数调用的本地变量,但是和OS线程不太一样的是一个goroutine的栈大小并不是固定的;栈的大小会根据需要动态地伸缩。而goroutine的栈的最大值有1GB,比传统的固定大小的线程栈要大得多,尽管一般情况下,大多goroutine都不需要这么大的栈。 调度不同 线程:OS线程会被操作系统内核调度。每几毫秒,一个硬件计时器会中断处理器,这会调用一个叫作scheduler的内核函数。这个函数会挂起当前执行的线程并保存内存中它的寄存器内容,检查线程列表并决定下一次哪个线程可以被运行,并从内存中恢复该线程的寄存器信息,然后恢复执行该线程的现场并开始执行线程。因为操作系统线程是被内核所调度,所以从一个线程向另一个“移动”需要完整的上下文切换,也就是说,保存一个用户线程的状态到内存,恢复另一个线程的到寄存器,然后更新调度器的数据结构。这几步操作很慢,因为其局部性很差需要几次内存访问,并且会增加运行的cpu周期。 goroutine:Go的运行时包含了其自己的调度器,这个调度器使用了一些技术手段,比如m:n调度,因为其会在n个操作系统线程上多工(调度)m个goroutine。Go调度器的工作和内核的调度是相似的,但是这个调度器只关注单独的Go程序中的goroutine。和操作系统的线程调度不同的是,Go调度器并不是用一个硬件定时器而是被Go语言"建筑"本身进行调度的。例如当一个goroutine调用了time.Sleep或者被channel调用或者mutex操作阻塞时,调度器会使其进入休眠并开始执行另一个goroutine直到时机到了再去唤醒第一个goroutine。因为这种调度方式不需要进入内核的上下文,所以重新调度一个goroutine比调度一个线程代价要低得多。 GOMAXPROCS Go的调度器使用了一个叫做GOMAXPROCS的变量来决定会有多少个操作系统的线程同时执行Go的代码。其默认的值是运行机器上的CPU的核心数,所以在一个有8个核心的机器上时,调度器一次会在8个OS线程上去调度GO代码。(GOMAXPROCS是前面说的m:n调度中的n)。在休眠中的或者在通信中被阻塞的goroutine是不需要一个对应的线程来做调度的。在I/O中或系统调用中或调用非Go语言函数时,是需要一个对应的操作系统线程的,但是GOMAXPROCS并不需要将这几种情况计算在内。 你可以用GOMAXPROCS的环境变量来显式地控制这个参数,或者也可以在运行时用runtime.GOMAXPROCS函数来修改它。我们在下面的小程序中会看到GOMAXPROCS的效果,这个程序会无限打印0和1。 1 2 3 4 5 6 7 8 9 for { go fmt.Print(0) fmt.Print(1) } 1 2 3 4 5 6 7 8 9 10 11 $ GOMAXPROCS=1 go run hacker-cliché.go 111111111111111111110000000000000000000011111... $ GOMAXPROCS=2 go run hacker-cliché.go 010101010101010101011001100101011010010100110... 在第一次执行时,最多同时只能有一个goroutine被执行。初始情况下只有main goroutine被执行,所以会打印很多1。过了一段时间后,GO调度器会将其置为休眠,并唤醒另一个goroutine,这时候就开始打印很多0了,在打印的时候,goroutine是被调度到操作系统线程上的。在第二次执行时,我们使用了两个操作系统线程,所以两个goroutine可以一起被执行,以同样的频率交替打印0和1。我们必须强调的是goroutine的调度是受很多因子影响的,而runtime也是在不断地发展演进的,所以这里的你实际得到的结果可能会因为版本的不同而与我们运行的结果有所不同。 Goroutine没有ID号 在大多数支持多线程的操作系统和程序语言中,当前的线程都有一个独特的身份(id),并且这个身份信息可以以一个普通值的形式被被很容易地获取到,典型的可以是一个integer或者指针值。这种情况下我们做一个抽象化的thread-local storage(线程本地存储,多线程编程中不希望其它线程访问的内容)就很容易,只需要以线程的id作为key的一个map就可以解决问题,每一个线程以其id就能从中获取到值,且和其它线程互不冲突。……

阅读全文

数组中重复的数字(Golang)

使用哈希表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import "fmt" func main() { a := [...]int{2,3,1,0,2,5,3} num := make(map[int]bool) for _, v := range a { if !num[v] { num[v] = true } else { fmt.Println(v) } } } 排序查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package main import ( "fmt" "sort" ) func main() { a := []int{2,3,1,0,2,5,3} sort.……

阅读全文

Golang 实现单例模式

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package main import "fmt" type Single struct { } var single *Single func GetSingle() *Single { if single == nil { single = &Single{} } return single } func main() { fmt.……

阅读全文

Gin上传文件到MongoDB gridfs

上传图片 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 func imagePost(c *gin.……

阅读全文

Golang不会自动把slice转换成interface{}类型的slice

例子 我们时常会写一些interface,例如: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 type A interface{ Print() } type B struct { } func (b *B) Print() { fmt.Println("Here is *B") } 赋值的时候: 1 2 3 4 5 var a A a = &B{} 诶,这样看视乎没什么问题,但是我们没有办法避免使用数组,然后顺手就写了这样的代码 1 2 3 4 5 var aa []A aa = make([]*B, 10) 好的,现在翻车了,会得到一个这样的报错:……

阅读全文

Golang mgo 模糊查询的使用

在日常使用的Mongodb中,有一项功能叫做模糊查询(使用正则匹配),例如: db.article.find({"title": {$regex: /a/, $options: "im"}}) 这是我们常用Mongodb的命令行使用的方式,但是在mgo中做出类似的方式视乎是行不通的: 1 2 3 query := bson.M{"title": bson.M{"$regex": "/a/", "$options": "im"}} 大家用这个方式去查询,能查询到算我输! 下面总结一下,正真使用的方式: 在Mongodb的命令行中,我们可以使用形如 \abcd\ 的方式来作为我们的pattern,但是在mgo是直接传入字符串来进行的,也就是传入的是"\a",而不是\a\。 根据第一点,我们将代码修改一下。 1 2 3 query := bson.M{"title": bson.M{"$regex": "a", "$options": "im"}} 但是我们会发现依然不能得到我们想要的结果,那么第二点就会产生了! 在mgo中要用到模糊查询需要mgo中自带的一个结构: bson.RegEx 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 // RegEx represents a regular expression.……

阅读全文

gin mongodb restful api设计: 动态的patch接口

什么是Patch? Patch方法可以用来更新资源的一个组成部分 什么时候使用Patch? 当你仅需更新资源的某一项,即不完全也不幂等 那当我们的模型在数据库中几乎每个字段都可能会遇到改变的时候,难道在patch的时候,或者专门写一个post的接口去一个一个if else操作吗,而我们又使用的是静态语言golang,有没有什么办法能够动态的让我们进行愉快的Patch呢? 答案当然是有的,先说说如何去实现: 我们需要利用golang的map[string]interface{}结构 我们需要一个动态的结构 在mgo中大多使用map[string]interface{}的结构,我们直接构造出这个结构有利于我们直接进行Update({"$set": xxxx})操作 使用BindJSON函数,当然你也可以使用Gin的其他函数对map[string]interface{}进行绑定,这里我们不使用一个struct进行绑定,原因是我们无法判断客户端,或者说是浏览器会传送哪些字段来,这样做也有利于我们客户端将某一个字段置为默认值(如果用struct就只能避开默认值了) 来,开始贴代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 // 这个是我们需要存到数据库中的Model type Article struct { ID bson.……

阅读全文

gin PostForm 方法不起作用

情景: 在httpie post 下,在 axios post下,总的来说,就是在form-data下只有c.Bind()会有用 如果一定要用c.PostForm() headers必须为x-www-form-urlencoded……

阅读全文