January 11, 2021
Runtime:源码解析Golang 的map实现原理
"go version 1.15.6\nmap作为一种常见的 key-value 数据结构,不同语言的实现原理基本差不多。首先在系统里分配一段连接的内存地址作为数组,然后通过对map键进行hash算法(最终将键转换成了一个整型数字)定位到不同的桶bucket(数组的索引位置),然后将值存储到对应的bucket里\n理想的情况下是一个bucket存储一个值,即数组的形式,时间复杂度为O(1)。\n如果存在键值碰撞的话,可以通过 链表法 或者 开放寻址法 来解决。\n链表法\n开放寻址法\n对于开放寻址法有多种算法,常见的有线性探测法,线性补偿探测法,随机探测法等,这里不再介绍。\nmap基本数据结构 hmap结构体 map的核心数据结构定义在 /runtime/map.go\n// A header for a Go map. type hmap struct { // Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go. // Make sure this stays in sync …"
December 26, 2020
Golang并发模式之扇入FAN-IN和扇出FAN-OUT
"在现实世界中,经常有一些工作是属于流水线类型的,它们的每一个步骤都是紧密关联的,第一步先做什么,再做什么,最后做什么。特别是制造业这个行业,基本全是流水线生产车间。在我们开发中也经常遇到这类的业务场景。\n假如我们有个流水线共分三个步骤,分别是 job1、job2和job3。代码: https://play.golang.org/p/e7ZlP9ofXB3\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func job1(count int) \u0026lt;-chan int { outCh := make(chan int, 2) go func() { defer close(outCh) for i := 0; i \u0026lt; count; i++ { time.Sleep(time.Second) fmt.Println(\u0026#34;job1 finish:\u0026#34;, 1) outCh \u0026lt;- 1 } }() return outCh } func job2(inCh \u0026lt;-chan int) \u0026lt;-chan int …"
December 24, 2020
重新认识Golang中的空结构体
"认识空结构体 低层实现原理 空结构体之内存对齐 应用场景 在golang中,如果我们想实现一个set集合的话,一般会使用map来实现,其中将set的值作为map的键,对于map的值一般使用一个空结构体来实现,当然对map值也可以使用一个bool类型或者数字类型等,只要符合一个键值对应关系即可。但我们一般推荐使用struct{}来实现,为什么呢?\npackage main import \u0026#34;fmt\u0026#34; func main() { m := make(map[int]struct{}) m[1] = struct{}{} m[2] = struct{}{} if _, ok := m[1]; ok { fmt.Println(\u0026#34;exists\u0026#34;) } } 上面这段代码是一个很简单的使用map实现的set功能,这里是采用空结构体struct{}来实现。\n在分析为什么使用struct{}以前,我看先认识一个struct。\n认识空结构体 struct 我们先看一个这段代码\npackage main import ( \u0026#34;fmt\u0026#34; …"
December 17, 2020
Golang中的内存重排(Memory Reordering)
"什么是内存重排 内存重排指的是内存的读/写指令重排。\n为什么要内存重排 为了提升程序执行效率,减少一些IO操作,一些硬件或者编译器会对程序进行一些指令优化,优化后的结果可能会导致程序编码时的顺序与代码编译后的先后顺序不一致。\n就拿做饭场景来说吧,是先蒸米还是先炒菜,这两者是没有冲突的,编译器在编译时有可能与你要求的顺序不一样。\n编译器重排 如下面这段代码\nX = 0 for i in range(100): X = 1 print X 要实现打印100次1,很显示在for里面每次都执行X=1语句有些浪费资源,如果将初始变量值修改为1,是不是要快的多。编译器也分析到了这一点,于是在编译时对代码做了以下优化\nX = 1 for i in range(100): print X 最终输出结果是一样的,两段代码功能也一样。\n但是如果此时有另一个线程里执行了一个 X=0 的赋值语句的话(两个线程同时运行),那么输出结果就可能与我们想要的不一样了。\n优化前情况:第一个线程执行到了第3次print X 后,第二个线程执行了X=0,把X 的值进行了修改,结果就有可能是1110 1111(在线程执行第5 …"
November 19, 2020
Golang中的并发原语 Singleflight
"在Golang中有一个并发原语是 Singleflight,好像知道的开发者并不多。其中著名的 https://github.com/golang/groupcache 就用到了这个并发原语。\nGolang版本 go1.15.5\n相关知识点 map、Mutex、channel、\n使用场景 一般用在对指定资源频繁操作的情况下,如高并发下的“缓存击穿”问题。\n缓存击穿:一个存在的key,在缓存过期的瞬间,同时有大量的请求过来,造成所有请求都去DB读取数据,这些请求都会击穿缓存到DB,造成瞬时DB请求量大、压力瞬间骤增,导致数据库负载过高,影响整个系统正常运行。(缓存击穿不同于 缓存雪崩 和 缓存穿透)\n怎么理解这个原语呢,简单的讲就是将对同一个资源的多个请求合并为一个请求。\n举例说明,假如当有10万个请求来获取同一个key的值的时候,正常情况下会执行10万次get操作。而使用singleflight并发语后,只需要首次的地个请求执行一次get操作就可以了,其它请求再过来时,只需要只需要等待即可。待执行结果返回后,再把结果分别返回给等待中的请求,每个请求再返回给客户端,由此看看,在一定的高并 …"
September 27, 2020
Protobuf协议实现原理
"protobuf是Google开源的一款支持跨平台、语言中立的结构化数据描述和高性能序列化协议,此协议完全基于二进制,所以性能要远远高于JSON/XML。由于出色的传输性能因此常见于微服务之间的通讯,其中最为著名的是Google开源的 gRPC 框架。\n那么protobuf是如何实现高性能的,又是如何实现数据的编码和解码的呢?\nprotobuf协议原理 基于128bits的数据存储方式(Base 128 Varints)\nVarint 是一种紧凑的表示数字的方法。它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。\n比如对于 int32 类型的数字,一般需要 4 个 byte 来表示。但是采用 Varint,对于很小的 int32 类型的数字,则可以用 1 个 byte 来表示。当然凡事都有好的也有不好的一面,采用 Varint 表示法,大的数字则需要 5 个 byte 来表示。从统计的角度来说,一般不会所有的消息中的数字都是大数,因此大多数情况下,采用 Varint 后,可以用更少的字节数来表示数字信息\nVarint 中的每个 byte 的最 …"
September 19, 2020
Golang开发中中使用GitHub私有仓库
"私有仓库地址为\ngithub.com/cfanbo/websocket 一、设置私有环境变量 GOPRIVATE $ go env -w GOPRIVATE=github.com/cfanbo/websocket 对于为什么需要设置 GOPRIMARY 变量,可以参考 这里\n对于GOPRIVATE值级别分为仓库级别和账号级别。\n如果只有一个仓库,直接设置为仓库地址即可。如果有多个私有仓库的话,使用”,”分开,都在这个账号下,也可以将值设置为账号级别,这样账号下的所有私有仓库都可以正常访问。如 http://github.com/cfanbo\n如果不想每次都重新设置,我们也可以利用通配符,例如:\n$ go env -w GOPRIVATE=\u0026#34;*.example.com\u0026#34; 这样子设置的话,所有模块路径为 example.com 的子域名(例如:git.example.com)都将不经过 Go module proxy 和 Go checksum database,需要注意的是不包括 example.com 本身。\n国内用户访问仓库建议设置 GORPOXY …"
July 21, 2020
MySQL DBA利器innodb_ruby
"innodb_ruby简介 innodb_ruby是一款用ruby写的用来分析 innodb 物理文件的专业DBA工具,可以通过这款工具来窥探innodb内部的一些结构。 注意不要在生产环境中使用此工具,以避对线上服务造成影响。官方网址 https://rubygems.org/gems/innodb_ruby。\n注意如果(Linux)平台安装中遇到错误一般情况是由于缺少依赖库造成的,可以先安装 sudo apt-get install libxslt1-dev libxml2-dev 相关库。\n命令语法 在执行以下命令时,建议切换到MySQL 的 datadir 目录里。\nsxf@ubuntu:~$ innodb_space --help Usage: innodb_space \u0026lt;options\u0026gt; \u0026lt;mode\u0026gt; innodb_space \u0026lt;选项\u0026gt; \u0026lt;模式\u0026gt; 命令主要分 options 和 mode 两大部分。 Invocation examples: innodb_space -s ibdata1 [-T tname [-I …"
June 6, 2020
利用jenkins+github实现应用的自动部署及回滚
"对于jenkins的介绍这里不再详细写了,此教程只是为了让大家对部署和回滚原理有所了解。\n一、创建项目 点击左侧的“New Item”,输入项目名称,如 rollback-demo。\n选中 ” 丢弃旧的构建(Discard old builds)”项,在“策略(Strategy” 选择”Log Rotation“, 并输入保留的最大构建个数。\n二、常规配置 设置参数,点击”Add Parameter“,依次选择 “Choice Parameter” 和 “String Parameter“这两,填写如下\n这里的Name 项为参数名称,用户在操作的时候,会在deploy 和 rollback 两个值中选择一项。\n三、源码管理 我们这里选择Git.并填写github.com上的项目地址,记得设置认证 Credentials。构建分支直接使用默认的 */master 即可以了。查看代码浏览器选择 githubweb,并填写项目的github地址。\n四、构建触发事件 选择 “GitHub hook trigger for GITScm polling”,表示使用github webhook来触 …"
May 27, 2020
golang中几种对goroutine的控制方法
"我们先看一段代码\nfunc listen() { ticker := time.NewTicker(time.Second) for { select { case \u0026lt;-ticker.C: fmt.Println(time.Now()) } } } func main() { go listen() time.Sleep(time.Second * 5) fmt.Println(\u0026#34;main exit\u0026#34;) } 非常简单的一个goroutine用法,想必每个gopher都看过的。\n不过在实际生产中,我们几乎看不到这种用法的的身影,原因很简单,我们无法实现对goroutine的控制,而一般业务中我们需要根据不同情况对goroutine进行各种操作。\n要实现对goroutine的控制,一般有以下两种。\n一、手动发送goroutine控制信号 这里我们发送一个退出goroutine的信号。\n// listen 利用只读chan控制goroutine的退出 func listen(ch \u0026lt;-chan bool) { ticker := …"