gocookbook
gocookbook copied to clipboard
go cook book
>转载自:https://www.liwenzhou.com/posts/Go/golang-unit-test-2/ 示例代码原作者已上传至Github,点击👉🏻https://github.com/Q1mi/golang-unit-test-demo 查看完整源代码。 本次我对原作者的示例做了延伸,具体看文末的代码链接 这是Go语言单元测试系列教程的第3篇,介绍了如何使用`go-sqlmock`和`miniredis`工具进行`MySQL`和`Redis`的`mock`测试。 在上一篇[《Go单元测试--模拟服务请求和接口返回》](https://mp.weixin.qq.com/s?__biz=MzUzNTY5MzU2MA==&mid=2247493046&idx=1&sn=ae752b8f59b6575438bda5372acbf819&chksm=fa833421cdf4bd37eaa3adf37ace2865b3921c7bba58a58a7e03e8df3644b11abe0aa1e6653f&token=1818003063&lang=zh_CN#rd)中,我们介绍了如何使用httptest和gock工具进行网络测试。 除了网络依赖之外,我们在开发中也会经常用到各种数据库,比如常见的MySQL和Redis等。本文就分别举例来演示如何在编写单元测试的时候对MySQL和Redis进行mock。 ## go-sqlmock sqlmock 是一个实现 `sql/driver` 的mock库。它不需要建立真正的数据库连接就可以在测试中模拟任何 sql 驱动程序的行为。使用它可以很方便的在编写单元测试的时候mock sql语句的执行结果。 ### 安装 ``` go get github.com/DATA-DOG/go-sqlmock ``` ### 使用示例 这里使用的是`go-sqlmock`官方文档中提供的基础示例代码。在下面的代码中,我们实现了一个`recordStats`函数用来记录用户浏览商品时产生的相关数据。具体实现的功能是在一个事务中进行以下两次SQL操作: - 在`products`表中将当前商品的浏览次数+1 - 在`product_viewers`表中记录浏览当前商品的用户id...
>转载自:https://www.liwenzhou.com/posts/Go/golang-unit-test-1/ 示例代码原作者已上传至Github,点击👉🏻https://github.com/Q1mi/golang-unit-test-demo 查看完整源代码。 本次我对原作者的示例做了延伸,具体看文末的代码链接 这是Go单元测试从入门到放弃系列教程的第2篇,介绍了如何使用`httptest`和`gock`工具进行网络测试。 在上一篇[《Go单元测试从入门到放弃—0.单元测试基础》](https://mp.weixin.qq.com/s?__biz=MzUzNTY5MzU2MA==&mid=2247492892&idx=1&sn=cee29fafc9a58ba731ac5b1a315aa27e&scene=19#wechat_redirect)中,我们介绍了Go语言编写单元测试的基础内容。 而实际工作中的业务场景往往会比较复杂,无论我们的代码是作为`server`端对外提供服务或者还是我们依赖别人提供的网络服务(调用别人提供的API接口)的场景,我们通常都不想在测试过程中真正的建立网络连接。本文就专门介绍如何在上述两种场景下`mock`网络测试。 ## httptest 在Web开发场景下的单元测试,如果涉及到HTTP请求推荐大家使用`Go`标准库 `net/http/httptest` 进行测试,能够显著提高测试效率。 在这一小节,我们以常见的`gin`框架为例,演示如何为 http server 编写单元测试。 假设我们的业务逻辑是搭建一个http server端,对外提供`HTTP`服务。我们编写了一个`helloHandler`函数,用来处理用户请求。 ```go // gin.go package httptest_demo import ( "fmt" "net/http" "github.com/gin-gonic/gin" ) //...
> 转载自:https://www.liwenzhou.com/posts/Go/golang-unit-test-0/ 示例代码原作者已上传至Github,点击👉🏻https://github.com/Q1mi/golang-unit-test-demo 查看完整源代码。 这是Go语言单元测试教程的第1篇,主要讲解在Go语言中如何做单元测试以及介绍了表格驱动测试、回归测试,并且介绍了常用的断言工具。 ## Go语言测试 ### go test工具 Go语言中的测试依赖`go test`命令。编写测试代码和编写普通的Go代码过程是类似的,并不需要学习新的语法、规则或工具。 go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以`_test.go`为后缀名的源代码文件都是`go test`测试的一部分,不会被`go build`编译到最终的可执行文件中。 在`*_test.go`文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。 | 类型 | 格式 | 作用 | | :------- | :-------------------- | :-----------------------------...
下面简单总结一下 Go 环境安装的步骤。 ## 下载安装包 安装包去 Go 的官网下载地址 https://go.dev/dl/ 下载对应系统的包,22年3月 最新的发行版是 1.18  页面上也有其他版本供选择下载。下载完成,打开安装器一路下一步即可:  安装器会吧 Go 安装在 /user/local/go 目录 ## 配置 go env 安装完成后,我们可以在命令行执行 go env 命令,看到默认的 go 环境变量:...
### 算法思想 想象有一个木桶,以固定的速度往木桶里加入令牌,木桶满了则不再加入令牌。服务收到请求时尝试从木桶中取出一个令牌,如果能够得到令牌则继续执行后续的业务逻辑;如果没有得到令牌,直接返回访问频率超限的错误码或页面等,不继续执行后续的业务逻辑 特点:由于木桶内只要有令牌,请求就可以被处理,所以令牌桶算法可以支持突发流量。  同时由于往木桶添加令牌的速度是固定的,且木桶的容量有上限,所以单位时间内处理的请求书也能够得到控制,起到限流的目的。假设加入令牌的速度为 1token/10ms,桶的容量为500,在请求比较的少的时候(小于每10毫秒1个请求)时,木桶可以先"攒"一些令牌(最多500个)。当有突发流量时,一下把木桶内的令牌取空,也就是有500个在并发执行的业务逻辑,之后要等每10ms补充一个新的令牌才能接收一个新的请求。 ### 参数设置 木桶的容量 - 考虑业务逻辑的资源消耗和机器能承载并发处理多少业务逻辑。 生成令牌的速度 - 太慢的话起不到“攒”令牌应对突发流量的效果。 ### 适用场景 适合电商抢购或者微博出现热点事件这种场景,因为在限流的同时可以应对一定的突发流量。如果采用均匀速度处理请求的算法,在发生热点时间的时候,会造成大量的用户无法访问,对用户体验的损害比较大。 ### Go代码实现 ``` type TokenBucket struct { rate int64 //固定的token放入速率, r/s capacity...
`Go`的`pprof`工具集,提供了`Go`程序内部多种性能指标的采样能力,我们常会用到的性能采样指标有这些: - profile:CPU采样 - heap:堆中活跃对象的内存分配情况的采样 - goroutine:当前所有goroutine的堆栈信息 - allocs: 会采样自程序启动所有对象的内存分配信息(包括已经被GC回收的内存) - threadcreate:采样导致创建新系统线程的堆栈信息 ### 怎么获取采样信息 网上最常见的例子是在服务端开启端口让客户端通过HTTP访问指定的路由进行各种信息的采样 ```go import ( "net/http/pprof" ) func main() { http.HandleFunc("/debug/pprof/heap", pprof.Index) http.HandleFunc("/debug/pprof/profile", pprof.Profile) http.HandleFunc("/", index) .........
在谈监控`Go`程序的资源使用情况前,我们先来谈一谈有哪些指标是需要监控的,一般谈论进程的指标最常见的就是进程的没存占用率、`CPU`占用率、创建的线程数。因为`Go`语言又在线程之上自己维护了`Goroutine`,所以针对`Go`进程的资源指标还需要加一个创建的`Goroutine`数量。 又因为现在服务很多都部署在`Kubernetes`集群上,一个`Go`进程往往就是一个`Pod`,但是容器的资源是跟宿主机共享的,只是在创建的时候指定了其资源的使用上限,所以在获取`CPU`和`Memory`这些信息的时候还需要具体情况分开讨论。 ### Go进程怎么获取自己的资源占比? 获取`Go`程序的资源使用情况使用`gopstuil`库即可完成。 ```go package main import ( "fmt" "github.com/shirou/gopsutil/process" "os" "runtime" "runtime/pprof" "time" ) func main() { // NewProcess 会返回一个持有PID的Process对象,方法会检查PID是否存在,如果不存在会返回错误 // 通过Process对象上定义的其他方法我们可以获取关于进程的各种信息。 p, err := process.NewProcess(int32(os.Getpid())) if...
掌握了`Go`语言的朋友们应该都知道,在`Go`的结构体类型声明里面,字段声明后可以跟一个可选的字符串标签。 ```go type User struct { Name string `json:"name"` } ``` 上面是一个标准的例子,`Name`字段声明中指定了标签`json:"name" xml:"name"` ,这个标签值看着有点类似`Java`程序里给类属性加的注解。 那么这些结构体标签有什么用途呢,我们随便写管用吗?我们平时工作中常用的结构体标签有哪些呢?我们能不能自己定义结构体标签?今天就带大家掰扯清楚这些问题! ## 结构体标签 Go语言允许我们通过结构体字段标签给一个字段附加可以被反射获取的”元信息“,正好我们上篇文章[实战演示Go反射的使用方法和应用场景](https://mp.weixin.qq.com/s/0FNClBKYzgheMiAwjOnKyA)中讲了Go语言反射使用方法相关的内容,对反射不清楚的可以先去再复习一下。 通常情况下,结构体标签被用于提供结构体字段如何被编码为或者解码自另外一种格式的转换信息(或者是以何种形式被保存至/获取自数据库)。不过,你也可以用它存储任何你想要设置的”元信息“,供其他包或者自己使用。 ### 使用规范 结构体标签在使用上通常是遵守下面三个规范。 结构体标签字符串的值是一个由空格分隔的 key:"value" 对列表,例如: ```go type User struct {...
本来CookBook里记录的都是开发时通用问题的解决方案,不过除了解决功能上的问题,项目的长期迭代的质量保证也是每个开发人员都拥有的基本常识,而编码规范则是保证质量的一个根本,下面列出来一些编码规范,只列出了关键的一些规范也是想避免矫枉过正。当我们所在的组织有自己的规范,不用怀疑去遵守就好,如果没有通过遵守下面这些编码规范能让我们的代码质量有个基础的保证。 # 命名规范 | 类型 | 规则 | 正确示例 | 错误示例 | | :----------------------- | :----------------------------------------------------------- | :--------------------------------------------- | :--------------------------------- | | 表名 | 使用SnakeCase 命名法多个单词用下划线 _ 分割使用单词的**复数**形式命名 | vip_members...
内容节选自我的原创文章[用手写一个工具的过程讲清楚Go反射的使用方法和应用场景](https://mp.weixin.qq.com/s/0FNClBKYzgheMiAwjOnKyA) ## Go语言的反射包 `Go`语言自带的`reflect`包实现了在运行时进行反射的功能,这个包可以帮助识别一个`interface{}`类型变量其底层的具体类型和值。我们的`createQuery`函数接收到一个`interface{}`类型的实参后,需要根据这个实参的底层类型和值去创建并返回`INSERT`语句,这正是反射包的作用所在。 在开始编写我们的通用`SQL`生成器函数之前,我们需要先了解一下`reflect`包中我们会用到的几个类型和方法,接下来我们先逐个学习一下。 ### reflect.Type 和 reflect.Value 经过反射后`interface{}`类型的变量的底层具体类型由`reflect.Type`表示,底层值由`reflect.Value`表示。`reflect`包里有两个函数`reflect.TypeOf()` 和`reflect.ValueOf()` 分别能将`interface{}`类型的变量转换为`reflect.Type`和`reflect.Value`。这两种类型是创建我们的`SQL`生成器函数的基础。 让我们写一个简单的例子来理解这两种类型。 ```go package main import ( "fmt" "reflect" ) type order struct { ordId int customerId int }...