golang

Gorm Model Find First Where等查询函数的区别

月盾
gorm是一款优秀的国产golang orm关系型数据库框架,在国内外使用比较广泛。它的链式调用还算是一种符合人类思维的风格。 不过在使用过程中也遇到一些困扰,比如:Model, Find, First, Where这些函数该什么时候使用,有时候会有边界不清楚,使用混乱的情况。 以下代码示例使用v2版本,v1和v2大体上相同,有些细微的不同 Where和Find search := User{UserName:"月盾"} db.Find(&user, search) // SELECT * FROM `user` WHERE `user`.`user_name` = '月盾' db.Where(search).Find(&user) // SELECT * FROM `user` WHERE `user`.`user_name` = '月盾' 以上两种查询方式结果一样。 Find(dest interface{}, conds ...interface{})Find函数有两个参数,dest是数据接收者,conds是查询条件。所以Find也是可以代替Where来传入条件的。 Where的参数主要分为两类:String,Struct&Map。还有其他不常用类型。 String参数 当使用string参数时,使用方式类似于fmt.Printf,第一个参数为字符串格式,使用?作为占位符,后面的参数作为值。 Struct&Map参数 使用结构体和映射作为参数时,则推荐一个参数即可,struct和map本身就是键值对格式。否则容易引起混淆。比如这样的: db.Where(&User{Name: "jinzhu"}, "name", "Age").Find(&users) // SELECT * FROM users WHERE name = "jinzhu" AND age = 0; db.Where(&User{Name: "jinzhu"}, "Age").Find(&users) // SELECT * FROM users WHERE age = 0; 注意 当使用结构作为条件查询时,GORM 只会查询非零值字段。这意味着如果您的字段值为 0、''、false 或其他 零值,该字段不会被用于构建查询条件,例如:

go单元测试初始化

