版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 引言 本文是一篇对mock库的小结。 2. 怎么理解mock这件事 2.1 为什么需要mock 最常见的场景是,一个服务B和它依赖的上游微服务A同时启动开发。为了加快开发速度,服务B不可能等到服务A开发完,在启动开发。因此服务A和服务B约定它们之间交互的接口,然后就可以启动开发了。 服务B为了验证自身的业务逻辑,它需要mock一个A服务(只需要mock相关接口)。但是mock A服务本身可能也比较麻烦,涉及到网络IO,端口占用等等。所以进一步的,我们只需把与服务A的交互封装到一个interface中。这样只需要mock这个interface就可以了。 2.2 示例 mock最常见的场景是用于单元测试 完整示例 biz.go type People interface { Say(s string) string } // 业务逻辑 func BizLogic(p People, s string) string { return “People Say:” + p.Say(s) } 在单元测试中,验证我们的业务逻辑 biz_test.go import… 继续阅读 Golang常见mock库小结

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 引言 假如你正在使用go mod管理某个项目的代码库依赖 case1: 某个项目的tag是超过v1版本的,你该怎么定义module case2: 某个项目要同时进行2个版本的开发,v2和v3 你该怎么定义module 2. 问题 为了验证效果,萌叔创建了项目 vearne/mod-multi-version 这个代码库只有一个文件 package mmv import “fmt” const Versoin = “v1.0.0” func PrintVersion(){ fmt.Println(“version:”, Versoin) } 其它项目使用vearne/mod-multi-version package main import ( “github.com/vearne/mod-multi-version” ) func main() { mmv.PrintVersion() } 代码库的tag列表中包含以下tag v1.0.0… 继续阅读 一个关于go module的有趣话题

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 萌叔有有一个开源项目vearne/passwordbox,用于密码的管理。笔者想法是在Mac上build出多个平台下的bin文件,这样用户,可以无需自己编译,直接使用编译好的bin文件。 可是 vearne/passwordbox 内部依赖了 mattn/go-sqlite3。这个库编译时,依赖操作系统上的共享库,无法直接进行交叉编译。 2. 解决 前段时间,萌叔在阅读buger/goreplay源码时,偶然发现它有一个思路是利用docker来实现Golang交叉编译。于是笔者借鉴了它的思路,修改了passwordbox Makefile。 现在在Mac上执行 make docker-img # 只需要执行一次,生成基础镜像即可 make release 就可以同时生成Mac和linux下的bin文件 pwbox-v0.0.10-darwin-amd64.tar.gz pwbox-v0.0.10-linux-amd64.tar.gz 完整代码见Makefile 2.1 build一个基础镜像用于linux环境的编译 docker-img: docker build –rm -t $(CONTAINER) -f Dockerfile.dev . Dockerfile.dev FROM golang:1.14 RUN apt-get update && apt-get… 继续阅读 利用docker实现Golang程序的交叉编译

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 有圈子的朋友介绍 uber-go/automaxprocs, 我才发现之前在docker中, Golang程序设置的GOMAXPROCS不正确,有必要在重新回顾一下了。 2. Go 调度器: M, P 和 G 我们知道在Go scheduler中,G代表goroutine, P代表Logical Processor, M是操作系统线程。在绝大多数时候,其实P的数量和M的数量是相等。 每创建一个p, 就会创建一个对应的M 只有少数情况下,M的数量会大于P golang runtime是有个sysmon的协程,他会轮询的检测所有的P上下文队列,只要 G-M 的线程长时间在阻塞状态,那么就重新创建一个线程去从runtime P队列里获取任务。先前的阻塞的线程会被游离出去了,当他完成阻塞操作后会触发相关的callback回调,并加入回线程组里。简单说,如果你没有特意配置runtime.SetMaxThreads,那么在没有可复用的线程的情况下,会一直创建新线程。 3. GOMAXPROCS的取值 3.1 虚拟机和物理机 我们知道可以通过 runtime.GOMAXPROCS() 来了设定P的值 Go 1.5开始, Go的GOMAXPROCS默认值已经设置为 CPU的核数, 这允许我们的Go程序充分使用机器的每一个CPU,最大程度的提高我们程序的并发性能。 但其实对于IO密集型的场景,我们可以把GOMAXPROCS的值超过CPU核数,在笔者维护的某个服务中,将GOMAXPROCS设为CPU核数的2倍,压测结果表明,吞吐能力大概能提升10% 3.2 容器中… 继续阅读 GOMAXPROCS你设置对了吗?

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1.引子 哈希表的原理是用数组来保存键值对,键值对存放的位置(下标)由键的哈希值决定,键的哈希值可以在参数时间内计算出来,这样哈希表插入、查找和删除的时间复杂度为O(1),但是这是理想的情况下,真实的情况是,键的哈希值存在冲突碰撞,也就是不同的键的哈希值可能相等,一个好的哈希函数应该是尽可能的减少碰撞。解决冲突碰撞的方法有分为两种:开放地址法和 链接法,这里不具体展开。哈希表一般都采用链接法来解决冲突碰撞,也就是用一个链表来将分配到同一个桶(键的哈希值一样)的键值对保存起来。 所谓的哈希碰撞攻击就是,针对哈希函数的特性,精心构造数据,使所有数据的哈希值相同,当这些数据保存到哈希表中,哈希表就会退化为单链表,哈希表的各种操作的时间复杂度提升一个数量级,因此会消耗大量CPU资源,导致系统无法快速响应请求,从而达到拒绝服务攻击(Dos)的目的。 绝大多数语言中map的默认实现都是HashMap,比如Golang、Python,Java有2种HashMap和TreeMap 2.攻击方法与防范机制 通过构造哈希值完全相同的字符串 比如 hash(str1) == hash(str2) 找到大量的哈希值相同的str1、str2、str3 … 这种攻击方法依赖的前提是hash函数本身是不变的。 那么如果hash函数是变化的,那么就会大大增加构造哈希值完全相同的字符串的难度。 由此衍生出方案1。 2.1 方案1 加salt 哈希值的计算变为 hash(salt + str) salt为特殊的字符串,一般都是固定值 由于salt是不对外暴露的,所以黑客构造哈希值完全相同的字符串的难度就增大了 2.2 方案2 可变的hash函数 如果hash函数本身就是可变的,那么黑客几乎无法构造哈希值完全相同的字符串。 我们来看Golang是怎么做的 type _type struct { … kind uint8 alg *typeAlg … }… 继续阅读 哈希碰撞攻击与防范机制

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 大家都知道golang的栈的动态增长的,并且是放在堆上的,理论上可以相当的大,那么怎么才能制造一个stack overflow 故障呢? 其实只要人为的加入循环引用就能做到 package main import ( //”github.com/json-iterator/go” “encoding/json” ) type A struct { ElementB *B } type B struct { ElementA *A } func main(){ a := A{} b := B{} a.ElementB = &b b.ElementA = &a //var json… 继续阅读 如何在Golang中制造stack overflow 故障

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 前言 有些朋友可能看我的这篇文章 简单的GOLANG 协程池 这是我做的简单协程池,功能还算完备,但是有些任务执行的时间很长,如果想在协程池运行起来以后就退出,就只能死等。这是非常不友好的,因此我又写了一个新的协程池GContextPool,利用context支持在任务运行过程中,结束任务。原来的GPool仍然可以继续使用。 下面我们来看看用法。 1. 安装 go get github.com/vearne/golib/utils 2. 使用 2.1 创建协程池 // 设定协程池的中协程的数量是30个 cxt, cancel := context.WithTimeout(context.Background(), 1 * time.Second) defer cancel() var p *utils.GContextPool = utils.NewGContextPool(ctx, 30) 2.2 定义任务处理函数 任务处理函数形如 type JobContextFunc func(ctx context.Context, key interface{})… 继续阅读 简单的GOLANG 协程池2 (带Cancel功能)

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 说到Golang的Redis库,用到最多的恐怕是 redigo 和 go-redis。其中 redigo 不支持对集群的访问。 本文想聊聊go-redis 2个高级用法 2. 开启对Cluster中Slave Node的访问 在一个负载比较高的Redis Cluster中,如果允许对slave节点进行读操作将极大的提高集群的吞吐能力。 开启对Slave 节点的访问,受以下3个参数的影响 type ClusterOptions struct { // Enables read-only commands on slave nodes. ReadOnly bool // Allows routing read-only commands to the closest master or slave… 继续阅读 聊聊go-redis的一些高级用法

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 前言 大家都知道写文件时数据流转的顺序是 用户空间文件缓冲区 -> 内核空间文件缓冲区 -> 内核空间IO队列 默认的ANSI C库,对用户空间文件缓冲区有三种方式 全缓冲 行缓冲 无缓冲 难道Golang没有?笔者不敢断定,但有以下的对比试验或许能说明些问题 write.go package main import ( “os” “log” “time” ) func main() { file, err := os.OpenFile(“test.txt”, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666) if err != nil { log.Fatal(err) } for i :=… 继续阅读 Golang标准库的读写文件,没有开启用户空间文件缓冲区?

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 1. 前言 在 聊聊golang中的锁(1) 中,笔者提到Golang的锁,可能引发的非常高的CPU消耗,本文我们一起来探究一下,CPU时钟都消耗再了哪里。 2. 分析 修改代码, 使用pprof package main import ( “fmt” “sync” “time” _ “net/http/pprof” “net/http” “log” ) type LockBox struct{ sync.Mutex Value int } func deal(wpg *sync.WaitGroup, bp *LockBox, count int){ for i:=0;i< count;i++{ bp.Lock() bp.Value++ bp.Unlock()… 继续阅读 聊聊Golang中的锁(2)