关于golang:MONGDB-安装与使用

MONGDB 装置与应用咱们来回顾一下上次分享的内容: 如何应用log 包log 包原理和具体实现自定义日志要是对 GO 的日志包还有点趣味的话,能够查看文章 GO的日志怎么玩 ? 明天咱们来玩个简略的 mongodb 的装置和应用 MONGODB介绍MongoDB 是一个基于分布式文件存储的数据库 应用 C++ 语言编写 MongoDB 次要是 为WEB利用提供可扩大的高性能数据存储解决方案 是非关系数据库当中性能最丰盛,它反对的数据结构十分涣散,是相似 JSON 的 BSON 格局的 MONGODB 的语法有点相似于面向对象的查询语言,咱们用起来也是很简略的 mongodb 装置下载mongodb安装包网址:https://www.mongodb.com/try/d... 官网上能够下载多个零碎的mongdb安装包,windows,centos,ubuntu等等,如图 依据页面上的内容,咱们能够抉择不同版本的,按需索取,默认咱们就做吃螃蟹的人吧,间接上最新的 装置MONGODB软件解压mongodb 压缩包tar xvf mongodb-linux-x86_64-rhel80-4.4.4.tgz将解压生成的目录 改名为mongodb ,并挪动到 /usr/local下mv mongodb-linux-x86_64-rhel80-4.4.4.tgz mongodbmv mongodb /usr/local/进入mongodb目录下创立data目录data目录下创立db目录data目录下创立log目录mkdir datacd datamkdir dbmkdir log回到mongo目录下,创立mongodb的配置文件,mongodb.conf,之后启动须要用到须要在mongodb.conf下配置 dbpath数据库的数据寄存地位 logpath日志文件寄存地位 logappend日志是否以追加的形式 port端口号 auth是否须要认证 fork是否以守护过程的形式运行 bind 限度近程拜访的ip,不做限度的话能够写0.0.0.0 dbpath=/usr/local/mongodb/data/dblogpath=/usr/local/mongodb/data/log/mongodb.loglogappend=trueport=27017auth=truefork=truebind_ip=0.0.0.0将mongodb可执行程序写入到/etc/profile 中,开机时会主动读取这个文件export MONGODB_HOME=/usr/local/mongodbexport PATH=$PATH:$MONGODB_HOME/bin启动mongodb服务mongod -f /usr/local/mongodb/mongodb.conf对于防火墙能够间接关闭系统的防火墙平时防火墙关上端口27017端口,以便于客户端进行连贯mongdb对于MONGODB的状态查看查问mongodb状态ps aux |grep mongodb查看端口netstat -antp |grep 27017敞开mongodb有如下两种操作,随你选,喜爱啥用啥 ...

September 12, 2021 · 2 min · jiezi

关于golang:Golang-启动Go-协程的使用技巧

// Go goruntine 调用的办法,防止开启的go 协程异样,导致退出func Go(x func()) { go func() { defer func() { if err := recover(); err != nil { fmt.Println(err) } }() x() }()}func GoParams(x func(interface{}, interface{}), a1, a2 interface{}) { go func() { defer func() { if err := recover(); err != nil { fmt.Println(err) } }() x(a1, a2) }()}Go中 函数的写法大全//写法1,一般写法(举荐)func div1(a, b int) (int, int) { return a / b, a % b // 返回多个值,此处返回两个数的商和余数}//写法2,为返回值定义名称,此种形式 仅用于逻辑非常简单的函数// 多返回值,不能随便应用,个别用法是 第一个为业务返回值,第二个为 错误信息func div2(a, b int) (q, r int) { q = a / b r = a % b return}//写法3,函数式编程,能够将函数的输出参数定义为函数func apply(op func(int, int) int, a, b int) int { //获取函数对应的指针 p := reflect.ValueOf(op).Pointer() opName := runtime.FuncForPC(p).Name() fmt.Printf("Calling function %s with args (%d,%d)", opName, a, b) fmt.Println() return op(a, b)}//写法4,可变参数列表func sum(numbers ...int) int { s := 0 for i := range numbers { s += numbers[i] } return s}//写法5,值传递与 指针传递//Go 中所有的 参数传递 都是 值传递,然而增加一些操作能够批改为指针传递// * 代表指针传递func swap(a, b *int) { *a, *b = *b, *a}////定义一个匿名函数并间接调用// func(){// //函数体// }()

September 12, 2021 · 1 min · jiezi

关于golang:一次带宽拉满引发的百分百超时血案

来自公众号:Gopher指北偈语: 未经别人苦,莫劝别人善 酣战两周无余,为了排查线上某接口百分百超时的起因,现在总算有些成绩。尽管仍有疑虑然而碍于工夫不容许和集体能力问题先做如下总结以备来日再战。 进口带宽拉满可能发现这个问题实属幸运。依稀记得这是一个风雨交加的夜晚,这风、这雨注定了今夜的不平庸。果然线上百分百超时的根因被发现了! 咱们的线上接口须要对外申请,而咱们的流出带宽被拉满天然耗时就长因而导致超时。当然这都是后果,毕竟两头过程的艰苦曾经远远超出老许的文字所能形容的范畴。 反思后果有了,该有的反思仍旧不能少。比方流出带宽被拉满为什么没有提前预警!无论是自信带宽足够还是经验不足都值得老许记上一笔。 而在带宽问题被真正发现之前,老许心田对带宽其实已有所狐疑,然而却没有认真进行验证,只听信了别人的揣测导致发现问题的工夫被推延。 httptrace有时候不得不吹一波Go对http trace的良好反对。老许也是基于此做了一个demo,该demo能够打印http申请各阶段耗时。 上述为一次http申请各阶段耗时输入,有趣味的可去https://github.com/Isites/go-...拿到源码。 老许对带宽的狐疑次要就是基于此demo中的源码进行线上分析测试给到的揣测。框架问题本局部更加适宜腾讯系的兄弟们去浏览,其余非腾讯系技术能够间接跳过。 我司的框架为TarsGo,咱们在线上设置handletimeout为1500ms,该参数次要用于管制某一接口总耗时不超过1500ms,而咱们的超时告警均为3s,因而即便带宽已满这个百分百超时告警也不应呈现。 为了钻研这个起因,老许只好花些系统的工夫去浏览源码,最终发现了TarsGo@v1.1.6的handletimeout管制是有效的。 上面看一下有问题的源码: func (s *TarsProtocol) InvokeTimeout(pkg []byte) []byte { rspPackage := requestf.ResponsePacket{} rspPackage.IRet = 1 rspPackage.SResultDesc = "server invoke timeout" return s.rsp2Byte(&rspPackage)}当某接口总执行工夫超过handletimeout时,会调用InvokeTimeout办法告知client调用超时,而上述的逻辑中疏忽了IRequestId的响应,这就导致client收到响应包时无奈将响应包和某次的申请对应起来,从而导致客户端始终期待响应直至超时。 最终批改如下: func (s *TarsProtocol) InvokeTimeout(pkg []byte) []byte { rspPackage := requestf.ResponsePacket{} // invoketimeout need to return IRequestId reqPackage := requestf.RequestPacket{} is := codec.NewReader(pkg[4:]) reqPackage.ReadFrom(is) rspPackage.IRequestId = reqPackage.IRequestId rspPackage.IRet = 1 rspPackage.SResultDesc = "server invoke timeout" return s.rsp2Byte(&rspPackage)}起初老许在本地用demo验证handletimeout终于能够管制失效。当然本次批改老许曾经在github下面提交issue和pr,目前已被合入master。相干issue和pr如下: ...

September 10, 2021 · 1 min · jiezi

关于golang:Go-专栏|函数那些事

