Avatar
HHFCodeRv
欢迎关注我的公众号:HHFCodeRv, 了解我的最新状态。
个人从事互联网基础架构系统研发十年余,包括我自己在内的很多朋友都是轮子党。 2011 年我在某大厂干活时,很多使用 C 语言进行开发的同事都有一个自己的私人 SDK 库,尤其是网络通信库。个人刚融入这个环境时,觉得不能写一个基于 epoll/iocp/kqueue 接口封装一个异步网络通信库,会在同事面前矮人三分。现在想起来当时很多同事很大胆,把自己封装的通信库直接在测试生产环境上线使用,据说那时候整个公
2021-10-07
17分钟阅读时长
Go
每隔一段时间就能看到一篇 GraphQL 的文章,但是打开文章一看,基本上就是简单的介绍下 GraphQL 的特性。很多文章其实就是 github 上找个 GraphQL 的项目,然后按照对应的 demo 跑起来而已。有些文章明显是没有完整的项目实践经历,却在狂吹 GraphQL 的各种优点,让不熟悉 GraphQL 的同学以为这是神丹妙药,弄不好还要在项目中实践一番。 因为项目的背景(后面会讲到),我有幸参与过 GraphQL 在实际项目中的落地,本篇文章我会谈谈我
2021-10-06
9分钟阅读时长
Go , graphql , bff
hi, 大家好,我是 hhf。 往期回顾: gin 源码阅读(1) - gin 与 net/http 的关系 gin 源码阅读(2) - http请求是如何流入gin的? 上面两篇文章基本讲清楚了 Web Server 如何接收客户端请求,以及如何将请求流转到 gin 的逻辑。建议没有看过的同学先从第一篇阅读。 gin 原理剖析说到这里,就完全进入 gin 的逻辑里面了。gin 已经拿到 http 请求了,第一件重要的事情肯定就是重写路由了,所以本节内容主要是分
2021-10-05
6分钟阅读时长
本篇文章是 gin 源码分析系列的第二篇,这篇文章我们主要弄清一个问题:一个请求通过 net/http 的 socket 接收到请求后, 是如何回到 gin 中处理逻辑的? 我们仍然以 net/http 的例子开始 func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello World")) }) if err := http.ListenAndServe(":8000", nil); err != nil { fmt.Println("start http server fail:", err) } } 这个例子中 http.HandleFunc 通过看源码,可以看到 URI “/” 被注册到了 DefaultServeMux 上。 func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) } net/http ServeHTTP 的作用 net/http 里面有个非常重要的 Handler interface。只有实现了这个方法才能请求
2021-10-05
2分钟阅读时长
Go , golang , gin
gin 是目前 Go 里面使用最广泛的框架之一了,弄清楚 gin 框架的原理,有助于我们更好的使用 gin. 这个系列 gin 源码阅读会逐步讲明白 gin 的原理。 gin 概览 想弄清楚 gin, 需要弄明白以下几个问题: request数据是如何流转的 gin框架到底扮演了什么角色 请求从gin流入net/http, 最后又是如何回到gin中 gin的context为何能承担起来复杂的需求 gin的路由算法 gin的中间
2021-10-05
5分钟阅读时长
Go , golang , gin
hi,大家好,我是 hhf。 最近在做 prometheus 生态的 cortex 优化工作,遇到一个比较坑的 go mod 的问题,这里分享一下。 我为什么将标题称为:如何欺骗 Go mod 呢?这个挺有意思的,这里先卖个关子,不过确实是突破了 Go mod 的相关特性。(嗯,曹大的 Go mod 十宗罪又可以增加一宗了) 在正式展开这个话题之前,需要简单的介绍下 cortex 和 thanos 这两个项目。 Prometheus 的局限性 说到业务开发基本上都离不开监控系统,Prome
2021-10-04
4分钟阅读时长
hi, 大家好,我是 hhf。 有这么一个 Go 面试题:请说出 slice 和 array 的区别? 这简直就是送分题。现在思考一下,你咋样回答才能让面试官满意呢? 我这里就不贴这道题的答案了。但是我想内存方面简单分析下 slice 和 array 的区别。 Array func main() { as := [4]int{10, 5, 8, 7} fmt.Println("as[0]:", as[0]) fmt.Println("as[1]:", as[1]) fmt.P
2021-10-03
2分钟阅读时长
hi, 大家好,我是 hhf。 《Goland 这些实操技巧,你可能还不会!》介绍了日常开发中一些比较好用的技巧。本篇文章继续介绍一些其他比较好用的技巧。 自定义结构 tag Goland 一个很好用的功能:tag 自动补全。在 struct 结构里,可以在字段类型后敲入 json 或 xml 向结构添加标记。 Goland 默认的 json 都是下滑线格式的。但是有的时候会有特殊的需求。 举个例子:正常情况下,公司对接口返回值字段有统一的
2021-10-02
2分钟阅读时长
工欲善其事必先利其器。对于开发工程师同样如此,一个合适的开发工具能够有效提升我们的开发效率和定位问题的能力。 Golang IDE 被 Gopher 接受的其实不太多,Goland 和 VSCode 应该是占比重最大的。vim-go 真心就不推荐了,入门曲线太过陡峭,太过华而不实(vim 党不要喷我),我们就写程序而已,何苦为难自己呢。 我日常开发中,Goland,Vscode 一般会配合使用。对于 VsCode 来说
2021-10-01
3分钟阅读时长
hi, 大家好,我是 haohongfan。 最近浏览 帖子 的时候看到一个有意思的吐槽。 大概意思是架构师没有选用 RPC 框架来做服务间调用,而选择用 MQ 来代替。是不是很意外? 当然不出意外的,评论区炸了! 现在提出一些疑问: 这个架构师的做法对吗 ? MQ 是否能做 RPC 调用 ? RPC 框架的职责 回答上面问题之前,稍微捋一下 RPC 框架。目前市面上比较流行的 RPC 框架其实并不多。 Java: SpringCloud
2021-10-01
4分钟阅读时长
hi, 大家好,我是 haohognfan。 可能你看过的 interface 剖析的文章比较多了,这些文章基本都是从汇编角度分析类型转换或者动态转发。不过随着 Go 版本升级,对应的 Go 汇编也发生了巨大的变化,如果单从汇编角度去分析 interface 变的非常有难度,本篇文章我会从内度分配+汇编角度切入 interface,去了解 interface 的原理。 限于篇幅 interface 有关动态转发和反射的内容,请关注后续的文章。本篇文章主
2021-08-10
4分钟阅读时长
为什么突然发了一篇这样的文章呢? 了解我的人都知道,我很鄙视语言歧视链,因为我不只写 Go,我同样也在写 PHP,而且还写过三年的 C++,同样也搞过 Python。但是我为什么以这个主题写一篇文章呢? 很简单,Go 源码分析文章暂时写不下去了,因为 Go 汇编我看不懂。。。 继续回到上面的话题:我为什么从 C++ 转到了 Go ? 为什么选择 C++ ? 我本科毕业那会,Java 还没有那么卷,
2021-07-10
3分钟阅读时长
本篇文章罗列一些开发业务时遇到的那些坑。 微服务银弹 当前的市面上各种微服务,DDD的课收割了一波又一波的韭菜,有同学听完了课就要迫不及待的尝试一下。学习新知识的动力当然值得肯定,但是具体落地需要根据公司实际场景来。 某前同事找我咨询架构相关的事情,跟他一番交流让我彻底无语了。 这是他们公司 JAVA 架构师落地的方案(2个开发,其中一个还是架构师,真是闹)。姑且不
2021-06-30
5分钟阅读时长
Go timer 是如何被调度的? hi,大家好,我是 haohongfan。 本篇文章剖析下 Go 定时器的相关内容。定时器不管是业务开发,还是基础架构开发,都是绕不过去的存在,由此可见定时器的重要程度。 我们不管用 NewTimer, timer.After,还是 timer.AfterFun 来初始化一个 timer, 这个 timer 最终都会加入到一个全局 timer 堆中,由 Go runtime 统一管理。 全局的 timer 堆也经历过三个阶段的重要升级。 Go 1.9 版本之前,所有的计时
2021-06-27
6分钟阅读时长
hi, 大家好,我是 haohongfan。 sync.Pool 应该是 Go 里面明星级别的数据结构,有很多优秀的文章都在介绍这个结构,本篇文章简单剖析下 sync.Pool。不过说实话 sync.Pool 并不是我们日常开发中使用频率很高的的并发原语。 尽管用的频率很低,但是不可否认的是 sync.Pool 确实是 Go 的杀手锏,合理使用 sync.Pool 会让我们的程序性能飙升。本篇文章会从使用方式,源码剖析,运用场景等方面,让你对 sync.Pool 有一个
2021-04-01
5分钟阅读时长
hi,大家好,我是haohongfan。 本篇主要介绍 WaitGroup 的一些特性,让我们从本质上去了解 WaitGroup。关于 WaitGroup 的基本用法这里就不做过多介绍了。相对于《这可能是最容易理解的 Go Mutex 源码剖析》来说,WaitGroup 就简单的太多了。 源码剖析 type WaitGroup struct { noCopy noCopy state1 [3]uint32 } WaitGroup 底层结构看起来简单,但 WaitGroup.state1 其实代表三个字段:counter,waiter,sema。 counter :可以理解为
2021-04-01
4分钟阅读时长
hi, 大家好,我是 haohongfan。 本篇文章会从使用方式和原码角度剖析 sync.Map。不过不管是日常开发还是开源项目中,好像 sync.Map 并没有得到很好的利用,大家还是习惯使用 Mutex + Map 来使用。 下面这段代码,看起来很有道理,其实是用错了(背景:并发场景中获取注册信息)。 instance, ok := instanceMap[name] if ok { return instance, nil } initLock.Lock() defer initLock.Unlock() // double check instance, ok = instanceMap[name] if ok { return instance, nil } 这里使用使用 sync.Map 会更合理些,因为 sync.Map 底层完
2021-04-01
5分钟阅读时长
hi,大家好,我是 haohongfan。 本篇文章会从源码角度去深入剖析下 sync.Cond。Go 日常开发中 sync.Cond 可能是我们用的较少的控制并发的手段,因为大部分场景下都被 Channel 代替了。还有就是 sync.Cond 使用确实也蛮复杂的。 比如下面这段代码: package main import ( "fmt" "time" ) func main() { done := make(chan int, 1) go func() { time.Sleep(5 * time.Second) done <- 1 }() fmt.Println("waiting") <-done fmt.Println("done") } 同样可以使用 sync.Cond 来实现 package main import ( "fmt" "sync" "time" ) func main() { cond := sync.NewCond(&sync.Mutex{}) var flag bool go func() { time.Sleep(time.Second * 5) cond.L.Lock() flag
2021-04-01
3分钟阅读时长
Hi,大家好,我是 haohongfan。 上一篇文章《一文完全掌握 Go math/rand》,我们知道 math/rand 的 global rand 有一个全局锁,我的文章里面有一句话:“修复方案: 就是把 rrRand 换成了 globalRand, 在线上高并发场景下, 发现全局锁影响并不大.”, 有同学私聊我“他们遇到线上服务的锁竞争特别激烈”。确实我这句话说的并不严谨。但是也让我有了一个思考:到底多高的 QPS 才能让 Mutex 产生强烈的锁竞争
2021-04-01
7分钟阅读时长
Go 获取随机数是开发中经常会用到的功能, 不过这个里面还是有一些坑存在的, 本文将完全剖析 Go math/rand, 让你轻松使用 Go Rand. 开篇一问: 你觉得 rand 会 panic 吗 ? 源码剖析 math/rand 源码其实很简单, 就两个比较重要的函数 func (rng *rngSource) Seed(seed int64) { rng.tap = 0 rng.feed = rngLen - rngTap //... x := int32(seed) for i := -20; i < rngLen; i++ { x = seedrand(x) if i >= 0 { var u int64 u = int64(x) << 40 x = seedrand(x) u ^= int64(x) << 20 x = seedrand(x) u ^= int64(x) u ^= rngCooked[i] rng.vec[i] = u } } } 这个函数就是在设置 seed, 其实就是对 rng.vec 各个位置设
2021-01-23
4分钟阅读时长