关于golang:gosum中特殊hash如何计算

Golang为了依赖的平安思考,在go.mod的根底上引入了go.sum,go.sum文件的作用次要是记录我的项目依赖的hash值,避免被人批改。 在剖析具体我的项目的go.sum文件后能够发现go.sum中不仅记录了go.mod等的hash值,也记录了整个模块的hash值,这是为什么呢? 这样作的目标次要是在下载整个模块外部的时候可找到子依赖,使得能够并行下载多个依赖。 起初我认为go.sum中记录的hash值是通过sha256间接计算再进行base64编码后的后果,然而在实际操作验证时失去的base64值和go.sum中记录的总是对不上,因而通过查看go的源码(/usr/local/go/src/cmd/go/上面对/usr/local/go/src/cmd/vendor/golang.org/x/mod/sumdb/dirhash包下有援用依赖,这里也是实现go.sum的底层算法外围)发现Golang对文件的hash和整个我的项目的hash计算并不是简略的sha256计算和base64编码。 案例剖析 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=# 下面的大抵意思是<module> <version>/go.mod h1:<sha256hash+base64># 第一段是模块依赖门路# 第二段是版本信息/具体文件# 第三段是针对该文件内容计算的sha256哈希值再进行bash64编码的值# 其中h1代表的意思就是sha256+base64非凡hash计算 go.mod的非凡hash计算 # 输出:go.mod的文件门路# 步骤:# 1.关上go.mod文件读取文件内容进行sha256哈希计算,失去sha256hash# 2.构建新的字符串 base64in = "sha256hash go.mod\n" ,两头用两个空格分隔,最初必须有一个环行符# 3.将base64in作为输出给base64进行编码失去base64encode# 4.字符串拼接失去go.sum中一样的后果 h1:base64encodego.mod的hash计算能够通过shell模仿得出后果,然而对于整个模块的hash计算就无能为力了,上面通过shell命令模仿上述过程 $ sha256sum go.mod 5a93925e1efdeecd8b5755d089fdba6dfb3c04eb85447e8dec8b31cdb44203ab go.mod #sha256hash$ vim base64in.txt 5a93925e1efdeecd8b5755d089fdba6dfb3c04eb85447e8dec8b31cdb44203ab go.mod # base64in字符串,留神上面的环行符不能少,不然和Golang中的后果对不上$ sha256sum base64in.txt | xxd -r -ps | base64+DbmgtsW3Ksw3QccfHlswRDLj07woKf4ku0C0xYA7u0= #base64encode# 最终的后果通过字符串拼接即可失去 h1:+DbmgtsW3Ksw3QccfHlswRDLj07woKf4ku0C0xYA7u0= #在写入go.sum时须要同时写上<module> <version>/go.mod h1:+DbmgtsW3Ksw3QccfHlswRDLj07woKf4ku0C0xYA7u0= 整个模块的非凡hash计算 对整个模块进行hash计算时不是间接对打包好的zip包求hash,而是对解压后的文件进行遍历hash计算后再进行一次总的hash计算,这样作的目标是防止因为zip算法进行打包时因为字节的差别导致对整个zip包的hash后果不统一 # 输出:模块所在目录和模块在的导入门路(在源码中应用时的那个导入门路)# 步骤:# 1. 遍历模块中所有文件# 只思考文件,不思考目录# 疏忽.git目录内的所有文件# 拼接每个文件相对路径与导入门路到一起# 例如:导入门路 "github.com/spf13/cobra",该包中command.go文件通过拼接后为:github.com/spf13/cobra/command.go# 将遍历的后果存储在一个列表中不便前面计算hash# 2. 对上一步失去的列表进行排序 (排序主是保障hash后果统一)# 3.而后进行遍历hash,其计算过程是在排序后的列表中读取一个文件进行sha256 hash 将"ha256hash github.com/spf13/cobra/command.go\n"字符串拼接在后一个文件hash后果后面,以此类推最初失去一个所有文件hash后果的字符串# 4.对下面的长字符串再进行sha256 hash计算失去后果sha256hash进行base64编码失去base64encode# 5.在写入go.sum时相似如下:github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=# 第一行是对整个包的hash后果# 第二行是对go.mod的hash后果下面的过程都能够在Golang源码中找到,在github找到了一位大神也对这种非凡的hash进行了复现:https://hub.fastgit.org/vikyd... ...

June 24, 2021 · 1 min · jiezi

关于golang:dubbogo-社区负责人于雨说

1. 请介绍下本人 好多人认为我名字是于雨,其实是 2012 年应用微信时用的微信名字,起初入职阿里时也用作花名,当初曾经成了我的罕用名了。 从业十一年来始终在服务端基础架构研发一线,工作内容宽泛,涵盖 RPC、NoSQL 存储、实时监控告警、即时通信、音讯推送等方面。钻研内容也挺宽泛,2018 年到 2019 年在 Google 内搜寻 RocksDB,集体的技术博客始终在 first page,我大略也是国内最早的 gogo protobuf 和 pulsar 钻研和鼓吹者。 起家工作语言是 C/C++,2013 年在 chinaunix 上接触到 Go 语言,跟着 go 官网博客开始学习,所以在 Go 语言方面我算是野路子出身,没有人正式带过我。集体酷爱开源,从 2015 年为 Redis 奉献代码开始,2015 年应用 C++11 重构 Muduo,2016 年构建 dubbogo,2018 年接触 Pika。过后除了给 pika 修复 bug,次要构建 Pika 和 Redis 之间数据代理 PikaPort,PikaPort 先后在 360、新浪、脉脉、全民高兴、顺丰、快手、好将来等公司投入使用,直到往年为止不少新浪、好将来和快手等公司不少 DBA 还因为这个工具找我交换。 目前业余次要精力在 dubbogo 社区推动 dubbogo 我的项目。业余时间,除了 dubbogo 社区,还在即时通讯圈子与从业者进行技术交换,IM 方面集体有七八年实战我的项目教训积攒。 2. 聊聊你最近一年正在做的我的项目,它的技术价值怎么?它的行业倒退情况是怎么?你负责我的项目的技术亮点和挑战是否开展讲讲? 集体从 2018 下半年开始全力构建 dubbogo 社区,其次要开源我的项目当然是 https://github.com/apache/dub...。 ...

June 23, 2021 · 2 min · jiezi

关于golang:代码重构实战