原文链接: Go 专栏|函数那些事 已经很长一段时间,我都为本人是互联网科技公司的一员而感到骄傲,我感觉咱们不同凡响。 咱们的治理更扁平化,没有那么多官僚主义,充满活力,暮气沉沉。而且咱们的产品正在扭转大家的衣食住行,咱们正在扭转世界。 但近几年产生的一系列事件,都让我的信念产生波动,不停在捶打我:醒醒吧,兄弟,事实不是你设想的那样。 我能做些什么呢?不晓得。 还是致力更文吧,争取早日不做打工人。 函数定义函数包含以下几个局部:关键词 func,函数名,参数列表,返回列表和函数体。 func name(param-list) ret-list { body}函数能够没有参数,也能够没有返回值。 func funcA() { fmt.Println("i am funcA") // i am funcA}函数的类型称作函数签名,当两个函数的参数列表和返回列表雷同时,则两个函数的类型或签名就雷同。 func add(x int, y int) int { return x + y}func sub(x int, y int) (z int) { z = x - y return}fmt.Printf("%T\n", add) // func(int, int) intfmt.Printf("%T\n", sub) // func(int, int) int参数多个相邻类型的参数能够应用简写模式,所以方才的 add 和 sub 函数还能够这样写: func add(x, y int) int { return x + y}func sub(x, y int) (z int) { z = x - y return}反对不定参数,应用 ...type 语法。留神不定参数必须是函数的最初一个参数。 ...

September 8, 2021 · 2 min · jiezi

关于golang:Go-专栏|流程控制一网打尽

原文链接: Go 专栏|流程管制,一网打尽 最近看奥运会看的我热血沸腾,中国奥运健儿几乎太棒了,不只是问题,还有气质,精气神,全方位的棒。 而且这次奥运会我感觉最打动的是,看到一些年纪大的运动员拿了好问题:吕小军 37 岁,马龙 32,苏炳添 32,巩立姣 32 岁才拿了本人的第一块奥运金牌。连这么受限于年龄的运动员都能一直冲破本人,何况咱们呢?还每天在网上焦虑程序员 35 岁就要被优化? 所以别给本人找年龄作为借口了,感觉年龄大了这不行那不行,干就完事了。 if-else特点: 条件语句不须要应用小括号 () 包起来;花括号 {} 必须有,并且左花括号 { 必须和 if 或 else 在同一行;在 if 之后,条件语句之前能够增加变量初始化语句,应用 ; 分隔。package mainimport "fmt"func main() { if 7%2 == 0 { fmt.Println("7 is even") } else { fmt.Println("7 is odd") // 7 is odd } if 8%4 == 0 { fmt.Println("8 is divisible by 4") // 8 is divisible by 4 } if num := 9; num < 0 { fmt.Println(num, "is negative") } else if num < 10 { fmt.Println(num, "has 1 digit") // 9 has 1 digit } else { fmt.Println(num, "has multiple digits") }}switch特点: ...

September 8, 2021 · 3 min · jiezi

关于golang:Go-专栏|基础数据类型整数浮点数复数布尔值和字符串

原文链接: Go 专栏|根底数据类型:整数、浮点数、复数、布尔值和字符串 Go 专栏的第三篇,本文内容仍旧很根底,很简略。如果有编程教训的话,可能扫一眼就了然于胸了。但如果刚开始接触编程,倡议还是好好看看,把文中的代码 demo 都本人跑一遍。只有根底打好了,能力向更高的指标迈进。 话不多说,走起~ 本文所有代码基于 go1.16.6 编写。Go 的数据类型分四大类: 根底类型: 数字 number,字符串 string 和布尔型 boolean。聚合类型: 数组 array 和构造体 struct。援用类型: 指针 pointer,切片 slice,字典 map,函数 func 和通道 channel。接口类型: 接口 interface。其中,根底类型又分为: 整型: int8、uint8、byte、int16、uint16、int32、uint32、int64、uint64、int、uint、uintptr。浮点型: float32,float64。复数类型: complex64、complex128。布尔型: bool。字符串: string。字符型: rune。整数整数一共有 12 种类型,分为有符号整数和无符号整数,为了不便查看,我在这里整顿了一个表格: 类型长度(字节)范畴int81-128 ~ 127uint810~255byte10~255int162-32768~32767uint1620~65535int324-2147483648~2147483647uint3240~4294967295int648-9223372036854775808~9223372036854775807uint6480~18446744073709551615int4/8同上uint4/8同上uintptr4/8同上,足以存储指针的 uint个别咱们在开发的时候,应用 int 和 uint 即可。除非有明确须要指定长度的需要,才用 int8 这种类型。 类型转换不论是算术运算,还是逻辑运算,Go 要求操作数的类型必须统一,如果不统一的话,会报错。 var a int = 10var b int32 = 20// fmt.Println(a + b) // 报错 invalid operation: a + b (mismatched types int and int32)这一点在写代码的时候,开发工具就会揭示了,而且还会把强制类型转换的代码给写好,还是很棒的。 ...

September 7, 2021 · 3 min · jiezi

关于golang:Go-专栏|变量和常量的声明与赋值

原文链接: Go 专栏|变量和常量的申明与赋值 上篇文章介绍了环境搭建,并实现了学习 Go 的第一个程序 Hello World。这篇文章持续学习 Go 的基础知识,来看看变量,常量的申明与赋值。 本文所有代码基于 go1.16.6 编写。变量Go 编程的命名格调更习惯应用「短名称」和「驼峰式」的名称,而且大小写敏感。 结尾必须是字母或者下划线,而且首字母是大写还是小写也是有非凡含意的。大写字母结尾能够被包外援用,小写字母结尾只能在包内应用,这个会在当前的文章中持续分享。 申明第一种应用关键字 var 申明变量: var name type = expression和 C 语言正好相同,类型是跟在变量名前面的。说实话,刚开始写 Go 代码的时候还真有些不习惯。 类型和表达式能够省略一个,但不能都省略。如果类型省略,则类型由初始化表达式决定;如果表达式省略,则初始化值为对应类型的零值。 对于数字是 0,布尔值是 false,字符串是 "",接口和援用(slice,指针,map,通道,函数)是 nil,对于数组或构造体这样的复合类型,零值是其所有元素或成员的零值。 // 没有初始值,会赋默认零值var v1 intvar v2 stringvar v3 boolvar v4 [10]int // 数组var v5 []int // 数组切片var v6 struct { e int}var v7 *int // 指针var v8 map[string]int // map,key 为 string 类型,value 为 int 类型var v9 func(e int) intfmt.Println(v1, v2, v3, v4, v5, v6, v7, v8, v9)// 输入// 0 false [0 0 0 0 0 0 0 0 0 0] [] {0} <nil> map[] <nil>所以在 Go 中是不存在未初始化的变量的。 ...

September 7, 2021 · 3 min · jiezi

关于golang:Go-专栏|开发环境搭建以及开发工具-VS-Code-配置

原文链接: Go 专栏|开发环境搭建以及开发工具 VS Code 配置 Go 专栏的第一篇,想学 Go 的同学们,走起~ Go 装置我的个人电脑是 Mac,而后工作次要应用 Linux,所以在这里次要介绍在这两个零碎下的装置。 下载地址: Go 官网下载地址:https://golang.org/dl/Go 官网镜像站(举荐):https://golang.google.cn/dl/ 间接装置最新版本 go1.16.6,后续文章都会在此版本下开发,测试。 Mac 下装置能够通过 brew 形式装置,也能够间接在官网下载可执行文件,而后双击安装包,不停下一步就能够了。 Linux 下装置下载安装包: $ wget https://golang.google.cn/dl/go1.16.6.linux-amd64.tar.gz解压到 /usr/local 目录: $ sudo tar -zxvf go1.16.6.linux-amd64.tar.gz -C /usr/local而后配置环境变量,关上 $HOME/.bash_profile 文件,减少上面两行代码: export GOROOT=/usr/local/goexport PATH=$PATH:$GOROOT/bin最初使环境变量失效: $ source $HOME/.bash_profile装置实现后,在终端执行查看版本命令,如果能正确输入版本信息,那就阐明装置胜利了。 $ go versiongo version go1.16.6 linux/amd64配置环境变量GOROOT 和 GOPATH 都是环境变量,其中 GOROOT 是咱们装置 Go 开发包的门路,GOPATH 会有一个默认目录。 因为 go1.11 之后应用 go mod 来治理依赖包,不再强制咱们必须把代码写在 GOPATH/src 目录下,所以应用默认即可,无需批改。 ...

September 7, 2021 · 1 min · jiezi

关于golang:推荐三个实用的-Go-开发工具

原文链接: 举荐三个实用的 Go 开发工具 孙悟空在花果山称王的时候,特意去了一趟东海,在那里淘到了如意金箍棒。因为身为一个山大王,怎么能没有一件趁手的兵器呢? 作为程序员的咱们也一样,除了咱们的傍身武器 Ctrl C + V 之外,还要不停的补充咱们的武器库。不仅要把 Ctrl C + V 用的高级,更要用的恰到好处。 明天介绍三款小工具,别离能够将 json,yaml 和 table 转成 Go 的 struct。下次再碰到这样的转换场景,再也不必皱眉挠头了,一键轻松搞定。 前两个间接在线转换,最初一个须要装置一个库,但也很不便。 json-to-go地址: https://mholt.github.io/json-... 输出: [ { "input_index": 0, "candidate_index": 0, "delivery_line_1": "1 N Rosedale St", "components": { "primary_number": "1", "street_predirection": "N", "street_name": "Rosedale", "street_suffix": "St", "city_name": "Baltimore", "state_abbreviation": "MD" } }]输入: type AutoGenerated []struct { InputIndex int `json:"input_index"` CandidateIndex int `json:"candidate_index"` DeliveryLine1 string `json:"delivery_line_1"` Components struct { PrimaryNumber string `json:"primary_number"` StreetPredirection string `json:"street_predirection"` StreetName string `json:"street_name"` StreetSuffix string `json:"street_suffix"` CityName string `json:"city_name"` StateAbbreviation string `json:"state_abbreviation"` } `json:"components"`}yaml-to-go地址: https://zhwt.github.io/yaml-t... ...

September 7, 2021 · 2 min · jiezi

关于golang:Go区块链开发手把手教你导入Go语言第三方库

作者:ReganYue 起源:恒生LIGHT云社区 一、应用 go get获取GO的命令 go get让咱们能够方便快捷的从网络中下载或更新Go语言包及其依赖文件,并将他们编译和装置。 先在命令行模式下输出go --help,可查看以下信息。Go is a tool for managing Go source code.Usage:       go <command> [arguments]The commands are:       bug         start a bug report       build       compile packages and dependencies       clean       remove object files and cached files       doc         show documentation for package or symbol       env         print Go environment information       fix         update packages to use new APIs       fmt         gofmt (reformat) package sources       generate   generate Go files by processing source       get         add dependencies to current module and install them       install     compile and install packages and dependencies       list       list packages or modules       mod         module maintenance       run         compile and run Go program       test       test packages       tool       run specified go tool       version     print Go version       vet         report likely mistakes in packagesUse "go help <command>" for more information about a command.Additional help topics:       buildconstraint build constraints       buildmode       build modes       c               calling between Go and C       cache           build and test caching       environment     environment variables       filetype       file types       go.mod         the go.mod file       gopath         GOPATH environment variable       gopath-get     legacy GOPATH go get       goproxy         module proxy protocol       importpath     import path syntax       modules         modules, module versions, and more       module-get     module-aware go get       module-auth     module authentication using go.sum       module-private module configuration for non-public modules       packages       package lists and patterns       testflag       testing flags       testfunc       testing functionsUse "go help <topic>" for more information about that topic.咱们能够看到 get add dependencies to current module and install them ...

September 7, 2021 · 4 min · jiezi

关于golang:微服务架构下的熔断框架hystrixgo

原文链接:# 微服务架构下的熔断框架:hystrix-go 背景随同着微服务架构被宣传得如火如茶,一些概念也被推到了咱们的背后。一提到微服务,就离不开这几个字:高内聚低耦合;微服务的架构设计最终目标也就是实现这几个字。在微服务架构中,微服务就是实现一个繁多的业务性能,每个微服务能够独立演进,一个利用可能会有多个微服务组成,微服务之间的数据交能够通过近程调用来实现,这样在一个微服务架构下就会造成这样的依赖关系: 微服务A调用微服务C、D,微服务B又依赖微服务B、E,微服务D依赖于服务F,这只是一个简略的小例子,理论业务中服务之间的依赖关系比这还简单,这样在调用链路上如果某个微服务的调用响应工夫过长或者不可用,那么对上游服务(按调用关系命名)的调用就会占用越来越多的系统资源,进而引起零碎解体,这就是微服务的雪蹦效应。 为了解决微服务的雪蹦效应,提出来应用熔断机制为微服务链路提供爱护机制。熔断机制大家应该都不生疏,电路的中保险丝就是一种熔断机制,在微服务中的熔断机制是什么样的呢? 当链路中的某个微服务不可用或者响应的工夫太长时,会进行服务的降级,进而熔断该节点微服务的调用,疾速返回谬误的响应信息,当检测到该节点微服务调用响应失常后,复原调用链路。本文咱们就介绍一个开源熔断框架:hystrix-go。 熔断框架(hystrix-go)Hystrix是一个提早和容错库,旨在隔离对近程零碎、服务和第三方服务的拜访点,进行级联故障并在故障不可避免的简单分布式系统中实现弹性。hystrix-go 旨在容许 Go 程序员轻松构建具备与基于 Java 的 Hystrix 库相似的执行语义的应用程序。所以本文就从应用开始到源码剖析一下hystrix-go。 疾速装置go get -u github.com/afex/hystrix-go/hystrix疾速应用hystrix-go真的是开箱即用,应用还是比较简单的,次要分为两个步骤: 配置熔断规定,否则将应用默认配置。能够调用的办法func Configure(cmds map[string]CommandConfig) func ConfigureCommand(name string, config CommandConfig)Configure办法外部也是调用的ConfigureCommand办法,就是传参数不一样,依据本人的代码格调抉择。 定义依赖于内部零碎的利用程序逻辑 - runFunc 和服务中断期间执行的逻辑代码 - fallbackFunc,能够调用的办法:func Go(name string, run runFunc, fallback fallbackFunc) // 外部调用Goc办法func GoC(ctx context.Context, name string, run runFuncC, fallback fallbackFuncC) func Do(name string, run runFunc, fallback fallbackFunc) // 外部调用的是Doc办法func DoC(ctx context.Context, name string, run runFuncC, fallback fallbackFuncC) // 外部调用Goc办法,解决了异步过程Go和Do的区别在于异步还是同步,Do办法在调用Doc办法内解决了异步过程,他们最终都是调用的Goc办法。前面咱们进行剖析。 举一个例子:咱们在Gin框架上加一个接口级的熔断中间件 ...

September 6, 2021 · 6 min · jiezi

关于golang:读书笔记深入操作系统虚拟内存上

为什么须要虚拟内存运行一个程序,须要将程序装入内存中,程序中拜访的内存地址就是物理地址。而且必须保障程序用到的内存总量小于理论物理机的总量。当运行多个程序的时候,须要为程序调配多个内存某台计算机总的内存大小是128M,当初同时运行两个程序A和B,A需占用内存10M,B需占用内存110。计算机在给程序分配内存时会采取这样的办法:先将内存中的前10M调配给程序A,接着再从内存中残余的118M中划分出110M调配给程序B。这种调配办法能够保障程序A和程序B都能运行,然而这种简略的内存调配策略问题很多存在的问题如下: 过程地址空间不隔离,A过程能够随便拜访B过程的数据,也能随便批改B过程的数据内存使用率低,如果又创立了C过程,并且C大于操作系统残余的8MB内存,此时须要在A,B过程中抉择将一个程序临时拷入磁盘,腾出空间供C应用程序运行地址不确定,调配的地址是随机的虚拟内存操作系统应用了分页(page)的形式,将零碎内存按page的形式调配虚拟内存,个别的操作系统目前每个page是4kb,按这种抉择,4GB虚拟地址空间共能够分成1048576个页,512M的物理内存能够分为131072个页。显然虚拟空间的页数要比物理空间的页数多得多。分页的思维是程序运行时用到哪页就为哪页分配内存,没用到的页临时保留在硬盘上。当用到这些页时再在物理地址空间中为这些页分配内存,而后建设虚拟地址空间中的页和刚调配的物理内存页间的映射。 分页思维的根本了解上面通过介绍一个可执行文件的装载过程来阐明分页机制的实现办法。一个可执行文件(PE文件)其实就是一些编译链接好的数据和指令的汇合,它也会被分成很多页,在PE文件执行的过程中,它往内存中装载的单位就是页。当一个PE文件被执行时,操作系统会先为该程序创立一个4GB的过程虚拟地址空间。后面介绍过,虚拟地址空间只是一个中间层而已,它的性能是利用一种映射机制将虚拟地址空间映射到物理地址空间,所以,创立4GB虚拟地址空间其实并不是要真的创立空间,只是要创立那种映射机制所须要的数据结构而已,这种数据结构就是页目和页表。 当创立完虚拟地址空间所须要的数据结构后,过程开始读取PE文件的第一页。在PE文件的第一页蕴含了PE文件头和段表等信息,过程依据文件头和段表等信息,将PE文件中所有的段一一映射到虚拟地址空间中相应的页(PE文件中的段的长度都是页长的整数倍)。这时PE文件的真正指令和数据还没有被装入内存中,操作系统只是依据PE文件的头部等信息建设了PE文件和过程虚拟地址空间中页的映射关系而已。当CPU要拜访程序中用到的某个虚拟地址时,当CPU发现该地址并没有相相关联的物理地址时,CPU认为该虚拟地址所在的页面是个空页面,CPU会认为这是个页谬误(Page Fault),CPU也就晓得了操作系统还未给该PE页面分配内存,CPU会将控制权交还给操作系统。操作系统于是为该PE页面在物理空间中调配一个页面,而后再将这个物理页面与虚拟空间中的虚构页面映射起来,而后将控制权再还给过程,过程从方才产生页谬误的地位从新开始执行。因为此时已为PE文件的那个页面调配了内存,所以就不会产生页谬误了。随着程序的执行,页谬误会一直地产生,操作系统也会为过程调配相应的物理页面来满足过程执行的需要。 分页办法的核心思想就是当可执行文件执行到第x页时,就为第x页调配一个内存页y,而后再将这个内存页增加到过程虚拟地址空间的映射表中,这个映射表就相当于一个y=f(x)函数。应用程序通过这个映射表就能够拜访到x页关联的y页了 虚拟内存的实现虚拟内存被组织为一个由寄存在磁盘上的N个间断的字节大小的单元组成的数组.每个字节都有惟一的虚拟地址,作为到数组的索引 在任意工夫,虚构页面的汇合都分为三个不相交的子集 未调配的缓存的未缓存的页表(page table)页面将虚构页映射到物理页.每次地址翻译硬件将一个虚拟地址转化为物理地址时,都会读取一个页表 图中的PTE((Page Table Entry,PTE)页面标目数组,每个PTE由一个无效位(valid bit)和一个地址组成,无效位表明了该虚构页以后是否存在于物理内存中,如果无效位是1,该PTE中就会存储物理内存中相应的物理页的起始地址。如果无效位是0,且PTE中的地址为null,这示意这个虚构页还未被调配,而如果无效位是0且PTE中有地址,那么这个地址指向该虚构页在磁盘上的起始地位 页命中和页异样 当读取PTE2,因为设置了无效位,地址翻译就晓得pv2是缓存再内存中的,即是页命中如果读出PTE3,从内存中取出PTE3,从无效位推断并未被缓存,并且不在内存中,触发一个页异样,缺页异样会调用内核中的缺页异样处理程序,就义一个页.如果vp4曾经被批改,那么内核将它复制到磁盘。不再缓存到主存中.从磁盘复制VP3到内存PP3,更新PET3,随后返回局部性原理:如果不命中的话,始终都会触发页异样,页面的调度会相当慢.局部性原理保障了任意时刻,程序将趋势一个较小的汇合工作虚拟内存的作用简化链接,独立的过程空间容许每个过程的内存映像应用雷同的根本格局,而不必管代码和数据寄存在内存的何处简化加载,虚拟内存使得更容易向内存中加载可执行文件和共享对象文件简化共享简化内存调配 为用户过程提供一个简略的调配额定内存的机制.当一个过程须要额定的堆空间时候,操作系统调配一个适当k个间断虚拟内存页面,并且将他们映射到物理内存中的k个工作物理页面爱护机制通过在PTE下面增加一个额定的标识位来达到爱护权限 TLBMMU: 将虚拟地址翻译为物理地址,由硬件实现每次MMU必须查阅一个PTE,以便在虚拟地址转化为物理地址.因为在MMU中蕴含了一个对于PTE的缓存 多级页表1.相似树的构造,应用多级构造 以上就是虚拟内存的基础知识参考文章 https://zhuanlan.zhihu.com/p/...https://www.cnblogs.com/logo-...参考视频 CMU传授的视频教程 - Lecture17:虚拟内存概念CMU传授的视频教程 - Lecture18:虚拟内存零碎

September 5, 2021 · 1 min · jiezi

关于golang:业务学习简述ID生成器

引言大家好,好久不见,时隔一年终于又拾起了写博客这件事。在咱们日常工作中,咱们常须要用全局惟一ID作为数据库主键,或者用于生成订单id,用于生成商品ID等等。本篇次要介绍咱们常见的ID生成器的形式:利用数据库生成和雪花算法。 利用数据库生成ID自增ID达成目标利用MYSQL自增主键的个性来结构ID生成器。首先生成一张ID生成器表,每次咱们须要生成ID的时候在这个表里插入一行记录,获取到这行记录生成的主键id,就能够拿到一个全局惟一id,根本满足需要。 | idctime 12021-09-05 12:00:0022021-09-05 12:01:00尝试进阶那么有的盆友们会说了,这个办法必定会影响性能啊,每次申请都须要去连贯DB获取id,伤不起啊。没关系,咱们优化一下。咱们采纳分段申请,即每次申请一段ID(例如20个ID)缓存到本地,这样就只须要等本地ID应用完了再去申请了,减少了性能。到这又有敌人说了,每次批量插入数据性能还是不行,而且在多业务方同时应用的时候性能更差,这怎么办呢?别着急,往下看。咱们能够将表设计成相似下表这个构造。 biz_tag(业务线)max_idstep(每次申请ID数)update_timeapp100010002021-09-05 12:00:00pc200010002021-09-05 12:00:00从上表咱们能够看出,每次咱们申请ID的时候的大略流程是这样的:获取到以后的max_id -> 拿业务线标识申请(max_id,max_id + step]之间的id -> id应用完 -> 从新发动流程。 这样DB的压力就会小很多,然而呢,在生产环境中也会存在一些问题:例如: - 零碎性能依赖DB的更新,如果更新DB呈现尖刺,服务性能将收到影响- 强依赖DB的可用性,DB一旦呈现宕机将整个不可用对于以上两个问题,咱们有以下解决思路: 预处理,当本地队列应用百分之七十的时候就去申请后新的ID(比例依据业务需要设置)在本地缓存一个队列作为“备胎”,当服务不可用且失常队列用完时能够应用“备胎”进行工作,并且一直的去申请新的队列。如果咱们服务的会呈现突增流量的状况,咱们也能够动静的调整每次申请的id数,设定适配业务的算法去调整这个step,具体的算法此处不再赘述。 当然,业界应用数据库去生成DB的形式有很多,在如何保障可用性上做了很多的优化,因为这种形式最终须要强依赖DB 雪花算法它给每台机器调配一个惟一标识,而后通过工夫戳+标识+自增实现全局惟一ID。这种形式益处在于ID生成算法齐全是一个无状态机,无网络调用,高效牢靠。这种办法也是咱们业务中所常见的形式,上面咱们来看看咱们采纳的的52位和64位的ID怎么生成。 64bit42b timestamp + 8b counter + 8b countspace + 6b serverid timestamp:毫秒级工夫戳counter: 毫秒内的自增counter,取值[0, 255]countspace:标识counterspace,同一个业务方下可能有不同的counterspace,此6bit不同serverid:服务器id,寰球惟一52bit32bit timestamp + 16bit counter + 4bit server_id timestamp:秒级工夫戳counter: 秒级自增counterserverid:服务器id,寰球惟一通过以上两种规定即可实现分布式ID的生成,当然,业界还有很多种不同的规定,然而都是一个情理,大家能够依照本人的需要去解决。 关注咱们欢送对本系列文章感兴趣的读者订阅咱们的公众号,关注博主下次不迷路~

September 5, 2021 · 1 min · jiezi

关于golang:为开源项目-goginapi-增加后台任务模块

工作治理界面 (WEB) 反对在 WEB 界面 中对工作进行治理,例如:新增工作、编辑工作、启用/禁用工作、手动执行工作 等。 工作的属性包含: 工作名称执行形式 SHELLHTTP表达式(/5 *)命令超时工夫(秒)重试次数重试距离(秒)执行完结是否告诉 不告诉失败告诉完结告诉后果关键字匹配告诉状态备注当执行形式为 HTTP 时,反对抉择申请形式 GET 或 POST; 当设置执行完结告诉时,反对抉择告诉形式 邮件 或 Webhook; 当设置邮件告诉时,反对输出邮箱地址多个用,宰割; 当设置后果关键字匹配告诉时,反对输出关键字多个用,宰割; 工作减少实现后,会把工作数据长久化到 MySQL 中。 任务调度器参考了两个开源组件: robfig/cronjakecoffman/cron最终抉择应用 jakecoffman/cron ,后者是在前者的根底上做了肯定的补充,例如 AddFunc() 减少了 name 参数,同时还减少了 RemoveJob(name string) 反对删除特定的工作。 // AddFunc adds a func to the Cron to be run on the given schedule.func (c *Cron) AddFunc(spec string, cmd func(), name string) { c.AddJob(spec, FuncJob(cmd), name)}...// RemoveJob removes a Job from the Cron based on name.func (c *Cron) RemoveJob(name string) { if !c.running { i := c.entries.pos(name) if i == -1 { return } c.entries = c.entries[:i+copy(c.entries[i:], c.entries[i+1:])] return } c.remove <- name}对其简略封装下就能够应用了,上面是封装的办法,办法的具体实现与应用从 go-gin-api 中获取。 ...

September 5, 2021 · 1 min · jiezi

关于golang:GO的GC辣鸡回收

用户程序通过内存分配器(Allocator)在堆上申请内存,而垃圾收集器(Collector)负责回收堆上的内存空间,内存分配器和垃圾收集器独特管理程序中的堆内存空间。 基本概念垃圾分类语义垃圾:也就是内存透露,指的是从语法上可达的对象,也就是被其余对象援用的,然而从语义上来讲是垃圾。这类垃圾,垃圾回收是不论的语法垃圾:从语法上是不可达的对象,也就是没有对象援用了,这些垃圾是垃圾回收重点关照的对象垃圾回收算法常见的垃圾回收算法: 援用计数: 某个对象的根援用计数变为0时,其所有节点均需被回收 标记压缩: 将存活对象挪动到一起,无效解决内存碎片问题 复制算法: 将所有正在应用的对象从From空间复制到To空间,堆利用率只有一半,也能解决内存碎片问题 标记革除: 标记垃圾对象,而后再革除。解决不了内存碎片问题,须要与能尽量避免内存碎片的内存分配器应用,如tcmalloc 标记革除标记革除算法是最常见的垃圾收集算法,标记革除收集器是跟踪式垃圾收集器,其执行过程能够分为标记、革除两个阶段: 标记阶段:从根对象登程查找并标记堆中所有存活的对象革除阶段:遍历堆中的全副对象,回收未被标记的垃圾对象并将回收的内存退出闲暇链表传统的标记革除算法,垃圾收集器从垃圾收集的根对象登程,递归遍历这些对象指向的子对象并将所有可达的对象标记成存活。标记完结后,垃圾收集器会顺次遍历堆中的对象并革除其中的垃圾,整个过程须要标记对象的存活状态,所以用户程序在垃圾收集的过程中不能执行,也就是常说的STW(Stop The World)。 三色形象为了解决原始标记革除算法带来的长时间STW,大部分追踪式垃圾收集器都会实现三色标记算法的变种以缩短STW的工夫三色标记算法将程序中的对象分成红色、彩色、灰色: 红色对象:潜在的垃圾,也就是没有被扫到的对象,其内存可能会被垃圾收集器回收彩色对象:沉闷的对象,包含不存在任何援用内部指针的对象以及从根对象可达的对象灰色对象:沉闷的对象,因为存在指向红色对象的内部指针,垃圾收集器会扫描这些对象的子对象标记过程:垃圾收集器开始工作的时候,不存在任何彩色对象,根对象会被标记成灰色,垃圾收集器只会从灰色对象汇合中取出对象开始扫描,当灰色汇合中不存在任何对象时,标记就会完结。大抵工作原理: 从灰色对象的汇合中抉择一个灰色对象并将其标记成彩色将彩色对象指向的所有对象都标记成灰色,保障该对象和被该对象援用的对象都不会被回收反复上述两个步骤直到不存在灰色对象当三色标记完结后,应用程序的堆中就不存在任何灰色对象,咱们只能看到彩色的存活对象以及红色的垃圾对象,垃圾收集器能够回收这些红色的垃圾。缺点:因为用户程序可能在标记执行的过程中批改对象的指针,所以三色标记革除算法自身是不能够并发或者增量执行的,仍须要STW。如果原本不应该被回收的对象被回收了,这在内存治理中是十分重大的谬误,这种谬误称为悬挂指针,也就是指针没有指向特定类型的非法对象,影响了内存的安全性,想要并发或者增量标记对象就须要应用屏障技术。 屏障技术内存屏障技术是一种屏障指令,能够让CPU或编译器在执行内存相干的操作时遵循特定的束缚,目前少数的古代处理器都会乱序执行指令以最大化性能,然而该技术可能保障内存操作的程序性,在内存屏障前执行的操作肯定会先于内存屏障后执行的操作。想在并发或增量的标记算法中保障正确性,须要达到两种三色不变性的其中一种。 三色不变性:强三色不变性:彩色对象不会指向红色对象,只会指向灰色对象或者彩色对象弱三色不变性:彩色对象指向的红色对象必须蕴含一条从灰色对象经由多个红色对象的可达门路屏障技术分为读屏障和写屏障,然而因为读屏障须要在读操作中退出代码片段,所以对用户程序的性能影响较大。解析一下go语言中应用的两种写屏障技术,插入写屏障和删除写屏障。 插入写屏障(DIJKSTRA)writePointer(slot, ptr): shade(ptr) *slot = ptr每当要执行*slot = ptr时,会先执行写屏障通过shade函数尝试扭转指针色彩。如果ptr指针是红色,那么会将该对象设置成灰色。这张图中能够看到,在一次失常的标记过程中,产生了用户程序批改了指针援用(或者新插入了一个援用关系)的状况,如果咱们采纳 插入写屏障 咱们就须要将新指向的对象标为灰色,以此保障强三色不变性。(对于新指向的对象来说,属于被插入一个援用,所以叫插入写屏障) 在插入写屏障中,咱们的标记过程变成(要害第2步): 根本标记流程不变,咱们快进到垃圾收集器将根对象指向A对象标记成彩色,并将A对象指向的对象B标记成灰色。这时用户程序批改A对象的指针,将本来A对象指向B对象的指针指向C对象,这个时候就会触发写屏障将C对象标记为灰色,防止被谬误回收。垃圾收集器顺次遍历程序中的其余灰色对象,将他们别离标记成彩色删除写屏障(YUASA)writePointer(slot, ptr) shade(*slot) *slot = ptr删除写屏障会在老对象的援用被删除的时候,将红色的老对象涂成灰色,这样就能够保障弱三色不变性,老对象援用的上游对象肯定能够被灰色对象援用。 应用删除写屏障技术的垃圾收集器和用户程序交替运行的场景中的标记过程: 垃圾收集器将根对象指向A对象标记成彩色并将A对象指向的对象B标记成灰色如果这时用户程序将B指向C的指针删除,那么C触发删除写屏障,因为C是红色,所以被涂成灰色垃圾收集器顺次遍历程序中的其余灰色对象,将他们别离标记成彩色第二步触发删除写屏障的着色,因为删除了B指向C的指针,所以C和D别离违反强三色不变性和弱三色不变性,着色后保障了三色不变性,防止悬挂指针。 简略来说就是,在扭转指针指向的时候,原来指向的那个对象是红色的话就要变成灰色,以此保障弱三色不变性。(对于老对象来说,援用关系被解除了,所以叫删除写屏障) 增量和并发两种策略优化垃圾收集器不会认为回收垃圾导致长时间STW: 增量垃圾收集:增量得标记和革除垃圾,升高应用程序暂停的最长工夫并发垃圾手机:利用多核的计算资源,在用户程序执行时并发标记和革除垃圾因为两种形式都须要垃圾收集器与用户程序交替执行,所以须要配合屏障技术。 增量收集器增量收集器将本来工夫较长的暂停工夫切分成多个更小的GC工夫片。增量收集器须要配合三色标记法和屏障技术一起应用。将GC过程分段执行,尽管拉长了总的垃圾回收工夫,然而缩小了程序STW的工夫。不过写屏障还是有些开销的。 并发收集器利用多核优势,将GC过程与用户程序并行执行(大部分状况下),也是要配合屏障技术。 Referenceshttps://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-garbage-collector/    https://spin.atomicobject.com/2014/09/03/visualizing-garbage-collection-algorithms/

September 2, 2021 · 1 min · jiezi

关于golang:工具库系列之Golang实现的能自动回收过期值的内存缓存库

Golang实现的能主动回收过期值的内存缓存库 English README 该库应用了红黑树和最小堆数据结构。利用最小堆堆顶是最老的值,从而疾速荡涤过期值。能够认为是一个有过期工夫的 K-V 本地内存数据库。 原理很简略: 一个 treeMap 用来保留 K-V,一个最小堆的齐全树用来荡涤过期 key。官网的 map 不会缩容,treemap 的话不会占用多余空间。开了个定时器惰性删除过期key,因为定时器每秒最多革除30个过期,可能不够实时,所以当客户端被动拿值时会进行实时删除key,参考的redis。 数据保留在内存中,又快又好,这个内存缓存库十分高效,不须要预调配空间。 应用间接执行: go get -v github.com/hunterhug/gocache例子参考以下办法: type Cache interface { Set(key string, value []byte, expireTime time.Duration) SetInterface(key string, value interface{}, expireTime time.Duration) SetByExpireUnixNanosecondDateTime(key string, value []byte, expireUnixNanosecondDateTime int64) SetInterfaceByExpireUnixNanosecondDateTime(key string, value interface{}, expireUnixNanosecondDateTime int64) Delete(key string) Get(key string) (value []byte, expireUnixNanosecondDateTime int64, exist bool) GetInterface(key string) (value interface{}, expireUnixNanosecondDateTime int64, exist bool) GetOldestKey() (key string, expireUnixNanosecondDateTime int64, exist bool) Size() int Index(index int) (value []byte, expireUnixNanosecondDateTime int64, exist bool) IndexInterface(index int) (value interface{}, expireUnixNanosecondDateTime int64, exist bool) KeyList() []string ShutDown()}设置缓存时,能够抉择应用 time.Duration 来设置过期工夫,外部转化之后的工夫是纳秒 expireUnixNanosecondDateTime。 ...

September 1, 2021 · 1 min · jiezi

关于golang:工具库系列之Golang日志库非常简单的记录一切

Golang日志库,非常简单的记录所有 感激 Uber 的开源我的项目 ZapLog,它的速度很快,十分快,且是工业级别利用于很多大型的生产环境。我从没见过这么快的日志库,于是我封装了一层,奥利给。 反对日志输入到控制台或文件,反对很多配置,如打印函数调用行,配置日志级别等。反对日志层级输入到不同文件,且反对文件切割,革除过期日志文件等。应用接口契约,你能够自行再次封装该库,我留有 SetCallerSkip 给你持续封装。应用起来非常简单,你值得领有,快点用起来吧,代替你那又慢又难用的日志库。 English README 如何应用非常简单,您只须要惯例执行: go get -v -u github.com/hunterhug/golog例子默认什么都不配置的话,日志是打印到终端控制台的,默认日志级别是 InfoLevel,会打印出函数调用者的门路,默认长门路,打印进去的日志是文本格式,高亮模式。 您能够批改某些配置,来进行定制化,比方批改输入调用者函数门路为短门路,批改打印出 JSON 格局等等。 例子1:默认用法package mainimport . "github.com/hunterhug/golog"func main() { // use default log Info("now is Info", 2, " good") Debug("now is Debug", 2, " good") Warn("now is Warn", 2, " good") Error("now is Error", 2, " good") Infof("now is Infof: %d,%s", 2, "good") Debugf("now is Debugf: %d,%s", 2, "good") Warnf("now is Warnf: %d,%s", 2, "good") Errorf("now is Errorf: %d,%s", 2, "good") Sync() // config log SetLevel(DebugLevel).SetCallerShort(true).SetOutputJson(true).InitLogger() Info("now is Info", 2, " good") Debug("now is Debug", 2, " good") Warn("now is Warn", 2, " good") Error("now is Error", 2, " good") Infof("now is Infof: %d,%s", 2, "good") Debugf("now is Debugf: %d,%s", 2, "good") Warnf("now is Warnf: %d,%s", 2, "good") Errorf("now is Errorf: %d,%s", 2, "good") Sync()}输入是: ...

September 1, 2021 · 3 min · jiezi

关于golang:Go-117-调用规约

Go 1.17 批改了用了很久的基于栈的调用规约,在理解 Go 的调用规约之前,咱们得晓得什么是调用规约。 x86 calling convention,简略概括一下,其实就是语言对于函数之间传参的一种约定。调用方要晓得我要把参数依照什么模式,什么程序传给被调用函数,被调用函数也恪守该标准去相应的地位找到传入的参数内容。 老版本的 Go 的参数传递图咱们曾经在很多很多中央见过了,这里贴一个我之前画的: 能够看到入参和返回值都在栈上,按程序,从低地址,到高地址排列。 这种基于栈的传参在设计和实现上的确要简略,但栈上传参会导致函数调用过程中数次产生从寄存器和内存之间的参数搬运操作。比方 call 的时候,要把参数全搬到 SP 的地位(这里从寄存器 -> 内存); ret 的时候,也要把参数从寄存器搬到 FP 地位。ret 结束之后,要把返回值从内存 -> 寄存器。 寄存器是 CPU 外部的组件,而主存个别都在内部,两者之间有数量级的性能差别,所以始终有人说 Go 的函数调用性能很差,须要优化(尽管这些人大概率也不是从零碎整体性能思考去做优化的)。 Go 1.17 设计了一套基于寄存器传参的调用规约,目前只在 x86 平台下开启,咱们能够通过反汇编对其进行简略的察看。这里仍然为了简化问题,咱们只用 int 参数(float 应用的不是通用寄存器)。 package main//go:noinlinefunc add(x int, y int, z int, a, b, c int, d, e, f int, g, h, l int) (int, int, int, int, int, int, int, int, int, int, int) {println(x, y)return 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}func main() {println(add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))}略微多传一些参数不便咱们察看,输出 12 个参数,返回 11 个值。 ...

September 1, 2021 · 2 min · jiezi

关于golang:4种Golang并发操作中常见的死锁情形

摘要:什么是死锁,在Go的协程外面死锁通常就是永恒阻塞了,你拿着我的货色,要我先给你而后再给我,我拿着你的货色又让你先给我,不然就不给你。我俩都这么想,这事就解决不了了。本文分享自华为云社区《Golang并发操作中常见的死锁情景》,作者:Regan Yue 。 什么是死锁,在Go的协程外面死锁通常就是永恒阻塞了,你拿着我的货色,要我先给你而后再给我,我拿着你的货色又让你先给我,不然就不给你。我俩都这么想,这事就解决不了了。 第一种情景:无缓存能力的管道,本人写完本人读先上代码: func main() { ch := make(chan int, 0) ch <- 666 x := <- ch fmt.Println(x)}咱们能够看到这是一个没有缓存能力的管道,而后往里面写666,而后就去管道外面读。这样必定会呈现问题啊!一个无缓存能力的管道,没有人读,你也写不了,没有人写,你也读不了,这正是一种死锁! fatal error: all goroutines are asleep - deadlock!解决办法很简略,开拓两条协程,一条协程写,一条协程读。 第二种情景:协程来晚了func main() { ch := make(chan int,0) ch <- 666 go func() { <- ch }()}咱们能够看到,这条协程开拓在将数字写入到管道之后,因为没有人读,管道就不能写,而后写入管道的操作就始终阻塞。这时候你就有纳闷了,不是开拓了一条协程在读吗?然而那条协程开拓在写入管道之后,如果不能写入管道,就开拓不了协程。 第三种情景:管道读写时,互相要求对方先读/写如果互相要求对方先读/写,本人再读/写,就会造成死锁。 func main() { chHusband := make(chan int,0) chWife := make(chan int,0) go func() { select { case <- chHusband: chWife<-888 } }() select { case <- chWife: chHusband <- 888 }}先来看看老婆协程,chWife只有能读出来,也就是老婆有钱,就给老公发个八百八十八的大红包。 ...

September 1, 2021 · 1 min · jiezi

关于golang:Excelize-开源五周年-

Excelize (github.com/xuri/excelize) 根底库自 2016 年开源以来已成为云原生利用尤其是 Go 语言开发者在解决电子表格办公文档时的热门抉择。Excelize 根底库在实质上以其业余反对与良好的兼容性而广受欢迎,正在被广泛应用于大型互联网公司、中小企业客户和初创公司。明天时值开源五周年,历经 16 个公布版本,百余位 Contributors 参加奉献代码,作为发起人感激所有反对我的项目倒退的敌人,也很快慰可能帮忙到有须要的人。对于集体而言创立并保护一个开源我的项目须要的不仅是写代码、写文档和社区治理的能力,更重要的是如何放弃这份激情,放弃开心、充满活力、且高效输入的状态。 Excelize Star History Excelize Contributors 将来将持续和社区的开发者们一起在性能、兼容性和性能上做优化,波及方向包含但不限于: 更多接口将提供并发平安反对对蕴含大规模数据文档读写性能的继续优化公式计算引擎减少更多计算函数简单条件格局的设置、数据预测反对等灵便管制图表款式数据透视表、数据透视图、切片器性能更加精准的数字格局表达式解析工作簿加密提供更多可用流式读写接口最初也欢送感兴趣的敌人参加到开源我的项目的建设中来。

September 1, 2021 · 1 min · jiezi

关于golang:微博热搜的历史查看和趋势分析吃瓜利器

微博热搜的历史查看和趋势剖析,吃瓜利器最近微博前阵子的瓜有点多,忽然有个想法,想写一个热搜的趋势变动查看,还有历史热搜的查看。次要是想看一下热搜数据是否不失常,比方某些明星的不良影响热搜间接被买下热搜等,还有我想剖析一下这些热搜数据,比方某位明星上热搜的次数啥的。 先看下当初实现的进度和成果: 图源是github有可能加载不进去,间接拜访hot-search能够预览。我的次要思路就是先收集数据,有了原始数据就能够进行后续的数据展现。所以我写的几个货色如下: 微博热搜爬虫,用go 语言编写,应用了colly这个爬虫框架。15分钟爬取一次,存入influxdb。 go-crawler微博热搜API, 这个API是我查找本人爬取的热搜数据,也是go 语言编写,gin框架实现。weibo-hot-search热搜展现,前端界面是用React编写的,为了让界面看起来没那么丑,用了 Ant Design 的UI库。weibo-hot-search-react热搜界面快照,用wkhtmltopdf 进行网页转图片,我想着这热搜上了得有个证据吧,就搞了这个,保留了图片和PDF。(嵌入到爬虫)实际上整体大略就这些,当初实现了个小demo,当初实现了以后热搜的查看,历史热搜的查看和热搜的热度和排名趋势变动。 当初demo能够在线预览了,感兴趣的敌人能够看下 hot-search ,预览下成果,有什么意见也能够分享一下啊。 这几个模块的源码github上都有,大家感兴趣的能够点个start。一起相互 followe 啊, 我的github链接放在这 akazwz 做的这个demo都是些根底的货色组合到一起,也是我这段时间自学go 和react 的一个实际,尽管是个简略的小东西然而也遇到了不少问题,后续我会挨个分享一下遇到的问题和解决的思路。感激大家浏览。

August 31, 2021 · 1 min · jiezi

关于golang:Go中的内存逃逸分析

前 言很多时候为了更快的开发效率,大多数程序员都是在应用形象层级更高的技术,包含语言,框架,设计模式等。所以导致很多程序员包含我本人在内对于底层和根底的常识都会有些陌生和,然而正是这些底层的货色构建了咱们熟知的解决方案,同时决定了一个技术人员的下限。 在写C和C++的时候动静分配内存是让程序员本人手动治理,这样做的益处是,须要申请多少内存空间能够很好的把握怎么调配,然而如果遗记开释内存,则会导致内存透露。 Rust又比下面俩门语言分配内存形式显得不同,Rust的内存治理次要特色能够看做是编译器帮你在适当的中央插入delete来开释内存,这样一来你不须要显式指定开释,runtime也不须要任何GC,然而要做到这点,编译器须要能剖析出在什么中央delete,这就须要你代码依照其规定来写了。 相比下面几种的内存治理形式的语言,像Java和Golang在语言设计的时候就退出了garbage collection也就runtime中的gc,让程序员不须要本人治理内存,真正解放了程序员的双手,让咱们能够专一于编码。 函数栈帧 当一个函数在运行时,须要为它在堆栈中创立一个栈帧(stack frame)用来记录运行时产生的相干信息,因而每个函数在执行前都会创立一个栈帧,在它返回时会销毁该栈帧。 通常用一个叫做栈基址(bp)的寄存器来保留正在运行函数栈帧的开始地址,因为栈指针(sp)始终保留的是栈顶的地址,所以栈指针保留的也就是正在运行函数栈帧的完结地址。 销毁时先把栈指针(sp)挪动到此时栈基址(bp)的地位,此时栈指针和栈基址都指向同样的地位。 Go内存逃逸能够简略得了解成一次函数调用外部申请到的内存,它们会随着函数的返回把内存还给零碎。上面来看看一个例子: package mainimport "fmt"func main() { f := foo("Ding") fmt.Println(f)}type bar struct { s string}func foo(s string) bar { f := new(bar) // 这里的new(bar)会不会产生逃逸??? defer func() { f = nil }() f.s = s return *f}我想很多人认为产生了逃逸,然而真的是这样的吗?那就用go build -gcflags=-m escape/struct.go看看会输入什么??? 其实没有产生逃逸,而escape/struct.go:7:13: f escapes to heap的逃逸是因为动静类型逃逸,fmt.Println(a …interface{})在编译期间很难确定其参数的具体类型,也能产生逃逸。 持续看上面这一个例子: package mainimport "fmt"func main() { f := foo("Ding") fmt.Println(f)}type bar struct { s string}func foo(s string) *bar { f := new(bar) // 这里的new(bar)会不会产生逃逸??? defer func() { f = nil }() f.s = s return f}f := new(bar)会产生逃逸吗? ...

August 31, 2021 · 2 min · jiezi

关于golang:流量录制与回放技术实践

文章导读本文次要介绍了流量录制与回放技术在压测场景下的利用。通过浏览本篇文章,你将理解到开源的录制工具如何外部系统集成、如何进行二次开发以反对 Dubbo 流量录制、怎么通过 Java 类加载机制解决 jar 包版本抵触问题、以及流量录制在自动化测试场景下的利用与价值等。文章共约 1.4 万字,配图17张。本篇文章是对我集体过来一年所负责的工作的总结,外面波及到了很多技术点,集体从中学到了很多货色,也心愿这篇文章能让大家有所播种。当然集体能力无限,文中不妥之处也欢送大家指教。具体章节安顿如下: 1. 前言本篇文章记录和总结了本人过来一年所主导的我的项目——流量录制与回放,该我的项目次要用于为业务团队提供压测服务。作为我的项目负责人,我承当了我的项目约 70% 的工作,所以这个我的项目承载了本人很多的记忆。从需要提出、技术调研、选型验证、问题处理、方案设计、两周内上线最小可运行零碎、推广应用、反对年中/终全链路压测、迭代优化、反对 dubbo 流量录制、到新场景落地产生价值。这里列举每一项本人都深度参加了,因而也从中学习到了很多货色。蕴含但不限于 go 语言、网络常识、Dubbo 协定细节,以及 Java 类加载机制等。除此之外,我的项目所产生的价值也让本人很欣慰。我的项目上线一年,帮忙业务线发现了十几个性能问题,帮忙中间件团队发现了根底组件多个重大的问题。总的来说,这个我的项目对于我集体来说具备不凡意义,受害良多。这里把过来一年的我的项目经验记录下来,做个总结。本篇文章着重讲实现思路,不会贴太多代码,有趣味的敌人能够依据思路本人定制一套。好了,上面开始注释吧。 2. 我的项目背景我的项目的呈现源自业务团队的一个诉求——应用线上实在的流量进行压测,使压测更为“实在”一些。之所以业务团队感觉应用老的压测平台(基于 Jmeter 实现)不实在,是因为压测数据的多样性有余,对代码的覆盖度不够。惯例压测工作通常都是对利用的 TOP 30 接口进行压测,如果人工去欠缺这些接口的压测数据,老本是会十分高的。基于这个需要,咱们调研了一些工具,并最终抉择了 Go 语言编写的 GoReplay 作为流量录制和回放工具。至于为什么抉择这个工具,接下来聊聊。 3. 技术选型与验证3.1 技术选型一开始选型的时候,经验不足,并没有思考太多因素,只从功能性和知名度两个维度进行了调研。首先性能上肯定要能满足咱们的需要,比方具备流量过滤性能,这样能够按需录制指定接口。其次,候选项最好有大厂背书,github 上有很多 star。依据这两个要求,选出了如下几个工具: 图1:技术选型 第一个是选型是阿里开源的工具,全称是 jvm-sandbox-repeater,这个工具其实是基于 JVM-Sandbox 实现的。原理上,工具通过字节码加强的模式,对指标接口进行拦挡,以获取接口参数和返回值,成果等价于 AOP 中的盘绕告诉 (Around advice)。 第二个选型是 GoReplay,基于 Go 语言实现。底层依赖 pcap 库提供流量录制能力。驰名的 tcpdump 也依赖了 pcap 库,所以能够把 GoReplay 看成极简版的 tcpdump,因为其反对的协定很繁多,只反对录制 http 流量。 第三个选型是 Nginx 的流量镜像模块 ngx_http_mirror_module,基于这个模块,能够将流量镜像到一台机器上,实现流量录制。 第四个选型是阿里云云效里的子产品——双引擎回归测试平台,从名字上能够看进去,这个零碎是为回归测试开发的。而咱们需要是做压测,所以这个服务里的很多性能咱们用不到。 通过比拟筛选后,咱们抉择了 GoReplay 作为流量录制工具。在剖析 GoReplay 优缺点之前,先来剖析下其余几个工具存在的问题。 ...

August 30, 2021 · 6 min · jiezi

关于golang:如何欺骗-Go-Mod

hi,大家好,我是 haohongfan。 最近在做 prometheus 生态的 cortex 优化工作,遇到一个比拟坑的 go mod 的问题,这里分享一下。 我为什么将题目称为:如何坑骗 Go mod 呢?这个挺有意思的,这里先卖个关子,不过的确是冲破了 Go mod 的相干个性。(嗯,曹大的 Go mod 十宗罪又能够减少一宗了) 在正式开展这个话题之前,须要简略的介绍下 cortex 和 thanos 这两个我的项目。 Prometheus 的局限性说到业务开发基本上都离不开监控零碎,Prometheus 做为云原生的宠儿,以优良的设计,灵便的应用形式,以优异成绩从 CNCF 顺利毕业,也是很多公司做监控的首选。 然而呢,Promethues 也有其本身局限性,其中影响最大的就是其数据的高可用计划和集群计划。监控也是业务零碎的重中一环,不能因为监控零碎宕机导致报警无奈及时收回。 Prometheus 官网也有提出联邦计划来解决集群问题,然而这个计划极其简单而且很多问题还是解决不了,于是就造就了另外两个 CNCF 的沙箱我的项目:cortex 和 thanos。这两个我的项目都是为了解决 Promethues 的集群,高可用的。 因为这两个我的项目要解决问题的目标是统一的,所以就会呈现很多性能都是能够互相复用的,于是乏味的事件就产生了。 cortex话说因为某些的需要,不得已须要更改下 thanos 的相干代码。我本地调试的时候将 cortex 依赖的 thanos 给 replace 了一下。 replace github.com/thanos-io/thanos => /Users/hhf/goproject/cortex/thanos再等我编译的时候,就编译不过了 # github.com/sercand/kuberesolver../../../go/pkg/mod/github.com/sercand/kuberesolver@v2.1.0+incompatible/builder.go:108:82: undefined: resolver.BuildOption../../../go/pkg/mod/github.com/sercand/kuberesolver@v2.1.0+incompatible/builder.go:163:32: undefined: resolver.ResolveNowOption这就让人很无奈,别着急,咱们看看这个 kuberesolver 是被谁依赖的。 先看下被 replace 之前: ▶ go mod graph| grep kuberesolvergithub.com/weaveworks/common@v0.0.0-20210419092856-009d1eebd624 github.com/sercand/kuberesolver@v2.1.0+incompatiblegithub.com/weaveworks/common@v0.0.0-20210112142934-23c8d7fa6120 github.com/sercand/kuberesolver@v2.1.0+incompatiblegithub.com/weaveworks/common@v0.0.0-20200206153930-760e36ae819a github.com/sercand/kuberesolver@v2.1.0+incompatiblegithub.com/weaveworks/common@v0.0.0-20201119133501-0619918236ec github.com/sercand/kuberesolver@v2.1.0+incompatiblegithub.com/weaveworks/common@v0.0.0-20200914083218-61ffdd448099 github.com/sercand/kuberesolver@v2.1.0+incompatiblegithub.com/weaveworks/common@v0.0.0-20200625145055-4b1847531bc9 github.com/sercand/kuberesolver@v2.1.0+incompatiblegithub.com/thanos-io/thanos@v0.13.1-0.20200731083140-69b87607decf github.com/sercand/kuberesolver@v2.4.0+incompatible能够看到失常版本下,kuberesolver@2.4.0 被 thanos 所依赖,kuberesolver@v2.1.0 被 weaveworks 所依赖。 replace 之后 ▶ go mod graph| grep kuberesolvergithub.com/weaveworks/common@v0.0.0-20210419092856-009d1eebd624 github.com/sercand/kuberesolver@v2.1.0+incompatible是不是很神奇,kuberesolver@v2.4.0 这个版本居然隐没了。因为 kuberesolver 的 v2.1.0 和 v2.4.0 是不兼容的,所以导致 replace 之后就无奈编译了。 ...

August 30, 2021 · 2 min · jiezi

关于golang:源码赏析Go官方设计的信号量库

原文链接:Go官网设计了一个信号量库 前言哈喽,大家好,我是asong。在写上一篇文章请勿滥用goroutine时,发现Go语言扩大包提供了一个带权重的信号量库Semaphore,应用信号量咱们能够实现一个"工作池"管制肯定数量的goroutine并发工作。因为对源码抱有好奇的态度,所以在周末认真看了一下这个库并进行了解析,在这里记录一下。何为信号量要想晓得一个货色是什么,我都爱去百度百科上搜一搜,输出"信号量",这答案不就来了。 百度百科解释: 信号量(Semaphore),有时被称为信号灯,是[多线程环境下应用的一种设施,是能够用来保障两个或多个要害代码段不被并发调用。在进入一个要害代码段之前,线程必须获取一个信号量;一旦该要害代码段实现了,那么该线程必须开释信号量。其它想进入该要害代码段的线程必须期待直到第一个线程开释信号量。为了实现这个过程,须要创立一个信号量VI,而后将Acquire Semaphore VI以及Release Semaphore VI别离搁置在每个要害代码段的首末端。确认这些信号量VI援用的是初始创立的信号量。通过这段解释咱们能够得悉什么是信号量,其实信号量就是一种变量或者抽象数据类型,用于管制并发零碎中多个过程对公共资源的拜访,拜访具备原子性。信号量次要分为两类: 二值信号量:顾名思义,其值只有两种0或者1,相当于互斥量,当值为1时资源可用,当值为0时,资源被锁住,过程阻塞无奈继续执行。计数信号量:信号量是一个任意的整数,起始时,如果计数器的计数值为0,那么创立进去的信号量就是不可取得的状态,如果计数器的计数值大于0,那么创立进去的信号量就是可取得的状态,并且总共获取的次数等于计数器的值。信号量工作原理信号量是由操作系统来保护的,信号量只能进行两种操作期待和发送信号,操作总结来说,外围就是PV操作: P原语:P是荷兰语Proberen(测试)的首字母。为阻塞原语,负责把以后过程由运行状态转换为阻塞状态,直到另外一个过程唤醒它。操作为:申请一个闲暇资源(把信号量减1),若胜利,则退出;若失败,则该过程被阻塞;V原语:V是荷兰语Verhogen(减少)的首字母。为唤醒原语,负责把一个被阻塞的过程唤醒,它有一个参数表,寄存着期待被唤醒的过程信息。操作为:开释一个被占用的资源(把信号量加1),如果发现有被阻塞的过程,则抉择一个唤醒之。在信号量进行PV操作时都为原子操作,并且在PV原语执行期间不容许有中断的产生。 PV原语对信号量的操作能够分为三种状况: 把信号量视为时某种类型的共享资源的残余个数,实现对一类共享资源的拜访把信号量用作过程间的同步视信号量为一个加锁标记,实现对一个共享变量的拜访具体在什么场景应用本文就不在持续剖析,接下来咱们重点来看一下Go语言提供的扩大包Semaphore,看看它是怎么实现的。 官网扩大包Semaphore咱们之前在剖析Go语言源码时总会看到这几个函数: func runtime_Semacquire(s *uint32)func runtime_SemacquireMutex(s *uint32, lifo bool, skipframes int)func runtime_Semrelease(s *uint32, handoff bool, skipframes int)这几个函数就是信号量的PV操作,不过他们都是给Go外部应用的,如果想应用信号量,那就能够应用官网的扩大包:Semaphore,这是一个带权重的信号量,接下来咱们就重点剖析一下这个库。 装置办法:go get -u golang.org/x/sync数据结构type Weighted struct { size int64 // 设置一个最大权值 cur int64 // 标识以后已被应用的资源数 mu sync.Mutex // 提供临界区爱护 waiters list.List // 阻塞期待的调用者列表}semaphore库外围构造就是Weighted,次要有4个字段: size:这个代表的是最大权值,在创立Weighted对象指定cur:相当于一个游标,来记录以后已应用的权值mu:互斥锁,并发状况下做临界区爱护waiters:阻塞期待的调用者列表,应用链表数据结构保障先进先出的程序,存储的数据是waiter对象,waiter数据结构如下:type waiter struct { n int64 // 期待调用者权重值 ready chan<- struct{} // close channel就是唤醒}这里只有两个字段: n:这个就是期待调用者的权重值ready:这就是一个channel,利用channel的close机制实现唤醒semaphore还提供了一个创立Weighted对象的办法,在初始化时须要给定最大权值: // NewWeighted为并发拜访创立一个新的加权信号量,该信号量具备给定的最大权值。func NewWeighted(n int64) *Weighted { w := &Weighted{size: n} return w}阻塞获取权值的办法 - Acquire先间接看代码吧: ...

August 30, 2021 · 3 min · jiezi

关于golang:分布式事务管理器dtm0100发布-支持grpc

更新日志反对grpcDTM是首款golang的开源分布式事务管理器,优雅的解决了幂等、空弥补、悬挂等分布式事务难题。提供了简略易用、高性能、易程度扩大的分布式事务解决方案。 受邀加入中国数据库大会分享多语言环境下分布式事务实际 谁在应用dtmIvydad 常青藤爸爸 Eglass 视咖镜小二 极欧科技 亮点极易接入 反对HTTP,提供非常简单的接口,极大升高上手分布式事务的难度,老手也能疾速接入应用简略 开发者不再放心悬挂、空弥补、幂等各类问题,框架层代为解决跨语言 可适宜多语言栈的公司应用。不便go、python、php、nodejs、ruby、c# 各类语言应用。易部署、易扩大 仅依赖mysql,部署简略,易集群化,易程度扩大多种分布式事务协定反对 TCC、SAGA、XA、事务音讯与其余框架比照目前开源的分布式事务框架,暂未看到非Java语言有成熟的框架。而Java语言的较多,有阿里的SEATA、华为的ServiceComb-Pack,京东的shardingsphere,以及himly,tcc-transaction,ByteTCC等等,其中以seata利用最为宽泛。 上面是dtm和seata的次要个性比照: 个性DTMSEATA备注反对语言Go、Java、python、php、c#...Javadtm可轻松接入一门新语言异样解决子事务屏障主动解决手动解决dtm解决了幂等、悬挂、空弥补TCC事务✓✓ XA事务✓✓ AT事务✗✓AT与XA相似,性能更好,但有脏回滚SAGA事务简略模式状态机简单模式dtm的状态机模式在布局中事务音讯✓✗dtm提供相似rocketmq的事务音讯通信协议HTTP、GRPCdubbo等协定,无HTTPdtm对云原生更加敌对从下面比照的个性来看,如果您的语言栈蕴含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也能够抉择接入dtm,应用子事务屏障技术,简化您的业务编写。 欢送拜访https://github.com/yedf/dtm!

August 29, 2021 · 1 min · jiezi

关于golang:一个实验带你真正搞懂-Go-结构体方法指针和值的区别

家喻户晓,Go struct 定义方法时应用指针还是值的区别就是在办法内批改属性值时,用值定义的办法所做的批改只限于办法内,而指针则没有这个局限。 文章如果到这里就完结了,那么就很平平无奇了,于是我打算带大家去做个无聊然而值得思考的试验。 在开始之前,先写段简略的代码跑一下后面说到的货色,顺便让大家相熟一下接下来试验代码的一些编码规定,哦对了,以下代码写于 2021.08,Go 版本是 1.16.5,如果你看到这篇文章的时候 Go 曾经更新了很多个版本了,可能就不实用了。废话不多说,上代码: package mainimport "fmt"type Foo struct { val int}/** * 在这里,我定义了两个 Set 办法,一个 P 结尾,一个 V 结尾,聪慧的你必定很快就反馈过去了: * P 即 Pointer,V 即 Value * * 另外我在这里加了个 callBy,不便追踪调用链 */func (f *Foo) SetP(v int, callBy string) { f.val = v fmt.Printf("In SetP(): call by:%s\tval:%d\n", callBy, f.val)}func (f Foo) SetV(v int, callBy string) { f.val = v fmt.Printf("In SetV(): call by:%s\tval:%d\n", callBy, f.val)}func main() { f := Foo{0} fmt.Printf("In main(): val:%d\n", f.val) fmt.Println("=====================================") f.SetP(1, "main") fmt.Printf("In main(): after f.SetP(1): val:%d\n", f.val) fmt.Println("=====================================") f.SetV(2, "main") fmt.Printf("In main(): after f.SetV(2): val:%d\n", f.val) fmt.Println("=====================================")}运行后果: ...

August 29, 2021 · 3 min · jiezi

关于golang:golang-string转换为byte

golang中将string转换为byte切片,能够应用规范转换方法,也能够通过强转形式。两种形式的后果一样,然而执行效率差异很大。如下是我的两种转化形式,效率比规范的转换高很多。 在贴代码前,先理解一下string和slice的Header定义 StringHeader如下,他是string的底层实现 type StringHeader struct { Data uintptr Len int}SliceHeader如下 type SliceHeader struct { Data uintptr Len int Cap int}能够发现这两个十分类似其实就十分好了解了Data指向理论内容的数组Len长度 那么咱们就能够开始如下两种操作 将StringHeader中的Data赋值给SliceHeader的Data将StringHeader中的Len赋值给SliceHeader的Len也能够间接将StringHeader强转成SliceHeader(因为各个fields的长度和程序都是一样的)上面间接上代码 func string2BytesSlicePlus(str string) []byte { bytesSlice := []byte{} //此处定义了一个空切片 stringData := &(*(*reflect.StringHeader)(unsafe.Pointer(&str))).Data//获得StringHeader的Data地址 byteSliceData := &(*(*reflect.SliceHeader)(unsafe.Pointer(&bytesSlice))).Data //获得SliceHeader的Data地址 *byteSliceData = *stringData //将StringHeader.Data的值赋给SliceHeader.Data (*(*reflect.SliceHeader)(unsafe.Pointer(&bytesSlice))).Len = (*(*reflect.StringHeader)(unsafe.Pointer(&str))).Len //设置长度 return bytesSlice}间接强转 func string2BytesSlicePlus2(str string) []byte { strSliceHeader := *(*reflect.StringHeader)(unsafe.Pointer(&str)) byteSlice := *(*[]byte)(unsafe.Pointer(&strSliceHeader)) return byteSlice}const ( TOTALCNT = 1000000)func string2BytesSlicePlus(str string) []byte { bytesSlice := []byte{} stringData := &(*(*reflect.StringHeader)(unsafe.Pointer(&str))).Data byteSliceData := &(*(*reflect.SliceHeader)(unsafe.Pointer(&bytesSlice))).Data *byteSliceData = *stringData (*(*reflect.SliceHeader)(unsafe.Pointer(&bytesSlice))).Len = (*(*reflect.StringHeader)(unsafe.Pointer(&str))).Len return bytesSlice}func string2BytesSlicePlus2(str string) []byte { strSliceHeader := *(*reflect.StringHeader)(unsafe.Pointer(&str)) byteSlice := *(*[]byte)(unsafe.Pointer(&strSliceHeader)) return byteSlice}func normalString2BytesSlice(str string) []byte { return []byte(str)}func TestStringConvert(t *testing.T) { origStr := "String convert test" convSlice := string2BytesSlicePlus(origStr) byteSlice := []byte(origStr) if !bytes.Equal(convSlice, byteSlice) { t.Fail() } convSlice = string2BytesSlicePlus2(origStr) if !bytes.Equal(convSlice, byteSlice) { t.Fail() }}//Run go test -bench="." -benchmem to verify efficiencyfunc Benchmark_NormalConvert(t *testing.B) { for count := 0; count < TOTALCNT; count++ { str := "This is string to byte slice convert test!!!" _ = normalString2BytesSlice(str) }}func Benchmark_PlusConvert(t *testing.B) { for count := 0; count < TOTALCNT; count++ { str := "This is string to byte slice convert test!!!" _ = string2BytesSlicePlus(str) }}func Benchmark_PlusConvert2(t *testing.B) { for count := 0; count < TOTALCNT; count++ { str := "This is string to byte slice convert test!!!" _ = string2BytesSlicePlus2(str) }}执行测试 ...

August 26, 2021 · 2 min · jiezi

关于golang:区块链开发工程师要干什么

区块链开发工程师要干什么?一是Go编程开发工程师,多从业于软件开发公司,从事以下畛域的编程:服务器编程、分布式系统编程、网络编程、数据库操作、开发云平台等。 目前很多云平台采纳Go开发,Go语言是目前我的项目转型区块链首选的语言,也是软件工程师转型首选的语言,是增加技术栈的首选语言。 二是区块链开发工程师,以后区块链开发人员多须要在以下行业:互联网金融行业、电子商务行业、物流行业、游戏行业等。 区块链工程师岗位职责:负责区块链底层技术研发。 以下是小编搜罗的几个区块链工程师岗位要求: 1、有比特币、以太坊、eos等出名区块链产品源码批改教训或者其余区块链产品底层研发教训; 2、精通c++、go、java、solidity中的一种或多种语言; 3、精通linux; 1、至多3年以上互联网开发教训; 2、熟练掌握Golang编程语言,对GRPC相熟; 3、熟练掌握Docker容器技术; 4、酷爱参加开源我的项目、相熟其余区块链开源我的项目者优先; 5、区块链技术狂热分子优先; 6、了解各类支流的共识算法(PoW,PoS,DPoS,PBFT,Paxos,Raft)者优化。 加分项: 相熟openssl相干加密算法。 1、参加基于区块链相干技术开发,蕴含点对点网络设计、加密技术利用、分布式算法的实现、数据存储技术; 2、参加钻研剖析以太坊以及智能合约等底层协定,运行机制和底层实现等; 3、参加开发、实现和改良加密协议,设计平安协定和架构; 4、参加对研发我的项目进行欠缺的平安模块设计与开发; 5、参加设计方案制订,及相干验证测试计划的制订和评审。

August 25, 2021 · 1 min · jiezi

关于golang:分布式事务管理器dtm091发布-新增python-node支持线上部署支持

更新日志增加环境变量反对、部署反对,文档增加部署反对dtmcli与gin、logrus齐全解耦,并且反对拆分到独自的仓库增加事务期待后果模式增加默认DB_DRIVER为mysqlDTM是首款golang的开源分布式事务管理器,优雅的解决了幂等、空弥补、悬挂等分布式事务难题。提供了简略易用、高性能、易程度扩大的分布式事务解决方案。 受邀加入中国数据库大会分享多语言环境下分布式事务实际 谁在应用dtmIvydad 常青藤爸爸 Eglass 视咖镜小二 亮点极易接入 反对HTTP,提供非常简单的接口,极大升高上手分布式事务的难度,老手也能疾速接入应用简略 开发者不再放心悬挂、空弥补、幂等各类问题,框架层代为解决跨语言 可适宜多语言栈的公司应用。不便go、python、php、nodejs、ruby、c# 各类语言应用。易部署、易扩大 仅依赖mysql,部署简略,易集群化,易程度扩大多种分布式事务协定反对 TCC、SAGA、XA、事务音讯与其余框架比照目前开源的分布式事务框架,暂未看到非Java语言有成熟的框架。而Java语言的较多,有阿里的SEATA、华为的ServiceComb-Pack,京东的shardingsphere,以及himly,tcc-transaction,ByteTCC等等,其中以seata利用最为宽泛。 上面是dtm和seata的次要个性比照: 个性DTMSEATA备注反对语言Go、Java、python、php、c#...Javadtm可轻松接入一门新语言异样解决子事务屏障主动解决手动解决dtm解决了幂等、悬挂、空弥补TCC事务✓✓ XA事务✓✓ AT事务✗✓AT与XA相似,性能更好,但有脏回滚SAGA事务简略模式状态机简单模式dtm的状态机模式在布局中事务音讯✓✗dtm提供相似rocketmq的事务音讯通信协议HTTP、GRPCdubbo等协定,无HTTPdtm对云原生更加敌对从下面比照的个性来看,如果您的语言栈蕴含了Java之外的语言,那么dtm是您的首选。如果您的语言栈是Java,您也能够抉择接入dtm,应用子事务屏障技术,简化您的业务编写。 如果您感觉https://github.com/yedf/dtm不错,或者对您有帮忙,请赏颗星吧!

August 25, 2021 · 1 min · jiezi

关于golang:Go读书社区web开发与高性能架构优化

download:Go读书社区web开发与高性能架构优化@SpringBootApplication注解分析以下是注解@SpringBootApplication的源码实现 @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {可能发现它是由泛滥注解组合而成的,上面具体分析下这里每个注解所起到的作用。 @Target Target通过ElementType来指定注解可使用范畴的枚举会合(FIELD/METHOD/PARAMETER...)@Retention Retention(保留)注解说明,这种类型的注解会被保留到那个阶段. 有三个值: RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其余使@Documented 注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包含注解的. 但如果申明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包含在生成的文档中@Inherited 容许子类继承父类的注解,仅限于类注解有用,对于方法和属性有效。@SpringBootConfiguration 注解实际上和@Configuration有雷同的作用,配备了该注解的类就能够以JavaConfig的形式实现一些配置,可能不再使用XML配置。@ComponentScan 这个注解实现的是主动扫描的功能,相当于Spring XML配置文件中的:<context:component-scan>,可使用basePackages属性指定要扫描的包,及扫描的条件。如果不设置则默认扫描@ComponentScan注解所在类的同级类和同级目录下的所有类,所以咱们的Spring Boot我的项目,一般会把入口类放在顶层目录中,这样就能够保障源码目录下的所有类都能够被扫描到。@EnableAutoConfiguration 这个注解是让Spring Boot的配置能够如此简化的关键性注解。我把EnableAutoConfiguration的实现端上来了,大家来鉴赏一下! @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {@AutoConfigurationPackage 注解用于保存主动配置类以供之后的使用,比如给JPA entity扫描器,用来扫描开发人员通过注解@Entity定义的entity类。通俗的讲就是,注册bean定义到容器中。@Import(AutoConfigurationImportSelector.class)是EnableAutoConfiguration注解中最要害的来,它借助AutoConfigurationImportSelector,可能帮助SpringBoot利用将所有符合条件的@Configuration配置都加载到以后SpringBoot创建并使用的IoC容器中。对于@Import注解要说的内容还比较多,改天再聊。对于注解的话题就先谈到这里,上面开启撸代码环节。 3 剖析代码查看SpringApplication的源代码可能发现SpringApplication的启动由两部分组成: new SpringApplication(primarySources):创建SpringApplication对象run(args):调用run方法3.1 实例化SpringApplication对象源码如下: public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader;//1、初始化资源加载器 Assert.notNull(primarySources, "PrimarySources must not be null");//2、断言资源加载类不能为 null this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//3、初始化加载资源类会合并去重 this.webApplicationType = deduceWebApplicationType();//4、 推断利用类型是Standard还是Web setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));//5、设置利用上下文初始化器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//6、设置监听器 this.mainApplicationClass = deduceMainApplicationClass();//7、推断利用入口类}上面将针对源码中的重要实现进行粗疏的分析。 ...

August 25, 2021 · 1 min · jiezi

关于golang:释放有限的资源以避免泄露

本文是对 《100 Go Mistackes:How to Avoid Them》 一书的翻译。因翻译程度无限,不免存在翻译准确性问题,敬请体谅。关注 公众号 “Go学堂”,获取更多系列文章家喻户晓,计算机的资源(内存、磁盘)都是无限的,在编程时,这些资源必须在代码的中的某个中央被敞开开释,以防止造成资源有余而泄露。但开发人员在编写代码时往往会疏忽敞开已关上的资源,从而因资源有余导致程序出现异常。 本文次要介绍在Go中,但凡实现了io.Closer接口的构造体,最终都必须要被敞开以开释资源。 上面这个例子是一个getBody函数,该函数会构建一个HTTP GET申请并解决失去的HTTP响应。 上面是第一版本的实现: func getBody(url string) (string, error) { resp, err := http.Get(url) if err != nil { return "", err } body, err := ioutil.ReadAll(resp.Body) ① if err != nil { return "", err } return string(body), nil}① 读取resp.Body并将其转换成一个字节数组[]byte 咱们应用了http.Get办法,而后咱们应用ioutil.ReadAll解析响应值。这个函数的性能看起来算是失常的。至多,它正确返回了HTTP响应。然而,这里存在一个资源泄露的问题。让咱们看看是在哪里。 resp是一个*http.Response指针类型。它蕴含一个io.ReaderCloser字段(io.ReadCloser同时蕴含io.Reader接口和io.Closer接口)。如果http.Get没有返回谬误,那该字段必须被敞开。否则,就会造成资源泄露。它会占用一些内存,这些内存在函数执行后就不再须要了,但因没有被动开释资源所以不能被GC回收,同时在资源匮乏的时候客户端还不能重用TCP连贯。 解决该主体敞开的最不便的办法就是应用defer语句: func getBody(url string) (string, error) { resp, err := http.Get(url) if err != nil { return "", err } defer resp.Body.Close() ① body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", err } return string(body), nil}① 如果http.Get没有返回谬误,咱们会应用defer来敞开响应值。 ...

August 24, 2021 · 2 min · jiezi

关于golang:见微知著-带你透过内存看-Slice-和-Array的异同

hi, 大家好,我是 hhf。 有这么一个 Go 面试题:请说出 slice 和 array 的区别? 这几乎就是送分题。然而你如何答复能力让面试官称心呢? 我这里就不贴这道题的答案了。然而我想内存方面简略剖析下 slice 和 array 的区别。 Arrayfunc main() {  as := [4]int{10, 5, 8, 7}    fmt.Println("as[0]:", as[0])  fmt.Println("as[1]:", as[1])  fmt.Println("as[2]:", as[2])  fmt.Println("as[3]:", as[3])}这段很简略的代码,申明了一个 array。当然输入后果也足够简略。 咱们当初玩点花活,如何通过非正常的伎俩拜访数组外面的元素呢?在做这个事件之前是须要先晓得 array 的底层构造的。其实很简略,Go array 就是一块间断的内存空间。如下图所示 写一段简略的代码,咱们不通过下标拜访的形式去获取元素。通过挪动指针的形式去获取对应地位的指针。 func main() {    as := [4]int{10, 5, 8, 7}    p1 := *(*int)(unsafe.Pointer(&as))    fmt.Println("as[0]:", p1)    p2 := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + unsafe.Sizeof(as[0])))    fmt.Println("as[1]:", p2)    p3 := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + unsafe.Sizeof(as[0])*2))    fmt.Println("as[2]:", p3)    p4 := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + unsafe.Sizeof(as[0])*3))    fmt.Println("as[3]:", p4)}后果: as[0]: 10as[1]: 5as[2]: 8as[3]: 7下图演示下获取对应地位的值的过程: Slice同样对于 slice 这段简略的代码: func main() {  as := []int{10, 5, 8, 7}    fmt.Println("as[0]:", as[0])  fmt.Println("as[1]:", as[1])  fmt.Println("as[2]:", as[2])  fmt.Println("as[3]:", as[3])}想要通过挪动指针的形式获取 slice 对应地位的值,依然须要晓得 slice 的底层构造。如图: func main() {    as := []int{10, 5, 8, 7}    p := *(*unsafe.Pointer)(unsafe.Pointer(&as))    fmt.Println("as[0]:", *(*int)(unsafe.Pointer(uintptr(p))))    fmt.Println("as[1]:", *(*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(&as[0]))))    fmt.Println("as[2]:", *(*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(&as[0])*2)))    fmt.Println("as[3]:", *(*int)(unsafe.Pointer(uintptr(p) + unsafe.Sizeof(&as[0])*3)))    var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + uintptr(8)))    fmt.Println("len", Len)     var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&as)) + uintptr(16)))    fmt.Println("cap", Cap) }后果: as[0]: 10as[1]: 5as[2]: 8as[3]: 7len 4cap 4用指针取 slice 的底层 Data 外面的元素跟 array 略微有点不同: 对 slice 变量 as 取地址后,拿到的是 SiceHeader 的地址,对这个指针进行挪动,失去是 slice 的 Data, Len, Cap。所以当拿到 Data 的值时,咱们拿到的是 Data 所指向的 array 的首地址的值。因为这个值是个指针,须要对这个值 *Data, 取到 array 真正的首地址的指针值而后对这个值 &(*Data),获取到真正的首地址,而后对这个值进行指针的挪动,能力获取到 slice 的数组里的值获取 slice cap 和 len: ...