月盾
go单元测试会遇到这样的场景: 写好了service层函数getUser()。然后测试测试getUser函数。有个问题是,函数中使用了数据库连接,如果直接测试的话会报错误,比如空指针错误。 panic: runtime error: invalid memory address or nil pointer dereference [recovered] panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x0 addr=0xb0 pc=0x167680d] 如果遇到这种情况很有可能就是数据库连接未初始化。但是单元测试并不会主动去初始化数据库连接。不用担心,有办法。 go test提供了用于初始化的方法:TestMain函数。只需要在这个函数中进行数据库初始化,后面需要用的的数据库连接可直接使用,不需要重复初始化。 func TestMain(m *testing.M) { fmt.Println("begin") dba, err := gorm.Open("sqlite3", "../../website.db") db.SQLLite = dba if err != nil { panic(err) } m.Run() fmt.Println("end") } func TestProjectUsers(t *testing.T) { userService := user.NewService(db.SQLLite) users, err := userService.GetProjectUsers(25) if err != nil { t.

goquery 中文乱码

月盾
乱码的情况目前有两种可能: 常规乱码,网页非utf-8。 非常规乱码,代码导致的乱码。 关于常规乱码可参考issue获取中文网页有乱码的问题 #185 非常规乱码就像我遇到的一样,最开始以为是网页问题,使用了github.com/djimenez/iconv-go转换还是乱码,使用了golang.org/x/text/encoding/simplifiedchinese还是乱码。 试试英文网页,还是乱码。最终一点点调试发现是由header引起的。 req.Header.Add("Accept-Encoding", "gzip, deflate") 这一行的作用是告诉服务器浏览器要接收的数据编码是gzip,dflate,到达浏览器后会自动解码。但是我们的代码并非浏览器,不会自动解码,所以接收到的就是非常规的压缩数据。

go mongo-driver动态条件

月盾
在go mongo中查询是使用的是bson.M类型的条件,但是直接使用时无法动态添加条件,只能初始化赋值,bson.M其实就是map类型,只能使用someMap[“someKey”]=“someValue” 的形式添加,这样的话只能是用if判断字段的值来决定是否添加map key/value,写起来比较繁琐。还有一种是利用结构体转换为bson.M来实现。 //构造一个查询结构体 search := User{ ID: id, Name: name, Age: age, } //构造一个条件变量 condition := bson.M{} //将结构体转为字节数组,userInfo中的字段根据需要设置值,需要保证没有值时不会有默认值出现 userbyte, err := bson.Marshal(search) if err != nil { return user, err } //将字节码转为bson.M类型 bson.Unmarshal(userbyte, &condition) log.Println(condition) if err = this.mongo.Collection("user").FindOne(context.TODO(), condition).Decode(&user); err != nil { return user, err } 以上基本就实现了动态条件查询的效果,其中: search := User{ ID: id, Name: name, Age: age, } search结构中的字段可能值为空,假设在前端并未传递age字段,那么最终condition=map[id:xxx,name:xxx],并不会出现age:0这个的字段,有效避免了零值情况。

golang操作mongodb

月盾
在之前mgo是一个使用广泛的mongodb驱动器,不过从2018年开始已不再维护,虽然觉得怪可惜的,但也不推荐使用了,毕竟mongodb本身一直在迭代,如果驱动器不更新后续也没法使用。 详细说明见仓库:https://github.com/go-mgo/mgo 而mongodb提供了官方驱动,目前能找到的中文文档大多比较旧了,推荐直接看官方文档,有完整的操作手册:https://www.mongodb.com/blog/search/golang 本文也不想做一次搬运工,毕竟也不能随时保持更新,还是直接看官方文档比较好。下面列出一些主要的文章链接: Stack Overflow Research of 100,000 Developers Finds MongoDB is the Most Wanted Database (2019-2-2) Official MongoDB Go Driver Now Available for Beta Testing (2019-2-2) mongodb将为go提供官方驱动支持 MongoDB Go Driver Tutorial (2019-5-30) MongoDB Go驱动程序教程 Go Migration Guide (2019-2-2) 从社区驱动(mgo)迁移到官方驱动 MongoDB Stitch Functions – The AWS re:Invent Stitch Rover Demo(2019-10-15) Calling the MongoDB Atlas API - How to do it from Go(2019-3-18) MongoDB Go Driver Tutorial Part 1: Connecting, Using BSON, and CRUD Operations(2019-4-23)

gorm模糊查询和分页查询同时查总条数

月盾
gorm概述 全功能ORM(几乎) 关联(包含一个,包含多个,属于,多对多,多种包含) Callbacks(创建/保存/更新/删除/查找之前/之后) 预加载(急加载) 事务 复合主键 SQL Builder 自动迁移 日志 可扩展,编写基于GORM回调的插件 每个功能都有测试 开发人员友好 已上支持基本满足了日常开发需要,一些基本的查询需求可以查看文档得到解决,不过文档并没有覆盖所有日常开发案例,尤其是一些组合需求,本文挑了一段常见的场景。 func (u *userService) GetuserList(offset, limit int, search User) (users []User, count int, err error) { if search.Name != "" { u.mysql = u.mysql.Where("name LIKE ?", search.Name+"%") } if search.Category != "" { u.mysql = u.mysql.Where("age =?", search.Age) } err = u.mysql.Offset(offset).Limit(limit).Find(&users).Offset(-1).Limit(-1).Count(&count).Error return users, count, err } 这简单的一小段已经包含了gorm的模糊查询,动态条件,分页查询,数据条数。 这就是一个最常见的列表查询,列表需要支持条件查询,模糊查询,分页,从代码可以直接看到。 1.if代码是动态组装条件。 2.err = u.mysql.Offset(offset).Limit(limit).Find(&users).Offset(-1).Limit(-1).Count(&count).Error 这行代码包含了数据列表查询和数据条数。

go-micro线上部署,注册服务到etcd

月盾
线上部署 在线上部署就不能使用go run main.go命令了,需要打包编译成可执行文件。 linux系统需要这样编译:GOOS=linux go build -o service main.go,就是在windows系统上进行交叉编译,可根据自己服务器情况修改参数。 go build -o service main.go go build -o api api/api.go 线上的restful api也不能使用micro api了。需要选择适合自己的web服务框架,在web服务中调用api服务。 etcd启动 线上etcd和本地启动有区别,如果etcd是单独的服务器,那么在不加任何参数的情况下直接启动,那基本是调不通的。 $ ./service --registry=etcd --registry_address=xx.xx.xx.xx:2379 2020-03-17 17:04:42 Starting [service] go.micro.srv.user 2020-03-17 17:04:42 Server [grpc] Listening on [::]:48493 2020-03-17 17:04:42 Registry [etcd] Registering node: go.micro.srv.user-f32a2950-8e59-44d4-ac86-f4e1ec103395 {"level":"warn","ts":"2020-03-17T17:04:47.849+0800","caller":"clientv3/retry_interceptor.go:61","msg":"retrying of unary invoker failed","target":"endpoint://client-e45decee-12bf-4a9b-a7ab-f92eece39420/xx.xx.xx.xx:2379","attempt":0,"error":"rpc error: code = DeadlineExceeded desc = latest connection error: connection error: desc = \"transport: Error while dialing dial tcp xx.

go-micro v2弃用了consul作为默认的服务发现

月盾
很遗憾,go-micro v2版本不再使用consul作为服务发现中间件,官方文档也没有consul相关的文档,而是默认改用了mdns,生产推荐etcd。 问题:I can’t set registry with consul 解答:《Deprecating Consul in favour of Etcd》 超过4年的时间,Consul一直是Micro的默认服务发现系统之一,为我们提供了良好的服务。实际上,从一开始,它就是用于注册表的默认机制以及入门所需的唯一基础依赖项。 从那时起,世界在不断发展,原生云技术也在不断发展。我们发现了许多与使用Consul的方式有关的问题。这不是对Consul的打击,而是对我们的用例的反思,以及对继续前进的需求。 例如,我们将元数据和服务端点信息进行二进制编码,压缩和base64编码,然后再将它们存储为Consul标签,因为没有其他方法可以这样做。我们还非常严重地滥用Consul的分布式属性,这导致了许多关于raft共识的问题。 不幸的是,我们发现现在该继续前进了。 自2014年以来,Kubernetes真正成为了容器编排和基础服务平台中的一支计算力。因此,etcd成为了他们选择的键值存储的一种,它是基于raft共识构建的分布式键值存储。它已经发展到可以满足kubernetes的规模需求,并且已经以其他开源项目所没有的方式经过了实战测试。 Etcd还是用于二进制数据的非常标准的Get / Put / Delete存储,这意味着我们可以轻松地编码和存储服务元数据,而不会出现零问题。它对所存储数据的格式没有意见。 过去一周中,我们已将etcd迁移为Micro中的默认服务发现机制之一,并将在未来几周内弃用Consul。这是什么意思?好吧,我们将领事移交给我们社区维护的go-plugins存储库,并专注于支持etcd。 我们知道许多用户正在使用Consul,这可能会导致中断。对我们来说,这是通往v2的重大突破,因此我们的下一个发行版将被标记为v2。您可以放心,您的v1发行版将继续按原样运行,但希望我们发布的下一个发行版是micro v2.0.0。 参考项目:micro-service

beego httplib库使用方法

月盾
beego是一个优秀的api,web框架,不只是其丰富的功能特性,更是因为其功能的独立性,可以根据自身需要单独添加使用。 常用的模块有以下这些: session 模块 cache 模块 logs 模块 httplib 模块 context 模块 toolbox 模块 config 模块 i18n 模块 本文要讲解的是httplib客户端请求的使用。 日常开发中不只是要接收请求,还会发起http请求,go本身提供了http库可以实现http请求,不过使用起来略微复杂一些。如果使用的框架是beego的话,那推荐使用httplib。 基本使用方法 import ( "github.com/astaxie/beego/httplib" ) 然后初始化请求方法,返回对象 req := httplib.Get("http://beego.me/") 然后我们就可以获取数据了 str, err := req.String() if err != nil { t.Fatal(err) } fmt.Println(str) 以上是最基本的使用方法,更多文档可以查看httplib文档,本文不再做一次搬运工。 下面提供一些使用实例以供参考: 获取body信息 func RequestByAjax3(region, language string) { req := httplib.Get(fmt.Sprintf("https://m.lagou.com/search.json?city=%s&positionName=%s&pageNo=1&pageSize=1", url.QueryEscape(region), language)) req.Header("Referer", "https://m.lagou.com/search.html") req.Header("Cookie", "JSESSIONID=ABAAAECAAHHAAFD8DC17DEB3DE2DF3C5FCAE8C3D4423759; user_trace_token=20200117101405-234d1d57-b8c1-4d66-956e-c49f35f28f75; LGSID=20200117101406-09c6fa83-38cf-11ea-b2e7-525400f775ce; PRE_LAND=https%3A%2F%2Fm.lagou.com%2Fsearch.html; LGUID=20200117101406-09c6fc06-38cf-11ea-b2e7-525400f775ce; X_HTTP_TOKEN=8e6e6bd15763030e425822975149ec77fc62d73ec7;") req.Header("Host", "m.lagou.com") req.Header("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.

部署golang到服务器

月盾
说起将开发好的程序部署到服务上,常用的有两种方式: 本地编译打包,上传到服务器 git push到远程仓库,在服务器上拉取(编译-打包) 无论以怎样的方式发布,都只有熟悉流程才能得心应手。今天我要说的是golang的部署流程。 如果是在公司内,自然有专人负责发布事宜,也有公司暂无运维人员,这时还是由开发人员负责服务器发布工作,当然,CI/CD这类工具一般也没有搭建起来。但这并不影响我们快速发布。 得益于go的编译速度,整个发布过程可能也就2分钟,接下来说明一下我个人的发布流程: 在项目目录下执行go打包命令 GOOS=linux GOARCH=amd64 go build 由于是要部署到Linux服务器上,所以加上GOOS=linux GOARCH=amd64就可以打包出对应系统的二进制可执行文件。可以将该命令写成脚本文件。 推送代码到git仓库,这一步并不是必须,之所以需要这一步,是因为go只打包*.go文件,并不会打包静态文件,所以还需要把相关静态文件推送的git仓库以便拉取。 上传打包好的二进制可执行文件到服务器的项目目录下。为什么是项目目录?因为还有静态文件需要使用,所以服务器上也要有同样的项目结构。可借助一些工具来上传,我使用了rz命令来上传。 git pull代码,主要是拉取静态文件。 重启应用。 整个过程比较耗时的操作是上传文件和推拉代码,打包和重启应用反而很快,基本是两三秒完成。 golang相对于其他语言,在服务,器上不需要安装运行时,不像Java和nodejs都需要安装正确的运行时版本,go只需要把打包好的二进制可执行文件扔上去就可以执行。