背景用户头像上传性能中,服务端上传流程如下: base64解码成字符串图片写入服务器本地长期目录上传到阿里云OSS/七牛云存储图片审核代码如下 type Parms struct { Head string //客户端发送base64字符串}func Upload(p Params){ if p.Head == ""{ return errors.New("img is empty") } str,err := Base64Decode(p.Head) if err != nil{ return err } filename := makeUniqueName() if err := FileWrite(filename,str);err != nil{ return err } err := UploadAliOss(filename) if err != nil{ return err } err := VerifyImg(filename) if err != nil{ return err }}问题以前多个我的项目通过jenkins公布不同的上线指令,每新建一个我的项目,运维须要独自写一套shell命令,无奈复用之前的,保护老本比拟高。起初咱们降级部署形式,只须要批改我的项目部署目录配置文件,具体后续再介绍。 始终以来稳固运行,周末收到反馈呈现问题,用户无奈上传头像。 查看代码的git历史提交记录,近期没有批改代码,排除代码bug报错提醒只在调用FileWrite和UploadAliOss两个办法时呈现在打包镜像的时候没有在容器中创立长期目录,所以无奈写入文件,用户头像无奈上传长期解决手动在容器中创立一个长期目录,保留图片文件。 齐全解决我认为,图片文件全副保留在第三方对象存储中,服务器保留一份是齐全没必要的,只会占用磁盘空间。 咱们能够创立Reader对象把图片文件读取到缓冲区,再上传到第三方对象存储。 代码如下 func Upload(p Params){ if p.Head == ""{ return errors.New("img is empty") } str,err := Base64Decode(p.Head) if err != nil{ return err } filename := makeUniqueName() buf := bytes.NewReader([]byte(str)) err := UploadAliOssBuff(filename,buf) if err != nil{ return err } err := VerifyImg(filename) if err != nil{ return err }}总结在接手保护老我的项目时候,防止不了遇到坑。除了吐槽前人写的蹩脚外,咱们能够尽我所能晋升代码保护品质,让后来者更容易保护。 ...

June 23, 2021 · 1 min · jiezi

关于golang:Go-Modules

61 Go Modules、Go Module Proxy 和 goproxy.cn 【 Go 夜读 】 视频地址: https://www.bilibili.com/vide... https://eddycjy.com/posts/go/...

June 23, 2021 · 1 min · jiezi

关于golang:同样都是使用接口JAVA和Go差距咋就这么大呢

这篇文章将形容代码中常常应用的抢占式接口模式,以及为什么我认为在Go中遵循这种模式通常是不正确的。 什么是抢占式接口接口是一种形容行为的形式,存在于大多数类型语言中。抢占式接口是指开发人员在理论须要呈现之前对接口进行编码。一个示例可能如下所示。 type Auth interface { GetUser() (User, error)}type authImpl struct { // ...}func NewAuth() Auth { return &authImpl}抢占式接口何时有用抢占接口通常用于在Java中,并且大获胜利,这是大部分程序员的想法。置信,很多Go开发者也是这么认为的。这种用法次要区别在于Java具备显式接口,而Go是隐式接口。让咱们看一些示例Java代码,这些代码显示了如果不应用Java中的抢占式接口可能会呈现的艰难。 // auth.javapublic class Auth { public boolean canAction() { // ... }}// logic.javapublic class Logic { public void takeAction(Auth a) { // ... }}当初假如您要更改Logic的takeAction办法中的参数Auth类型的对象,只有它具备canAction()办法即可。可怜的是,你不能。Auth没有在其中实现带有canAction()的接口。你当初必须批改Auth为其提供一个接口,而后您能够在takeAction中承受该接口,或者将Auth包装在一个除了实现的办法之外什么都不做的类中。即便logic.java定义了一个Auth接口以在takeAction()中承受,也可能很难让Auth实现该接口。您可能无权批改Auth,或者Auth可能位于第三方库中。兴许Auth的作者不批准你的批改。兴许在代码库中与共事共享Auth,当初须要在批改之前达成共识。这是心愿的Java代码。 // auth.javapublic interface Auth { public boolean canAction()}// authimpl.javaclass AuthImpl implements Auth {}// logic.javapublic class Logic { public void takeAction(Auth a) { // ... }}如果Auth的作者最后编码并返回一个接口,那么你在尝试扩大takeAction时,永远不会遇到问题。它天然实用于任何Auth接口。在具备显式接口的语言中,当前你会感激过来的本人应用了抢占式接口。 为什么这在Go中不是问题让咱们在Go中设置雷同的状况。 ...

June 22, 2021 · 1 min · jiezi

关于golang:LeetCode-字符串的排列全排列问题剑指offer38

func permutation(s string) []string { //思路:应用寻找下一个增长序列(同官网解题) //步骤:1.升序排序 2.顺次应用寻找下一个序列查找下一个符合条件的序列 3.返回后果 bs := []rune(s) l := len(bs) if l == 0 || l > 8 { //panic("参数长度异样") return []string{} } sort.Slice(bs, func (a, b int) bool { return bs[a] < bs[b] }) list := make([]string, 0, l) for { list = append(list, string(bs)) if !nextPermutation(bs) { break } } return list}func nextPermutation(bs []rune) bool { l := len(bs) //右边起始查找位,依照之前置换准则,只有右数比左数大能力有下一个更大序列 lIdx := l - 2 for lIdx >= 0 && bs[lIdx] >= bs[lIdx+1] { lIdx-- } if (lIdx < 0) { return false } //从最左边开始找,第一个比lIdx大的数即为,最小的大值 rIdx := l - 1 for rIdx >= 0 && bs[lIdx] >= bs[rIdx] { rIdx-- } bs[lIdx], bs[rIdx] = bs[rIdx], bs[lIdx] //依照后面的置换准则,将前面的序列,反转即为新的升序[) reverse(bs[lIdx+1:]) return true}func reverse(list []rune) { for i, l := 0, len(list); i < l/2; i++ { list[i], list[l-1-i] = list[l-1-i], list[i] }}

June 22, 2021 · 1 min · jiezi

关于golang:Go-每日一库之-ozzovalidation

简介ozzo-validation是一个十分弱小的,灵便的数据校验库。与其余基于 struct tag 的数据校验库不同,ozzo-validation认为 struct tag 在应用过程中比拟容易出错。因为 struct tag 实质上就是字符串,齐全基于字符串的解析,无奈利用语言的动态查看机制,很容易在人不知;鬼不觉中写错而不易觉察,理论代码中呈现谬误也很难排查。 ozzo-validation提倡用代码指定规定来进行校验。实际上ozzo是辅助开发 Web 应用程序的一套框架,包含 ORM 库ozzo-dbx,路由库ozzo-routing,日志库ozzo-log,配置库ozzo-config以及最闻名的,应用最为宽泛的数据校验库ozzo-validation。作者甚至还搞出了一个开发 Web 应用程序的模版go-rest-api。 疾速应用本文代码应用 Go Modules。 创立目录并初始化: $ mkdir ozzo-validation && cd ozzo-validation$ go mod init github.com/darjun/go-daily-lib/ozzo-validation装置ozzo-validation库: $ go get -u github.com/go-ozzo/ozzo-validation/v4ozzo-validation的程序写起来都比拟直观: package mainimport ( "fmt" "github.com/go-ozzo/ozzo-validation/v4/is" "github.com/go-ozzo/ozzo-validation/v4")func main() { name := "darjun" err := validation.Validate(name, validation.Required, validation.Length(2, 10), is.URL) fmt.Println(err)}ozzo-validation应用函数Validate()对根本类型值进行校验,传入的第一个参数就是要校验的数据,前面以可变参数传入一个或多个校验规定。上例中对一个字符串做校验。咱们用代码来表白规定: validation.Required:示意值必须设置,对于字符串来说就是不能为空;validation.Length(2, 10):指定长度的范畴;is.URL:is子包中内置了大量的辅助办法,is.URL限度值必须是 URL 格局。Validate()函数依据传入的规定按程序顺次对数据进行校验,直到遇到某个规定校验失败,或所有规定都校验胜利。如果一个规定返回失败,则跳过前面的规定间接返回谬误。如果数据通过了所有规定,则返回一个nil。 运行下面程序输入: must be a valid URL因为字符串"darjun"显著不是一个非法的 URL。如果去掉is.URL规定,则运行输入nil。 构造体应用ValidateStruct()函数能够对一个构造体对象进行校验。咱们须要顺次指定构造体中各个字段的校验规定: type User struct { Name string Age int Email string}func validateUser(u *User) error { err := validation.ValidateStruct(u, validation.Field(&u.Name, validation.Required, validation.Length(2, 10)), validation.Field(&u.Age, validation.Required, validation.Min(1), validation.Max(200)), validation.Field(&u.Email, validation.Required, validation.Length(10, 50), is.Email)) return err}ValidateStruct()承受一个构造体的指针作为第一个参数,前面顺次指定各个字段的规定。字段规定应用validation.Field()函数指定,该函数承受一个指向具体字段的指针,后跟一个或多个规定。下面咱们限度,名字长度在[2, 10]之间,年龄在[1, 200]之间(权且认为当初人类最多能活 200 年),电子邮箱长度在[10, 50]之间,并且应用is.Email限度它必须是一个非法的邮箱地址。同时这 3 个字段都是必填的(用validation.Required限度的)。 ...

June 22, 2021 · 4 min · jiezi

关于golang:golang中对引用传递的误解

前情提要最近看很多教程或者说博客上都说 golang 中的 slice、map、channel、func 都是“援用传递”,然而一方面又说 golang 中所有类型都是值传递,总感觉有些云里雾里的,于是我亲自做了下测试和思考。 这里是代码局部:package mainimport ( "fmt")func test(a *int) { fmt.Println("传入变量的值:", a) fmt.Println("传入变量的地址:", &a)}func main() { va := 666 vad := &va fmt.Println("须要传入的值:", vad) fmt.Println("须要传入的值的地址", &vad) test(vad)}这里是执行后果须要传入的值: 0xc000018658须要传入的值的地址 0xc000006058传入变量的值: 0xc000018658传入变量的地址: 0xc000006060思考讲解也就是说传入和理论接管的值都是指针变量,这个两个指针变量 vad 和 a 的值都为指针所指向的变量 va 的地址 0xc000018658。 而后再看函数外部的这个传入的这个指针 a 的地址(指针)0xc000006060,比照里面寄存指针 vad 的地址 0xc000006058,这两个值是不一样的,阐明指针类型也是值传递,也就是说复制了一份指针的值传递给函数。 所以来说,函数 test 外部的 a 变量和内部的 vad 变量齐全不是同一个货色,a是vad的复制体,然而这两个变量的值寄存的都是va变量的地址,所以操作 a 会对变量 va产生批改。 从这里来看,集体感觉“ slice、map、channel、func 都是援用传递”的表述形式感觉容易引起误会,会狐疑golang的设计对这几个货色非凡看待,是援用传递。 实际上golang的设计,所有类型都是以值的模式传递。只不过对这几种类型来说,底层的实现就是这几种类型的数据创立胜利后,变量所接管的数据是这些类型所对应的地址,或者说被赋值的变量所承受到的是这几种类型的值的地址。而不应该是这几种类型在传递的时候是什么援用类型。

June 21, 2021 · 1 min · jiezi

关于golang:leetcode-401-组合问题

func readBinaryWatch(turnedOn int) []string { if turnedOn >= 9 { //panic("不能正确示意") return []string{} } maxHnum := 3 maxMnum := 5 returnData := make([]string, 0, 255) for hourNum, minuteNum := 0, 0; hourNum <= maxHnum; hourNum++ { minuteNum = turnedOn - hourNum if (minuteNum < 0) { break; } if minuteNum >maxMnum { continue } hourList := getHour(hourNum) minuteList := getMinute(minuteNum) for _, h := range hourList { for _, m := range minuteList { returnData = append(returnData, h + ":" + m) } } } return returnData}func getMinute(n int) []string { if (n == 0) { return []string{"00"} } cSet := []int{1, 2, 4, 8, 16, 32} num := make([]string, 0, 60) list := sumNumItem(cSet, n) for _, v := range list { if v > 59 { continue } num = append(num, fmt.Sprintf("%02d", v)) } return num}func getHour(n int) []string { if (n == 0) { return []string {"0"} } cSet := []int{1, 2, 4, 8} num := make([]string, 0, 60) list := sumNumItem(cSet, n) for _, v := range list { if v > 11 { continue } num = append(num, fmt.Sprintf("%d", v)) } return num}func sumNumItem(list []int, num int) []int { l := len(list) if num > l { //panic("求和数量异样") return []int{} } if num == 1 { return list } returnData := make([]int, 0, 60) //应用位图对应形式,顺次确认站位组合 indexList := make([]int, l) for i := 0; i < num; i++ { indexList[i] = 1 } returnData = append(returnData, sumIdxNum(list, indexList)) for { find := false for i := 0; i < l - 1; i++ { if (indexList[i] == 1 && indexList[i + 1] == 0) { find = true indexList[i], indexList[i + 1] = 0, 1 if indexList[0] == 0 && i > 1 { resetZeroIdx(indexList, i) } returnData = append(returnData, sumIdxNum(list, indexList)) break } } if !find { break } } return returnData}func sumIdxNum(list []int, idxSet []int) int { num := 0 for idx, b := range idxSet { if b == 1 { num += list[idx] } } return num}func resetZeroIdx(idxList []int, k int) { sum := 0 for i := 0; i < k; i++ { if idxList[i] == 1 { sum++ } } //将前sum个改为1,之后的改为0 for i := 0; i < k; i++ { if i < sum { idxList[i] = 1 } else { idxList[i] = 0 } }}

June 21, 2021 · 2 min · jiezi

关于golang:gcli-脚手架工具

_ _ __ _ ___| (_) / _` |/ __| | || (_| | (__| | | \__, |\___|_|_| |___/ gcli 脚手架工具 能够疾速搭建我的项目框架,内置两套我的项目根底框架 (echo-framework、iris-framework),该我的项目根底框架曾经集成了gorm读写拆散、jwt、socket.io、nsq等,迅速开发罕用的性能,无需从零搭建,快捷开发 装置go get -u github.com/nelsonkti/gcliwindows 用户: 若想全局应用gcli命令,请将该命令配置(${GOPATH}/src/bin/gcli.exe)退出零碎的环境变量中用法gcli -h 创立我的项目 gcli create demo指定创立我的项目的门路 gcli create demo --path /Users/Go/src/*****指定框架,抉择 echo、iris 默认 echo gcli create demo --fw echo我的项目框架具体介绍 // 以 echo 框架为根底的我的项目框架 echo-framework:https://github.com/nelsonkti/echo-framework// 以 iris 框架为根底的我的项目框架iris-framework:https://github.com/nelsonkti/iris-framework环境要求go >= 1.13 windows 用户:若想全局应用gcli命令,请将该命令配置 ${GOPATH}/src/bin/gcli.exe 退出零碎的环境变量中

June 21, 2021 · 1 min · jiezi

关于golang:go协程并发和swoole协程并发底层执行细节

https://www.bilibili.com/vide...

June 21, 2021 · 1 min · jiezi

关于golang:gomod-的使用

gomod 的作用失常状况下,当咱们在 go 文件里 import 依赖包之后,在第一次构建程序时,就会拉取依赖包的最新版本到本地环境上。 如果咱们应用 docker 或 k8s 这种每次都会从新构建环境的公布形式时, 那么每次都会 go get 到最新的依赖包。 而这种部署形式是有问题的,因为谁也不晓得最新的依赖包批改了哪些内容。如果每次可能拉取指定的版本,那么就能够解决这个问题了,这就是 go mod 所解决的问题。 在 go 1.11 之前也有相似的版本解决方案,比方 像 dep 或者 vendor。起初 golang 官网在 1.11 推出了对应的版本管理工具: go module,也就是咱们常常在我的项目里看到的 go mod 文件。 在这个文件里记录了以后我的项目里所有的依赖包名以及对应的版本号,后续在构建编译时,就会依据对应的版本号去拉取依赖包。 gomod 文件的创立命令: go mod init 我的项目名 比方,我的我的项目是 manage, 那么就能够这样应用: go mod init manage此时就会在以后我的项目下生成 gomod 文件: 留神, 如果以后的我的项目是要给内部应用的,最好是配合代码仓库命名,比方 go mod init github.com/lincoln/manage 以便其余我的项目能够go get 援用失去。 依赖包的版本生成下面的命令只是生成一个 gomod 文件,至于依赖包的版本号信息临时是还没有生成的,能够应用上面2个命令进行获取: 命令1:go get 包名如果依赖包比拟多,那么go get 就比拟麻烦了。能够应用另外一个命令: ...

June 20, 2021 · 1 min · jiezi

关于golang:文件压缩的一种实现

记录一下对文件进行压缩的实现,这里只列出了外围代码 /*函数名: makeTarGzip 制作压缩包,进步在线传输速率参 数: encFile , 密文文件 signFile , 签名文件返回值: 压缩后文件名 错误信息创立工夫及创建者: 2021-06-15 Yuan_sr*/func makeTarGzip(encFile, signFile string) (string, error){ tarGzipFileName = "example.tar.gz" // file write fw, err := os.Create(tarGzipFileName) if err != nil { return "", err } defer fw.Close() // gzip write gw := gzip.NewWriter(fw) defer gw.Close() // tar write tw := tar.NewWriter(gw) defer tw.Close() // 关上文件夹 dir, err := os.Open("./") if err != nil { return "", err } defer dir.Close() // 读取文件列表 fis, err := dir.Readdir(0) if err != nil { return "", err } for _, fi := range fis{ if fi.IsDir(){ continue } if fi.Name() == encFile || fi.Name() == signFile{ //去掉这个条件能够实现该目录下所有文件的压缩 ef, err := os.Open(dir.Name() + "/" + fi.Name()) if err != nil { return "", err } defer ef.Close() //信息头 h := new(tar.Header) h.Name = fi.Name() h.Size = fi.Size() h.Mode = int64(fi.Mode()) h.ModTime = fi.ModTime() //写信息头 err = tw.WriteHeader(h) if err != nil { return "", err } _, err = io.Copy(tw, ef) if err != nil { return "", err } } } return tarGzipFileName, err}/*函数名: unTarGzip 文件解压缩参 数: tarGzipFile , 待解压缩文件返回值: 错误信息创立工夫及创建者: 2021-06-15 Yuan_sr*/func unTarGzip (tarGzipFile string) error { fr, err := os.Open(tarGzipFile) if err != nil { return err } defer fr.Close() gr, err := gzip.NewReader(fr) if err != nil { return err } defer gr.Close() tr := tar.NewReader(gr) for { h, err := tr.Next() if err == io.EOF { break } if err != nil { return err } fw, err := os.OpenFile(h.Name, os.O_CREATE | os.O_WRONLY, 0755) if err != nil { return err } defer fw.Close() _, err = io.Copy(fw, tr) if err != nil { return err } } return nil}

June 20, 2021 · 2 min · jiezi

关于golang:密码技术应用SM2文件签名验签

记录一下对一些稍大文件进行SM2签名验签的实现,这里只列出了外围代码,其余不波及的代码或者有任何疑难能够查看我之前写的明码技术专题博客 /*函数名: sm2Sign sm2 签名算法实现对文件的签名参 数: filePathIn , 待签名文件 priKey , 签名私钥文件返回值: 签名后文件名 错误信息创立工夫及创建者: 2021-06-17 Yuan_sr*/func sm2Sign (filePathIn, priKey string) (string, error){ //1.关上磁盘的私钥文件 file, err := os.Open(priKey) if err != nil { return "", err } defer file.Close() //2.将私钥文件中的内容读出 fileInfo, err := file.Stat() if err != nil { return "", err } buf := make([]byte, fileInfo.Size()) _, err = file.Read(buf) if err != nil { return "", err } //3.将pem格局私钥文件解码并反序列化 privateKeyFromPem, err := gmx509.ReadPrivateKeyFromPem(buf, nil) if err != nil { return "", err } //4.创立一个哈希对象 hash := sm3.New() inFile, err := os.Open(filePathIn) if err != nil { return "", err } defer inFile.Close() for { n, err := inFile.Read(buf) if err == io.EOF{ break } if err != nil && err != io.EOF { return "", err } _, err = hash.Write(buf[:n]) if err != nil { return "", err } } hashed := hash.Sum(nil) //5.签名 signText, err := privateKeyFromPem.Sign(rand.Reader, hashed, nil) if err != nil { return "", err } outFile, err := os.Create(signFileName) if err != nil { return "", err } defer outFile.Close() outFile.Write(signText) return signFileName, nil}/*函数名: sm2Verify sm2 验签算法实现对文件的验签参 数: encFile , 密文文件 signFile , 签名文件 pubKey , 验签公钥返回值: 验签后果 错误信息创立工夫及创建者: 2021-06-17 Yuan_sr*/func sm2Verify(encFile, signFile, pubKey string) (bool, error) { //1.关上磁盘公钥文件 file, err := os.Open(pubKey) if err != nil { return false, err } defer file.Close() fileInfo, err := file.Stat() if err != nil { return false, err } buf := make([]byte, fileInfo.Size()) _, err = file.Read(buf) if err != nil { return false, err } //2.将pem格局公钥解码并反序列化 publicKeyFromPem, err := gmx509.ReadPublicKeyFromPem(buf) if err != nil { return false, err } //3.进行哈西运算 hash := sm3.New() inFile, err := os.Open(dvOutPath + encFile) if err != nil { return false, err } defer inFile.Close() for { n, err := inFile.Read(buf) if err == io.EOF{ break } if err != nil && err != io.EOF { return false, err } _, err = hash.Write(buf[:n]) if err != nil { return false, err } } hashed := hash.Sum(nil) //4.读取承受到的签名值 sr, err := os.Open(dvOutPath + signFile) if err != nil { return false, err } defer sr.Close() srInfo, err := sr.Stat() if err != nil { return false, err } srBuf := make([]byte, srInfo.Size()) _, err = sr.Read(srBuf) if err != nil { return false, err } //5.签名认证 verify := publicKeyFromPem.Verify(hashed, srBuf) return verify, nil}

June 20, 2021 · 2 min · jiezi

关于golang:密码技术应用RSA文件签名验签

记录一下对一些稍大文件进行RSA签名验签的实现,这里只列出了外围代码,其余不波及的代码或者有任何疑难能够查看我之前写的明码技术专题博客 /*函数名: rsaSign rsa 签名算法实现对文件的签名参 数: filePathIn , 待签名文件 priKey , 签名私钥文件返回值: 签名后文件名 错误信息创立工夫及创建者: 2021-06-15 Yuan_sr*/func rsaSign (filePathIn, priKey string) (string, error){ //1.关上磁盘的私钥文件 file, err := os.Open(priKey) if err != nil { return "", err } defer file.Close() //2.将私钥文件中的内容读出 fileInfo, err := file.Stat() if err != nil { return "", err } buf := make([]byte, fileInfo.Size()) _, err = file.Read(buf) if err != nil { return "", err } //3.应用pem对数据解码,失去pem.Block构造体变量 block, _ := pem.Decode(buf) //4.x509将数据解析成私钥构造体失去私钥 privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return "", err } //5.创立一个哈希对象 hash := sha256.New() inFile, err := os.Open(filePathIn) if err != nil { return "", err } defer inFile.Close() for { n, err := inFile.Read(buf) if err == io.EOF{ break } if err != nil && err != io.EOF { return "", err } _, err = hash.Write(buf[:n]) if err != nil { return "", err } } hashed := hash.Sum(nil) signText, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed) if err != nil { return "", err } outFile, err := os.Create(signFileName) if err != nil { return "", err } defer outFile.Close() outFile.Write(signText) return signFileName, nil}/*函数名: rsaVerify rsa 验签算法实现对文件的验签参 数: encFile , 密文文件 signFile , 签名文件 pubKey , 验签公钥返回值: 验签后果 错误信息创立工夫及创建者: 2021-06-15 Yuan_sr*/func rsaVerify(encFile, signFile, pubKey string) (bool, error) { //1.关上磁盘公钥文件 file, err := os.Open(pubKey) if err != nil { return false, err } defer file.Close() fileInfo, err := file.Stat() if err != nil { return false, err } buf := make([]byte, fileInfo.Size()) _, err = file.Read(buf) if err != nil { return false, err } //2.应用pem解码失去pem.block构造体变量 block, _ := pem.Decode(buf) //3.应用x509对pem.block中的变量进行解析失去一个公钥接口 pubKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return false, err } //4.进行类型断言失去公钥构造体 publicKey := pubKeyInterface.(*rsa.PublicKey) //5.进行哈西运算 hash := sha256.New() inFile, err := os.Open(dvOutPath + encFile) if err != nil { return false, err } defer inFile.Close() for { n, err := inFile.Read(buf) if err == io.EOF{ break } if err != nil && err != io.EOF { return false, err } _, err = hash.Write(buf[:n]) if err != nil { return false, err } } hashed := hash.Sum(nil) //6.读取承受到的签名值 sr, err := os.Open(dvOutPath + signFile) if err != nil { return false, err } defer sr.Close() srInfo, err := sr.Stat() if err != nil { return false, err } srBuf := make([]byte, srInfo.Size()) _, err = sr.Read(srBuf) if err != nil { return false, err } //7.签名认证 err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed, srBuf) if err != nil { return false, err } return true, nil}

June 20, 2021 · 2 min · jiezi

关于golang:密码技术应用SM4文件加解密

记录一下对一些稍大文件进行SM4加解密的实现,这里只列出了外围代码,其余不波及的代码或者有任何疑难能够查看我之前写的明码技术专题博客 var key = []byte("1234567812345678")var iv = []byte("1111111122222222")/*函数名: paddingLastGroup 对 CBC 加密模式的加密算法提供最初一个分块的明文数据填充参 数: plainText , 明文数据 blockSize , CBC 分块大小返回值: 填充后的明文数据创立工夫及创建者: 2021-06-15 Yuan_sr*/func paddingLastGroup(plainText []byte, blockSize int) []byte{ padNum := blockSize - len(plainText) % blockSize char := []byte{byte(padNum)} newPlain := bytes.Repeat(char, padNum) newText := append(plainText, newPlain...) return newText}/*函数名: unpaddingLastGroup 对 CBC 加密模式的加密算法提供最初一个分块的明文数据去填充参 数: plainText , 明文数据返回值: 去填充后的明文数据创立工夫及创建者: 2021-06-15 Yuan_sr*/func unpaddingLastGroup(plainText []byte) []byte{ length := len(plainText) lastChar := plainText[length - 1] number := int(lastChar) return plainText[:length - number]}/*函数名: sm4Enctrpt sm4 加密算法实现对文件的加密参 数: filePathIn , 待加密文件 key , 加密密钥返回值: 加密后密文文件名 错误信息创立工夫及创建者: 2021-06-17 Yuan_sr*/func sm4Enctrpt(filePathIn string, key []byte) (string, error){ inFile, err := os.Open(filePathIn) if err != nil { return "", err } defer inFile.Close() outFile, err := os.Create(encryptFileName) if err != nil { return "", err } defer outFile.Close() buf := make([]byte, bufferSize) //初始化一个底层加密算法 block, err := sm4.NewCipher(key) if err != nil { return "", err } //抉择加密模式 blockMode := cipher.NewCBCEncrypter(block, iv) for { n, err := inFile.Read(buf) if err == io.EOF{ break } if err != nil && err != io.EOF { return "", err } //判断时最初一段数据则进行数据填充 if n != bufferSize{ groupData := paddingLastGroup(buf[:n], block.BlockSize()) n = len(groupData) buf = make([]byte, n) buf = groupData } cipherText := make([]byte, n) blockMode.CryptBlocks(cipherText, buf[:n]) _, err = outFile.Write(cipherText) if err != nil { return "", err } } //outFile.Write(iv) return encryptFileName, nil}/*函数名: sm4Decrypt sm4 解密算法实现对文件的解密参 数: cipherFile , 密文文件 key , 解密密钥返回值: 解密后文件名 错误信息创立工夫及创建者: 2021-06-17 Yuan_sr*/func sm4Decrypt(cipherFile string, key []byte) (string, error){ //字符串解决 imgTagName := getImgTagName(dvImgName) plainFileName := imgTagName + ".tar" if dvOutPath != "./" { err := os.MkdirAll(dvOutPath, 0755) if err != nil { return "", err } plainFileName = dvOutPath + imgTagName + ".tar" } //1.创立一个aes底层明码接口 block, err := sm4.NewCipher(key) if err != nil { return "", err } //2.抉择解密模式 blockMode := cipher.NewCBCDecrypter(block, iv) //3.解密 fr, err := os.Open(dvOutPath + cipherFile) if err != nil { return "", err } defer fr.Close() fileInfo, err := fr.Stat() if err != nil { return "", err } blockNum := fileInfo.Size() / bufferSize var num int64 fw, err := os.Create(plainFileName) if err != nil { return "", err } defer fw.Close() buf := make([]byte, bufferSize) for { num += 1 n, err := fr.Read(buf) if err == io.EOF{ break } if err != nil && err != io.EOF { return "", err } plainText := make([]byte, n) blockMode.CryptBlocks(plainText, buf[:n]) //判断时最初一段数据则进行数据去填充 if num == blockNum + 1{ plainText = unpaddingLastGroup(plainText) n = len(plainText) } _, err = fw.Write(plainText[:n]) if err != nil { return "", err } } return plainFileName, nil}

June 20, 2021 · 2 min · jiezi

关于golang:密码技术应用AES文件加解密

记录一下对一些稍大文件进行AES加解密的实现,这里只列出了外围代码,其余不波及的代码或者有任何疑难能够查看我之前写的明码技术专题博客 var key = []byte("1234567812345678")var iv = []byte("1111111122222222")/*函数名: paddingLastGroup 对 CBC 加密模式的加密算法提供最初一个分块的明文数据填充参 数: plainText , 明文数据 blockSize , CBC 分块大小返回值: 填充后的明文数据创立工夫及创建者: 2021-06-15 Yuan_sr*/func paddingLastGroup(plainText []byte, blockSize int) []byte{ padNum := blockSize - len(plainText) % blockSize char := []byte{byte(padNum)} newPlain := bytes.Repeat(char, padNum) newText := append(plainText, newPlain...) return newText}/*函数名: unpaddingLastGroup 对 CBC 加密模式的加密算法提供最初一个分块的明文数据去填充参 数: plainText , 明文数据返回值: 去填充后的明文数据创立工夫及创建者: 2021-06-15 Yuan_sr*/func unpaddingLastGroup(plainText []byte) []byte{ length := len(plainText) lastChar := plainText[length - 1] number := int(lastChar) return plainText[:length - number]}/*函数名: aesEnctrpt aes 加密算法实现对文件的加密参 数: filePathIn , 待加密文件 key , 加密密钥返回值: 加密后密文文件名 错误信息创立工夫及创建者: 2021-06-15 Yuan_sr*/func aesEnctrpt(filePathIn string, key []byte) (string, error){ inFile, err := os.Open(filePathIn) if err != nil { return "", err } defer inFile.Close() fileInfo, err := inFile.Stat() if err != nil { return "", err } blockNum := fileInfo.Size() / bufferSize var num int64 outFile, err := os.Create(encryptFileName) if err != nil { return "", err } defer outFile.Close() buf := make([]byte, bufferSize) //初始化一个底层加密算法 block, err := aes.NewCipher(key) if err != nil { return "", err } //抉择加密模式 //stream := cipher.NewCTR(block, iv) blockMode := cipher.NewCBCEncrypter(block, iv) for { num += 1 n, err := inFile.Read(buf) if err == io.EOF{ break } if err != nil && err != io.EOF { return "", err } //判断时最初一段数据则进行数据填充 if num == blockNum + 1{ groupData := paddingLastGroup(buf[:n], block.BlockSize()) n = len(groupData) buf = make([]byte, n) buf = groupData } cipherText := make([]byte, n) //stream.XORKeyStream(cipherText, buf[:n]) blockMode.CryptBlocks(cipherText, buf[:n]) _, err = outFile.Write(cipherText) if err != nil { return "", err } } //outFile.Write(iv) return encryptFileName, nil}/*函数名: aesDecrypt aes 解密算法实现对文件的解密参 数: cipherFile , 密文文件 key , 解密密钥返回值: 解密后文件名 错误信息创立工夫及创建者: 2021-06-15 Yuan_sr*/func aesDecrypt(cipherFile string, key []byte) (string, error){ plainFileName = "plainText.file" //1.创立一个aes底层明码接口 block, err := aes.NewCipher(key) if err != nil { return "", err } //2.抉择解密模式 blockMode := cipher.NewCBCDecrypter(block, iv) //3.解密 fr, err := os.Open(dvOutPath + cipherFile) if err != nil { return "", err } defer fr.Close() fileInfo, err := fr.Stat() if err != nil { return "", err } blockNum := fileInfo.Size() / bufferSize var num int64 fw, err := os.Create(plainFileName) if err != nil { return "", err } defer fw.Close() buf := make([]byte, bufferSize) for { num += 1 n, err := fr.Read(buf) if err == io.EOF{ break } if err != nil && err != io.EOF { return "", err } plainText := make([]byte, n) blockMode.CryptBlocks(plainText, buf[:n]) //判断时最初一段数据则进行数据去填充 if num == blockNum + 1{ plainText = unpaddingLastGroup(plainText) n = len(plainText) } _, err = fw.Write(plainText[:n]) if err != nil { return "", err } } return plainFileName, nil}

June 20, 2021 · 2 min · jiezi

关于golang:数据结构队列数组的一种实现

单向队列(数组实现) package mainimport ( "errors" "fmt" "os")//队列是一种重要的数据结构,利用也绝对宽泛,实现队列的形式次要有数组和链表两种形式//上面首先应用数组实现一个简略的单向队列//队列的信息包含队列的大小、队列的存储模式(数组)、队列的头(不指向第一元素)、队列的尾(指向最初一个元素)//对列的操作方法次要有向队列增加一个元素、从队列中获取一个元素、显示队列中的元素//定义一个构造体用于保留队列的信息type signalQueue struct { maxSize int array [5]int //模仿队列 head int tail int}//定义一个向队列增加元素的办法func (q *signalQueue) Push(val int) error{ //先判断队列是否已满 if q.tail == q.maxSize - 1 { //tail含最初一个元素 return errors.New("队列已满") } q.tail++ //向后挪动尾部 q.array[q.tail] = val return nil}//定义一个从队列获取元素的办法func (q *signalQueue) Pop() (val int, err error) { //判断队列是否为空 if q.tail == q.head { return -1, errors.New("队列为空") } q.head++ //因为head指向第一个元素的后面,因而要先向后挪动 val = q.array[q.head] return val, nil}//定义一个显示队列元素的办法func (q *signalQueue) List() error { //找到队首(不含第一个元素),而后遍历到队尾 for i := q.head + 1; i <= q.tail; i++{ fmt.Printf("array[%d]=%d\t", i, q.array[i]) } fmt.Println() return nil}func main (){ queue := &signalQueue{ maxSize: 5, array: [5]int{}, head: -1, tail: -1, } var key string var val int //增加数据 for { fmt.Println("1.add增加数据到队列") fmt.Println("2.get从队列获取数据") fmt.Println("3.show显示队列") fmt.Println("4.exit退出队列") fmt.Scanln(&key) switch key { case "add": fmt.Println("输出你要入队列的数据") fmt.Scanln(&val) err := queue.Push(val) if err != nil { fmt.Println(err) } else { fmt.Println("胜利入队") } case "get": val, err := queue.Pop() if err != nil { fmt.Println(err) } else { fmt.Println("出队列的数为:", val) } case "show": queue.List() case "exit": os.Exit(0) } }}//总结://下面实现的单向队列存在的一个最大的问题是://没有无效的利用数据的无效空间,会存在增加数据显示队列已满,然而获取数据又显示队列为空的状况//解决的方法就是将数组的尾部和头部连贯到一起实现一个环形队列即可解决下面的问题环形队列(数组实现) ...

June 20, 2021 · 2 min · jiezi

关于golang:数据结构稀疏矩阵的一种实现

package mainimport ( "encoding/json" "fmt" "os")//定义一个构造体保留原始矩阵的信息type sparseNode struct { Row int Col int Val int}//定义一个构造体保留反序列化后矩阵的信息type unSparseNode struct { Row int Col int Val int}func main(){ //1.定义一个原始矩阵 var arr [11][11]int arr[1][2] = 1 arr[2][3] = 2 //2.打印原始矩阵 for _, v1 := range arr{ for _, v2 := range v1{ fmt.Printf("%d ", v2) } fmt.Println() } //3.将原始矩阵转换为一个稠密矩阵 //思路: // 定义一个切片用来作为稠密矩阵 // 稠密矩阵只保留原始矩阵的非凡信息: // 稠密矩阵的第一个元素保留矩阵的大小和默认值 // 稠密矩阵中保留原始矩阵中不是默认值的行、列、值的信息 //定义一个稠密矩阵切片 var sparseArr []sparseNode //先保留稠密矩阵中第一个元素,即原始矩阵的大小和默认值 valNode := sparseNode{ Row: 11, Col: 11, Val: 0, } sparseArr = append(sparseArr, valNode) //接着保留原始矩阵中的非凡信息到稠密矩阵 for i, v1 := range arr{ for j, v2 := range v1{ if v2 != 0{ valNode := sparseNode{ Row: i, Col: j, Val: v2, } sparseArr = append(sparseArr, valNode) } } } //打印稠密矩阵 for _, v := range sparseArr{ fmt.Printf("%d %d %d\n", v.Row, v.Col, v.Val) } //4.到这里就实现了原始矩阵转换稠密矩阵,依据理论利用能够将稠密矩阵落盘到文件或者通过网络发送给其他人 //其中落盘的形式有多种,能够依照稠密矩阵间接落盘,或者将稠密矩阵进行序列化为json字符串落盘到文件 //这里应用较为简单的序列化落盘形式实现,json字符串既能够落盘,也能够网络传输 bytes, err := json.Marshal(&sparseArr) if err != nil { panic(err) } //5.打印序列化后的JSON字符串 fmt.Println(string(bytes)) //6.到这里就能够将JSON字符串进行落盘或者网络传输了,这里先实现落盘存储 w, err := os.Create("sparseArr.json") if err != nil { panic(err) } defer w.Close() _, err = w.Write(bytes) if err != nil { panic(err) } //------------------------还原稠密矩阵为原始矩阵------------------------------ //1.关上磁盘文件读取数据 r, err := os.Open("sparseArr.json") if err != nil { panic(err) } defer r.Close() fileInfo, err := r.Stat() if err != nil { panic(err) } buf := make([]byte, fileInfo.Size()) _, err = r.Read(buf) if err != nil { panic(err) } //2.反序列化JSON字符串,首先定义一个构造体unSparseNode用来保留反序列化数据 //再定义一个切片存储反序列化后的所有数据 var unSparseArr []unSparseNode err = json.Unmarshal(buf, &unSparseArr) if err != nil { panic(err) } //3.打印反序列化后的数据 fmt.Println(unSparseArr) //4.依据反序列化后的切片数据还原原始矩阵 //在初始化二维数组的时候遇到了一个问题,就是在初始化的时候数组大小必须指定常量 //如果哪位大神有其余思路欢送留言一起探讨,我临时就这样写死了 unArr := make([][11]int, unSparseArr[0].Row) for i, v1 := range unSparseArr{ if i != 0 { unArr[v1.Row][v1.Col] = v1.Val } } //5.打印还原后的原始矩阵 for _, v1 := range unArr{ for _, v2 := range v1{ fmt.Printf("%d ", v2) } fmt.Println() }}

June 20, 2021 · 2 min · jiezi

关于golang:冒泡排序数组的一种实现

package mainimport "fmt"//数组的冒泡排序func BubbleSort(arr *[5]int){ for i := 0; i < len(*arr) - 1; i++{ for j := 0; j < len(*arr) - 1 - i; j++{ if (*arr)[j] > (*arr)[j + 1] { (*arr)[j], (*arr)[j + 1] = (*arr)[j + 1], (*arr)[j] } } }}func main(){ arr := [5]int{24, 13, 67, 54, 80} fmt.Println("排序前:", arr) BubbleSort(&arr) fmt.Println("排序后:", arr)}

June 20, 2021 · 1 min · jiezi

关于golang:二分查找的一种实现

package mainimport "fmt"func BinaryFind(arr *[6]int, leftIndex int, rightIndex int, findVal int){ if leftIndex > rightIndex{ fmt.Println("没有找到", findVal) } middleIndex := (leftIndex + rightIndex) / 2 if (*arr)[middleIndex] > findVal { BinaryFind(arr, leftIndex, middleIndex - 1, findVal) }else if (*arr)[middleIndex] < findVal{ BinaryFind(arr, middleIndex + 1, rightIndex, findVal) }else{ fmt.Printf("找到%v了,下标为:%v\n", findVal, middleIndex) }}func main(){ //有序数组 arr := [6]int{1, 3, 5, 7, 9, 11} BinaryFind(&arr, 0, len(arr) - 1, 11)}

June 20, 2021 · 1 min · jiezi

关于golang:GO-中-Chan-实现原理分享

GO 中 Chan 实现原理分享嗨,我是小魔童哪吒,还记得咱们之前分享过GO 通道 和sync包的应用吗?咱们来回顾一下 分享了通道是什么,通道的品种无缓冲,有缓冲,单向通道具体对应什么对于通道的具体实际分享了对于通道的异常情况整顿简略分享了sync包的应用要是对上述内容还有点趣味的话,欢送查看文章 GO通道和 sync 包的分享 chan 是什么?是一种非凡的类型,是连贯并发goroutine的管道 channel 通道是能够让一个 goroutine 协程发送特定值到另一个 goroutine 协程的通信机制。 通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规定,保障收发数据的程序,这一点和管道是一样的 一个协程从通道的一头放入数据,另一个协程从通道的另一头读出数据 每一个通道都是一个具体类型的导管,申明 channel 的时候须要为其指定元素类型。 本篇文章次要是分享对于通道的实现原理,对于通道的应用,能够查看文章 GO通道和 sync 包的分享 ,这里有具体的阐明 GO 中 Chan 的底层数据结构理解每一个组件或者每一个数据类型的实现原理,咱们都会去看源码中的数据结构是如何设计的 同样,咱们一起来看看 GO 的 Chan 的数据结构 GO 的 Chan 的源码实现是在 : src/runtime/chan.go type hchan struct { qcount uint // total data in the queue dataqsiz uint // size of the circular queue buf unsafe.Pointer // points to an array of dataqsiz elements elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // send index recvx uint // receive index recvq waitq // list of recv waiters sendq waitq // list of send waiters // lock protects all fields in hchan, as well as several // fields in sudogs blocked on this channel. // // Do not change another G's status while holding this lock // (in particular, do not ready a G), as this can deadlock // with stack shrinking. lock mutex}hchan 是实现通道的外围数据结构,对应的成员也是不少,咱们依据源码正文一个参数一个参数的来看看 ...

June 19, 2021 · 6 min · jiezi

关于golang:GO-中-map-的实现原理

GO 中 map 的实现原理嗨,我是小魔童哪吒,咱们来回顾一下上一次分享的内容 分享了切片是什么切片和数组的区别切片的数据结构切片的扩容原理空切片 和 nil 切片的区别要是对 GO 的slice 原理还有点趣味的话,欢送查看文章 GO 中 slice 的实现原理 map 是什么?是 GO 中的一种数据类型,底层实现是 hash 表,看到 hash 表 是不是会有一点相熟的感觉呢 咱们在写 C/C++ 的时候,外面也有 map 这种数据结构,是 key - value 的模式 可是在这里咱们可别搞混了,GO 外面的 map 和 C/C++ 的map 可不是同一种实现形式 C/C++ 的 map 底层是 红黑树实现的GO 的 map 底层是hash 表实现的可是别忘了C/C++中还有一个数据类型是 unordered_map,无序map,他的底层实现是 hash 表,与咱们GO 外面的 map 实现形式相似 map 的数据结构是啥样的?后面说到的 GO 中 string 实现原理,GO 中 slice 实现原理, 都会对应有他们的底层数据结构 哈,没有例外,明天说的 map 必然也有本人的数据结构, 相对来说会比前者会多一些成员,咱们这就来看看吧 ...

June 19, 2021 · 4 min · jiezi

关于golang:Go-面试系列六-err-shadow-是什么鬼

在日常工作中,咱们常常应用 err != nil 来判断程序或函数是否报错,或者应用 defer {recover = err} 来判断是否有 panic 严重错误,但稍不留神,很容易掉进 err shadow 的陷阱。 1. 变量作用域package mainimport "fmt"func main() { x := 100 func() { x := 200 // x will shadow outer x fmt.Println(x) }() fmt.Println(x)}输入如下: 200100后果剖析:x 变量在 func 外面打印为 200,在外层打印为 100,这就是变量的作用域(variable scope)。func 外面的变量 x 是一个新变量,只不过与外层 x 重名了(variable redeclaration),此时里层 x 的作用域仅限于 func {} block,而外层 x 的作用域则是 main {} block,此时里层变量 x 产生了 variable shadowing,外层 x 不受影响,仍然是 100。 改一下写法: ...

June 19, 2021 · 3 min · jiezi

关于golang:命令行工具cobra的使用

装置cobra go get -v github.com/spf13/cobra/cobra切换到GOPATH的src目录下并创立一个新文件夹:demo cd $GOPATH/srcmkdir democd demo初始cobra $ ../../bin/cobra Cobra is a CLI library for Go that empowers applications.This application is a tool to generate the needed filesto quickly create a Cobra application.Usage: cobra [command]Available Commands: add Add a command to a Cobra Application help Help about any command init Initialize a Cobra ApplicationFlags: -a, --author string author name for copyright attribution (default "YOUR NAME") --config string config file (default is $HOME/.cobra.yaml) -h, --help help for cobra -l, --license string name of license for the project --viper use Viper for configuration (default true)Use "cobra [command] --help" for more information about a command.能够看到cobra反对两个命令,一个是init, 一个是add,其中init是初始化一个cobra工程,add是给工程中增加一个子命令 ...

June 18, 2021 · 6 min · jiezi

关于golang:Go-在不影响原切片情况下返回一个去除某个元素的新切片

在用Go时发现的一个小坑 在Python中,返回一个去除原列表中下标为i的元素的新列表,能够用切片语法,将下标i前后的列表切开再组合成一个新的切片,这是不言而喻的。 list1=[1,2,3,4,5]list2=list1[:2]+list1[3:]print(list1,list2)# [1, 2, 3, 4, 5] [1, 2, 4, 5]但在Go中,仿佛没有这么想当然。如果想当然的用append()去切割合并切片,会影响原来的切片。 slice1 := []int{1, 2, 3, 4, 5}fmt.Println(slice1)slice2 := append(slice1[:2], slice1[3:]...)fmt.Println(slice2)fmt.Println(slice1)// 输入// [1 2 3 4 5]// [1 2 4 5]// [1 2 4 5 5]为什么会这样?首先,Go的切片只是个构造体,蕴含了一个指向底层数组的指针、长度、容量。当咱们应用切片语法b:=a[low:high]时,新的切片b只是获取了一个构造体,蕴含指向a底层数组的指针,并且可能有不同的长度和容量。 关键在于这个指向a底层数组的指针,这意味着,go中的切片,只是对某个数组的援用。对某个切片或数组重复进行切割,产生的切片只是对同一个底层数组的援用,他们之间会相互影响。 其次咱们要理解append()函数做了什么 func append(slice []Type, elems ...Type) []Typeappend会承受一个切片slice,而后是一系列元素。如果slice增加元素后超过本身容量,就会产生扩容(扩容的具体规定这里并不探讨),append会申请一个新的更大的数组。最初返回一个新切片,新切片指向一个新的底层数组。这个是大部分状况下用append()时的场景。 然而如果slice增加元素后没有超过容量会怎么样?那返回的新切片就会持续应用原来的底层数组。在某些状况下,比方当你通过原切片返回一个去除某个元素的新切片,并且不影响原切片。当你应用了这样的语法,就会出错 slice2 := append(slice1[:2], slice1[3:]...)这里产生了什么?用图表示意的话 本来slice1是这样的通过slice2 := append(slice1[:2], slice1[3:]...)时append()收到slice1[:2],这是一个和slice1共用底层数组的切片。指针指向slice1的第一个元素,长度为2,容量为5。(容量的意思就是,一个切片的第1个元素,到底层数组的最初1个元素,一共有多少个元素。)append()收到的第二个参数是slice1[3:]...,这意味着,一一增加slice1从下标3开始的每一个元素,这里是4和5。最初,slice2增加了2个元素,从长度2增长到了长度4。也就是没有超过容量,因而没有产生扩容。所以slice2依然应用slice1的底层数组,造成了对slice1的影响。 那该怎么办呢? 广泛的解决方案是,结构新切片,而后复制原切片的局部数据到新切片。具体如何结构,能够先创立空切片,而后用append()增加元素,空切片容量为0,这会导致append创立新的底层数组,不会影响原切片。咱们能够输入这两个切片的首元素地址看看。 slice1 := []int{1, 2, 3, 4, 5}fmt.Printf("slice1,%v%T%p\n", slice1, slice1, slice1)slice2 := append([]int(nil),slice1[:2]...)slice2 = append(slice2,slice1[3:]...)fmt.Printf("slice2,%v%T%p\n", slice2, slice2, slice2)fmt.Printf("slice1,%v%T%p\n", slice1, slice1, slice1)// 输入// slice1,[1 2 3 4 5][]int0xc000018330// slice2,[1 2 4 5][]int0xc00001a2a0// slice1,[1 2 3 4 5][]int0xc000018330

June 17, 2021 · 1 min · jiezi

关于golang:用-Go-实现一个-GitHub-Trending-API

背景上一篇文章Go 每日一库之 bubbletea咱们介绍了炫酷的 TUI 程序框架 — bubbletea。最初实现了一个拉取 GitHub Trending 仓库,并显示在控制台的程序。因为 GitHub 没有提供官网的 Trending API,咱们用goquery本人实现了一个。上篇文章因为篇幅关系,没有介绍如何实现。本文我整顿了一下代码,并以独自的代码库模式凋谢进去。 先察看首先,咱们来察看一下 GitHub Trending 的构造: 左上角能够切换仓库(Repositories)和开发者(Developers)。左边能够抉择语言(Spoken Language,本地语言,汉语、英文等)、语言(Language,编程语言,Golang、C++等)和工夫范畴(Date Range,反对 3 个维度,Today、This week、This month)。 而后上面是每个仓库的信息: ① 仓库作者和名字 ② 仓库形容 ③ 次要应用的编程语言(创立仓库时设置的),也可能没有 ④ 星数 ⑤ fork 数 ⑥ 贡献者列表 ⑦ 选定的工夫范畴内(Today、This week、This month)新增多少星数 开发者页面也是相似的,只不过信息少了很多: ① 作者信息 ② 最火的仓库信息 留神到切换的开发者页面后,URL 变成为github.com/trending/developers。另外当咱们抉择本地语言为中文、开发语言为 Go 和工夫范畴为 Today 后,URL 变为https://github.com/trending/go?since=daily&spoken_language_code=zh,通过在 query-string 中减少相应的键值对示意这种抉择。 筹备在 GitHub 上创立仓库ghtrending,clone 到本地,执行go mod init初始化: $ go mod init github.com/darjun/ghtrending而后执行go get下载goquery库: ...

June 17, 2021 · 3 min · jiezi

关于golang:一切皆有可能Golang中的ThreadLocal库

开源仓库: https://github.com/go-eden/ro... 本文介绍的是新写的routine库,它封装并提供了一些易用、高性能的goroutine上下文拜访接口,能够帮忙你更优雅地拜访协程上下文信息,但你也可能就此关上了潘多拉魔盒。 介绍Golang语言从设计之初,就始终在不遗余力地向开发者屏蔽协程上下文的概念,包含协程goid的获取、过程外部协程状态、协程上下文存储等。 如果你应用过其余语言如C++/Java等,那么你肯定很相熟ThreadLocal,而在开始应用Golang之后,你肯定会为短少相似ThreadLocal的便捷性能而深感困惑与苦恼。 当然你能够抉择应用Context,让它携带着全副上下文信息,在所有函数的第一个输出参数中呈现,而后在你的零碎中到处穿梭。 而routine的外围指标就是开拓另一条路:将goroutine local storage引入Golang世界,同时也将协程信息裸露进去,以满足某些人人可能有的需要。 应用演示此章节简要介绍如何装置与应用routine库。 装置go get github.com/go-eden/routine应用goid以下代码简略演示了routine.Goid()与routine.AllGoids()的应用: package mainimport ( "fmt" "github.com/go-eden/routine" "time")func main() { go func() { time.Sleep(time.Second) }() goid := routine.Goid() goids := routine.AllGoids() fmt.Printf("curr goid: %d\n", goid) fmt.Printf("all goids: %v\n", goids)}此例中main函数启动了一个新的协程,因而Goid()返回了主协程1,AllGoids()返回了主协程及协程18: curr goid: 1all goids: [1 18]应用LocalStorage以下代码简略演示了LocalStorage的创立、设置、获取、跨协程流传等: package mainimport ( "fmt" "github.com/go-eden/routine" "time")var nameVar = routine.NewLocalStorage()func main() { nameVar.Set("hello world") fmt.Println("name: ", nameVar.Get()) // other goroutine cannot read nameVar go func() { fmt.Println("name1: ", nameVar.Get()) }() // but, the new goroutine could inherit/copy all local data from the current goroutine like this: routine.Go(func() { fmt.Println("name2: ", nameVar.Get()) }) // or, you could copy all local data manually ic := routine.BackupContext() go func() { routine.InheritContext(ic) fmt.Println("name3: ", nameVar.Get()) }() time.Sleep(time.Second)}执行后果为: ...

June 17, 2021 · 1 min · jiezi

关于golang:用-Go-struct-不能犯的一个低级错误

微信搜寻【脑子进煎鱼了】关注这一只爆肝煎鱼。本文 GitHub github.com/eddycjy/blog 已收录,有我的系列文章、材料和开源 Go 图书。大家好,我是煎鱼。 前段时间我分享了 《手撕 Go 面试官:Go 构造体是否能够比拟,为什么?》的文章,把根本 Go struct 的比拟根据钻研了一番。这不,最近有一位读者,遇到了一个对于 struct 的新问题,不得解。 大家一起来看看,倡议大家在看到代码例子后先思考一下答案,再往下看。 独立思考很重要。 纳闷的例子其给出的例子一如下: type People struct {}func main() { a := &People{} b := &People{} fmt.Println(a == b)}你认为输入后果是什么呢? 输入后果是:false。 再稍加革新一下,例子二如下: type People struct {}func main() { a := &People{} b := &People{} fmt.Printf("%p\n", a) fmt.Printf("%p\n", b) fmt.Println(a == b)}输入后果是:true。 他的问题是 "为什么第一个返回 false 第二个返回 true,是什么起因导致的? 煎鱼进一步的精简这个例子,失去最小示例: func main() { a := new(struct{}) b := new(struct{}) println(a, b, a == b) c := new(struct{}) d := new(struct{}) fmt.Println(c, d) println(c, d, c == d)}输入后果: ...

June 17, 2021 · 2 min · jiezi

关于golang:使用-Docker-快速部署-Golang-应用

文章目录:指标镜像 GolangAlpine端口映射 占用本地端口 8081备注 Dockerfile文章应用到的软件: Mac 12.0 Beta(macOS Monterey),处理器为:M1Portainer.io:2.5.1Docker:20.10.6 指标 疾速部署 Golang 利用构建Dockerfile Golang 网址Alpine 网址Goproxy 网址FROM golang as build# 配置模块代理ENV GOPROXY=https://goproxy.cn,directADD . /www# 进入工作目录WORKDIR /www# 打包鲲鹏架构# RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o go_server# 打包AMD64架构RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o go_server# 设置时区,未验证RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezoneFROM alpine:3.7# 配置国内源RUN echo "http://mirrors.aliyun.com/alpine/v3.7/main/" > /etc/apk/repositoriesRUN apk updateRUN apk add ca-certificates# dnsRUN echo "hosts: files dns" > /etc/nsswitch.conf# 创立文件夹(依据集体抉择)RUN mkdir -p /www/conf /www/runtime/WORKDIR /www# 复制打包的Go文件到零碎用户可执行程序目录下COPY --from=build /www/go_server /usr/bin/go_server# 复制程序配置文件(依据集体抉择)ADD ./conf /www/conf# 设置Go程序权限RUN chmod +x /usr/bin/go_server# 容器启动时运行的命令ENTRYPOINT ["go_server"]打包Docker镜像 ...

June 16, 2021 · 1 min · jiezi

关于golang:Go语言接口interface简单应用

go语言中的多态个性次要是通过接口来实现的,例如在事实世界中能够把手机,相机,U盘等插到USB标准接口上,而不必放心该USB接口是为哪种设施提供的,起因是商家在设计USB接口时都恪守了一个USB规范,如尺寸,排线等,如果电脑将USB作为输出接口,那么只有是可能插到该接口的设施实践上都能够连贯电脑进行工作。上面通过Go语言实现这种场景: package mainimport ( "fmt")//定义一个USB接口,该接口提供两个办法type USB interface { start() stop()}//定义一个手机的构造体type phone struct {}//让手机实现USB接口中的两个办法,且必须都要实现func (p phone) start (){ fmt.Println("phone start")}func (p phone) stop(){ fmt.Println("phone stop")}//定义一个相机构造体type camera struct {}//让相机也实现USB接口中的两个办法,且必须都要实现func (c camera) start () { fmt.Println("camera start")}func (c camera) stop (){ fmt.Println("camera stop")}//定义一个电脑构造体type computer struct {}//定义一个电脑的成员办法,该办法将USB作为输出接口,并且该办法启动USB接口中的两个办法,不是必须都要将两个启动func (c computer) working (u USB){ u.start() u.stop()}func main(){ phone := phone{} camera := camera{} computer := computer{} //只有是实现了USB接口(所谓实现USB接口就是实现了USB接口中定义的所有办法)的设施,都能够通过电脑启动工作 computer.working(phone) computer.working(camera)}执行后果: phone startphone stopcamera startcamera stop

June 16, 2021 · 1 min · jiezi

关于golang:Go-Runtime的调度器

以goroutine模式进行Go并发编程是一种十分不便的办法,但有没有想过他是如何无效地运行这些goroutine?上面从设计的角度,深刻理解和钻研Go运行时调度程序,以及如何在性能调试过程中应用它来解释Go程序的调度程序跟踪信息。 要理解为什么须要有一个运行时的调度以及它是如何工作的,先要回到操作系统的历史上,在这里将找到答案,因为如果不理解问题的本源。 操作系统的历史 单用户(无操作系统)批处理 单编程 运行实现多程序多程序的目标是使CPU和I/O重叠。如何做到的呢? 多批次 IBM OS / MFT(具备固定数量的工作的多重编程)多批次 IBM OS / MVT(具备可变数量的工作的多重编程)—在这里,每个作业仅取得其所需的内存量。即,随着作业的进出,内存的分区发生变化。分时 这是在作业之间疾速切换的多道程序设计。决定何时切换以及切换到哪些作业称为调度。古代大多数操作系统应用分时调度程序。 这些调度程序调度的是什么呢? 1 不同的程序正在执行(过程) 2 作为过程子集存在的CPU利用率(线程)的根本单位 这些都是有代价的。 调度老本 因而,应用一个蕴含多个线程的过程效率更高,因为过程创立既耗时又消耗资源。随后呈现了多线程问题:C10k问题是次要的问题。 例如,如果将调度程序周期定义为10毫秒(毫秒),并且有2个线程,则每个线程将别离取得5毫秒。如果您有5个线程,则每个线程将取得2ms。然而,如果有1000个线程怎么办?给每个线程10s(微秒)的工夫片?你将破费大量工夫进行上下文切换,然而无奈实现真正的工作。 你须要限度工夫片的长度。在最初一种状况下,如果最小工夫片是2ms,并且有1000个线程,则调度程序周期须要减少到2s(秒)。如果有10,000个线程,则调度程序周期为20秒。在这个简略的示例中,如果每个线程都应用其全时切片,则所有线程一次运行须要20秒。因而,咱们须要一些能够使并发老本升高而又不会造成过多开销的货色。 用户级线程 • 线程齐全由运行时零碎(用户级库)治理。 • 现实状况下,疾速高效:切换线程不比函数调用贵多少。 • 内核对用户级线程无所不知,并像看待单线程过程一样对其进行治理。 在Go中,咱们叫它“ Goroutine”(在逻辑上) Goroutine 协程是轻量级线程,由Go运行时治理(逻辑上一个执行的线程)。要go在函数调用之前启动运行go关键字。 func main() { var wg sync.WaitGroup wg.Add(11) for i := 0; i <= 10; i++ { go func(i int) { defer wg.Done() fmt.Printf("loop i is - %d\n", i) }(i) } wg.Wait() fmt.Println("Hello, Welcome to Go")}运行后果loop i is - 10loop i is - 0loop i is - 1loop i is - 2loop i is - 3loop i is - 4loop i is - 5loop i is - 6loop i is - 7loop i is - 8loop i is - 9Hello, Welcome to Go看一下输入,就会有两个问题。 ...

June 16, 2021 · 2 min · jiezi

关于golang:详解-Go-程序的启动流程你知道-g0m0-是什么吗

微信搜寻【脑子进煎鱼了】关注这一只爆肝煎鱼。本文 GitHub github.com/eddycjy/blog 已收录,有我的系列文章、材料和开源 Go 图书。大家好,我是煎鱼。 自古应用程序均从 Hello World 开始,你我所写的 Go 语言亦然: import "fmt"func main() { fmt.Println("hello world.")}这段程序的输入后果为 hello world.,就是这么的简略又间接。但这时候又不禁思考了起来,这个 hello world. 是怎么输入来,经验了什么过程。 真是十分的好奇,明天咱们就一起来探一探 Go 程序的启动流程。其中波及到 Go Runtime 的调度器启动,g0,m0 又是什么? 车门焊死,正式开始吸鱼之路。 Go 疏导阶段查找入口首先编译上文提到的示例程序: $ GOFLAGS="-ldflags=-compressdwarf=false" go build 在命令中指定了 GOFLAGS 参数,这是因为在 Go1.11 起,为了缩小二进制文件大小,调试信息会被压缩。导致在 MacOS 上应用 gdb 时无奈了解压缩的 DWARF 的含意是什么(而我恰好就是用的 MacOS)。 因而须要在本次调试中将其敞开,再应用 gdb 进行调试,以此达到察看的目标: $ gdb awesomeProject (gdb) info filesSymbols from "/Users/eddycjy/go-application/awesomeProject/awesomeProject".Local exec file: `/Users/eddycjy/go-application/awesomeProject/awesomeProject', file type mach-o-x86-64. Entry point: 0x1063c80 0x0000000001001000 - 0x00000000010a6aca is .text ...(gdb) b *0x1063c80Breakpoint 1 at 0x1063c80: file /usr/local/Cellar/go/1.15/libexec/src/runtime/rt0_darwin_amd64.s, line 8.通过 Entry point 的调试,可看到真正的程序入口在 runtime 包中,不同的计算机架构指向不同。例如: ...

June 16, 2021 · 3 min · jiezi

关于golang:快来这里有23种设计模式的Go语言实现

摘要:设计模式(Design Pattern)是一套被重复应用、少数人通晓的、通过分类编目标、代码设计教训的总结,应用设计模式是为了可重用代码、让代码更容易被别人了解并且保障代码可靠性。本文分享自华为云社区《快来,这里有23种设计模式的Go语言实现》,原文作者:元闰子 。 前言从1995年GoF提出23种设计模式到当初,25年过来了,设计模式仍旧是软件畛域的热门话题。在当下,如果你不会一点设计模式,都不好意思说本人是一个合格的程序员。设计模式通常被定义为: 设计模式(Design Pattern)是一套被重复应用、少数人通晓的、通过分类编目标、代码设计教训的总结,应用设计模式是为了可重用代码、让代码更容易被别人了解并且保障代码可靠性。 从定义上看,设计模式其实是一种教训的总结,是针对特定问题的简洁而优雅的解决方案。既然是经验总结,那么学习设计模式最间接的益处就在于能够站在伟人的肩膀上解决软件开发过程中的一些特定问题。然而,学习设计模式的最高境界是习得其中解决问题所用到的思维,当你把它们的实质思维吃透了,也就能做到即便曾经忘掉某个设计模式的名称和构造,也能在解决特定问题时信手拈来。 好的货色有人吹捧,当然也会招黑。设计模式被鞭挞次要因为以下两点: 1、设计模式会减少代码量,把程序逻辑变得复杂。这一点是不可避免的,然而咱们并不能仅仅只思考开发阶段的老本。最简略的程序当然是一个函数从头写到尾,然而这样前期的保护老本会变得十分大;而设计模式尽管减少了一点开发成本,然而能让人们写出可复用、可维护性高的程序。援用《软件设计的哲学》里的概念,前者就是战术编程,后者就是策略编程,咱们应该对战术编程Say No! 2、滥用设计模式。这是初学者最容易犯的谬误,当学到一个模式时,巴不得在所有的代码都用上,从而在不该应用模式的中央刻意地应用了模式,导致了程序变得异样简单。其实每个设计模式都有几个要害因素:实用场景、解决办法、优缺点。模式并不是万能药,它只有在特定的问题上能力显现出成果。所以,在应用一个模式前,先问问本人,以后的这个场景实用这个模式吗? 《设计模式》一书的副标题是“可复用面向对象软件的根底”,但并不意味着只有面向对象语言能力应用设计模式。模式只是一种解决特定问题的思维,跟语言无关。就像Go语言一样,它并非是像C++和Java一样的面向对象语言,然而设计模式同样实用。本系列文章将应用Go语言来实现GoF提出的23种设计模式,依照创立型模式(Creational Pattern)、结构型模式(Structural Pattern)和行为型模式(Behavioral Pattern)三种类别进行组织,文本次要介绍其中的创立型模式。 单例模式(Singleton Pattern) 简述单例模式算是23中设计模式里最简略的一个了,它次要用于保障一个类仅有一个实例,并提供一个拜访它的全局拜访点。 在程序设计中,有一些对象通常咱们只须要一个共享的实例,比方线程池、全局缓存、对象池等,这种场景下就适宜应用单例模式。 然而,并非所有全局惟一的场景都适宜应用单例模式。比方,思考须要统计一个API调用的状况,有两个指标,胜利调用次数和失败调用次数。这两个指标都是全局惟一的,所以有人可能会将其建模成两个单例SuccessApiMetric和FailApiMetric。依照这个思路,随着指标数量的增多,你会发现代码里类的定义会越来越多,也越来越臃肿。这也是单例模式最常见的误用场景,更好的办法是将两个指标设计成一个对象ApiMetric下的两个实例ApiMetic success和ApiMetic fail。 如何判断一个对象是否应该被建模成单例? 通常,被建模成单例的对象都有“中心点”的含意,比方线程池就是治理所有线程的核心。所以,在判断一个对象是否适宜单例模式时,先思考下,这个对象是一个中心点吗? Go实现在对某个对象实现单例模式时,有两个点必须要留神:(1)限度调用者间接实例化该对象;(2)为该对象的单例提供一个全局惟一的拜访办法。 对于C++/Java而言,只需把类的构造函数设计成公有的,并提供一个static办法去拜访该类点惟一实例即可。但对于Go语言来说,即没有构造函数的概念,也没有static办法,所以须要另寻前途。 咱们能够利用Go语言package的拜访规定来实现,将单例构造体设计成首字母小写,就能限定其拜访范畴只在以后package下,模仿了C++/Java中的公有构造函数;再在以后package下实现一个首字母大写的拜访函数,就相当于static办法的作用了。 在理论开发中,咱们常常会遇到须要频繁创立和销毁的对象。频繁的创立和销毁一则耗费CPU,二则内存的利用率也不高,通常咱们都会应用对象池技术来进行优化。思考咱们须要实现一个音讯对象池,因为是全局的中心点,治理所有的Message实例,所以将其实现成单例,实现代码如下: package msgpool ... // 音讯池 type messagePool struct { pool *sync.Pool } // 音讯池单例 var msgPool = &messagePool{ // 如果音讯池里没有音讯,则新建一个Count值为0的Message实例 pool: &sync.Pool{New: func() interface{} { return &Message{Count: 0} }}, } // 拜访音讯池单例的惟一办法 func Instance() *messagePool { return msgPool } // 往音讯池里增加音讯 func (m *messagePool) AddMsg(msg *Message) { m.pool.Put(msg) } // 从音讯池里获取音讯 func (m *messagePool) GetMsg() *Message { return m.pool.Get().(*Message) } ...测试代码如下: ...

June 16, 2021 · 6 min · jiezi

关于golang:Go-mod-replace-使用

日常开发离不开第三方库,大部分的时候,这些库都是满足咱们的须要,但有的时候,咱们须要 fork 一份,做一些批改。go mod 作为以后 go 语言的官网包管理器,天然也思考到了这种状况。在 go.mod 文件中,通过 replace 指令,将旧的库地址,替换为新的库地址来实现这一操作。 上面通过一个示例来解说 go replace 的应用,以及常见问题的解决。 咱们首先新建一个我的项目,并在其中援用 ozgio/strutil: String utilities for Go (github.com) 这个字符串解决库,而后轻易写段代码,确保其能够失常工作: package mainimport ( "fmt" "github.com/ozgio/strutil")func main() { fmt.Println(strutil.Align("lorem ipsum", strutil.Right, 20))}go mod 的初始化go mod init project_name go mod init 命令执行后,会主动生成 go.mod 文件,该文件中,列出了我的项目所依赖的第三方包,以及所应用的版本。 而后执行 go mod tidy,该命令做两件事: 解析我的项目文件,并找到所应用的包生成 go.sum 文件,其中保留了所应用包的版本而后执行 go run main.go,来执行我的项目。 当初,我的项目应该曾经能够失常执行,并返回执行后果了。 假如咱们此时想调用一个过滤字符串中,HTML 标签的办法,但翻了一下并没有,于是 fork 了一份这个库,咱们本人增加了进去: https://github.com/shiweifu/strutil/blob/master/escape.go上面咱们来看如何调用这个新的办法。 第一种形式: fork strutil 库,关上 go.mod 文件,将第一行中的 module name 批改为一个新的 name减少所须要的办法减少新的 git tag在你以后我的项目中,援用你批改后的这个 repo,替换地址以及版本号这种形式相当于援用了一个新的库,与之前那个库曾经没有什么关系了。大多数时候,因为对代码批改过多,咱们并不会想要这么用。go mod 当然也思考到了这一点,go mod 提供了 go mod replace 办法来应答这种场景。 ...

June 15, 2021 · 1 min · jiezi

关于golang:Go-语言的切片

切片的申明func TestSliceInit(t *testing.T) { var s0 []int t.Log(len(s0), cap(s0)) s0 = append(s0, 1) t.Log(len(s0), cap(s0)) s1 := []int{1, 2, 3, 4} t.Log(len(s1), cap(s1)) s2 := make([]int, 3, 5) // 长度3,容量5 t.Log(len(s2), cap(s2)) // 只能拜访前三个元素,不能拜访s2[3] t.Log(s2[0], s2[1], s2[2]) s2 = append(s2, 1) // 长度4,容量5 t.Log(len(s2), cap(s2)) // 能够拜访前四个元素,不能拜访s2[4] t.Log(s2[0], s2[1], s2[2], s2[3])}切片的容量增长func TestSliceGrowing(t *testing.T) { s := []int{} for i := 0; i < 10; i++ { s = append(s, i) // 容量会翻倍增长 t.Log(len(s), cap(s)) }}切片共享内容func TestSliceShareMemory(t *testing.T) { months := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"} Q2 := months[3:6] //[...] 3 9 t.Log(Q2, len(Q2), cap(Q2)) summer := year[5:8] t.Log(summer, len(summer), cap(summer)) summer[0] = "Unknow" t.Log(Q2) t.Log(months)}切片的比拟func TestSliceComparing(t *testing.T) { a := []int{1, 2, 3, 4} b := []int{1, 2, 3, 4} // 切片不能进行比拟 if a == b { t.Log("equal") }}

June 15, 2021 · 1 min · jiezi

关于golang:Go-语言的数组

数组的申明var arr [3]intt.Log(arr[1], arr[2])arr[0] = 1b := [3]int{1, 2, 3}c := [2][2]int{{1, 2}, {3, 4}}d := [...]int{1, 3, 5, 7}数组的遍历func TestArrayTravel(t *testing.T) { d := [...]int{1, 3, 5, 7} for i := 0; i < len(d); i++ { t.Log(d[i]) } for idx, e := range d { t.Log(idx, e) } for _, e := range d { t.Log(idx, e) }}数组截取func TestArraySection(t *testing.T) { arr := [...]int{1, 2, 3, 4, 5, 6, 7} // 前三个元素 tmp := arr[:3] // 下标为3的元素及其后的所有元素 tmp = arr[3:] // 下标为1和2的元素 tmp = arr[1:3]}

June 15, 2021 · 1 min · jiezi

关于golang:永久激活GoLang无限重置30天试用期

有限重置补丁在网盘中能够找到,如果还没有失去,在Java大神公众号中回复“永恒”即可 有限重置30天试用期,补丁,简略来说:勾选了Auto reset before per restart则无需再管,一劳永逸。 分享一个一劳永逸的暴力破解办法,通过有限重置 30 天试用期的形式,无需再为激活码、装置参数生效担心,亲测无效,办法简略,上面是具体教程哟~前言 Jetbrains 家的产品有一个很良心的中央,会容许你试用30天(这个数字写死在代码里了)以评估是否你真的须要为它而付费。 zhile大佬的破解补丁我的项目曾经进行更新了,然而提供了另外一个办法让咱们能够应用 IDEA : 有限重置30天试用期的办法,再也不必为激活码、装置参数过期发愁了~留神 本教程实用于 IntelliJ IDEA 2020.3.3 以下所有版本,请释怀食用~本教程实用于 JetBrains 全系列产品,包含 Pycharm、IDEA、WebStorm、Phpstorm、Datagrip、RubyMine、CLion、AppCode 等。本教程实用 Windows/Mac/Linux 零碎,文中以 Windows 零碎为例做解说,其余零碎依照教程程序即可。一、开始装置首先,下载有限重置30天试用期补丁: 补丁的网盘链接在文章最初,小伙伴们自行获取即可~**补丁的网盘链接在文章最初,小伙伴们自行获取即可~**补丁的网盘链接在文章最初,小伙伴们自行获取即可~**下载实现后,将 zip 插件包拖入 IDE 界面中。如果无奈拖动装置,你能够在Settings/Preferences... -> Plugins 里手动装置插件(Install Plugin From Disk...): PS: macOS 零碎可能会主动解压,而后把zip包丢进回收站, 须要留神以下~插件装置胜利后,会提醒如下: 二、 如何应用 一般来说,在IDE窗口切出去或切回来时(窗口失去/失去焦点)会触发事件,检测是否长达25天都没有重置,会给出告诉让你抉择。(首次装置因为无奈获取上次重置工夫,会间接给予提醒)也能够手动唤出插件的主界面:如果IDE没有关上我的项目,在Welcome界面点击菜单:Get Help -> Eval Reset如果IDE关上了我的项目,点击菜单:Help -> Eval Reset 唤出的插件主界面中蕴含了一些显示信息,2个按钮,1个勾选项:按钮:Reload 用来刷新界面上的显示信息。按钮:Reset 点击会询问是否重置试用30天并重启IDE。抉择Yes则执行重置操作并重启IDE失效,抉择No则什么也不做。(此为手动重置形式) 勾选项:Auto reset before per restart 如果勾选了,则自勾选后每次重启/退出IDE时会主动重置试用信息,你无需做额定的事件。(此为主动重置形式)三、如何查看残余的试用期 进入 IDEA 界面后,点击 Help -> Register 查看: ...

June 15, 2021 · 1 min · jiezi

关于golang:Go-语言的循环及条件语句

循环Go 语言只反对 for 循环。 func TestWhileLoop(t *testing.T) { n := 0 for n < 5 { n++ fmt.Println(n) }}有限循环 n := 0for { fmt.Println(n)}IF条件语句func TestIf(t *testing.T) { if a := 1 == 1; a { t.Log("1 == 1") }}switch 条件语句Go 语言的 switch 不须要应用 break 来退出一个 case func TestSwitch(t *testing.T) { for i := 0; i < 5; i++ { switch i { case 0, 2: t.Log("Even") case 1, 3: t.Log("Odd") default: t.Log("not 0-3") } }}case 中应用表达式: ...

June 14, 2021 · 1 min · jiezi

关于golang:Go-语言学习路线来啦

时不时的有人问我一些对于 Go 语言学习路线、学习资源方面的问题,这篇文章就来具体说一说。借此心愿给那些正在学习,或是想学习 Go 语言的敌人一些帮忙。 须要阐明的是,依照我举荐的来学习,齐全把握 Go 相干开发常识,并且找到 Go 开发的工作应该是问题不大的,当然具体能达到什么样的水平,得看集体了。 阐明一下,文中提到的所有书籍,都能够在公众号【roseduan写字的中央】后盾回复相应的关键字获取:【算法、操作系统、网络、数据库、Go、微服务】,按需自取。 1. 基础知识无论你学习什么编程语言,这些基础知识是通用的,并且它们很重要,所以我感觉还是拿出来说一说,只不过当初网上这方面的常识曾经很多了,知乎、公众号搜一下,就可能找到很多相干的内容,所以我这里就简略说下。 1.1 数据结构和算法举荐入门书籍《大话数据结构》、《啊哈!算法》、《漫画算法·小灰的算法之旅》、《算法图解》,进阶的话能够看看《算法导论》、《算法》。 如果感觉书籍比拟干燥,网上也有一些付费专栏,能够订阅来看看。留神一点,如果不是搞算法方向的话,把握最根底罕用的内容就能够了,没必要去死磕太多高级的数据结构和算法。 坚固算法常识的话,能够上 Leetcode 刷几个题。当然,这块内容还有一个作用,那就是应酬面试,大家都懂的。我的 Github 下面有一个我的项目,能够帮忙学习学习和坚固算法,地址: https://github.com/roseduan/algo-learn 我的项目应用 Go、Java、Python 实现了罕用的数据结构和算法,以及相干 Leetcode 题目。 1.2 操作系统这块的内容,简略看下就好,理解下根底的概念,在工作的前几年里,你可能不太会用得上太多操作系统下面的常识,但置信我,这是职业生涯进阶必过的坎,迟早会遇上的。 学习资源举荐:书籍《操作系统概念》、《深刻了解计算机系统》、《古代操作系统》。书籍看不下去的,很能了解你(因为我也看不下去),这里举荐一个学堂在线的操作系统课程: https://www.xuetangx.com/course/THU08091000267/1516699 是清华大学的公开课程,概念比拟根底容易了解,想要进阶深刻的话,人家也有配套的操作系统试验能够跟着做。 1.3 计算机网络根底书籍:《图解 HTTP》、《图解 TCP_IP》、《网络是怎么连贯的》。 进阶书籍:《计算机网络·自顶向下办法》、《TCP_IP 详解》。 同样,晓得你看不下去书籍,在中国大学 MOOC 下面有一些名校的公开的网络课程,都是比拟根底的,能够在下面找找,有挺多品质不错的。 1.4 数据库数据库的重要性就不用说了,必备的常识。 根底的 sql 把握起来应该没啥难度,本人多练练就行了,举荐书籍《SQL 必知必会》、《MySQL 必知必会》。 想要进阶,能够多理解下数据库一些根本设计概念,举荐书籍《数据库系统概念》,对于 mysql 进阶,例如 B+ 树存储模型、事务、索引、锁等,举荐书籍《高性能 mysql》、《MySQL 技术底细:InnoDB 存储引擎》。 2. Go 语言根底终于来到正题 Go 语言了,如果你是 Go 语言零根底,或者刚入门不久,都能够顺着我的这个门路学习坚固一下。 举荐入门书籍《Go 语言学习笔记》、《Go 语言趣学指南》、《Head First Go》,跟着书籍多敲敲代码,入门没有太大的问题。还有我私藏的一些在线学习 Go 语言的材料: ...

June 14, 2021 · 1 min · jiezi

关于golang:Go-语言中的运算符

运算符Go 语言没有前置的 ++ 和 -- 运算符。 package operator_testimport ( "fmt") //引入代码依赖func TestCompareArray(t *testing.T) { a := [...]int{1, 2, 3, 4} b := [...]int{1, 3, 2, 4} c := [...]int{1, 2, 3, 4, 5} d := [...]int{1, 2, 3, 4} t.Log(a == b) //false t.Log(a == c) //编译报错,长度不统一 t.Log(a == d) //true}按位清零运算符。 const ( Readable = 1 << iota Writable Executable)func TestBitClear(t *testing.T) { a := 7 //0111 a = a &^ Readable //革除读权限 a = a &^ Executable //革除执行权限 t.Log(a&Readable == Readable, a&Writable == Writable, a&Executable == Executable)}

June 14, 2021 · 1 min · jiezi

关于golang:Go-语言的指针类型和-string-类型

指针类型Go 语言不反对指针运算。 func TestPoint(t *testing.T) { a := 1 aPtr := &a // aPtr = aPtr + 1 // 不反对指针运算 // 输入值:1 0xc00007c0e0 t.Log(a, aPtr) // 输入类型:int, *int t.Logf("%T %T", a, aPtr)}string 是值类型,初始值为空字符串,不是nil。 func TestString(t *testing.T) { var s string // 输入空字符串 ** t.Log("*" + s + "*") t.Log(len(s)) if s == "" { t.Log("空字符串判断") }}

June 14, 2021 · 1 min · jiezi

关于golang:服务注册与发现之ETCD

[TOC] 服务注册与发现之ETCD咱们一起来回顾一下上次的分享: 通道是什么,通道的品种无缓冲,有缓冲,单向通道具体对应什么对于通道的具体实际分享了对于通道的异常情况整顿简略分享了sync包的应用要是对上述 GO 的通道 和 sync 包有点趣味的话,欢送查看文章 GO通道和 sync 包的分享 明天咱们来看看服务注册与发现 什么是服务注册和发现?服务注册和发现的基本原理如下: 服务注册 指服务实例启动的时候将本身的信息注册到服务注册与发现核心,并在运行的时候通过心跳的形式向服务注册发现核心汇报本身服务状态 服务发现 指服务实例向服务注册与发现核心获取的其余服务实例信息,用于进行后续的近程调用。 服务注册和发现的作用?依据网络资源和 GO 相干的书籍介绍,简略梳理了如下 3 个点: 治理实例信息治理以后注册到服务注册与发现核心的微服务实例元数据信息,这些信息包含服务实例的服务名,IP地址,端口号,服务状态和服务形容等等信息 健康检查服务注册与发现核心会与曾经注册 ok 的微服务实例维持心跳,定期检查注册表中的服务是否失常在线,并且会在过程中剔除掉有效的服务实例信息 提供服务发现的作用如一个人服务须要调用服务注册与发现核心中的微服务实例,能够通过服务注册与发现核心获取到其具体的服务实例信息 对于服务注册和发现,不得不说的一个定理是 CAP 定理 CAP原理是个啥?是形容分布式系统下节点数据同步的根本定理 有如下 3 个个性: 一致性指数据的一致性。 零碎的数据信息,这里包含备份的数据信息,在同一时刻下都是统一的。 在咱们当初的分布式系统中,同一份数据可能存在多个实例当中,在这个个性的要求下,每一个实例若批改了其中一份数据,都必须同步到他所有的备份当中 可用性指服务的可用性 这里是要求服务实例,在接管到客户端的申请后,都可能给出相应的响应 这里是考量零碎的可用性 ,例如在零碎中某个节点宕机了,客户端申请服务的时候,服务端依然能够做出对应的响应 分区容忍性这个个性是这样了解的 当初咱们分布式的零碎环境中,每一个节点之间都是通过网络通信连接起来的, 可是,咱们要晓得,基于网络通信,还是会存在不牢靠的中央,处在不同的网络环境,该环境下的服务节点是会有可能呈现连贯不上,通信失败的。 对于以上这个问题,若零碎能够容忍,那么这个零碎就满足了 分区容忍性 服务注册和发现都有哪些组件?罕用的服务注册和发现框架有如下一些,我这里列举几个: ETCD基于HTTP 协定的分布式 key/value 存储组件 Consul基于 Raft 算法的开箱即用的服务发现组件 Zookeeper重量级一致性服务组件 Eureka基于集群配置的组件 咱们明天来看看这些服务注册和发现组件中的 ETCD ,也是因为他比较简单,易于应用,并且是 GO 开发的 ETCD 是个啥?ETCD 一个开源的、高可用的分布式key-value存储系统,能够用于配置共享和服务的注册和发现 ...

June 14, 2021 · 1 min · jiezi

关于golang:Go-语言的数据类型

根本数据类型Go 语音不容许隐式类型转换,也不容许别名类型和原类型进行隐式类型转换。 boolstringint int8 int16 int32 int64uint uint8 uint16 uint32 uint64byte // alias for uint8rune // alias for int32float32 float64complex64 complex128package type_testimport ( "fmt") //引入代码依赖type MyInt int64 // 定义别名func TestConstant0(t *testing.T) { var a int = 1 var b int32 = 1 var c int64 c = a // 不反对 c = b // 不反对 c = int64(a) // 反对 c = int64(b) // 反对 var d MyInt d = MyInt(b) t.Log(a, b, c, b)}类型的预约义值math.MaxInt64math.MaxFloat64math.MaxUint32

June 13, 2021 · 1 min · jiezi

关于golang:Go-语言变量及常量的定义与使用

变量的定义与应用package fib_testimport ( "fmt") //引入代码依赖func TestFibList(t *testing.T) { var a int = 1 var b int = 1 fmt.Print(a) for i := 0; i < 5; i++ { fmt.Print(" ", b) tmp := a a = b b = tmp + a } fmt.Println() t.Log("finish.")}// 替换两个变量的值func TestFibList(t *testing.T) { a := 1 b := 1 a, b = b, a t.Log(a, b)}常量的定义与应用package constant_testimport ( "fmt") //引入代码依赖const ( Mon = iota + 1 Tue Wed)// 位运算const ( Readable = 1 << iota Writable Executable)func TestConstant0(t *testing.T) { t.Log(Mon, Tue, Wed)}func TestConstant1(t *testing.T) { a := 1 //0001,可读 t.Log(a&Readable == Readable, a&Writable == Writable, a&Executable == Executable) a := 7 //0111,可读可写可执行 t.Log(a&Readable == Readable, a&Writable == Writable, a&Executable == Executable)}

June 13, 2021 · 1 min · jiezi

关于golang:Go-语言开发环境搭建及编写第一个Go程序

开发环境构建GOPATH在1.8版本之前必须设置 GOPATH 环境变量1.8版本(含)之后能够不设置,默认值为: Unix:$HOME/goWindows:%USERPROFILE%/goMac:批改 ~./bash_profile 来设置查看 Go 版本go version创立目录go_learning src ch1 main hello_world.go# hello_world.gopackage main //包名,申明代码所在的模块import ( "fmt" "os") //引入代码依赖func main() { if len(os.Args) > 1 { fmt.Println("Hello", os.Args[1]) } os.Exit(0)}运行程序间接运行 cd go_learning/src/ch1/main/go run hello_world.go World先编译后运行 go build hello_world.gols./hello_world World程序阐明程序入口必须是 main 包,即 package main程序入口必须是 main 办法,即 func main() {}文件名不须要是 main.gomain 办法不反对任何返回值,能够通过 os.Exit 来返回状态main 办法不反对传入参数,能够通过 os.Args 来获取命令行参数

June 13, 2021 · 1 min · jiezi

关于golang:GO的锁和原子操作分享

[TOC] GO的锁和原子操作分享 上次咱们说到协程,咱们再来回顾一下: 协程相似线程,是一种更为轻量级的调度单位线程是零碎级实现的,常见的调度办法是工夫片轮转法协程是应用软件级实现,原理与线程相似协程的调度基于 GPM 模型实现要是对协程的应用感兴趣的话,能够看看这篇文章简略理解一下瞅一眼就会应用GO的并发编程分享 明天咱们来聊聊GO外面的锁 锁是什么?锁 是用于解决隔离性的一种机制 某个协程(线程)在拜访某个资源时先锁住,避免其它协程的拜访,等拜访结束解锁后其余协程再来加锁进行拜访 在咱们生存中,咱们应该不会生疏,锁是这样的 本意是指置于可启闭的器物上,以钥匙或暗码开启,引申义是用锁锁住、关闭生存中用到的锁 上锁根本是为了避免外人进来、避免本人财物被盗 编程语言中的锁 锁的品种更是多种多样,每种锁的加锁开销以及利用场景也不尽相同 锁是用来做什么的?用来管制各个协程的同步,避免资源竞争导致错乱问题 在高并发的场景下,如果选对了适合的锁,则会大大提高零碎的性能,否则性能会升高。 那么晓得各种锁的开销,以及利用场景很有必要 GO中的锁有哪些? 互斥锁读写锁咱们在编码中会存在多个 goroutine 协程同时操作一个资源(临界区),这种状况会产生竞态问题(数据竞态) 举一个生存中的例子 生存中最显著的例子就是,大家抢着上厕所,资源无限,只能一个一个的用 举一个编码中的例子 package mainimport ( "fmt" "sync")// 全局变量var num int64var wg sync.WaitGroupfunc add() { for i := 0; i < 10000000; i++ { num = num + 1 } // 协程退出, 记录 -1 wg.Done()}func main() { // 启动2个协程,记录 2 wg.Add(2) go add() go add() // 期待子协程退出 wg.Wait() fmt.Println(num)}依照上述代码,咱们的输入后果应该是 20000000,每一个协程计算 10000000 次,可是理论后果却是 ...

June 12, 2021 · 3 min · jiezi

关于golang:Go-面试系列五-ioReadAll-怎样读全部

在进行本地 file 文件内容读取,或进行 HTTP 网络接口通信的时候,咱们常常应用 io.ReadAll 来读取近程接口返回的 resp.Body,但接口返回数据量有大有小,io.ReadAll 是怎么实现全副数据的读取的? 带着此疑难,让咱们走近 io.ReadAll 源码一探到底: 1. Demo 读取文件内容package mainimport ( "fmt" "io" "os")func main() { // 读取文件内容 fileInfo, err := os.Open("./abc.go") if err != nil { panic(err) } contentBytes, err := io.ReadAll(fileInfo) if err != nil { panic(err) } fmt.Println(string(contentBytes))}此时读取的 IO stream 大小并不知道,io.ReadAll 应用什么策略读取全副数据呢?滑动窗口?线性/指数递增读取?Talk is cheap. Show me the code. 2. io.ReadAll Codego1.16/src/io/io.go#L626 // ReadAll reads from r until an error or EOF and returns the data it read.// A successful call returns err == nil, not err == EOF. Because ReadAll is// defined to read from src until EOF, it does not treat an EOF from Read// as an error to be reported.func ReadAll(r Reader) ([]byte, error) { b := make([]byte, 0, 512) for { if len(b) == cap(b) { // Add more capacity (let append pick how much). b = append(b, 0)[:len(b)] } //println(cap(b)) n, err := r.Read(b[len(b):cap(b)]) b = b[:len(b)+n] if err != nil { if err == EOF { err = nil } return b, err } }}源码解析:从下面源码能够看到,应用 make 先默认申请 cap = 512 的 []byte,而后进入 for 循环迭代,直到数据全副读取实现。for 循环中,首先通过 len(b) == cap(b) 判断 b 的容量是否满了,如果曾经满了,应用 append(b, 0) 追加一个元素,此时会产生什么呢? ...

June 12, 2021 · 4 min · jiezi

关于golang:瞅一眼就会使用GO的并发编程分享

[TOC] GO的并发编程分享之前咱们分享了网络编程,明天咱们来看看GO的并发编程分享,咱们先来看看他是个啥 啥是并发编程呢?指在一台处理器上同时解决多个工作 此处说的同时,可不是同一个工夫一起手拉手做同一件事件 并发是在同一实体上的多个事件,而这个事件在同一时间距离产生的,同一个时间段,有多个工作执行,可是同一个工夫点,只有一个工作在执行 为啥要有并发编程?随着互联网的遍及,互联网用户人数原来越多,这对系统的性能带来了微小的挑战。 咱们要通过各种形式来高效利用硬件的性能(压迫),从而进步零碎的性能进而晋升用户体验,晋升团队或者企业的竞争力。 并发是为了解决什么问题?目标是啥? 是充沛的利用好处理器的每一个核,以达到最高的解决性能,尽可能的使用好每一块砖 可是因为当初咱们应用的CPU,内存,IO三者之间速度不尽相同 咱们为了进步零碎性能,计算机系统会将这三者速度进行均衡,以达到最优的成果,都有如下措施: 操作系统减少了过程、线程,以分时复用 CPU,进而平衡 CPU 与 I/O 设施的速度差别;CPU 减少了缓存,以平衡与内存的速度差别;编译程序优化指令执行秩序,使得缓存可能失去更加正当地利用。说到过程和线程,他们都是干啥的呢,咱们顺带说一下? 过程是程序在操作系统中的一次执行过程是 零碎进行资源分配和调度的一个独立单位。 线程是过程的一个执行实体是 CPU 调度和分派的根本单位,它是比过程更小的能独立运行的根本单位。 一个过程能够创立和撤销多个线程, 并且同一个过程中的多个线程之间能够并发执行。讲到并发编程不得不说并发和并行有啥区别?是不是总是有小伙伴弄不分明他们到底是啥区别,如同一样,又如同不一样 并发和并行的区别一言蔽之,区别如下: 并发 多线程程序在一个核的 CPU 上运行 并行 多线程程序在多个核的 CPU 上运行 并发就像多个小伙伴跑接力,同一个工夫点只会有一个小伙伴在跑,相互有影响 并行就像是多个小伙伴同一个终点一起跑,互不烦扰 咱们须要记住一点,再强调一波: 并发不是并行 并发次要由切换工夫片来实现"同时"运行 并行则是间接利用多核实现多线程的运行, 在 GO 能够设置应用核数,以施展多核计算机的能力,不过设置核数都是依赖于硬件的 那么,讲到GO的并发编程,就必须上咱们的配角,那就是协程 协程 goroutine 是啥?协程是一种程序组件 是由子例程(过程、函数、例程、办法、子程序)的概念泛化而来的 子例程只有一个入口点且只返回一次,而协程容许多个入口点,能够在指定地位挂起和复原执行。 协程和线程别离有啥特点嘞 协程独立的栈空间,共享堆空间,调度由用户本人管制 实质上有点相似于用户级线程,这些用户级线程的调度也是本人实现的。 线程一个线程上能够跑多个协程,协程是轻量级的线程。 GO 高并发的起因是啥?goroutine 奉行通过通信来共享内存每个一个GO的实例有4~5KB的栈内存占用,并且因为 GO 实现机制而大幅缩小的创立和销毁开销Golang 在语言层面上就反对协程 goroutine GOLANG并发编程波及哪些知识点呢?根本协程的原理,实现形式,尽管说,GO中应用协程很不便,能够咱们必须要知其然而值其所以然Goroutine 池runtime 包的应用Channel 通道定时器并发且平安的锁原子操作select 多路复用等等...Goroutine的那些事咱们写C/C++的时候,咱们必然也是要实现并发编程 ...

June 11, 2021 · 2 min · jiezi

关于golang:跟面试官聊-Goroutine-泄露的-6-种方法真刺激

微信搜寻【脑子进煎鱼了】关注这一只爆肝煎鱼。本文 GitHub github.com/eddycjy/blog 已收录,有我的系列文章、材料和开源 Go 图书。大家好,我是煎鱼。 前几天分享 Go 群友发问的文章时,有读者在朋友圈下提到,心愿我可能针对 Goroutine 泄露这块进行解说,他在面试的时候常常被问到。 明天的男主角,就是 Go 语言的著名品牌标识 Goroutine,一个随随便便就能开几十万个慢车进车道的大杀器。 for { go func() {}() }本文会聚焦于 Goroutine 泄露的 N 种办法,进行详解和阐明。 为什么要问面试官为啥会问 Goroutine(协程)泄露这种奇异的问题呢? 能够猜想是: Goroutine 切实是应用门槛切实是太低了,顺手就一个就能起,呈现了不少滥用的状况。例如:并发 map。Goroutine 自身在 Go 语言的规范库、复合类型、底层源码中利用宽泛。例如:HTTP Server 对每一个申请的解决就是一个协程去运行。很多 Go 工程在线上出事变时,根本 Goroutine 的关联,大家都会作为救火队长,风风火火的跑去看指标、看日志,通过 PProf 采集 Goroutine 运行状况等。 天然他也就是最受注目的那颗 “星” 了,所以在日常面试中,被问几率也就极高了。 Goroutine 泄露理解分明大家爱问的起因后,咱们开始对 Goroutine 泄露的 N 种办法进行钻研,心愿通过前人留下的 “坑”,理解其原理和避开这些问题。 泄露的起因大多集中在: Goroutine 内正在进行 channel/mutex 等读写操作,但因为逻辑问题,某些状况下会被始终阻塞。Goroutine 内的业务逻辑进入死循环,资源始终无奈开释。Goroutine 内的业务逻辑进入长时间期待,有一直新增的 Goroutine 进入期待。接下来我会援用在网上冲浪收集到的一些 Goroutine 泄露例子(会在文末参考注明出处)。 channel 使用不当Goroutine+Channel 是最经典的组合,因而不少泄露都呈现于此。 ...

June 11, 2021 · 3 min · jiezi

关于golang:使用-MixGo-快速开发-API-项目视频教程

本视频采纳 MixGo V1.1 版本 当咱们习惯将 go 生态的不同的风行库拼装起来开发我的项目时,mix-go 就非常适合你,他帮忙你创立骨架代码把各种风行的库组合起来,帮忙你疾速开发各种类型的我的项目。 装置 golang下载 https://golang.google.cn/配置环境变量批改 golang 本人的环境变量 开启 go mod配置代理配置库哈希效验网站go env -w GO111MODULE="on" GOPROXY="https://goproxy.io,direct" GOSUMDB="sum.golang.org"将 $GOPATH/bin 目录退出到 $PATH打印 GOPATH go env GOPATH编辑 ~/.bashrc or ~/.zshrc 退出 $GOPATH/bin export PATH="$PATH:/Users/**/go/bin"装置 mixcligo get github.com/mix-go/mixcli创立我的项目$ mixcli new helloUse the arrow keys to navigate: ↓ ↑ → ← ? Select project type: CLI ▸ API Web (contains the websocket) gRPC骨架细节解说观看视频收听以下内容 配置文件di 依赖注入gin 相干gorm 的应用jwt 解决脱漏未讲的内容: 代码公布残缺的代码目录构造如下: .├── .env├── .gitignore├── README.md├── bin│   ├── .gitignore│   └── hello_linux├── commands│   ├── api.go│   ├── main.go│   └── welcome.go├── conf│   └── config.yml├── configor│   └── main.go├── controllers│   ├── auth.go│   ├── hello.go│   └── user.go├── di│   ├── goredis.go│   ├── gorm.go│   ├── server.go│   ├── session.go│   └── zap.go├── dotenv│   └── main.go├── go.mod├── go.sum├── main.go├── middleware│   ├── auth.go│   └── cors.go├── models│   └── users.go├── routes│   └── main.go└── runtime ├── .gitignore └── logs └── mix.log咱们只须要将编译好的二进制(穿插编译)加上内部文件公布到线上即可 ...

June 11, 2021 · 1 min · jiezi

关于golang:Go-语言中的零拷贝优化

博客原文Go 语言中的零拷贝优化 导言置信那些已经应用 Go 写过 proxy server 的同学应该对 io.Copy()/io.CopyN()/io.CopyBuffer()/io.ReaderFrom 等接口和办法不生疏,它们是应用 Go 操作各类 I/O 进行数据传输常常须要应用到的 API,其中基于 TCP 协定的 socket 在应用上述接口和办法进行数据传输时利用到了 Linux 的零拷贝技术 sendfile 和 splice。 我前段时间为 Go 语言外部的 Linux splice 零拷贝技术做了一点优化:为 splice 零碎调用实现了一个 pipe pool,复用管道,缩小频繁创立和销毁 pipe buffers 所带来的零碎开销,实践上来说可能大幅晋升 Go 的 io 规范库中基于 splice 零拷贝实现的 API 的性能。因而,我想从这个优化工作登程,分享一些我集体对多线程编程中的一些不成熟的优化思路。 因自己满腹经纶,故行文之间恐有纰漏,望诸君海涵,不吝赐教,若能予以斧正,则感激不尽。 splice纵观 Linux 的零拷贝技术,相较于mmap、sendfile和 MSG_ZEROCOPY 等其余技术,splice 从应用老本、性能和适用范围等维度综合来看更适宜在程序中作为一种通用的零拷贝形式。 splice() 零碎调用函数定义如下: #include <fcntl.h>#include <unistd.h>int pipe(int pipefd[2]);int pipe2(int pipefd[2], int flags);ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);fd_in 和 fd_out 也是别离代表了输出端和输入端的文件描述符,这两个文件描述符必须有一个是指向管道设施的,这算是一个不太敌对的限度。 ...

June 11, 2021 · 5 min · jiezi

关于golang:Github-Actions如何执行GO-Mod-私有库

Github Actions如何依赖Go Mod 公有库Golang版本应用golang1.13以上的版本 GO mod依赖github 公有库的根本设置设置GOPRIVATE:export GOPRIVATE=github.com/xxx/common生成GitHub access token设置git 配置 git config --global url."https://xxx:${{ xxx.ACCESS_TOKEN }}@github.com".insteadOf "https://github.com"设置了下面的三步之后,就能够失常的获取依赖了; GitHub Actions设置一个前提:咱们不想在GitHub 的actions中填写本人的access token明文所以须要在repo中找到Settings-->Secrets中加以一个名为ACCESS_TOKEN的Secrets,当然这个名字能够本人定 设置批改Actions代码 - name: Run tests shell: bash run: | export GO111MODULE=on export GOPRIVATE=github.com/bytom/common export GOPROXY=https://goproxy.cn git config --global url."https://user_name:${{ secrets.ACCESS_TOKEN }}@github.com".insteadOf "https://github.com" make ci 设置完之后就能够失常的运作了

June 10, 2021 · 1 min · jiezi

关于golang:你不知道的-Go-之-pprof

简介Go 有十分多好用的工具,pprof 能够用来剖析一个程序的性能。pprof 有以下 4 种类型: CPU profiling(CPU 性能剖析):这是最常应用的一种类型。用于剖析函数或办法的执行耗时;Memory profiling:这种类型也常应用。用于分析程序的内存占用状况;Block profiling:这是 Go 独有的,用于记录 goroutine 在期待共享资源破费的工夫;Mutex profiling:与 Block profiling 相似,然而只记录因为锁竞争导致的期待或提早。咱们次要介绍前两种类型。Go 中 pprof 相干的性能在包runtime/pprof中。 CPU profilingpprof 应用非常简单。首先调用pprof.StartCPUProfile()启用 CPU profiling。它承受一个io.Writer类型的参数,pprof会将剖析后果写入这个io.Writer中。为了不便预先剖析,咱们写到一个文件中。 在要剖析的代码后调用pprof.StopCPUProfile()。那么StartCPUProfile()和StopCPUProfile()之间的代码执行状况都会被剖析。不便起见能够间接在StartCPUProfile()后,用defer调用StopCPUProfile(),即剖析这之后的所有代码。 咱们当初实现一个计算斐波那契数列的第n数的函数: func fib(n int) int { if n <= 1 { return 1 } return fib(n-1) + fib(n-2)}而后应用 pprof 剖析一下运行状况: func main() { f, _ := os.OpenFile("cpu.profile", os.O_CREATE|os.O_RDWR, 0644) defer f.Close() pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() n := 10 for i := 1; i <= 5; i++ { fmt.Printf("fib(%d)=%d\n", n, fib(n)) n += 3 * i }}执行go run main.go,会生成一个cpu.profile文件。这个文件记录了程序的运行状态。应用go tool pprof命令剖析这个文件: ...

June 9, 2021 · 4 min · jiezi

关于golang:Go工程化结构

一、构造要点阐明:(一)、我的项目的骨干:/cmd每个我的项目的目录名应该与你想要的可执行文件的名称相匹配,例如:/cmd/myapp。不要在这个目录中搁置太多的代码,如果代码是能够导入其余我的项目中应用,则应该放在/pkg目录下。如果代码不是可重用的,或者不心愿其他人重用,应该把代码放在/internal目录中。 (二)、私人的应用程序和代码库:/internal这是你不心愿其他人在其应用程序或者库中导入代码,这个布局是由Go编译器自身来执行的。须要留神的是:internal目录并不局限于顶级目录,还能够在其余的目录下创立。能够向internal目录中增加额定的构造,以分隔共享和非共享的外部代码。这不是必须的(特地是对于较小的我的项目),然而最好有可视化的线索来显示预期的包的用处。理论的应用程序的代码能够放在/internal/app目录下(例如:/internal/app/myapp),这些应用程序共享的代码能够放在/internal/pkg目录中(例如:/internal/pkg/myprovilb)。因为咱们习惯把相干的服务,比方账号服务,外部有rpc、job、admin等,相干的服务整合到一起后须要辨别app。繁多的服务能够去掉/internal/myapp。 (三)、内部程序应用的库代码:/pkg内部程序能够应用的库代码(例如:/pkg/mypubliclib)。其余的我的项目会导入这些库,所以在这里放的货色应该谨慎。/pkg目录中能够参考Go规范库的组织形式,依照性能分类。/internal/pkg个别用于我的项目内的跨多利用的公共共享代码,然而其作用域仅在单个我的项目工程内。 留神:internal目录是确保公有包不可导入的更好办法,因为它是Go强制执行的,/pkg目录仍是一种很好的形式,能够显式的示意该目录中的代码对于其他人来说能够平安应用的好办法 (四)、我的项目根底库:/kit每个公司都应该为不同的微服务创立一个对立的kit工具包我的项目(根底库/框架)和app我的项目。根底库kit为独立我的项目,公司级倡议只有一个,依照性能目录来拆分会带来不少的管理工作,所以倡议合并整合。kit我的项目必须具备的特点: 对立规范库形式布局高度形象反对插件

June 8, 2021 · 1 min · jiezi

关于golang:Go语言中的神奇函数init

前言哈喽,兄弟们,我是asong。明天与大家聊一聊Go语言中的神奇函数init,为什么叫他神奇函数呢?因为该函数能够在所有程序执行开始前被调用,并且每个包下能够有多个init函数。这个函数应用起来比较简单,然而你们晓得他的执行程序是怎么的嘛?本文咱们就一起来解密。init函数的个性先简略介绍一下init函数的根本个性: init 函数先于main函数主动执行每个包中能够有多个init函数,每个包中的源文件中也能够有多个init函数init函数没有输出参数、返回值,也未声明,所以无奈援用不同包的init函数依照包导入的依赖关系决定执行程序无论包被导入多少次,init函数只会被调用一次,也就是只执行一次init函数的执行程序我在刚学习init函数时就对他的执行程序很好奇,在谷歌上搜了几篇文章,他们都有一样的图: 下图来源于网络: 这张图片很清晰的反馈了init函数的加载程序: 包加载优先级排在第一位,先层层递归进行包加载每个包中加载程序为:const > var > init,首先进行初始化的是常量,而后是变量,最初才是init函数。针对包级别的变量初始化程序,Go官网文档给出这样一个例子:var ( a = c + b // == 9 b = f() // == 4 c = f() // == 5 d = 3 // == 5 after initialization has finished)func f() int { d++ return d}变量的初始化按呈现的程序从前往后进行,假若某个变量须要依赖其余变量,则被依赖的变量先初始化。所以这个例子中,初始化程序是 d -> b -> c -> a。 上图只是表白了init函数大略的加载程序,有些细节咱们还是不晓得的,比方:以后包下有多个init函数,依照什么程序执行,以后源文件下有多个init函数,这又依照什么程序执行呢?原本想写个例子挨个验证一下的,起初一看Go官网文档中都有阐明,也就没有必要再写一个例子啦,间接说论断吧: 如果以后包下有多个init函数,首先依照源文件名的字典序从前往后执行。若一个文件中呈现多个init函数,则依照呈现程序从前往后执行。后面说的有点乱,对init函数的加载程序做一个小结: 从以后包开始,如果以后包蕴含多个依赖包,则先初始化依赖包,层层递归初始化各个包,在每一个包中,依照源文件的字典序从前往后执行,每一个源文件中,优先初始化常量、变量,最初初始化init函数,当呈现多个init函数时,则依照程序从前往后顺次执行,每一个包实现加载后,递归返回,最初在初始化以后包!init函数的应用场景还记得我之前的这篇文章吗:go解锁设计模式之单例模式,借用init函数的加载机制咱们能够实现单例模式中的饿汉模式,具体怎么实现能够参考这篇文章,这里就不在写一遍了。 init函数的应用场景还是挺多的,比方进行服务注册、进行数据库或各种中间件的初始化连贯等。Go的规范库中也有许多中央应用到了init函数,比方咱们常常应用的pprof工具,他就应用到了init函数,在init函数外面进行路由注册: //go/1.15.7/libexec/src/cmd/trace/pprof.gofunc init() { http.HandleFunc("/io", serveSVGProfile(pprofByGoroutine(computePprofIO))) http.HandleFunc("/block", serveSVGProfile(pprofByGoroutine(computePprofBlock))) http.HandleFunc("/syscall", serveSVGProfile(pprofByGoroutine(computePprofSyscall))) http.HandleFunc("/sched", serveSVGProfile(pprofByGoroutine(computePprofSched))) http.HandleFunc("/regionio", serveSVGProfile(pprofByRegion(computePprofIO))) http.HandleFunc("/regionblock", serveSVGProfile(pprofByRegion(computePprofBlock))) http.HandleFunc("/regionsyscall", serveSVGProfile(pprofByRegion(computePprofSyscall))) http.HandleFunc("/regionsched", serveSVGProfile(pprofByRegion(computePprofSched)))}这里就不扩大太多了,更多规范库中的应用办法大家能够本人去摸索一下。 ...

June 8, 2021 · 1 min · jiezi

关于golang:Go-每日一库之-ants源码赏析

简介继上一篇Go 每日一库之 ants,这篇文章咱们来一起看看ants的源码。 Pool通过上篇文章,咱们晓得ants池有两种创立形式: p, _ := ants.NewPool(cap):这种形式创立的池子对象须要调用p.Submit(task)提交工作,工作是一个无参数无返回值的函数;p, _ := ants.NewPoolWithFunc(cap, func(interface{})):这种形式创立的池子对象须要指定池函数,并且应用p.Invoke(arg)调用池函数。arg就是传给池函数func(interface{})的参数。在ants中这两种池子应用不同的构造来示意:ants.Pool和ants.PoolWithFunc。咱们先来介绍Pool。PoolWithFunc构造也是相似的,介绍完Pool之后,咱们再简略比拟一下它们。 Pool构造定义在文件pool.go中: // src/github.com/panjf2000/ants/pool.gotype Pool struct { capacity int32 running int32 workers workerArray state int32 lock sync.Locker cond *sync.Cond workerCache sync.Pool blockingNum int options *Options}各个字段含意如下: capacity:池容量,示意ants最多能创立的 goroutine 数量。如果为正数,示意容量无限度;running:曾经创立的 worker goroutine 的数量;workers:寄存一组 worker 对象,workerArray只是一个接口,示意一个 worker 容器,前面详述;state:记录池子以后的状态,是否已敞开(CLOSED);lock:锁。ants本人实现了一个自旋锁。用于同步并发操作;cond:条件变量。解决工作期待和唤醒;workerCache:应用sync.Pool对象池治理和创立worker对象,晋升性能;blockingNum:阻塞期待的工作数量;options:选项。上一篇文章曾经具体介绍过了。这里明确一个概念,ants中为每个工作都是由 worker 对象来解决的,每个 worker 对象会对应创立一个 goroutine 来解决工作。ants中应用goWorker示意 worker: // src/github.com/panjf2000/ants/worker.gotype goWorker struct { pool *Pool task chan func() recycleTime time.Time}后文具体介绍这一块内容,当初咱们只须要晓得Pool.workers字段就是寄存goWorker对象的容器。 Pool创立创立Pool对象需调用ants.NewPool(size, options)函数。省略了一些解决选项的代码,最终代码如下: // src/github.com/panjf2000/ants/pool.gofunc NewPool(size int, options ...Option) (*Pool, error) { // ... p := &Pool{ capacity: int32(size), lock: internal.NewSpinLock(), options: opts, } p.workerCache.New = func() interface{} { return &goWorker{ pool: p, task: make(chan func(), workerChanCap), } } if p.options.PreAlloc { if size == -1 { return nil, ErrInvalidPreAllocSize } p.workers = newWorkerArray(loopQueueType, size) } else { p.workers = newWorkerArray(stackType, 0) } p.cond = sync.NewCond(p.lock) go p.purgePeriodically() return p, nil}代码不难理解: ...

June 8, 2021 · 7 min · jiezi

关于golang:GO的网络编程分享

[TOC] GO的网络编程分享回顾一下咱们上次分享的网络协议5层模型 物理层数据链路层网络层传输层应用层每一层有每一层的独立性能,大多数网络都采纳分层的体系结构,每一层都建设在它的上层之上,向它的上一层提供肯定的服务,而把如何实现这一服务的细节对上一层加以屏蔽。 每一层背地的协定有哪些,具体有啥为什么呈现的,感兴趣的能够看看互联网协议知多少 理解了网络协议的分层,数据包是如何封包,如何拆包,如何失去源数据的,往下看心里就有点谱了 GO网络编程指的是什么?GO网络编程,这里是指的是SOCKET编程 置信写过c/c++网络编程的敌人看到这里并不生疏吧,咱们再来回顾一下 网络编程这一块,分为客户端局部的开发,和服务端局部的开发,会波及到相应的要害流程 服务端波及的流程 socket建设套接字bind绑定地址和端口listen设置最大监听数accept开始阻塞期待客户端的连贯read读取数据write回写数据close 敞开客户端波及的流程 socket建设套接字connect 连贯服务端write写数据read读取数据咱们来看看SOCKET编程是啥?SOCKET就是套接字,是BSD UNIX的过程通信机制,他是一个句柄,用于形容IP地址和端口的。 当然SOCKET也是能够了解为TCP/IP网络的API(利用程序接口),SOCKET定义了许多函数,咱们能够用它们来开发TCP/IP网络上的应用程序。 电脑上运行的应用程序通常通过SOCKET向网络发出请求或者应答网络申请。 哈,忽然想到面向接口编程 顾名思义,就是在一个面向对象的零碎中,零碎的各种性能是由许许多多的不同对象合作实现的。 在这种状况下,各个对象外部是如何实现本人的,对系统设计人员来讲就不那么重要了; 各个对象之间的协作关系则成为零碎设计的要害,面向接口编程的晓得思维就是,无论模块大小,对应模块之间的交互都必须要在零碎设计的时候着重思考。 哈,一味的依赖他人提供的接口,对于接口外部会不会有坑,为什么会失败,咱们就不得而知了 开始socket编程先上一张图,咱们一起瞅瞅 Socket是应用层与TCP/IP协定族通信的两头软件形象层 在设计模式中,Socket其实就是一个门面模式,它把简单的TCP/IP协定族暗藏在Socket前面 对用户来说只须要调用Socket规定的相干函数就能够了,让Socket去做剩下的事件 Socket,应用程序通常通过Socket向网络收回申请 / 应答网络申请 罕用的Socket类型有2种: 流式Socket(stream)流式是一种面向连贯的Socket,针对于面向连贯的TCP服务利用 数据报式Socket数据报式Socket是一种无连贯的Socket,针对于无连贯的UDP服务利用 简略比照一下: TCP:比拟靠谱,面向连贯,平安,牢靠的传输方式 , 然而 比较慢UDP: 不是太靠谱,不牢靠的,丢包不会重传,然而 比拟快举一个当初生存中最常见的例子: 案例一 他人买一个小礼物给你,还要货到付款,这个时候快递员将货送到你家的时候,必须看到你人,而且你要付钱,这才是实现了一个流程 , 这是TCP 案例二 还是快递的例子,比方你在网上轻易抢了一些不太重要的小东西,小玩意,快递员送货的时候,间接就把你的包含扔到某个快递点,头都不回一下的那种, 这是UDP 网络编程无非简略来看就是TCP编程和UDP编程 咱们一起来看看GOLANG如何实现基于TCP通信 和 基于UDP通信的 GO基于TCP编程那咱们先来看看TCP协定是个啥? TCP/IP(Transmission Control Protocol/Internet Protocol) 传输控制协议/网间协定,是一种面向连贯(连贯导向)的、牢靠的、基于字节流的传输层(Transport layer)通信协议 因为是面向连贯的协定,数据像水流一样传输,这样会产生黏包问题。 上述提了个别socket编程的服务端流程和客户端流程,实际上go的底层实现也离不开这几步,然而咱们从利用的角度来看看go的TCP编程,服务端有哪些流程 TCP服务端TCP服务端能够同时连贯很多个客户端,这个毋庸置疑,要是一个服务端只能承受一个客户端的连贯,那么你完了,你能够拾掇货色回家了 举个栗子 最近也要开始的各种疯狂购物流动,他们的服务端,寰球各地的客户端都会去连贯,那么TCP服务端又是如何解决的嘞,在C/C++中咱们会基于epoll模型来进行解决,来一个客户端的连贯/申请事件,咱们就专门开一个线程去进行解决 那么golang中是如何解决的呢? golang中,每建设一个连贯,就会开拓一个协程goroutine来解决这个申请 ...

June 7, 2021 · 5 min · jiezi

关于golang:go的时间转换和比较

工夫转换 timeLayout := "2006-01-02 15:04:05"1.获取服务器工夫fmt.Println(time.Now().Format(timeLayout))2.获取UTC工夫fmt.Println(time.Now().UTC().Format(timeLayout))3.服务器工夫转换为UTC工夫timeStr := "2021-05-18 12:00:00" // 认为服务器工夫location, _ := time.LoadLocation("Local") // timeStr是北京工夫,留神应用LocallocalTime, _ := time.ParseInLocation(timeLayout, timeStr, location)fmt.Println(localTime.UTC().Format(timeLayout)) // 转为UTC工夫````阐明:留神timeStr值,此时咱们把它当做服务器工夫,所以time.LoadLocation("Local") 应用Local### 4.UTC工夫转换为服务器工夫timeStr := "2021-05-18 12:00:00" // 认为是UTC器工夫location, _ := time.LoadLocation("UTC") // timeStr是北京工夫,留神应用UTClocalTime, _ := time.ParseInLocation(timeLayout, timeStr, location)fmt.Println(localTime.Local().Format(timeLayout)) // 转为Local服务器工夫 阐明:留神timeStr值,此时咱们把它当做UTC工夫,所以time.LoadLocation("UTC") 应用Local## 工夫比拟```gotimeLayout := "2006-01-02 15:04:05"``````gotimeStr := "2021-05-17 11:00:00" // 认为服务器工夫location, _ := time.LoadLocation("Local") // timeStr是北京工夫,留神应用LocallocalTime, _ := time.ParseInLocation(timeLayout, timeStr, location)fmt.Println(localTime.UTC().Format(timeLayout)) // 转为UTC工夫compareTimeStr := "2021-05-17 02:00:00" // 认为UTC工夫location, _ = time.LoadLocation("UTC") // timeStr是北京工夫,留神应用UTCcompareTime, _ := time.ParseInLocation(timeLayout, compareTimeStr, location)fmt.Println(compareTime.Local().Format(timeLayout)) // 转为服务器工夫fmt.Println("------------------------------------")fmt.Println(compareTime.Before(localTime)) // Before 会对立工夫基准点fmt.Println(compareTime.After(localTime)) // After 会对立工夫基准点```

June 7, 2021 · 1 min · jiezi

关于golang:一文讲懂服务的优雅重启和更新

在服务端程序更新或重启时,如果咱们间接 kill -9 杀掉旧过程并启动新过程,会有以下几个问题: 旧的申请未解决完,如果服务端过程间接退出,会造成客户端链接中断(收到 RST)新申请打过去,服务还没重启结束,造成 connection refused即便是要退出程序,间接 kill -9 依然会让正在解决的申请中断很间接的感触就是:在重启过程中,会有一段时间不能给用户提供失常服务;同时粗鲁敞开服务,也可能会对业务依赖的数据库等状态服务造成净化。 所以咱们服务重启或者是从新公布过程中,要做到新旧服务无缝切换,同时能够保障变更服务 零宕机工夫! 作为一个微服务框架,那 go-zero 是怎么帮开发者做到优雅退出的呢?上面咱们一起看看。 优雅退出在实现优雅重启之前首先须要解决的一个问题是 如何优雅退出: 对 http 服务来说,个别的思路就是敞开对 fd 的 listen , 确保不会有新的申请进来的状况下解决完曾经进入的申请, 而后退出。go 原生中 http 中提供了 server.ShutDown(),先来看看它是怎么实现的: 设置 inShutdown 标记敞开 listeners 保障不会有新申请进来期待所有沉闷链接变成闲暇状态退出函数,完结别离来解释一下这几个步骤的含意: inShutdownfunc (srv *Server) ListenAndServe() error { if srv.shuttingDown() { return ErrServerClosed } .... // 理论监听端口;生成一个 listener ln, err := net.Listen("tcp", addr) if err != nil { return err } // 进行理论逻辑解决,并将该 listener 注入 return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})}func (s *Server) shuttingDown() bool { return atomic.LoadInt32(&s.inShutdown) != 0}ListenAndServe 是http启动服务器的必经函数,外面的第一句就是判断 Server 是否被敞开了。 ...

June 7, 2021 · 2 min · jiezi

关于golang:golang-struct-map-json-之间的相互转换

Golang 的 struct,map,json 互转本文用于记录我在 golang 学习阶段遇到的类型转换问题,针对的是 json 、map、struct 之间互相转换的问题,用到的技术 json 、mapstructure、reflect 三个类库公共代码区域package mainimport ( "encoding/json" "fmt" "testing")type UserInfoVo struct { Id string `json:"id"` UserName string `json:"user_name"` Address []AddressVo `json:"address"`}type AddressVo struct { Address string `json:"address"`}var beforeMap = map[string]interface{}{ "id": "123", "user_name": "酒窝猪", "address": []map[string]interface{}{{"address": "address01"}, {"address": "address02"}},}var User UserInfoVofunc init() { User = UserInfoVo{ Id: "01", UserName: "酒窝猪", Address: []AddressVo{ { Address: "湖南", }, { Address: "北京", }, }, }}一、map, struct 互转1.map 转 structmap 转 struct 有两种形式 ...

June 6, 2021 · 2 min · jiezi

关于golang:你知道-Go-结构体和结构体指针调用有什么区别吗

微信搜寻【脑子进煎鱼了】关注这一只爆肝煎鱼。本文 GitHub github.com/eddycjy/blog 已收录,有我的系列文章、材料和开源 Go 图书。大家好,我是煎鱼。 前几天在分享《Go 构造体是否能够比拟,为什么?》时,有小伙伴提出了新的问题: 尽管大家提问题的速度曾经超出了本鱼写文章的速度...不过作为宠粉狂鱼,在此刻清明假期时还是写下了这篇文章。 我在网上冲浪时搜寻了相干问题,发现 6 年前就有 Go 开发者有截然不同的疑难,真是困扰了一代又一代的小伙伴。 本期的男主角是《Go 构造体和构造体指针调用有什么区别》,心愿对大家有所帮忙,带来一些思考。 请在此处默念本人心目中的答案,再和煎鱼一起研究一波 Go 的技术哲学。 构造体是什么在 Go 语言中有个根本类型,开发者们称之为构造体(struct)。是 Go 语言中十分罕用的,根本定义: type struct_variable_type struct { member definition member definition ... member definition}简略示例: package mainimport "fmt"type Vertex struct { Name1 string Name2 string}func main() { v := Vertex{"脑子进了", "煎鱼"} v.Name2 = "蒸鱼" fmt.Println(v.Name2)}输入后果: 蒸鱼这部分属于基础知识,因而不再过多解释。如果看不懂,倡议重学 Go 语言语法根底。 构造体和指针调用解说前置概要后,间接进入本文主题。如下例子: type MyStruct struct { Name string}func (s MyStruct) SetName1(name string) { s.Name = name}func (s *MyStruct) SetName2(name string) { s.Name = name}该程序申明了一个 User 构造体,其蕴含两个构造体办法,别离是 SetName1 和 SetName2 办法,两者之间的差别就是援用的形式不同。 ...

June 6, 2021 · 1 min · jiezi

关于golang:Go也能Eval了

Go Eval 库Golang 的 eval() 函数第三方实现。 背景家喻户晓,Golang 是一门动态语言,笔者作为动静语言转过来的老同志(别猜了我是phper),习惯了用 eval() 就想着Go 动静执行代码呢。 如何在 golang 中应用 eval() 函数,php,javascript 自带该性能。golang 官网是没有提供相干库的。 在丰盛的Go第三方生态中,着实没找到相干的库。倒是看到有人做了一个demo 挂到博客。我就来拿来改改,做成了一个库,望宽广 gopher 用的称心。 装置$ go get github.com/PaulXu-cn/goeval性能介绍这个 goeval 库,传入 golang 代码字符串,而后执行 eval() 函数,就能取得该 字符串代码 输入到 stdout 上的内容。 应用例子: package mainimport ( "fmt" eval "github.com/PaulXu-cn/goeval")func main() { if re, err := eval.Eval("", "fmt.Print(\"Hello World itjsz.com\")", "fmt"); nil == err { fmt.Print(string(re)) } else { fmt.Print(err.Error()) }}输入: Hello World itjsz.com这里咱们引入了 goeval 包,调用它的 Eval 函数,第一个参数是,构造体定义代码(因为代码中没有有用自定义构造体,所有这里传空), 第二个字符串是要执行的代码,第三及当前的字符串是import的包。 ...

June 5, 2021 · 1 min · jiezi

关于golang:记一次-gomicro-服务异常退出问题的根因分析

origin: https://github.com/x1nchen/bl... Table of Contents前情提要问题形容剖析起因 容器进行这个操作到底执行的是什么?go-micro 如何解决 linux signal?通过二分法代码测试查看,发现过程 gops 后的问题解决方案经验总结遇到一个很有意思的问题,在此记录一下。 <span class="underline">TLDR: golang 服务通过 signal.Notify 注册 signal handler 的行为有且只有一次,否则会呈现不可预知的行为。</span> 前情提要公司开发微服务用的 go-micro 框架,这个框架提供了一个及其有价值的个性:micro.AfterStop 函数容许服务退出之后执行一段自定义的逻辑,通常用于清理资源,期待 goroutine 退出等。 另一方面,咱们在生产环境也对每个服务集成了 gops ,这个工具极大了晋升了服务的可察看性。 问题形容某天,某位仔细的共事告知 micro.AfterStop 自定义函数在容器重启时,并没有执行。现场复现确认问题存在。 上面是一个 minimal reproducible example package mainimport ( "fmt" "log" "github.com/micro/go-micro/v2" grpcServer "github.com/micro/go-micro/v2/server/grpc" "github.com/google/gops/agent")func main() { if err := agent.Listen(agent.Options{ Addr: "0.0.0.0:0", ShutdownCleanup: true}); err != nil { log.Fatal(err) } defer agent.Close() srv.Init( micro.Name("service.name"), micro.Server(grpcServer.NewServer()), micro.AfterStop(func() error { fmt.Println("after stop") return nil }), ) if err := srv.Run(); err != nil { fmt.Println("server run err", err) }}https://docs.docker.com/engin... ...

June 4, 2021 · 2 min · jiezi

关于golang:Go-面试官单核-CPU开两个-Goroutine其中一个死循环会怎么样

微信搜寻【脑子进煎鱼了】关注这一只爆肝煎鱼。本文 GitHub github.com/eddycjy/blog 已收录,有我的系列文章、材料和开源 Go 图书。大家好,我是煎鱼。 最近金三银四,是面试的节令。在我的 Go 读者交换群里呈现了许多小伙伴在探讨本人面试过程中所遇到的一些 Go 面试题。 明天的配角,是 Go 面试的万能题 GMP 模型的延长题(疑难),那就是 ”GMP 模型,为什么要有 P?“ 进一步斟酌问题的背地,其实这个面试题实质是想问:”GMP 模型,为什么不是 G 和 M 间接绑定就完了,还要搞多一个 P 进去,那么麻烦,为的是什么,是要解决什么问题吗?“ 这篇文章煎鱼就带你一起摸索,GM、GMP 模型的变迁是因为什么起因。 GM 模型在 Go1.1 之前 Go 的调度模型其实就是 GM 模型,也就是没有 P。 明天带大家一起回顾过去的设计。 解密 Go1.0 源码咱们理解一个货色的方法之一就是看源码,和煎鱼一起看看 Go1.0.1 的调度器源码的外围关键步骤: static voidschedule(G *gp){ ... schedlock(); if(gp != nil) { ... switch(gp->status){ case Grunnable: case Gdead: // Shouldn't have been running! runtime·throw("bad gp->status in sched"); case Grunning: gp->status = Grunnable; gput(gp); break; } gp = nextgandunlock(); gp->readyonstop = 0; gp->status = Grunning; m->curg = gp; gp->m = m; ... runtime·gogo(&gp->sched, 0);}调用 schedlock 办法来获取全局锁。获取全局锁胜利后,将以后 Goroutine 状态从 Running(正在被调度) 状态批改为 Runnable(能够被调度)状态。调用 gput 办法来保留以后 Goroutine 的运行状态等信息,以便于后续的应用;调用 nextgandunlock 办法来寻找下一个可运行 Goroutine,并且开释全局锁给其余调度应用。获取到下一个待运行的 Goroutine 后,将其的运行状态批改为 Running。调用 runtime·gogo 办法,将刚刚所获取到的下一个待执行的 Goroutine 运行起来。思考 GM 模型通过对 Go1.0.1 的调度器源码分析,咱们能够发现一个比拟乏味的点。那就是调度器自身(schedule 办法),在失常流程下,是不会返回的,也就是不会完结主流程。 ...

June 4, 2021 · 2 min · jiezi

关于golang:go使用swagger生成接口文档

装置swag 工具go get -u github.com/swaggo/swag/cmd/swag在入口文件写上我的项目的简介 package mainimport ("flag""open-api/internal/app/fund")// @title 这里写题目`// @version 1.0`// @description 这里写形容信息`// @termsOfService [http://swagger.io/terms/](http://swagger.io/terms/)`// @contact.name 这里写联系人信息`// @contact.url [http://www.swagger.io/support](http://www.swagger.io/support)`// @contact.email support@swagger.io`// @license.name Apache 2.0`// @license.url [http://www.apache.org/licenses/LICENSE-2.0.html](http://www.apache.org/licenses/LICENSE-2.0.html)`// @host 这里写接口服务的host`// @BasePath 这里写base path`func main() {var configPath stringflag.StringVar(&configPath, "config", "./configs", "config path")flag.Parse()fund.Run(configPath)}在你代码中解决申请的接口函数(通常位于controller层)按如下形式写上正文: // @Summary 领取列表// @Description 获取领取列表// @Accept json// @Produce json// @param Authorization header string true "验证参数Bearer和token空格拼接"// @Param body body st.PaySearch true "交款查问参数"// @Success 200 {object} response.Response{data=st.PayListResponse}// @Failure 500 {object} response.Response// @Router /app/pay/list [post]func PayList(ctx *gin.Context) {var ( paySearch st.PaySearch data []st.PayListResponse err error)if err := ctx.Bind(&paySearch); err != nil { response.Failed(ctx, response.InvalidParams, response.GetMsg(response.InvalidParams), data, err) return}data, err = logic.GetPayListNew(p)if err != nil { response.Failed(ctx, response.Error, response.GetMsg(response.Error), data, err) return}response.Successed(ctx, response.Success, response.GetMsg(response.Success), data)}下面正文中参数类型应用了object,st.PaySearch st.PayListResponse response.Response具体定义如下: ...

June 4, 2021 · 2 min · jiezi

关于golang:Golang笔记

channelfunc main() { m := make(chan int64, 10) fmt.Println(len(m), cap(m))}func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { fmt.Println("Hello world") wg.Done() }() } wg.Wait()}func test3() { _ = make(map[chan interface{}]interface{})}

June 4, 2021 · 1 min · jiezi

关于golang:go语言高级编程笔记

1.6 常见并发模式

June 4, 2021 · 1 min · jiezi

关于golang:go语言中利用匿名函数和闭包实现文件名修改

package mainimport ( "fmt" "strings")//利用匿名函数和闭包实现文件后缀批改func makeSuffix(suffix string) func (string) string{ return func (fileName string) string{ //如果文件后缀不是以suffix结尾,则加上后缀,否则返回原文件名 if !strings.HasSuffix(fileName, suffix) { return fileName + suffix } return fileName }}func main(){ f := makeSuffix(".jpg") fileName1 := "file.jpg" fileName2 := "file" fmt.Println(f(fileName1)) fmt.Println(f(fileName2))}执行后果file.jpgfile.jpg阐明这里makeSuffix返回的匿名函数f和作为内部传入的参数".jpg"独特形成了一个闭包,使得在传入一次".jpg"的状况下能够重复使用,这就奇妙的用到了闭包中保留上次应用的值的个性代替了重复传值的问题。

June 3, 2021 · 1 min · jiezi

关于golang:来我们探究一下nethttp的代码流程

[TOC] 这是我参加更文挑战的第 3 天,流动详情查看: 更文挑战探索一下net/http 的代码流程net/http 是什么?是GO的其中一个规范库,用于Web利用的开发,应用这个库,能够让开发变得更加迅速和简便,且易于上手。 那么问题来了 应用库,的确不便,无脑调接口,拼拼凑凑能跑就行,管他效率性能,出了问题,删库跑路就行了。。。 理论真的是这个样子吗?作为一个开发,肯定要想方法弄明确不分明的事件,要弄明确用到工具的原理,更须要清晰的晓得本人开发产品的运作原理,正所谓 知其然,而不知其所以然,欲摹写其情状,而心不能自喻,口不能自宣,笔不能自传。咱们对于技术要有摸索精力,对代码要有敬畏之心,那明天咱们就来看看net/http的代码流程吧 应用框架/库,必要要承受其本身的一套约定和模式,咱们必须要理解和相熟这些约定和模式的用法,否则就会陷入用错了都不晓得的地步。 在GOLANG中,net/http的组成部分有客户端 和 服务端 库中的构造和函数有的只反对客户端和服务器这两者中的一个,有的同时反对客户端和服务器,用图谈话: 只反对客户端的Client , response 只反对服务端的ServerMux,Server ,ResponseWriter,Handler 和 HandlerFunc 客户端,服务端都反对的Header , Request , Cookie net/http构建服务器也很简略,大体框架如下: 客户端 申请 服务器,服务器外面应用 net/http包,包中有多路复用器,和对应多路复用器的接口,服务器中的多个处理器解决不同的申请,最终须要落盘的数据即入库 万里长城第一步,咱们发车了 开始写一个简略的Requestpackage mainimport ( "fmt" "net/http")func main() { http.HandleFunc("/Hi", func(w http.ResponseWriter, r *http.Request) { // <h1></h1> 是html 标签 w.Write([]byte("<h1>Hi xiaomotong</h1>")) }) // ListenAndServe 不写ip 默认服务器地址是 127.0.0.1 if err := http.ListenAndServe(":8888", nil); err != nil { fmt.Println("http server error:", err) } }运行服务器代码,在浏览器中输出127.0.0.1:8888/Hi或者localhost:8888/Hi,即可看到成果 ...

June 3, 2021 · 5 min · jiezi

关于golang:函数的不定参数你会用吗

如果一个办法中须要传递多个参数且某些参数又是非必传,应该如何解决? 案例// NewFriend 寻找气味相投敌人func NewFriend(sex int, age int, hobby string) (string, error) { // 逻辑解决 ... return "", nil}NewFriend(),办法中参数 sex 和 age 为非必传参数,这时办法如何怎么写? 传参应用不定参数! 想一想怎么去实现它? 看一下这样写能够吗? // Sex 性别type Sex int// Age 年龄type Age int// NewFriend 寻找气味相投的敌人func NewFriend(hobby string, args ...interface{}) (string, error) { for _, arg := range args { switch arg.(type) { case Sex: fmt.Println(arg, "is sex") case Age: fmt.Println(arg, "is age") default: fmt.Println("未知的类型") } } return "", nil}有没有更好的计划呢? ...

June 3, 2021 · 1 min · jiezi

关于golang:从0开始在centos安装neovimgo开发环境

一、配置GO环境在本人的环境变量中配置好go的path 77 export GOROOT=/usr/local/go 78 export PATH=$PATH:$GOROOT/bin 79 export GOPATH=/data/gowork二、装置nvim1.在官网下载 nvim-linux64.tar.gz 这个安装包 tar -zxvf nvim-linux64.tar.gz 解压后应用 ln -s建设链接 ln -s /root/nvim/nvim-linux64/bin/nvim nvim#在配置文件中增加申明,使nvim成为关键字export PATH=$PATH:/root/nvim/nvim-linux64/bin而后就能够像应用vim一样应用nvim了 三、装置nodejs & pythone3因为后续装置的一些代码补全插件须要这两个组件的反对,所以先提前装置 1.装置nodejs1.先去https://npm.taobao.org/mirror...2.tar zxvf 解压到想要寄存的文件目录下3.在/etc/profile 文件中申明 export PATH=$PATH:/root/node/node-v14.16.1-linux-x64/bin4.设立链接 ln -s /root/node/node-v14.16.1-linux-x64/bin/npm ~/npmln -s /root/node/node-v14.16.1-linux-x64/bin/node ~/node#source 让配置的path失效source /etc/profile[root@VM-0-16-centos ~]# npm -v6.14.12[root@VM-0-16-centos ~]# node -vv14.16.1查看版本胜利即装置胜利2.装置python31.下载python安装包https://www.python.org/ftp/py... 2.tar -xzf Python-3.8.6rc1.tgz3.进入解压完的目录 执行如下命令, 门路为想装置的py门路./configure --prefix="/root/python3.8"4.等执行完后持续在解压完的目录下顺次执行makemake install5.编译实现后在环境变量中增加 alias python3=/root/python3.8/bin/python3.8alias pip3=/root/python3.8/bin/pip3.8而后查看pthone3 -vpip3 -v测验装置胜利 四、装置nvim的插件1.创立 mkdir -p ~/./config/nvim 文件夹而后在~/./config/nvim中创立 init.vim 文件 (相当于配置文件)pulgged 文件夹 (在配置文件中设置插件下载保留的地位)autoload 文件夹 (放plug.vim)2.git clone https://github.com/junegunn/v... 进入vim-plug目录将 plug.vim这个文件拷贝到刚刚创立的autoload文件夹中 ...

June 2, 2021 · 4 min · jiezi

关于golang:难受生产-Go-timerAfter-内存泄露之痛

微信搜寻【脑子进煎鱼了】关注这一只爆肝煎鱼。本文 GitHub github.com/eddycjy/blog 已收录,有我的系列文章、材料和开源 Go 图书。大家好,我是煎鱼。 前几天分享了一篇 Go timer 源码解析的文章《难以驾驭的 Go timer,一文带你参透计时器的神秘》。 在评论区有小伙伴提到了经典的 timer.After 泄露问题,心愿我能聊聊,这是一个不能不知的一个大 “坑”。 明天这篇文章煎鱼就带大家来研究一下这个问题。 timer.After明天是男主角是Go 规范库 time 所提供的 After 办法。函数签名如下: func After(d Duration) <-chan Time 该办法能够在肯定工夫(依据所传入的 Duration)后被动返回 time.Time 类型的 channel 音讯。 在常见的场景下,咱们会基于此办法做一些计时器相干的性能开发,例子如下: func main() { ch := make(chan string) go func() { time.Sleep(time.Second * 3) ch <- "脑子进煎鱼了" }() select { case _ = <-ch: case <-time.After(time.Second * 1): fmt.Println("煎鱼进来了,超时了!!!") }}在运行 1 秒钟后,输入后果: 煎鱼进来了,超时了!!!上述程序在在运行 1 秒钟后将触发 time.After 办法的定时音讯返回,输入了超时的后果。 ...

June 2, 2021 · 2 min · jiezi

关于golang:优雅地处理错误真是一门学问啊

errors这个大家必定应用过,规范库的 errors 谬误实现比较简单,无奈进行堆栈追溯,对于产生谬误时的下层调用者来讲不是很敌对,无奈取得谬误的调用链详细信息。 // 不带堆栈err := errors.New("error msg")fmt.Printf("%+v\n", err)// 输入error msgpkg/errorsgithub.com/pkg/errors 反对堆栈信息,能够取得谬误的调用链详细信息。 一般的// 带堆栈err := errors.New("error msg")fmt.Printf("%+v\n", err)// 输入error msgmain.main /Users/xinliang/go/project/demo/err/err.go:14runtime.main /usr/local/go/src/runtime/proc.go:225runtime.goexit /usr/local/go/src/runtime/asm_amd64.s:1371带堆栈,包装形容err := errors.Wrap(err error, message string)或err := errors.Wrapf(err error, format string, args ...interface{})带堆栈,不包装形容err := errors.WithStack(err error)不带堆栈,包装形容err := errors.WithMessage(err error, message string)或 err := errors.WithMessagef(err error, format string, args ...interface{})思考大家想一想,咱们在应用 pkg/errors 时,会遇到什么问题? 会遇到反复堆栈的问题! 比方,一个办法的调用链路比拟长,就会呈现这种状况,举个例子: func main() { err := func1() fmt.Printf("%+v\n", errors.Wrapf(err, "func1 error occurred"))}func func1() error { err := func2() return errors.Wrapf(err, "func2 error occurred")}func func2() error { err := errors.New("error msg") return err}想想看,会打印出什么? ...

June 1, 2021 · 1 min · jiezi

关于golang:Go-面试官Go-结构体是否可以比较为什么

微信搜寻【脑子进煎鱼了】关注这一只爆肝煎鱼。本文 GitHub github.com/eddycjy/blog 已收录,有我的系列文章、材料和开源 Go 图书。大家好,我是煎鱼。 最近金三银四,是面试的节令。在我的 Go 读者交换群里呈现了许多小伙伴在探讨本人面试过程中所遇到的一些 Go 面试题。 明天的男主角,是 Go 工程师的必修技能,也是极容易踩坑的中央,就是 “Go 面试题:Go 构造体(struct)是否能够比拟?” 如果能够比拟,是为什么?如果不能够比拟,又是为什么? 请在此处默念本人心目中的答案,再往和煎鱼一起研究一波 Go 的技术哲学。 构造体是什么在 Go 语言中有个根本类型,开发者们称之为构造体(struct)。是 Go 语言中十分罕用的,根本定义: type struct_variable_type struct { member definition member definition ... member definition}简略示例: package mainimport "fmt"type Vertex struct { Name1 string Name2 string}func main() { v := Vertex{"脑子进了", "煎鱼"} v.Name2 = "蒸鱼" fmt.Println(v.Name2)}输入后果: 蒸鱼这部分属于基础知识,因而不再过多解释。如果看不懂,倡议重学 Go 语言语法根底。 比拟两下例子一接下来正式开始研究 Go 构造体比拟的问题,第一个例子如下: type Value struct { Name string Gender string}func main() { v1 := Value{Name: "煎鱼", Gender: "男"} v2 := Value{Name: "煎鱼", Gender: "男"} if v1 == v2 { fmt.Println("脑子进煎鱼了") return } fmt.Println("脑子没进煎鱼")}咱们申明了两个变量,别离是 v1 和 v2。其都是 Value 构造体的实例化,是同一个构造体的两个实例。 ...

June 1, 2021 · 2 min · jiezi

关于golang:周末利用-Golang-复刻童年游戏

ad88888ba 88 d8" "8b 88 Y8, 88 `Y8aaaaa, 8b,dPPYba, ,adPPYYba, 88 ,d8 ,adPPYba, `"""""8b, 88P' `"8a "" `Y8 88 ,a8" a8P_____88 `8b 88 88 ,adPPPPP88 8888[ 8PP""""""" Y8a a8P 88 88 88, ,88 88`"Yba, "8b, ,aa "Y88888P" 88 88 `"8bbdP"Y8 88 `Y8a `"Ybbd8"' > 记得小时候能在手机上玩到贪吃蛇就是一件很开心的事。>> 最近正好在学 golang ,试试它的能力边界>> 能在终端上复刻经典游戏,想想就很冲动 喜爱的小伙伴就分享给身边的敌人吧,一起重温高兴的时光 如何获取:在公众号回复 “贪吃蛇”或者在我的 github 主页获取。https://github.com/Megatron7-...(没法贴内部链接了,公众号回复就能收到地址)外围玩法:> 上下左右键管制走向,吃到10颗果实博得胜利 > 撞击墙壁、本身都会失败 >【ctrl + c】退出游戏 贪吃蛇原理:> 贪吃蛇在挪动的时候是将尾部元素放到头部来实现挪动。 > 吃到果子不须要挪到尾部,而是在头部新增一个节点。> 这和 list 构造就很像了,所以 snake 的本体构造我用 list 来形容。> 渲染局部用的是数组,不便随机寻址。 ...

June 1, 2021 · 1 min · jiezi

关于golang:自适应微服务治理背后的算法

前言go-zero 群里常常有同学问: 服务监控是通过什么算法实现的? 滑动窗口是怎么工作的?是否讲讲这块的原理? 熔断算法是怎么设计的?为啥没有半开半闭状态呢? 本篇文章,来剖析一下 go-zero 中指标统计背地的实现算法和逻辑。 指标怎么统计这个咱们间接看 breaker : type googleBreaker struct { k float64 stat *collection.RollingWindow proba *mathx.Proba}go-zero 中默认的 breaker 是以 google SRE 做为实现底本。当 breaker 在拦挡申请过程中,会记录以后这类申请的胜利/失败率: func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { ... // 执行理论申请函数 err := req() if acceptable(err) { // 理论执行:b.stat.Add(1) // 也就是说:外部指标统计胜利+1 b.markSuccess() } else { // 原理同上 b.markFailure() } return err}所以其实底层说白了就是:申请执行结束,会依据谬误产生次数,外部的统计数据结构会相应地加上统计值(可正可负)。同时随着工夫迁徙,统计值也须要随工夫进化。 简略来说:工夫序列内存数据库【也没数据库这么猛,就是一个存储,只是一个内存版的】 上面就来说说这个工夫序列用什么数据结构组织的。 滑动窗口咱们来看看 rollingwindow 定义数据结构: ...

June 1, 2021 · 2 min · jiezi

关于golang:小猴启蒙数据迁移实战

作者:刘都都 背景学前启蒙教育赛道,竞争日益激励,好将来团体旗下“小猴英语”,“小猴语文”,”小猴思维“三条独立产品站线,策略调整,资源合并,重拳出击,打造全新”小猴启蒙“品牌,专为2-6岁学前儿童打造的启蒙课程。 现状语言层面:小猴英语主技术栈应用JAVA语言; 小猴语文、思维主技术栈应用PHP语言构造层面:数据依赖关系、类型差异化大,无奈互相转换存储层面:课程存储构造、课前、课外、课中存储构造差异化大经营层面:独立经营、面向群体、策略形式均不雷同课程层面:课程差异化大,制作流程均不雷同计划行业常见的迁徙计划有“停机迁徙”、”双写迁徙“、”实时(单向)迁徙“,每一种计划都各自的特点和优劣势,对各个计划进行剖析。 停机迁徙:主动型,迁徙机会可控,复杂度较低,危险较低,成本低,对业务有损,对用户不敌对双写迁徙:被动型,迁徙机会可控,复杂度较高、危险较高,老本较高,对业务无损,对用户敌对实时迁徙:被动型,迁徙机会不可控,复杂度高,危险较高,老本中等,对业务无损,对用户敌对 挑战在团体策略背景推动下,联合以后现状及对将来布局,冀望摈弃历史包袱,轻装上阵,综合思考后采纳实时迁徙计划。面临诸多挑战,例如: 迁徙机会:不可控,用户自主触发迁徙不可逆:一旦触发迁徙,过程不可逆,应答突发状况,要有应急预案一致性:数据一致性,迁徙前和迁徙后的数据不能失落给飞机换引擎:兼顾用户体验,尽可能确保整个迁徙过程用户无感知,平滑过渡 迁徙计划小猴启蒙是一个全新的品牌,全新架构设计、存储结构设计,课程制作&排课设计,兼容旧的三科产品状态,同时具备灵便可扩大的能力。 第一步、迁徙机会明确迁徙机会是用户下载小猴启蒙APP,登录之后触发机会,触发机会后首先告诉三科服务,限度不可用,确保不会有新的数据长生,此时启动迁徙脚本进行数据迁徙,在此期间要确保迁徙速度足够快并且确保数据一致性,从而晋升用户体验,整个过程不可逆,一旦呈现紧急情况,须要有应急预案,迁徙胜利之后,用户即可在小猴启蒙APP上课(英语、思维、语文)。再也不必在三个APP中上课 第二步、数据分析&归类通过对业务及数据分析&归类、数据可分两大类根底数据、用户数据。两种数据类型特点也各不相同,例如: 根底数据:私有数据,其中包含售卖商品、积分商品、课包、绘本、儿歌用户数据:个人隐私数据,其中包含权利、积分、报告、课内数据,通过细分发现用户数据可分为外围数据、交易数据,例如:外围数据:权利、积分交易数据:交易记录、上课、完课记录等明细数据第三步、针对不同数据特点,制订合适迁徙计划通过上述剖析理解业务以后的数据及特点剖析,例如: 根底数据:数据量少、根底前置数据,可控迁徙机会及迁徙工夫,危险可控,可人工校验。用户数据:这部分数据比拟非凡,外围数据量较少且重要,明细数据量较大且重要水平没有外围数据的高,迁徙整体复杂度较高,外围数据实时迁徙,交易数据一旦产生就不会在有变更了,如果数据量大,可前置迁徙,配合着用户触发迁徙进行最终的数据迁徙。迁徙机会及工夫不可控,用户触发,危险不可控。第四步、”实时迁徙“技术计划分析 整个迁徙架构设计思维”透明化、先于用户发现问题,分钟级解决,数据可回溯“。在用户触发迁徙机会后,调度核心通过采纳Invoke反射机制,启动迁徙子工作&监控个子工作的状态(耗时、里程牌),子工作视数据量决定是否分片解决。 外围架构设计点:RDS-TO-DRDS:面对交易明细或学习记录这类数据,为了在迁徙时晋升查问速度,能够思考将数据同步到DRDS中,依照用户ID做分库分表的主键 工作解耦:面对多个业务模块或工作数据高低依赖关系时,通过解耦的形式升高或缩小高低依赖,例如:商品、订单、物流这几个模块,都是有档次关系的,惯例流程必定是先商品而后在订单之后依据订单生产物流数据,串行形式,这样带来的问题就是效率,故通过某种形式进行解耦,数据之间的依赖关系个别状况下依赖ID,这里咱们能够放弃某种状况下雷同旧的ID生成的新ID要保持一致,另外在迁徙的过程中除了有历史数据,还有新增数据,倡议通过ID位数辨别开来,防止撞针,另外也便于排查问题,依据ID即分辨出新的业务数据还是历史数据。多任务并行:并行是为了晋升迁徙速度,这块没什么可说的。可回溯:历史数据的关联主键ID是字符串类型的,须要雷同旧的ID生成的新ID要保持一致,记录&存储下来,当然还有一种是依据ID通过算法生成,而后通过算法反推出来。数据分片:数据分片也是为了晋升迁徙速度。幂等性:为了保障因不可控因素导致中断或异样,保证数据的一致性所做的工作隔离:首先保障用户与用户之间是隔离的,其次是工作是隔离的,为了保障稳定性进度监控:为了可能对线上的状况一目了然,每一个系统监控都是不可或缺的。状态计算:多个子工作同时工作,须要通过调度核心计算最终的迁徙状态,后果,耗时等。调度核心:波及所有各个业务模块的数据,故每个模块都称之为子工作,通过调度核心对立调度,监控,计算整体迁徙进度;触发所有的迁徙子工作&监控&计算。计算所有子工作的迁徙状态计算最终的迁徙进度,迁徙耗时,迁徙数据量,外围要害数据。监控告警:为用户迁徙内容,提供透明化的监控伎俩,先于用户发现问题品质保障体系:为数据一致性提供的伎俩,及突发状况的预案。数据分片Proxy:升高迁徙耗时晋升用户体验,解决大数据量迁徙问题。异构数据转换:从旧业务数据提供数据,通过格局转换、数据建模,后果计算,后置解决实现整个子工作的数据迁徙;其中里程碑是记录外围关键步骤,目标是为了疾速定位问题。业务ID转换器:数据ID类型产生了变动,字符串变成数值类型,为了确保数据一致性,故所有数据都可回溯,便于追溯数据前生今世。第五步、品质保障体系设计为了晋升用户体验及迁徙的速度及数据一致性所做的工作,使呈现突发状况,咱们可能对突发状况有应急预案。 对各个模块(工作)状态&进度监控,装备了主动修复机制,解决肯定水平上的问题,当主动修复机制解决不了时,及时的预警解决,咱们通过补丁助手能够疾速修复问题。 上线上线是对咱们后期所有的工作的一个考验及验证,这个过程咱们还是要审慎操作的。 内测阶段分为两步:内测:内部测试+数据演练外测:抽样不同类型的用户,作为种子用户进行测试。天然阶段:这个阶段还是比拟激进,求稳为主,逐渐阶段。倡议阶段:相对来说曾经肯定的信念了,能够中等规模的告诉迁徙。强制阶段:大规模的迁徙阶段了缄默阶段:对缄默用户做迁徙了,这部分数据是最初一批用户数据了复盘在上线的每一个阶段,对每个问题都要进行复盘总结,防止同样的问题再次发生,当复盘之后会发现,呈现的问题都很低级,不应该呈现的问题。例如: 环境问题:刚刚开始在预发环境,前期上线后对某个数据库地址批改拉下了,导致数据迁徙不是最新的 Redis问题:缓存过期工夫没有管制好,导致飞速增长。 ID问题:两边事先沟通不分明、导致数据对不上。 总结数据模型建设的办法 深刻理解两侧业务设计、数据结构设计新旧映射关系整顿(点对点转换)依据新的数据结构设计反向推旧零碎的数据结构设计字段属性映射、关系映射字段非凡类 型的含意新构造新增的字段整顿新增字段的数据起源新增字段的类型转换新增数据结构整顿获取数据源前置条件获取数据源办法后果属性和新增数据结构映射战绩迁徙数据量过20亿 迁徙人次近110万人次 未呈现汇集性问题、失败率0.1‰ 专利产出(已受权)数据迁徙办法、安装、电子设备及存储介质 成长对个人成长还是比拟大的,在技术上,架构设计衡量各方利益关系,没有完满的架构,只有最适宜的,不能只关注架构层面,对细节点及落地也须要更加的关注,细节决定胜利。在项目管理上,对我的项目沟通协调、节奏、危险把控也有很大的晋升,作为此次我的项目Owner,负责并协调七八种角色,将近30人,此次我的项目的成绩离不开组内及各位搭档的定力反对,另外也要感激刘俊海老师对整体计划的领导。

May 31, 2021 · 1 min · jiezi

关于golang:Go语言如何实现可重入锁

原文链接:Go语言如何实现可重入锁? 前言哈喽,大家好,我是asong。前几天一个读者问我如何应用Go语言实现可重入锁,忽然想到Go语言中如同没有这个概念,平时在业务开发中也没有要用到可重入锁的概念,一时懵住了。之前在写java的时候,就会应用到可重入锁,然而写了这么久的Go,却没有应用过,这是怎么回事呢?这一篇文章就带你来解密~什么是可重入锁之前写过java的同学对这个概念应该一目了然,可重入锁又称为递归锁,是指在同一个线程在外层办法获取锁的时候,在进入该线程的内层办法时会主动获取锁,不会因为之前曾经获取过还没开释而阻塞。美团技术团队的一篇对于锁的文章当中针对可重入锁进行了举例: 假如当初有多个村民在水井排队打水,有管理员正在照管这口水井,村民在打水时,管理员容许锁和同一个人的多个水桶绑定,这个人用多个水桶打水时,第一个水桶和锁绑定并打完水之后,第二个水桶也能够间接和锁绑定并开始打水,所有的水桶都打完水之后打水人才会将锁还给管理员。这个人的所有打水流程都可能胜利执行,后续期待的人也可能打到水。这就是可重入锁。 下图摘自美团技术团队分享的文章: 如果是非可重入锁,,此时管理员只容许锁和同一个人的一个水桶绑定。第一个水桶和锁绑定打完水之后并不会开释锁,导致第二个水桶不能和锁绑定也无奈打水。以后线程呈现死锁,整个期待队列中的所有线程都无奈被唤醒。 下图仍旧摘自美团技术团队分享的文章: 用Go实现可重入锁既然咱们想本人实现一个可重入锁,那咱们就要理解java中可重入锁是如何实现的,查看了ReentrantLock的源码,大抵实现思路如下: ReentrantLock继承了父类AQS,其父类AQS中保护了一个同步状态status来计数重入次数,status初始值为0,当线程尝试获取锁时,可重入锁先尝试获取并更新status值,如果status == 0示意没有其余线程在执行同步代码,则把status置为1,以后线程开始执行。如果status != 0,则判断以后线程是否是获取到这个锁的线程,如果是的话执行status+1,且以后线程能够再次获取锁。开释锁时,可重入锁同样先获取以后status的值,在以后线程是持有锁的线程的前提下。如果status-1 == 0,则示意以后线程所有反复获取锁的操作都曾经执行结束,而后该线程才会真正开释锁。 总结一下实现一个可重入锁须要这两点: 记住持有锁的线程统计重入的次数统计重入的次数很容易实现,接下来咱们考虑一下怎么实现记住持有锁的线程? 咱们都晓得Go语言最大的特色就是从语言层面反对并发,Goroutine是Go中最根本的执行单元,每一个Go程序至多有一个Goroutine,主程序也是一个Goroutine,称为主Goroutine,当程序启动时,他会主动创立。每个Goroutine也是有本人惟一的编号,这个编号只有在panic场景下才会看到,Go语言却刻意没有提供获取该编号的接口,官网给出的起因是为了防止滥用。然而咱们还是通过一些非凡伎俩来获取Goroutine ID的,能够应用runtime.Stack函数输入以后栈帧信息,而后解析字符串获取Goroutine ID,具体代码能够参考开源我的项目 - goid。 因为go语言中的Goroutine有Goroutine ID,那么咱们就能够通过这个来记住以后的线程,通过这个来判断是否持有锁,就能够了,因而咱们能够定义如下构造体: type ReentrantLock struct { lock *sync.Mutex cond *sync.Cond recursion int32 host int64}其实就是包装了Mutex锁,应用host字段记录以后持有锁的goroutine id,应用recursion字段记录以后goroutine的重入次数。这里有一个特地要阐明的就是sync.Cond,应用Cond的目标是,当多个Goroutine应用雷同的可重入锁时,通过cond能够对多个协程进行协调,如果有其余协程正在占用锁,则以后协程进行阻塞,直到其余协程调用开释锁。具体sync.Cond的应用大家能够参考我之前的一篇文章:源码分析sync.cond(条件变量的实现机制)。 构造函数func NewReentrantLock() sync.Locker{ res := &ReentrantLock{ lock: new(sync.Mutex), recursion: 0, host: 0, } res.cond = sync.NewCond(res.lock) return res}Lockfunc (rt *ReentrantLock) Lock() { id := GetGoroutineID() rt.lock.Lock() defer rt.lock.Unlock() if rt.host == id{ rt.recursion++ return } for rt.recursion != 0{ rt.cond.Wait() } rt.host = id rt.recursion = 1}这里逻辑比较简单,大略解释一下: ...

May 31, 2021 · 1 min · jiezi

关于golang:Go-面试官GMP-模型为什么要有-P

微信搜寻【脑子进煎鱼了】关注这一只爆肝煎鱼。本文 GitHub github.com/eddycjy/blog 已收录,有我的系列文章、材料和开源 Go 图书。大家好,我是煎鱼。 最近金三银四,是面试的节令。在我的 Go 读者交换群里呈现了许多小伙伴在探讨本人面试过程中所遇到的一些 Go 面试题。 明天的配角,是 Go 面试的万能题 GMP 模型的延长题(疑难),那就是 ”GMP 模型,为什么要有 P?“ 进一步斟酌问题的背地,其实这个面试题实质是想问:”GMP 模型,为什么不是 G 和 M 间接绑定就完了,还要搞多一个 P 进去,那么麻烦,为的是什么,是要解决什么问题吗?“ 这篇文章煎鱼就带你一起摸索,GM、GMP 模型的变迁是因为什么起因。 GM 模型在 Go1.1 之前 Go 的调度模型其实就是 GM 模型,也就是没有 P。 明天带大家一起回顾过去的设计。 解密 Go1.0 源码咱们理解一个货色的方法之一就是看源码,和煎鱼一起看看 Go1.0.1 的调度器源码的外围关键步骤: static voidschedule(G *gp){ ... schedlock(); if(gp != nil) { ... switch(gp->status){ case Grunnable: case Gdead: // Shouldn't have been running! runtime·throw("bad gp->status in sched"); case Grunning: gp->status = Grunnable; gput(gp); break; } gp = nextgandunlock(); gp->readyonstop = 0; gp->status = Grunning; m->curg = gp; gp->m = m; ... runtime·gogo(&gp->sched, 0);}调用 schedlock 办法来获取全局锁。获取全局锁胜利后,将以后 Goroutine 状态从 Running(正在被调度) 状态批改为 Runnable(能够被调度)状态。调用 gput 办法来保留以后 Goroutine 的运行状态等信息,以便于后续的应用;调用 nextgandunlock 办法来寻找下一个可运行 Goroutine,并且开释全局锁给其余调度应用。获取到下一个待运行的 Goroutine 后,将其的运行状态批改为 Running。调用 runtime·gogo 办法,将刚刚所获取到的下一个待执行的 Goroutine 运行起来。思考 GM 模型通过对 Go1.0.1 的调度器源码分析,咱们能够发现一个比拟乏味的点。那就是调度器自身(schedule 办法),在失常流程下,是不会返回的,也就是不会完结主流程。 ...

May 31, 2021 · 2 min · jiezi

关于golang:一文带你搞懂-RPC-到底是个啥

RPC(Remote Procedure Call),是一个大家既相熟又生疏的词,只有波及到通信,必然须要某种网络协议。咱们很可能用过HTTP,那么RPC又和HTTP有什么区别呢?RPC还有什么特点,常见的选型有哪些?1. RPC是什么RPC能够分为两局部:用户调用接口 + 具体网络协议。前者为开发者须要关怀的,后者由框架来实现。 举个例子,咱们定义一个函数,咱们心愿函数如果输出为“Hello World”的话,输入给一个“OK”,那么这个函数是个本地调用。如果一个近程服务收到“Hello World”能够给咱们返回一个“OK”,那么这是一个近程调用。咱们会和服务约定好近程调用的函数名。因而,咱们的用户接口就是:输出、输入、近程函数名,比方用 SRPC 开发的话,client端的代码会长这样: int main(){ Example::SRPCClient client(IP, PORT); EchoRequest req; // 用户自定义的申请构造 EchoResponse resp; // 用户自定义的回复构造 req.set_message("Hello World"); client.Echo(&req, &resp, NULL); // 调用近程函数名为Echo return 0;}具体网络协议,是框架来实现的,把开发者要收回和接管的内容以某种应用层协定打包进行网络收发。这里能够和HTTP进行一个显著的比照: HTTP也是一种网络协议,但包的内容是固定的,必须是:申请行 + 申请头 + 申请体;RPC是一种自定义网络协议,由具体框架来定,比方SRPC里反对的RPC协定有:SRPC/thrift/BRPC/tRPC这些RPC协定都和HTTP平行,是应用层协定。咱们再进一步思考,HTTP只蕴含具体网络协议,也能够返回比方咱们常见的HTTP/1.1 200 OK,但好像没有用户调用接口,这是为什么呢? 这里须要搞清楚,用户接口的性能是什么?最重要的性能有两个: 定位要调用的服务;让咱们的音讯向前/向后兼容;咱们用一个表格来看一下HTTP和RPC别离是怎么解决的: 定位要调用的服务音讯前后兼容HTTPURL开发者自行在音讯体里解决RPC指定Service和Method名交给具体IDL因而,HTTP的调用缩小了用户调用接口的函数,然而就义了一部分音讯向前/向后兼容的自由度。然而,开发者能够依据本人的习惯进行技术选型,因为RPC和HTTP之间大部分都是协定互通的!是不是很神奇?接下来咱们看一下RPC的档次架构,就能够明确为什么不同RPC框架之间、以及RPC和HTTP协定是如何做到互通的。 2. RPC有什么咱们能够从SRPC的架构档次上来看,RPC框架有哪些层,以及SRPC目前所横向反对的性能是什么: 用户代码(client的发送函数/server的函数实现)IDL序列化(protobuf/thrift serialization)数据组织 (protobuf/thrift/json)压缩(none/gzip/zlib/snappy/lz4)协定 (Sogou-std/Baidu-std/Thrift-framed/TRPC)通信 (TCP/HTTP)咱们先关注以下三个层级: 如图从左到右,是用户接触得最多到起码的档次。IDL层会依据开发者定义的申请/回复构造进行代码生成,目前小伙伴们用得比拟多的是protobuf和thrift,而方才说到的用户接口和前后兼容问题,都是IDL层来解决的。SRPC对于这两个IDL的用户接口实现形式是: thrift:IDL纯手工解析,用户应用srpc是不须要链thrift的库的 !!!protobuf:service的定义局部纯手工解析两头那列是具体的网络协议,而各RPC能互通,就是因为大家实现了对方的“语言”,因而能够协定互通。 而RPC作为和HTTP并列的档次,第二列和第三列实践上是能够两两联合的,只须要第二列的具体RPC协定在发送时,把HTTP相干的内容进行特化,不要依照本人的协定去发,而依照HTTP须要的模式去发,就能够实现RPC与HTTP互通。 3. RPC的生命周期到此咱们能够通过SRPC看一下,把request通过method发送进来并解决response再回来的整件事件是怎么做的: 依据上图, 能够更分明地看到方才提及的各个层级,其中压缩层、序列化层、协定层其实是相互解耦买通的,在SRPC代码上实现得十分对立,横向减少任何一种压缩算法或IDL或协定都不须要也不应该改变现有的代码,才是一个精美的架构~ 咱们始终在说生成代码,到底有什么用呢?图中能够得悉,生成代码是连接用户调用接口和框架代码的桥梁,这里以一个最简略的protobuf自定义协定为例:example.proto syntax = "proto3";message EchoRequest{ optional string message = 1;};message EchoResponse{ optional string message = 1;};service ExamplePB{ rpc Echo(EchoRequest) returns (EchoResponse);};咱们定义好了申请、回复、近程服务的函数名,通过以下命令就能够生成出接口代码example.srpc.h: ...

May 31, 2021 · 2 min · jiezi

关于golang:来瞧一瞧-gRPC的拦截器

[TOC] 瞧一瞧 gRPC的拦截器 上一次说到gRPC的认证总共有4种,其中介绍了罕用且重要的2种: 能够应用openssl做认证证书,进行认证客户端还能够将数据放到metadata中,服务器进行认证可是敌人们,有没有想过,要是每一个客户端与服务端通信的接口都进行一次认证,那么这是否会十分多余呢,且每一个接口的实现都要做一次认证,这真的太难受了 咱作为程序员,就应该要摸索高效的办法来解决一些繁琐简单冗余的事件。 明天咱们来分享一下gRPC的interceptor,即拦截器 ,相似于web框架里的中间件。 中间件是什么?是一类提供系统软件和应用软件之间连贯、便于软件各部件之间的沟通的计算机软件,它为软件应用程序提供操作系统以外的服务,被形象的形容为“软件胶水”直白的说,中间件即是一个系统软件和应用软件之间的沟通桥梁。例如他能够记录响应时长、记录申请和响应数据日志等 中间件能够在拦挡到发送给 handler 的申请,且能够拦挡 handler 返回给客户端的响应 拦截器是什么?拦截器是gRPC生态中的中间件 能够对RPC的申请和响应进行拦挡解决,而且既能够在客户端进行拦挡,也能够对服务器端进行拦挡。 拦截器能做什么?哈哈,他能做的可多了,最终要的一点是,拦截器能够做对立接口的认证工作,再也不须要每一个接口都做一次认证了,多个接口屡次拜访,只须要在对立个中央认证即可 这是不是大大的进步了接口的应用和认证效率了呢,同时还能够缩小代码的冗余度 拦截器有哪些分类呢?依据不同的侧重点,会有如下2种分类: 侧重点不同,分类的拦截器也不同,不过应用的形式都是大同小异的。 如何应用拦截器?服务端会用到的办法 UnaryServerInterceptor提供了一个钩子来拦挡服务器上繁多RPC的执行,拦截器负责调用处理程序来实现RPC 其中参数中的UnaryHandler定义了由UnaryServerInterceptor调用的处理程序 客户端会用到的办法 type UnaryClientInterceptor func( ctx context.Context, // 上下文 method string, // RPC的名字,例如此处咱们应用的是gRPC req, reply interface{}, // 对应的申请和响应音讯 cc *ClientConn, // cc是调用RPC的ClientConn invoker UnaryInvoker, // invoker是实现RPC的处理程序,次要是调用它是拦截器 opts ...CallOption) error // opts蕴含所有实用的调用选项,包含来自ClientConn的默认值以及每个调用选项整体案例代码构造代码构造与上2篇分享到的构造统一,本次拦截器,是对立做认证,把认证的中央对立放在同一个地位,而不是扩散到每一个接口 若须要具体的proto源码,能够查看我的上一期文章,如下为代码构造图示 开始书写案例在原有代码根底上退出interceptor的性能,目前案例中注册一个拦截器gRPC + openssl + token + interceptorserver.go 次要退出UnaryServerInterceptor来对拦截器的利用package mainimport ( "fmt" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "log" "net" pb "myserver/protoc/hi" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" // 引入grpc认证包)const ( // Address gRPC服务地址 Address = "127.0.0.1:9999")// 定义helloService并实现约定的接口type HiService struct{}// HiService Hello服务var HiSer = HiService{}// SayHello 实现Hello服务接口func (h HiService) SayHi(ctx context.Context, in *pb.HiRequest) (*pb.HiResponse, error) { // 解析metada中的信息并验证 md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, grpc.Errorf(codes.Unauthenticated, "no token ") } var ( appId string appKey string ) // md 是一个 map[string][]string 类型的 if val, ok := md["appid"]; ok { appId = val[0] } if val, ok := md["appkey"]; ok { appKey = val[0] } if appId != "myappid" || appKey != "mykey" { return nil, grpc.Errorf(codes.Unauthenticated, "token invalide: appid=%s, appkey=%s", appId, appKey) } resp := new(pb.HiResponse) resp.Message = fmt.Sprintf("Hi %s.", in.Name) return resp, nil}// 认证tokenfunc myAuth(ctx context.Context) error { md, ok := metadata.FromIncomingContext(ctx) if !ok { return grpc.Errorf(codes.Unauthenticated, "no token ") } log.Println("myAuth ...") var ( appId string appKey string ) // md 是一个 map[string][]string 类型的 if val, ok := md["appid"]; ok { appId = val[0] } if val, ok := md["appkey"]; ok { appKey = val[0] } if appId != "myappid" || appKey != "mykey" { return grpc.Errorf(codes.Unauthenticated, "token invalide: appid=%s, appkey=%s", appId, appKey) } return nil}// interceptor 拦截器func interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { // 进行认证 log.Println("interceptor...") err := myAuth(ctx) if err != nil { return nil, err } // 持续解决申请 return handler(ctx, req)}func main() { log.SetFlags(log.Ltime | log.Llongfile) listen, err := net.Listen("tcp", Address) if err != nil { log.Panicf("Failed to listen: %v", err) } var opts []grpc.ServerOption // TLS认证 creds, err := credentials.NewServerTLSFromFile("./keys/server.pem", "./keys/server.key") if err != nil { log.Panicf("Failed to generate credentials %v", err) } opts = append(opts, grpc.Creds(creds)) // 注册一个拦截器 opts = append(opts, grpc.UnaryInterceptor(interceptor)) // 实例化grpc Server, 并开启TLS认证,其中还有拦截器 s := grpc.NewServer(opts...) // 注册HelloService pb.RegisterHiServer(s, HiSer) log.Println("Listen on " + Address + " with TLS and interceptor") s.Serve(listen)}client.go ...

May 30, 2021 · 4 min · jiezi

关于golang:Go-面试官进程线程都有-ID为什么-Goroutine-没有-ID

微信搜寻【脑子进煎鱼了】关注这一只爆肝煎鱼。本文 GitHub github.com/eddycjy/blog 已收录,有我的系列文章、材料和开源 Go 图书。大家好,我是煎鱼。 最近金三银四,是面试的节令。在我的 Go 读者交换群里呈现了许多小伙伴在探讨本人面试过程中所遇到的一些 Go 面试题。 明天的配角,是大家在既有语言根底的状况下,学 Goroutine 时会容易纠结的一点。就是 “过程、线程都有 ID,为什么 Goroutine 没有 GoroutineID?”。 这是为什么呢,怎么做那些跨协程解决呢? GoroutineID 是什么咱们要晓得,为什么大家会下意识的想去要 GoroutineID,上面援用 Go 语言圣经中的表述: 在大多数反对多线程的操作系统和程序语言中,以后的线程都有一个独特的身份(ID),并且这个身份信息能够以一个一般值的模式被很容易地获取到,典型的能够是一个 integer 或者指针值。这种状况下咱们做一个抽象化的 thread-local storage(线程本地存储,多线程编程中不心愿其它线程拜访的内容)就很容易,只须要以线程的 ID 作为 key 的一个 map 就能够解决问题,每一个线程以其 ID 就能从中获取到值,且和其它线程互不抵触。也就在惯例的过程、线程中都有其 ID 的概念,咱们能够在程序中通过 ID 来获取其余过程、线程中的数据,甚至是传输数据。就像一把钥匙一样,有了他干啥都能够。 GoroutineID 的概念也是相似的,也就是协程的 ID。咱们下意识的就冀望通过协程 ID 来进行跨协程的操作。 但,在 Go 语言中 GoroutineID 并没有显式获取的方法,这就要打个大大的纳闷了。 为什么没有 GoroutineID为什么在 Go 语言中没有 GoroutineID 呢,是从一开始就没有的,还是,这样子设计的起因是什么呢? 其实 Go 语言在以前是有裸露办法去获取 GoroutineID 的,但在 Go1.4 后就把该办法给暗藏起来了,不倡议大家应用。 也就是明面上没有 GoroutineID,是一个无意而为之的行为。起因是:依据以往的教训,认为 thread-local storage 存在被滥用的可能性,且带来许多不必要的复杂度。 ...

May 30, 2021 · 2 min · jiezi

关于golang:简单的-Golang-多色日志包

比拟简洁的多色日志包,没有特地多的性能,仅提供在终端输入黑白日志的性能。 应用在go.mod文件中增加log包: require github.com/thep0y/go-logger latest在其余文件中应用: package mainimport ( "strings" "github.com/thep0y/go-logger/log")func main() { log.Info("这是默认 info 音讯") log.Infof("这是默认格式化的音讯:%s", "info") log.Warn("这是默认 warning 音讯") log.Warnf("这是默认格式化的音讯:%s", "warning") log.Error("这是默认error 音讯") log.Errorf("这是默认格式化的音讯:%s", "error") // log.Fatal("这是默认 fatal 音讯") println(strings.Repeat("-", 60)) logger := log.NewLogger() logger.Info("这是 info 音讯") logger.Infof("这是格式化的音讯:%s", "info") logger.Warn("这是 warning 音讯") logger.Warnf("这是格式化的音讯:%s", "warning") logger.Error("这是 error 音讯") logger.Errorf("这是格式化的音讯:%s", "error") logger.Fatal("这是 fatal 音讯")}error 和 fatal 音讯会输入调用函数所在文件和调用代码行号。 运行后果: main.main:main.go:21,第一个 main 是文件名,第二个 main 是办法 / 函数名,冒号前面的是哪个文件中的第几行代码打印的这个日志。 其余性能Logger 外还有其余的办法能够调用,但对于一个根本的日志这些额定的性能都是无关痛痒的存在,当前再写相干的应用办法。

May 28, 2021 · 1 min · jiezi

关于golang:时间戳防盗链七牛云-oss-为例的实现过程

每个对象存储服务商都会提供工夫戳防盗链的反对,原理根本一样,本文以七牛云为例,服务端是用 Go 实现的 API。 生成签名的 url 的时候,也要将新生成的 url 保留到 redis 中,避免每次拜访都生成新的签名,即节约服务器资源,又会减少 CDN 流量。 签名代码: // Sign 每一个图片生成 2 分钟有效期的签名//// imgPath 是图片门路,不是残缺链接。func Sign(imgPath string) (string, error) { var ( // 七牛云设置的签名的 Key key = "xxxxxxxxxx" // 过期工夫 expire = time.Now().Unix() + 60*2 ) rawStr := fmt.Sprintf("%s/%s%x", key, imgPath, expire) hash := md5.New() hash.Write([]byte(rawStr)) sign := fmt.Sprintf("%x", hash.Sum(nil)) var newPath string newPath = fmt.Sprintf("%s?sign=%s&t=%x", imgPath, sign, expire) // 生成签名后放进 redis 中,过期工夫比签名过期工夫短 1 秒,下次再拜访时先从 redis 中查找签名过的链接 err := redis.CacheSignedURL(imgPath, newPath, 60*2-1) if err != nil { return "", err } return newPath, nil}如果下次访问时没有从 redis 中获取到签名的 url ,再生成新的签名,伪码: ...

May 28, 2021 · 1 min · jiezi

关于golang:利用掘金部分数据实现-Elasticsearch-示例项目

爬取掘金的热门举荐页面局部信息作为示例数据保留到 es 中进行查问。 本我的项目中对 es 根本的创立、查问和删除操作均有简略实现。 我的项目地址:thep0y/juejin-hot-es-example 简介查问时应用命令行进行,示例我的项目的命令如下: juejin allows you to index and search hot-recommended article's titlesUsage: juejin [command]Available Commands: delete Delete item with id help Help about any command index Index juejin hot-recommended articles into Elasticsearch search Search juejin hot recommended articlesFlags: -h, --help help for juejin -i, --index string Index name (default "juejin")Use "juejin [command] --help" for more information about a command.可选命令为index、search和delete。 其中index也有可选命令: --pages int The count of pages you want to crawl (default 5) --setup Create Elasticsearch index本我的项目应用的是本地 es ,举荐用 docker 创立,es 中须要装置 ik 中文分词插件。 ...

May 28, 2021 · 1 min · jiezi

关于golang:Elasticsearch-Go-客户端

本文代码来自于官网示例。 一、配置此例演示的是配置客户端的传输Transport。 类例中的配置次要用于阐明其作用,不适用于生产环境。 默认的传输就够用了。 package mainimport ( "github.com/elastic/go-elasticsearch/v8")func main() { cfg := elasticsearch.Config{ Addresses: []string{"http://localhost:9200"}, Transport: &http.Transport{ MaxIdleConnsPerHost: 10, ResponseHeaderTimeout: time.Millisecond, DialContext: (&net.Dialer{Timeout: time.Nanosecond}).DialContext, TLSClientConfig: &tls.Config{ MinVersion: tls.VersionTLS11, }, }, } es, err := elasticsearch.NewClient(cfg) if err != nil { panic(err) } resp, err := es.Info() if err != nil { panic(err) } fmt.Println(resp) // => panic: dial tcp: i/o timeout}二、自定义传输自定义传输用于读取或操作申请和响应,自定义日志记录,将自定义 header 传递给申请等。 CountingTransport将自定义 header 增加到申请,记录无关申请和响应的信息,并统计申请次数。 因为它实现了http.RoundTripper接口,因而能够将其作为自定义 HTTP 传输实现传递给客户端。 ...

May 28, 2021 · 18 min · jiezi

关于golang:Elasticsearch-的-easyjson-示例

mailru/easyjson库的特点就是咱们只须要提供构造体,而后用这些构造体生成编码解码的代码。 示例的我的项目名为elasticsearch/encoding/json。 一、创立 models在我的项目中新建 model 目录,目录中新建两个文件model.go和response.go,在这两个文件头都增加一行代码生成正文: //go:generate easyjson -all -snake_case $GOFILE//后不能有空格models.go: //go:generate easyjson -all -snake_case $GOFILEpackage modelimport ( "time")type Article struct { ID uint Title string Body string Published time.Time Author *Author}type Author struct { FirstName string LastName string}response.go: //go:generate easyjson -all -snake_case $GOFILEpackage modeltype ErrorResponse struct { Info *ErrorInfo `json:"error,omitempty"`}type ErrorInfo struct { RootCause []*ErrorInfo Type string Reason string Phase string}type IndexResponse struct { Index string `json:"_index"` ID string `json:"_id"` Version int `json:"_version"` Result string}type SearchResponse struct { Took int64 Hits struct { Total struct { Value int64 } Hits []*SearchHit }}type SearchHit struct { Score float64 `json:"_score"` Index string `json:"_index"` Type string `json:"_type"` Version int64 `json:"_version,omitempty"` Source Article `json:"_source"`}二、欠缺主逻辑代码main.go: ...

May 28, 2021 · 3 min · jiezi

关于golang:docker-搭建-Elasticsearch-和-Kibana

一、ElasticSearch1 pullElasticSearch的 docker 镜像不反对默认的 latest 标签,所以在 Pull 时须要指定标签: docker pull elasticsearch:7.12.02 创立网络连接用来与其余容器服务进行通信,比方 Kibana。 docker network create es-test3 创立容器docker run -itd --name es --net es-test -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.12.04 批改配置,容许跨域拜访docker exec -it es bashecho -e "http.cors.enabled: true\nhttp.cors.allow-origin: \"*\"" >> config/elasticsearch.yml5 装置 ik 分词器es自带的分词器对中文分词不是很敌对,所以咱们下载开源的 IK 分词器来解决这个问题。 上面的命令接上一步: cd pluginselasticsearch-plugin install https://ghproxy.com/https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.0/elasticsearch-analysis-ik-7.12.0.zip6 重启 esexitdocker restart es拜访 http://localhost:9200 ,胜利的话会失去相似以下响应: { "name": "0d096b662146", "cluster_name": "docker-cluster", "cluster_uuid": "KFeS1JjNTlqo56RmN8DiFw", "version": { "number": "7.12.0", "build_flavor": "default", "build_type": "docker", "build_hash": "78722783c38caa25a70982b5b042074cde5d3b3a", "build_date": "2021-03-18T06:17:15.410153305Z", "build_snapshot": false, "lucene_version": "8.8.0", "minimum_wire_compatibility_version": "6.8.0", "minimum_index_compatibility_version": "6.0.0-beta1" }, "tagline": "You Know, for Search"}二、Kibana1 pull拉取对应版本的 Kibana: ...

May 28, 2021 · 1 min · jiezi

关于golang:docker-配置本地-etcd-集群并使用-clientapiv3-管理集群

一、用 docker 搭建集群etcd 没有在 docker hub 中创立 image,所以天然拉取不到。 本文意在模仿应用步骤,所以创立三个 go 环境的容器,在每个容器中配置 etcd。 1 创立 go 容器hub 中有 golang 镜像,能够间接拉取: docker pull golang拉取到的镜像是基于 debian buster 制作。 创立三个容器: docker run -itd --name etcd1 golangdocker run -itd --name etcd2 golangdocker run -itd --name etcd3 golang2 在每个镜像中 clone etcddocker exec -it etcd1 bashdocker exec -it etcd2 bashdocker exec -it etcd3 bash在每个容器中克隆: git clone https://github.com/etcd-io/etcd.git3 编绎 etcd在每个容器中都须要执行上面的命令。 cd etcd./build.sh编绎脚本会拉取一些 golang 库,所以先设置好 goproxy 是十分有必要的。 go env -w GO111MODULE=ongo env -w GOPROXY=https://goproxy.cn,direct编绎实现后,会多一个 bin 目录,外面有两个可执行文件etcd和etcdctl,别离为服务端和客户端文件,搭建集群,应用的是etcd。 ...

May 28, 2021 · 8 min · jiezi

关于golang:Zookeeper-的-Golang-客户端

应用 docker 创立的三个 Zookeeper 服务端组成的集群,其 ip 地址别离为: 172.17.0.2172.17.0.3172.17.0.4一、增删改查1 增 / create创立新节点一共有四种: 长久节点长期节点长久时序节点长期时序节点代码: package mainimport ( ... "github.com/go-zookeeper/zk")func main() { conn, _, err := zk.Connect([]string{"172.17.0.2", "172.17.0.3", "172.17.0.4"}, time.Second) if err != nil { panic(err) } defer conn.Close() // 创立长久节点 path, err := conn.Create("/hello", []byte("world"), 0, zk.WorldACL(zk.PermAll)) if err != nil { panic(err) } println("Created", path) // 创立长期节点,创立此节点的会话完结后立刻革除此节点 ephemeral, err := conn.Create("/ephemeral", []byte("1"), zk.FlagEphemeral, zk.WorldACL(zk.PermAll)) if err != nil { panic(err) } println("Ephemeral node created:", ephemeral) // 创立长久时序节点 sequence, err := conn.Create("/sequence", []byte("1"), zk.FlagSequence, zk.WorldACL(zk.PermAll)) if err != nil { panic(err) } println("Sequence node created:", sequence) // 创立长期时序节点,创立此节点的会话完结后立刻革除此节点 ephemeralSequence, err := conn.Create("/ephemeralSequence", []byte("1"), zk.FlagEphemeral|zk.FlagSequence, zk.WorldACL(zk.PermAll)) if err != nil { panic(err) } println("Ephemeral-Sequence node created:", ephemeralSequence)}2 查 / get增完了节点,接下来当然是查看一下节点信息: ...

May 28, 2021 · 6 min · jiezi

关于golang:Golang-调度器和-GMP-模型

一、调度器的由来调度自身是指操作系统中为每个任务分配其所需资源的办法。 在操作系充中,线程是工作执行的最小单位,是系统调度的根本单元。 尽管线程比过程轻量,然而在调度时也有比拟大的额定开销,每个线程都会占用几 M 的内存,上下文切换时也会耗费几微秒的工夫,这些都是高并发的妨碍。 Go 语言的诞生有一个很重要的目标就是并发编程,所以开发者为Go语言设计了一个调度器来解决这些问题。 Go 的调度器通过应用与 CPU 数量相等的线程缩小线程频繁切换的内存和工夫开销,同时在每一个线程上执行额定开销更低的协程来升高资源占用,实现更高并发。 1.1 Go 调度器的历史单线程调度器最后的调度器极其简陋,只有 40 多行代码,程序只能存在一个沉闷线程,由 $G$ (goroutine) - $M$ (thread)组成。 多线程调度器退出了多线程后,能够运行多线程工作了,然而存在重大的同步竞争问题。 工作窃取调度器引入了处理器 $P$ (processor),形成了 GMP 模型。$P$ 是调度器的核心,任何线程想要运行 $G$ ,都必须服务 $P$ 的安顿,由 $P$ 实现工作窃取。如果 $G$ 产生阻塞,$G$ 并不会被动让出线程,导致其余 $G$ 饥饿。 另外,这一版的调度器的垃圾回收机制应用了比拟轻便的 $SWT$ (stop the world) 机制,在回收垃圾时,会暂停程序很长时间。 抢占式调度器(正在应用)抢占式调度器也倒退了两个阶段: 基于合作的抢占式调度器(1.12 ~ 1.13)。编译器在函数调用时查看以后goroutine是否发动抢占申请。也有 $STW$ 的问题。基于信号的抢占式调度器(1.14 至今)。垃圾回收在扫描栈时会触发抢占调度,但抢占的点不够多,不能笼罩全副的边缘状况。非平均存储拜访调度器(提案)对运行时的各种资源进行分区,但实现非常复杂,只停留在实践阶段。 1.2 单线程调度器最后的调度器只包含 $G$ 和 $M$ 两种构造,全局只有一个线程,所以零碎中只能一个工作一个工作地执行,一旦产生阻塞,整个后续工作都无奈执行。 单过程调度器的意义就是建设了 $G$ 、$M$ 构造,为后续的调度器倒退打下了根底。 1.3 多线程调度器在 1.0 版本中,Go 更换了多线程调度器,与单线程调度器相比,能够说是质的飞跃。 多线程调度器的整体逻辑与单线程调度器没有太大的区别,因为引入了多线程,所以 Go 应用环境变量GOMAXPROCS帮忙咱们灵便控制程序中的最大处理器数,即沉闷线程数。 ...

May 28, 2021 · 4 min · jiezi

关于golang:Go-的-sync-同步原语库

官网包的正文: // Package sync provides basic synchronization primitives such as mutual// exclusion locks. Other than the Once and WaitGroup types, most are intended// for use by low-level library routines. Higher-level synchronization is// better done via channels and communication.sync包提供根底的同步原语,sync.Mutext、sync.RWMutex、sync.WaitGroup、sync.Once和sync.Cond。 一、MutexGo 语言的sync.Mutex由两个字段state和sema组成。其中,state示意以后互斥锁的状态,sema是用来管制锁状态的信号量。 type Mutex struct { state int32 sema uint32}上述两个加起来只占 8 字节空间的构造体表过了 Go 语言中的互斥锁。 1 状态互斥锁的状态: const ( mutexLocked = 1 << iota // 锁定 mutexWoken // 唤醒 mutexStarving // 饥饿 ...)2 模式sync.Mutex有两种模式——失常模式和饥饿模式。 ...

May 28, 2021 · 6 min · jiezi

关于golang:如何优雅地实现并发编排任务

业务场景在做工作开发的时候,你们肯定会碰到以下场景: 场景1:调用第三方接口的时候, 一个需要你须要调用不同的接口,做数据组装。场景2:一个利用首页可能依靠于很多服务。那就波及到在加载页面时须要同时申请多个服务的接口。这一步往往是由后端对立调用组装数据再返回给前端,也就是所谓的 BFF(Backend For Frontend) 层。 针对以上两种场景,假如在没有强依赖关系下,抉择串行调用,那么总耗时即: time=s1+s2+....sn依照当代秒入百万的有为青年,这么长时间早就把你祖宗十八代问候了一遍。 为了平凡的KPI,咱们往往会抉择并发地调用这些依赖接口。那么总耗时就是: time=max(s1,s2,s3.....,sn)当然开始堆业务的时候能够先串行化,等到下面的人焦急的时候,亮出绝招。 这样,年底 PPT 就能够加上浓厚的一笔流水账:为业务某个接口进步百分之XXX性能,间接产生XXX价值。 当然这所有的前提是,做老板不懂技术,做技术”懂”你。 言归正传,如果批改成并发调用,你可能会这么写, package mainimport ( "fmt" "sync" "time")func main() { var wg sync.WaitGroup wg.Add(2) var userInfo *User var productList []Product go func() { defer wg.Done() userInfo, _ = getUser() }() go func() { defer wg.Done() productList, _ = getProductList() }() wg.Wait() fmt.Printf("用户信息:%+v\n", userInfo) fmt.Printf("商品信息:%+v\n", productList)}/********用户服务**********/type User struct { Name string Age uint8}func getUser() (*User, error) { time.Sleep(500 * time.Millisecond) var u User u.Name = "wuqinqiang" u.Age = 18 return &u, nil}/********商品服务**********/type Product struct { Title string Price uint32}func getProductList() ([]Product, error) { time.Sleep(400 * time.Millisecond) var list []Product list = append(list, Product{ Title: "SHib", Price: 10, }) return list, nil}先不论其余问题。从实现上来说,须要多少服务,你会开多少个 G,利用 sync.WaitGroup 的个性,实现并发编排工作的成果。 ...

May 27, 2021 · 4 min · jiezi

关于golang:什么无限缓冲的队列一

介绍事件的起因是前几周看到鸟窝写了一篇对于实现有限缓冲 channel 的文章,过后忙着和小姐姐聊天没看,明天想起来了。 不过这篇文章不会波及到鸟窝本人实现的 chanx,咱们会在下一篇提到。 咱们都晓得,channel 有两种类型:无缓冲和有缓冲的。 当咱们创立一个有缓冲的通道并指定了容量,那么在这个通道的生命周期内,咱们将再也无奈扭转它的容量。 有时候,咱们并不知道也无奈预估写入通道的数量规模。如果此时通道的写入速度远远超过读取速度,那么必然会在某个工夫点塞满通道,导致写入阻塞。比方之前我翻译的一篇文章 应用 Go 每分钟解决百万申请 中,作者就呈现处理速度太慢,导致通道塞满,其余申请被阻塞,响应工夫缓缓减少。 此时有人就会提到,能不能提供一个有限缓冲(Unbounded or Unlimited)的通道。 这个问题早在 2017 年就有人提过 issues,最终 go 官网没有实现这个提案。 不过,这个 issues 上面总共产生了 67 个 comments,评论很精彩。 比方有人提到: cznic:Unlimited capacity channels ask for a machine with unlimited memory.rsc:The limited capacity of channels is an important source of backpressure in a set of communicating goroutines. It is typically a mistake to use an unbounded channel, because you lose that backpressure. If one goroutine falls sufficiently behind, you usually want to take some action in response, not just queue its messages forever. The appropriate response varies by situation: maybe you want to drop messages, maybe you want to keep summary messages, maybe you want to take different responses as the goroutine falls further and further behind. Making it trivial to reach for unbounded channels keeps developers from thinking about this, which I believe is a strong disadvantage.那么如何实现一个有限缓冲的通道呢? ...

May 27, 2021 · 2 min · jiezi

关于golang:什么无限缓冲的队列二

chanx上篇文章咱们提到,当咱们创立一个有缓冲的通道并指定了容量,那么在这个通道的生命周期内,咱们将再也无奈扭转它的容量。 由此引发了对于有限缓存的 channel 话题探讨。咱们剖析了一个实现有限缓冲的代码。 最初,咱们也提到了它还能够持续优化的点。 鸟窝的 chanx 正是基于此计划革新而成的,咱们来看看他俩的不同之处。 上篇文章说过,所谓的有限缓冲,无非是借助一个中间层的数据结构,暂存长期数据。 在 chanx 中,构造是这样的: type UnboundedChan struct { In chan<- T // channel for write Out <-chan T // channel for read buffer *RingBuffer // buffer}in 和 out 的职责在上篇文章曾经阐明,这里的 buffer 就是咱们所谓的两头长期存储层。其中的 RingBuffer 构造咱们前面再说。 func NewUnboundedChan(initCapacity int) UnboundedChan { return NewUnboundedChanSize(initCapacity, initCapacity, initCapacity)}func NewUnboundedChanSize(initInCapacity, initOutCapacity, initBufCapacity int) UnboundedChan { in := make(chan T, initInCapacity) out := make(chan T, initOutCapacity) ch := UnboundedChan{In: in, Out: out, buffer: NewRingBuffer(initBufCapacity)} go process(in, out, ch) return ch}它提供了两个初始化 UnboundedChan 的办法,从代码中咱们能够显著的看出,NewUnboundedChanSize 能够给每个属性自定义本人的容量大小。仅此而已。 ...

May 27, 2021 · 2 min · jiezi

关于golang:GOGo安全指南

参考资料Go平安指南 在对slice进行操作时,必须判断长度是否非法,避免程序panic进行指针操作时,必须判断该指针是否为nil,避免程序panic,尤其在进行构造体Unmarshal时在进行make分配内存时,须要对外部可控的长度进行校验,避免程序panic反复开释个别存在于异样流程判断中,如果歹意攻击者结构出异样条件使程序反复开释channel,则会触发运行时恐慌,从而造成DoS攻打。启动一个协程就会做一个入栈操作,在零碎不退出的状况下,协程也没有设置退出条件,则相当于协程失去了管制,它占用的资源无奈回收,可能会导致内存泄露。slice是援用类型,在作为函数入参时采纳的是地址传递,对slice的批改也会影响原始数据在进行文件操作时,如果对外部传入的文件名未做限度,可能导致任意文件读取或者任意文件写入,重大可能导致代码执行依据创立文件的敏感性设置不同级别的拜访权限,以避免敏感数据被任意权限用户读取。例如,设置文件权限为:-rw-r-----

May 27, 2021 · 1 min · jiezi

关于golang:数据库SQL-server数据实时同步到mysql2

golang代码实现1. 目录构造.├── config //配置文件│   ├── config.go│   ├── config_test.go│   └── dev_custom.yml├── dao // mysql数据库操作│   ├── mysql.go // 初始化mysql│   ├── record.go // 记录表操作│   ├── record_test.go│   ├── users.go │   └── users_test.go├── go.mod├── go.sum├── logic // 业务逻辑│   ├── logic.go│   └── logic_test.go├── main.go // 入口函数├── models // 数据库字段模型│   ├── casbin_rule.go│   ├── record.go│   ├── reverse // xorm数据库主动生成表构造工具│   │   ├── README.txt│   │   ├── custom.yml│   │   └── gennerate.sh // 可执行文件│   ├── user.go│   └── users.go├── sqldao // sql server数据库操作│   ├── connect_test.go │   ├── gorm_test.go│   ├── sql.go // 初始化sql│   ├── users.go // users表操作│   └── users_test.go└── sqlserver.exe2.各个文件内容2.1 dev_custom.yml ...

May 26, 2021 · 2 min · jiezi

关于golang:GO文件操作

1. 获取以后文件门路 import ( "errors" "runtime") func CurrentFile() string { _, file, _, ok := runtime.Caller(1) if !ok { panic(errors.New("Can not get current file info")) } return file}2.获取可执行文件目录 path, err := os.Getwd()if err != nil { panic(err) return}3. 获取以后文件所在目录 func CurrentFileDir() string { _, file, _, ok := runtime.Caller(1) if !ok { return "失败" } i := strings.LastIndex(file, "/") // linux环境 if i < 0 { i = strings.LastIndex(file, "\\") // windows环境 } return string(file[0 : i+1])}

May 26, 2021 · 1 min · jiezi

关于golang:GO解析yaml文件

参考资料Golang应用第三方包viper读取yaml配置信息 1. 书写dev_custom.yml文件内容 mySql: dns: "sqlserver://sa:123456@DESKTOP-HMTA87I:1433?database=wzz"sqlServer: dns: "me:123abc@tcp(10.0.0.0:3306)/wzz_db?charset=utf8"2. 解析dev_custom.yml文件内容 package configimport ( "github.com/mitchellh/mapstructure" "github.com/spf13/viper" "log" "os")// CustomT CustomTtype Mysql struct { DNS string `yaml:"dns"`}type SqlServer struct { DNS string `yaml:"dns"`}// CustomT CustomTtype CustomT struct { Mysql Mysql `yaml:"mySql"` SqlServer SqlServer `yaml:"sqlServer"`}// Custom Customvar Custom CustomT// ReadConfig ReadConfig for customfunc ReadConfig(configName string, configPath string, configType string) *viper.Viper { v := viper.New() v.SetConfigName(configName) v.AddConfigPath(configPath) v.SetConfigType(configType) err := v.ReadInConfig() if err != nil { return nil } res := v.AllKeys() log.Println("res=", res) return v}// InitConfig InitConfigfunc InitConfig() (err error) { path, err := os.Getwd() if err != nil { return err } v := ReadConfig("dev_custom", path, "yml") md := mapstructure.Metadata{} err = v.Unmarshal(&Custom, func(config *mapstructure.DecoderConfig) { config.TagName = "yaml" config.Metadata = &md }) return err}3. 测试 ...

May 26, 2021 · 1 min · jiezi