August 24, 2021 · 1 min · jiezi

关于golang:译|There-Are-No-Reference-Types-in-Go

有一天,我用谷歌搜寻一个 Go 问题,谷歌将我疏导到  Go FAQ 页面。问题解决后,我浏览了整个 FAQ。 这是一次很棒的浏览,我从文章中学到了很多。但我留神到一个问题, 为什么数组是值,而 map、slice 和 channel 是援用?回答如下: 此话题历史长远。在晚期,map 和 channel 都是语法指针,不能申明和应用非指针实例。此外,咱们在全力以赴摸索数组如何工作。最终,咱们认为指针和值的严格拆散使语言更难应用。将这些类型更改为对关联的共享数据结构的援用,就解决了这些问题。扭转给语言减少了一些令人遗憾的复杂性,但却对可用性产生了很大的影响:Go 一经推出,就成为了一种更高效、更难受的语言。令我诧异的是,Go 官网文档仍在应用“援用类型”的概念,因为自 2013 年 4 月 3 日以来,“援用类型”的概念已从 Go 标准中齐全删除。当初 Go 标准中有 10 个“援用”词,没有一个代表“援用类型”的概念。 另一个惊喜是这句话: ...指针和值的严格拆散使该语言更难应用。...此回答将指针和值视为两个不兼容的概念。然而,Go 标准将指针视为非凡值,指针被称为“指针值”。值只是类型的实例。显然,Go 标准中“指针”一词的定义很好。我认为如果应用“指针值和非指针值”会更好。 所以,我认为此回答给 Go 社区带来了很多困惑。它与以后 Go 标准抵触,并且突破了概念的一致性。 谈回第一个惊喜,我认为称说   map/slice/channel 值为援用值齐全没有必要。不仅因为 “reference” 这个词在编程世界中被滥用了,还因为 map/slice/channel 值只是一般的正常值 以下是 map/slice/channel 类型的外部申明: Type FamilyType Declarationmapstruct { m *internalHashtable }channelstruct { c *internalChannel }slicestruct { array *internalArray; len   int; cap   int }请留神,下面的申明可能不齐全与官网或非官方的 Go 实现中的申明雷同。Go 实现能够间接应用指针示意 map 和 channel 的值,但 Go 标准/编译器永远不会将它们视为指针。因而,你能够释怀的将 map/slice/channel 类型视为下面申明的指针包装类型,而不会有任何问题。 ...

August 24, 2021 · 1 min · jiezi

关于golang:Go微服务入门到容器化实践

download:Go微服务入门到容器化实际1.什么是spring boot 简略的说,spring boot就是整合了很多优良的框架,不必咱们本人手动的去写一堆xml配置而后进行配置。 从实质上来说,Spring Boot就是Spring,它做了那些没有它你也会去做的Spring Bean配置。它应用“习惯优于配置”(我的项目中存在大量的配置,此外还内置了一个习惯性的配置,让你无需手动进行配置)的理念让你的我的项目疾速运行起来。使 用Spring Boot很容易创立一个独立运行(运行jar,内嵌Servlet容器)、准生产级别的基于Spring框架的我的项目,应用Spring Boot你能够不必或者只须要很少的Spring配置。 Spring Boot精要Spring将很多魔法带入了Spring应用程序的开发之中,其中最重要的是以下四个外围。 主动配置:针对很多Spring应用程序常见的利用性能,Spring Boot能主动提供相干配置起步依赖:通知Spring Boot须要什么性能,它就能引入须要的库。命令行界面:这是Spring Boot的可选个性,借此你只需写代码就能实现残缺的应用程序,无需传统我的项目构建。Actuator:让你可能深刻运行中的Spring Boot应用程序,一探到底。 动静语言:不须要编译,间接运行,比方JS。 动态语言:先编译再运行。 配置指的是xml配置,低下的开发效率指的是编写完代码之后须要进行大量的xml配置,然而单从写代码而言,Java开发效率还是比拟高的,因为有许多写好的第三方jar包。 微服务:将子系统拆成一个一个的jar包运行就是微服务。 2.优缺点 ---------------------------第二种意识------------------------- 1 . springboot简略介绍(http://projects.spring.io/spr...) 当初的web我的项目简直都会用到spring框架,而要应用spring不免须要配置大量的xml配置文件,而 springboot的呈现解 决了这一问题,一个我的项目甚至不必部署到服务器上间接开跑,真像springboot所说:“just run”。 springboot的很多默认编码方式都是utf-8,真是福利啊。org.spring 2013年新开发的框架springboot , 它让一个独自我的项目的创立变得更加的简略,让所有依赖spring的程序能够做到“just run”。springboot提供大量第三方libraries让咱们能够十分轻松的开始创立一个spring工程,甚至不须要再去配置一些繁琐的 xml配置文件框架特点:1:创立独立的spring利用。2:嵌入Tomcat, Jetty Undertow 而且不须要部署他们。3:提供的“starters”poms来简化Maven配置4:尽可能主动配置spring利用。5:提供生产指标,强壮检查和内部化配置6:相对没有代码生成和XML配置要求2.SpringBoot运行环境Spring Boot最新版能够运行在Java6+的环境下,然而Spring官网倡议应用Java8。 Servlet 容器 Name Servlet Version Java VersionTomcat 8 3.1 Java 7+Tomcat 7 3.0 Java 6+Jetty 9 3.1 Java 7+Jetty 8 3.0 Java 6+Undertow 1.1 3.1 Java 7+3.pringBoot反对哪些利用应用SpringBoot能够疾速创立一般Java我的项目和Web我的项目,以及其余我的项目。 ...

August 23, 2021 · 2 min · jiezi

关于golang:golang-系列context-详解

摘要在很多的 Go 开源框架里,咱们常常能看到 context 的身影,它的应用场景有很多,像超时告诉,勾销告诉都用到了 context。明天咱们就来好好的认识一下它,看看 context 的相干常识和底层原理。 context 介绍context 从它的字面量就可以看进去,是用来传递信息的。当然,这种传递并不仅仅是将数据塞给被调用者,它还能进行链式的传递,通过保留父子 context 关系,一直的迭代遍历来获取数据。 除此之外,context 还能进行链式的流传 channel 信号。 咱们晓得 channel 是用来做 goroutine 通信应用的。这就使得 goroutine 之间可能进行链式的信号告诉了,进而达到自上而下的告诉成果。 例如告诉所有跟 context 有血统关系的 goroutine 进行勾销动作。 Context 接口在 Go 里并没有间接为咱们提供一个对立的 context 对象,而是设计了一个接口类型的 Context。而后在这些接口上来实现了几种具体类型的 context。 这样的益处就是咱们只有依据凋谢进去的接口定义,也可能实现属于本人的 context,进而跟官网的 context 一起配合应用。 在剖析官网的几种 context 之前,咱们先来看看 context 要求实现的几个接口: Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key interface{}) interface{}其中: Deadline() 示意如果有截止工夫的话,得返回对应 deadline 工夫;如果没有,则 ok 的值为 false。 Done() 示意对于 channel 的数据通信,而且它的数据类型是 struct{},一个空构造体,因而在 Go 里都是间接通过 close channel 来进行告诉的,不会波及具体数据传输。 ...

August 22, 2021 · 2 min · jiezi

关于golang:Go中的channel怎么实现的

概述置信大家在开发的过程中常常会应用到go中并发利器channel,channel 是CSP并发模型中最重要的一个组件,两个独立的并发实体通过共享的通信channel进行通信。大多数人只是会用这么个构造很少有人探讨它底层实现,这篇文章讲写写channel的底层实现。 channelchannel的底层实现是一个构造体,源代码如下: 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}可能看源代码不是很好看得懂,这里我集体画了一张图不便大家查看,我在下面标注了不同色彩,并且正文其作用。 ...

August 22, 2021 · 3 min · jiezi

关于golang:源码剖析goframe的平滑重启并不平滑

首先说一下平滑重启的定义:优雅的重启服务,重启过程不会中断曾经沉闷的链接。咱们熟知的nginx reload、php-fpm reload都是平滑重启,重启时,正在进行的申请仍然能执行上来,直到超过指定的超时工夫,而这里特地提一下php-fpm reload有个坑,它默认不是平滑重启的,因为process_control_timeout(设置子过程承受主过程复用信号的超时工夫)的配置默认为0秒,代表超时工夫为0,这就导致php-fpm reload都会中断请求,也不晓得为什么官网要把默认值设置为0秒。 而后这篇文章要讲的就是goframe的平滑重启其实是"假平滑",重启过程中会有一部分申请中断。 官网的平滑重启文档在这里:https://goframe.org/pages/vie... 1、测试环境版本:gf v1.16.5go 1.15centos 2核cpu 64位 2、先来试验一下文档中实例1的代码 package mainimport ( "time" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gproc" "github.com/gogf/gf/net/ghttp")func main() { s := g.Server() s.BindHandler("/", func(r *ghttp.Request){ r.Response.Writeln("哈喽!") }) s.BindHandler("/pid", func(r *ghttp.Request){ r.Response.Writeln(gproc.Pid()) }) s.BindHandler("/sleep", func(r *ghttp.Request){ r.Response.Writeln(gproc.Pid()) time.Sleep(10*time.Second) r.Response.Writeln(gproc.Pid()) }) s.EnableAdmin() s.SetPort(8999) s.Run()}config.toml配置 [server] Graceful = true3、执行 go build main.go && ./main 4、这时候咱们失常申请sleep接口,在新窗口同时申请restart进行“平滑重启”,会看到申请被中断了 curl 127.0.0.1:8999/sleepcurl 127.0.0.1:8999/debug/admin/restart 5、而如果咱们把代码中的10秒改成2秒,这就有概率不中断请求失常平滑重启,这取决与你试验的手速,这是为什么呢,接下来咱们剖析一下ghttp源码 s.BindHandler("/sleep", func(r *ghttp.Request){ r.Response.Writeln(gproc.Pid()) time.Sleep(2*time.Second) r.Response.Writeln(gproc.Pid()) })6、定位到ghttp_server.go的195行(大略地位)重启的时候会创立子过程,子过程启动http server之后会在2秒后向父过程发消息(adminGProcCommGroup),告诉父过程退出,并且重启过程中,因为父过程没有马上进行接管新申请,就导致还会有局部申请进入到父过程中,所以把2改大也不事实,也就是说即便你的申请耗时100ms,也可能执行不完。 // If this is a child process, it then notifies its parent exit. if gproc.IsChild() { gtimer.SetTimeout(2*time.Second, func() { if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil { //glog.Error("server error in process communication:", err) } }) }7、adminGProcCommGroup音讯的监听在ghttp_server_admin_process.go的256行能够看到最初会调用shutdownWebServersGracefully(),咱们再找到这个办法 ...

August 21, 2021 · 2 min · jiezi

关于golang:go语言之consul

装置version: "3.1"services: consul: image: consul restart: always container_name: consul ports: - 8500:8500 - 8300:8300 - 8301:8301 - 8302:8302 - 8600:8600/udp volumes: - ./data:/consul/data - ./data:/consul/config command: agent -dev -client=0.0.0.0测试是否装置胜利1.UI界面关上HOSTNAME:8500能够进入到consul的UI页面 2.shell测试端口$ dig @HOSTNAME -p 8600 consul.service.consul SRV# linux/mac下间接应用dig命令测试consul是否正确装置, HOSTNAMET替换对应地址应用服务注册接口(PUT): http://192.168.4.5:8500/v1/ag...参数: { "Name": "order-server", // 服务名称 "ID": "order", // 服务ID "Tags": ["mxshop", "Felix", "good"], // 标签 "Address": "192.168.4.5", "Port": 50051}服务登记接口(PUT): http://192.168.4.5:8500/v1/ag...("order是服务ID") 健康检查

August 21, 2021 · 1 min · jiezi

关于golang:Go-让-Apache-APISIX-如虎添翼

为什么是 GoApache APISIX 容许用户通过插件的形式来拓展性能,如鉴权、限流、申请改写等外围性能都是通过插件的形式实现的。尽管 Apache APISIX 外围代码是应用 Lua 编写的,然而 Apache APISIX 反对多语言开发插件,比方 Go 、Java。 这篇文章将具体解说如何用 Go 来开发 Apache APISIX 插件。通过拥抱 Go 的生态圈,为 Apache APISIX 创始一片新天地,心愿 Go 能让 Apache APISIX 锦上添花! 装置采纳库的形式来应用 Go Runner,apisix-go-plugin-runner 中的 cmd/go-runner 官网给出的例子,展现该如何应用 Go Runner SDK。将来也会反对通过 Go Plugin 的机制加载事后编译好的插件。 开发应用 Go Runner SDK 进行开发$ tree cmd/go-runnercmd/go-runner├── main.go├── main_test.go├── plugins│ ├── say.go│ └── say_test.go└── version.go下面是官网示例的目录构造。main.go 是入口,其中最要害的局部在于: cfg := runner.RunnerConfig{}...runner.Run(cfg)RunnerConfig 能够用来管制日志等级和日志输入地位。 runner.Run 会让利用监听指标地位,接管申请并执行注册好的插件。利用会始终处于这一状态直到退出。 关上 plugins/say.go: func init() { err := plugin.RegisterPlugin(&Say{}) if err != nil { log.Fatalf("failed to register plugin say: %s", err) } }因为 main.go 导入了 plugins 包, ...

August 20, 2021 · 3 min · jiezi

关于golang:golang-系列atomic-原子操作

sync/atomic 介绍当咱们想要对某个变量并发平安的批改,除了应用官网提供的 mutex,还能够应用 sync/atomic 包的原子操作,它可能保障对变量的读取或批改期间不被其余的协程所影响。 atomic 包的原子操作是通过 CPU 指令,也就是在硬件档次去实现的,性能较好,不须要像 mutex 那样记录很多状态。 当然,mutex 不止是对变量的并发管制,更多的是对代码块的并发管制,2 者侧重点不一样。 sync/atomic 操作atomic 包有几种原子操作,次要是 Add、CompareAndSwap、Load、Store、Swap。 Addatomic 的 Add 是针对 int 和 uint 进行原子加值的: func AddInt32(addr *int32, delta int32) (new int32)func AddUint32(addr *uint32, delta uint32) (new uint32)func AddInt64(addr *int64, delta int64) (new int64)func AddUint64(addr *uint64, delta uint64) (new uint64)func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)CompareAndSwap比拟并替换办法实现了相似乐观锁的性能,只有原来的值和传入的 old 值一样,才会去批改: func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)须要留神的是,CompareAndSwap 有可能产生 ABA 景象产生。也就是原来的值是 A,前面被批改 B,再前面批改为 A。在这种状况下也合乎了 CompareAndSwap 规定,即便中途有被改变过。 ...

August 19, 2021 · 2 min · jiezi

关于golang:vscode的remote模式下环境变量

vscode的终端go的环境变量默认应用的是 ~/.bashrcexport GO111MODULE=autoexport PATH=$PATH:/usr/local/go/bin来配置的,放在其余中央可能应用没失效

August 19, 2021 · 1 min · jiezi

关于golang:Go开发工程师迎接上升风口踏入蓝海行业完结

download:Go开发工程师:迎接回升风口,踏入蓝海行业!【完结】include<iostream.h>typedef int Status;typedef char Cstack; define OK 1define ERROR 0typedef struct StackNode{ Cstack data;struct StackNode *next;}StackNode,*LinkStack; Status InitStack(LinkStack &S){ S=NULL;return OK;}Status Push(LinkStack &S,Cstack e){ StackNode *p;p=new StackNode;p->data=e;p->next=S;S=p;return OK;}Status Pop(LinkStack &S,Cstack &e){ StackNode *p;if(S==NULL) return ERROR;e=S->data;p=S;S=S->next;delete p;return OK;}Cstack GetTop(LinkStack S){ if(S!=NULL)return S->data;}Status In(Cstack ch){ cin>>ch;if(ch=='+') return OK;else if(ch=='-') return OK;else if(ch=='*') return OK;else if(ch=='/') return OK;else if(ch=='#') return OK;else return ERROR;}Cstack Precede(Cstack t1,Cstack t2){ switch(t1){case '+': switch(t2) { case '+':return '>';break; case '-':return '>';break; case '*':return '<';break; case '/':return '<';break; case '(':return '<';break; case ')':return '>';break; case '#':return '>';break; } break; case '-': switch(t2) { case '+':return '>';break; case '-':return '>';break; case '*':return '<';break; case '/':return '<';break; case '(':return '<';break; case ')':return '>';break; case '#':return '>';break; } break; case '*': switch(t2) { case '+':return '>';break; case '-':return '>';break; case '*':return '>';break; case '/':return '>';break; case '(':return '<';break; case ')':return '>';break; case '#':return '>';break; } break; case '/': switch(t2) { case '+':return '>';break; case '-':return '>';break; case '*':return '>';break; case '/':return '>';break; case '(':return '<';break; case ')':return '>';break; case '#':return '>';break; } break; case '(': switch(t2) { case '+':return '<';break; case '-':return '<';break; case '*':return '<';break; case '/':return '<';break; case '(':return '<';break; case ')':return '=';break; case '#':return '>';break; } break; case ')': switch(t2) { case '+':return '>';break; case '-':return '>';break; case '*':return '>';break; case '/':return '>';break; case '(':return '=';break; case ')':return '>';break; case '#':return '>';break; } break; case '#': return '='; break;}}Cstack Operator(Cstack t1,Cstack t2,Cstack t3){ ...

August 19, 2021 · 2 min · jiezi

关于golang:golang-系列syncCond-机制

前言在 Go 里有专门为同步通信而生的 channel,所以较少看到 sync.Cond 的应用。不过它也是并发管制伎俩里的一种,明天咱们就来意识下它的相干实现,加深对同步机制的使用。 sync.Condsync.Cond 提供了三个办法:Wait()、Signal()、Broadcast(),它们的用法如下: Wait():阻塞以后的 goroutine,期待唤起。Signal():唤起一个阻塞的 goroutine。Broadcast():唤起所有阻塞的 goroutine。通过下面的办法形容,咱们就能够简略的实现一个工作池性能:先批量的创立 goroutine,而后调用 sync.Cond 的 Wait() 办法让其阻塞的期待。 当有一个工作到来时,则通过 Signal() 唤起刚刚在阻塞的某一个 goroutine,去执行工作。 通过工作池性能,咱们发现 sync.Cond 的使用很简略,但 Go 官网并不举荐咱们应用 sync.Cond 来实现协程间的同步通信。 因为它并不合乎 Go 官网 “通过通信来共享内存” 的设计思维,当场景简单时,则会耦合各个业务性能。 sync.Cond 源码剖析咱们来看下 sync.Cond 的构造体,代码在 /sr/sync/cond.go 下: type Cond struct { noCopy noCopy // 不可复制 L Locker // 锁 notify notifyList // 告诉唤起列表 checker copyChecker // 复制检测}能够看到 Cond 上有 notify 列表,而这正是保护了须要唤起的 goroutine 列表。 当咱们调用 Wait() 办法的时候就会保护以后 goroutine 到对应的 notifyList 里: ...

August 18, 2021 · 1 min · jiezi

关于golang:自适应负载均衡算法原理与实现

背景在抉择负载平衡算法时,咱们心愿满足以下要求: 具备分区和机房调度亲和性 每次抉择的节点尽量是负载最低的每次尽可能选择响应最快的节点无需人工干预故障节点 当一个节点有故障时,负载平衡算法能够主动隔离该节点当故障节点复原时,可能主动复原对该节点的流量散发基于这些思考,go-zero 抉择了 p2c+EWMA 算法来实现。 算法的核心思想p2cp2c (Pick Of 2 Choices) 二选一: 在多个节点中随机抉择两个节点。 go-zero 中的会随机的抉择3次,如果其中一次抉择的节点的衰弱条件满足要求,就中断抉择,采纳这两个节点。 EWMAEWMA (Exponentially Weighted Moving-Average) 指数挪动加权平均法: 是指各数值的加权系数随工夫呈指数递加,越凑近以后时刻的数值加权系数就越大,体现了最近一段时间内的平均值。 公式: 变量解释: Vt: 代表的是第 t 次申请的 EWMA值Vt-1: 代表的是第 t-1 次申请的 EWMA值: 是一个常量EWMA 算法的劣势相较于一般的计算平均值算法,EWMA 不须要保留过来所有的数值,计算量显著缩小,同时也减小了存储资源。传统的计算平均值算法对网络耗时不敏感, 而 EWMA 能够通过申请频繁来调节 ,进而迅速监控到网络毛刺或更多的体现整体平均值。 当申请较为频繁时, 阐明节点网络负载升高了, 咱们想监测到此时节点解决申请的耗时(侧面反映了节点的负载状况), 咱们就相应的调小。越小,EWMA值 就越靠近本次耗时,进而迅速监测到网络毛刺;当申请较为不频繁时, 咱们就绝对的调大值。这样计算出来的 EWMA值 越靠近平均值计算go-zero 采纳的是牛顿冷却定律中的衰减函数模型计算 EWMA 算法中的 值: 其中 t 为两次申请的距离,e,k 为常数 gRPC 中实现自定义负载均衡器首先咱们须要实现 google.golang.org/grpc/balancer/base/base.go/PickerBuilder 接口, 这个接口是有服务节点更新的时候会调用接口里的Build办法 type PickerBuilder interface { // Build returns a picker that will be used by gRPC to pick a SubConn. Build(info PickerBuildInfo) balancer.Picker}还要实现 google.golang.org/grpc/balancer/balancer.go/Picker 接口。这个接口次要实现负载平衡,筛选一个节点供申请应用 ...

August 18, 2021 · 3 min · jiezi

关于golang:Go-语言没有引用类型指针也与众不同

面向对象编程强调数据和操作绑定,办法是有状态的,自身可能会批改数据。因而编程时确定办法是否会批改原始数据尤其要害。少数从其余语言转到 Go 语言的开发者,都会首先理解 Go 语言传递参数的时候到底是 “传值” 还是 “传援用”。如果第一门开发语言是 C 语言或者 C++ 的开发者,还会辨别下什么时候 “传指针”。 概念什么是内存?能够把内存想想为一系列单元格,一个个排列成一行,每个单元格都有一个惟一的编号。编号程序递增,代表内存的地位,也即是内存地址。 每个单元格都能够寄存一个值,能够通过编号读取和替换单元格内的先前写入的值。 什么是变量?蹩脚的是,如果间接应用编号编程,就须要开发者本人治理内存,也难以和其余程序同时运行,极大的减少了编写大型程序的难度。 为了解决这个问题,就须要引入“变量”的概念。变量只是编号的假名、标签或昵称。 var a = 6var b = a * 3什么是指针?而指针的值是另一个变量的编号。指针指向变量的内存地址,就像变量代表值的内存地址一样。 func main() { a := 200 b := &a *b++ fmt.Println(a)} 什么是援用?在 C++ 语言中,为现有变量申明别名,称为援用变量。如代码所示,a、b、c 三个变量均共享同一内存地址 #include <stdio.h>int main() { int a = 10; int &b = a; int &c = b; printf("%p %p %p\n", &a, &b, &c); // 0x7ffe114f0b14 0x7ffe114f0b14 0x7ffe114f0b14 return 0;}Go 语言没有援用类型Go 程序能够创立指向对立内存地址的变量,但无奈创立共享雷同内存地址的两个变量。如代码所示,b 和 c 具备雷同的值(a的地址)然而,b 和 c 的内存地址是惟一的。更新 b 的内容对 c 没有影响。 ...

August 18, 2021 · 2 min · jiezi

关于golang:Go-117-正式发布

新版本的编译器采纳了一种新的函数参数和后果传递形式。官网称此次改良将 Go 程序的性能晋升了大概 5%,并将 amd64 平台的二进制包大小缩小了大概 2%,将来还打算反对更多平台。 新版本减少了对 Windows 上 64 位 ARM 架构的反对,让 Go 开发者可能在更多设施上原生运行 Go。 新版本减少了 pruned module graphs 性能。官网对此性能的形容为,当 Modules 在其go.mod文件中指定了 go 1.17或更高版本,其 module graph 只包含其余 Go 1.17 模块的间接依赖,而不是其全副的横向依赖。这将有助于防止 go.mod 为其余不相干的依赖下载或读取文件,从而在日常开发中节省时间。 语言方面,Go 1.17 蕴含三个针对语言个性的改良: 反对从 slice (切片)到数组指针的转换unsafe.Add:unsafe.Add(ptr, len)将 len 增加 ptr 并返回更新的指针unsafe.Pointer(uintptr(ptr) + uintptr(len))unsafe.Slice : 对于*T 类型的 ptr 表达式,unsafe.Slice(ptr, len) 返回一个[] T 类型的切片,其底层数组从 ptr 开始, 其长度和容量为 len

August 18, 2021 · 1 min · jiezi

关于golang:Golang详解内存对齐

原文链接:详解内存对齐 前言哈喽,大家好,我是asong。好久不见,上周停更了一周,因为工作有点忙,好在这周末闲了下来,就连忙来肝文喽。明天咱们来聊一聊一道常见的面试八股文——内存对齐,咱们平时在业务开发中基本不care内存对齐,然而在面试中,这就是一个高频考点,明天咱们就一起来看一看到底什么是内存对齐。前情概要在理解内存对齐之前,先来明确几个对于操作系统的概念,更加方面咱们对内存对齐的了解。 内存治理:咱们都晓得内存是计算中重要的组成之一,内存是与CPU进行沟通的桥梁,用于暂存CPU中的运算数据、以及与硬盘等内部存储器替换的数据。晚期,程序是间接运行在物理内存上的,间接操作物理内存,然而会存在一些问题,比方应用效率低、地址空间不隔离等问题,所以就呈现了虚拟内存,虚拟内存就是在程序和物理内存之间引入了一个中间层,这个中间层就是虚拟内存,这样就达到了对过程地址和物理地址的隔离。在linux零碎中,将虚拟内存划分为用户空间和内核空间,用户过程只能拜访用户空间的虚拟地址,只有通过零碎调用、外设中断或异样能力拜访内核空间,咱们次要来看一下用户空间,用户空间被分为5个不同内存区域: 代码段:寄存可执行文件的操作指令,只读数据段:用来寄存可执行文件中已初始化全局变量,寄存动态变量和全局变量BSS段:用来存未初始化的全局变量栈区:用来存长期创立的局部变量堆区:用来存动态分配的内存段 内存的常识先介绍个大略,对于本文的了解应该够了,咱们接着介绍操作系统几个其余概念。 CPU:地方处理单元(Cntral Pocessing Unit)的缩写,也叫处理器;CPU是计算机的运算外围和管制外围,咱们人类靠着大脑思考,电脑就是靠着CPU来运算、管制,起到协调和管制作用,从性能来看,CPU 的外部由寄存器、控制器、运算器和时钟四局部组成,各局部之间通过电信号连通。CPU和内存的工作关系:当咱们执行一个程序时,首先由输出设施向CPU收回操作指令,CPU接管到操作指令后,硬盘中对应的程序就会被间接加载到内存中,尔后,CPU 再对内存进行寻址操作,将加载到内存中的指令翻译进去,而后发送操作信号给操作控制器,实现程序的运行或数据的解决。存在于内存中的目标就是为了CPU可能过总线进行寻址,取指令、译码、执行取数据,内存与寄存器交互,而后CPU运算,再输入数据至内存。 os:os全称为Operating System,也就是操作操作系统,是一组主管并管制计算机操作、使用和运行硬件、软件资源和提供公共服务组织用户交互的互相关联的系统软件,同时也是计算机系统的内核与基石。编译器:编译器就是将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”的程序。一个古代编译器的次要工作流程:源代码 (source code) → 预处理器(preprocessor) → 编译器 (compiler) → 指标代码 (object code) → 链接器 (Linker) → 可执行程序(executables)。写在最初的一个知识点: 计算机中,最小的存储单元为字节,实践上任意地址都能够通过总线进行拜访,每次寻址能传输的数据大小就跟CPU位数无关。常见的CPU位数有8位,16位,32位,64位。位数越高,单次操作执行的数据量越大,性能也就越强。os的位数个别与CPU的位数相匹配,32位CPU能够寻址4GB内存空间,也能够运行32位的os,同样情理,64位的CPU能够运行32位的os,也能够运行64位的os。何为内存对齐以下内容来源于网络总结: 古代计算机中内存空间都是依照字节(byte)进行划分的,所以从实践上讲对于任何类型的变量拜访都能够从任意地址开始,然而在理论状况中,在拜访特定类型变量的时候常常在特定的内存地址拜访,所以这就须要把各种类型数据依照肯定的规定在空间上排列,而不是依照程序一个接一个的排放,这种就称为内存对齐,内存对齐是指首地址对齐,而不是说每个变量大小对齐。为何要有内存对齐次要起因能够归结为两点: 有些CPU能够拜访任意地址上的任意数据,而有些CPU只能在特定地址拜访数据,因而不同硬件平台具备差异性,这样的代码就不具备移植性,如果在编译时,将调配的内存进行对齐,这就具备平台能够移植性了CPU每次寻址都是要生产工夫的,并且CPU 拜访内存时,并不是一一字节拜访,而是以字长(word size)为单位拜访,所以数据结构应该尽可能地在天然边界上对齐,如果拜访未对齐的内存,处理器须要做两次内存拜访,而对齐的内存拜访仅须要一次拜访,内存对齐后能够晋升性能。举个例子:假如以后CPU是32位的,并且没有内存对齐机制,数据能够任意寄存,当初有一个int32变量占4byte,寄存地址在0x00000002 - 0x00000005(纯假如地址,莫当真),这种状况下,每次取4字节的CPU第一次取到[0x00000000 - 0x00000003],只失去变量1/2的数据,所以还须要取第二次,为了失去一个int32类型的变量,须要拜访两次内存并做拼接解决,影响性能。如果有内存对齐了,int32类型数据就会依照对齐规定在内存中,下面这个例子就会存在地址0x00000000处开始,那么处理器在取数据时一次性就能将数据读出来了,而且不须要做额定的操作,应用空间换工夫,进步了效率。没有内存对齐机制: 内存对齐后: 对齐系数每个特定平台上的编译器都有本人的默认"对齐系数",罕用平台默认对齐系数如下: 32位零碎对齐系数是464位零碎对齐系数是8这只是默认对齐系数,实际上对齐系数咱们是能够批改的,之前写C语言的敌人晓得,能够通过预编译指令#pragma pack(n)来批改对齐系数,因为C语言是预处理器的,然而在Go语言中没有预处理器,只能通过tags和命名约定来让Go的包能够治理不同平台的代码,然而怎么批改对齐系数,感觉Go并没有凋谢这个参数,找了良久没有找到,等前面再认真看看,找到了再来更新! 既然对齐系数无奈更改,然而咱们能够查看对齐系数,应用Go语言中的unsafe.Alignof能够返回相应类型的对齐系数,应用我的mac(64位)测试后发现,对齐系数都合乎2^n这个法则,最大也不会超过8。 func main() { fmt.Printf("string alignof is %d\n", unsafe.Alignof(string("a"))) fmt.Printf("complex128 alignof is %d\n", unsafe.Alignof(complex128(0))) fmt.Printf("int alignof is %d\n", unsafe.Alignof(int(0)))}运行后果string alignof is 8complex128 alignof is 8int alignof is 8留神:不同硬件平台占用的大小和对齐值都可能是不一样的。 ...

August 17, 2021 · 2 min · jiezi

关于golang:golang-系列syncOnce-讲解

sync.Once 介绍之前提到过 Go 的并发辅助对象:WaitGroup。同样的, sync.Once 也是 Go 官网的一并发辅助对象,它可能让函数办法只执行一次,达到相似 init 函数的成果。咱们来看看它的简略用法: func main() { var once sync.Once onceFunc := func() { fmt.Println("Only once") } for i := 0; i < 10; i++ { once.Do(onceFunc) }}这里执行后咱们将只看到一次 Only once 的打印信息,这就是 sync.Once 的一次性成果。 sync.Once 源码咱们来看下 sync.Once 的源码: type Once struct { done uint32 m Mutex}func (o *Once) Do(f func()) { // 原子加载标识值,判断是否已被执行过 if atomic.LoadUint32(&o.done) == 0 { o.doSlow(f) }}func (o *Once) doSlow(f func()) { // 还没执行过函数 o.m.Lock() defer o.m.Unlock() if o.done == 0 { // 再次判断下是否已被执行过函数 defer atomic.StoreUint32(&o.done, 1) // 原子操作:批改标识值 f() // 执行函数 }}从下面能够剖析出,sync.Once 是通过对一个标识值,原子性的批改和加载,来缩小锁竞争的。 ...

August 16, 2021 · 1 min · jiezi

关于golang:GO进阶训练营完结

download:GO进阶训练营【完结】读写更便利 public class PersistenceUtil { private Context context; public Context getContext() { return context; } public void setContext(Context context) { this.context = context; } public PersistenceUtil(Context context) { this.context = context; } public void savePersistence(String name, String key, Integer value) { SharedPreferences sharedPreferences = context.getSharedPreferences( name, Context.MODE_PRIVATE); Editor editor = sharedPreferences.edit(); editor.putInt(key, value); editor.commit(); } public void savePersistence(String name, String key, String value) { SharedPreferences sharedPreferences = context.getSharedPreferences( name, Context.MODE_PRIVATE); Editor editor = sharedPreferences.edit(); editor.putString(key, value); editor.commit(); } public void savePersistence(String name, String key, Boolean value) { SharedPreferences sharedPreferences = context.getSharedPreferences( name, Context.MODE_PRIVATE); Editor editor = sharedPreferences.edit(); editor.putBoolean(key, value); editor.commit(); } public String getPersistenceString(String name, String key) { SharedPreferences sharedPreferences = context.getSharedPreferences( name, Context.MODE_PRIVATE); String value = sharedPreferences.getString(key, "null"); return value; } public Boolean getPersistenceBoolean(String name, String key) { SharedPreferences sharedPreferences = context.getSharedPreferences( name, Context.MODE_PRIVATE); Boolean value = sharedPreferences.getBoolean(key, false); return value; } public Integer getPersistenceInteger(String name, String key) { SharedPreferences sharedPreferences = context.getSharedPreferences( name, Context.MODE_PRIVATE); Integer value = sharedPreferences.getInt(key, -1); return value; } } ...

August 16, 2021 · 1 min · jiezi

关于golang:Go语言如何通过Go来更好的开发并发程序

并行、并发并行和并发的区别: 并行:两个或多个程序在同一时刻执行。并发:两个或多个程序在同一个时间段内执行。并行执行的程序,在同一时刻,是真真正正的有多个程序在 CPU 上执行,这也就须要 CPU 提供多核计算的能力。而并发执行的程序,只是在宏观的角度观察到有多个程序在 CPU 上执行,宏观上是它们在 CPU 上被疾速轮换执行。 对于过程、线程、协程,并发、并行,在我之前的文章中讲并发把握时也有介绍过,感兴趣的能够过来瞅一眼。传送门在此Go通关09:并发把握,goroutine和channel申明与应用! Go 的 MPG 线程模型之所以 Go 被认为是高性能开发语言,在于它在原生态反对协程并发。协程是一种用户线程,是轻量级线程。协程的调度齐全取决于用户空间的代码管制。 协程领有本人的寄存器上下文和栈,并存储在用户空间,协程在切换时无需切换到内核态来拜访内核空间,切换速度极快。开发人员须要在用户空间解决协程切换时候的上下文信息的保留和复原、栈空间大小的治理等技术问题。 Go语言采纳了一种非凡的两级线程模型,即 MPG 线程模型: M,即 machine,相当于内核线程在 Go 过程中的映射,它与内核线程一一对应,代表真正执行计算的资源。M 的生命周期内,只会与一个内核线程相关联。P,即 processor,代表 Go 代码片段执行所需的上下文环境。 M 和 P 的联合能够为 G 提供无效的运行环境。它们之间的联合关系不是固定的。P 的最大数量决定了 Go 程序的并发规模,由 runtime.GOMAXPROCS 变量来决定。G,即 goroutine,是一种轻量级的用户线程,是对代码片段的封装,领有执行时的栈、状态和代码片段等信息。在理论执行过程中,多个可执行的 G 会程序挂载在 P 的可执行 G 队列上面,期待调度和指向。当 G 中存在一些 I/O 零碎调用阻塞了 M 时,P 会断开 M 的分割,从调度器闲暇 M 队列中获取一个 M 或创立一个新的 M 进行组合执行,从而保障 P 中可执行 G 队列中其余 G 失去执行,因为程序中并行执行的 M 数量没有变,所以程序的 CPU 有很高的利用率。 ...

August 16, 2021 · 2 min · jiezi

关于golang:golang-系列waitgroup-解析

摘要Golang 提供了简洁的 go 关键字来让开发者更容易的进行并发编程,同时也提供了 WaitGroup 对象来辅助并发管制。明天咱们就来剖析下 WaitGroup 的应用办法,顺便瞧一瞧它的底层源码。 WaitGroup 的应用场景和办法当咱们有很多工作要同时进行时,如果并不需要关怀各个工作的执行进度,那间接应用 go 关键字即可。 如果咱们须要关怀所有工作实现后能力往下运行时,则须要 WaitGroup 来阻塞期待这些并发工作了。 WaitGroup 如同它的字面意思,就是期待一组 goroutine 运行实现,次要有三个办法组成: Add(delta int) :增加工作数Wait():阻塞期待所有工作的实现Done():实现工作上面是它们的具体用法,具体的作用都在正文上: package mainimport ( "fmt" "sync" "time")func worker(wg *sync.WaitGroup) { doSomething() wg.Done() // 2.1、实现工作}func main() { var wg sync.WaitGroup wg.Add(5) // 1、增加 5 个工作 for i := 1; i <= 5; i++ { go worker(&wg) // 2、每个工作并发执行 } wg.Wait() // 3、阻塞期待所有工作实现}WaitGroup 源码剖析下面 WaitGroup 的应用很简略,接下来咱们到 src/sync/waitgroup.go 里剖析下它的源码。首先,是 WaitGroup 的构造体: ...

August 15, 2021 · 3 min · jiezi

关于golang:goeventbus事件总线

go-eventbus事件总线地址: github.com/lockp111/go-eventbus 参考go-observable, 然而那个代码有问题, 会因为map同时读写呈现crash, 我修复之后提交给作者也始终不理我, 于是就本人fork一份自用, 起初感觉反射调用的损耗还是比拟大的, 而且应用func如果不留神, 有可能呈现两个雷同的func, 于是改成interface, Benchmark显示晋升十分大 Usagego get -u github.com/lockp111/go-eventbusNew()Create a new bus struct reference bus := eventbus.New()On(topic string, e ...Event)Subscribe event type ready struct{}func (e ready) Dispatch(msg interface{}){ fmt.Println("I am ready!")}bus.On("ready", &ready{})You can also subscribe multiple events for example: type run struct{}func (e run) Dispatch(msg interface{}){ fmt.Println("I am run!")}bus.On("ready", &ready{}, &ready{}).On("run", &run{})Off(topic string, e ...Event)Unsubscribe event e := &ready{}bus.On("ready", e)bus.Off("ready", e)You can also unsubscribe multiple events for example: ...

August 14, 2021 · 1 min · jiezi

关于golang:golangvue3开发的一个im应用

这是一個开源的前后端拆散的IM网页利用。 简略的性能[x] 反对微博登录[x] 端对端音讯推送、图片发送、表情包[x] 语音性能[ ] 视频性能[x] 反对离线音讯推送[ ] 创立群聊[ ] 群聊音讯推送[ ] 好友性能[x] 响应式的前端界面反对pc与h5【仿网页微信UI】线上地址:https://im.pltrue.top/开源了服务端:https://github.com/pl1998/go-im web端:https://github.com/pl1998/web... 有趣味能够点个star,前面会缓缓欠缺。

August 14, 2021 · 1 min · jiezi

关于golang:JWT生成与解析

jwt生成须要用到:github.com/dgrijalva/jwt-go包 创立构造体import ( "crypto/md5" "encoding/hex" "strconv" "time" "github.com/dgrijalva/jwt-go")type Claims struct { Flag string jwt.StandardClaims}生成token。生成token须要用到用户惟一标识,应用md5依据id生成惟一标识,不能应用用户名和明码去生成token,这会造成不平安。// GenerateToken:生成jwtfunc GenerateToken(userID int64) (string, error) { tokenConfig := config.Token ex := tokenConfig.ExpiredTime flag := GenerateUserFlag(userID) expiredTime := time.Now().Add(time.Duration(ex) * time.Second) claims := &Claims{ Flag: flag, StandardClaims: jwt.StandardClaims{ ExpiresAt: expiredTime.Unix(), // 过期工夫 IssuedAt: time.Now().Unix(), // 颁发工夫 Id: flag, // 编号 Issuer: tokenConfig.Issue, // 颁发者 NotBefore: time.Now().Unix(), // 失效工夫 Subject: "user token", // token主题 }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString([]byte(tokenConfig.Salt)) if err != nil { return "", err } return tokenString, nil}// GenerateUserFlag: 依据id应用md5生成用户惟一标识func GenerateUserFlag(userID int64) string { md5 := md5.New() md5.Write([]byte(strconv.FormatInt(userID, 10))) return hex.EncodeToString(md5.Sum([]byte("")))}须要留神的是:jwt..NewWithClaims的第一个参数必须是jwt.SigningMethodHS256,否则会报key is valid的谬误。解析Token// ParseToken: 解析tokenfunc ParseToken(token string) (*Claims, error) { tokenConfig := config.Token tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) ( interface{}, error) { return []byte(tokenConfig.Salt), nil }) if tokenClaims != nil { if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid { return claims, nil } } return nil, err}

August 14, 2021 · 1 min · jiezi

关于golang:rosedb-事务实践

一、前言事务是传统关系型数据库中必不可少的性能,例如 Mysql、Oracle、PostgreSql 都反对事务,然而在 NoSQL 数据库中,事务的概念比拟弱化,在实现上也没有关系型数据库那么简单。 然而为了数据的残缺一致性,大多数 k-v 都会实现事务的根本个性,例如 k-v 数据库的两大鼻祖 LevelDB 和 RocksDB,一些 Go 语言实现的开源 k-v 也都反对事务,例如 Bolt,Badger 等。 rosedb 的事务目前刚实现了一个高级的版本,代码还比较简单,只不过在我的预期构思内,后续可能会缓缓演变得更加简单。 须要阐明的是,在实现 rosedb 的事务之前,我对事务的了解也仅限于 ACID 这些根底概念,所以这次实现齐全是摸着石头过河,可能存在一些槽点,大家有什么疑难能够指出来,我前面也会持续学习并欠缺。 二、基本概念说到事务,就很容易想到事务的 ACID 个性,带大家回顾一下: 原子性(Atomicity):一个事务中的所有操作,要么全副实现,要么全副失败,不会在中间环节完结。如果事务执行过程中产生谬误,可能被回滚至事务开始之前的状态。一致性(Consistency):在事务开始前和完结后,数据库的完整性没有被毁坏,这意味着数据状态始终合乎预期。隔离性(Isolation):隔离性形容的是多个执行中的事务相互影响的水平,有常见的四种隔离级别,示意事务之间不同的影响水平: 读未提交(read uncommitted):一个事务还未提交,另一个事务就能看到它所做的批改(存在脏读)读提交(read committed):一个事务对数据的批改,只能等到它提交之后,其余事务能力看到(没有脏读,然而不可反复读)可反复读(repeatable read):一个事务在执行过程中获取到的数据,和事务开始时的数据统一(没有脏读,能够反复读,然而有幻读)串行化(serializable):读写互斥,防止事务并发,一个事务必须等到前一个事务提交后能力执行(无脏读,可反复读,无幻读)持久性(Durability):一个事务提交之后,它所做的批改是永恒的,即便数据库解体之后也可能保障平安。ACID 的概念看起来挺多,但并不难理解,要实现事务,其实就是保障在数据读写时,满足事务的这几个基本概念,其中 AID 是必须保障的。 而 Consistency 即一致性,能够简略了解为它就是事务的最终目标,数据库通过 AID 来保障一致性,而咱们在利用层面也要保障一致性,如果咱们写入的数据自身逻辑上就是谬误的,那么即便数据库事务再欠缺,也无奈保障一致性。 三、具体实现在解说事务实现之前,先来看看 rosedb 当中事务的根本用法: // 关上数据库实例db, err := rosedb.Open(rosedb.DefaultConfig())if err != nil { panic(err)}// 在事务中操作数据err = db.Txn(func(tx *Txn) (err error) { err = tx.Set([]byte("k1"), []byte("val-1")) if err != nil { return } err = tx.LPush([]byte("my_list"), []byte("val-1"), []byte("val-2")) if err != nil { return } return})if err != nil { panic(fmt.Sprintf("commit tx err: %+v", err))}首先还是会关上一个数据库实例,而后调用 Txn 办法,这个办法的入参是一个函数,事务的操作都在这个函数中实现,在提交的时候一次性执行。 ...

August 14, 2021 · 2 min · jiezi

关于golang:GO的内置数据结构channel

一、channelchannel分为有buffer的和没有buffer的。没有buffer的能够当成有buffer然而buffersize为0的状况。buffer数据结构: type hchan struct { qcount uint // 以后chan中有多少数据 dataqsiz uint // 环形数组队列的大小,也就是咱们定义的缓冲区大小 buf unsafe.Pointer // 指向环形数组队列的指针 elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // 发送时插入的地位(环形数组的下标) recvx uint // 接管时取数据的地位(环形数组的下标) recvq waitq // 接管链表,当buf为空的时候,打包goroutine现场后放在这里 sendq waitq // 发送链表,当buf满的时候,打包goroutine现场后放在这里 // 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}发送流程像图中发送数据到channel中,每次qcount和sendx会随之变动,sendx会在插入前标记以后的插入地位变到插入后标记下一个数据插入地位(因为是环形数组,所以如果在最初地位插入后索引归0)当buf外面的数据满的时候,再往里面发送数据,此时qcount==dataqsize示意满,此时咱们会将以后G的现场与channel打包成一个sudog的构造,链在sendq上。 ...

August 13, 2021 · 1 min · jiezi

关于golang:项目配置文件获取及更新热更新

在我的项目开发中,配置文件的正当的获取和更新是一个根本的性能。我的项目上线后批改配置项,批改配置之后如果还重新启动我的项目能力失效,这样成果并不好。为了可能在不影响我的项目失常运行的状况下批改配置项,就须要用到配置热更新。例如:上线后想要批改日志的级别,能够间接批改配置文件,我的项目主动扫描配置文件,如果发现文件被批改,则从新获取配置信息。读取配置和配置热更新有两种形式: 形式一:调用github.com/fsnotify/fsnotify包,监控配置变动,并应用其余的包来读取文件形式二:调用github.com/spf13/viper包,这个包是由Steve Francia开发,提供了监控和配置的设置、获取的办法一、形式一的应用main.go文件package mainimport ( "file-store/handler" "file-store/models" "file-store/utils" "fmt" "log" "net/http" "github.com/fsnotify/fsnotify")func main() { configPath := "/Users/apple/workplace/file-store/config/config.yaml" configDir := "/Users/apple/workplace/file-store/config" // 加载配置文件 _ = utils.InitConfig(configPath, "config") fmt.Printf("配置为:%s\n", utils.Conf.Token.Salt) watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal(err) } defer watcher.Close() done := make(chan bool) go func() { for { select { case event := <-watcher.Events: if event.Op&fsnotify.Write == fsnotify.Write { _ = utils.InitConfig(configPath, "config") fmt.Printf("更新配置为:%s\n", utils.Conf.Token.Salt) } case err := <-watcher.Errors: log.Println("error:", err) } } }() fmt.Printf("数据库配置为:%s, %s\n", utils.Conf.Mysql.Drive, utils.Conf.Mysql.Address) // 数据库操作 models.MysqlInit(utils.Conf.Mysql.Drive, utils.Conf.Mysql.Address) // 监听端口 err = http.ListenAndServe("127.0.0.1:8000", nil) if err != nil { fmt.Printf("Failed to start server, err %s", err.Error()) } // 监控文件 err = watcher.Add(configPath) if err != nil { log.Fatal(err) } // 监控文件夹 err = watcher.Add(configDir) if err != nil { log.Fatal(err) } <-done}config.go:该文件用于获取配置信息,读取配置用到两个包:ioutil读取配置,gopkg.in/yaml.v2用于数据的转换package utilsimport ( "fmt" "io/ioutil" "sync" "gopkg.in/yaml.v2")type Config struct { File *File `yaml:"file"` Mysql *Mysql `yaml:"mysql"` Token *Token `yaml:"token"`}type File struct { Path string `yaml:"path"`}type Mysql struct { Drive string `yaml:"drive"` Address string `yaml:"address"`}type Token struct { Salt string `yaml:"salt"` Issue string `yaml:"issue"`}var Conf *Config// InitConfig 读取yaml配置文件func InitConfig(configPath, configName string) error { var locker = new(sync.RWMutex) yamlFile, err := ioutil.ReadFile(configPath) if err != nil { panic(err) } locker.Lock() err1 := yaml.Unmarshal(yamlFile, &Conf) if err1 != nil { panic(err) } locker.Unlock() fmt.Println(Conf.Token.Salt) return nil}二、形式二的应用config.go文件package utilsimport ( "fmt" "github.com/spf13/viper")type Config struct { File *File `yaml:"file"` Mysql *Mysql `yaml:"mysql"` Token *Token `yaml:"token"`}type File struct { Path string `yaml:"path"`}type Mysql struct { Drive string `yaml:"drive"` Address string `yaml:"address"`}type Token struct { Salt string `yaml:"salt"` Issue string `yaml:"issue"`}// 全局配置var config = new(Config)// InitConfig 读取yaml配置文件func InitConfig(configPath, configName, configType string) error { viper.SetConfigName(configName) // 配置文件名 viper.SetConfigType(configType) // 配置文件类型,例如:toml、yaml等 viper.AddConfigPath(configPath) // 查找配置文件所在的门路,屡次调用能够增加多个配置文件搜寻的目录 // 读取配置文件配置,并处理错误 if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { return err } } // 监控配置文件变动 viper.WatchConfig() viper.Unmarshal(config) if err := validateConfig(config); err != nil { return err } return nil}// 获取全局配置func GetConfig() *Config { return config}// validateConfig:校验配置信息func validateConfig(conf *Config) error { var ( file = conf.File.Path drive = conf.Mysql.Drive address = conf.Mysql.Address salt = conf.Token.Salt issue = conf.Token.Issue ) if file == "" { return fmt.Errorf("invalid file path: %s\n", file) } if drive == "" { return fmt.Errorf("invalid drive: %s\n", drive) } if address == "" { return fmt.Errorf("invalid address: %s\n", address) } if salt == "" { return fmt.Errorf("invalid salt: %s\n", salt) } if issue == "" { return fmt.Errorf("invalid issue: %s\n", issue) } return nil}main.go文件package mainimport ( "file-store/handler" "file-store/models" "file-store/utils" "fmt" "net/http")func main() { configPath := "/Users/apple/workplace/file-store/config" // 配置初始化 err := utils.InitConfig(configPath, "config", "yaml") if err != nil { fmt.Printf("Failed to init config, err is %s\n", err) } // 获取全局配置 conf := utils.GetConfig() fmt.Println(conf.File.Path) // 数据库操作 models.MysqlInit(conf.Mysql.Drive, conf.Mysql.Address) // 监听端口 err = http.ListenAndServe("127.0.0.1:8000", nil) if err != nil { fmt.Printf("Failed to start server, err %s", err.Error()) }}

August 13, 2021 · 3 min · jiezi

关于golang:go语言for循环中的break

明天遇到一个小坑,就是在for包裹的select中应用break,只会跳出select,不会跳出for循环,case如下: func main() { cxt,cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() for { select { case <-cxt.Done(): time.Sleep(time.Second) fmt.Println("it cancel") break //只会跳出select 不会跳出for循环 //在这里能够间接return //或者配合label标签推出循环 default: fmt.Println("Go go go") time.Sleep(time.Second) } } }() time.Sleep(1500*time.Millisecond) cancel() wg.Wait()}输入 Go go goGo go goit cancelit cancelit cancelit cancelit cancel··· //死循环

August 13, 2021 · 1 min · jiezi

关于golang:Go实现的后台管理系统主框架Gin

源码地址https://gitee.com/termites/monkey-admin 平台简介基于Gin的后盾管理系统前端采纳ruoyi-ui 、Vue、Element UI。后端采纳GO语言 框架 Gin。本我的项目由猴酷团队开发。内置性能用户治理:用户是零碎操作者,该性能次要实现零碎用户配置。部门治理:配置零碎组织机构(公司、部门、小组),树结构展示反对数据权限。岗位治理:配置零碎用户所属负责职务。菜单治理:配置零碎菜单,操作权限,按钮权限标识等。角色治理:角色菜单权限调配、设置角色按机构进行数据范畴权限划分。字典治理:对系统中常常应用的一些较为固定的数据进行保护。参数治理:对系统动静配置罕用参数。演示地址http://www.monkeycool.cn账号:admin 明码:admin123 配置我的项目数据库文件 /data/db.sql 创立数据库导入后批改配置/config/config-*.ini 运行go run main.go 间接拜访http://localhost:8080 账号:admin 明码:admin123 我的项目为前后端拆散,前端代码在monkey-ui目录下 docker镜像构建docker装置应用请参考官网 依据状况批改Dockerfile文件在我的项目根目录下应用命令docker build -t <你要出的进行名>:<版本号> .演示图 感激(排名不分先后)Gin框架 https://github.com/gin-gonic/gin gotoolhttps://github.com/druidcaesa/gotool RuoYi-Vue https://gitee.com/y_project/RuoYi-Vue jwt https://github.com/dgrijalva/jwt-go excelize https://github.com/qax-os/excelize xorm https://github.com/go-xorm/xorm 免责申明:1、monkey-admin仅限本人学习应用,所有商业行为与monkey-admin无关。 2、用户不得利用monkey-admin从事非法行为,用户该当非法合规的应用,发现用户在应用产品时有任何的非法行为,monkey-admin有权配合无关机关进行考察或向政府部门举报,monkey-admin不承当用户因非法行为造成的任何法律责任,所有法律责任由用户自行承当,如因用户应用造成第三方侵害的,用户该当依法予以抵偿。 3、所有与应用monkey-admin相干的资源间接危险均由用户承当。

August 12, 2021 · 1 min · jiezi

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

简介testify能够说是最风行的(从 GitHub star 数来看)Go 语言测试库了。testify提供了很多不便的函数帮忙咱们做assert和错误信息输入。应用规范库testing,咱们须要本人编写各种条件判断,依据判断后果决定输入对应的信息。 testify外围有三局部内容: assert:断言;mock:测试替身;suite:测试套件。筹备工作本文代码应用 Go Modules。 创立目录并初始化: $ mkdir -p testify && cd testify$ go mod init github.com/darjun/go-daily-lib/testify装置testify库: $ go get -u github.com/stretchr/testifyassertassert子库提供了便捷的断言函数,能够大大简化测试代码的编写。总的来说,它将之前须要判断 + 信息输入的模式: if got != expected { t.Errorf("Xxx failed expect:%d got:%d", got, expected)}简化为一行断言代码: assert.Equal(t, got, expected, "they should be equal")构造更清晰,更可读。相熟其余语言测试框架的开发者对assert的相干用法应该不会生疏。此外,assert中的函数会主动生成比拟清晰的谬误形容信息: func TestEqual(t *testing.T) { var a = 100 var b = 200 assert.Equal(t, a, b, "")}应用testify编写测试代码与testing一样,测试文件为_test.go,测试函数为TestXxx。应用go test命令运行测试: $ go test--- FAIL: TestEqual (0.00s) assert_test.go:12: Error Trace: Error: Not equal: expected: 100 actual : 200 Test: TestEqualFAILexit status 1FAIL github.com/darjun/go-daily-lib/testify/assert 0.107s咱们看到信息更易读。 ...

August 12, 2021 · 5 min · jiezi

关于golang:关于Golang-struct用法和注意事项

引自ljq@GitHub struct {}struct {}是一个无元素的构造体类型,通常在没有信息存储时应用。长处:不须要内存来存储struct{}类型的值。struct{}{}struct{}{}是一个复合字面量,它结构了一个struct{}类型的值,该值也是空。两个structt{}{}地址相等package mainimport "fmt"type idBval struct { Id int}func main() { idA := struct{}{} fmt.Printf("idA: %T and %v \n\n", idA, idA) idB := idBval{ 1, } idB.Id = 2 fmt.Printf("idB: %T and %v \n\n", idB, idB) idC := struct { Id int }{ 1, } fmt.Printf("idC: %T and %v \n\n", idC, idC) mapD := make(map[string]struct{}) mapD["mapD"] = struct{}{} _, ok := mapD["mapD"] fmt.Printf("mapD['mapD'] is %v \n\n", ok) sliceE := make([]interface{}, 2) sliceE[0] = 1 sliceE[1] = struct{}{} fmt.Printf("idE: %T and %v \n\n", sliceE, sliceE)}Output: ...

August 11, 2021 · 1 min · jiezi

关于golang:不懂汇编也能看懂的-Go-interface-原理一

hi, 大家好,我是 haohognfan。 可能你看过的 interface 分析的文章比拟多了,这些文章根本都是从汇编角度剖析类型转换或者动静转发。不过随着 Go 版本升级,对应的 Go 汇编也产生了微小的变动,如果单从汇编角度去剖析 interface 变的十分有难度,本篇文章我会从内度调配+汇编角度切入 interface,去理解 interface 的原理。 限于篇幅 interface 无关动静转发和反射的内容,请关注后续的文章。本篇文章次要是对于类型转换,以及相干的容易呈现谬误的中央。 efacefunc main() { var ti interface{} var a int = 100 ti = a fmt.Println(ti)}这段最常见的代码,当初提出一些问题: 如何查看 ti 是 eface 还是 iface ?值 100 保留在哪里了 ?如何看 ti 的实在的值的类型 ?大部分源码剖析都是从汇编动手来看的,这里也把对应的汇编贴出来 0x0040 00064 (main.go:44) MOVQ $100, (SP)0x0048 00072 (main.go:44) CALL runtime.convT64(SB)0x004d 00077 (main.go:44) MOVQ 8(SP), AX0x0052 00082 (main.go:44) MOVQ AX, ""..autotmp_3+64(SP)0x0057 00087 (main.go:44) LEAQ type.int(SB), CX0x005e 00094 (main.go:44) MOVQ CX, "".ti+72(SP)0x0063 00099 (main.go:44) MOVQ AX, "".ti+80(SP)这段汇编有上面这些特点: ...

August 10, 2021 · 3 min · jiezi

关于golang:译|What-accept-interfaces-return-structs-means-in-Go

“承受接口、返回构造” 的个别准则,我在前一篇文章中写到,也屡次在代码评审时向共事介绍,但常常遇到“为什么”的疑难。特地是因为这不是一条硬性规定。该想法的关键在于放弃灵活性的同时防止事后形象,并了解何时扭转它。 事后形象使零碎变得复杂计算机科学畛域的任何问题都能够通过减少一个间接的中间层来解决,当然,间接过多的问题除外 - David J. Wheeler软件工程师喜爱形象。就我集体而言,从未见过编写代码比创立形象更投入的共事。Go 中,接口形象脱离了构造,该间接层甚至没有最低档次的嵌入复杂性。遵循软件设计 您不会用到它 的哲学,在须要之前制作这种复杂性毫无意义。函数调用返回接口的一个常见起因是让用户专一于函数凋谢的 API。因为有隐式接口,Go 不须要这样做。构造的 public function iu 就是其 API。 总是当 真正 须要时 [形象],不要当 预感 须要时 [形象]。某些语言要求你预感将来须要的每个接口。隐式接口的一大长处是,它们容许预先进行优雅的形象,而无需事后进行形象。 因人而异的“须要”当真正须要时如何定义何时须要形象?对于返回类型,这很容易。你是编写该函数的人,因而您确切晓得何时须要将返回值形象。 对于函数输出,需要不在你的管制范畴之内。你可能认为 database struct 就足够了,但用户可能须要用其余货色装璜它。就算不是不可能,预测每个人应用你的函数的状态也是很艰难的。可能准确管制输入,但无奈预测用户输出。相比对输入的抽象化,这种不均衡造成了对输出的抽象化更强烈的并重。 去除不必要的代码细节 简化的另一方面是去除不必要的细节。函数就像烹饪食谱:给定输出,就会失去一个蛋糕!没有食谱会列出不须要的配料。相似地,函数也不应该列出不须要的输出。你如何看以下函数? func addNumbers(a int, b int, s string) int { return a + b }对于大多数程序员来说,很显著,参数 s 不失当。当参数是构造时,却不太显著。 type Database struct{ } func (d *Database) AddUser(s string) {...} func (d *Database) RemoveUser(s string) {...}func NewUser(d *Database, firstName string, lastName string) { d.AddUser(firstName + lastName) }就像配料太多的食谱一样,NewUser 接管一个能够做太多事件的 Database 对象。它只须要 AddUser,但接管的参数还有 RemoveUser。应用接口创立的函数,能够只依赖于必须。 ...

August 9, 2021 · 1 min · jiezi

关于golang:惊Go里面居然有这样精妙的小函数

来自公众号:Gopher指北各位哥麻烦腾个道,后面是大型装逼现场。 首先老许要感激别人的认同,这是我乐此不彼的能源,同时我也须要反思。这位小姐姐还是比拟婉转, 但用咱们四川话来说,前一篇文章的题目是真的cuo。 老许重复思考后决定哗众取宠一波,感叹号双连取名曰“惊!Go外面竟然有这样精妙的小函数!”。上面就让咱们来看看和题目没那么合乎的一些小函数。 返回a/b向上舍入最靠近的整数 func divRoundUp(n, a uintptr) uintptr { return (n + a - 1) / a}这个办法用过的人应该不少,最典型的就是分页计算。 判断x是否为2的n次幂 func isPowerOfTwo(x uintptr) bool { return x&(x-1) == 0}这个也挺容易了解的,惟一须要留神的是x须要大于0,因为该等式0也是成立的。 向上/下将x舍入为a的倍数,且a必须是2的n次幂 // 向上将x舍入为a的倍数,例如:x=6,a=4则返回值为8func alignUp(x, a uintptr) uintptr { return (x + a - 1) &^ (a - 1)}// 向上将x舍入为a的倍数,例如:x=6,a=4则返回值为4func alignDown(x, a uintptr) uintptr { return x &^ (a - 1)}在这里老许再次明确一个概念,2的n次幂即为1左移n位。而后上述代码中^为单目运算法按位取反,则^ (a - 1)的运算后果是除了最低n位为0其余位全为1。残余的局部则是一个简略的加减运算以及按位与。 上述代码离开来看每一部分都意识,合在一起就一脸懵逼了。侥幸的是,通过老许的不懈努力终于找到了一种可能了解的形式。 以x=10,a=4为例。a为2的2次幂即1左移2位。x可看作两局部之和,第一局部x1为0b1000,第二局部x2为0b0011。x的拆分形式是1左移n位可失去a来决定的,即x的最低n位为x2,x1则为x-x2。因而x1相当于0b10左移2位失去,即x1曾经是a的整数倍,此时x2只有大于0则x2+a-1肯定会向后退1,x1+1或x1不就是x向上舍入的a的整数倍嘛,最初和^ (a - 1)进行与运算将最低2位清零失去最终的返回后果。 有一说一,我必定是写不出这样的逻辑,这也令我不得不感叹大佬们对计算机的了解几乎炉火纯青。这样的函数牛逼归牛逼,然而在理论开发中还是尽量少用。一是有应用场景的限度(a必须为2的n次幂),二是不易了解,当然炫技和装逼除外(性能要求极高也除外)。 布尔转整形 ...

August 8, 2021 · 3 min · jiezi

关于golang:Golang并发编程包之-errgroup

原文链接:并发编程包之 errgroup 前言哈喽,大家好,我是asong,明天给大家介绍一个并发编程包errgroup,其实这个包就是对sync.waitGroup的封装。咱们在之前的文章—— 源码分析sync.WaitGroup(文末思考题你能解释一下吗?),从源码层面剖析了sync.WaitGroup的实现,应用waitGroup能够实现一个goroutine期待一组goroutine干活完结,更好的实现了工作同步,然而waitGroup却无奈返回谬误,当一组Goroutine中的某个goroutine出错时,咱们是无奈感知到的,所以errGroup对waitGroup进行了一层封装,封装代码仅仅不到50行,上面咱们就来看一看他是如何封装的?errGroup如何应用老规矩,咱们先看一下errGroup是如何应用的,后面吹了这么久,先来验验货; 以下来自官网文档的例子: var ( Web = fakeSearch("web") Image = fakeSearch("image") Video = fakeSearch("video"))type Result stringtype Search func(ctx context.Context, query string) (Result, error)func fakeSearch(kind string) Search { return func(_ context.Context, query string) (Result, error) { return Result(fmt.Sprintf("%s result for %q", kind, query)), nil }}func main() { Google := func(ctx context.Context, query string) ([]Result, error) { g, ctx := errgroup.WithContext(ctx) searches := []Search{Web, Image, Video} results := make([]Result, len(searches)) for i, search := range searches { i, search := i, search // https://golang.org/doc/faq#closures_and_goroutines g.Go(func() error { result, err := search(ctx, query) if err == nil { results[i] = result } return err }) } if err := g.Wait(); err != nil { return nil, err } return results, nil } results, err := Google(context.Background(), "golang") if err != nil { fmt.Fprintln(os.Stderr, err) return } for _, result := range results { fmt.Println(result) }}下面这个例子来自官网文档,代码量有点多,然而外围次要是在Google这个闭包中,首先咱们应用errgroup.WithContext创立一个errGroup对象和ctx对象,而后咱们间接调用errGroup对象的Go办法就能够启动一个协程了,Go办法中曾经封装了waitGroup的管制操作,不须要咱们手动增加了,最初咱们调用Wait办法,其实就是调用了waitGroup办法。这个包不仅缩小了咱们的代码量,而且还减少了错误处理,对于一些业务能够更好的进行并发解决。 ...

August 8, 2021 · 2 min · jiezi

关于golang:golang-系列mutex-详解

摘要Go 号称是为了高并发而生的,在高并发场景下,势必会波及到对公共资源的竞争。当对应场景产生时,咱们常常会应用 mutex 的 Lock() 和 Unlock() 办法来占有或开释资源。尽管调用简略,但 mutex 的外部却波及挺多的。明天,就让咱们好好钻研一下。 mutex 初步意识mutex 的源码次要是在 src/sync/mutex.go文件里,它的构造体比较简单,如下: type Mutex struct { state int32 sema uint32}咱们能够看到有一个字段 sema,它示意信号量标记位。所谓的信号量是用于 Goroutine 之间阻塞或唤醒的。这有点像操作系统里的 PV 原语操作,咱们先来意识下 PV 原语操作: PV 原语解释: 通过操作信号量 S 来解决过程间的同步与互斥的问题。 S>0:示意有 S 个资源可用;S=0 示意无资源可用;S<0 绝对值示意期待队列或链表中的过程个数。信号量 S 的初值应大于等于 0。 P 原语:示意申请一个资源,对 S 原子性的减 1,若 减 1 后仍 S>=0,则该过程继续执行;若 减 1 后 S<0,示意已无资源可用,须要将本人阻塞起来,放到期待队列上。 V 原语:示意开释一个资源,对 S 原子性的加 1;若 加 1 后 S>0,则该过程继续执行;若 加 1 后 S<=0,示意期待队列上有期待过程,须要将第一个期待的过程唤醒。通过下面的解释,mutex 就能够利用信号量来实现 goroutine 的阻塞和唤起了。 ...

August 8, 2021 · 4 min · jiezi

关于golang:反射reflect机制

什么是反射官网对此有个十分扼要的介绍,两句话回味无穷: 反射提供一种让程序查看本身构造的能力反射是困惑的源泉要深刻理解反射,个人感觉须要花工夫在官网博客上再加以练习,循序渐进,缓缓领会。 反射的三个定律反射就是查看interface的(value, type)对的,因为任何类型的变量或办法都是实现了空接口。具体一点说就是Go提供一组办法提取interface的value,提供另一组办法提取interface的type. 官网提供了三条定律来阐明反射,比拟清晰,上面也依照这三定律来总结。 反射包里有两个接口类型要先理解一下. reflect.Type 提供一组接口解决interface的类型,即(value, type)中的typereflect.Value提供一组接口解决interface的值,即(value, type)中的value上面会提到反射对象,所谓反射对象即反射包里提供的两种类型的对象。 reflect.Type 类型对象reflect.Value类型对象1. 反射的第一定律:反射能够将interface类型变量转换成为reflect类型变量这里的reflect类型指的是reflect.Type和reflect.Value类型的变量 package mainimport ( "fmt" "reflect")func main() { var x float64 = 8.5 t := reflect.TypeOf(x) // 这里的t类型为:reflect.Type fmt.Println("type:", t) v := reflect.ValueOf(x) // 这里的v类型为:reflect.Value fmt.Println("value:", v)}2. 反射第二定律:反射能够将reflect类型对象还原成interface类型对象package mainimport ( "fmt" "reflect")func main() { var x float64 = 8.5 v := reflect.ValueOf(x) //这里v的类型为:reflect.Value var y float64 = v.Interface().(float64) //v通过Interface()函数将反射类型转换为interface类型变量,再通过断言为float64获取值 fmt.Println("value:", y)}3. 反射第三定律:如果要批改reflect类型对象,则value必须是可设置的package mainimport ( "reflect" "fmt")func main() { var x float64 = 8.5 fmt.Println("x :", x) v := reflect.ValueOf(&x) fmt.Println("settability of v:", v.CanSet()) //v.SetFloat(5.8) // 这里会报panic的谬误,因为这时候v是不可设置的 fmt.Println("settability of v:", v.Elem().CanSet()) v.Elem().SetFloat(5.8) fmt.Println("x :", v.Elem().Interface().(float64))}下面11行v代表的是指针地址,咱们要设置的是指针所指向的内容,也即咱们想要批改的是*v。 那怎么通过v批改x的值呢? ...

August 6, 2021 · 2 min · jiezi

关于golang:Go函数调用惯例

本文旨在探讨Go函数中的一个问题:为什么Go函数能反对多参数返回,而C/C++、java不行?这其实牵涉到了一个叫做函数调用常规的问题。 调用常规在程序代码中,函数提供了最小性能单元,程序执行实际上就是函数间互相调用的过程。在调用时,函数调用方和被调用方必须恪守某种约定,它们的了解要统一,该约定就被称为函数调用常规。 函数调用常规往往由编译器来规定,本文次要关怀两个点: 函数的参数(入参加出参)是通过栈还是寄存器传递?如果通过栈传递,是从左至右,还是从右至左入栈?栈栈是古代计算机程序里最为重要的概念之一,没有栈就没有函数,也没有局部变量。栈保留了一个函数调用所须要的保护信息,这经常被称为堆栈帧(Stack Frame)或流动记录 (Activate Record)。堆栈帧个别包含如下几方面内容: 函数的返回地址和参数。长期变量:包含函数的非动态局部变量以及编译器主动生成的其余长期变量。保留的上下文信息:包含在函数调用前后须要放弃不变的寄存器。一个堆栈帧能够用指向栈顶的栈指针寄存器SP与保护以后栈帧的基准地址的基准指针寄存器BP来示意。因而,一个典型的函数流动记录能够示意为如下 在参数及其之后的数据即以后函数的流动记录。BP固定在图中所示的地位(通过它便于索引参数与变量等),它不会随着函数的执行而变动。而SP始终指向栈顶,随着函数的执行,SP会一直变动。在BP之前是该函数的返回地址,在32位机器示意为BP+4,64位机器示意为BP+8,再往前就是压入栈中的参数。BP所间接指向的数据是调用该函数前BP的值,这样在函数返回的时候,BP能够通过读取这个值复原到调用前的值。 汇编代码解析上面,咱们来比照剖析C和Go调用常规差别。 C调用常规假如有main.c的C程序源文件,其中main函数调用add函数,具体代码如下。 // main.cint add(int arg1, int arg2, int arg3, int arg4,int arg5, int arg6,int arg7, int arg8) { return arg1 + arg2 + arg3 + arg4 + arg5 + arg6 + arg7 + arg8;}int main() { int i = add(10, 20, 30, 40, 50, 60, 70, 80);}咱们通过clang编译器在x86_64平台上进行编译。 $ clang -vApple clang version 12.0.0 (clang-1200.0.32.29)Target: x86_64-apple-darwin19.5.0main.c 编译后失去的汇编代码如下 $ clang -S main.c ..._main: ... subq $32, %rsp movl $10, %edi // 将参数1数据置于edi寄存器 movl $20, %esi // 将参数2数据置于esi寄存器 movl $30, %edx // 将参数3数据置于edx寄存器 movl $40, %ecx // 将参数4数据置于ecx寄存器 movl $50, %r8d // 将参数5数据置于r8d寄存器 movl $60, %r9d // 将参数6数据置于r9d寄存器 movl $70, (%rsp) // 将参数7数据置于栈上 movl $80, 8(%rsp) // 将参数8数据置于栈上 callq _add // 调用add函数 xorl %ecx, %ecx movl %eax, -4(%rbp) movl %ecx, %eax // 最终通过eax寄存器承载着返回值返回 addq $32, %rsp popq %rbp retq ... _add: ... movl 24(%rbp), %eax movl 16(%rbp), %r10d movl %edi, -4(%rbp) // 将edi寄存器上的数据搁置于栈上 movl %esi, -8(%rbp) // 将esi寄存器上的数据搁置于栈上 movl %edx, -12(%rbp) // 将edx寄存器上的数据搁置于栈上 movl %ecx, -16(%rbp) // 将ecx寄存器上的数据搁置于栈上 movl %r8d, -20(%rbp) // 将r8d寄存器上的数据搁置于栈上 movl %r9d, -24(%rbp) // 将edi寄存器上的数据搁置于栈上 movl -4(%rbp), %ecx // 将栈上的数据 10 搁置于ecx寄存器 addl -8(%rbp), %ecx // 理论为:ecx = ecx + 20 addl -12(%rbp), %ecx // ecx = ecx + 30 addl -16(%rbp), %ecx // ecx = ecx + 40 addl -20(%rbp), %ecx // ecx = ecx + 50 addl -24(%rbp), %ecx // ecx = ecx + 60 addl 16(%rbp), %ecx // ecx = ecx + 70 addl 24(%rbp), %ecx // ecx = ecx + 80 movl %eax, -28(%rbp) movl %ecx, %eax // 最终通过eax寄存器承载着返回值返回 popq %rbp retq ... 因而,在main函数调用add函数之前,其参数寄存如下图所示 ...

August 4, 2021 · 3 min · jiezi

关于golang:golang-16内存管理机制变更

背景link 变更link

August 4, 2021 · 1 min · jiezi

关于golang:理解gomicro一-前置知识准备

引言学习golang不久后,因工作须要接触到了go-micro这一微服务框架。通过读源码,写业务代码,定制个性化插件,解决框架问题这些过程后,对它有了更粗浅的了解。总的来说,这是一个性能较为齐全,形象较为正当的微服务框架,非常适合用来强化golang的学习以及加深对微服务畛域常识的了解,但是否达到了生产规范的要求至今仍是个未知数,须要更多的测验。 本系列文章基于asim/go-micro v3.5.2版本,读者可于https://github.com/asim/go-micro拉取源代码进行学习。 筹备抛开微服务的畛域常识,go-micro的整体设计次要基于Functional Options以及Interface Oriented,把握这两点基本上就把握住了它的代码格调,对于之后的学习、应用、扩大大有裨益。因而首先介绍这两种设计模式,为之后的深刻了解做好铺垫。 Functional Options一. 问题引入 在介绍Functional Options之前,咱们先来思考一个平时编程的惯例操作:配置并初始化一个对象。例如生成一个Server对象,须要指明IP地址和端口,如下所示: type Server struct { Addr string Port int}很天然的,构造函数能够写成如下模式: func NewServer(addr string, port int) (*Server, error) { return &Server{ Addr: addr, Port: port, }, nil} 这个构造函数简略间接,但事实中一个Server对象远不止Addr和Port两个属性,为了反对更多的性能,例如监听tcp或者udp,设置超时工夫,限度最大连接数,须要引入更多的属性,此时Server对象变成了如下模式: type Server struct { Addr string Port int Protocol string Timeout time.Duration MaxConns int}为了投合属性变动,构造函数会变成以下模式: func NewServer(addr string, port int, protocol string, timeout time.Duration, maxConns int) (*Server, error) { return &Server{ Addr: addr, Port: port, Protocol: protocol, Timeout: timeout, MaxConns: maxConns, }, nil} 置信大家曾经发现了,随着属性的增多,这种模式的构造函数会越来越长,最初臃肿不堪。如果这个对象只是本人开发和应用,把控好具体细节和复杂度,还是能承受的,只须要将参数换行就行。但如果这个对象作为库的公共成员被其余开发者应用,那么这个构造函数即API会带来以下问题: ...

August 4, 2021 · 4 min · jiezi

关于golang:golang-系列深入认识-map

摘要map 通过 hasTable 实现了咱们最常见的 key-value 存储,能疾速的对数据集增删查改。同时 Go 里的 map 也有很多非凡的中央,比方它的无序性、并发不平安等。明天,就让咱们对 map 进行深入研究,看看它是怎么设计的。 map 根本意识当咱们用 dataMap := make(map[int]string)创立一个 map 对象的时候,失去的是一个 hmap 指针构造。通过对 src/runtime/map.go文件剖析,咱们看到了对应的构造体如下: type hmap struct { count int // 以后的元素个数 flags uint8 B uint8 // 字段 buckets 数组的长度关联参数,即 buckets 长度为 2^B noverflow uint16 // overflow buckets 的近似数 hash0 uint32 // 哈希种子,作 hash 运算用的 buckets unsafe.Pointer // 数组对象,存 key-value的 oldbuckets unsafe.Pointer // 扩容时用到的 buckets,大小是之前 buckets 的一半。 nevacuate uintptr // 扩容进度 extra *mapextra // optional fields}在这外面,有一个十分要害的字段: buckets,它就是用来存储 key-value 的。 ...

August 4, 2021 · 3 min · jiezi

关于golang:详解Go语言中的内存逃逸

原文链接:## 面试官:小松子来聊一聊内存逃逸 前言哈喽,大家好,我是asong。最近无聊看了一下Go语言的面试八股文,发现面试官都喜爱问内存逃逸这个话题,这个激发了我的趣味,我对内存逃逸的理解很浅,所以找了很多文章精读了一下,在这里做一个总结,不便日后查阅、学习。什么是内存逃逸首次看到这个话题,我是懵逼的,怎么还有内存逃逸,内存逃逸到底是干什么的?接下来咱们一起来看看什么是内存逃逸。 咱们都晓得个别状况下程序寄存在rom或者Flash中,运行时须要拷贝到内存中执行,内存会别离存储不同的信息,内存空间蕴含两个最重要的区域:堆区(Heap)和栈区(Stack),对于我这种C语言出身的人,对堆内存和栈内存的理解还是挺深的。在C语言中,栈区域会专门寄存函数的参数、局部变量等,栈的地址从内存高地址往低地址增长,而堆内存正好相同,堆地址从内存低地址往高地址增长,然而如果咱们想在堆区域分配内存须要咱们手动调用malloc函数去堆区域申请内存调配,而后我应用完了还须要本人手动开释,如果没有开释就会导致内存透露。写过C语言的敌人应该都晓得C语言函数是不能返回局部变量地址(特指寄存于栈区的局部变量地址),除非是部分动态变量地址,字符串常量地址、动态分配地址。其起因是个别局部变量的作用域只在函数内,其存储地位在栈区中,当程序调用完函数后,局部变量会随此函数一起被开释。其地址指向的内容不明(原先的数值可能不变,也可能扭转)。而部分动态变量地址和字符串常量地址寄存在数据区,动态分配地址寄存在堆区,函数运行完结后只会开释栈区的内容,而不会扭转数据区和堆区。 所以在C语言中咱们想在一个函数中返回局部变量地址时,有三个正确的形式:返回动态局部变量地址、返回字符串常量地址,返回动态分配在堆上的地址,因为他们都不在栈区,即便开释函数,其内容也不会受影响,咱们以在返回堆上内存地址为例看一段代码: #include "stdio.h"#include "stdlib.h"//返回动态分配的地址 int* f1(){ int a = 9; int *pa = (int*) malloc(8); *pa = a; return pa;}int main(){ int *pb; pb = f1(); printf("after : *pb = %d\tpb = %p\n",*pb, pb); free(pb); return 1;}通过下面的例子咱们晓得在C语言中动态内存的调配与开释齐全交与程序员的手中,这样就会导致咱们在写程序时如履薄冰,益处是咱们能够齐全掌控内存,毛病是咱们一不小心就会导致内存透露,所以很多古代语言都有GC机制,Go就是一门带垃圾回收的语言,真正解放了咱们程序员的双手,咱们不须要在像写C语言那样思考是否能返回局部变量地址了,内存治理交与给编译器,编译器会通过逃逸剖析把变量正当的调配到"正确"的中央。 说到这里,能够简略总结一下什么是内存逃逸了: 在一段程序中,每一个函数都会有本人的内存区域寄存本人的局部变量、返回地址等,这些内存会由编译器在栈中进行调配,每一个函数都会调配一个栈桢,在函数运行完结后进行销毁,然而有些变量咱们想在函数运行完结后依然应用它,那么就须要把这个变量在堆上调配,这种从"栈"上逃逸到"堆"上的景象就成为内存逃逸。什么是逃逸剖析下面咱们晓得了什么是内存逃逸,上面咱们就来看一看什么是逃逸剖析? 上文咱们说到C语言应用malloc在堆上动静分配内存后,还须要手动调用free开释内存,如果不开释就会造成内存透露的危险。在Go语言中堆内存的调配与开释齐全不须要咱们去管了,Go语言引入了GC机制,GC机制会对位于堆上的对象进行主动治理,当某个对象不可达时(即没有其对象援用它时),他将会被回收并被重用。尽管引入GC能够让开发人员升高对内存治理的心智累赘,然而GC也会给程序带来性能损耗,当堆内存中有大量待扫描的堆内存对象时,将会给GC带来过大的压力,尽管Go语言应用的是标记革除算法,并且在此基础上应用了三色标记法和写屏障技术,进步了效率,然而如果咱们的程序仍在堆上调配了大量内存,依赖会对GC造成不可漠视的压力。因而为了缩小GC造成的压力,Go语言引入了逃逸剖析,也就是想法设法尽量减少在堆上的内存调配,能够在栈中调配的变量尽量留在栈中。 小结逃逸剖析: 逃逸剖析就是指程序在编译阶段依据代码中的数据流,对代码中哪些变量须要在栈中调配,哪些变量须要在堆上调配进行动态剖析的办法。堆和栈相比,堆适宜不可预知大小的内存调配。然而为此付出的代价是调配速度较慢,而且会造成内存碎片。栈内存调配则会十分快。栈分配内存只须要两个CPU指令:“PUSH”和“RELEASE”,调配和开释;而堆分配内存首先须要去找到一块大小适合的内存块,之后要通过垃圾回收能力开释。所以逃逸剖析更做到更好内存调配,进步程序的运行速度。Go语言中的逃逸剖析Go语言的逃逸剖析总共实现了两个版本: 1.13版本前是第一版1.13版本后是第二版粗略看了一下逃逸剖析的代码,大略有1500+行(go1.15.7)。代码我倒是没认真看,正文我倒是认真看了一遍,正文写的还是很具体的,代码门路:src/cmd/compile/internal/gc/escape.go,大家能够本人看一遍正文,其逃逸剖析原理如下: pointers to stack objects cannot be stored in the heap:指向栈对象的指针不能存储在堆中pointers to a stack object cannot outlive that object:指向栈对象的指针不能超过该对象的存活期,也就说指针不能在栈对象被销毁后仍旧存活。(例子:申明的函数返回并销毁了对象的栈帧,或者它在循环迭代中被反复用于逻辑上不同的变量)咱们大略晓得它的剖析准则是什么就好了,具体逃逸剖析是怎么做的,感兴趣的同学能够依据源码自行钻研。 既然逃逸剖析是在编译阶段进行的,那咱们就能够通过go build -gcflags '-m -m -l'命令查看到逃逸剖析的后果,咱们之前在剖析内联优化时应用的-gcflags '-m -m',能看到所有的编译器优化,这里应用-l禁用掉内联优化,只关注逃逸优化就好了。 ...

August 3, 2021 · 3 min · jiezi

关于golang:如何用好-Go-interface

interface 是 Go 语言最精华的个性之一,始终以来想写一篇对于 interface 的文章,然而始终没敢写。继续几年之久,还是斗胆总结下。 Concrete typesstruct 定义数据的内存布局。一些晚期倡议将办法蕴含在 struct 中,然而被放弃了。相同,办法如一般函数一样申明在类型之外。形容 (data) 和行为 (methods) 是独立且正交的。 一方面,办法只是一个带有 “receiver” 参数的函数。 type Point struct { x, y float }func (p Point) Abs() float { return math.Sqrt(p.x*p.x + p.y*p.y)}func Abs(p Point) float { return math.Sqrt(p.x*p.x + p.y*p.y)}Abs 编写为一个惯例函数,性能没有变动。 什么时候应该应用办法,什么时候应该应用函数呢?如果办法不依赖类型的状态,则应该将其定义为函数。另一方面,办法在定义其行为时,应用了类型的值时,与所附加的类型严密关联。办法能够从对应的类型中获取值,如果有指针 “receiver”,还能够操纵其状态。 “类型” 有时候很有用,有时候又很厌恶。因为类型是对底层内存布局的一个形象,会让代码关注于非业务逻辑上的货色,然而代码又须要在不同类型的数据间做解决。interface 就是其中一种泛型解决方案。 // Package sort provides primitives for sorting slices and user-defined collections.package sort// An implementation of Interface can be sorted by the routines in this package.// The methods refer to elements of the underlying collection by integer index.type Interface interface { // Len is the number of elements in the collection. Len() int // Less reports whether the element with index i // must sort before the element with index j. Less(i, j int) bool // Swap swaps the elements with indexes i and j. Swap(i, j int)}// Sort sorts data.func Sort(data Interface) { ...}Abstract typesGo 的 interface 仅仅是函数的汇合,也定义了行为。 interface 与类型之间没有显式的关系,类型也能够同时满足多个 interface 的要求。 ...

August 3, 2021 · 2 min · jiezi

关于golang:golang-结合etcd利用雪花算法实现全局递增唯一ID

1.雪花算法图片来自 https://zhuanlan.zhihu.com/p/... 生成的序列号是由64位示意最高位为0,示意是负数第2到第42位示意工夫距离,其计算是应用以后工夫减去一个起始工夫失去一个工夫距离,41位大概能够保留69年的工夫范畴,也就是说起始工夫是2021年的话,大概在2090年用完,足够应用了第43位到第52位为具体服务的id,服务的id是应用etcd来实现全局惟一的也能够应用redis进行实现,最多1024个,也就是说所有服务最多1024个,包含起来多个的服务第53位到第64位为同一时间下递增的序列号2.源码如下,钻研能够看正文实现只放在一个源文件外面了,具体的能够进行放在不同的包中进行调用,雪花算法的实现能够独自放在一个worker包中供所有服务调用 package mainimport ( "context" "errors" "go.etcd.io/etcd/clientv3" "log" "strconv" "sync" "time")const( WORKERIDBits = 10 //wokerId 占10位bit SEQUENCEBITS = 12 //序列号占的bit位数 MAXSEQUENCE = int64(-1) ^ (int64(-1) << SEQUENCEBITS) //序列号的最大值 MAXWORKERID = int64(-1) ^ (int64(-1) << WORKERIDBits) //workerId的最大值 TIMESTAMP_OFFSET = uint(22) //工夫戳的偏移位数 WORKERID_OFFSET = uint(12) //workerId的偏移位数 TIME_START_STAMP = int64(1589923200000) // 起始工夫 2020-05-20 08:00:00 +0800 CST)//-----------------------------workerId---------------------------var CurrentWorkNodeNum string //以后节点number 节点number从1开始 最大值为1024var WokerNodePrefix = "worker" //节点key前缀 节点值为CurrentWorkNodeNum 例如 worker1 = 1 worker2 = 2var wg sync.WaitGroupfunc WorkerId() error { if len(CurrentWorkNodeNum) != 0 { //CurrentWorkNodeNum 如果曾经初始化过了,间接返回 return errors.New("CurrentWorkNodeNum inited") } client, err := clientv3.New(clientv3.Config{ Endpoints: []string{"http://192.168.56.111:2379"}, //etcd端 能够抽离进去 DialTimeout: 2 * time.Second, }) if err != nil { log.Println("create client err:",err) } ctx, cancel := context.WithTimeout(context.Background(), time.Second) resp,err := client.Get(ctx, WokerNodePrefix,clientv3.WithPrefix()) //获取所有前缀为worker的节点 cancel() if err != nil { return errors.New("get prefix worker node error") } existNodeMap := make(map[int]int) //定义一个map,保留曾经存在的节点 for _,ev := range resp.Kvs { num, _ := strconv.Atoi(string(ev.Value)) existNodeMap[num] = num //put到existNodeMap中 log.Printf("%s:%s \n",ev.Key,ev.Value) } count := 1 //从1到1024找最小的number for ;count < 1025; count++ { if _, ok := existNodeMap[count];!ok { //如果不存在,就会间接break CurrentWorkNodeNum = strconv.Itoa(count) break } } if count == 1024 { //代表1024个节点都曾经用完了,或者局部节点曾经挂掉了,而后key的租期还没有完结,能够重新启动 return errors.New("服务节点数目大于1024") } go ActiveCurrentWorkerNode(client) //启动一个协程始终激活以后key,如果以后服务挂了,key就会在租期完结后查问不到了 return nil}func ActiveCurrentWorkerNode(client *clientv3.Client){ for { leasetime := int64(60) //租期工夫 sleeptime := 50 //以后协程睡眠工夫,小于租期工夫即可 lease := clientv3.NewLease(client) log.Println("active currerntNode :",CurrentWorkNodeNum) if leaseRes,err := lease.Grant(context.TODO(),leasetime);err != nil { panic(err) }else { _, err := client.Put(context.Background(), WokerNodePrefix+CurrentWorkNodeNum, CurrentWorkNodeNum,clientv3.WithLease(leaseRes.ID)) if err != nil { panic(err) } } time.Sleep(time.Second * time.Duration(sleeptime)) }}//-----------------------------workerId---------------------------type SnowFlakeWorker struct{ mu sync.Mutex //互斥锁 LastTimestamp int64 //上一次的工夫距离 WorkerID int64 //该服务的wokerID Sequence int64 //同一时间戳下的序列号}func New(wokerID int64) *SnowFlakeWorker{ return &SnowFlakeWorker{ WorkerID: wokerID, LastTimestamp: 0, Sequence: 0, }}func (s *SnowFlakeWorker) getMilliSeconds() int64{ return time.Now().UnixNano() / 1e6 //以后工夫的毫秒数}func (s *SnowFlakeWorker) NextID() (uint64,error){ s.mu.Lock() //加锁 defer s.mu.Unlock() timeStamp := s.getMilliSeconds() //以后工夫毫秒数 if timeStamp < s.LastTimestamp { //以后工夫毫秒数小于上一次的毫秒数,谬误间接抛出异样 return 0,errors.New("currentTime is before timestamp") } if timeStamp == s.LastTimestamp { //如果相等则sequenc加1 s.Sequence = (s.Sequence + 1) & MAXSEQUENCE if s.Sequence == 0 { //加1取余MAXSEQUENCE 阐明以后毫秒数的序列号应用结束,须要期待下一个毫秒数 for timeStamp <= s.LastTimestamp { //期待到下一个毫秒数就退出 timeStamp = s.getMilliSeconds() } } }else { s.Sequence = 0 //如果大于LastTimestamp 则sequence为0 } s.LastTimestamp = timeStamp return uint64((timeStamp - TIME_START_STAMP) << TIMESTAMP_OFFSET | s.WorkerID << WORKERIDBits |s.Sequence),nil}func main() { wg.Add(1) err := WorkerId() if err != nil { log.Println("worker inited error:",err) return } currentWorkerNodeNum, _ := strconv.Atoi(CurrentWorkNodeNum) worker := New(int64(currentWorkerNodeNum)) for i := 1;i<10;i++ { id, err := worker.NextID() if err != nil { log.Println("generate snowflake id,error:",err) return } log.Println("snowflakeId:",id) } wg.Wait()}3.测试后果2021-08-03 17:38:16.416241 I | worker1:12021-08-03 17:38:16.416241 I | worker2:22021-08-03 17:38:16.416241 I | snowflakeId: 1596364534988175362021-08-03 17:38:16.417196 I | snowflakeId: 1596364534988175372021-08-03 17:38:16.417196 I | snowflakeId: 1596364535030118402021-08-03 17:38:16.417196 I | snowflakeId: 1596364535030118412021-08-03 17:38:16.417196 I | snowflakeId: 1596364535030118422021-08-03 17:38:16.417196 I | snowflakeId: 1596364535030118432021-08-03 17:38:16.417196 I | snowflakeId: 1596364535030118442021-08-03 17:38:16.417196 I | snowflakeId: 1596364535030118452021-08-03 17:38:16.417196 I | snowflakeId: 1596364535030118462021-08-03 17:38:16.417196 I | active currerntNode : 3

August 3, 2021 · 3 min · jiezi

关于golang:并发安全Context包的使用

前言--为什么须要ContextGolang context是Golang利用开发罕用的并发控制技术,它与WaitGroup最大的不同点是context对于派生goroutine有更强的控制力,它能够管制多级的goroutine。 context翻译成中文是”上下文”,即它能够管制一组呈树状构造的goroutine,每个goroutine领有雷同的上下文。 典型的应用场景如下图所示: 当一个申请被勾销或超时时,所有用来解决该申请的 goroutine 都应该迅速退出,而后零碎能力开释这些 goroutine 占用的资源。上图中因为goroutine派生出子goroutine,而子goroutine又持续派生新的goroutine,这种状况下应用WaitGroup就不太容易,因为子goroutine个数不容易确定。而应用context就能够很容易实现。 1.全局变量形式退出package mainimport ( "fmt" "sync" "time")var wg sync.WaitGroupvar exit bool// 全局变量形式存在的问题:// 1. 应用全局变量在跨包调用时不容易对立// 2. 如果worker中再启动goroutine,就不太好管制了。func worker() { for { fmt.Println("worker") time.Sleep(time.Second) if exit { break } } wg.Done()}func main() { wg.Add(1) go worker() time.Sleep(time.Second * 3) // sleep3秒免得程序过快退出 exit = true // 批改全局变量实现子goroutine的退出 wg.Wait() fmt.Println("over")}2.Channel的形式退出package mainimport ( "fmt" "sync" "time")var wg sync.WaitGroup// 管道形式存在的问题:// 1. 应用全局变量在跨包调用时不容易实现标准和对立,须要保护一个共用的channelfunc worker(exitChan chan struct{}) {LOOP: for { fmt.Println("worker") time.Sleep(time.Second) select { case <-exitChan: // 期待接管下级告诉 break LOOP default: } } wg.Done()}func main() { var exitChan = make(chan struct{}) wg.Add(1) go worker(exitChan) time.Sleep(time.Second * 3) // sleep3秒免得程序过快退出 exitChan <- struct{}{} // 给子goroutine发送退出信号 close(exitChan) wg.Wait() fmt.Println("over")}3.Context形式退出package mainimport ( "context" "fmt" "sync" "time")var wg sync.WaitGroupfunc worker(ctx context.Context) {LOOP: for { fmt.Println("worker") time.Sleep(time.Second) select { case <-ctx.Done(): // 期待下级告诉 break LOOP default: } } wg.Done()}func main() { ctx, cancel := context.WithCancel(context.Background()) wg.Add(1) go worker(ctx) time.Sleep(time.Second * 3) cancel() // 告诉子goroutine完结 wg.Wait() fmt.Println("over")}并且当子goroutine又开启另外一个goroutine时,只须要将ctx传入即可: ...

August 3, 2021 · 5 min · jiezi

关于golang:并发安全Sync包的使用

有时候在Go代码中可能会存在多个goroutine同时操作一个资源(临界区),这种状况会产生竞态问题(数据竞态)。Sync包次要实现了并发工作同步WaitGroup的几种办法和并发平安的互斥锁和读写锁办法,还实现了比拟非凡的两个办法,一个是放弃只执行一次的Once办法和线程平安的Map。 sync.WaitGroup(同步期待)sync.WaitGroup外部保护着一个计数器Add(),计数器的值能够减少和缩小。例如当咱们启动了N 个并发工作时,就将计数器值减少N。每个工作实现时通过调用Done()办法将计数器减1,底层为Add(-1)。通过调用Wait()来期待并发工作执行完,当计数器值为0时,示意所有并发工作曾经实现。 var x int64var wg sync.WaitGroupfunc add() { for i := 0; i < 5000; i++ { x = x + 1 //数据竞争 } wg.Done()}func main() { wg.Add(2) go add() go add() wg.Wait() fmt.Println(x)}下面的代码中咱们开启了两个goroutine去累加变量x的值,这两个goroutine在拜访和批改x变量的时候就会存在数据竞争,导致最初的后果与期待的不符。 sync.Mutex(互斥锁)互斥锁是一种罕用的管制共享资源拜访的办法,它可能保障同时只有一个goroutine能够访问共享资源。Go语言中应用sync包的Mutex类型来实现互斥锁。 应用互斥锁来修复下面代码的问题: var x int64var wg sync.WaitGroupvar lock sync.Mutexfunc add() { for i := 0; i < 5000; i++ { lock.Lock() // 加锁 x = x + 1 lock.Unlock() // 解锁 } wg.Done()}func main() { wg.Add(2) go add() go add() wg.Wait() fmt.Println(x)}应用互斥锁可能保障同一时间有且只有一个goroutine进入临界区,其余的goroutine则在期待锁;当互斥锁开释后,期待的goroutine才能够获取锁进入临界区,多个goroutine同时期待一个锁时,唤醒的策略是随机的。 ...

August 3, 2021 · 3 min · jiezi

关于golang:定时器Timer和Ticker

TimerTimer是一个定时器,代表将来的一个繁多事件,通过Timer自身提供的管道将事件传递进来,话中有话是只执行一次。 Timer的数据结构如下: type Timer struct { C <-chan Time r runtimeTimer}package mainimport ( "fmt" "time")func main() { // 1.timer根本应用 timer1 := time.NewTimer(2 * time.Second) t1 := time.Now() fmt.Printf("以后工夫为:%v\n", t1) // 从定时器拿工夫数据 t2 := <-timer1.C fmt.Printf("定时两秒后:%v\n", t2) // 2.验证timer只能响应1次 timer2 := time.NewTimer(time.Second) for { <-timer2.C fmt.Println("工夫到,以后for循环不再执行") } // 3.timer实现延时的性能 //(1)休眠一秒钟 time.Sleep(time.Second) //(2)定时两秒钟 timer3 := time.NewTimer(2 * time.Second) <-timer3.C fmt.Println("定时2秒到") //(3)延时两秒钟 <-time.After(2*time.Second) fmt.Println("延时2秒到") // 4.进行定时器 timer4 := time.NewTimer(2 * time.Second) go func() { <-timer4.C fmt.Println("定时器执行了") }() // 进行定时器 b := timer4.Stop() if b { fmt.Println("timer4曾经进行") } // 5.设置定时为三秒钟 timer5 := time.NewTimer(3 * time.Second) // 重置定时器为一秒钟 timer5.Reset(1 * time.Second) fmt.Println(time.Now()) fmt.Println(<-timer5.C) for { }}TickerTicker是周期性定时器,即周期性的触发一个事件,通过Ticker自身提供的管道将事件传递进来,话中有话是能够反复执行。 ...

August 3, 2021 · 1 min · jiezi

关于golang:Select多路复用

在某些场景下咱们须要同时从多个通道接收数据。通道在接收数据时,如果没有数据能够接管将会产生阻塞,而select就能够同时监听一个或多个channel,直到其中一个channel筹备好。 select的应用相似于switch语句,它有一系列case分支和一个默认的分支。每个case会对应一个通道的通信(接管或发送)过程。select会始终期待,直到某个case的通信操作实现时,就会执行case分支对应的语句。具体格局如下: select { case <-chan1: // 如果chan1胜利读到数据,则进行该case解决语句 case chan2 <- 1: // 如果胜利向chan2写入数据,则进行该case解决语句 default: // 如果下面都没有胜利,则进入default解决流程 }package mainimport ( "fmt" "time")func test1(ch chan string) { time.Sleep(time.Second * 1) ch <- "test1"}func test2(ch chan string) { time.Sleep(time.Second * 2) ch <- "test2"}func main() { // 2个管道 output1 := make(chan string) output2 := make(chan string) // 跑2个子协程,写数据 go test1(output1) go test2(output2) for { // 用select监控 select { case s1 := <-output1: fmt.Println("s1=", s1) case s2 := <-output2: fmt.Println("s2=", s2) default: ticker := time.NewTicker(1 * time.Second) fmt.Printf("%v\n", <-ticker.C) } }}判断通道是否曾经存满 ...

August 3, 2021 · 1 min · jiezi

关于golang:golang-viper配置文件管理包使用

1.装置go get github.com/spf13/viper2.根本应用及注意事项config.toml文件,注意事项在程序正文中 appname = "user_web77"loglevel = "info"port = 8089[mysql]ip = "192.168.56.19"port = 3310user = "root"password = 123456database = "cmp"[redis]ip = "192.168.56.101"port = 6379[rabbitmq]ip = "192.168.56.101"port = 5991程序 package mainimport ( "fmt" "github.com/fsnotify/fsnotify" "github.com/spf13/viper" "log" "time")type MysqlConfig struct{ Ip string //私有拜访 Port string User string Password string Database string}type RedisConfig struct{ Ip string Port string}type RabbitmqConfig struct{ Ip string Port string}type Config struct{ //Config对象和config.toml文件保持一致 AppName string LogLevel string Port string Mysql MysqlConfig //须要定义子类型对应的变量,如果不定义映射不胜利 Redis RedisConfig Rabbitmq RabbitmqConfig}func main() { viper.AutomaticEnv() //获取环境变量的值 viper.SetConfigName("config") //定义配置文件名称 viper.SetConfigType("toml") //定义格局 viper.AddConfigPath("./config/") //定义文件门路 viper.SetDefault("redis.port",6379) //设置默认值 viper.SetDefault("kafka.port",6856) err := viper.ReadInConfig() //读取 if err != nil { log.Fatalln("read config failed: %v",err) } viper.WatchConfig() //监听配置文件扭转 并且须要搁置在viper.ReadInConfig之后 viper.OnConfigChange(func(e fsnotify.Event) { //配置文件批改完后的回调函数 log.Printf("Config file:%s Op:%s\n", e.Name, e.Op) }) log.Println(viper.GetString("appname")) //以string格局获取 log.Println(viper.GetString("log_level")) log.Println(viper.GetString("port")) fmt.Println("mysql ip:",viper.GetString("mysql.ip")) fmt.Println("mysql port:",viper.GetString("mysql.port")) fmt.Println("kafka port:",viper.GetString("kafka.port")) fmt.Println("GOPATH: ", viper.Get("GOPATH")) time.Sleep(time.Second) var config Config viper.Unmarshal(&config) for { log.Println(config.LogLevel) //通过绑定到对象上不会实时更新 log.Println(config.AppName) log.Println(config.Mysql.Ip) fmt.Println("mysql ip:",viper.GetString("mysql.ip")) //能够更新 time.Sleep(time.Second * 5) }}3.后果2021/08/03 14:31:25 info2021/08/03 14:31:25 user_web772021/08/03 14:31:25 192.168.56.138mysql ip: 192.168.56.1382021/08/03 14:31:28 Config file:C:\Users\admin\xiayuedu\golangdemo\config\config.toml Op:WRITE2021/08/03 14:31:28 Config file:C:\Users\admin\xiayuedu\golangdemo\config\config.toml Op:WRITE2021/08/03 14:31:30 info2021/08/03 14:31:30 user_web772021/08/03 14:31:30 192.168.56.138mysql ip: 192.168.56.192021/08/03 14:31:35 info2021/08/03 14:31:35 user_web772021/08/03 14:31:35 192.168.56.138mysql ip: 192.168.56.19

August 3, 2021 · 1 min · jiezi

关于golang:go-pprof-性能分析

1.装置下载Graphviz地址 http://www.graphviz.org/downl... 我本地下载好windows版本后,须要退出到环境变量中 装置pprof go get github.com/pkg/profile退出到环境变量中 2.代码样例package mainimport ( "github.com/pkg/profile" "log" "time")func joinSlice() []string { var arr []string for i := 0; i < 100000; i++ { arr = append(arr, "arr") } return arr}func main() { // 开始性能剖析, 返回一个进行接口 stopper := profile.Start(profile.CPUProfile, profile.ProfilePath(".")) // 在main()完结时进行性能剖析 defer stopper.Stop() // 剖析的外围逻辑 joinSlice() // 让程序至多运行1秒 time.Sleep(time.Second) log.Println("finished")}//go build -o cpu cpu.go//go tool pprof --pdf cpu.exe cpu.pprof > cpu.pdf优化后的 func joinSlice() []string { arr := make([]string,100000) for i := 0; i < 100000; i++ { arr = append(arr, "arr") } return arr}依照程序执行 ...

August 3, 2021 · 1 min · jiezi

关于golang:go-validator参数校验器自定义规则及提示

1.装置validatorgo get -u github.com/go-playground/validator/v102.应用package mainimport ( "fmt" "github.com/go-playground/validator/v10" //"gopkg.in/go-playground/validator.v10" "net/http")type User struct{ UserName string `validate:"min=3,max=13"` Password string `validate:"min=3,max=13"`}func main() { u := User{UserName: "z"} validate := validator.New() fmt.Println(u) err := validate.Struct(u) fmt.Println("err:",err)}谬误提醒 {z }err: Key: 'User.UserName' Error:Field validation for 'UserName' failed on the 'min' tagKey: 'User.Password' Error:Field validation for 'Password' failed on the 'min' tag3.校验规定len:length 等于,长度相等max:小于等于min:大于等于eq:等于,字符串相等ne:不等于gt:大于gte:大于等于lt:小于lte:小于等于,例如lte=10;oneof:值中的一个,例如oneof=1 2反对工夫范畴的比拟lte 工夫 RegTime time.Time `validate:"lte"` 小于等于以后工夫跨字段束缚eqfield=ConfirmPasswordeqcsfield=InnerStructField.Field字符串规定contains=:蕴含参数子串containsany:蕴含参数中任意的 UNICODE 字符containsrune:蕴含参数示意的 rune 字符excludes:不蕴含参数子串excludesall:不蕴含参数中任意的 UNICODE 字符excludesrune:不蕴含参数示意的 rune 字符startswith:以参数子串为前缀endswith:以参数子串为后缀应用unqiue来指定唯一性束缚,对不同类型的解决如下:对于数组和切片,unique束缚没有反复的元素;对于map,unique束缚没有反复的值;对于元素类型为构造体的切片,unique束缚构造体对象的某个字段不反复,通过unqiue=name非凡规定-:跳过该字段,不测验;|:应用多个束缚,只须要满足其中一个,例如rgb|rgba;required:字段必须设置,不能为默认值;omitempty:如果字段未设置,则疏忽它。4.自定义规定及自定义提示信息package mainimport ( "fmt" "github.com/go-playground/validator/v10" "log" "reflect" //"gopkg.in/go-playground/validator.v10" "net/http")type User struct{ UserName string `validate:"minReg" reg_error_info:"用户名至多6个字符"` //通过reg_error_info标签记录 //reg_error_info也能够是标记谬误的惟一标识,通过传入的local_language 从库中或者缓存中找到对应国家的谬误提示信息 Password string `validate:"minReg" reg_error_info:"明码至多6个字符"` }//自定义的校验规定,能够应用正则表达式进行匹配,这里仅仅应用了长度判断func minRegFun(f validator.FieldLevel) bool { value := f.Field().String() log.Println(f) if len(value) < 6 { return false }else { return true }}func main() { u := User{UserName: "zzzzzz",Password: "xxxx"} validate := validator.New() validate.RegisterValidation("minReg", minRegFun) //注册自定义的校验函数 minReg和validate tag值保持一致 err := validate.Struct(u) //校验 errorInfo := processErr(u, err) //处理错误信息 if len(errorInfo) != 0 { log.Println(errorInfo) } else { log.Println("校验通过") }}func processErr(u interface{},err error) string { if err == nil { //如果为nil 阐明校验通过 return "" } invalid, ok := err.(*validator.InvalidValidationError) //如果是输出参数有效,则间接返回输出参数谬误 if ok { return "输出参数谬误:" + invalid.Error() } validationErrs := err.(validator.ValidationErrors) //断言是ValidationErrors for _, validationErr := range validationErrs { fieldName := validationErr.Field() //获取是哪个字段不合乎格局 field, ok := reflect.TypeOf(u).FieldByName(fieldName) //通过反射获取filed if ok { errorInfo := field.Tag.Get("reg_error_info") //获取field对应的reg_error_info tag值 return fieldName + ":" + errorInfo //返回谬误 }else { return "缺失reg_error_info" } } return ""}后果 ...

August 3, 2021 · 2 min · jiezi

关于golang:golang-zap日志库使用

1.装置zapgo get go.uber.org/zapgo get github.com/natefinch/lumberjack //日志文件宰割包2.根本应用func main() { logger, _ := zap.NewProduction() //生产环境实例 defer logger.Sync() //将缓存中的日志同步到文件中 url := "http://xiayuedu.com" logger.Info("info", //info级别的日志 zap.String("url",url), zap.Bool("bool",true), ) logger.Info("info", zap.Namespace("namespace-1"), //定义多级日志 zap.String("url",url), //应用对应的类型 zap.Bool("bool",true), ) logger.Info("info", zap.Namespace("namespace-2"), zap.String("url",url), zap.Bool("bool",true), ) logger.Error("password is error", //error级别 zap.String("username","zhangsan"), zap.String("password","123456"), ) logger.Debug("record user info", //debug级别 zap.String("username","zhangsan"), zap.String("password","123456"), ) sugar := logger.Sugar() //应用非zap.xxxx类型进行输入,性能较低,非核心代码处能够应用 sugar.Info("sugar info",true,"123q342534",time.Second) sugar.Error("sugar info",true,"123q342534",time.Second) sugar.Debug("sugar info",true,"123q342534",time.Second) sugar.Warn("sugar info",true,"123q342534",time.Second)}输入后果 {"level":"info","ts":1627910714.8048513,"caller":"golangdemo/main.go:357","msg":"info","url":"http://xiayuedu.com","bool":true}{"level":"info","ts":1627910714.805827,"caller":"golangdemo/main.go:361","msg":"info","namespace-1":{"url":"http://xiayuedu.com","bool":true}}{"level":"info","ts":1627910714.806803,"caller":"golangdemo/main.go:366","msg":"info","namespace-2":{"url":"http://xiayuedu.com","bool":true}}{"level":"error","ts":1627910714.806803,"caller":"golangdemo/main.go:371","msg":"password is error","username":"zhangsan","password":"123456","stacktrace":"main.main\n\tC:/Users/admin/xiayuedu/golangdemo/main.go:371\nruntime.main\n\tC:/Program Files/Go/src/runtime/proc.go:203"}{"level":"info","ts":1627910714.8077793,"caller":"golangdemo/main.go:381","msg":"sugar infotrue123q3425341s"}{"level":"error","ts":1627910714.8077793,"caller":"golangdemo/main.go:382","msg":"sugar infotrue123q3425341s","stacktrace":"main.main\n\tC:/Users/admin/xiayuedu/golangdemo/main.go:382\nruntime.main\n\tC:/Program Files/Go/src/runtime/proc.go:203"}{"level":"warn","ts":1627910714.8087556,"caller":"golangdemo/main.go:384","msg":"sugar infotrue123q3425341s"}3.定制化应用 ...

August 2, 2021 · 1 min · jiezi

关于golang:Goroutine池

goroutine池的利用 实质上是生产者消费者模型能够无效管制goroutine数量,避免暴涨需要: 计算一个数字的各个位数之和,例如数字123,后果为1+2+3=6随机生成数字进行计算控制台输入后果如下:job id: 164362 random: 3002688297310473558 result: 81job id: 164363 random: 8188014692039052954 result: 84job id: 164364 random: 9199514953162082256 result: 87job id: 164365 random: 6547609431377245779 result: 96job id: 164366 random: 5158862497998501304 result: 94job id: 164367 random: 7591045689235318611 result: 84job id: 164368 random: 4913457935486468007 result: 93job id: 164369 random: 6484925446766292271 result: 94job id: 164370 random: 1630566468428792798 result: 101job id: 164371 random: 3999715154713986432 result: 96job id: 164372 random: 8436839935373284876 result: 106job id: 164373 random: 7590654756672405712 result: 88job id: 164374 random: 5127823813978664887 result: 103job id: 164375 random: 5630536624069526117 result: 77job id: 164376 random: 3445557283367509019 result: 86job id: 164377 random: 6087330610397339825 result: 83job id: 164378 random: 3391465925899927215 result: 99main.go ...

August 2, 2021 · 2 min · jiezi

关于golang:Channel

为什么要用channel?单纯地将函数并发执行是没有意义的。函数与函数间须要替换数据能力体现并发执行函数的意义。 尽管能够应用共享内存进行数据交换,然而共享内存在不同的goroutine中容易产生竞态问题。为了保障数据交换的正确性,必须应用互斥量对内存进行加锁,这种做法势必造成性能问题。 Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。 如果说goroutine是Go程序并发的执行体,channel就是它们之间的连贯。channel是能够让一个goroutine发送特定值到另一个goroutine的通信机制。 Go 语言中的通道(channel)是一种非凡的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规定,保障收发数据的程序。每一个通道都是一个具体类型的导管,也就是申明channel的时候须要为其指定元素类型。 创立channel通道是援用类型,通道类型的空值是nil。 var ch chan intfmt.Println(ch) // <nil>申明的通道后须要应用make函数初始化之后能力应用。 创立channel的格局如下: make(chan 元素类型, [缓冲大小])channel的缓冲大小是可选的。 举几个例子: ch4 := make(chan int)ch5 := make(chan bool)ch6 := make(chan []int)channel操作通道有发送(send)、接管(receive)和敞开(close)三种操作。 发送和接管都应用<-符号。 当初咱们先应用以下语句定义一个通道: ch := make(chan int)发送将一个值发送到通道中。 ch <- 10 // 把10发送到ch中接管从一个通道中接管值。 x := <- ch // 从ch中接管值并赋值给变量x<-ch // 从ch中接管值,疏忽后果敞开咱们通过调用内置的close函数来敞开通道。 close(ch)对于敞开通道须要留神的事件是,只有在告诉接管方goroutine所有的数据都发送结束的时候才须要敞开通道。通道是能够被垃圾回收机制回收的,它和敞开文件是不一样的,在完结操作之后敞开文件是必须要做的,但敞开通道不是必须的。 敞开后的通道有以下特点: 1.对一个敞开的通道再发送值就会导致panic。 2.对一个敞开的通道进行接管会始终获取值直到通道为空。 3.对一个敞开的并且没有值的通道执行接管操作会失去对应类型的零值。 4.敞开一个曾经敞开的通道会导致panic。无缓冲的通道无缓冲的通道又称为阻塞的通道,咱们来看一下上面的代码: func main() { ch := make(chan int) ch <- 10 fmt.Println("发送胜利")}下面这段代码可能通过编译,然而执行的时候会呈现以下谬误: ...

August 2, 2021 · 2 min · jiezi

关于golang:Goroutine

什么是GoroutineGoroutine的概念相似于线程,但 goroutine是由Go的运行时(runtime)调度和治理的。Go程序会智能地将 goroutine 中的工作正当地调配给每个CPU。Go语言之所以被称为现代化的编程语言,就是因为它在语言层面曾经内置了调度和上下文切换的机制。 在Go语言编程中你不须要去本人写过程、线程、协程,你的技能包里只有一个技能–goroutine,当你须要让某个工作并发执行的时候,你只须要把这个工作包装成一个函数,开启一个goroutine去执行这个函数就能够了,就是这么简略粗犷。 应用GoroutineGo语言中应用goroutine非常简单,只须要在调用函数的时候在后面加上go关键字,就能够为一个函数创立一个goroutine。 一个goroutine必然对应一个函数,能够创立多个goroutine去执行雷同的函数。 启动单个Goroutinefunc main() { go hello() // 启动另外一个goroutine去执行hello函数 fmt.Println("main goroutine done!") time.Sleep(time.Second)}启动多个Goroutine var wg sync.WaitGroupfunc hello(i int) { defer wg.Done() // goroutine完结就注销-1 fmt.Println("Hello Goroutine!", i)}func main() { for i := 0; i < 10; i++ { wg.Add(1) // 启动一个goroutine就注销+1 go hello(i) } wg.Wait() // 期待所有注销的goroutine都完结}这里应用了sync.WaitGroup代替下面的sleep来实现goroutine的同步;这10个goroutine是并发执行的,而goroutine的调度是随机的。 runtime包runtime.Gosched()让出CPU工夫片,该语句前面的工作期待重新安排 package mainimport ( "fmt" "runtime")func main() { go func(s string) { for i := 0; i < 20; i++ { fmt.Println(s) } }("优先执行") // 主协程 for i := 0; i < 20; i++ { // 让出CPU工夫片,从新期待安顿工作 runtime.Gosched() fmt.Println("失常执行") }}runtime.Goexit()退出以后协程 ...

August 2, 2021 · 1 min · jiezi

关于golang:golang-系列神秘的内存管理

工具与资源核心帮忙开发者更加高效的工作,提供围绕开发者全生命周期的工具与资源 https://developer.aliyun.com/... 一、概述内存治理在任何的编程语言里都是重头戏,Golang 也不例外。Go 借鉴了 Google 的 TCMalloc,它是高性能的用于 c++ 的内存分配器。其核心思想是内存池 + 多级对象治理 ,能放慢调配速度,升高资源竞争。 二、根底构造在 Go 里用于内存治理的对象构造次要是上面几个: mheap、mspan、arenas、mcentral、mcache。 其中,mspan 是一个根底构造,分配内存时,根本以它为单位。 mcache、mcentral、mheap 起到了内存池的作用,会被预分配内存,当有对应大小的对象须要调配时会先到它们这一层申请。如果这一层内存池不够用时,会依照上面的程序一层一层的往上申请内存: mcache -> mcentral-> mheap -> 操作系统 mspan&&arenas先来看看 mspan 这个根底构造体。首先,当 Go 在程序初始化的时候,会将申请到的虚拟内存划分为以下三个局部:内存治理arenas 也就是动态分配的堆区,它将调配到的内存以 8k 为一页进行治理。然而 "页" 这个单位还是太细了,因而再形象出 mspan 这一层来治理,mspan 示意一组间断的页面。 mspan 记录了这组间断页面的起止地址、页数量、以及类型规格。 对于 mspan 的类型规格有 67 种,每一种都被定义了一个固定大小,当有对象须要分配内存时,就会筛选适合规格的 mspan 调配给对象。 class 1 2 3 4 5 6 ··· 63 64 65 66 bytes 8 16 32 48 64 80 ··· 24576 27264 28672 32768例如给大小为 30B 的对象分配内存时,就会抉择类型规格 class 为 3,也就是大小为 32B 的 mspan 调配。这种调配办法跟 linux 用于内存调配的搭档算法差不多,能无效地缩小内存碎片。 ...

August 2, 2021 · 1 min · jiezi

关于golang:golang-笔记

1.foreachfor循环是对值的拷贝 type student struct { Name string Age int}func forDemo() { m := make(map[string] * student) stus := [] student { { Name: "zhang", Age: 24, }, { Name: "li", Age: 23, }, { Name: "zhao", Age: 22, }, } for i := 0; i < len(stus); i++ { stus[i].Age = stus[i].Age + 10 //这样操作 } for i := 0;i<len(stus);i++ { m[stus[i].Name] = & stus[i] //这样操作 } for k, v := range m { println(k, "=>", v.Name,v.Age) }}2.chan作为互斥锁func add(h chan int, wg *sync.WaitGroup) { defer wg.Done() h <- 1 num += 1 <-h}

August 2, 2021 · 1 min · jiezi

关于golang:Excelize-发布-241-版本新增并发安全支持

Excelize 是 Go 语言编写的用于操作 Office Excel 文档根底库,基于 ECMA-376,ISO/IEC 29500 国际标准。能够应用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创立的电子表格文档。反对 XLSX / XLSM / XLTM 等多种文档格局,高度兼容带有款式、图片(表)、透视表、切片器等简单组件的文档,并提供流式读写 API,用于解决蕴含大规模数据的工作簿。可利用于各类报表平台、云计算、边缘计算等零碎。入选 2020 Gopher China - Go 畛域明星开源我的项目(GSP)、2018 年开源中国码云最有价值开源我的项目 GVP (Gitee Most Valuable Project),目前已成为 Go 语言最受欢迎的 Excel 文档根底库。 开源代码GitHub: github.com/xuri/excelize Gitee: gitee.com/xurime/excelize 中文文档: xuri.me/excelize/zh-hans 2021年8月2日,社区正式公布了 2.4.1 版本,该版本蕴含了多项新增性能、谬误修复和兼容性晋升优化。上面是无关该版本更新内容的摘要,残缺的更改列表可查看 changelog。 此版本中最显著的变动包含: 兼容性提醒Go Modules 包援用地址调整为 github.com/xuri/excelize/v2 新增性能新增流式设置工作表列宽度反对,相干 issue #625新增流式创立合并单元格反对,相干 issue #826公式计算引擎新增 2 项公式函数反对: BESSELK, BESSELY公式计算引擎反对自定义名称援用,相干 issue #856增加图表时反对设置不显示次要横纵坐标轴通过 AddPivotTable 创立数据透视表反对通过自定义名称动静援用数据源以下函数新增反对并发平安调用,相干 issue #861 AddPicture 和 GetPicture 并发插入/获取图片Rows 和 Cols 并发行/列迭代SetSheetRow 并发按行赋值SetCellStyle 并发设置单元格款式NewStyle 并发创立款式导出 24 个外部异样音讯兼容性晋升晋升外部默认 XML 命名空间兼容性,修复局部状况下生成文档损坏的问题兼容带有非标准页面布局属性数据类型的电子表格文档,防止关上失败的问题减少外部共享字符表计数解除通过给定的工夫设置单元格的值时,须要协调世界时 (UTC) 的限度,相干 issue #409减少对外部 XML 控制字符的兼容重命名导出字段 File.XLSX 为 File.Pkg批改 NewSheet, GetSheetIndex, DeleteSheet 对工作表名称大小写不敏感,相干 issue #873修复条件格局与数据透视表的兼容性问题,解决 issue #883改良与页面布局中有效的首页编号属性的兼容性SetCellRichText 减少字符数下限查看并修复保留字符失落问题问题修复修复局部状况下 12/24 制小时工夫格局解析异样的问题,解决 issue #823 和 issue #841修复局部状况下无奈通过 GetComments 获取批注的问题,解决 issue #825修复设置和获取批注时反对多个批注作者,解决 issue #829 和 #830修复命名空间地址解析异样而产生反复命名空间,导致删除再创立同名工作表后的生成文档损坏问题,解决 issue #834修复当设置工作表分组默认属性 showOutlineSymbols、summaryBelow 和 summaryRight 为 false 时,设置生效的问题修复局部状况下 GetRows 返回冗余工作表尾部空行的问题,解决 issue #842修复局部状况下获取获取单元格的值时,未返回带有公式的空单元格的问题,解决 issue #855修复局部状况下 IF 公式条件运算谬误问题,解决 issue #858修复通过 GetRowHeight 获取行高度谬误的问题修复局部状况下因范畴解析异样导致获取和删除自定义名称谬误的问题,解决 issue #879修复设置自定义名称时关联工作表索引谬误的问题修复设置列款式时已有单元格款式未被更新的问题,解决 issue #467修复应用非法数据援用范畴创立数据透视表时导致的潜在 panic 的问题修复局部状况下读取数字精度异样的问题,解决 issue #848 和 #852修复设置数据验证规定时,局部状况下因未进行 XML 字符本义解决导致生成文档损坏的问题,解决 issue #971修复设置数据验证规定长度校验不精确问题,解决 issue #972修复由工夫解析异样导致的,局部状况下读取带有工夫或日期数字格局单元格时 CPU 资源占用率过高问题,解决 issue #974修复局部状况下,当自定义数字格局为日期时,月份解析失败的问题性能优化通过 Save 保留或 SaveAs 另存文档时的内占用升高约 19%其余修复潜在的代码平安问题 CWE-190 和 CWE-681Go Modules 依赖模块更新单元测试与文档更新继续集成服务改用 GitHub Action蕴含简体中文、英语、法语、俄语、日语、韩语、阿拉伯语、德语和西班牙语的多国语言文档网站更新

August 2, 2021 · 1 min · jiezi

关于golang:GO进阶训练营完结

download:GO进阶训练营【完结】import java.io.BufferedReader;import java.io.DataOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.net.HttpURLConnection;import java.net.URL;import java.net.URLEncoder;import java.util.HashMap;import java.util.Map;import net.sf.json.JSONObject;/***星座运势调用示例代码 - 聚合数据*在线接口文档:http://www.juhe.cn/docs/58**/public class JuheDemo { public static final String DEF_CHATSET = "UTF-8";public static final int DEF_CONN_TIMEOUT = 30000;public static final int DEF_READ_TIMEOUT = 30000;public static String userAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36";//配置您申请的KEYpublic static final String APPKEY ="*************************";//1.运势查问public static void getRequest1(){ String result =null; String url ="http://web.juhe.cn:8080/constellation/getAll";//请求接口地址 Map params = new HashMap();//请求参数 params.put("key",APPKEY);//利用APPKEY(利用粗疏页查问) params.put("consName","");//星座名称,如:白羊座 params.put("type","");//运势类型:today,tomorrow,week,nextweek,month,year try { result =net(url, params, "GET"); JSONObject object = JSONObject.fromObject(result); if(object.getInt("error_code")==0){ System.out.println(object.get("result")); }else{ System.out.println(object.get("error_code")+":"+object.get("reason")); } } catch (Exception e) { e.printStackTrace(); }}public static void main(String[] args) {}/** * * @param strUrl 请求地址 * @param params 请求参数 * @param method 请求方法 * @return 网络请求字符串 * @throws Exception */public static String net(String strUrl, Map params,String method) throws Exception { HttpURLConnection conn = null; BufferedReader reader = null; String rs = null; try { StringBuffer sb = new StringBuffer(); if(method==null || method.equals("GET")){ strUrl = strUrl+"?"+urlencode(params); } URL url = new URL(strUrl); conn = (HttpURLConnection) url.openConnection(); if(method==null || method.equals("GET")){ conn.setRequestMethod("GET"); }else{ conn.setRequestMethod("POST"); conn.setDoOutput(true); } conn.setRequestProperty("User-agent", userAgent); conn.setUseCaches(false); conn.setConnectTimeout(DEF_CONN_TIMEOUT); conn.setReadTimeout(DEF_READ_TIMEOUT); conn.setInstanceFollowRedirects(false); conn.connect(); if (params!= null && method.equals("POST")) { try { DataOutputStream out = new DataOutputStream(conn.getOutputStream()); out.writeBytes(urlencode(params)); } catch (Exception e) { // TODO: handle exception } } InputStream is = conn.getInputStream(); reader = new BufferedReader(new InputStreamReader(is, DEF_CHATSET)); String strRead = null; while ((strRead = reader.readLine()) != null) { sb.append(strRead); } rs = sb.toString(); } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { reader.close(); } if (conn != null) { conn.disconnect(); } } return rs;}//将map型转为请求参数型public static String urlencode(Map<String,Object>data) { StringBuilder sb = new StringBuilder(); for (Map.Entry i : data.entrySet()) { try { sb.append(i.getKey()).append("=").append(URLEncoder.encode(i.getValue()+"","UTF-8")).append("&"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return sb.toString();}} ...

August 1, 2021 · 2 min · jiezi

关于golang:Go-爬虫框架-predator

predator 是一款基于 fasthttp 开发的高性能爬虫框架。 以后版本尽管尚未实现全副性能,但已可应用。 应用上面是一个示例,根本蕴含了以后已实现的所有性能,应用办法能够参考正文。 1 创立一个 Crawlerimport "github.com/thep0y/predator"func main() { crawler := predator.NewCrawler( predator.WithUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0"), predator.WithCookies(map[string]string{"JSESSIONID": cookie}), predator.WithProxy(ip), // 或者应用代理池 predator.WithProxyPool([]string) )}创立Crawler时有一些可选项用来性能加强。所有可选项参考predator/options.go。 2 发送 Get 申请crawler.Get("http://www.baidu.com")对申请和响应的解决参考的是 colly,我感觉 colly 的解决形式十分难受。 // BeforeRequest 能够在发送申请前,对申请进行一些修补crawler.BeforeRequest(func(r *predator.Request) { headers := map[string]string{ "Accept": "*/*", "Accept-Language": "zh-CN", "Accept-Encoding": "gzip, deflate", "X-Requested-With": "XMLHttpRequest", "Origin": "http://example.com", } r.SetHeaders(headers) // 申请和响应之间的上下文传递,上下文见上面的上下文示例 r.Ctx.Put("id", 10) r.Ctx.Put("name", "tom")})crawler.AfterResponse(func(r *predator.Response) { // 从申请发送的上下文中取值 id := r.Ctx.GetAny("id").(int) name := r.Ctx.Get("name") // 对于 json 响应,倡议应用 gjson 进行解决 body := gjson.ParseBytes(r.Body) amount := body.Get("amount").Int() types := body.Get("types").Array()})// 申请语句要在 BeforeRequest 和 AfterResponse 前面调用crawler.Get("http://www.baidu.com")3 发送 Post 申请与 Get 申请有一点不同,通常每个 Post 的申请的参数是不同的,而这些参数都在申请体中,在BeforeRequest中解决申请体尽管能够,但绝非最佳抉择,所以在结构 Post 申请时,能够间接传入上下文,用以解决与响应的信息传递。 ...

August 1, 2021 · 3 min · jiezi

关于golang:如何借助go-pprof分析真实场景问题

作者:名扬 前言本文将分享一个 笔者在理论业务开发当中遇到的一个乏味的问题。并且本文还会讲述笔者是如何借助 go tool pprof 剖析和定位问题起因的。心愿本篇的分享 可能给读者在日常开发与定位问题方面 带来一些启发。本文如有任何纰漏与倡议欢送在评论区斧正与探讨(笔芯)。 浏览本文后,心愿你能播种: 1.把握剖析与定位问题的办法。2.可能应用 pprof 定位问题或优化性能。3.理解value context 的实现机制。整个问题的剖析及定位步骤如下,通过下图,读者能够对全篇文章 定位问题的流程有个整体的意识,便于后续文章的浏览与了解。 步骤一:编写demo。定位特定问题时,我喜爱独自写个污浊的demo。1 这样能够去除业务等其余代码带来的烦扰。2 便于 debug调试。 3. 便于控制变量,这里的控制变量 就相似 咱们在做科学实验时,如果影响试验后果的因素很多,咱们心愿管制一些变量不变,只变动个别变量,这样更容易找出科学实验中的 因果关系,其实咱们日常定位问题也常常应用这种办法。控制变量法。写个demo 能够更不便的去控制变量。 步骤二:查看git 提交日志。当定位问题时,这其实是一个很天然的想法,如果之前的代码没问题,当初呈现问题了,那么可能是某个谬误的提交造成的。通过查看git 的提交日志能够疾速的缩写问题定位的范畴。这里举荐个 git 好用的工具 git bisect ,git bisect 借助二分查找法,可能在海量的提交里疾速定位到问题的起因。尽管本文没用到这个工具,但理论工作中git bisect 还是十分实用的,感兴趣的小伙伴倡议自行搜寻理解下。 步骤三:引入 pprof 工具。pprof 工具是 go 官网提供的一个可能监控go 程序 cpu、内存、协程 、block 等信息的工具,pprof常被用于问题定位、性能优化、过程监控等方面。pprof 简直是代码无侵入的,任何go程序都能够十分轻松的应用此工具,后文会具体介绍。步骤四:剖析源代码。任何的问题定位,要想齐全理解其背地的起因,都逃不过应用 最奢侈 简略的形式,看代码。 1、问题形容背景形容:公司外部有个redisdao插件,是基于开源库go-redis进行的封装。其目标是提供更加敌对的接口及引入了配置管理、日志记录等性能。读者只须要晓得 redisdao是基于go-redis开源库封装的即可。 问题形容:在开发一个须要频繁向redis写入数据的需要时,发现了一个乏味的景象:通过 redisdao 循环向redis写入数据会越来越慢(留神这里是越来越慢。慢的速度是线性增长的)。这就有些不堪设想了,查看云端redis服务的负载并不高,应用本地redis试验也是如此。为了简化问题定位,去除业务逻辑,写了一个纯正的从redis中的循环读取数据的demo。伪代码如下。通过重复验证发现(上文提到的控制变量法),问题出在redisdao 1.0.21 之后的版本(1.0.21版本没有问题的)。但应用redisdao 1.0.24版本则能够复现此问题。 func main(){ ... key := "testRedisDao" //获取redis实例 r := redisdao.NewSimpleXesRedis(context.Background(), "rediscon") i :=0 t1,t2 := time.Now(),time.Now() for { val, err := r.Get(key, nil) if err == redis.Nil { // 此key不存在 fmt.Printf("not exist key\n") } else if err != nil { // 异样 fmt.Printf("error :%s\n",err.Error()) } i++ if i % 10000 ==0{ t2 = time.Now() fmt.Printf("get value :%s cost: %d ms\n",val,t2.Sub(t1).Milliseconds()) t1 = time.Now() } }}其实定位到这里,很天然的会想到就是redisdao在降级过程中引入了bug。 ...

July 31, 2021 · 3 min · jiezi

关于golang:golang-系列神秘的内存管理

一、概述内存治理在任何的编程语言里都是重头戏,Golang 也不例外。Go 借鉴了 Google 的 TCMalloc,它是高性能的用于 c++ 的内存分配器。其核心思想是内存池 + 多级对象治理 ,能放慢调配速度,升高资源竞争。 二、根底构造在 Go 里用于内存治理的对象构造次要是上面几个: mheap、mspan、arenas、mcentral、mcache。 其中,mspan 是一个根底构造,分配内存时,根本以它为单位。 mcache、mcentral、mheap 起到了内存池的作用,会被预分配内存,当有对应大小的对象须要调配时会先到它们这一层申请。如果这一层内存池不够用时,会依照上面的程序一层一层的往上申请内存: mcache -> mcentral-> mheap -> 操作系统 mspan&&arenas先来看看 mspan 这个根底构造体。首先,当 Go 在程序初始化的时候,会将申请到的虚拟内存划分为以下三个局部: arenas 也就是动态分配的堆区,它将调配到的内存以 8k 为一页进行治理。 然而 "页" 这个单位还是太细了,因而再形象出 mspan 这一层来治理,mspan 示意一组间断的页面。 mspan 记录了这组间断页面的起止地址、页数量、以及类型规格。 对于 mspan 的类型规格有 67 种,每一种都被定义了一个固定大小,当有对象须要分配内存时,就会筛选适合规格的 mspan 调配给对象。 class 1 2 3 4 5 6 ··· 63 64 65 66bytes 8 16 32 48 64 80 ··· 24576 27264 28672 32768例如给大小为 30B 的对象分配内存时,就会抉择类型规格 class 为 3,也就是大小为 32B 的 mspan 调配。这种调配办法跟 linux 用于内存调配的搭档算法差不多,能无效地缩小内存碎片。 ...

July 30, 2021 · 1 min · jiezi

关于golang:Golang-常见的-Http-Post-Get请求

总结下应用Go 申请, 罕用的几种办法.func httpGet() { resp, err := http.Get("https://www.baidu.com?id=1") if err != nil { // handle error } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { // handle error } fmt.Println(string(body))}//一种是应用http.Post形式func httpPost() { resp, err := http.Post("https://www.baidu.com", "application/x-www-form-urlencoded", strings.NewReader("name=cjb")) if err != nil { fmt.Println(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { // handle error } fmt.Println(string(body))}//一种是应用http.PostForm办法func httpPostForm() { resp, err := http.PostForm("https://www.baidu.com", url.Values{"key": {"Value"}, "id": {"123"}}) if err != nil { // handle error } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { // handle error } fmt.Println(string(body))}//有时须要在申请的时候设置头参数、cookie之类的数据,就能够应用http.Do办法。func httpDo() { client := &http.Client{} req, err := http.NewRequest("POST", "https://www.baidu.com", strings.NewReader("name=cjb")) if err != nil { // handle error } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Cookie", "name=anny") resp, err := client.Do(req) defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { // handle error } fmt.Println(string(body))}//针对登录申请之后, 302 Found func httpPostFound() { client := &http.Transport{} req, err := http.NewRequest("POST", "https://www.baidu.com/login.php", strings.NewReader("act=login&user_name=111&password=1111")) if err != nil { // handle error } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Connection", "Keep-Alive") response, err := client.RoundTrip(req) defer response.Body.Close() var cookie string status := response.Status if status == "302 Found" { cookies := response.Cookies() for _, item := range cookies { if item.Name == "FFPOST" { cookie = item.Value break } } } fmt.Println(cookie)}

July 30, 2021 · 1 min · jiezi

关于golang:Go-Lang

1 Go 的根底组成有以下几局部包申明引入包函数变量语句&表达式正文package mainimport "fmt"func main(){ /**/ fmt.Println("hello,world!")}每个 Go 应用程序都蕴含一个名为 main 的包fmt 包实现了格式化 IO(输出/输入)的函数下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须蕴含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。当标识符(包含常量、变量、类型、函数名、构造字段等等)以一个大写字母结尾,如:Group1,那么应用这种模式的标识符的对象就能够被内部包的代码所应用(客户端程序须要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母结尾,则对包外是不可见的,然而他们在整个包的外部是可见并且可用的(像面向对象语言中的 protected )须要留神的是 { 不能独自放在一行,所以以下代码在运行时会产生谬误:···文件名与包名没有间接关系,不肯定要将文件名与包名定成同一个。 文件夹名与包名没有间接关系,并非须要统一。 同一个文件夹下的文件只能有一个包名,否则编译报错。··· Go 语法根底Go 程序能够由多个标记组成,能够是关键字,标识符,常量,字符串,符号。如以下 GO 语句由 6 个标记组成: 数据类型Go语言变量申明变量的个别模式是应用 var 关键字: var identifier typepackage mainimport "fmt"function main(){ var a string = "Runoob" fmt.Println(a) var b,c int = 1,2 fmt.Println(b,c)变量申明第一种,指定变量类型,如果没有初始化,则变量默认为零值第二种,依据值自行判断变狼类型第三种,省略var,留神:=左侧如果没有申明新的变量,就产生编译谬误intVal := 1 // 此时不会产生编译谬误,因为有申明新的变量,因为 := 是一个申明语句相当于 var intVal int intVal =1 多变量申明//类型雷同多个变量, 非全局变量var vname1, vname2, vname3 typevname1, vname2, vname3 = v1, v2, v3var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不须要显示申明类型,主动推断vname1, vname2, vname3 := v1, v2, v3 // 呈现在 := 左侧的变量不应该是曾经被申明过的,否则会导致编译谬误// 这种因式分解关键字的写法个别用于申明全局变量var ( vname1 v_type1 vname2 v_type2)值类型和援用类型Go语言常量const identifier [type] = value多个雷同类型的申明能够简写为: ...

July 30, 2021 · 3 min · jiezi

关于golang:如何有效地测试Go代码

单元测试如果把开发程序比作盖房子,那么咱们必须确保所有的用料都是合格的,否则盖起来的房子就会存在问题。对于程序而言,咱们能够将盖房子的砖头、钢筋、水泥等当做一个个性能单元,如果每个单元是合格的,咱们将有信念认为程序是强壮的。单元测试(Unit Test,UT)就是测验性能单元是否合格的工具。 一个没有UT的我的项目,它的代码品质与工程保障是堪忧的。但在理论开发工作中,很多程序员往往并不写测试代码,他们的开发周期可能如下图所示。 而做了充沛UT的程序员,他们的我的项目开发周期更大概率如下。 我的项目开发中,不写UT兴许能使代码交付更快,然而咱们无奈保障写进去的代码真的可能正确地执行。写UT能够缩小前期解决bug的工夫,也能让咱们释怀地应用本人写进去的代码。从久远来看,后者更能无效地节俭开发工夫。 既然UT这么重要,是什么起因在阻止开发人员写UT呢?这是因为除了开发人员的惰性习惯之外,编写UT代码同样存在难点。 代码耦合度高,短少必要的形象与拆分,以至于不晓得如何写UT。存在第三方依赖,例如依赖数据库连贯、HTTP申请、数据缓存等。可见,编写可测试代码的难点就在于解耦与依赖。 接口与Mock对于难点1,咱们须要面向接口编程。在《接口Interface——塑造强壮与可扩大的Go应用程序》一文中,咱们探讨了应用接口给代码带来的灵便解耦与高扩大个性。接口是对一类对象的抽象性形容,表明该类对象能提供什么样的服务,它最次要的作用就是解耦调用者和实现者,这成为了可测试代码的要害。 对于难点2,咱们能够通过Mock测试来解决。Mock测试就是在测试过程中,对于某些不容易结构或者不容易获取的对象,用一个虚构的对象来创立以便测试的测试方法。 如果咱们的代码都是面向接口编程,调用方与服务方将是松耦合的依赖关系。在测试代码中,咱们就能够Mock 出另一种接口的实现,从而很容易地替换掉第三方的依赖。 测试工具1. 自带测试库:testing在介绍Mock测试之前,先看一下Go中最简略的测试单元应该如何写。假如咱们在math.go文件下有以下两个函数,当初咱们须要对它们写测试案例。 // math.gopackage mathfunc Add(x, y int) int { return x + y}func Multi(x, y int) int { return x * y}如果咱们的IDE是Goland,它有一个十分好用的一键测试代码生成性能。 如上图所示,光标置于函数名之上,右键抉择 Generate,咱们能够抉择生成整个package、以后file或者以后选中函数的测试代码。以 Tests for selection 为例,Goland 会主动在以后 math.go 同级目录新建测试文件math_test.go,内容如下。 // math_test.gopackage mathimport "testing"func TestAdd(t *testing.T) { type args struct { x int y int } tests := []struct { name string args args want int }{ // TODO: Add test cases. } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := Add(tt.args.x, tt.args.y); got != tt.want { t.Errorf("Add() = %v, want %v", got, tt.want) } }) }}能够看到,在Go测试常规中,单元测试的默认组织形式就是写在以 _test.go 结尾的文件中,所有的测试方法也都是以 Test 结尾并且只承受一个 testing.T 类型的参数。同时,如果咱们要给函数名为 Add 的办法写单元测试,那么对应的测试方法个别会被写成 TestAdd 。 ...

July 29, 2021 · 4 min · jiezi

关于golang:讲的是切片但好像又不只是切片

来自公众号:Gopher指北我心田始终有一个欲望,想要高声吆喝“我胡汉三又回来了”,而当初就是适合的机会。 正式开干之前有点手生,太久没有写技术类的文章,总有点怠惰,不得不说保持的确是一件很难的事件。如果不是因为愧疚和写点货色能让本人略微平静下来一些,我可能还将持续怠惰上来。 另外还有一件很有意思的事件分享一下。前一篇在公众号上的文章仅思考就花了近一个月,写只花了一天,而技术文章我个别边思考边写均匀耗时一周。后果是不会骗人的,前一篇文章浏览量首次冲破一千,果然这届读者的思维深度至多也有一个月那么多,老许拜服拜服。 切片底层构造切片和构造体的互转其余不扯多了,咱们还是回归本篇主题。 在正式理解切片底层构造之前, 咱们先看几行代码。 type mySlice struct { data uintptr len int cap int}s := mySlice{}fmt.Println(fmt.Sprintf("%+v", s))// {data:0 len:0 cap:0}s1 := make([]int, 10)s1[2] = 2fmt.Println(fmt.Sprintf("%+v, len(%d), cap(%d)", s1, len(s1), cap(s1))) // [0 0 2 0 0 0 0 0 0 0], len(10), cap(10)s = *(*mySlice)(unsafe.Pointer(&s1))fmt.Println(fmt.Sprintf("%+v", s)) // {data:824634515456 len:10 cap:10}fmt.Printf("%p, %v\n", s1, unsafe.Pointer(s.data)) // 0xc0000c2000, 0xc0000c2000在上述代码中,通过获取切片的地址,并将其转为*mySlice, 胜利取得了切片的长度和容量。以及一个相似于指针一样的货色。而这个指针就是指向存储实在数据的数组,上面咱们来进行验证。 //Data强转为一个数组s2 := (*[5]int)(unsafe.Pointer(s.data))s3 := (*[10]int)(unsafe.Pointer(s.data))// 批改数组中的数据后切片中对应地位的值也产生了变动s2[4] = 4fmt.Println(s1) // [0 0 2 0 4 0 0 0 0 0]fmt.Println(*s2) // [0 0 2 0 4]fmt.Println(*s3) // [0 0 2 0 4 0 0 0 0 0]到这里,切片的底层构造曾经跃然纸上了,不过为了做更进一步的验证,咱们持续测试构造体转为切片的过程。 ...

July 29, 2021 · 3 min · jiezi

关于golang:Go语言如何通过-RPC-来实现跨平台服务

什么是RPC 服务RPC(Remote Procedure Call)近程过程调用,是在分布式系统中,不同节点之间的一种调用形式,能够了解为,在 A 服务器上,调用 B 服务器上利用提供的函数/办法,RPC 由客户端发动,调用服务端的办法进行通信,而后服务端把后果再返回给客户端。 RPC 的外围点有两个:通信协议和序列化。序列化和反序列化是一种把传输数据进行编码和解码的形式,常见的编解码形式有 JSON、Protobuf 等。 RPC 调用的流程:(图片来自百度百科) 客户端调用客户端句柄,并把参数传给客户端句柄客户端句柄将参数打包编码客户端本地零碎发送信息到服务器服务器零碎将信息发送到服务端句柄服务端句柄解析信息(解码)服务端句柄调用真正的服务端程序服务端解决后,通过同样的形式,再把后果返回给客户端网络通信个别是通过 Socket 通信。 Go 语言 RPC 简略入门在 Go SDK 中,内置了 net/rpc 包来实现 RPC。net/rpc 包提供了通过网络拜访服务端对象办法的能力。 咱们通过一个加法运行来展现RPC的调用,服务端示例: server/math_server.go package servertype MathService struct {}type Args struct { A, B int}func (m *MathService) Add(args Args, reply *int) error { *reply = args.A + args.B return nil}下面代码中: 定义了一个 MathService,示意一个近程服务对象;Args 构造体示意参数;Add 办法实现了加法性能,后果通过 replay 指针变量返回。有了这个服务对象,就能够把它注册到裸露的服务列表中,来提供其余客户端的应用。注册 RPC 服务对象,通过 RegisterName 办法即可,代码如下: server_main.go package mainimport ( "log" "net" "net/rpc" "rpctest/server")func main() { rpc.RegisterName("MathService", new(server.MathService)) l, err := net.Listen("tcp", ":8088") //留神 “:” 不要忘了写 if err != nil { log.Fatal("listen error", err) } rpc.Accept(l)}下面代码中: ...

July 29, 2021 · 4 min · jiezi

关于golang:使用聚合模式设计领域模型

畛域驱动设计-聚合模式几种宽泛应用的根本元素实体 具备长久化ID的对象。值对象 作为值汇合的对象。例子:Money对象,由币种、金额组成。工厂 负责实现创建对象逻辑的对象或办法。存储库 用来拜访长久化实体的对象。服务 实现不属于实体或值对象的业务逻辑的对象。聚合聚合是一个边界内的畛域对象的集群,能够将其视为一个单元。它由根实体和可能的一个或多个其余实体和值对象组成。 模式:聚合将畛域模型组织为聚合的汇合,每个聚合都是能够作为一个单元进行解决的一组对象形成的图 聚合规定(点餐零碎)点餐零碎有很多概念,这里只拿简略的,订单、餐厅、用户,来形容一下规定 只援用聚合根 餐厅只援用订单。 用户对于餐厅,只是订单中的内容 订单援用餐厅,用户。 作为关联餐厅、用户,记录各自主键,如有必要,做数据冗余 用户只援用订单。 餐厅对于用户,只是订单中的内容信息。聚合间援用应用主键 餐厅只援用订单主键信息。 订单只援用,餐厅、用户信息。 比拟非凡的是,对于历史订单,可能须要做其余的数据冗余。因为用户地址信息,餐厅信息可能会变动。 用户只援用订单主键信息。在一个事务中,只创立、编辑、删除一个聚合 在一个事务中,只对餐厅、订单、用户,其中之一的信息做改变。 反例: 更新用户信息,并批改用户订单信息。 自身这个操作,就是两个操作。

July 29, 2021 · 1 min · jiezi

关于golang:go-reflect学习

go的reflect很多博客都写了如何应用和各种示例,这里次要剖析下reflect中几个重要构造的关系。 reflect包中最罕用的两个对外接口就是: func TypeOf(i interface{}) Typefunc ValueOf(i interface{}) Value所以不得不提到Type和Value两个类型,看了代码会发现,Type是个接口,Value是个构造体。然而其实两者又都与reflect包中一个公有构造体无关,即rtype看下图:从红色的①②能够看到Value构造体的次要字段typ就是rtype类型从彩色的①②能够看到TypeOf接口返回的就是rtype类型自身,只是这个类型实现了Type定义的办法。所以要看TypeOf返回值调用的办法,就看rtype类型的办法实现。 再来看下Value这个构造体实现的办法 从图中能够看到,尽管Value和Type都有field的概念,然而其两者的field不是一个货色,Type接口中波及的field是一个新的构造体StructField,而Value办法中的field还是Value自身 另外,还有各个办法的调用条件也有限度,个别field的办法只能类型是reflect.Struct的类型能力调用。这个Struct是reflect定义的哦,跟平时说的struct相干,但不齐全是一个货色。 这次先总结到这,后续持续欠缺。

July 28, 2021 · 1 min · jiezi