关于go:PHP转GO必看为什么我觉得GoFrame的garray比PHP的array还好用

前言写过PHP的同学都晓得 PHP的数组Array十分好用,特地灵便。 我在写PHP之前应用Java做安卓开发,在接触PHP的数组Array之后,直呼太香了! 而在学习Go基础知识的时候理解到Go的数组和PHP的数组并不一样;从肯定水平上讲,Go的slice切片类型和PHP的数组array比拟像(不固定长度、援用类型、动静扩容等),然而在开发应用中远远不像PHP的array灵便。 初识GoFrame最近在应用基于Go语言的GoFrame框架撸我的项目,发现GoFrame封装的garray居然比PHP的array还要好用。 近期曾经更新了一系列GoFrame的文章,下文将GoFrame简称为gf。 gf框架有个特点,提供的组件根本都反对设置并发平安开关。显然PHP是不反对并发平安开关的,PHP的数组是并发平安的。PHP-FPM是阻塞的单线程模型,PHP-FPM每个过程里只有一个线程,一个过程同时只能服务一个客户端。 garray特点简介garray反对int/string/interface{}三种罕用的数据类型。garray反对一般数组和排序数组,一般数组的构造体名称定义为Array格局,排序数组的构造体名称定义为SortedArray格局,如下:Array, intArray, StrArray,SortedArray, SortedIntArray, SortedStrArray其中排序数组SortedArray,须要给定排序比拟办法,在工具包gutil中也定义了很多ComparatorXXX的比拟办法,用起来很不便。当然也反对自定义排序形式。根本应用package mainimport ( "fmt" "github.com/gogf/gf/container/garray")func main() { //创立并发平安的int型数组 a := garray.NewIntArray(true) //增加数组项 for i := 0; i < 10; i++ { a.Append(i) } // 打印后果: fmt.Println(a) //"[0,1,2,3,4,5,6,7,8,9]" fmt.Println("数组长度:", a.Len()) fmt.Println("数组的值:", a.Slice()) fmt.Println((a.Get(5))) //依据索引取值 返回值和是否取到了值 5 true // 在指定索引前后插入值 _ = a.InsertAfter(9, 10) _ = a.InsertBefore(0, -1) fmt.Println(a.Slice()) // 搜寻数据项,返回对应的索引 fmt.Println("搜寻值,返回对应索引:", a.Search(5)) // 删除 a.Remove(0) fmt.Println(a.Slice()) // 并发平安 写锁操作 a.LockFunc(func(array []int) { //将最初一项的值改为100 array[len(array)-1] = 100 }) fmt.Println(a) //"[0,1,2,3,4,5,6,7,8,9,100]" // 并发平安 读锁操作 a.RLockFunc(func(array []int) { fmt.Println(array[len(array)-1]) //100 }) // 清空数组 a.Clear() fmt.Println("清空数组之后:", a.Slice())}打印后果 ...

November 14, 2022 · 3 min · jiezi

关于go:Go-微服务实战之如何实现加解密操作的微服务开发

1 前言在上一篇文章——《Go 微服务实战之如何应用 go-micro 写微服务利用》中,咱们介绍了微服务的相干概念和 go-micro 框架的特点。 接下来,咱们将以循序渐进的形式建设一个繁难的提供加解密服务的 Go 微服务项目。首先为了创立微服务,须要后期设计几个实体: 定义服务的 RPC 办法的 protocol buffer 文件具体方法实现的 handler 文件一个公开 RPC 办法的服务器 server一个能够收回 RPC 申请并取得响应后果的客户端 client 2 创立 encryption.proto 文件首先,为了将 protocol buffer 文件编译为 Go 包,须要先装置 protoc,下载点此处,抉择你对应的零碎。 本文是以 Win 进行的示例开发,下载的是 protoc-21.9-win32.zip,解压完后增加到零碎环境变量,如图所示: 而后装置 proto-gen-micro,应用如下命令: go install github.com/go-micro/generator/cmd/[email protected] 接下来,创立咱们的我的项目目录 encryptService 文件夹,而后在其中创立一个 proto 目录,新建一个 encryption.proto 文件,写入如下内容: syntax = "proto3";package main;option go_package="./proto";service Encrypter { rpc Encrypt(Request) returns (Response) {} rpc Decrypt(Request) returns (Response) {}}message Request { string message = 1; string key = 2;}message Response { string result = 2;}下面的文件命名了一个 Encrypter 的服务,有着 Request 和 Response 两条音讯。这两条信息是用来申请加密和解密的。 ...

November 8, 2022 · 4 min · jiezi

关于go:记录一次bufioReader遇到问题后的思考

背景书接上回,话说在上一篇文章中我讲述了对于nutsdb重启速度的优化历程,最初是引入了bufio.Reader来在重启时候读取数据,因为他会在程序和磁盘之间加一层缓存,起到缩小零碎调用的作用。做完之后提交代码发文章,正是春风得意之时,第二周的nutsdb周会(组织个别每周都会开周会,探讨一些事件的进度),佳军和我说这个货色如同有点问题,他用了一些case测试了一下,会报一个CRC校验的异样。 什么是CRC的异样呢,因为磁盘尽管是长久化存储,然而也会有数据失真的危险。在咱们写数据到磁盘之前会做顺便生成一个数据的校验值,也一起存进去。把数据取出来的时候也会用同样的办法生成一个校验值来和读出来的校验值做比对,如果校验值比对不上了就能够阐明拿进去的数据和存进去的数据不统一。 所以如果看到了CRC的异样,大概率是读取的代码有问题,因为磁盘出问题是小概率事件。所以到底是什么神奇的魔法呢?让咱们来一探到底。 剖析问题 当咱们拿到问题的时候当然是想着复现问题啦,能复现的问题都不是大问题。所以我拿到了佳军的测试代码之后在我本地跑了起来,刚开始的时候还抱着侥幸心理,感觉可能是电脑之间的差别?在我本地跑就不会有事了。不过事实很快就打脸了。 打个断点在报错的中央,看看报错那一刻的上下文是怎么样的。查看一个读取进去的数据,如下图所示。咱们能够看到在这个buffer前面存在大量的空数据。也就是说很多数据没有被读出来。那么为什么会这样子呢?我看了一眼代码,其实我只是简简单单的调用bufio.Reader提供的Read办法去读取数据,那么要持续深刻探索这个问题很显著就须要深刻到bufio.Reader的源码层面了。 type Reader struct { buf []byte rd io.Reader // reader provided by the client r, w int // buf read and write positions err error lastByte int // last byte read for UnreadByte; -1 means invalid lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid}func (b *Reader) Read(p []byte) (n int, err error) { n = len(p) if n == 0 { if b.Buffered() > 0 { return 0, nil } return 0, b.readErr() } if b.r == b.w { if b.err != nil { return 0, b.readErr() } if len(p) >= len(b.buf) { n, b.err = b.rd.Read(p) if n < 0 { panic(errNegativeRead) } if n > 0 { b.lastByte = int(p[n-1]) b.lastRuneSize = -1 } return n, b.readErr() } b.r = 0 b.w = 0 n, b.err = b.rd.Read(b.buf) if n < 0 { panic(errNegativeRead) } if n == 0 { return 0, b.readErr() } b.w += n } n = copy(p, b.buf[b.r:b.w]) b.r += n b.lastByte = int(b.buf[b.r-1]) b.lastRuneSize = -1 return n, nil}从代码中能够看出,其实Reader是本人先读一个比拟大的货色存进Buffer外面,而后咱们调用Read读取他再从Buffer外面拿。Read的运作流程是怎么的呢?流程是这样的。 ...

November 8, 2022 · 3 min · jiezi

关于go:Go-十年了终于想起要统一-log-库了

本文参加了思否技术征文,欢送正在浏览的你也退出。大家好,我是煎鱼。 在日常工作中,打日志是很常见的动作。毕竟不打日志,从外部来讲,一旦出问题,定位、排查都会变的十分艰难。谁也不想大半夜在那靠猜解决问题。 在其余方面,对日志的存储的内容、时长、平安均有不同水平的合规要求,应答客户诉求和提单上门的事件。 日志好不好用,就成了重要的诉求了。 规范库 log 很痛思考一个问题:平时你在写 Go 工程时,是否很少间接应用官网规范库 log? 在正式我的项目中,大多是优先应用几个爆款第三方库,例如:Logrus、Zap、zerolog。而规范库 log,在长期调试,屏幕输入的场景居多,占比拟少。 这问题出在了哪里?次要集中在以下方面: 没有日志分级。不便于分类、定位、排查问题,例如:Error、Warn、Info、Debug 等。没有结构化日志。只提供格式化日志,不提供结构化,不便于程序读取、解析,例如:Json 格局。没有扩展性,灵便度差。规范库 log 的日志输入都是固定格局,没有一个 Logger 接口标准,让大家都恪守,以至于当初社区纯天然演进,难相互兼容。除此之外,在用户场景上,有着不蕴含上下文(context)信息、性能不够强劲、无奈引入自定义插件等扩大诉求。基本上第三方库均有实现的,根本都用户的痛点之一。 为什么不早点解决你可能会想,规范库 log 作为 Go 生态里的外围库,为什么不早点解决? 实际上在 2017 年时,有在社区进行了大规模探讨,惋惜放弃了。起因是:“咱们还没有找到足够多的导入和应用具体 Logger 的 Go 库,因而没有理由持续发展这项工作”。 如下图: 持续摆烂。 救星 slog 库诞生探讨和指标在 2022 年 8 月,Go 团队的 @Jonathan Amsterdam 发动了 discussion: structured, leveled logging 的探讨,试图与这个乱象再度一决雌雄。 提案(含探讨)的指标是: 使用方便。对现有 Logger 库的考察阐明,开发人员更想要一个简洁且易懂的日志 API。高性能。新的 API 心愿做到最大限度的缩小内存调配和锁定。与运行时跟踪集成。Go 团队正在开发和改良运行时跟踪零碎,基于新 Logger 库的日志将能够无缝连接到这个跟踪零碎中,开发人员可能实现程序操作与运行时的行为相关联。指标涵盖了前文背景中提到的痛点。我关注到上述的第三点,来自 Go 团队本人的需要,果然最优先要做的需要都是本人想要 PUSH 的需要?雾了雾了。 毕竟曾经 10 年了,本探讨中失去了许多人的倡议和推动,胜利孵化。 ...

November 8, 2022 · 2 min · jiezi

关于go:Go渗透测试编程二TCP扫描器和代理

留神因为系列文章仅是我在浏览BHG时进行的一些记录,并不是一个很谨严的教程,且本章题目会追随书的章节目录。 TCP协定传输控制协议(Transmission Control Protocel,TCP)是面向连贯的牢靠通信的次要规范,以及古代网络的根底。BHG要求咱们可能从TCP的工作原理动手,开发能够辨认关上/敞开的端口,通过端口转发绕过进口限度。 TCP的握手机制分为以下三种状况 端口凋谢如果端口是凋谢的,就会进行经典的三次握手:syn->syn-ack->ack;端口敞开如果端口敞开,服务器会以rst数据包而非syn-ack数据包进行响应;防火墙过滤如果流量被防火墙过滤,客户端通常不会从服务器收到任何响应;编写TCP扫描器通过编写一个端口扫描器去了解TCP握手过程以及3种端口状态,以确定TCP端口是否可用,或者已敞开和应用过滤状态进行响应 Go的net包通过net.Dial(network, address string),启动客户端到服务端的连贯network参数:字符串类型,用于标识要启动的连贯的类型。Dial不仅实用于TCP,还能够用于创立UNIX套接字、UDP和第4层协定的连贯address参数:字符串类型,标识要连贯的主机。对于IPv4/TCP连贯,应用host:port的模式返回值:Conn,err;若连贯胜利,err为nil 示例:根本的单端口扫描器 package mainimport ( "fmt" "net")func main() { _, err := net.Dial("tcp", "scanme.namp.org:80") if err == nil { fmt.Println("Connection successful") }}示例:非并发多端口扫描器 package mainimport ( "fmt" "net")func main() { for i := 1; i <= 1024; i++ { address := fmt.Sprintf("scanme.nmap.org:%d", i) connm err := net.Dial("tcp", address) if err != nil { continue } conn.Close() fmt.Println("%d open\n", i) }}示例:并发扫描器 package mainimport ( "fmt" "net" "sync")func main() { //同步计数器 var wg sync.WaitGroup //扫描前1024个端口 for i := 1; i < 1024; i++ { wg.Add(1) //启动goroutine go func(j int) { //延后至外层函数返回时执行 defer wg.Done() address := fmt.Sprintf("scanme.nmap.org:%d", j) conn, err := net.Dial("tcp", address) if err != nil { return } conn.Close() fmt.Printf("%d open\n", j) }(i) } //函数阻塞,期待同步计数器为0 wg.Wait()}毛病:扫描过多的主机或端口,可能会导致网络或零碎限度,造成后果不正确应用goroutine池治理正在进行的并发工作,通过for循环创立肯定数量的worker goroutine作为资源池 ...

November 7, 2022 · 2 min · jiezi

关于go:go如何从零编写protoBuf-插件

本期的次要内容将手把手教会大家,编写probuf的go插件,以我本人编写的一个生成构造体的插件为例子。我的项目位于 https://github.com/hisheng/pr... 一、自定义ProtoBuf插件介绍咱们罕用的go反对protobuf插件有 插件名称介绍protoc-gen-go通过.proto文件生成.pb.go文件protoc-gen-doc通过.proto文件生成文档protoc-gen-go-errors通过.proto文件生成errorprotoc-gen-go-errors通过.proto文件生成errorprotoc-gen-go-grpc通过.proto文件生成grpcprotoc-gen-go-http通过.proto文件生成httpprotoc-gen-openapi通过.proto文件生成openapiprotoc-gen-validate通过.proto文件生成validate验证protoc-gen-go-enum通过.proto文件生成go自定义枚举基本上该有的插件,都曾经有写过了,所以咱们能够查看他人的源码,来察看怎么来写一个插件。 二、手把手写自定义插件(以protoc-gen-go-struct为例)2.1 新建protoc-gen-go-struct文件夹,并且进入到这个文件夹里。mkdir protoc-gen-go-struct && cd protoc-gen-go-struct2.2 新建go module我的项目咱们执行 go mod init modName 命名来生成我的项目如下: go mod init github/hisheng/protoc-gen-go-struct此时咱们查看文件夹发现生产了一个 go.mod文件,查看一下代码如下: module github/hisheng/protoc-gen-go-structgo 1.192.3 写main函数咱们在我的项目根目录,写一个main.go如下 touch main.go此时咱们发现我的项目根目录下,生成了一个main.go文件。咱们写一个main函数,代码如下: package mainimport ( "google.golang.org/protobuf/compiler/protogen")func main() { protogen.Options{}.Run(func(gen *protogen.Plugin) error { for _, f := range gen.Files { if !f.Generate { continue } generateFile(gen, f) } return nil })}这个main()函数大家能够间接复制,根本所有的插件都是这样的格局,当然这个main也能够承受参数,这里咱们简化,先不介绍,感兴趣的人,能够参考protoc-gen-go的main函数来写。咱们本人写的办法次要是 generateFile(gen, f) 这个函数。这个函数用来读取.proto文件,并且生产go文件。 2.4 自定义generateFile(gen, f)函数这个函数全称是 generateFile(gen protogen.Plugin, file protogen.File),承受的两个参数 gen *protogen.Plugin 为生成的插件,次要用来生成go文件file *protogen.File 为.proto文件对他的file对象我这里的次要代码是: ...

November 7, 2022 · 2 min · jiezi

关于go:Go-微服务实战之如何使用-gomicro-写微服务应用

什么是微服务?什么是微服务(microservice)?这是企业界正在向计算界提出的问题。一个产品的可持续性取决于它的可批改水平。 大型产品如果不能失常保护,就须要在某个工夫点停机保护。而微服务架构用细化的服务取代了传统的单体服务,这些服务定义了明确的 RPC 或音讯驱动的 API 边界。 微服务架构有别于更为传统的单体式计划,可将利用拆分成多个外围性能。每个性能都被称为一项服务,能够独自构建和部署,这意味着各项服务在工作(和呈现故障)时不会相互影响。 微服务带来了以下益处: 每个服务都能够由专一于此服务的团队独立开发。小团队能够通过在一组小的性能上工作来进行并行迭代。开发人员能够自由选择开发技术,对新的开发人员来说,可扩展性很强。微服务架构能够使每个微服务独立部署。对系统的单个组件反对继续集成(CI)和继续交付(CD)。微服务架构使得每个服务都可独立扩大。利用松耦合的架构提供更轻松的软件替换。微服务架构不与特定的技术相分割。在议论微服务时,编排和服务发现是微服务中十分重要的局部。像 Kubernetes 这样的工具能够用来编排和协调 Docker 容器。一般来说,微服务的最佳实际就是每个微服务有一个 Docker 容器。 服务发现是对微服务实例的 IP 地址的自动检测。这种形式打消了硬编码 IP 地址的潜在威逼,硬编码会导致服务之间不足分割。 单体架构与微服务架构的区别下图描述了单体架构和微服务架构的结构图。 图的右边就是单体架构的示意图,如图所示:单体架构将所有的性能(如 UI、日志、数据层、零碎逻辑、数据库等)都集成在一个零碎中,像是一个紧耦合的架构。 相同,微服务是独立的实体,每个性能都是独自的服务,如日志服务、文件服务、零碎逻辑服务等,更易于批改和替换,每个服务都能够通过各种近程传输机制进行沟通,如 HTTP、REST 或者 RPC。服务之间的替换的数据格式能够是 JSON 或者 Protocol buffers, 微服务还能够解决各种申请点,如 UI 和 API 客户端。 微服务能够被任何语言实现(Java、Go、Python、 Rust、 NodeJS 等),因为其有着松耦合的性质,每个独立的服务还能够今后被任何其余新技术或业务所须要的技术所替换。 对于微服务的相干常识就简略介绍到这,感兴趣的同学能够看看文末的举荐浏览局部,都是十分好的微服务学习材料。Go Micro 介绍优良微服务框架一览Java 社区中有着十分驰名的框架用于构建微服务零碎。如: Spring: Spring Boot 是用于编写微服务的风行 Java 框架。Spring Cloud:基于 Spring Boot,为微服务体系开发中的架构问题,提供了一整套的解决方案——服务注册与发现,服务生产,服务爱护与熔断,网关,分布式调用追踪,分布式配置管理等。Dropwizard:一个开源的 RESTful 疾速开发框架,对微服务的开发也极其敌对,而且性能很强Micronaut:是一个古代的、基于 JVM 的全栈微服务框架,旨在构建模块化、易于测试的微服务应用程序Apache Dubbo:由阿里巴巴开源的分布式服务化治理框架,是一款微服务框架,为大规模微服务实际提供高性能 RPC 通信、流量治理、可观测性等解决方案,涵盖 Java、Golang 等多种语言 SDK 实现。以上都是十分有名的微服务框架,在 Go 语言中,也有很多驰名的框架(go-kit、go-kratos、go-zero 等), Go Micro 也是其中之一,截止发文 Github Star 数量达到了 19.6k。 ...

November 7, 2022 · 1 min · jiezi

关于go:Go编程题多协程顺次打印数字

1. 问题形容创立N个协程,每个协程负责打印一个数字,编程实现将所有数字依次输入。Input: N = 5Output: 1 2 3 4 52. 解决方案2.1. 竞争计划通过竞争互斥锁实现import ( "fmt" "sync")var N = 5// 竞争型func race() { // 以后执行到的序号 curSeq := 1 var mu sync.Mutex var wg sync.WaitGroup for i := 1; i <= N; i++ { // 将以后协程退出wg wg.Add(1) go func(id int) { for { // 尝试获取锁,并打印数据 mu.Lock() if id == curSeq { fmt.Println(curSeq) curSeq += 1 // 打印工作实现,解锁并跳出循环 mu.Unlock() break } // 发现没到本人打印的时候,解锁,把机会留给他人 mu.Unlock() } // 子协程工作实现,来到wg wg.Done() }(i) } // 主协程期待子协程实现 wg.Wait()}2.2. 协同计划通过channel通信协同打印import ( "fmt" "sync")var N = 5func collaborate() { chans := make([]chan bool, N+1) for i := 0; i <= N; i++ { chans[i] = make(chan bool) } for i := 1; i <= N; i++ { go func(id int) { // 以后协程阻塞,直到收到前一个协程的信号 <-chans[id-1] // 打印本人对应的序号 fmt.Println(id) // 告诉下一个协程干活 chans[id] <- true }(i) } chans[0] <- true // 期待最初一个协程实现工作 <-chans[N]}

November 6, 2022 · 1 min · jiezi

关于go:Go常见错误第14篇过度使用getter和setter方法

前言这是Go常见谬误系列的第14篇:适度应用getter和setter办法。 素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 常见谬误和最佳实际现状写Java或者C++的人,可能会习惯上面的编程模式: 将不心愿内部间接拜访的类成员变量设置为private公有成员。在类里定义public的get和set办法,用于内部获取和批改这个成员变量的值。get办法咱们叫做getter,set办法叫做setter。这是一种数据封装模式,在Java和C++里被宽泛应用。 然而在Go语言里,官网素来没有倡议应用getter和setter,咱们能够间接拜访构造体里的成员变量。 成员变量的可见性通过构造体标识符首字母大小写以及成员变量首字母大小写来管制到package这个层面。 如果构造体要被其它package应用,那构造体的标识符或者说构造体的名称首字母要大写。如果构造体的成员要被其它package应用,那构造体和构造体的成员标识符首字母都要大写,否则只能在以后包里应用。举个Go规范库里的time.Timer构造体的例子: // The Timer type represents a single event.// When the Timer expires, the current time will be sent on C,// unless the Timer was created by AfterFunc.// A Timer must be created with NewTimer or AfterFunc.type Timer struct { C <-chan Time r runtimeTimer}Timer构造体定义如上所示,外面有一个成员变量C用于接管Timer到点后的以后工夫。 Timer和C都是大写,所以咱们能够间接在上面的代码里拜访Timer里的成员变量C拿到以后工夫。 package mainimport ( "fmt" "time")func main() { // print current time fmt.Println(time.Now()) // NewTimer creates a new Timer that will send // the current time on its channel after at least duration d. timer := time.NewTimer(5 * time.Second) // print current time fmt.Println(<-timer.C)}下面程序执行后果是: ...

November 6, 2022 · 1 min · jiezi

关于go:Go-Web实战之如何增加应用配置模块

1 介绍当咱们为本人编写程序时,通常会将一些重要的配置项间接写在源代码里,比方:服务器监听的端口、数据库应用的名称和端口号、HTTP申请超时的持续时间... 然而,如果咱们尝试将这个我的项目开源分享给别人应用,用户应用的数据库的用户名和名称可能与你不雷同,甚至你还要为他们的服务器应用另一个端口。 如果你还设置了数据库的明码的话,为了平安,更不可能在代码中信息泄露进去。因而,本节,将介绍如何减少咱们的 sports 利用的配置模块。 2 减少配置模块在许多的开源我的项目中,配置都是通过键值(key-value) 数据结构来解决的。在实在利用中,你常常会发现一个公开配置选项的类(或者是构造体),这个类常常会将文件解析进去,将每个抉择赋值。应用程序通常会提出命令行选项以调整配置。 2.1 定义 Configuration 接口接下来,咱们为应用程序减少配置的能力,这样下面说的很多配置就不必在代码文件中定义。1、创立 sports/config 文件夹,而后新建一个 config.go 文件,写入如下的代码: package configtype Configuration interface { GetString(name string) (configValue string, found bool) GetInt(name string) (configValue int, found bool) GetBool(name string) (configValue bool, found bool) GetFloat(name string) (configValue float64, found bool) GetStringDefault(name, defVal string) (configValue string) GetIntDefault(name string, defVal int) (configValue int) GetBoolDefault(name string, defVal bool) (configValue bool) GetFloatDefault(name string, defVal float64) (configValue float64) GetSection(sectionName string) (section Configuration, found bool)}能够看到,Configuration 接口定义了检索配置设置的办法,反对获取字符串 string、数字 int、浮点型 float64、布尔型 bool 的值: ...

November 4, 2022 · 4 min · jiezi

关于go:Go-Web-项目实战之如何创建项目及增加日志功能

从本文开始,咱们来看一下如何从零搭建一个 Go 我的项目。 回顾一下根底的 Go 我的项目运行过程首先,新建一个 sports 的文件,而后键入此文件目录下,抉择在终端中关上,应用如下命令初始化我的项目: go mod init sports 而后,咱们创立一个 main.go 的文件,写入如下代码: package mainimport "fmt"func writeMessage() { fmt.Println("Let's Go")}func main() { writeMessage()}回到终端,编译并执行咱们的我的项目: go run .就像之前第一次写一个 HelloWorld 我的项目一样,run 命令会胜利编译并执行咱们的 Println() 内的字符串 Let's Go,输入后果如下: $ go run .Let's Go创立一些根本的我的项目性能家喻户晓,Web 利用有一些根底的服务和性能如日志、文件配置等等。所以咱们能够为 sports 我的项目提供一些根本的服务,这些服务将为运行网络应用程序提供根底。 创立日志接口 Logger先来实现第一个服务器性能——日志。Go 规范库中的 log 包为创立日志提供了一套十分用户敌对的基本功能,但在理论开发过程中,仍须要一些额定的性能来筛选一些信息详情。 创立 sports/logging 文件夹,而后在这个文件夹上面创立一个 logging.go 文件,而后写入如下代码: package loggingtype LogLevel intconst ( Trace LogLevel = iota Debug Information Warning Fatal None)type Logger interface { Trace(string) Tracef(string, ...interface{}) Debug(string) Debugf(string, ...interface{}) Info(string) Infof(string, ...interface{}) Warn(string) Warnf(string, ...interface{}) Panic(string) Panicf(string, ...interface{})}下面的代码定义了 Logger 接口,并具体申明了具体的日志音讯的具体方法,别离有着不同的日志级别,LogLevel 从 Trace 到 Fatal 。值得注意的是也有一个无级别的 None, 意味着没有日志输入。 ...

November 4, 2022 · 2 min · jiezi

关于go:Go切片排序

Go 语言规范库提供了sort包,用于对切片和用户定义的汇合进行排序。具体示例如下: 根本排序package mainimport ( "fmt" "sort")func main() { //float 从小到大排序 f := []float64{5.2, -1.3, 0.7, -3.8, 2.6} // unsorted sort.Float64s(f) fmt.Println(f) //[-3.8 -1.3 0.7 2.6 5.2] //float 倒序 sort.Sort(sort.Reverse(sort.Float64Slice(f))) fmt.Println(f) //[5.2 2.6 0.7 -1.3 -3.8] // int 正序 从小到大排序 i := []int{5, 6, 3, 7, 9} // unsorted sort.Ints(i) fmt.Println(i) //[3 5 6 7 9] //int 倒序 sort.Sort(sort.Reverse(sort.IntSlice(i))) fmt.Println(i) //[9 7 6 5 3] //string 正序 字母程序 s := []string{"Go", "Bravo", "Gopher", "Alpha", "Grin", "Delta"} sort.Strings(s) fmt.Println(s) //[Alpha Bravo Delta Go Gopher Grin] //int 倒序 sort.Sort(sort.Reverse(sort.StringSlice(s))) fmt.Println(s) //[Grin Gopher Go Delta Bravo Alpha]}在升序切片查找value在已排序的切片中搜寻x,并返回由搜寻指定的索引。如果x不存在,返回值是要插入x的索引(它能够是len (a))。切片必须按升序排序。 ...

November 4, 2022 · 3 min · jiezi

关于go:在goland中goimports和gofmt的安装和使用

一、goimports介绍二、gofmt介绍三、在goland中的装置golang曾经帮咱们在 Tools/File Watcher中集成了gofmt曾经goimports了,咱们只有点击 + 号,把装置一下就好。

November 4, 2022 · 1 min · jiezi

关于go:使用建造者模式封装go的http库

在日常开发中常常会用到http库来发送申请,就将罕用的申请办法封装了一个库,灵便又好用。这里用到了建造者模式,相似的应用场景都能够这样来实现:具体实现 package requestsimport ( "bytes" "context" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "net/url" "strings" "time")const ( contentType = "Content-Type")type HttpSend struct { ctx context.Context // 上下文 client *http.Client // 客户端 request *http.Request // 申请体 url, httpProxy string // 申请门路 getValue interface{} // get申请体 postValue interface{} // post申请体 postFormValue url.Values // form-data post申请体 header map[string]string // header timeout time.Duration // 超时工夫 tryNum int // 重试次数 doNum int // 已重试次数 intervalTime time.Duration // 重试间隔时间 err error // 谬误}// 应用建造者模式初始化HttpClientfunc NewHttpClient(ctx context.Context, options ...HttpOptions) *HttpSend { client := new(HttpSend) client.ctx = ctx for i := 0; i < len(options); i++ { options[i](client) } client.NewClient(ctx) if client.timeout != 0 { client.client.Timeout = client.timeout } return client}// 实例化HttpClientfunc (h *HttpSend) NewClient(ctx context.Context) { h.client = new(http.Client) if h.httpProxy != "" { var proxyUrl *url.URL proxyUrl, h.err = url.Parse(h.httpProxy) if h.err != nil { log.Errorf(ctx, fmt.Sprintf("初始化httpClient-httpProxy解析谬误, error:%s", h.err)) } h.client.Transport = &http.Transport{ Proxy: http.ProxyURL(proxyUrl), } }}// 包装了重试的do办法func (h *HttpSend) HttpDo() []byte { var resp *http.Response var body []byte resp, h.err = h.client.Do(h.request) if h.err == nil { log.Infof(h.ctx, fmt.Sprintf("HttpDo,resp:%+v, error:%+v", resp, h.err)) defer resp.Body.Close() if resp.StatusCode == http.StatusOK { body, h.err = ioutil.ReadAll(resp.Body) } else { h.err = errors.New(resp.Status) } } else { for h.doNum < h.tryNum { h.doNum++ log.Errorf(h.ctx, fmt.Sprintf("HttpDo,url:%s,resp:%+v,重试次数:%d,error:%s", h.url, resp, h.doNum, h.err)) time.Sleep(h.intervalTime) h.NewClient(h.ctx) body = h.HttpDo() return body } } return body}// 发送get申请func (h *HttpSend) HttpGet() ([]byte, error) { if h.err != nil { return []byte{}, h.err } defer func(t time.Time) { log.Info(h.ctx, fmt.Sprintf("took %v/s", time.Since(t).Seconds())) }(time.Now()) log.Info(h.ctx, fmt.Sprintf("url:%s,request:%s", h.url, h.url)) h.request, h.err = http.NewRequest(http.MethodGet, h.url, nil) if h.err != nil { return []byte{}, h.err } h.setHeader() var body []byte body = h.HttpDo() log.Infof(h.ctx, fmt.Sprintf("url:%s,response:%s,error:%s", h.url, string(body), h.err)) return body, h.err}// 发送json参数的get申请func (h *HttpSend) HttpGetWithJson() ([]byte, error) { if h.err != nil { return []byte{}, h.err } getValue, err := json.Marshal(h.getValue) if err != nil { log.Errorf(h.ctx, fmt.Sprintf("err:%s", err)) return nil, err } log.Infof(h.ctx, fmt.Sprintf("url:%s,request:%+v", h.url, h.getValue)) h.request, h.err = http.NewRequest(http.MethodGet, h.url, strings.NewReader(string(getValue))) if _, ok := h.header[contentType]; !ok { h.request.Header.Set(contentType, "application/json") } h.setHeader() var body []byte body = h.HttpDo() log.Infof(h.ctx, fmt.Sprintf("url:%s,response:%s,error:%s", h.url, string(body), h.err)) return body, h.err}// 发送json格局的post申请func (h *HttpSend) HttpPost() ([]byte, error) { if h.err != nil { return []byte{}, h.err } postValue, err := json.Marshal(h.postValue) if err != nil { log.Errorf(h.ctx, fmt.Sprintf("err:%s", err)) return nil, err } log.Infof(h.ctx, fmt.Sprintf("url:%s,request:%s", h.url, h.postValue)) h.request, h.err = http.NewRequest(http.MethodPost, h.url, strings.NewReader(string(postValue))) if _, ok := h.header[contentType]; !ok { h.request.Header.Set(contentType, "application/json") } h.setHeader() var body []byte body = h.HttpDo() log.Infof(h.ctx, fmt.Sprintf("url:%s,response:%s", h.url, string(body))) return body, h.err}// 发送form-data的post申请func (h *HttpSend) HttpPostForm() ([]byte, error) { if h.err != nil { return []byte{}, h.err } log.Infof(h.ctx, fmt.Sprintf("url:%s,postValue:%+v", h.url, h.postFormValue)) h.request, h.err = http.NewRequest(http.MethodPost, h.url, strings.NewReader(h.postFormValue.Encode())) if h.err != nil { return []byte{}, h.err } h.request.Header.Set(contentType, "application/x-www-form-urlencoded") var body []byte body = h.HttpDo() log.Infof(h.ctx, fmt.Sprintf("send post success,body:%s,h.err:%s", string(body), h.err)) return body, h.err}// 设置headerfunc (h *HttpSend) setHeader() { for k, v := range h.header { h.request.Header.Set(k, v) }}type HttpOptions func(*HttpSend)// 设置申请门路func UrlOptions(url string) HttpOptions { return func(client *HttpSend) { client.url = url }}// 设置post申请体func PostValueOptions(postValue interface{}) HttpOptions { return func(client *HttpSend) { client.postValue = postValue }}// 设置form-data格局的post申请体func PostFormValueOptions(postFormValue url.Values) HttpOptions { return func(client *HttpSend) { client.postFormValue = postFormValue }}// 设置get申请体func GetValueOptions(getValue interface{}) HttpOptions { return func(client *HttpSend) { client.getValue = getValue }}// 设置超时工夫func TimeOptions(timeout time.Duration) HttpOptions { return func(client *HttpSend) { client.timeout = timeout }}// 设置代理func ProxyOptions(proxy string) HttpOptions { return func(client *HttpSend) { client.httpProxy = proxy }}// 设置申请头func HeaderOptions(header map[string]string) HttpOptions { return func(client *HttpSend) { client.header = header }}// 设置重试参数func TryOptions(tryNum int, intervalTime time.Duration) HttpOptions { return func(client *HttpSend) { client.tryNum = tryNum client.intervalTime = intervalTime }}应用 ...

November 3, 2022 · 3 min · jiezi

关于go:Go语言躲坑经验总结

作者 | 百度小程序团队 导读 本文收集一些应用Go开发过程中非常容易踩坑的case,所有的case都有具体的代码示例,以及针对的代码修复办法,以防止大家再次踩坑。通常这些坑的特点就是代码失常能编译,但运行后果不迭预期或是引入内存破绽的危险。 全文7866字,预计浏览工夫20分钟。 01 参数传递误用1.1 误对指针计算Sizeof对任何指针进行unsafe.Sizeof计算,返回的后果都是 8 (64位平台下)。稍不留神就会引发谬误。 谬误示例: func TestSizeofPtrBug(t *testing.T) { type CodeLocation struct { LineNo int64 ColNo int64 } cl := &CodeLocation{10, 20} size := unsafe.Sizeof(cl) fmt.Println(size) // always return 8 for point size}倡议应用示例:独自编写一个只解决值大小的函数 ValueSizeof。 func TestSizeofPtrWithoutBug(t *testing.T) { type CodeLocation struct { LineNo int64 ColNo int64 } cl := &CodeLocation{10, 20} size := ValueSizeof(cl) fmt.Println(size) // 16}func ValueSizeof(v any) uintptr { typ := reflect.TypeOf(v) if typ.Kind() == reflect.Pointer { return typ.Elem().Size() } return typ.Size()}1.2 可变参数为any类型时,误传切片对象当参数的可变参数是any类型时,传入切片对象时肯定要用开展形式。 ...

November 3, 2022 · 5 min · jiezi

关于go:golang中的错误处理

0.1、索引https://waterflow.link/articles/1666716727236 1、panic当咱们执行panic的时候会完结上面的流程: package mainimport "fmt"func main() { fmt.Println("hello") panic("stop") fmt.Println("world")}go run 9.go hellopanic: stop然而panic也是能够捕捉的,咱们能够应用defer和recover实现: package mainimport "fmt"func main() { defer func() { if r := recover(); r != nil { fmt.Println("recover: ", r) } }() fmt.Println("hello") panic("stop") fmt.Println("world")}go run 9.gohellorecover: stop那什么时候适宜panic呢?在 Go 中,panic 用于示意真正的异样,例如程序谬误。咱们常常会在一些内置包外面看到panic的身影。 比方strings.Repeat反复返回一个由字符串 s 的计数正本组成的新字符串: func Repeat(s string, count int) string { if count == 0 { return "" } // if count < 0 { panic("strings: negative Repeat count") } else if len(s)*count/count != len(s) { panic("strings: Repeat count causes overflow") } ...}咱们能够看到当反复的次数小于0或者反复count次之后s的长度溢出,程序会间接panic,而不是返回谬误。这时因为strings包限度了error的应用,所以在程序谬误时会间接panic。 ...

November 2, 2022 · 4 min · jiezi

关于go:go环境变量-GOROOTGOPATHGOPRIVATEGOPROXY-一文理解

一、GOROOT和GOPATHgo开发环境,最重要的两个环境变量。设置好这两个,go就能够跑起来了。 GOROOT:GOROOT就是Go的装置目录,外面寄存着go的执行命令。GOPATH:GOPATH是咱们的工作空间,保留go我的项目代码和第三方依赖包。比方咱们应用 go version 调用的go命令,就是存储在GOROOT中。 1.1 GOROOT

October 29, 2022 · 1 min · jiezi

关于go:go精通protobuf连载五如何从零编写ProtoBuf-插件

go精通protobuf连载五:如何从零编写ProtoBuf 插件

October 29, 2022 · 1 min · jiezi

关于go:go精通protobuf连载四ProtoBuf常用插件介绍

go精通protobuf连载四:ProtoBuf罕用插件介绍

October 29, 2022 · 1 min · jiezi

关于go:go精通protobuf连载三protobuf使用示例深入了解protoc命令

go精通protobuf连载三:protobuf应用示例,深刻理解protoc命令

October 29, 2022 · 1 min · jiezi

关于go:go精通protobuf连载二理解protobuf中protoc与protocgengo的关系

一、protobuf 介绍Protobuf全称是Google Protocol Buffer,是一种高效轻便的结构化数据存储形式,如同xml,json罕用来序列化数据,用来存储以及传输。相当于json和xml,Protobuf占用的宽带更小,序列化更快。目前在grpc等服务中,越来越风行。 二、protoc三、protoc-gen-go

October 29, 2022 · 1 min · jiezi

关于go:go精通protobuf连载一安装protobuf

一、介绍protobuf是一种与语言无关、与平台无关的可扩大的插件,用于序列化结构化数据。只须要定义一下protobuf构造的文件 .proto 而后就能够应用protoc 命令生成对应的编程语言的构造的文件。目前proto3本部中曾经反对绝大多数支流语言,如Java、Python、Objective-C 和 C++ Kotlin、Dart、Go、Ruby和C#等,生成的代码。 二、操作系统装置 protobuf2.1 装置 protobuf咱们这里应用brew装置,只有输出上面的命令就能够了 brew install protobuf装置实现后,输出 protoc --version 查看是否失效 ➜ protoc --versionlibprotoc 3.21.7装置胜利。

October 28, 2022 · 1 min · jiezi

关于go:为什么说Go的字符串类型不能修改

在接触Go这么语言,可能你常常会听到这样一句话。对于字符串不能批改,可能你很纳闷,日常开发中咱们对字符串进行批改也是很失常的,为什么又说Go中的字符串不能进行批改呢? 本文就来通过理论案例给大家演示,为什么Go中的字符串不能进行批改。 在演示这个问题之前,咱们先对字符串类型的基础知识做个大抵的演示,这样便于大家对问题的进一步理解。 本文已收录Gitee、Github。分享Go、PHP、MySQL、Redis等等技术干货。举荐拜访Gitee,速度快并且可能失常解析文档中的图片文件。字符串定义字符串是一种用来示意字符的数据类型。在应用时,应用" "将字符内容蕴含起来。例如上面的模式: package mainimport "fmt"func main() { var str string = "Hello World!"}在Go中,字符串通常有三种定义形式: // 第一种(全量定义)var 变量名称 string = "字符串内容"// 类型推导var 变量名称 = "字符串内容"// 短标记(只实用于局部变量)变量名称 := "字符串内容"字符串的定义,其实也能够通过字节的形式。这里列举的形式是最为常见的形式。字符串的组成Go中的字符串合乎Unicode规范,并且采纳UTF-8编码。字符串底层其实也是由byte组成(前面会认真解说)。通过上面的示例,打印查看具体的字节内容: s := "Hello World!"for _, v := range s { fmt.Print(v) fmt.Print("\t")}// 72 101 108 108 111 32 87 111 114 108 100 33下面代码打印的内容,就是每一个字符所示意的字节码。字符串不能批改通过下面的大抵演示,咱们对字符串有一个根本的理解。对于字符串不能批改,可能你很纳闷,日常开发中咱们对字符串进行从新赋值也是很失常的,为什么又说Go中的字符串不能进行批改呢? 其实这里要纠正这个谈话,对于字符串批改并不等价于从新赋值。开发中罕用的形式,其实是一种从新赋值的概念。 str := "Hello World!"// 从新赋值str = "Hello Go!"// 字符串批改str[0] = "I"通常听到的不能批改,其实就是指的下面代码的第二种形式。并且通过这种形式批改会报错::cannot assign to s[0] (value of type byte)回归正题,为什么Go中的字符串不能通过下标的形式来进行批改呢?这是因为Go中的字符串的数据结构体是由一个指针和长度组成的构造体,该指针指向的一个切片才是真正的字符串值。Go中源码有这样一段定义: ...

October 28, 2022 · 1 min · jiezi

关于go:golang中的锁竞争问题

索引:https://www.waterflow.link/articles/1666884810643 当咱们打印谬误的时候应用锁可能会带来意想不到的后果。 咱们看上面的例子: package mainimport ( "fmt" "sync")type Courseware struct { mutex sync.RWMutex Id int64 Code string Duration int}func (c *Courseware) UpdateDuration(duration int) error { c.mutex.Lock() // 1 defer c.mutex.Unlock() if duration < 60 { return fmt.Errorf("课件时长必须大于等于60秒: %v", c) // 2 } c.Duration = duration return nil}// 3func (c *Courseware) String() string { c.mutex.RLock() defer c.mutex.RUnlock() return fmt.Sprintf("id %d, duration %d", c.Id, c.Duration)}func main() { c := &Courseware{} fmt.Println(c.UpdateDuration(0))}下面的代码看起来貌似没有什么问题,然而却会导致死锁: ...

October 27, 2022 · 3 min · jiezi

关于go:Go语言syncMap

Go语言中的 map 在并发状况下,只读是线程平安的,同时读写是线程不平安的。如果想实现并发线程平安有两种办法: map加互斥锁或读写锁规范库sync.map(Go1.19+新个性)sync.map源码https://github.com/golang/go/... sync.map 实现原理及优化利用map只读不必锁,通过冗余 read 和 dirty 两个字段将读写拆散,读的数据存在只读字段 read 上,将最新写入的数据则存在 dirty 字段上,只在dirty读写上加锁,进步程序只读效率。读取时会先查问 read,不存在再查问 dirty,写入时则只写入 dirty读取 read 并不需要加锁,而读或写 dirty 都须要加锁另外有 misses 字段来统计 read 被穿透的次数(被穿透指须要读 dirty 的状况),超过肯定次数则将 dirty 数据同步到 read 上对于删除数据则间接通过标记来提早删除具体数据结构可参考:https://blog.csdn.net/u010853...https://www.haohongfan.com/do... sync.map 应用场景map+Mutex: 通过Mutex互斥锁来实现多个goroutine对map的串行化拜访,读写都须要通过Mutex加锁和开释锁,实用于读写比靠近的场景map+RWMutex:通过RWMutex来实现对map的读写进行读写锁拆散加锁,从而实现读的并发性能进步,同Mutex相比实用于读多写少的场景sync.Map:底层通拆散读写map和原子指令来实现读的近似无锁,并通过提早更新的形式来保障读的无锁化。读多批改少,元素减少删除频率不高的状况,在大多数状况下代替上述两种实现 sync.map 应用办法如下package mainimport ( "fmt" "sync")func main() { var m sync.Map //增加一个元素 m.Store(1, "a") m.Store("a", 2) //读取一个元素 fmt.Println(m.Load(1)) //a true fmt.Println(m.Load("a")) //2 true //读取不存在的元素 fmt.Println(m.Load(2)) //<nil> false //存在就返回,不存在就插入 fmt.Println(m.LoadOrStore("3", 33)) //33 false fmt.Println(m.Load("3")) //33 true //如果存在的话,同时删除这个 key fmt.Println(m.LoadAndDelete("3")) // 33 true //删除某个元素 m.Delete("3") // 遍历所有sync.Map中的键值对 m.Range(func(k, v interface{}) bool { fmt.Println("iterate:", k, v) return true })}通过以上示例能够看到sync.map具备以下个性: ...

October 27, 2022 · 1 min · jiezi

关于go:go-读取-json-文件

一、介绍json的全称是JavaScript Object Notation。是js用来标记对象。因为通俗易懂,目前json格局被宽泛用在存储以及传输中。json是一种简略的 kv构造。key用字符串示意,并且字符串用双引号示意。如: { "users": [ { "name": "hi", "age": 1 }, { "name": "fff", "age": 2 } ], "redis": { "base": { "addr": "test.redis.com:6379", "password": "pwd", "db": "1" } }}把下面的构造存储到redis.json中,上面咱们用go写代码读取。 二、go读取json文件2.1 咱们先写一个构造体来接管 yaml文件type JsonFile struct { Users []struct { Name string Age int } Redis struct { Base struct { Addr string Password string Db string } }}在这个 JsonFile 构造体外面,咱们定义的字段须要和下面的json文件一一对应。 2.2 咱们再应用encoding/json库来解析读取的json文件。咱们应用官网的 encoding/json 作为读取json文件的库。 func TestJsonRead(t *testing.T) { // 1读取文件 data, err := ioutil.ReadFile("redis.json") if err != nil { t.Log(err) } t.Log(string(data)) // 2解析文件 var y JsonFile err = json.Unmarshal(data, &y) t.Log(y, err)}输入: ...

October 27, 2022 · 1 min · jiezi

关于go:转发来自西红柿李乐的深入理解Go语言

作者:李乐 原文地址:https://segmentfault.com/a/11... 第一章 Go语言疾速入门 第一篇 根本语法 第二讲 数组与切片 第三讲 字符串 第四讲 哈希表MAP 第五讲 构造体与接口 第六讲 反射 第七讲 泛型第二章 并发编程 第八讲 GMP调度模型 第九讲 协程治理 第十讲 调度器schedule 第十一讲 网络IO 第十二讲 管道chan 第十三讲 定时器timer 第十四讲 零碎调用 第十五讲 panic defer recover 第十六讲 并发编程第三章 垃圾回收(GC) 第十七讲 内存治理 第十八讲 三色标记与写屏障 第十九讲 标记 清理 第二十讲 GC调度与调优第四章 罕用规范库 第二十一讲 net/http.server 第二十二讲 net/http.client 第二十三讲 上下文context 第二十四讲 单元测试第五章 实战 第二十五讲 Go程序剖析利器pprof 第二十六讲 dlv调试 第二十七讲 HTTP服务假死问题剖析 第二十八讲 Go服务502总结 第二十九讲 Go微服务发现问题剖析 第三十讲 平滑降级

October 27, 2022 · 1 min · jiezi

关于go:下篇一文玩转Go接口

空接口既然能够存储任意类型的值,那么从空接口获取到的值是否能够间接应用?看上面栗子package mainimport ( "fmt")var a interface{}var b interface{}func main() { a = 1024 b = 100 res := a + b fmt.Println(res)}报错: invalid operation: operator + not defined on a (variable of type interface{}) (exit status 2)程序报错的起因:因为空接口的类型是不能够间接应用的,须要正告类型断言转换方可应用。这次咱们应用类型断言来将接口类型转成int类型package mainimport ( "fmt")var a interface{}var b interface{}func main() { a = 1024 b = 100 val1, res1 := a.(int) fmt.Println(val1, res1) val2, res2 := b.(int) fmt.Println(val2, res2) //val1和val2接管转换后的值,res1和res2是类型断言的状态(胜利或失败),断言胜利是true,反之false}输入: 1024 true100 true类型断言新姿态:当应用一个值承受断言后果时,则会间接返回断言后的值package mainimport ( "fmt")var a interface{}var b interface{}func main() { a = 1024 b = 100    //类型断言转换 a1 := a.(int) b1 := b.(int)    //转换后进行相加,就不会报错了 res := a1 + b1 fmt.Println(res)}领会一下应用类型断言转换失败的快感package mainimport ( "fmt" "log")var a interface{}func main() { a = 1024 if a1, r := a.(string); r {  fmt.Println(a1) } else {  log.Fatalln("类型断言转换失败") }}输入: 2022/10/25 10:30:48 类型断言转换失败变量a存储值是整型,视图应用类型断言将其转换为字符串,后果报错了,这么玩是不行的,玩不起。类型断言+switch实现数据类型判断package mainimport ( "fmt")func TestFunc(value interface{}) { switch value.(type) { case int:  fmt.Printf("value=%v Type Int\n", value) case float32:  fmt.Printf("value=%v Type Float32\n", value) case float64:  fmt.Printf("value=%v Type Float64\n", value) case string:  fmt.Printf("value=%v Type string\n", value) }}func main() { TestFunc("hello") TestFunc(100) TestFunc(89.12)}输入: value=hello Type stringvalue=100 Type Intvalue=89.12 Type Float64还能够将接口类型转换成另一个接口类型,上面的栗子是将A接口转换成B接口package mainimport ( "fmt")type A interface{}type B interface{}func main() { var a A = 100 b := a.(B) fmt.Println(b)}在之前的栗子,都是将接口类型转换成根本的数据类型,而这个栗子是将一个自定义的接口类型转换成另一个自定义的接口类型。还能够将接口类型转成指针类型,看上面的栗子package mainimport "fmt"func main() {    //定义接口类型的变量ainter var ainter interface{} num := 100 ainter = &num //将地址赋给接口变量 v, r := ainter.(*int) fmt.Println(v, r)}下面的栗子中,应用类型断言将接口类型转成了int指针类型接口能够嵌套吗?实战通知你package mainimport "fmt"type action1 interface { insert()}type action2 interface { delete()}type actionInterface interface { action1 action2 query()}type Db struct { Data string}func (d Db) insert() { fmt.Print("插入数据...", d.Data)}func (d Db) delete() { fmt.Print("删除数据...", d.Data)}func (d Db) query() { fmt.Print("查问数据...", d.Data)}func main() { d := Db{Data: "hello"} d.query() d.delete() d.insert()}通过下面的实战,接口是能够嵌套的,留神了,只有实现接口中所有的办法(含所有嵌套接口里的所有办法),那么才算是真正实现了接口。实现error接口中的Error办法,来玩一个自定义谬误类型的栗子package mainimport ( "fmt")type AddError struct { ErrorMsg string}func (m AddError) Error() string { return fmt.Sprintf("Add error %v", m.ErrorMsg)}func add(a int, b int) (int, error) { if a == 0 || b == 0 {  errinfo := fmt.Sprintf("a=%v, b=%v", a, b)  return 0, AddError{ErrorMsg: errinfo} } else {  return a + b, nil }}func main() { res, err := add(8, 0) fmt.Println(res, err)}下面的栗子中,曾经隐式的实现了error接口中的Error办法如果不玩自定义的谬误类型,也能够间接应用errors.New办法返回一个错误信息package mainimport ( "errors" "fmt")func add(a int, b int) (int, error) { if a == 0 || b == 0 {  return 0, errors.New("不能为0") } else {  return a + b, nil }}func main() { res, err := add(9, 1) fmt.Println(res, err)}本文转载于(喜爱的盆友关注咱们):https://mp.weixin.qq.com/s/Ac...

October 26, 2022 · 1 min · jiezi

关于go:内存分配与GC

内存调配与GCGo应用值传递协程栈记录了协程执行现场协程栈在堆上由GC回收编译原理相干逃逸剖析局部变量太大栈帧回收后,须要持续应用的变量不是所有变量读能放在协程栈上触发逃逸的情景指针逃逸函数返回了对象的指针(函数外能够拜访,变量此时不是局部变量) func a()*int{ v := 0 return &v}func main(){ i := a()}空接口逃逸func b()*int{ v := 0 // interface{}类型的函数往往会应用反射 fmt.Println(v)}func main(){ i := a()}大变量逃逸变量过大会导致栈空间有余,64位,个别超过64KB的变量会逃逸栈扩容Go栈的初始空间为2KB在函数调用前判断栈空间(morestack)必要时对栈进行扩容晚期应用分段栈,前期应用间断栈当空间有余时扩容,变为原来的2倍当空间使用率有余1/4时缩容,变为原来的1/2/* * support for morestack */// Called during function prolog when more stack is needed.//// The traceback routines see morestack on a g0 as being// the top of a stack (for example, morestack calling newstack// calling the scheduler calling newm calling gc), so we must// record an argument size. For that purpose, it has no arguments.TEXT runtime·morestack(SB),NOSPLIT,$0-0 // Cannot grow scheduler stack (m->g0). get_tls(CX) MOVQ g(CX), BX MOVQ g_m(BX), BX MOVQ m_g0(BX), SI CMPQ g(CX), SI JNE 3(PC) CALL runtime·badmorestackg0(SB) CALL runtime·abort(SB) // Cannot grow signal stack (m->gsignal). MOVQ m_gsignal(BX), SI CMPQ g(CX), SI JNE 3(PC) CALL runtime·badmorestackgsignal(SB) CALL runtime·abort(SB) // Called from f. // Set m->morebuf to f's caller. NOP SP // tell vet SP changed - stop checking offsets MOVQ 8(SP), AX // f's caller's PC MOVQ AX, (m_morebuf+gobuf_pc)(BX) LEAQ 16(SP), AX // f's caller's SP MOVQ AX, (m_morebuf+gobuf_sp)(BX) get_tls(CX) MOVQ g(CX), SI MOVQ SI, (m_morebuf+gobuf_g)(BX) // Set g->sched to context in f. MOVQ 0(SP), AX // f's PC MOVQ AX, (g_sched+gobuf_pc)(SI) LEAQ 8(SP), AX // f's SP MOVQ AX, (g_sched+gobuf_sp)(SI) MOVQ BP, (g_sched+gobuf_bp)(SI) MOVQ DX, (g_sched+gobuf_ctxt)(SI) // Call newstack on m->g0's stack. MOVQ m_g0(BX), BX MOVQ BX, g(CX) MOVQ (g_sched+gobuf_sp)(BX), SP CALL runtime·newstack(SB) CALL runtime·abort(SB) // crash if newstack returns RETheapArenaGo每次申请的虚拟内存单元为64MB最多有20^20个虚拟内存单元所有的heapArena组成了mheap(Go堆内存)// A heapArena stores metadata for a heap arena. heapArenas are stored// outside of the Go heap and accessed via the mheap_.arenas index.////go:notinheap// 申请的信息type heapArena struct { // bitmap stores the pointer/scalar bitmap for the words in // this arena. See mbitmap.go for a description. Use the // heapBits type to access this. bitmap [heapArenaBitmapBytes]byte // spans maps from virtual address page ID within this arena to *mspan. // For allocated spans, their pages map to the span itself. // For free spans, only the lowest and highest pages map to the span itself. // Internal pages map to an arbitrary span. // For pages that have never been allocated, spans entries are nil. // // Modifications are protected by mheap.lock. Reads can be // performed without locking, but ONLY from indexes that are // known to contain in-use or stack spans. This means there // must not be a safe-point between establishing that an // address is live and looking it up in the spans array. // 记录mspan spans [pagesPerArena]*mspan // pageInUse is a bitmap that indicates which spans are in // state mSpanInUse. This bitmap is indexed by page number, // but only the bit corresponding to the first page in each // span is used. // // Reads and writes are atomic. pageInUse [pagesPerArena / 8]uint8 // pageMarks is a bitmap that indicates which spans have any // marked objects on them. Like pageInUse, only the bit // corresponding to the first page in each span is used. // // Writes are done atomically during marking. Reads are // non-atomic and lock-free since they only occur during // sweeping (and hence never race with writes). // // This is used to quickly find whole spans that can be freed. // // TODO(austin): It would be nice if this was uint64 for // faster scanning, but we don't have 64-bit atomic bit // operations. pageMarks [pagesPerArena / 8]uint8 // pageSpecials is a bitmap that indicates which spans have // specials (finalizers or other). Like pageInUse, only the bit // corresponding to the first page in each span is used. // // Writes are done atomically whenever a special is added to // a span and whenever the last special is removed from a span. // Reads are done atomically to find spans containing specials // during marking. pageSpecials [pagesPerArena / 8]uint8 // checkmarks stores the debug.gccheckmark state. It is only // used if debug.gccheckmark > 0. checkmarks *checkmarksMap // zeroedBase marks the first byte of the first page in this // arena which hasn't been used yet and is therefore already // zero. zeroedBase is relative to the arena base. // Increases monotonically until it hits heapArenaBytes. // // This field is sufficient to determine if an allocation // needs to be zeroed because the page allocator follows an // address-ordered first-fit policy. // // Read atomically and written with an atomic CAS. zeroedBase uintptr}type mheap struct { // lock must only be acquired on the system stack, otherwise a g // could self-deadlock if its stack grows with the lock held. lock mutex pages pageAlloc // page allocation data structure sweepgen uint32 // sweep generation, see comment in mspan; written during STW // allspans is a slice of all mspans ever created. Each mspan // appears exactly once. // // The memory for allspans is manually managed and can be // reallocated and move as the heap grows. // // In general, allspans is protected by mheap_.lock, which // prevents concurrent access as well as freeing the backing // store. Accesses during STW might not hold the lock, but // must ensure that allocation cannot happen around the // access (since that may free the backing store). allspans []*mspan // all spans out there // _ uint32 // align uint64 fields on 32-bit for atomics // Proportional sweep // // These parameters represent a linear function from gcController.heapLive // to page sweep count. The proportional sweep system works to // stay in the black by keeping the current page sweep count // above this line at the current gcController.heapLive. // // The line has slope sweepPagesPerByte and passes through a // basis point at (sweepHeapLiveBasis, pagesSweptBasis). At // any given time, the system is at (gcController.heapLive, // pagesSwept) in this space. // // It is important that the line pass through a point we // control rather than simply starting at a 0,0 origin // because that lets us adjust sweep pacing at any time while // accounting for current progress. If we could only adjust // the slope, it would create a discontinuity in debt if any // progress has already been made. pagesInUse atomic.Uint64 // pages of spans in stats mSpanInUse pagesSwept atomic.Uint64 // pages swept this cycle pagesSweptBasis atomic.Uint64 // pagesSwept to use as the origin of the sweep ratio sweepHeapLiveBasis uint64 // value of gcController.heapLive to use as the origin of sweep ratio; written with lock, read without sweepPagesPerByte float64 // proportional sweep ratio; written with lock, read without // TODO(austin): pagesInUse should be a uintptr, but the 386 // compiler can't 8-byte align fields. // scavengeGoal is the amount of total retained heap memory (measured by // heapRetained) that the runtime will try to maintain by returning memory // to the OS. // // Accessed atomically. scavengeGoal uint64 // Page reclaimer state // reclaimIndex is the page index in allArenas of next page to // reclaim. Specifically, it refers to page (i % // pagesPerArena) of arena allArenas[i / pagesPerArena]. // // If this is >= 1<<63, the page reclaimer is done scanning // the page marks. reclaimIndex atomic.Uint64 // reclaimCredit is spare credit for extra pages swept. Since // the page reclaimer works in large chunks, it may reclaim // more than requested. Any spare pages released go to this // credit pool. reclaimCredit atomic.Uintptr // arenas is the heap arena map. It points to the metadata for // the heap for every arena frame of the entire usable virtual // address space. // // Use arenaIndex to compute indexes into this array. // // For regions of the address space that are not backed by the // Go heap, the arena map contains nil. // // Modifications are protected by mheap_.lock. Reads can be // performed without locking; however, a given entry can // transition from nil to non-nil at any time when the lock // isn't held. (Entries never transitions back to nil.) // // In general, this is a two-level mapping consisting of an L1 // map and possibly many L2 maps. This saves space when there // are a huge number of arena frames. However, on many // platforms (even 64-bit), arenaL1Bits is 0, making this // effectively a single-level map. In this case, arenas[0] // will never be nil. // 堆由所有heapArena组成 arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena // heapArenaAlloc is pre-reserved space for allocating heapArena // objects. This is only used on 32-bit, where we pre-reserve // this space to avoid interleaving it with the heap itself. heapArenaAlloc linearAlloc // arenaHints is a list of addresses at which to attempt to // add more heap arenas. This is initially populated with a // set of general hint addresses, and grown with the bounds of // actual heap arena ranges. arenaHints *arenaHint // arena is a pre-reserved space for allocating heap arenas // (the actual arenas). This is only used on 32-bit. arena linearAlloc // allArenas is the arenaIndex of every mapped arena. This can // be used to iterate through the address space. // // Access is protected by mheap_.lock. However, since this is // append-only and old backing arrays are never freed, it is // safe to acquire mheap_.lock, copy the slice header, and // then release mheap_.lock. allArenas []arenaIdx // sweepArenas is a snapshot of allArenas taken at the // beginning of the sweep cycle. This can be read safely by // simply blocking GC (by disabling preemption). sweepArenas []arenaIdx // markArenas is a snapshot of allArenas taken at the beginning // of the mark cycle. Because allArenas is append-only, neither // this slice nor its contents will change during the mark, so // it can be read safely. markArenas []arenaIdx // curArena is the arena that the heap is currently growing // into. This should always be physPageSize-aligned. curArena struct { base, end uintptr } _ uint32 // ensure 64-bit alignment of central // central free lists for small size classes. // the padding makes sure that the mcentrals are // spaced CacheLinePadSize bytes apart, so that each mcentral.lock // gets its own cache line. // central is indexed by spanClass. // 136个 central [numSpanClasses]struct { mcentral mcentral pad [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte } spanalloc fixalloc // allocator for span* cachealloc fixalloc // allocator for mcache* specialfinalizeralloc fixalloc // allocator for specialfinalizer* specialprofilealloc fixalloc // allocator for specialprofile* specialReachableAlloc fixalloc // allocator for specialReachable speciallock mutex // lock for special record allocators. arenaHintAlloc fixalloc // allocator for arenaHints unused *specialfinalizer // never set, just here to force the specialfinalizer type into DWARF}调配线性调配闲暇链表调配线性调配与闲暇链表调配会产生碎片分级调配内存治理单元mspan依据隔离适应策略,应用内存时最小单位为mspan每个mspan是N个雷同的小格子67个mspan// class bytes/obj bytes/span objects tail waste max waste min align// 1 8 8192 1024 0 87.50% 8// 2 16 8192 512 0 43.75% 16// 3 24 8192 341 8 29.24% 8// 4 32 8192 256 0 21.88% 32// 5 48 8192 170 32 31.52% 16// 6 64 8192 128 0 23.44% 64// 7 80 8192 102 32 19.07% 16// 8 96 8192 85 32 15.95% 32// 9 112 8192 73 16 13.56% 16// 10 128 8192 64 0 11.72% 128// 11 144 8192 56 128 11.82% 16// 12 160 8192 51 32 9.73% 32// 13 176 8192 46 96 9.59% 16// 14 192 8192 42 128 9.25% 64// 15 208 8192 39 80 8.12% 16// 16 224 8192 36 128 8.15% 32// 17 240 8192 34 32 6.62% 16// 18 256 8192 32 0 5.86% 256// 19 288 8192 28 128 12.16% 32// 20 320 8192 25 192 11.80% 64// 21 352 8192 23 96 9.88% 32// 22 384 8192 21 128 9.51% 128// 23 416 8192 19 288 10.71% 32// 24 448 8192 18 128 8.37% 64// 25 480 8192 17 32 6.82% 32// 26 512 8192 16 0 6.05% 512// 27 576 8192 14 128 12.33% 64// 28 640 8192 12 512 15.48% 128// 29 704 8192 11 448 13.93% 64// 30 768 8192 10 512 13.94% 256// 31 896 8192 9 128 15.52% 128// 32 1024 8192 8 0 12.40% 1024// 33 1152 8192 7 128 12.41% 128// 34 1280 8192 6 512 15.55% 256// 35 1408 16384 11 896 14.00% 128// 36 1536 8192 5 512 14.00% 512// 37 1792 16384 9 256 15.57% 256// 38 2048 8192 4 0 12.45% 2048// 39 2304 16384 7 256 12.46% 256// 40 2688 8192 3 128 15.59% 128// 41 3072 24576 8 0 12.47% 1024// 42 3200 16384 5 384 6.22% 128// 43 3456 24576 7 384 8.83% 128// 44 4096 8192 2 0 15.60% 4096// 45 4864 24576 5 256 16.65% 256// 46 5376 16384 3 256 10.92% 256// 47 6144 24576 4 0 12.48% 2048// 48 6528 32768 5 128 6.23% 128// 49 6784 40960 6 256 4.36% 128// 50 6912 49152 7 768 3.37% 256// 51 8192 8192 1 0 15.61% 8192// 52 9472 57344 6 512 14.28% 256// 53 9728 49152 5 512 3.64% 512// 54 10240 40960 4 0 4.99% 2048// 55 10880 32768 3 128 6.24% 128// 56 12288 24576 2 0 11.45% 4096// 57 13568 40960 3 256 9.99% 256// 58 14336 57344 4 0 5.35% 2048// 59 16384 16384 1 0 12.49% 8192// 60 18432 73728 4 0 11.11% 2048// 61 19072 57344 3 128 3.57% 128// 62 20480 40960 2 0 6.87% 4096// 63 21760 65536 3 256 6.25% 256// 64 24576 24576 1 0 11.45% 8192// 65 27264 81920 3 128 10.00% 128// 66 28672 57344 2 0 4.91% 4096// 67 32768 32768 1 0 12.50% 8192// alignment bits min obj size// 8 3 8// 16 4 32// 32 5 256// 64 6 512// 128 7 768// 4096 12 28672// 8192 13 32768//go:notinheaptype mspan struct { next *mspan // next span in list, or nil if none prev *mspan // previous span in list, or nil if none list *mSpanList // For debugging. TODO: Remove. startAddr uintptr // address of first byte of span aka s.base() npages uintptr // number of pages in span manualFreeList gclinkptr // list of free objects in mSpanManual spans // freeindex is the slot index between 0 and nelems at which to begin scanning // for the next free object in this span. // Each allocation scans allocBits starting at freeindex until it encounters a 0 // indicating a free object. freeindex is then adjusted so that subsequent scans begin // just past the newly discovered free object. // // If freeindex == nelem, this span has no free objects. // // allocBits is a bitmap of objects in this span. // If n >= freeindex and allocBits[n/8] & (1<<(n%8)) is 0 // then object n is free; // otherwise, object n is allocated. Bits starting at nelem are // undefined and should never be referenced. // // Object n starts at address n*elemsize + (start << pageShift). freeindex uintptr // TODO: Look up nelems from sizeclass and remove this field if it // helps performance. nelems uintptr // number of object in the span. // Cache of the allocBits at freeindex. allocCache is shifted // such that the lowest bit corresponds to the bit freeindex. // allocCache holds the complement of allocBits, thus allowing // ctz (count trailing zero) to use it directly. // allocCache may contain bits beyond s.nelems; the caller must ignore // these. allocCache uint64 // allocBits and gcmarkBits hold pointers to a span's mark and // allocation bits. The pointers are 8 byte aligned. // There are three arenas where this data is held. // free: Dirty arenas that are no longer accessed // and can be reused. // next: Holds information to be used in the next GC cycle. // current: Information being used during this GC cycle. // previous: Information being used during the last GC cycle. // A new GC cycle starts with the call to finishsweep_m. // finishsweep_m moves the previous arena to the free arena, // the current arena to the previous arena, and // the next arena to the current arena. // The next arena is populated as the spans request // memory to hold gcmarkBits for the next GC cycle as well // as allocBits for newly allocated spans. // // The pointer arithmetic is done "by hand" instead of using // arrays to avoid bounds checks along critical performance // paths. // The sweep will free the old allocBits and set allocBits to the // gcmarkBits. The gcmarkBits are replaced with a fresh zeroed // out memory. allocBits *gcBits gcmarkBits *gcBits // sweep generation: // if sweepgen == h->sweepgen - 2, the span needs sweeping // if sweepgen == h->sweepgen - 1, the span is currently being swept // if sweepgen == h->sweepgen, the span is swept and ready to use // if sweepgen == h->sweepgen + 1, the span was cached before sweep began and is still cached, and needs sweeping // if sweepgen == h->sweepgen + 3, the span was swept and then cached and is still cached // h->sweepgen is incremented by 2 after every GC sweepgen uint32 divMul uint32 // for divide by elemsize allocCount uint16 // number of allocated objects spanclass spanClass // size class and noscan (uint8) state mSpanStateBox // mSpanInUse etc; accessed atomically (get/set methods) needzero uint8 // needs to be zeroed before allocation elemsize uintptr // computed from sizeclass or from npages limit uintptr // end of data in span speciallock mutex // guards specials list specials *special // linked list of special records sorted by offset.}每个heapArena中的mspan都不确定 ...

October 26, 2022 · 38 min · jiezi

关于go:channel

channelchannel的申明与应用// 无缓冲区的channel// 无缓冲区的channel必须有协程在期待它才能够向channel发送数据ch := make(chan string)// 向channel发送数据ch <- "hllo"// 从channel承受数据并赋给xx = <-ch// 从channel接收数据并抛弃<-ch// 有缓冲区的cahnnelc := make(chan string,10)channel的数据结构type hchan struct { // 环形缓冲区 // 队列中的总数据 qcount uint // total data in the queue // 循环队列的大小 dataqsiz uint // size of the circular queue // 指向 dataqsiz 的数组的元素 buf unsafe.Pointer // points to an array of dataqsiz elements // 元素的大小 elemsize uint16 // 是否closed 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. // 爱护hchan所有字段 // channel并不是无锁的 // 只在产生数据和接收数据时加锁,其余期间不加锁 lock mutex}type waitq struct { first *sudog last *sudog}// sudog represents a g in a wait list, such as for sending/receiving// on a channel.//// sudog is necessary because the g ↔ synchronization object relation// is many-to-many. A g can be on many wait lists, so there may be// many sudogs for one g; and many gs may be waiting on the same// synchronization object, so there may be many sudogs for one object.//// sudogs are allocated from a special pool. Use acquireSudog and// releaseSudog to allocate and free them.type sudog struct { // The following fields are protected by the hchan.lock of the // channel this sudog is blocking on. shrinkstack depends on // this for sudogs involved in channel ops. g *g next *sudog prev *sudog elem unsafe.Pointer // data element (may point to stack) // The following fields are never accessed concurrently. // For channels, waitlink is only accessed by g. // For semaphores, all fields (including the ones above) // are only accessed when holding a semaRoot lock. acquiretime int64 releasetime int64 ticket uint32 // isSelect indicates g is participating in a select, so // g.selectDone must be CAS'd to win the wake-up race. isSelect bool // success indicates whether communication over channel c // succeeded. It is true if the goroutine was awoken because a // value was delivered over channel c, and false if awoken // because c was closed. success bool parent *sudog // semaRoot binary tree waitlink *sudog // g.waiting list or semaRoot waittail *sudog // semaRoot c *hchan // channel} ...

October 26, 2022 · 16 min · jiezi

关于go:gookitconfig-Go语言读取多种格式配置文件

gookit/config - Go利用配置管理,反对读取多种格局 JSON(默认), JSON5, INI, Properties, YAML, TOML, HCL, ENV, Flags,多文件加载,反对数据合并,解析环境变量名等等 <!--truncate--> 性能简介反对多种格局: JSON(默认), JSON5, INI, Properties, YAML, TOML, HCL, ENV, Flags JSON 内容反对正文,能够设置解析时革除正文其余驱动都是按需应用,不应用的不会加载编译到利用中反对多个文件、多数据加载反对从 OS ENV 变量数据加载配置反对从近程 URL 加载配置数据反对从命令行参数(flags)设置配置数据反对在配置数据更改时触发事件 可用事件: set.value, set.data, load.data, clean.data反对数据笼罩合并,加载多份数据时将按key主动合并反对将全副或局部配置数据绑定到构造体 config.BindStruct("key", &s) NEW: 反对通过构造体标签 default 解析并设置默认值反对通过 . 分隔符来按门路获取子级值,也反对自定义分隔符。 e.g map.key arr.2反对解析ENV变量名称。 like shell: ${SHELL} -> shell: /bin/zsh简洁的应用API Get Int Uint Int64 String Bool Ints IntMap Strings StringMap ...欠缺的单元测试(code coverage > 95%)Github: https://github.com/gookit/config应用示例这里应用yaml格局内容作为示例: name: app2debug: falsebaseKey: value2shell: ${SHELL}envKey1: ${NotExist|defValue}map1: key: val2 key2: val20arr1: - val1 - val21示例代码请看 _examples/yaml.go:package mainimport ( "github.com/gookit/config/v2" "github.com/gookit/config/v2/yamlv3")// go run ./examples/yaml.gofunc main() { // 设置选项反对 ENV 解析 config.WithOptions(config.ParseEnv) // 增加驱动程序以反对yaml内容解析(除了JSON是默认反对,其余的则是按需应用) config.AddDriver(yamlv3.Driver) // 加载配置,能够同时传入多个文件 err := config.LoadFiles("testdata/yml_base.yml") if err != nil { panic(err) } // fmt.Printf("config data: \n %#v\n", config.Data()) // 加载更多文件 err = config.LoadFiles("testdata/yml_other.yml") // 也能够一次性加载多个文件 // err := config.LoadFiles("testdata/yml_base.yml", "testdata/yml_other.yml") if err != nil { panic(err) }}应用阐明能够应用 WithOptions() 增加更多额定选项性能. 例如: ParseEnv, ParseDefault能够应用 AddDriver() 增加须要应用的格局驱动器(json 是默认加载的的,无需增加)而后就能够应用 LoadFiles() LoadStrings() 等办法加载配置数据 ...

October 26, 2022 · 2 min · jiezi

关于go:golang安装配置与docker构建

装置操作系统是Ubuntu22 64 x86 拜访golang官网文档,获取最新安装包 https://go.dev/doc/install下载与解压,目前最新的版本是1.19 $ wget https://dl.google.com/go/go1.19.2.linux-amd64.tar.gz# 删除旧版本go$ rm -rf /usr/local/go$ tar -C /usr/local -xzf go1.19.2.linux-amd64.tar.gz配置环境变量GOPATH 示意golang的工作空间,蕴含三个子目录src 外面的子目录都是包,蕴含我的项目源码 src上面举荐蕴含两级目录,github.com(仓库域名)以及用户名称目录,构造变成src/github.com/user/project-a比方go get -u github.com/gin-gonic/gin这样的,在用户获取一个包的时候蕴含域名,组织或者用户,具体的我的项目pkg 编译后生成的,包的指标文件bin生成的可执行文件创立工作空间如下 $ mkdir $HOME/golang$ cd $HOME/golang $ mkdir src pkg bin$ cd src$ mkdir -p github.com/gong/hello-worldGOROOT golang的装置目录配置环境变量,创立文件/etc/profile.d/golang.sh export GOROOT=/usr/local/goexport PATH=$GOROOT/bin:$PATHexport GOPATH=$HOME/golang以后shell失效验证 $ source /etc/profile$ go version配置go获取依赖的代理 $ go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/Hello world用编译器关上$HOME/golang/github.com/gong/hello-world 新建一个文件main.go package mainimport "fmt"func main() { fmt.Println("Hello World!")}编译之前须要执行一条命令,创立包的依赖文件,该文件记录了包的依赖信息 $ go mod init更新依赖 $ go mod tidy编译,在与main.go文件同级目录下执行如下 ...

October 26, 2022 · 2 min · jiezi

关于go:Go-Cobra快速上手教程持续更新中

环境变量设置export GOBIN=$GOPATH/binexport PATH=$GOBIN:$PATHexport GOPROXY=https://goproxy.cn/,direct装置依赖go get -u github.com/spf13/[email protected]go get -u github.com/spf13/[email protected]Cobra我的项目流程参考 Create rootCmdCreate your main.goCreate additional commandsReturning and handling errors# 为了疾速实现我的项目&上班,倡议应用cobra-cli来实现以上步骤# 上面的演示也是用cobra-cli来实现应用 Cobra CLI 初始化我的项目参考 mkdir myapp && cd myapp && go mod init myappcobra-cli initmyapp/ $ tree .├── cmd│ └── root.go├── go.mod├── go.sum├── LICENSE└── main.gomyapp/ $ go run main.go A longer description that spans multiple lines and likely containsexamples and usage of using your application. For example:Cobra is a CLI library for Go that empowers applications.This application is a tool to generate the needed filesto quickly create a Cobra application.// // cmd/root.go/*Copyright © 2022 NAME HERE <EMAIL ADDRESS>*/package cmdimport ( "os" "github.com/spf13/cobra")// rootCmd represents the base command when called without any subcommandsvar rootCmd = &cobra.Command{ Use: "myapp", Short: "A brief description of your application", Long: `A longer description that spans multiple lines and likely containsexamples and usage of using your application. For example:Cobra is a CLI library for Go that empowers applications.This application is a tool to generate the needed filesto quickly create a Cobra application.`, // Uncomment the following line if your bare application // has an action associated with it: // Run: func(cmd *cobra.Command, args []string) { },}// Execute adds all child commands to the root command and sets flags appropriately.// This is called by main.main(). It only needs to happen once to the rootCmd.func Execute() { err := rootCmd.Execute() if err != nil { os.Exit(1) }}func init() { // Here you will define your flags and configuration settings. // Cobra supports persistent flags, which, if defined here, // will be global for your application. // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.myapp.yaml)") // Cobra also supports local flags, which will only run // when this action is called directly. rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")}我的项目增加command$ cobra-cli add config [10:38:42]config created at /home/project/myapp$ ls cmd config.go root.go// cmd/config.go/*Copyright © 2022 NAME HERE <EMAIL ADDRESS>*/package cmdimport ( "fmt" "github.com/spf13/cobra")// configCmd represents the config commandvar configCmd = &cobra.Command{ Use: "config", Short: "A brief description of your command", Long: `A longer description that spans multiple lines and likely contains examplesand usage of using your command. For example:Cobra is a CLI library for Go that empowers applications.This application is a tool to generate the needed filesto quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { fmt.Println("config called") },}func init() { rootCmd.AddCommand(configCmd) // Here you will define your flags and configuration settings. // Cobra supports Persistent Flags which will work for this command // and all subcommands, e.g.: // configCmd.PersistentFlags().String("foo", "", "A help for foo") // Cobra supports local flags which will only run when this command // is called directly, e.g.: // configCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")}

October 26, 2022 · 3 min · jiezi

关于go:首次开课Go运维开发项目实战课程介绍

首次开课!本次Go运维开发实战我的项目课程费用只需199元,手把手带你从前端撸到后端,开发带有UI界面的运维小工具,晋升你在运维工作中的战斗力。试听1节课,合乎本人的需要才报名。Go运维开发我的项目实战课程详情开撸我的项目:警报音讯推送治理平台前端html根底css根底javascript根底Bootstrap框架根底后端go语言根底http协定精讲规范库net/http规范库html/templatesession、cookie数据库mysql环境筹备mysql根底,库、表创立,主键、外键等等罕用sql,增删改查go连贯、操作mysql数据库go Orm框架我的项目成果预览 寻找对象运维萌新想转运维开发的运维工程师只会写零散脚本(python、shell)的运维工程师Python转Go的运维工程师学完后您将取得具备用开发思维解决运维问题的能力具备开发带有UI界面运维工具的能力想要理解请关注关注公众号:“不背锅运维”,发送音讯:“报名”学习形式和周期学习形式腾讯课堂直播,每周日早晨19:30-21:30平时微信群交换、一对一答疑学习周期32课时预约开撸工夫2022年11月6号周日早晨19:30-21:30当前有福利本次我的项目课程完结后,报名下次开课我的项目有大大滴优惠哦!打算下一次开课的实战课程是:Prometheus二开,实现登录认证、账号治理。本文转载于(喜爱的盆友关注咱们):https://mp.weixin.qq.com/s/8R...

October 25, 2022 · 1 min · jiezi

关于go:上篇Go的反射基础

什么是反射反射的机制是在运行时能够获取到其变量的类型和值,且能够在运行时对其变量类型和值进行查看,能够对其值进行批改。这种机制,其实在编写业务代码时是比拟少用到的,那么在框架中,应用的反射的机制是比拟常见,如web框架、Orm框架,实现通用性的目标。go的反射go的反射是由其规范库中的reflect包实现,该reflect包实现了在运行时进行反射的能力,本篇先解说一下reflect的罕用的几个办法,下篇模仿一些贴近理论的利用场景来分析到底怎么用。实战reflect包中罕用的几个类型和办法通过反射获取变量的类型(Type) package mainimport ( "fmt" "reflect")func main() { fmt.Println("公众号搜寻:不背锅运维") a := 100.89 b := "hello" //获取变量a和b的类型,即应用reflect.TypeOf反射出了根本数据类型的变量的类型。 fmt.Println(reflect.TypeOf(a)) //float64 fmt.Println(reflect.TypeOf(b)) //string // 反射获取构造体类型信息 type User struct { Name string Age int } u := User{"tantianran", 18} fmt.Println(reflect.TypeOf(u)) //main.User}应用反射获取变量对应的值(Value) package mainimport ( "fmt" "reflect")func main() { fmt.Println("公众号搜寻:不背锅运维") a := 100.89 b := "hello" //应用反射获取变量的Value。 fmt.Println(reflect.ValueOf(a)) //100.89 fmt.Println(reflect.ValueOf(b)) //hello // 应用反射获取构造体变量的Value type User struct { Name string Age int } u := User{"tantianran", 18} fmt.Println(reflect.ValueOf(u)) //{tantianran 18}}TypeOf()办法获取到变量的类型后,再调用它的Kind()办法能够将返回的后果用来进行类型的判断 ...

October 25, 2022 · 2 min · jiezi

关于go:Go常见错误系列的第13篇init函数的常见错误和最佳实践

前言这是Go常见谬误系列的第13篇:init函数的常见谬误和最佳实际。 素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 常见谬误和最佳实际很多Go语言开发者会谬误地应用package里的init函数,导致代码难懂,保护艰难。 咱们先回顾下package里init函数的概念,而后解说init函数的常见谬误和最佳实际。 init基本概念Go语言里的init函数有如下特点: init函数没有参数,没有返回值。如果加了参数或返回值,会编译报错。一个package上面的每个.go源文件都能够有本人的init函数。当这个package被import时,就会执行该package下的init函数。一个.go源文件里能够有一个或者多个init函数,尽管函数签名齐全一样,然而Go容许这么做。.go源文件里的全局常量和变量会先被编译器解析,而后再执行init函数。示例1咱们来看如下的代码示例: package mainimport "fmt"func init() { fmt.Println("init")}func init() { fmt.Println(a)}func main() { fmt.Println("main")}var a = func() int { fmt.Println("var") return 0}()go run main.go执行这段程序的后果是: varinit0main全局变量a的定义尽管放在了最初面,然而先被编译器解析,而后执行init函数,最初执行main函数。 示例2有2个package: main和redis,main这个package依赖了redis这个package。 package main import ( "fmt" "redis") func init() { // ...} func main() { err := redis.Store("foo", "bar") // ...}package redis // imports func init() { // ...} func Store(key, value string) error { // ...}因为main import了redis,所以redis这个package里的init函数先执行,而后再执行main这个package里的init函数。 ...

October 25, 2022 · 1 min · jiezi

关于go:Go-基本数据类型的相互转换

根本数据类型的互相转换Go在不同类型的变量之间赋值时须要显示转换,不能主动转换 根本语法表达式 T(v): 将值v转换成类型T T就是数据类型: int32, int64, float32... v就是须要转换的变量 不思考溢出的状况下,类型转换不会扭转数值大小var i int8 = 100var n int32 = int32(i)fmt.Println(i, n)输入:100 100 不反对隐式转换, 代码查看不通过,编译不能通过var n1 int32 = 30var n2 int16var n3 int64n2 = n1 + 2 // n1为int32, n1 + 2失去的还是int32类型, 而n2是int16类型n3 = n1 + 2 // 同上fmt.Println(n1, n2, n3)cannot use n1 + 2 (value of type int32) as int16 value in assignment批改如下:显示转换 n1为int32, n2为int16, 所以先把加数n1转换成int16, 再做加法 n2 = int16(n1) + 2n3 = int64(n1) + 2fmt.Println(n1, n2, n3)输入:30 32 32 ...

October 25, 2022 · 3 min · jiezi

关于go:Go一文玩转接口

接口的根本分析 package mainimport "fmt"type Test interface { show()}type myString stringfunc (mys myString) show() { fmt.Println(mys)}func main() { var a myString = "tantianran" a.show()}输入: tantianran定义接口:上述代码中,定义了接口Test,该接口规定了有show()办法,而且,Test接口只有1个办法。实现接口:接下来就是实现接口,在go中,任意类型只有实现了接口的所有办法(这里的Test接口只有1个办法),那么就实现了该接口(而且是隐式实现),刚提到任意类型,于是这里定义了一个类型为string的自定义类型myString,且由该自定义类型myString实现了show()办法,刚提到只有实现了接口的所有办法就实现了该接口,也就是说自定义的myString类型曾经实现了Test接口。 应用接口:定义了类型为myString的变量a,且给它赋值了"tantianran",这时候,它就领有了show办法。所以说,一个变量不论它是什么类型,只有实现了Test接口,就能调用它的show办法。 在上一个栗子的根底上,持续看看这个小栗子 刚提到任意类型只有实现了Test接口,就能调用它的show办法,来验证一下package mainimport "fmt"type Test interface { show()}type myInts int16func (myi myInts) show() { fmt.Println(myi)}func main() { var a myInts = 200 a.show()}输入: 200果然,自定义了一个int16类型的自定义类型myInts,它也用了show办法。持续上一个例子,看看上面代码 定义个构造体类型的User试试package mainimport "fmt"type Test interface { show()}type User struct { name string age int}func (u User) show() { fmt.Println(u)}func main() { u := User{"tantianran", 18} u.show()}输入: ...

October 24, 2022 · 2 min · jiezi

关于go:go微服务框架Kratos-连载三-创建restful接口

更不便的在微信公众号阅读文章能够关注公众号:海生的go花园 在上一节文章里 go微服务框架Kratos (连载二) :定义api接口以及实现 咱们学会了 定义以及实现kratos的api办法 本章节,咱们学习如何定义一个 创立一个本人的 restful接口 。 一、创立user的restful接口 proto 文件在官网文档 https://go-kratos.dev/docs/co... 外面,介绍了,如何创立一个 api 。咱们这里生成一个user.proto,应用: //生成 proto文件模版kratos proto add api/helloworld/v1/user.proto咱们发现在 api/helloworld/v1文件夹里,生成了一份 user.proto文件生成了user的增删改查,接口 CreateUser() 示意增DeleteUser() 示意删UpdateUser() 示意批改GetUser() 查问一条ListUser() 查问多条二、批改proto文件,定义restful路由咱们先引入proto的http的类库 import "google/api/annotations.proto";在user.proto文件,引入下面的库 2.1 批改 CreateUser()的http api 路由咱们把源码的 rpc CreateUser (CreateUserRequest) returns (CreateUserReply);批改如下: rpc CreateUser (CreateUserRequest) returns (CreateUserReply){ option (google.api.http) = { post: "/user", body: "*", }; };2.2 批改 DeleteUser()的http api 路由咱们把源码的 rpc DeleteUser (DeleteUserRequest) returns (DeleteUserReply);批改如下: rpc DeleteUser (DeleteUserRequest) returns (DeleteUserReply){ option (google.api.http) = { delete: "/user/{id}", }; };因为这里承受了 id 参数,所以咱们须要定义下DeleteUserRequest把源码的 ...

October 24, 2022 · 2 min · jiezi

关于go:go微服务框架Kratos-连载二-创建restful接口

更不便的在微信公众号阅读文章能够关注公众号:海生的go花园 在上一节文章里 go微服务框架Kratos(连载一):入门教程(装置以及跑通示例)咱们学会了 装置kratos。 本章节,咱们学习如何创立一个 http服务,以及对外提供 restful接口 一、运行样例咱们先启动一个kratos样例的http服务在上节创立的helloworld我的项目目录执行 go run ./cmd/helloworld -conf configs/config.yaml能够启动http服务。咱们关上 浏览器输出 http://localhost:8000/helloworld/1会返回能够发现样例运行胜利了。 二、样例api接口浏览咱们关上应用kratos建设的样例我的项目helloworld,在 greeter.proto文件里有 上面一个路由 get: "/helloworld/{name}" 2.1 批改样例,新增一个 get 申请2.1.1 咱们关上 greeter.proto文件,对照 SayHello减少一个接口SayHi如下:// Sends a hi rpc SayHi (HelloRequest) returns (HelloReply) { option (google.api.http) = { get: "/hi/{name}" }; }写好后如下: 2.1.2 执行make api,生产api接口➜ make api输入: protoc --proto_path=./api \ --proto_path=./third_party \ --go_out=paths=source_relative:./api \ --go-http_out=paths=source_relative:./api \ --go-grpc_out=paths=source_relative:./api \ --openapi_out=fq_schema_naming=true,default_response=false:. \ api/helloworld/v1/error_reason.proto api/helloworld/v1/greeter.proto如果执行make api 报错,比方提醒,未装置 protoc-gen-go: program not found or is not executable,能够在 make api 执行之前,先执行一下 make init 装置一下kratos须要的依赖和插件。此时咱们能够看 greeter_http.pb.go 外面的代码,减少SayHi()如下: ...

October 24, 2022 · 2 min · jiezi

关于go:关于golang的逗号ok模式的使用整理

对于golang的逗号ok模式的应用整顿 /*@Time : 2019-02-23 11:49 @Author : Tenlu@File : ok@Software: GoLand*/package main import "fmt" // 逗号ok模式func main() { useMap() useChan() userType() useType2()} //1.检测map中是否含有指定keyfunc useMap() { company := map[string]string{"polly": "tencent", "robin": "baidu", "jack": "alibaba", "tenlu": "itech8"} if _, ok := company["toms"]; !ok { fmt.Println("key toms is not exists") } for ck, cv := range company { fmt.Println(ck + "->" + cv) }} // 2. 检测chan是否敞开func useChan() { ch := make(chan int, 10) for i := 1; i <= 5; i++ { ch <- i } close(ch) // chan赋值完结,必须敞开通道 elem := <-ch fmt.Println(elem) // 1 // FIFO队列,先发送的chan肯定最先被接管 for cc := range ch { fmt.Println(cc) } // ch <-8 // 此时再往ch这个chan里发送数据,就会报错,因为通道曾经敞开,panic:send on closed channel elem2 := <-ch elem3 := <-ch fmt.Printf("elem2:%d\n", elem2) // 0,因为通道曾经敞开 fmt.Printf("elem3:%d\n", elem3) // 0 if _, isClosed := <-ch; !isClosed { fmt.Println("channel closed") } // go是没有while循环的,此处相似其余语言的while(true) for { i, ok := <-ch if !ok { fmt.Println("channel closed!") break } fmt.Println(i) }} // 3.检测是否实现了接口类型type I interface { // 有一个办法的接口 I Get() Int} type Int int // Int 类型实现了 I 接口func (i Int) Get() Int { return i} func userType() { var myint Int = 5 var inter I = myint // 变量赋值给接口 val, ok := inter.(Int) fmt.Printf("%v, %v\n", val, ok) // 输入为:5,true}func useType2() { // chan 类型的空值是 nil,申明后须要配合 make 后能力应用。 var ch=make(chan interface{},10) ch<-10 ch<-"jack" ch<-map[string]interface{}{"name":"jack","age":11} close(ch) // 此处不close通道,遍历时则报错 fmt.Printf("%v\n",<- ch) for c:=range ch { fmt.Printf("for:%v\n",c) fmt.Sprintf("type:%T\n", c) if newValue,ok:=c.(map[string]interface{});ok{ fmt.Printf("type:%s\n",reflect.TypeOf(c)) // 获取变量类型 // map[string]interface {} if _,ok:=newValue["name"];ok{ for k,v:=range newValue { fmt.Printf("%v->%v,\n",k,v) } } } } fmt.Printf("%v\n",<- ch) // nil}func useType2() { // chan 类型的空值是 nil,申明后须要配合 make 后能力应用。 var ch=make(chan interface{},10) ch<-10 ch<-"jack" ch<-map[string]interface{}{"name":"jack","age":11} close(ch) // 此处不close通道,遍历时则报错 fmt.Printf("%v\n",<- ch) for c:=range ch { fmt.Printf("for:%v\n",c) fmt.Sprintf("type:%T\n", c) if newValue,ok:=c.(map[string]interface{});ok{ fmt.Printf("type:%s\n",reflect.TypeOf(c)) // 获取变量类型 // map[string]interface {} if _,ok:=newValue["name"];ok{ for k,v:=range newValue { fmt.Printf("%v->%v,\n",k,v) } } } } fmt.Printf("%v\n",<- ch) // nil}

October 24, 2022 · 2 min · jiezi

关于go:大意遇到的初级坑chan的读取参数的问题

invalid operation: range ch (receive from send-only type chan<- int) package main import ( "fmt" "os" "os/signal" "syscall")// 向chan 发送数据,只能向chan里写数据func producters(factor int,c chan<- int) { for i:=1; i<10;i++ { c<- factor * i }}// 承受参数,// 只能取channel中的数据,千万不要写成chan<-// 不然会报错:// ./producersAndConsumers.go:24:9:// invalid operation: range ch (receive from send-only type chan<- int)func consumers(ch <-chan int) { for v:=range ch { fmt.Println("reev:",v) }}func main() { var status chan int if status == nil { fmt.Println("未初始化的chan 值是nil") } status = make(chan int) status <- 1 status <- 2 // 如果塞值超过 设置的chan 缓冲值,或者是个无缓冲的chan,当超过容量就回呈现死锁 ch:=make(chan int,10) go producters(2,ch) go producters(3,ch) go consumers(ch) // Ctrl+C 退出 sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) fmt.Printf("quit (%v)\n", <-sig)}

October 24, 2022 · 1 min · jiezi

关于go:Go-try-新提案靠谱吗想简化错误处理了

大家好,我是煎鱼。 在近日新的 try 提案《proposal: Go 2: error handling: try statement with handler》在社区引发了热议。 明天煎鱼和大家一起关上来看看,这能把 Go 错误处理机制给掀开重整不。 背景来自 PingCAP 的提案作者 @Greg Weber 会干这事基于两个因素,一个是在《Go Developer Survey 2022 Q2 Results》中明确提到。 随着 Go1.18 泛型的公布,原先矛盾最深的泛型曾经失去一个初步的解决方案。在社区调研上,开发者在应用 Go 时面临的最大挑战曾经转移到了错误处理上,须要投入精力去 “解决” 它。 另外一个因素就是众所皆知的,Go 错误处理代码比拟繁琐,常被工程师们戏称一个 Go 工程里有 30% 都 if err = nil。 如下代码: _, err := f()if err != nil { ...}_, err = r()if err != nil { ...}_, err = w()if err != nil { ...}心愿让其更优雅。也有许多小伙伴认同这个设计,的确是简略、直观的解决,在社区造成了角力。 ...

October 24, 2022 · 3 min · jiezi

关于go:Go语言三十讲目录

第一章 Go语言疾速入门 第一篇 根本语法 第二讲 数组与切片 第三讲 字符串 第四讲 哈希表MAP 第五讲 构造体与接口 第六讲 反射 第七讲 泛型第二章 并发编程 第八讲 GMP调度模型 第九讲 协程治理 第十讲 调度器schedule 第十一讲 网络IO 第十二讲 管道chan 第十三讲 定时器timer 第十四讲 零碎调用 第十五讲 panic defer recover 第十六讲 并发编程第三章 垃圾回收(GC) 第十七讲 内存治理 第十八讲 三色标记与写屏障 第十九讲 标记 清理 第二十讲 GC调度与调优第四章 罕用规范库 第二十一讲 net/http.server 第二十二讲 net/http.client 第二十三讲 上下文context 第二十四讲 单元测试第五章 实战 第二十五讲 Go程序剖析利器pprof 第二十六讲 dlv调试 第二十七讲 HTTP服务假死问题剖析 第二十八讲 Go服务502总结 第二十九讲 Go微服务发现问题剖析 第三十讲 平滑降级

October 24, 2022 · 1 min · jiezi

关于go:56-Golang实战平滑升级

 Go服务作为常驻过程,想降级怎么办?你是不是想说这还不简略,先杀掉老的服务,再启动新的服务不就完了。可是你有没有想过,在你杀掉老服务的时候,正在解决的申请怎么办?以及老服务退出新服务启动的过程中,客户端申请达到了怎么办?这一简略粗犷的操作,必然会引起刹时的申请异样。那怎么办,想方法平滑降级呗。 信号 为什么要先介绍信号呢?因为当咱们须要让过程退出的时候,通常就是给过程发送一个退出信号,比方ctrl+C组合其实就是给过程发送了SIGINT信号。发送了信号而后呢?过程当然能够捕捉信号了,零碎容许过程收到信号后(退出前)做一些解决工作,那这样咱们是不是能还能持续解决以后申请,而后敞开连贯、开释资源等,实现后再退出,从而实现所谓得平滑退出。 咱们简略介绍下信号,信号分为规范信号(不牢靠信号)和实时信号(牢靠信号),规范信号是从1-31,实时信号是从32-64。咱们熟知的信号比方,SIGINT,SIGQUIT,SIGKILL等等都是规范信号。个别咱们给某个过程发送信号,能够应用kill命令,比方kill -9 pid,就是发送SIGKILL信号;kill -INT pid,就能够发送SIGINT信号给过程。 信号处理器是指当捕捉指定信号时(传递给过程)时将会调用的一个函数,信号处理器程序可能随时打断过程的主程序流程。Go语言注册的信号处理器是runtime.sighandler函数。 当然Go语言中应用信号还是比较简单的,不须要咱们再注册信号处理器之类的,如上面程序所示: package mainimport ( "fmt" "os" "os/signal" "sync" "syscall")func main() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) wg := sync.WaitGroup{} wg.Add(1) go func() { <- c fmt.Println("quit signal receive, quit") wg.Done() }() wg.Wait()}/*^C quit signal receive, quit*/ "^C"阐明咱们按下ctrl+C组合键,这样会给过程发送SIGINT信号,能够看到,先输入语句程序再退出。你能够试一试,如果没有监听SIGINT信号,程序会间接退出,并输入"Process finished with exit code 2"。 signal.Notify函数注册咱们想监听的信号,第一个参数是管道chan类型,当过程捕捉到该信号时,会向管道写入数据,此时管道可读,所以咱们能够通过读管道感知信号的到来。 最初,咱们简略介绍下Go语言信号处理框架,如下图所示: signal.Notify函数注册管道与监听信号的映射关系,这些数据保护在一个全副的map,key为管道变量,value称之为mask,位标记须要监听的哪些信号;如果之前没有监听过该信号,这里还须要为该信号注册(signal_enable)信号处理器sighandler。过程捕捉到信号后,会执行信号处理器sighandler,其再通过异步形式散发信号,一旦咱们程序中应用了signal.Notify函数,就会启动子协程循环异步接管信号,并做散发,也就是写数据到对应的管道。 平滑退出 咱们曾经理解到如何监听并解决信号了,那如何实现Go过程的平滑退出呢?假如Go过程作为HTTP服务,正在解决申请,接管到退出信号后,是不是应该持续解决这些申请,另外是不是应该防止新的申请进来(敞开监听的socket),等所有处理完毕后,Go过程再退出。 Go语言自身就提供了平滑完结HTTP服务的办法,所以咱们只须要监听退出信号(如SIGINT、SIGTERM等),接管到信号之后调用对应办法就行了: ...

October 24, 2022 · 3 min · jiezi

关于go:55-Golang实战Go微服务发现问题分析

问题引入 某个夜黑风高的早晨,忽然接管到大量谬误日志报警『failed to dial server: dial tcp xxxx:yy: i/o timeout』。原来是微服务客户端申请服务端,连贯失败。 简略介绍下服务现状:咱们的服务部署在k8s环境,微服务框架咱们应用的是smallnest/rpcx,注册核心基于zookeeper,链路如下图所示: 接下来初步剖析下问题现状: 第一步:这些连贯超时的服务端是有什么异样吗?依据连贯超时的IP地址查找对应的容器服务,发现没有一个容器服务是这个IP地址;那这个IP地址是从哪来的呢?难道是在某个隐秘的角落启动的?第二步:连贯注册核心,查看该服务端注册的IP列表,发现也不存在下面超时的IP地址。进一步:这个异样IP地址,k8s环境历史的确调配过;猜想服务端重启后,IP地址变了,然而客户端却没有更新IP列表,导致还申请老的IP地址。 另外,谬误日志只集中在一个客户端实例,即只有一个客户端容器服务没有更新服务端的IP列表。初步猜想可能有两种起因:1)这个客户端与zookeeper之间连贯存在异样,所以zookeeper无奈告诉数据变更;2)服务发现框架存在代码异样,且只在某些场景触发,导致无奈更新本地IP列表,或者是没有watch数据变更。 针对第一种猜想,很简略就能验证,登录到该异样pod,查看与zookeeper之间的连贯即可: # netstat -anp | grep 2181tcp 0 0 xxxx:51970 yyyy:2181 ESTABLISHED 9/xxxxtcp 0 0 xxxx:40510 yyyy:2181 ESTABLISHED 9/xxxx 能够看到存在两条失常的TCP连贯,为什么是两条呢?因为该过程不止作为客户端拜访其余服务,还作为服务端供其余客户端调用,其中一条连贯是用来注册服务,另外一条连贯用来发现服务。tcpdump抓包看看这两条连贯的数据交互: 23:01:58.363413 IP xxxx.51970 > yyyy.2181: Flag [P.], seq 2951753839:2951753851, ack 453590484, win 356, length 1223:01:58.363780 IP yyyy.2181 > xxxx.51970: Flags [P.], seq 453590484:453590504, ack 2951753851, win 57, length 2023:01:58.363814 IP xxxx.51970 > yyyy.2181: Flags [.], ack 453590504, win 356, length 0…… 下面省略了抓包的内容局部。留神zookeeper点采纳二进制协定,不太不便辨认然而根本能够确信,这是ping-pong心跳包(定时交互,且每次数据统一)。并且,两条连贯都有失常的心跳包频繁交互。也就是说客户端与zookeeper之间连贯失常,那么很可能就是服务发现框架的代码问题了。 ...

October 24, 2022 · 4 min · jiezi

关于go:54-Golang实战Go服务502总结

问题引入 生产环境Golang服务有时会产生502报警,排查发现大多是以下三种起因造成的: http.Server配置了WriteTimeout,申请解决超时,Golang服务断开连接;http.Server配置了IdleTimeout,且网关和Golang之间应用长连贯,Golang服务被动断开连接;Golang服务解决HTTP申请时呈现了panic。 本篇文章次要介绍这三种502产生的具体起因,另外本篇文章提到的网关是基于Nginx搭建的。 WriteTimeout WriteTimeout的含意是:从接管到客户端申请开始,到返回响应的最大超时工夫,默认为0意味着不超时。正文如下: type Server struct { // WriteTimeout is the maximum duration before timing out // writes of the response. It is reset whenever a new // request's header is read. Like ReadTimeout, it does not // let Handlers make decisions on a per-request basis. // A zero or negative value means there will be no timeout. WriteTimeout time.Duration} 正文表明,当接管到新的申请时,超时工夫会重置。那么什么时候设置写超时工夫呢?当然是在http.Server读取客户端申请实现时了,参考readRequest办法: func (c *conn) readRequest(ctx context.Context) (w *response, err error) { if d := c.server.WriteTimeout; d != 0 { //defer,也就是函数返回时设置定时器 defer func() { c.rwc.SetWriteDeadline(time.Now().Add(d)) }() }} 可是,为什么WriteTimeout会导致502呢?如果你凑巧也遇到了之类问题,能够查看网关拜访日志,你会看到所有申请的响应request_time都大于WriteTimeout,并且谬误日志显示的是"upstream prematurely close connection while reading response header from upstream",也就是在网关等到上游响应时,上游敞开连贯了。剩下的问题就是为什么上游会敞开连贯了。 ...

October 24, 2022 · 5 min · jiezi

关于go:53-Golang实战HTTP服务假死问题分析

问题形容 下午15点左右,QA反馈灰度环境大量申请超时。kibana查问灰度网关日志,的确存在局部申请响应工夫超过60秒,HTTP状态码504。进一步剖析日志,所有504申请的上游地址都是xxxx:80。 目前该服务部署了两套环境,k8s + kvm,k8s环境上游ingress(即Nginx)端口80,kvm环境上游Golang服务端口19001。是单单k8s环境服务有问题吗? 登录到k8s服务终端,手动curl申请(healthCheck接口,没有简单的业务解决,间接返回数据),发现申请没有任何响应,且始终阻塞。很大概率是该Golang服务有问题了。 排查过程 healthCheck接口逻辑十分的简略,为什么会阻塞呢?服务没有接管到该申请吗?tcpdump抓包看看: //xxxx为k8s入口ingress地址curl http://xxxx/v1/healthCheck -H "Host:xxxxxxx"//三次握手10:20:21.940968 IP xxxx.40970 > server.19001: Flags [S], seq 3201212889, win 29200, length 010:20:21.941003 IP server.19001 > xxxx.40970: Flags [S.], seq 1905160768, ack 3201212890, win 28960, length 010:20:21.941175 IP xxxx.40970 > server.19001: Flags [.], ack 1905160769, win 58, length 0//发送HTTP申请数据10:20:21.941219 IP xxxx.40970 > server.19001: Flags [P.], seq 3201212890:3201213201, ack 1905160769, win 58, length 31110:20:21.941223 IP server.19001 > xxxx.40970: Flags [.], ack 3201213201, win 59, length 0//客户端被动断开连接10:21:21.945740 IP xxxx.40970 > server.19001: Flags [F.], seq 3201213201, ack 1905160769, win 58, length 010:21:21.985062 IP server.19001 > xxxx.40970: Flags [.], ack 3201213202, win 59, length 0 能够看到,三次握手胜利,客户端发送了HTTP数据,服务端也失常返回ACk;然而没下文了,客户端期待60秒后,被动断开了连贯。(Nginx配置:proxy_read_timeout=60秒)。 ...

October 24, 2022 · 2 min · jiezi

关于go:Go-面试题收集持续charge

01一个函数内defer的执行程序与其申明程序相同呈现panic语句时,会先执行defer后执行panic呈现panic语句时,申明在panic前面的任何代码不再执行package mainimport( "fmt")func main(){ defer func(){fmt.Println("打印前")}() defer func(){fmt.Println("打印中")}() defer func(){fmt.Println("打印后")}() panic("完结")}$ go run main.go 打印后打印中打印前panic: 完结02for range 循环的时候会创立每个元素的正本,而不是元素的援用所以 m[key] = &val 取的都是变量 val 的地址所以最初 map 中的所有元素的值都是变量 val 的地址,因为最初 val 被赋值为3,所有输入都是3package mainimport( "fmt")func main(){ slice := []int{0,1,2,3} m := make(map[int]*int) for key,val := range slice { m[key] = &val } for k,v := range m { fmt.Println(k,"->",*v) } }$ go run main.go 0 -> 31 -> 32 -> 33 -> 303// 上面两段代码输入什么// 1. func main() { s := make([]int, 5) s = append(s, 1, 2, 3) fmt.Println(s) }// [0,0,0,0,0,1,2,3]// 2. func main() { s := make([]int,0) s = append(s,1,2,3,4) fmt.Println(s)}// [1,2,3,4]// 上面这段代码有什么缺点func funcMui(x,y int)(sum int,error){ return x+y,nil}// 第二个返回值没有命名// 正确写法func funcMui(x,y int)(sum int, err error){ return x+y,nil}// new() 与 make() 的区别package mainimport( "fmt") func main(){ s1 := new([]int) s2 := make([]int, 0) fmt.Println(s1) fmt.Println(s2)}// 输入: $ go run main.go &[][]04// 上面这段代码是否通过编译,不能的话起因是什么;如果能,输入什么。func main() { list := new([]int) list = append(list, 1) fmt.Println(list)}// 不能通过编译,new([]int) 之后的 list 是一个 *[]int 类型的指针,// 不能对指针执行 append 操作。// 能够改写为:func main() { list := new([]int) *list = append(*list, 1) fmt.Println(*list)}// 上面这段代码是否通过编译,如果能够,输入什么?func main() { s1 := []int{1, 2, 3} s2 := []int{4, 5} s1 = append(s1, s2) fmt.Println(s1)}// 不能通过编译。// append() 的第二个参数不能间接应用 slice,需应用 … 操作符,将一个切片追加到另一个切片上:append(s1,s2…)。或者间接跟上元素,形如:append(s1,1,2,3)。// 上面这段代码是否通过编译,如果能够,输入什么?var( size := 1024 max_size = size*2)func main() { fmt.Println(size,max_size)}// 不能通过编译。变量申明的简短模式,形如:x := 100。有限度:// 只能在函数外部应用简短模式05// 上面这段代码是否通过编译package mainimport( "fmt") func main() { sn1 := struct { age int name string }{age: 11, name: "qq"} sn2 := struct { age int name string }{age: 11, name: "qq"} if sn1 == sn2 { fmt.Println("sn1 == sn2") } sm1 := struct { age int m map[string]string }{age: 11, m: map[string]string{"a": "1"}} sm2 := struct { age int m map[string]string }{age: 11, m: map[string]string{"a": "1"}} if sm1 == sm2 { fmt.Println("sm1 == sm2") }}// 不能通过编译, 因为// invalid operation: sm1 == sm2 (struct containing map[string]string cannot be compared)// 什么是可比拟的呢,常见的有 bool、数值型、字符、指针、数组等,像切片、map、函数等是不能比拟的

October 24, 2022 · 2 min · jiezi

关于go:彻底理解闭包实现原理

前言闭包对于一个长期写 Java 的开发者来说预计鲜有耳闻,我在写 Python 和 Go 之前也是没怎么理解,光这名字感觉就有点"神秘莫测",这篇文章的次要目标就是从编译器的角度来剖析闭包,彻底搞懂闭包的实现原理。 函数一等公民一门语言在实现闭包之前首先要具备的个性就是:First class function 函数是第一公民。 简略来说就是函数能够像一个一般的值一样在函数中传递,也能对变量赋值。 先来看看在 Go 里是如何编写的: package mainimport "fmt"var varExternal intfunc f1() func(int) int { varInner := 20 innerFun := func(a int) int { fmt.Println(a) varExternal++ varInner++ return varInner } return innerFun}func main() { varExternal = 10 f2 := f1() for i := 0; i < 2; i++ { fmt.Printf("varInner=%d, varExternal=%d \n", f2(i), varExternal) } fmt.Println("======") f3 := f1() for i := 0; i < 2; i++ { fmt.Printf("varInner=%d, varExternal=%d \n", f3(i), varExternal) }}// Output:0varInner=21, varExternal=11 1varInner=22, varExternal=12 ======0varInner=21, varExternal=13 1varInner=22, varExternal=14 这里体现了闭包的两个重要个性,第一个天然就是函数能够作为值返回,同时也能赋值给变量。 ...

October 24, 2022 · 2 min · jiezi

关于go:golang中的nil接收器

索引:https://waterflow.link/articles/1666534616841 咱们先看一个简略的例子,咱们自定义一个谬误,用来把多个谬误放在一起输入: type CustomError struct { errors []string}func (c *CustomError) Add(err string) { c.errors = append(c.errors, err)}func (c *CustomError) Error() string { return strings.Join(c.errors, ";")}因为实现了Error() string办法,所以它实现了error接口。 当初咱们要实现一个增加课件的性能,然而增加之前须要验证参数的合法性,所以咱们创立了一个Validate办法,咱们可能会这么写: package mainimport ( "errors" "fmt" "strings")type CustomError struct { errors []string}func (c *CustomError) Add(err error) { c.errors = append(c.errors, err.Error())}func (c *CustomError) Error() string { return strings.Join(c.errors, ";")}type Courseware struct { Name string Code string}func (c *Courseware) Validate() error { var m *CustomError // 1 if c.Name == "" { // 2 m = &CustomError{} m.Add(errors.New("课件名不能为空")) } if c.Code == "" { // 3 if m == nil { m = &CustomError{} } m.Add(errors.New("课件编号不能为空")) } return m // 4}func main() { m := Courseware{ Name: "多媒体课件", Code: "CW330", } if err := m.Validate(); err != nil { fmt.Println("valid err: ", err) }}看上去如同一点问题都没有: ...

October 23, 2022 · 2 min · jiezi

关于go:Go-Zero-快速构建高并发微服务

Go - Zero根本架构图 筹备工作装置 etcd, mysql, redis go # etcd(这个自己之前装的比拟少, 所以上面会贴出装置步骤, 仅供参考, 自行谷歌)curl -L https://github.com/coreos/etcd/releases/download/v3.2.1/etcd-v3.2.1-linux-amd64.tar.gz -o etcd-v3.2.1-linux-amd64.tar.gztar xzvf etcd-v3.2.1-linux-amd64.tar.gzmv etcd-v3.2.1-linux-amd64 etcdcd etcd$ ./etcd --versionetcd Version: 3.2.1Git SHA: 61fc123Go Version: go1.8.3Go OS/Arch: linux/amd64# mysql, redis(这两个比拟常见,上面仅贴出启动步骤)sudo service mysql startsudo service redis-server start装置 protoc-gen-go go get -u github.com/golang/protobuf/[email protected]装置 protoc wget https://github.com/protocolbuffers/protobuf/releases/download/v3.14.0/protoc-3.14.0-linux-x86_64.zipunzip protoc-3.14.0-linux-x86_64.zipsudo cp bin/protoc /usr/local/bin/sudo chmod 777 /usr/local/bin/protoc$ protoc --version libprotoc 3.14.0装置 goctl 工具 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/[email protected]创立工作目录 shorturl 和 shorturl/api ...

October 23, 2022 · 2 min · jiezi

关于go:golang中的字符串

0.1、索引https://waterflow.link/articles/1666449874974 1、字符串编码在go中rune是一个unicode编码点。 咱们都晓得UTF-8将字符编码为1-4个字节,比方咱们罕用的汉字,UTF-8编码为3个字节。所以rune也是int32的别名。 type rune = int32当咱们打印一个英文字符hello的时候,咱们能够失去s的长度为5,因为英文字母代表1个字节: package mainimport "fmt"func main() { s := "hello" fmt.Println(len(s)) // 5}然而当咱们打印嗨的时候,会打印3个字节。因为应用UTF-8,这个字符会被编码成3个字节: package mainimport "fmt"func main() { s := "嗨" fmt.Println(len(s)) // 3}所以,咱们应用len内置函数输入的并不是字符数,而是字节数。 上面看一个乏味的例子,咱们都晓得汉字符应用3个字节编码,别离是0xE5, 0x97, 0xA8。咱们运行上面代码会失去汉字嗨: package mainimport "fmt"func main() { s := string([]byte{0xE5, 0x97, 0xA8}) fmt.Println(s) // 嗨}所以咱们须要晓得: 字符集是一组字符,而编码形容了如何将字符集转换为二进制在 Go 中,字符串援用任意字节的不可变切片Go 源码应用 UTF-8 编码。 因而,所有字符串文字都是 UTF-8 字符串。 然而因为字符串能够蕴含任意字节,如果它是从其余中央(不是源码)取得的,则不能保障它是基于 UTF-8 编码的应用 UTF-8,一个 Unicode 字符能够编码为 1 到 4 个字节在 Go 中对字符串应用 len 返回字节数,而不是字符数2、字符串遍历咱们在开发中常常会用到对字符串进行遍历的场景。 兴许咱们想对字符串中的每个 rune 执行一个操作,或者实现一个自定义函数来搜寻特定的子字符串。 在这两种状况下,咱们都必须遍历字符串的不同字符。 但往往会失去让咱们意想不到的后果。 ...

October 22, 2022 · 3 min · jiezi

关于go:Go-操作MySQL

环境筹备# MySQL筹备工作$ sudo service mysql start * Starting MySQL database server mysqld ...done.$ mysql -uroot -e "create database test;"$ mysql -urootMariaDB [test]> CREATE TABLE `person` ( -> `id` int(11) NOT NULL AUTO_INCREMENT, -> `name` varchar(260) DEFAULT NULL, -> `age` int(28) DEFAULT NULL, -> PRIMARY KEY (`id`) -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;Query OK, 0 rows affected (0.014 sec)# 导包$ go get github.com/go-sql-driver/mysql$ go get github.com/jmoiron/sqlx连贯数据库$ mkdir mysql-demo && cd mysql-demo && go mod init mysql-demo$ vi main.go// main.gopackage mainimport( "fmt" "github.com/jmoiron/sqlx" _ "github.com/go-sql-driver/mysql")func main(){ db, err := sqlx.Open("mysql", "root:@tcp(localhost:3306)/test") if err != nil{ panic(err) } defer db.Close() fmt.Println("connect to MySQL success...")}$ go run main.go connect to MySQL success...Insert 操作// main.gopackage mainimport( "fmt" "github.com/jmoiron/sqlx" _ "github.com/go-sql-driver/mysql")var ( Db *sqlx.DB)func init(){ db, err := sqlx.Open("mysql", "root:@tcp(localhost:3306)/test") if err != nil{ fmt.Println("connect to MySQL failed...") panic(err) } // defer db.Close() Db = db fmt.Println("connect to MySQL success...")}func main(){ defer Db.Close() r, err := Db.Exec("insert into person(name, age)values(?,?)","xiaoming","18") if err!=nil{ fmt.Println("exec failed, ", err) panic(err) } id, err := r.LastInsertId() if err != nil { fmt.Println("exec failed, ", err) return } fmt.Println("insert succ:", id)}$ go run main.go connect to MySQL success...insert succ: 1Select 操作// main.gopackage mainimport( "fmt" "github.com/jmoiron/sqlx" _ "github.com/go-sql-driver/mysql")var ( Db *sqlx.DB)type Person struct { Id int `db:"id"` Name string `db:"name"` Age string `db:"age"`}func init(){ db, err := sqlx.Open("mysql", "root:@tcp(localhost:3306)/test") if err != nil{ fmt.Println("connect to MySQL failed...") panic(err) } // defer db.Close() Db = db fmt.Println("connect to MySQL success...")}func main(){ defer Db.Close() var p []Person err := Db.Select(&p, "select id,name,age from person where id = ?", 1) if err!=nil{ fmt.Println("exec failed, ", err) panic(err) } fmt.Println("select succ:", p)}$ go run main.go connect to MySQL success...select succ: [{1 xiaoming 18}]Update操作package mainimport( "fmt" "github.com/jmoiron/sqlx" _ "github.com/go-sql-driver/mysql")var ( Db *sqlx.DB)func init(){ db, err := sqlx.Open("mysql", "root:@tcp(localhost:3306)/test") if err != nil{ fmt.Println("connect to MySQL failed...") panic(err) } // defer db.Close() Db = db fmt.Println("connect to MySQL success...")}func main(){ defer Db.Close() res, err := Db.Exec("update person set name=? where id=?", "xiaobai", 1) if err != nil { fmt.Println("exec failed, ", err) return } row, err := res.RowsAffected() if err != nil { fmt.Println("rows failed, ",err) } fmt.Println("update succ:",row)}$ go run main.go connect to MySQL success...update succ: 1Delete 操作package mainimport( "fmt" "github.com/jmoiron/sqlx" _ "github.com/go-sql-driver/mysql")var ( Db *sqlx.DB)func init(){ db, err := sqlx.Open("mysql", "root:@tcp(localhost:3306)/test") if err != nil{ fmt.Println("connect to MySQL failed...") panic(err) } // defer db.Close() Db = db fmt.Println("connect to MySQL success...")}func main(){ defer Db.Close() res, err := Db.Exec("delete from person where id=?", 1) if err != nil { fmt.Println("exec failed, ", err) return } row, err := res.RowsAffected() if err != nil { fmt.Println("rows failed, ",err) } fmt.Println("delete succ:",row)}$ go run main.go connect to MySQL success...delete succ: 1MySQL事务事务具备四个个性(ACID):原子性 一致性 隔离性 持久性开启事务 Db.Begin()提交事务 Db.Commit()回滚事务 Db.Rollback()package mainimport( "fmt" "github.com/jmoiron/sqlx" _ "github.com/go-sql-driver/mysql")var ( Db *sqlx.DB)func init(){ db, err := sqlx.Open("mysql", "root:@tcp(localhost:3306)/test") if err != nil{ fmt.Println("connect to MySQL failed...") panic(err) } // defer db.Close() Db = db fmt.Println("connect to MySQL success...")}func main(){ defer Db.Close() // begin transaction conn, err := Db.Begin() if err != nil { fmt.Println("begin failed :", err) return } r, err := conn.Exec("insert into person(name, age)values(?, ?)", "xiaoming", 18) if err != nil { fmt.Println("exec failed, ", err) conn.Rollback() // rollback transaction return } id, err := r.LastInsertId() if err != nil { fmt.Println("exec failed, ", err) conn.Rollback() // rollback transaction return } fmt.Println("insert succ:", id) r, err = conn.Exec("insert into person(name, age)values(?, ?)", "xiaohong", 18) if err != nil { fmt.Println("exec failed, ", err) conn.Rollback() // rollback transaction return } id, err = r.LastInsertId() if err != nil { fmt.Println("exec failed, ", err) conn.Rollback() // rollback transaction return } fmt.Println("insert succ:", id) conn.Commit() // commit transaction}$ go run main.go connect to MySQL success...insert succ: 2insert succ: 3

October 22, 2022 · 3 min · jiezi

关于go:安装使用go多个版本解决方案goland方案

一、前言go介绍go理论应用中,咱们依据我的项目的建设工夫,会应用到go的多个版本。理论开发的我的项目越来越多,咱们应用的版本也越来越多。每个版本会对应独立的 go命令以及,此版本的依赖以及插件等等。 goland介绍GoLand 是 JetBrains 公司推出的 Go 语言集成开发环境。领有十分弱小的工具,此时也对多个版本出了本人的计划。次要应用 设置 GOROOT 和GOPATH 来解决这个多版本问题。 二、goland多版本计划-已有我的项目2.1 已有我的项目比方现有一个 https://github.com/hisheng/xo... 我的项目。 2.2 下载代码git clone https://github.com/hisheng/xormmodel2.3 用goland关上 xormmodel 我的项目 2.4 设置GOROOT咱们关上 go.mod 文件查看go 对应的版本号,发现是 go1.18,形式如下。而后咱们在关上goland的GOROOT设置页面,如下点击加号,呈现一个 “下载 Go SDK”弹出框在版本外面抉择 go1.18.7而后再抉择地位,go要下载的中央,点击确定。而后再点击 利用和确定。 2.5 设置GOPATH关上GOPATH设置页面第一个 方框点击加号 [email protected]目录为咱们新建的专门存储go依赖的目录。而后再去掉第二框√号 “应用零碎环境中定义的gopath” 再利用和确定。此时咱们就针对这个我的项目 独立的设置了 GOROOT和GOPATH,应用的是 go1.18.7版本。装置的依赖放到了 [email protected]目录 (当然你也能够建一个[email protected]目录)我这里感觉把1.18版本的都放到一个目录了。 本文以实现,浏览更多其余优质go语言文章,欢送关注公众号

October 22, 2022 · 1 min · jiezi

关于go:Go-基本数据类型

前言根本数据类型,变量存的就是值,也叫值类型。每一种数据都定义了明确的数据类型,在内存中调配了不同大小的内存空间。 Printf 和 Println 的区别printf 输入后不换行, println输入后主动换行;printf 格式化输入,println 间接输入内容Printf 依据format参数生成格式化的字符串并写入规范输入。判断数据类型以及查看变量应用内存空间package main// import "unsafe"// import "fmt"// 引入多个import ("fmt""unsafe")func main() { var i int8 = 12 fmt.Println("i= ", i) var i2 uint8 = 129 fmt.Println("i2= ", i2) var n = 12 fmt.Printf("n的类型是%T\n", n) // n的类型是int var name = "xiao" fmt.Printf("name的类型是%T\n", name) // name的类型是string // 查看变量占用的字节大小和数据类型 fmt.Printf("name的类型是%T", name, unsafe.Sizeof(name))}根本数据类型数值型整数类型 (默认值是0)int, int8, int16, int32, int64uint, uint8, uint16, uint32, uint64 无符号数(示意的范畴更大)byte bit: 计算机中的最小存储单位byte: 计算机中根本存储单元1byte = 8bit (一个字节8位) ...

October 22, 2022 · 2 min · jiezi

关于go:精通-Golang-适配器模式

原文首发于我的博客:https://kaolengmian7.com/post... 示例代码曾经上传到我的 Github:https://github.com/kaolengmian7/golang-demo/tree/master/design_pattern/adapter,能够把代码拉下来跑一跑~ 核心思想适配器是两个不兼容的接口之间的桥梁,使接口不兼容的对象可能相互合作。 应用场景有动机地批改一个失常运行的零碎的接口,这时应该思考应用适配器模式。1. 封装有缺点的接口2. 封装多个接口3. 替换依赖4. 兼容老版本接口5. 适配不同格局数据 肯定要留神:适配器为的是解决正在退役的我的项目的问题,而不是在设计阶段增加的。如果在设计阶段就思考应用适配器模式,那这个设计肯定不合格。实战如果你正在开发一款股票市场监测程序, 它会从不同起源下载 XML 格局的股票数据, 而后向用户呈现出好看的图表。 type XmlHandler interface { ProcessXml(xml string)}type xmlHandler struct{}func (x *xmlHandler) ProcessXml(xml string) { log.Printf("xml:%s processing", xml)}在开发过程中, 你决定在程序中整合一个第三方智能剖析函数库。 然而遇到了一个问题, 那就是剖析函数库只兼容 JSON 格局的数据。 // 剖析库接口type JsonHandler interface { ProcessJson(json []byte)}type jsonHandler struct {}func (j *jsonHandler) ProcessJson(json []byte) { log.Printf("json processing")}你能够批改函数库来反对 XML。 然而, 这可能须要大量代码。 或者,咱们能够将 XML 转换成 JSON? xml 与 json 代表了事实中不兼容的两个接口。为了演示不便,我用string类型示意xml文件,用[]byte示意json文件适配器就是为两个兼容两个接口而生,在此例中:xmlAdapter继承了XmlHandler接口,其中封装了对接口的解决。后果就是,客户端的 JSON 解决被“适配”成 XML 解决。 type xmlAdapter struct { JsonHandler}func (x *xmlAdapter) ProcessXml(xml string) { // 模仿对象转换:xml -> json json := []byte(xml) // 解决 json 文件 x.ProcessJson(json)}func TestAdapter(t *testing.T) { inputJson := []byte("json_file") // 原客户端 handler := &jsonHandler{} handler.ProcessJson(inputJson) // 客户端接入适配器,利用 原有的 JsonHandler 解决 xml 文件。 inputXml := "xml_file" adapter := &xmlAdapter{&jsonHandler{}} adapter.ProcessXml(inputXml)}长处灵活性强,上线新性能不须要改变大量源代码,仅须要在接口层做一层类型转换。 ...

October 22, 2022 · 2 min · jiezi

关于go:Go-数据结构与算法持续更新中

参考数据结构实质上,数据结构底层存储形式就两种: 数组 和 链表基本操作就四种: 增删改查遍历形式就两种: 迭代 和 递归算法计算机算法(非数学算法)的实质就是穷举如何穷举?即无脱漏的列举所有可能解,此类问题个别是递归类问题,最典型的是DP系列问题如何聪慧的穷举?即防止所有冗余计算,耗费尽可能少的资源求出答案,一些耳熟能详的非递归算法技巧,都能够归在这一类如何刷算法题: 先刷数组、链表相干的根本算法把握根本算法之后,刷二叉树相干的罕用算法最初刷动静布局、回溯、DPS等算法数组双指针技巧快、慢指针 定义 对数组而言,两个指针对应两个下标,两个下标往同一个方向前进,一个快,一个慢经典例题求一个有序数组的不反复元素最多有多少个?如 [1,2,3,3,5,6,8,9,9] 应输入 [1,2,3,5,6,8,9] 7个解析:快指针一直往后移,如果碰到的元素与慢指针指向的元素不同,则慢指针右移一步,且将快指针指向的值 赋给 以后 慢指针指向 的 数组元素package doublePointerfunc SlowFast(sorted_nums []int) []int{ sorted_nums_len := len(sorted_nums) if sorted_nums_len <= 1 { return sorted_nums } slow, fast := 0, 0 for ;fast < sorted_nums_len; fast++{ if sorted_nums[slow] != sorted_nums[fast]{ slow++ sorted_nums[slow] = sorted_nums[fast] } } return sorted_nums[:slow+1]}左右指针 定义 对数组而言,两个指针对应两个下标,两个下标由中间往两头聚拢 或者 由两头向中间挪动前缀和数组定义 前缀和数组由老数组结构而得,前缀和数组中每个下标i的值 为 老数组前i个小标所指向的元素之和那么在计算老数组[i:j]时,只需计算 前缀和数组[j+1] - 前缀和数组[i] 即可一维数组的前缀和数组 package preSumListfunc NewList(oldList []int) []int { newList_len := len(oldList) + 1 newList := make([]int, newList_len) // 老数组前0项之和为0 newList[0] = 0 for i, v := range oldList{ newList[i+1] = newList[i] + v } return newList}

October 21, 2022 · 1 min · jiezi

关于go:golang中的map

0.1、索引https://waterflow.link/articles/1666339004798 1、map的构造map提供了键值对的无序汇合,所有的键都是不反复的。在go中map是基于bmap数据结构的。在外部hash表是一个桶数组,每个桶是一个指向键值对数组的指针。每个桶外面能够保留8个元素。咱们能够简化成上面的构造。 如果咱们持续插入一个元素,hash键返回雷同的索引,则另一个元素也会插入雷同的桶中。 如果失常桶中的元素已满,还有元素持续往雷同的索引插入的话,go会创立另一个蕴含8个元素的桶并将前一个桶指向他。 所以当咱们读取、更新和删除map元素时,Go 必须计算相应的数组索引。 而后 Go 顺次遍历所有键,直到找到提供的键。 因而,这三个操作的最坏状况工夫复杂度为 O(p),其中 p 是桶中元素的总数(默认为一个桶,溢出时为多个桶)。 2、map初始化首先咱们先初始化一个蕴含3个元素的map: m := map[string]int{ "haha": 3, "hehe": 5, "hoho": 7, }咱们可能只须要遍历2个桶就能够找到下面的所有元素。 然而当咱们增加100万个元素的时候,咱们可能须要遍历上千个桶去找到指定的元素。 为了应答元素的增长,map会抉择扩容,个别是以后桶数量增加一倍。那什么时候会扩容呢? 负载因子大于6.5溢出桶太多当map扩容的时候,所有的键都会重新分配到新的桶。所以最坏状况下,插入元素有可能是O(n)。 咱们晓得,在应用切片时,如果咱们事后晓得要增加到切片的元素数量,咱们能够用给定的大小或容量对其进行初始化。这防止了一直应答切片增长导致底层数组频繁复制的问题。map与此相似。实际上,咱们能够应用 make 内置函数在创立地图时提供初始大小。例如,如果咱们要初始化一个蕴含 100 万个元素的map,能够这样写: m := make(map[string]int, 1000000)通过指定大小,go应用适当数量的桶创立map以存储 100 万个元素。 这节俭了大量计算工夫,因为map不必动态创建桶并解决桶溢出后rehash的问题。 指定大小 n 并不是说创立最多有100万个元素的map。 咱们能够持续往map增加元素。 这理论代表着 Go 运行时至多 须要为n 个元素分配内存。 咱们能够运行下基准测试看下这两个的性能差别: package mainimport ( "testing")var n = 1000000func BenchmarkWithSize(b *testing.B) { for i := 0; i < b.N; i++ { m := make(map[string]int, n) for j := 0; j < n; j++ { m["hhs" + string(rune(j))] = j } }}func BenchmarkWithoutSize(b *testing.B) { for i := 0; i < b.N; i++ { m := make(map[string]int) for j := 0; j < n; j++ { m["hhs"+string(rune(j))] = j } }}go test -bench=.goos: darwingoarch: amd64pkg: go-demo/5cpu: Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHzBenchmarkWithSize-8 6 178365104 ns/opBenchmarkWithoutSize-8 3 362949513 ns/opPASSok go-demo/5 4.563s咱们能够看到初始化map大小的性能是高于未设置初始化大小的性能。其中的起因下面应该解释的很分明了。 ...

October 21, 2022 · 1 min · jiezi

关于go:Golang-GC

垃圾回收优化在前文中提到,golang 的垃圾回收算法属于 标记-革除,是须要 STW 的。STW 就是 Stop The World 的意思,在 golang 中就是要停掉所有的 goroutine,分心进行垃圾回收,待垃圾回收完结后再复原 goroutine。而 STW 工夫的长短间接影响了利用的执行,如果工夫过长,那将是灾难性的。为了缩短 STW 工夫,golang 不对优化垃圾回收算法,其中写屏障(Write Barrier)和辅助GC(Mutator Assist)就是两种优化垃圾回收的办法。 写屏障(Write Barrier):下面说到的 STW 的目标是避免 GC 扫描时内存变动引起的凌乱,而写屏障就是让 goroutine 与 GC 同时运行的伎俩,尽管不能齐全打消 STW,然而能够大大减少 STW 的工夫。写屏障在 GC 的特定工夫开启,开启后指针传递时会把指针标记,即本轮不回收,下次 GC 时再确定。辅助 GC(Mutator Assist):为了避免内存调配过快,在 GC 执行过程中,GC 过程中 mutator 线程会并发运行,而 mutator assist 机制会帮助 GC 做一部分的工作。 一文弄懂 Golang GC、三色标记、混合写屏障机制【图文解析GC】浅析 Golang 垃圾回收机制

October 21, 2022 · 1 min · jiezi

关于go:记一次-go-mod-vendor-报错排查经过

一. 问题症状咱们应用 jenkins 脚本执行 go build ,用来构建线上服务器应用的二进制文件。构建过程中有这样一个步骤: go mod vendor 该步骤将以 go.mod 文件中写明的包和版本为准下载第三方依赖并保留到本地的 vendor 目录。下载过程中将校验 go.sum 中的 hash 值是否同文件 hash 统一。 在理论执行中,遇到这样的谬误: internal error: failed to find embedded files of github.com/marten-seemann/qtls-go1-18: //go:build comment without // +build comment 二. 排查通过通过 qtls-go1-18 的仓库名能够察看到问题可能跟 go 1.18 的版本无关。关上依赖的 github 仓库可见简介:Go standard library TLS 1.3 implementation, modified for QUIC. For Go 1.18.而咱们构建的环境 go env 输入的版本为 1.16PS: go env 在 go 1.18 的 release notes 中查找相干信息://go:build linesGo 1.17 introduced //go:build lines as a more readable way to write build constraints, instead of // +build lines. As of Go 1.17, gofmt adds //go:build lines to match existing +build lines and keeps them in sync, while go vet diagnoses when they are out of sync. ...

October 21, 2022 · 2 min · jiezi

关于go:52-Golang实战dlv调试

 Go程序出异样怎么办?pprof工具剖析啊,可是如果是代码方面bug等呢?剖析代码bug有时须要联合执行过程,加日志呗,可是某些异样问题服务重启之后,可能会很难复现。这时候咱们能够断点调试,这样就能剖析每一行代码的执行,每一个变量的后果,C语言通常应用GDB调试,Go语言有专门的调试工具dlv,本篇文章次要介绍dlv的根本应用。 dlv 概述 dlv全称delve,装置也比较简单,go install就能装置: //下载&装置$ git clone https://github.com/go-delve/delve$ cd delve$ go install github.com/go-delve/delve/cmd/dlv//go 1.16版本以上# Install at a specific version or pseudo-version:$ go install github.com/go-delve/delve/cmd/[email protected]#On macOS make sure you also install the command line developer tools:xcode-select --install dlv反对多种形式跟踪你的Go程序,help命令查看: dlv help//参数传递Pass flags to the program you are debugging using `--`, for example:`dlv exec ./hello -- server --config conf/config.toml`Usage: dlv [command]Available Commands: //罕用来调试异样过程 attach Attach to running process and begin debugging. //启动并调试二进制程序 exec Execute a precompiled binary, and begin a debug session. debug Compile and begin debugging main package in current directory, or the package specified. ...... dlv与GDB还是比拟相似的,可打印变量的值,可设置断点,可单步执行,可查看调用栈,另外还能够查看以后Go过程的所有协程、线程等;罕用的性能(命令)如下: ...

October 21, 2022 · 4 min · jiezi

关于go:Go-区块链实现

区块链七层架构模型 创立工程文件mkdir BlockChain && cd BlockChain && go mod init BlockChainmkdir block blockChaintouch main.go block/block.go blockChain/blockChain.go数据层hashfunc CalcHash(tobeHash string) string { // "crypto/sha256" // func Sum256(data []byte) [Size]byte // size = 32, 32 byte = 256 bit tobeHash_256 := sha256.Sum256([]byte(tobeHash)) // "encoding/hex" // func EncodeToString(src []byte) string return hex.EncodeToString(tobeHash_256[:])}block类定义 // blocktype Block struct{ Index int64 // 区块编号 Timestamp int64 // 区块工夫戳 PrevBlockHash string // 上一个区块的哈希值 Data string // 以后区块数据 Hash string // 以后区块哈希值}办法 ...

October 21, 2022 · 2 min · jiezi

关于go:golang中的切片

索引:https://waterflow.link/articles/1666277946416 在go中切片的底层是数组,所以切片的数据间断存储在数组的数据结构中。如果底层的数组满了,切片还须要增加元素的话,底层数组就须要扩容。如果底层数组简直为空时,就会缩容。 在切片外部其蕴含一个指向底部数组的指针、切片的长度、切片的容量。长度是指切片蕴含的元素树,容量底层数组中的元素数。 咱们先看个例子: s := make([]int, 2, 4)咱们初始化一个长度为2,容量为4的切片。长度是必选的,容量可选。当不指定容量时,容量和长度雷同,也就是底层数组的长度。 因为长度为2,所以go只初始化了2个元素。咱们能够打印下看下后果: package mainimport "fmt"func main() { s := make([]int, 2, 4) fmt.Printf("s:%v,len(s):%d,cap(s):%d\n", s, len(s), cap(s))}go run 4.gos:[0 0],len(s):2,cap(s):4上面咱们把s[1]设置为1,所以s的第二个元素会被更新为1,s的长度和容量不会变动 咱们看下执行后果: package mainimport "fmt"func main() { s := make([]int, 2, 4) s[1] = 1 fmt.Printf("s:%v,len(s):%d,cap(s):%d\n", s, len(s), cap(s))}go run 4.gos:[0 1],len(s):2,cap(s):4然而当咱们设置s[2] = 1时会报错,像上面这样: go run 4.gopanic: runtime error: index out of range [2] with length 2然而咱们的容量是4,也就是底层数组还有2个闲暇元素。那咱们该如何给剩下的元素赋值呢? 咱们能够应用go的内置函数append,他能够将元素追加到切片的开端。它能够追加一个元素到切片开端: s = append(s, 1)也能够追加多个元素到切片开端: s = append(s, 1, 2)s = append(s, []int{1, 2}...)还有一种比拟非凡的状况,将字符串追加到字节切片开端: ...

October 20, 2022 · 2 min · jiezi

关于go:golang-sql-转-struct-插件实现

写go curd的时候,常常须要sql 转 struct,比拟麻烦,写个主动转换的代码:main.go package mainimport ( "convert/convert" "flag" "fmt" "log" "os" "path")const VERSION = "1.0.0"const VersionText = "Convert of mysql schema to golang struct"var saveFilePath stringfunc init() { pa, err := os.Getwd() if err != nil { fmt.Println("获取运行文件门路获取失败") return } saveFilePath = path.Join(pa, "models") if isexists, _ := PathExists(saveFilePath); !isexists { os.Mkdir(saveFilePath, os.ModePerm) }}func main() { connStr :="root:[email protected](127.0.0.1:3306)/test?charset=utf8mb4&parseTime=true" dsn := flag.String("dsn", connStr, "连贯数据库字符串") file := flag.String("file", saveFilePath, "保留门路") table := flag.String("table", "", "要生成的表名") //不填默认所有表 realNameMethod := flag.String("realNameMethod", "TableName", "构造体对应的表名") packageName := flag.String("packageName", "", "生成struct的包名(默认为空的话, 则取名为: package models)") tagKey := flag.String("tagKey", "gorm", "字段tag的key") prefix := flag.String("prefix", "", "表前缀") version := flag.Bool("version", false, "版本号") v := flag.Bool("v", false, "版本号") enableJsonTag := flag.Bool("enableJsonTag", true, "是否增加json的tag,默认false") enableFormTag := flag.Bool("enableFormTag", false, "是否增加form的tag,默认false") // 版本号 if *version || *v { fmt.Println(fmt.Sprintf("\n version: %s\n %s\n using -h param for more help \n", VERSION, VersionText)) return } // 初始化 t2t := convert.NewTable2Struct() // 个性化配置 t2t.Config(&convert.T2tConfig{ // json tag是否转为驼峰(大驼峰式),默认为false,不转换 JsonTagToHump: true, //false=>json tag: request_method true=> json tag RequestMethod JsonTagToFirstLow: true, // json tag首字母是否转换小写, 默认true => requestMethod // 构造体名称是否转为驼峰式,默认为false StructNameToHump: true, // 如果字段首字母原本就是大写, 就不增加tag, 默认false增加, true不增加 RmTagIfUcFirsted: false, // tag的字段名字首字母是否转换为小写, 如果自身有大写字母的话, 默认false不转 TagToLower: false, // 字段首字母大写的同时, 是否要把其余字母转换为小写,默认false不转换 UcFirstOnly: false, // 每个struct放入独自的文件,默认false,放入同一个文件 SeperatFile: false, }) // 开始迁徙转换 err := t2t. // 指定某个表,如果不指定,则默认全副表都迁徙 Table(*table). // 表前缀 Prefix(*prefix). // 是否增加json tag EnableJsonTag(*enableJsonTag). EnableFormTag(*enableFormTag). // 生成struct的包名(默认为空的话, 则取名为: package model) PackageName(*packageName). // tag字段的key值,默认是gorm TagKey(*tagKey). // 是否增加构造体办法获取表名 RealNameMethod(*realNameMethod). // 生成的构造体保留门路 SavePath(*file). // 数据库dsn Dsn(*dsn). // 执行 Run() if err != nil { log.Println(err.Error()) }}// PathExists 判断所给门路文件/文件夹是否存在func PathExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } //isnotexist来判断,是不是不存在的谬误 if os.IsNotExist(err) { //如果返回的谬误类型应用os.isNotExist()判断为true,阐明文件或者文件夹不存在 return false, nil } return false, err //如果有谬误了,然而不是不存在的谬误,所以把这个谬误一成不变的返回}tableTostruct.go 代码: ...

October 20, 2022 · 7 min · jiezi

关于go:Go-UDPCS-模型

创立工程文件mkdir udpServer udpClientcd udpServer && go mod init udpServer && touch server.gocd ../udpClient && go mod init udpClient && touch client.go服务端// server.gopackage mainimport( "net" "fmt")func main(){ addr, err := net.ResolveUDPAddr("udp", ":8080") if err!=nil{ panic(err) } fmt.Println("udp address resolve success: ", addr) udpSocket, err := net.ListenUDP("udp", addr) if err!=nil{ panic(err) } fmt.Println("udp socket create success...") defer udpSocket.Close() buf := make([]byte, 2048) n, clientUDPAddr, err := udpSocket.ReadFromUDP(buf) if err!=nil{ panic(err) } fmt.Println("the client addr is: ", clientUDPAddr) fmt.Printf("receive from %s is: %s\n", clientUDPAddr, buf[:n]) _, err = udpSocket.WriteToUDP([]byte("Data processing completed!"), clientUDPAddr) if err != nil{ panic(err) }}$ go run server.goudp address resolve success: :8080udp socket create success...客户端// client.gopackage mainimport( "net" "fmt")func main(){ conn, err := net.Dial("udp", ":8080") if err != nil{ panic(err) } defer conn.Close() fmt.Println("connect to udp server 8080 ...") conn.Write([]byte("hello, my friend...")) buf := make([]byte, 2048) n, err := conn.Read(buf) if err != nil{ panic(err) } fmt.Printf("receive from server: %s\n", buf[:n])}$ go run client.goconnect to udp server 8080...测试$ go run client.goconnect to udp server 8080 ...receive from server: Data processing completed!$ go run server.goudp address resolve success: :8080udp socket create success...the client addr is: 127.0.0.1:48313receive from 127.0.0.1:48313 is: hello, my friend...

October 20, 2022 · 1 min · jiezi

关于go:Go-单元基准测试

单元测试简介单元测试的代码文件必须以_test.go结尾单元测试的函数必须以Test结尾,必须是可导出的单元测试用的参数有且只有一个,在这里是 t *testing.T单元测试函数不能有返回值创立工程目录&文件mkdir unittest && cd unittest && go mod init unittesttouch calc.go calc_test.go// calc.gopackage mainfunc Add(a, b int) int { return a + b}func Sub(a, b int) int { return a - b}func Mul(a, b int) int { return a * b}// calc_test.gopackage mainimport( "testing")func TestAdd(t *testing.T){ if ret := Add(8, 8); ret != 16 { t.Errorf("8 + 8 equal to 16, but %d got", ret) } if ret := Add(2, 6); ret != 8 { t.Errorf("2 + 6 equal to 16, but %d got", ret) }}func TestSub(t *testing.T){ if ret := Sub(8, 8); ret != 0 { t.Errorf("8 - 8 equal to 0, but %d got", ret) } if ret := Sub(2, 6); ret != -4 { t.Errorf("2 - 6 equal to -4, but %d got", ret) }}func TestMul(t *testing.T){ if ret := Mul(8, 8); ret != 64 { t.Errorf("8 * 8 equal to 64, but %d got", ret) } if ret := Mul(2, 6); ret != 12 { t.Errorf("2 * 6 equal to 12, but %d got", ret) }}运行$ go test PASSok unittest 0.003s基准测试简介基准测试的代码文件必须以_test.go结尾基准测试的函数必须以Benchmark结尾,必须是可导出的基准测试函数必须承受一个指向Benchmark类型的指针作为惟一参数基准测试函数不能有返回值创立工程目录&文件mkdir benchMarktest && cd benchMarktest && go mod init benchMarktesttouch spf_test.go// spf_test.gopackage mainimport ( "fmt" "testing")func BenchmarkSprintf(b *testing.B) { num := 1000 b.ResetTimer() for i := 0; i < b.N; i++ { fmt.Sprintf("%d", num) }}运行$ go test -bench=. goos: linuxgoarch: amd64pkg: benchMarktestBenchmarkSprintf-4 7432339 163 ns/opPASSok benchMarktest 1.382s

October 20, 2022 · 2 min · jiezi

关于go:Go-TCPCS-模型

创立工程文件mkdir tcpServer tcpClientcd tcpServer && go mod init tcpServer && touch server.gocd ../tcpClient && go mod init tcpClient && touch client.go服务端// server.gopackage mainimport( "net" "fmt" "bufio")func main(){ // get listener listener, err := net.Listen("tcp", ":8080") if err != nil{ fmt.Println("listen failed: ") panic(err) } fmt.Println("tcp server running at 127.0.0.1:8080...") for { // get connection conn, err := listener.Accept() if err != nil{ fmt.Println("accept failed: ") panic(err) } process(conn) }}func process(conn net.Conn){ defer conn.Close() for { reader := bufio.NewReader(conn) var buf [128]byte n, err := reader.Read(buf[:]) if err != nil{ fmt.Println("read from client failed:") panic(err) } recvStr := string(buf[:n]) fmt.Println("receive from client: ", recvStr) conn.Write([]byte(recvStr + " from server")) }}$ go run server.go tcp server running at 127.0.0.1:8080...客户端// client.gopackage mainimport( "os" "fmt" "net" "bufio" "strings")func main(){ // create conn to server :8080 conn, err := net.Dial("tcp", ":8080") if err != nil{ fmt.Println("connnect to server failed: ") panic(err) } defer conn.Close() inputReader := bufio.NewReader(os.Stdin) for { input, _ := inputReader.ReadString('\n') input_data := strings.Trim(input, "\r\n") if strings.ToUpper(input_data) == "Q"{ return } // send data to server _, err := conn.Write([]byte(input_data)) if err != nil{ fmt.Println("send data to server failed: ", err) return } // receive data from server buf := [512]byte{} n, err := conn.Read(buf[:]) if err != nil{ fmt.Println("receive data from server failed: ", err) return } fmt.Println(string(buf[:n])) }}$ go run client.goconnect to tcp server 0.0.0.0:8080...测试$ go run client.goconnect to tcp server 0.0.0.0:8080...pingping from serveryesyes from serverokok from server$ go run server.gotcp server running at 127.0.0.1:8080...receive from client: pingreceive from client: yesreceive from client: ok

October 20, 2022 · 2 min · jiezi

关于go:Go-分布式缓存系统

cascsacas

October 20, 2022 · 1 min · jiezi

关于go:全网URL采集工具msray支持关键词采集域名采集联系人采集

明天介绍的这款全网URL采集工具能够使用于全网域名/网址/IP信息检索、指定关键词批量数据采集、SEO、网络推广剖析、内容源收集,以及为各种大数据分析等提供数据撑持。 软件提供本地WEB治理后盾对软件进行相干操作,无需应用简单的命令,功能强大且简略易上手! 软件劣势:1:多搜索引擎反对反对全网采集,目前根本反对全网支流的搜索引擎,包含baidu,sogou,bing,Google,Yandex,Want,神马,DuckDuckGo, 后续还会推出更多的反对,敬请期待...... 2: 多种过滤计划灵便的过滤计划能够依据咱们的业务需要,本人定制合乎的过滤计划,防止取得反复冗余数据,使咱们的数据更加的准确,高效零碎内置了多种过滤计划: 同时反对依据域名,IP归属,网页题目,网页内容,拜访状态等..进行自定义过滤 3: 灵便的推送计划软件不仅反对将后果保留在本地,而且还反对近程的数据推送,能够和本人外部的业务零碎相结合,便于数据的再次利用剖析,外围性能1: 关键词采集依据提供的关键词采集全网的数据, 反复判断:能够抉择依据域名或者网址进行反复判断, 采集字段包含域名,网址,IP地址,IP所属国家,题目,形容,拜访状态等。 过滤计划反对:能够杜绝本人的业务须要设置本人的过滤计划 反对相干词扩大,主动增加网页举荐的相干词 关键词主动扩大反对 推送计划WebHook反对:依据本人业务须要进行近程数据推送,不便再次做统计分析。 后果预览: 2: URL采集依据提供的URL数据批量采集全网被收录的数据, 反复判断:能够抉择依据域名或者网址进行反复判断, 反对线程数自定义,可依据本人机器配置调整最优 采集字段包含域名,网址,IP地址,IP所属国家,题目,形容,拜访状态等。 过滤计划反对:能够杜绝本人的业务须要设置本人的过滤计划 防站群陷阱:可避免二级域名站群导致爬虫陷阱 推送计划WebHook反对:依据本人业务须要进行近程数据推送,不便再次做统计分析。 创立爬虫工作 后果预览 3: 分割工作可依据提供的域名地址采集被收录的联系方式等信息 蕴含手机。电话,qq,邮箱等 近程后果推送反对:可将后果推送到近程的服务器 创立分割工作 后果预览 URL采集工具能够帮忙咱们进行数据抓取、解决、剖析,开掘。 URL采集工具帮忙咱们灵便迅速地抓取网页上散乱散布的数据信息,并通过一系列的剖析解决,精确挖掘出所需数据。这样能够保障,数据的完整性,时效性,特征性,进步剖析的准确性。

October 20, 2022 · 1 min · jiezi

关于go:51-Golang实战Go程序分析利器pprof

 不晓得你有没有遇到这种状况,Go服务总是是不是的响应十分慢排查发现所有的依赖还都挺快,感觉是Go服务本身慢又不晓得怎么慢在哪里;或者说申请甚至齐全没响应,Go服务明明在失常运行,申请去哪儿了呢?或者说Go服务内存占用总是居高不下,内存都节约在哪呢?这些问题都能够通过pprof剖析,本篇文章将为你演示如何基于pprof分析程序性能(问题)。 pprof概述 为什么pprof能够帮忙咱们剖析Go程序性能呢?因为它能够采集程序运行时数据:比如说协程栈,这样服务阻塞在哪里是不是高深莫测了;比如说内存分配情况包含调用栈,这样哪里消耗内存是不是也分明了。 当然想要通过pprof分析程序性能,也是要引入一丢丢代码吧,毕竟采集程序运行时数据,是须要消耗一些资源的。引入pprof采样也非常简单,几行代码就能搞定: package mainimport ( "net/http" _ "net/http/pprof")func main() { go func() { http.ListenAndServe("0.0.0.0:6060", nil) }()} 就是这么简略就能够了。那怎么看pprof采集的数据呢?同样很简略,浏览器拜访某个接口就能够,接口返回页面如下: pprof采集的数据指标阐明如下: //浏览器输出:http://127.0.0.1:8888/debug/pprof///内存调配采样,只有引入net/http/pprof才会采集allocs: A sampling of all past memory allocations//采集协程阻塞事件,默认不开启;可通过runtime.SetBlockProfileRate开启block: Stack traces that led to blocking on synchronization primitives//程序启动命令cmdline: The command line invocation of the current program//协程调用栈桢goroutine: Stack traces of all current goroutines//与allocs采样数据一样,然而可用来采样存活对象内存调配(采样前可运行GC)heap: A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.//采集锁事件,默认不开启;可通过runtime.SetMutexProfileFraction开启mutex: Stack traces of holders of contended mutexes//CPU采样,能够通过参数seconds指定采样工夫;可用来分析程序热点profile: CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.//线程创立调用栈threadcreate: Stack traces that led to the creation of new OS threads//记录Go服务在做什么,采集包含协程调度,GC,零碎调用等事件trace: A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace. 举一个例子,goroutine指标输入所有线程栈桢,可用于分析程序阻塞状况。比方,大量协程阻塞在获取锁这一处,那是不是因为锁没被开释导致死锁了;比方,大量协程阻塞在管道写操作(生产生产业务),那是不是因为生产协程解决太慢了,导致管道数据满了,生产者无奈写入数据了,等等。goroutine指标输入内容如下: ...

October 20, 2022 · 4 min · jiezi

关于go:url采集工具关键词采集联系方式采集

msray 可依据使用者提供的关键词,主动从多个搜索引擎获取搜寻后果数据, 并且反对主动去反复、多种定制化性能如过滤、导出、推送等,便于数据分析与解决。并同时具备零碎自带引擎,可有限抓取互联网公开内容。 一:关键词匹配采集工作 借助一个或多个搜索引擎,对自定义的多个关键词进行抓取,并对后果进行解决与反对导出的工作; 应用搜寻工作,咱们能够一次性创立多个采集工作,可视化界面使咱们的操作变得简略,不须要咱们具备业余的编程常识就能够实现采集(如图)。 二:URL可视化采集工作 对自定义的种子url文件中的数据,进行HTTP申请与数据抓取,并对申请返回的后果中蕴含的其余网站的链接数据,进行解决与反对导出的工作(有限深度); 数据采集工具在大数据时代无疑是一个好的产品。通过智能的剖析,统计,帮忙咱们饿解决生存,工作中的数据处理问题。让咱们能够做到,更快,更精确,更高效。 对于网站url采集,关键词采集就分享到这里,更多请查看官网地址

October 20, 2022 · 1 min · jiezi

关于go:Go-Gossip-协议

Gossip 协定一种共识算法,具体的原理倡议给位小伙伴自行谷歌Gossip 协定包引入$ go get github.com/hashicorp/[email protected]测试// gossip.gopackage mainimport( "fmt" "github.com/hashicorp/memberlist")func main(){ // 应用 Create 办法创立一个成员实例, 这个成员实例就代表着一群的机器 list, err := memberlist.Create(memberlist.DefaultLocalConfig()) if err != nil{ panic(err) } // 应用 Join 办法退出一个曾经存在的机器群 // 这里的参数是一个 string 切片,只有填写这个集群中的一台机器即可, // gossip 协定会将这个“谰言”散播给机器群的所有机器 _, err = list.Join([]string{"0.0.0.0"}) if err != nil{ panic(err) } for _, node := range list.Members(){ fmt.Println(node.Name, node.Addr) }}$ go run gossip.go2022/10/20 00:28:52 [DEBUG] memberlist: Initiating push/pull sync with: 127.0.0.1:79462022/10/20 00:28:52 [DEBUG] memberlist: Stream connection from=127.0.0.1:32970634abb028f5466864d91f4a1 192.168.42.6

October 20, 2022 · 1 min · jiezi

关于go:Go-操作redis

启动redis服务$ sudo service redis-server startStarting redis-server: redis-server.$ redis-cli127.0.0.1:6379> pingPONG下载redis驱动包$ go get -u github.com/gomodule/redigo/redis操作测试$ cd redis && go mod init redis$ vi redis-demo.go// redis-demo.gopackage mainimport( "fmt" "github.com/gomodule/redigo/redis")func main(){ // access redis's conn conn, err := redis.DialURL("redis://127.0.0.1:6379") if err != nil{ panic(err) } defer conn.Close() // set res, err := redis.String(conn.Do("set", "k", "v")) fmt.Println(res, err) // get res, err = redis.String(conn.Do("get", "k")) fmt.Println(res, err) // del n, err := redis.Int64(conn.Do("del", "k")) fmt.Println(n, err) res, err = redis.String(conn.Do("get", "k")) fmt.Println(res, err)}$ go run redis-demo.go OK <nil>v <nil>1 <nil> redigo: nil returned

October 19, 2022 · 1 min · jiezi

关于go:Go常见错误系列第12篇冗余的嵌套代码

前言这是Go常见谬误系列的第12篇:Go语言中冗余的嵌套代码,俗称箭头型代码。 素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 常见谬误咱们先看看如下的代码: func join(s1, s2 string, max int) (string, error) { if s1 == "" { return "", errors.New("s1 is empty") } else { if s2 == "" { return "", errors.New("s2 is empty") } else { concat, err := concatenate(s1, s2) if err != nil { return "", err } else { if len(concat) > max { return concat[:max], nil } else { return concat, nil } } } }} func concatenate(s1 string, s2 string) (string, error) { // ...}这段代码要做的事件很简略: ...

October 19, 2022 · 2 min · jiezi

关于go:Go-分布式微服务

Go - 散布式微服务环境搭建wget https://go.dev/dl/go1.17.2.linux-amd64.tar.gzsudo tar -C /usr/local -xzf go1.17.2.linux-amd64.tar.gzgo versionexport GOBIN=$GOPATH/binexport PATH=$GOPATH:$PATHexport GOROOT=/usr/local/goexport GOPROXY=https://goproxy.cn/,direct# goctlgo install github.com/zeromicro/go-zero/tools/[email protected]服务拆分一个商城我的项目能够拆分为用户服务(user)、订单服务(order)、产品服务(product)、领取服务(pay)...每个服务都能够再分为 api 服务和 rpc 服务api 服务对外,可提供给 app 调用rpc 服务是对内的,可提供给外部 api 服务或者其余 rpc 服务调用 创立我的项目mkdir mall && cd mall && go mod init mallmkdir service && cd service用户服务api服务端口: 8000rpc服务端口: 9000login用户登录接口login用户登录接口register用户注册接口register用户注册接口userinfo用户信息接口userinfo用户信息接口... mkdir -p user/api user/rpc user/model产品服务api服务端口: 8001rpc服务端口: 9001create产品创立接口create产品创立接口update产品批改接口update产品批改接口remove产品删除接口remove产品删除接口detail产品详情接口detail产品详情接口... mkdir -p product/api product/rpc product/model订单服务api服务端口: 8002rpc服务端口: 9002create订单创立接口create订单创立接口update订单批改接口update订单批改接口remove订单删除接口remove订单删除接口detail订单详情接口detail订单详情接口... mkdir -p order/api order/rpc order/model领取服务api服务端口: 8003rpc服务端口: 9003create领取创立接口create领取创立接口detail领取详情接口update领取详情接口callback领取回调接口remove领取回调接口... mkdir -p pay/api pay/rpc pay/model$ tree mall mall├── common├── go.mod└── service ├── order │ ├── api │ ├── model │ └── rpc ├── pay │ ├── api │ ├── model │ └── rpc ├── product │ ├── api │ ├── model │ └── rpc └── user ├── api ├── model └── rpc用户服务产品服务订单服务领取服务Auth 验证服务监控链路追踪分布式事务参考https://github.com/zeromicro/go-zero/blob/master/readme-cn.mdhttps://learnku.com/articles/64566

October 19, 2022 · 1 min · jiezi

关于go:golang中的接口

0.1、索引https://waterflow.link/articles/1666171320273 1、概念接口提供了一种指定对象行为的办法。 咱们应用接口来创立多个对象能够实现的通用形象。 Go 接口不同的起因在于它们是隐式的。 没有像 implements 这样的显式关键字来标记对象 A实现了接口B。 为了了解接口的弱小,咱们能够看下规范库中两个罕用的接口:io.Reader 和 io.Writer。 io 包为 I/O 原语提供形象。 在这些形象中,io.Reader 从数据源读取数据,io.Writer 将数据写入指标。 io.Reader 蕴含一个 Read 办法: type Reader interface { Read(p []byte) (n int, err error)}io.Reader 接口的自定义实现应该接管一个字节切片p,把数据读取到p中并返回读取的字节数或谬误。 io.Writer 定义了一个办法,Write: type Writer interface { Write(p []byte) (n int, err error)}io.Writer 的自定义实现应该将来自切片的数据p写入底层数据流并返回写入的字节数或谬误。 因而,这两个接口都提供了根本的形象: io.Reader 从一个源对象读取数据io.Writer 将数据写到一个指标对象假如咱们须要实现一个将一个文件的内容复制到另一个文件的函数。 咱们能够创立一个特定的函数copyFile,它将应用 io.Reader 和 io.Writer 形象创立一个更通用的函数: package mainimport ( "io" "log" "os")func main() { // 1 关上一个源文件 source, err := os.Open("a.txt") if err != nil { log.Fatal(err) } defer source.Close() // 2 创立一个指标文件 dest, err := os.Create("b.txt") if err != nil { log.Fatal(err) } defer dest.Close() // 从把源数据复制到指标 err = CopyFile(source, dest) if err != nil { log.Fatal(err) }}// 复制func CopyFile(source io.Reader, dest io.Writer) error { var buffer = make([]byte, 1024) for { n, err := source.Read(buffer) if err == nil { _, err = dest.Write(buffer[:n]) if err != nil { return err } } if err == io.EOF { _, err = dest.Write(buffer) if err != nil { return err } return nil } return err }}咱们利用os.Open关上一个文件,该函数返回一个*os.File句柄,*os.File 实现了 io.Reader 和 io.Writer咱们应用os.Create创立一个新的文件,该函数返回一个*os.File句柄咱们应用copyFile函数,该函数有两个参数,source为一个实现io.Reader接口的参数,dest为一个实现 io.Writer接口的参数该函数实用于 os.File 参数(因为 os.File 实现了 io.Reader 和 io.Writer)以及任何其余能够实现这些接口的类型。 例如,咱们能够创立本人的 io.Writer 写入数据库,并且代码将放弃不变。 它减少了函数的通用性; 因而,他是可重用的,这很重要。 ...

October 19, 2022 · 3 min · jiezi

关于go:go-泛型的使用

泛型参考文章: https://blog.csdn.net/qq_4206... 实现批量切片性能, 相似 php array_chunk()函数func ArrayChunk[T any](s []T, size int) [][]T { if size < 1 { panic("size: cannot be less than 1") } length := len(s) chunks := int(math.Ceil(float64(length) / float64(size))) var n [][]T for i, end := 0, 0; chunks > 0; chunks-- { end = (i + 1) * size if end > length { end = length } n = append(n, s[i*size:end]) i++ } return n}

October 19, 2022 · 1 min · jiezi

关于go:Cobra-Command-详解

参考:https://www.kancloud.cn/zatko...

October 19, 2022 · 1 min · jiezi

关于go:Golang-遍历日期参数开始日期和结束日期

输出: 开始日期 完结日期 格局:"2006-01-02"工夫距离 day/month输入:一个string array 蕴含着所有遍历过的工夫day 的话是开始和完结中的所有日期("2022-07-14")month 则是所有月份("2022-07") func traverseTime(startDate string, endDate string, interval string) []string { // layout 日期工夫格局 layout := "2006-01-02" // 首先将startDate和endDate转换为time类型 endTime, _ := time.ParseInLocation(layout, endDate, time.Local) startTime, _ := time.ParseInLocation(layout, startDate, time.Local) if interval == "day" { // dateArray 开始以及完结日期中的所有日期 dateArray := make([]string, 0) // After 判断工夫先后 for endTime.After(startTime) { dateArray = append(dateArray, startTime.Format("2006-01-02")) startTime = startTime.AddDate(0, 0, 1) } dateArray = append(dateArray, endDate) return dateArray } if interval == "month" { //monthArray 开始以及完结日期中的所有日期 monthArray := make([]string, 0) for endTime.After(startTime) { monthArray = append(monthArray, startTime.Format("2006-01")) startTime = startTime.AddDate(0, 1, 0) } monthArray = append(monthArray, endTime.Format("2006-01")) return monthArray } fmt.Println("Wrong interval") return nil }https://blog.csdn.net/weixin_... ...

October 19, 2022 · 1 min · jiezi

关于go:Go-RPC

Go - RPC参考https://geektutu.com/post/quick-go-rpc.htmlRPC简介RPC(Remote Procedure Call)近程过程调用,计算机通信协议的一种RPC下 调用程序 会搁置client节点的机器,被调办法子程序 会搁置Server节点的机器, client通过向Server发送申请、Server回调响应来获取 调用程序 的 后果调用方和执行方别离在不同的机器上本地调用的例子$ mkdir ./goRpc && cd goRpc && go mod init goRpc$ vi main.gopackage mainimport( "fmt")type Result struct { Num int Ret int}type Cal intfunc (cal *Cal) Square(num int) *Result{ return &Result{ Num: num, Ret: num*num, }}func main(){ c := new(Cal) ret := c.Square(8) fmt.Println(ret) fmt.Println(ret.Num) fmt.Println(ret.Ret)}$ go run main.go &{8 64}864$ RPC被调用子办法须要满足的条件func (t *T) MethodName(argType T1, replyType *T2) error办法类型(T)是导出的(首字母大写)办法名(MethodName)是导出的办法有2个参数(argType T1, replyType *T2),均为导出/内置类型办法的第2个参数一个指针(replyType *T2)办法的返回值类型是 error// 革新Square函数...func (cal *Cal) Square(num int, ret *Result) error{ ret.Num = num ret.Ret = num * num return nil}...RPC 服务与调用创立服务端$ mkdir goRPC_server && cd goRPC_server && go mod init goRPC_server$ vi server.gopackage mainimport( "fmt" "net/rpc" "net/http")type Result struct { Num int Ret int}type Cal intfunc (cal *Cal) Square(num int, ret *Result) error{ ret.Num = num ret.Ret = num * num return nil}func main(){ rpc.Register(new(Cal)) rpc.HandleHTTP() fmt.Println("server running on port 8080...") if err := http.ListenAndServe(":8080", nil);err != err{ fmt.Println("error: ", err) }}$ go run server.goserver running on port 8080...创立客户端(同步调用)$ mkdir goRPC_client && cd goRPC_client && go mod init goRPC_client$ vi client.gopackage mainimport( "fmt" "net/rpc")type Result struct { Num int Ret int}func main(){ var ret Result client, err := rpc.DialHTTP("tcp", ":8080") if err != nil{ fmt.Println("create rpc client error: ", err) return } if err := client.Call("Cal.Square", 8, &ret);err != nil{ fmt.Println("failed to call Cal.Square: ", err) return } fmt.Printf("success to call Cal.Square of 8: %d\n", ret.Ret)}$ go run client.go success to call Cal.Square of 8: 64创立客户端(异步调用)package mainimport( "fmt" "net/rpc")type Result struct { Num int Ret int}func main(){ var ret Result client, err := rpc.DialHTTP("tcp", ":8080") if err != nil{ fmt.Println("create rpc client error: ", err) return } asyCall := client.Go("Cal.Square", 8, &ret, nil) <-asyCall.Done // 阻塞以后程序直到 RPC 调用完结 fmt.Printf("success to call Cal.Square of 8: %d\n", ret.Ret)}

October 19, 2022 · 2 min · jiezi

关于go:Go-高并发抽奖实现

Go - 高并发抽奖实现抢红包红包构造定义 id 红包惟一标识[]int 红包金额应用sync.Map示意映射关系: id -> []int业务剖析 发红包: 事后设定红包id 金额 数量抢红包: 把红包id发送到工作channel内,goroutine监听到工作channel内有值,而后随机返回金额侥幸大转盘

October 19, 2022 · 1 min · jiezi

关于go:44-Golang常用标准库单元测试

 日常我的项目开发中,单元测试是必不可少的,Go语言自身就提供了单元测试规范库,很不便咱们发展根底测试,性能测试,事例测试,含糊测试以及剖析代码覆盖率,本篇文章次要介绍Go单元测试的基本操作。 单元测试概述 "Go test" 命令可用于运行指定门路下的Go文件,这些Go文件必须以 "x_test.go" 格局命名,并且测试函数也必须以肯定格局命名。"Go test" 命令编译相干Go文件,并运行对应的测试函数,最终输入测试后果,包含测试后果,包名,运行工夫等,而如果执行失败,还会输入具体错误信息。 "Go test" 命令格局能够通过help命令查看: go help testusage: go test [build/test flags] [packages] [build/test flags & test binary flags] [-args|-json|-c|......]-args Pass the remainder of the command line (everything after -args)-json Convert test output to JSON suitable for automated processing-c Compile the test binary to pkg.test but do not run it//等等 咱们举一个简略的根底测试例子,测试取绝对值函数是否正确,如下所示,根底测试函数定义必须为 func TestXxx(* testing.T),包testing定义了与单元测试无关的函数或者构造。 func TestAbs(t *testing.T) { got := Abs(-1) if got != 1 { t.Errorf("Abs(-1) = %d; want 1", got) }} Go语言都反对哪几种类型的单元测试呢?次要分为根底测试,性能测试,事例测试以及含糊测试,这四种类型测试都有不同的命名格局,同样能够通过help命令查看: ...

October 19, 2022 · 4 min · jiezi

关于go:全网采集工具msray百度搜索引擎进行关键词全网采集

Msray-plus,是一款采纳GO语言开发的企业级综合性爬虫/采集软件。援用反对:搜索引擎后果采集、域名采集、URL采集、网址采集、全网域名采集、CMS采集、分割信息采集援用反对亿级数据存储、导入、反复判断等。无需应用简单的命令,提供本地WEB治理后盾对软件进行相干操作,功能强大且简略易上手!援用1:可从国内外多个搜索引擎批量采集用户导入的关键词对应的搜寻后果(SERP数据),并进行结构化数据存储与自定义过滤解决;援用2:可从用户提供的url种子地址,源源不断的主动爬取全网网站数据,并进行结构化数据存储与自定义过滤解决;援用3:可从用户提供的网站列表数据中,全自动的提取出网站联系方式信息,包含但不限于邮箱、手机/电话、QQ、微信、facebook、twitter等。援用同时反对存储域名、根网址、网址(url)、IP、IP所属国家、题目、形容、拜访状态等多种数据,次要使用于全网域名/网址/采集、行业市场钻研剖析、指定类型网站采集与剖析、网络推广剖析以及为各种大数据分析等提供数据撑持。 MSRAY-PLUS可从国内外多个搜索引擎批量采集用户导入的关键词对应的搜寻后果(SERP数据),并进行结构化数据存储与自定义过滤解决。反对存储与导出的数据包含: 所属引擎:如 baidu关键词:如 招牌域名:如 www.msray.net根网址: 如 http://www.msray.net网址(url): 如 http://www.msray.net/page/1.htmlIP: 如 113.123.12.123IP所属国家: 如 美国题目:如 这是一个网站的题目形容:如 这是一个网站的形容内容拜访状态码:如 200援用目前反对百度手机端、百度电脑端、必应、谷歌、神马、搜狗、Yandex、QWANT、DuckDuckGo等等支流搜索引擎,并且一直增加中...援用反对导出主动拓展出的关键词数据,并可显示拓展词的起源;援用反对持续性的依据种子关键词,全自动拓展出相干词并采集(有限采集);目前反对百度手机端、百度电脑端、必应、谷歌、神马、搜狗、Yandex、QWANT、DuckDuckGo等等支流搜索引擎,并且一直增加中...援用反对导出主动拓展出的关键词数据,并可显示拓展词的起源;援用反对持续性的依据种子关键词,全自动拓展出相干词并采集(有限采集);1:配置采集参数 2: 执行采集工作 3: 采集后果预览

October 19, 2022 · 1 min · jiezi

关于go:go微服务框架Kratos连载一入门教程安装以及第一个接口

一、介绍Kratos 一套轻量级 Go 微服务框架,蕴含大量微服务相干框架及工具。由bilibili开源进去,在b站大量应用。官网网址:https://go-kratos.dev/docs/ 本入门教程,将会教大家装置以及入门应用。 二、装置1、装置go能够到官网 https://go.dev/dl/下载对应的包,而后装置。 2、装置protobuf咱们应用brew装置 brew install protobuf装置好了后,咱们能够应用 执行一下 protoc --version ➜ protoc --versionlibprotoc 3.21.73、装置protobuf的go扩大工具 protoc-gen-gogo install google.golang.org/protobuf/cmd/protoc-gen-go4、装置kratosgo install github.com/go-kratos/kratos/cmd/kratos/[email protected]三、创立我的项目通过 kratos 命令创立我的项目模板: kratos new helloworld咱们进入helloworld查看一下文件构造 ➜ cd helloworld➜ tree构造如下: ├── Dockerfile├── LICENSE├── Makefile├── README.md├── api│   └── helloworld│   └── v1│   ├── error_reason.pb.go│   ├── error_reason.proto│   ├── greeter.pb.go│   ├── greeter.proto│   ├── greeter_grpc.pb.go│   └── greeter_http.pb.go├── cmd│   └── helloworld│   ├── main.go│   ├── wire.go│   └── wire_gen.go├── configs│   └── config.yaml├── go.mod├── go.sum├── internal│   ├── biz│   │   ├── README.md│   │   ├── biz.go│   │   └── greeter.go│   ├── conf│   │   ├── conf.pb.go│   │   └── conf.proto│   ├── data│   │   ├── README.md│   │   ├── data.go│   │   └── greeter.go│   ├── server│   │   ├── grpc.go│   │   ├── http.go│   │   └── server.go│   └── service│   ├── README.md│   ├── greeter.go│   └── service.go├── openapi.yaml└── third_party四、Kratos我的项目构造介绍4.1、Makefile 文件为make命令的一个文件,咱们关上有如下命令4.1.1 make initinit: go install google.golang.org/protobuf/cmd/[email protected] go install google.golang.org/grpc/cmd/[email protected] go install github.com/go-kratos/kratos/cmd/kratos/[email protected] go install github.com/go-kratos/kratos/cmd/protoc-gen-go-http/[email protected] go install github.com/google/gnostic/cmd/[email protected] go install github.com/google/wire/cmd/[email protected]会初始化装置这个框架的一个依赖。如果之后本人还有一些工具之类的须要装置,能够放到这里。 ...

October 19, 2022 · 1 min · jiezi

关于go:golang中的init初始化函数

0.1、索引https://waterflow.link/articles/1666090530880 1、概念1.1、源文件里的代码执行程序init 函数是用于初始化应用程序状态的函数。 它不承受任何参数并且不返回任何后果(一个 func() 函数)。 初始化包时,将初始化包中的所有常量和变量申明。 而后,执行初始化函数。 上面是一个初始化主包的例子: package mainimport "fmt"// 1var a = func() int { fmt.Println("var a") return 0}()// 2func init() { fmt.Println("init")}// 1var b = func() int { fmt.Println("var b") return 1}()// 3func main() { fmt.Println("main")}下面代码的初始化程序是: 初始化常量/变量(尽管b在init函数前面,然而会首先初始化)初始化init函数执行main函数咱们看下打印的后果: go run 2.govar avar binitmain1.2、不同包的init函数执行程序初始化包时会执行一个 init 函数。 在上面的例子中,咱们定义了两个包,main 和 redis,其中 main 依赖于 redis。 首先, 2 .go 是主包: package mainimport ( "fmt" "go-demo/100gomistakes/2/redis")// 2func init() { fmt.Println("main init")}// 3func main() { err := redis.Store("ni", "hao") fmt.Println(err)}咱们能够看到main包中调用了redis包的办法。 ...

October 18, 2022 · 2 min · jiezi

关于go:多搜索引擎关键词采集域名采集URL采集联系信息采集工具

多搜索引擎关键词采集域名采集URL采集分割信息采集工具Msray-plus,是一款采纳GO语言开发的企业级综合性爬虫/采集软件。1:可从国内外多个搜索引擎批量采集用户导入的关键词对应的搜寻后果(SERP数据),并进行结构化数据存储与自定义过滤解决;2:可从用户提供的url种子地址,源源不断的主动爬取全网网站数据,并进行结构化数据存储与自定义过滤解决;3:可从用户提供的网站列表数据中,全自动的提取出网站联系方式信息,包含但不限于邮箱、手机/电话、QQ、微信、facebook、twitter等。同时反对存储域名、根网址、网址(url)、IP、IP所属国家、题目、形容、拜访状态等多种数据,次要使用于全网域名/网址/采集、行业市场钻研剖析、指定类型网站采集与剖析、网络推广剖析以及为各种大数据分析等提供数据撑持。反对多种搜索引擎目前曾经集成了市面上支流的搜索引擎,而且还在继续的集成增加中...... Msray-plus次要性能1:关键词采集MSRAY-PLUS可从国内外多个搜索引擎批量采集用户导入的关键词对应的搜寻后果(SERP数据),并进行结构化数据存储与自定义过滤解决。反对存储与导出的数据包含:所属引擎:如 baidu关键词:如 招牌域名:如 www.msray.net根网址: 如 http://www.msray.net网址(url): 如 http://www.msray.net/page/1.htmlIP: 如 113.123.12.123IP所属国家: 如 美国题目:如 这是一个网站的题目形容:如 这是一个网站的形容内容拜访状态码:如 200反对导出主动拓展出的关键词数据,并可显示拓展词的起源;反对持续性的依据种子关键词,全自动拓展出相干词并采集(有限采集);2:外链采集工作引擎MSRAY-PLUS可从用户提供的url种子地址,源源不断的主动爬取全网网站数据(有限爬取),并进行结构化数据存储与自定义过滤解决;反对存储的数据包含:域名:如 www.msray.net根网址: 如 http://www.msray.net网址(url): 如 http://www.msray.net/page/1.htmlIP: 如 113.123.12.123IP所属国家: 如 美国题目:如 这是一个网站的题目形容:如 这是一个网站的形容内容拜访状态码:如 200如果咱们在创立搜索引擎工作的时候,开启了【关联外链抓取工作】,那么创立搜索引擎工作后,零碎也会主动生成对应的外链引擎工作!3:分割信息采集工作引擎企业推广销售最重要的环节就是获取客户资源。在事实生产过程中咱们会遇到很多瓶颈:1:线下获取资源效率低下,往往都在访问过程中节约大量的工夫2:资源起源聚道无限,繁多3: 资源芜杂,有效数据多,不都精准4:获取资源古老,过期MSRAY-PLUS提供基于浏览器的图形化操作界面,智能获取资源。反对亿级数据处理,利用弱小的数据获取能力,抓取互联网上所有支流渠道的企业信息材料,并反对导出,再次整顿。分割信息采集工作模块,反对批量采集导入的URL种子文件中的每个网站的分割信息。包含电话、手机号、QQ、微信、邮箱、facebook账号、twitter账号等。并且可自定义开启与敞开须要采集的内容;电话/手机号兼容多种格局,包含但不限于手机号,400电话号码,以及如000-000-0000,020-0000-000等格局;邮箱兼容多种格局,并且反对交叉空格模式的邮箱内容!facebook账号同时兼容ID格局与账号名格局!反对主动保留采集进度,可进行后下次接着采集;同时反对自定义导出字段内容与自定义导出格局;同时反对导出后果文件下载到本地,以及导出保留到服务器目录;同时反对导出单条信息与多条。比方有的联系方式蕴含两个电话号码,那么咱们能够抉择仅导出一个(不便其他软件辨认),也能够导出多个。

October 18, 2022 · 1 min · jiezi

关于go:Go-单机版缓存实现

Go - 单机版缓存工程目录树$ tree cache-servercache-server├── caches│ └── cache.go├── go.mod├── go.sum├── main.go├── servers│ ├── http.go│ └── IServer.go└── utils └── byte.goutils// byte.gopackage utilsfunc Copy(src []byte) []byte{ dst := make([]byte, len(src)) copy(dst, src) return dst}caches// cache.gopackage cachesimport( "sync" "cache-server/utils")// cache data structtype Cache struct{ data map[string][]byte count int64 lock *sync.RWMutex}func Newcache() *Cache{ return &Cache{ data: make(map[string][]byte, 256), count:0, lock: &sync.RWMutex{}, }}// cache's crud methods// create method & update methodfunc (c *Cache) Set(k string, v []byte){ c.lock.Lock() _, ok := c.data[k] if !ok { // c.data[k] = v c.count++ } c.data[k] = utils.Copy(v) c.lock.Unlock()}// read methodfunc (c *Cache) Get(k string) ([]byte, bool){ c.lock.RLock() defer c.lock.RUnlock() v, ok := c.data[k] return v,ok}// delete methodfunc (c *Cache) Delete(k string){ c.lock.Lock() _, ok := c.data[k] if ok { delete(c.data, k) c.count-- } c.lock.Unlock()}// count methodfunc (c *Cache) Count() int64 { c.lock.RLock() defer c.lock.RUnlock() return c.count }servers// IServer.gopackage serverstype Server interface{ Run(ipAddress string)error}// http.gopackage serversimport( "net/http" "encoding/json" "io/ioutil" "cache-server/caches" "github.com/julienschmidt/httprouter")type HttpServer struct { cache *caches.Cache}// 返回一个对于cache的新HTTP服务器func NewHttpServer(cache *caches.Cache) *HttpServer{ return &HttpServer{ cache: cache, }}func (this *HttpServer) Run(ipAddress string) error{ return http.ListenAndServe(ipAddress, this.routerHandler())}// register routersfunc (this *HttpServer) routerHandler() http.Handler{ // github.com/julienschmidt/httprouter r := httprouter.New() // get cache by key r.GET("/cache/:key", this.getHandler) // set cache by key r.PUT("/cache/:key", this.setHandler) // delete cache by key r.DELETE("/cache/:key", this.deleteHandler) // get cache's data count r.GET("/counts", this.countHandler) return r}func (this *HttpServer) getHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) { key := params.ByName("key") value, ok := this.cache.Get(key) if ok { w.Write(value) }else{ w.WriteHeader(http.StatusNotFound) return }}func (this *HttpServer) setHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) { key := params.ByName("key") value, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } this.cache.Set(key, value) w.Write([]byte("set success"))}func (this *HttpServer) deleteHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) { key := params.ByName("key") _, ok := this.cache.Get(key) if !ok { w.WriteHeader(http.StatusNotFound) } this.cache.Delete(key)}func (this *HttpServer) countHandler(w http.ResponseWriter, r *http.Request, params httprouter.Params) { counts, err := json.Marshal(map[string]interface{}{ "count": this.cache.Count()}) if err != nil{ w.WriteHeader(http.StatusInternalServerError) return } w.Write(counts)}main// main.gopackage mainimport( "cache-server/caches" "cache-server/servers")func main(){ c := caches.Newcache() err := servers.NewHttpServer(c).Run(":8080") if err != nil { panic(err) }}启动$ cd ./cache-server && go mod init cache-server$ go get github.com/julienschmidt/httprouter$ go run main.go测试$ curl http://127.0.0.1:8080/cache/k $ curl http://127.0.0.1:8080/cache/k -X PUT -d 'v' set success% $ curl http://127.0.0.1:8080/cache/k v% $ curl http://127.0.0.1:8080/cache/k -X DELETE $ curl http://127.0.0.1:8080/cache/k $ curl http://127.0.0.1:8080/counts {"count":0}%$ curl http://127.0.0.1:8080/cache/k -X PUT -d 'v'set success% $ curl http://127.0.0.1:8080/counts {"count":1}

October 18, 2022 · 2 min · jiezi

关于go:GoJson转结构体

解决理论需要,案例分享。申请Zabbix API,通过itemid获取到AppName(利用集名称) package mainimport ( "encoding/json" "fmt" "io/ioutil" "log" "net/http" "strings")func PostRequest(payload string, url string) { method := "POST" pl := strings.NewReader(payload) client := &http.Client{} req, err := http.NewRequest(method, url, pl) if err != nil { fmt.Println(err) return } req.Header.Add("Content-Type", "application/json") res, err := client.Do(req) if err != nil { fmt.Println(err) return } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { log.Println(err) return } fmt.Println(string(body))}func main() { const api = "http://192.168.11.11:28080/api_jsonrpc.php" const token = "a638200c24a8bea7f78cd5cabf3d1dd5" const itemid = "33918" a := fmt.Sprintf(`{ "jsonrpc": "2.0", "method": "application.get", "params": {"itemids": "%s"}, "auth": "%s","id": 2 }`, itemid, token) PostRequest(a, api)}响应后果: ...

October 18, 2022 · 2 min · jiezi

关于go:43-Golang常用标准库上下文context

 Context顾名思义上下文,可用于在整个申请上下文传值以及管制超时,本篇文章次要介绍Context的设计思路,以及根本应用形式。 Context 应用形式 构想有一个Go HTTP服务,在申请的整个解决链路,可能随时须要获取一些公共数据,如用户ID等,怎么办呢?通过参数呗,每个函数的第一个输出参数都是用户ID不就行了,如果再加一个公共数据呢?再加一个参数吗?如果将所有这些公共参数封装成一个构造体呢?这样貌似也能够。 不过Go语言自身就为咱们提供了context.Context(context.valueCtx),其能够存储一个key-value键值对,那不行啊,只能存储一个必定不够啊;怎么办,基于老的context.valueCtx对象再衍生新的context.valueCtx对象,两个context.valueCtx对象各能存储一个key-value键值对,想存储更多的数据,持续衍生就能够了。事例程序如下: package mainimport ( "context" "fmt")func main() { ctx := context.Background() ctx1 := context.WithValue(ctx, "k1", "v1") ctx2 := context.WithValue(ctx1, "k2", "v2") fmt.Println(ctx1.Value("k1")) fmt.Println(ctx1.Value("k2")) //返回nil fmt.Println(ctx2.Value("k1")) fmt.Println(ctx2.Value("k2"))}// v1 <nil> v1 v2 基于context.WithValue函数能够衍生新的context.valueCtx对象,同时传递须要存储的key-value键值对;留神第一个参数须要一个context.Context(这是一个接口,context.valueCtx实现了该接口)对象,context.Background函数可返回一个空的context.valueCtx对象。 仔细观察输入后果,ctx1对象只能获取到k1,ctx2对象能够获取到k1以及k2,因为ctx2对象是基于ctx1对象衍生进去的,也能够说ctx1对象是ctx2对象的父对象,而ctx2.Value既能够获取到本人存储的数据,也能获取到父对象存储的数据。最初值得一提的是,context.valueCtx存储的key-value键值对,类型都是interface{},所以获取到数据之后个别须要进行类型转换能力应用。 既然通过context.Context就能实现key-value数据的存储,那就应用它呗,只须要我的项目中所有函数的第一个参数都是context.Context,就能实现在整个申请链路传值。 context.Context就这么点作用?当然不是,最罕用的还是它的超时管制性能。假如某项工作有工夫限度,最多执行3秒,超时后勾销工作的继续执行,这不很简略,通过定时器就能实现,那如果工作比较复杂,又分为多个子工作并启动了多个子协程别离执行呢,3秒超时后能同时完结整个工作吗,包含主工作以及多个子工作?这时候定时器能实现吗?比拟艰难。 上面程序展现了基于context.Context实现的工作超时管制。 package mainimport ( "context" "fmt" "sync" "time")func main() { ctx := context.Background() //可勾销的context ctx1, cancel := context.WithCancel(ctx) //WaitGroup管制并发工作,主协程需期待子工作完结能力退出 wg := sync.WaitGroup{} wg.Add(1) go task(ctx1, &wg) //三秒后勾销子工作 time.AfterFunc(time.Second*3, func() { cancel() }) //期待子工作完结 wg.Wait()}func task(ctx context.Context, wg *sync.WaitGroup) { defer wg.Done() for { //context勾销时会敞开管道,从而可读,实现工作完结return select { case <-ctx.Done(): fmt.Println("context cancel and return") return default: } //子工作1秒周期执行一次,死循环 fmt.Println("exec task", time.Now()) time.Sleep(time.Second) }} context.WithCancel函数返回可勾销的上下文(contetx.cancelCtx对象,同样实现了接口context.Context),函数返回两个值,第一个值就是contetx.cancelCtx对象,第二个值是一个函数,可用于完结该上下文,完结之后ctx.Done函数返回的管道变为可读的,所以子工作能够通过其判断上下文是否被完结,是否该完结当前任务。下面程序事例,定时器3秒超时后完结以后上下文,当然你也能够基于任何条件判断是否须要完结上下文。 ...

October 18, 2022 · 3 min · jiezi

关于go:Beego在线商城

Beego-在线商城商城架构架构介绍 前端: React + redux后端: beego + MySQL性能介绍 用户模块(登录、注册)商品模块(查看、类型筛选、点赞、评论)购物车(退出购物车、购物车结算)订单模块(查看订单、订单确认)商品秒杀缓存设计环境筹备设置环境变量 export GOPATH=/home/project/golangexport GOBIN=/home/project/golang/binexport PATH=$GOBIN:$PATH装置beego&bee go get github.com/astaxie/[email protected]go get github.com/beego/[email protected]验证 $ bee Bee is a Fast and Flexible tool for managing your Beego Web Application.USAGE bee command [arguments]AVAILABLE COMMANDS version Prints the current Bee version migrate Runs database migrations api Creates a Beego API application bale Transforms non-Go files to Go source files fix Fixes your application by making it compatible with newer versions of Beego pro Source code generator dlv Start a debugging session using Delve dockerize Generates a Dockerfile for your Beego application generate Source code generator hprose Creates an RPC application based on Hprose and Beego frameworks new Creates a Beego application pack Compresses a Beego application into a single file rs Run customized scripts run Run the application by starting a local development server server serving static content over HTTP on port update Update BeeUse bee help [command] for more information about a command.ADDITIONAL HELP TOPICSUse bee help [topic] for more information about that topic.疾速开始 ...

October 17, 2022 · 2 min · jiezi

关于go:golang中的变量阴影

索引:https://waterflow.link/articles/1666019023270 在 Go 中,在块中申明的变量名能够在外部块中从新申明。 这种称为变量暗影的原理很容易呈现常见谬误。 以下示例显示了因为变量暗影而导致的意外副作用。 它以两种不同的形式获取课件信息,依据printLog这个布尔值,判断是否打印日志而走到不同的代码分支: package mainimport "fmt"type Courseware struct { Id int64 Name string Code string}func main() { printLog := false var courseware *Courseware // 1 if printLog { courseware , err := getCoursewareAndLog() // 2 if err != nil { fmt.Println("get courseware err: ", err) } fmt.Println(courseware) // 3 } else { courseware, err := getCourseware() // 2 if err != nil { fmt.Println("get courseware err: ", err) } fmt.Println(courseware) // 3 } fmt.Println(courseware) // 4}func getCoursewareAndLog() (*Courseware, error) { fmt.Println("打印日志。。。") return &Courseware{ Id: 1, Name: "多媒体课件", Code: "CW100", }, nil}func getCourseware() (*Courseware, error) { return &Courseware{ Id: 2, Name: "多媒体课件1", Code: "CW101", }, nil}咱们能够剖析下下面的代码: ...

October 17, 2022 · 2 min · jiezi

关于go:goGIN入门到持续更新中

go-GIN入门到....(继续更新中)装置gin参考 https://gin-gonic.com/docs/quickstart/设置环境变量 export GOPATH=~/golangexport PATH=$PATH:$GOPATH/bin# or by vim ~/.bashrc or by ~/.zshrcgin $ go get -u github.com/gin-gonic/gin疾速开始创立文件 // example.gopackage mainimport( "github.com/gin-gonic/gin")func main(){ r := gin.Default() r.GET("/", func(c *gin.Context){ c.JSON(200, gin.H{ "msg": "pong", }) }) r.Run()}运行文件 $ go run main.go[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode)[GIN-debug] GET / --> main.main.func1 (3 handlers)[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default[GIN-debug] Listening and serving HTTP on :8080路由无参数 ...

October 17, 2022 · 2 min · jiezi

关于go:42-Golang常用标准库nethttpclient

 Go语言中,当咱们须要拜访第三方服务时,通常基于http.Client实现,顾名思义其代表HTTP客户端。http.Client的应用绝对比较简单,不过底层有一些细节还是要多留神,包含长连贯(连接池问题),可能偶现的reset状况等等。本篇文章次要介绍http.Client的根本应用形式,实现原理,以及一些注意事项。 http.Client 概述 Go语言中想发动一个HTTP申请真的是非常简单,net/http包封装了十分好用的函数,基本上一行代码就能搞定,如上面几个函数,用于发动GET申请或者POST申请: func Post(url, contentType string, body io.Reader) (resp *Response, err error)func PostForm(url string, data url.Values) (resp *Response, err error)func Get(url string) (resp *Response, err error) 这些函数其实都是基于http.Client实现的,其代表着HTTP客户端,如下所示: //应用默认客户端DefaultClientfunc PostForm(url string, data url.Values) (resp *Response, err error) { return DefaultClient.PostForm(url, data)}func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) { return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))} 那么,http.Client是如何实现HTTP申请的发动过程呢?咱们先看看http.Client构造的定义,非常简单,只有4个字段: type Client struct { //顾名思义传输层 Transport RoundTripper //解决重定向形式(当301、302等之类重定向怎么办) CheckRedirect func(req *Request, via []*Request) error //存储预置cookie,向外发动申请时主动增加cookie Jar CookieJar //超时工夫 Timeout time.Duration}type RoundTripper interface { RoundTrip(*Request) (*Response, error)} http.RoundTripper是一个接口,只自定义了一个办法,用于实现如何传输HTTP申请(长连贯还是短连贯等);如果该字段为空,默认应用http.DefaultTransport,其类型为http.Transport构造(实现了RoundTripper接口)。 ...

October 17, 2022 · 4 min · jiezi

关于go:Golang-轮子之-Supervisor

Supervisor 是一个弱小的 过程管理工具。 在非容器化治理的服务器上, Supervisor 是有十分宽泛的应用场景的。 例如: 服务批量重启,多服务按程序启动,服务oom后主动拉起,服务std日志收集等,甚至服务健康检查它都能做。 原 Supervisor (Python)git: https://github.com/Supervisor... doc: http://supervisord.org/ 新轮子 Supervisor (Golang)git: https://github.com/ochinchina... 比照两个 Supervisor 比照 指标\语言PythonGolang起源20042017以后版本4.2.40.7.3语言版本要求2.7+ 或 3.4+1.11+*unix反对反对MacOS反对反对Widnows不反对能跑安装包大小Pyton环境(40MB) + 脚本(490KB)4.2MBWeb GUI反对反对性能反对状况 共呢个\语言PythonGolang分组反对反对挂了主动拉起反对反对定时重启反对反对web端治理反对反对监控文件主动重启反对反对依赖程序启动反对反对... 这里只是列举了罕用的性能,根本都实现了的,依附golang的 按需runtime+可执行代码打包后,二进制部署相较 python 是更为不便和玲珑的。 装置gihub 上没有二进制包,须要clone代码,手动编译。 $ git clone https://github.com/ochinchina/supervisord$ cd supervisord$ go generate# 以下代码会编译出 linux 平台二进制可执行文件$ GOOS=linux go build -tags release -a -ldflags "-linkmode external -extldflags -static" -o supervisord# mac 下$ go build -tags release -o supervisord试试 $ ./supervisord --helpUsage: supervisord [OPTIONS] <command>Application Options: -c, --configuration= the configuration file -d, --daemon run as daemon --env-file= the environment fileHelp Options: -h, --help Show this help messageAvailable commands: ctl Control a running daemon init initialize a template service install/uninstall/start/stop service version show the version of supervisor应用先创立一个配置文件$ vi supervisor.conf[program:test]command = watch -n 5 "echo Hello!"启动$ supervisord -c supervisor.confINFO[2022-10-15T17:31:24+08:00] load configuration from file file=./supervisor.confINFO[2022-10-15T17:31:24+08:00] create process:testINFO[2022-10-15T17:31:24+08:00] stop listeningINFO[2022-10-15T17:31:24+08:00] try to start program program=testDEBU[2022-10-15T17:31:24+08:00] wait program exit program=testINFO[2022-10-15T17:31:25+08:00] success to start program program=test## 此时该 supervisord 会前台运行,退出终端,或者 Ctrl+C 都会推出,会完结所有的程序。^CINFO[2022-10-15T17:32:39+08:00] receive a signal to stop all process & exit signal=interruptINFO[2022-10-15T17:32:39+08:00] stop the program program=testINFO[2022-10-15T17:32:39+08:00] force to kill the program program=testINFO[2022-10-15T17:32:39+08:00] Send signal to program program=test signal=killedINFO[2022-10-15T17:32:39+08:00] program stopped with status:signal: killed program=testINFO[2022-10-15T17:32:39+08:00] program exited program=testINFO[2022-10-15T17:32:39+08:00] Stopped by user, don't start it again program=test启动并运行到后盾$ supervisord -c supervisor.conf -d这样就启动了 ...

October 17, 2022 · 3 min · jiezi

关于go:全网搜索引擎采集msrayURL采集关键词采集域名采集

搜索引擎全网采集Msray-plus,是企业级综合性爬虫/采集软件。反对亿级数据存储、导入、反复判断等。无需应用简单的命令,提供本地WEB治理后盾对软件进行相干操作,功能强大且简略易上手!1:可从国内外多个搜索引擎批量采集用户导入的关键词对应的搜寻后果(SERP数据),并进行结构化数据存储与自定义过滤解决;2:可从用户提供的url种子地址,源源不断的主动爬取全网网站数据,并进行结构化数据存储与自定义过滤解决;3:可从用户提供的网站列表数据中,全自动的提取出网站联系方式信息,包含但不限于邮箱、手机/电话、QQ、微信、facebook、twitter等。同时反对存储域名、根网址、网址(url)、IP、IP所属国家、题目、形容、拜访状态等多种数据,次要使用于全网域名/网址/采集、行业市场钻研剖析、指定类型网站采集与剖析、网络推广剖析以及为各种大数据分析等提供数据撑持1:采集注意事项1:搜索引擎是依据关键词采集的,采集之前要筹备好关键词(关键词能够为txt文档,一行一个)2:配置流程1:上传关键词文件 2:抉择适宜本人需要的过滤规定(可放弃默认) 3:抉择须要应用到的搜索引擎 4:过滤计划的应用,能够放弃默认,也能够自定义过滤规定,可依据域名,ip地址,国家信息进行过滤 3:对采集的数据进行 导出和数据分析软件可进行全网公开数据挖掘,大规模采集互联网公开数据,精准挖取采集内容。可将采集到的数据进行本地存储,或者近程推送到本人的客户端,进行二次利用,业务剖析。4:更多关注>>msray官网:https://www.msray.net/在线文档: https://www.msray.net/doc/免费版获取:https://github.com/super-l/msray

October 17, 2022 · 1 min · jiezi

关于go:Kafka-log和consumer-offset过期测试

consumer生产consumer本地会记录两种offset: • 生产拉取的offset, 初始化和rebalance时会fetchInitialOffset从broker获取上次生产的offset而后从指定的offset拉取音讯。接下来Consumer会一直通过fetchNewMessages() 到broker从指定offset拉取音讯。• 生产提交的offset,生产后主动/手动提交曾经生产的offset 讲下提交步骤:1.本地先标记offset, MarkOffset: 留神此时只是本地标记曾经生产的, 并没有真正提交到broker2.再执行CommitOffset提交到broker,而CommitOffset有上面两种形式: 主动提交(默认都是这种),Consumer.Offsets.AutoCommit.Enable(默认关上) 两种状况触发CommitOffset主动提交:1.定时提交,与Consumer.Offsets.AutoCommit.Interval相干2.进行consumer sessions中断生产时退出的时候会触发提交手动提交(不倡议)_consumer_offsets__consumer_offsets这个topic外面存储的是consumer offsets信息,清理策略log.cleanup.policy默认是compact,简略的说就是压缩雷同key, 保留最初一个。其余topic log默认log.cleanup.policy默认是delete。 log.retention参数针对的是delete的清理策略,对compact不失效。compact是压缩后会清理掉垃圾文件次要和log.cleaner配置无关。可浏览:Kafka 2.2.0 消息日志清理机制:日志删除 日志压缩 演示kafka broker版本:v2.0.0, sarama (go sdk)版本 v1.26.1批改配置:• log.retention.minutes: 5 (默认log.retention.hours=168) 日志保留工夫• log.retention.check.interval.ms: 1000 (默认600000ms,10分钟) 日志过期查看频率• offsets.retention.minutes: 1 (以前默认24小时,2.0版本后默认10080, 7天) 消费者的offset保留工夫• offsets.retention.check.interval.ms: 1000 (默认300000ms, 5分钟) 消费者的过期offset查看频率• 消费者初始化生产设置为OffsetOldest 消费者过期后从最老的log offset生产consumer offset 过期工夫1.关上生产者和消费者 其中offset10是上一轮生产的 2.读取__consumer_offsets音讯查看消费者offset的状况 ./kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic __consumer_offsets --formatter 'kafka.coordinator.group.GroupMetadataManager$OffsetsMessageFormatter'|grep -v console-consumer-其中ExpirationTime-CommitTime=1分钟,阐明保留工夫是一分钟了 3.敞开生产者,一分钟(与offsets.retention.minutes和offsets.retention.check.interval.ms无关) 后__consumer_offsets外面[my-group,sync_time_test3,0] 接管到了null音讯,代表consumer的offsets过期了。 4.重启生产者consumer持续上次生产,此时如果不重启消费者是没事的。此时尽管broker外面曾经没有了consumer生产的offset,然而consumer本地记住了上次fetch的offset, 会持续拉取下一条音讯。只有初始化和rebalance时会fetchInitialOffset从broker获取上次生产的offset而后从指定的offset拉取音讯。 5.再次敞开生产者,期待consumer offset过期 ...

October 15, 2022 · 2 min · jiezi

关于go:Goravel-V1-正式版发布让-Go-开发更简单高效

历时近一年工夫,14个版本的更新,实现 13个功能模块,Goravel 终于达到了第一个里程碑:V1 正式版公布。 Goravel 是一个性能齐备、具备良好扩大能力的 Web 应用程序框架。作为一个起始脚手架帮忙 Golang 开发者疾速构建本人的利用。 感激 Laravel 伟人的肩膀,让应用 Goravel 进行 Golang 开发时也领有了丝滑的体验。让宽广 Phper 不必再深陷另一个语言框架的学习,就能很好的实现一个 Golang 我的项目的开发。 目前,Goravel 还只是实现了一小步,期待将来更简略、高效的 Goravel! 开源我的项目的成长离不开你的反对: 欢送 PR;感激 Issues;期待你对中英文文档的进一步欠缺;Goravel 工夫线: 2021年10月16日,初始化仓库;2021年11月22日,实现架构设计与第一个模块;2021年12月09日,中文文档公布;2021年12月12日,独立文档站点 https://www.goravel.dev 在 GitBook 上线;2021年12月14日,英文文档公布;2022年01月24日,Goravel 作为集体我的项目的底层实现,正式部署生产环境,始终体现稳固;2022年09月06日,文档站点由 Gitbook 迁徙至 Vuepress,实现了更丰盛的内容;2022年10月09日,Goravel 实现整体笼罩测试: goravel/testing ,让框架稳固更有保障;2022年10月10日,Goravel V1 正式版公布;将来可期……

October 14, 2022 · 1 min · jiezi

关于go:GoWeb基础

Go-Web根底Web服务器工作原理client 通过TCP/IP协定 建设到 server 的TCP链接(三次握手)链接胜利建设后,client 向 server 发送 HTTP协定申请包,申请server的资源文档server 响应 client 的 http申请,返回 对应的资源文档client断开与server的链接(四次挥手)三次握手:client (am i connetc to server?) --------> serverclient <-------- (yes, you r & am i connect to client?) serverclient (yes, you r) --------> server四次挥手:client (886, ack?) --------> serverclient <-------- (ack.) serverclient <-------- (886, ack?) serverclient (ack.) --------> serverURLuniform resources locator对立资源定位符根本格局:schema://host[:port]/path/../[?query-string][anchor]schema 协定(http https ftp)host 服务器的IP地址或域名port 服务器的端口号(http默认80 https默认443)path 申请资源的门路query-string 申请字段anchor 锚DNSdomain name system 域名零碎次要性能是将域名转换为IP地址DNS解析工作原理:1 浏览器输出域名2 操作系统首先查看本地hosts文件 有无 该域名->IP地址的映射关系 有间接拜访 无往下走3 查看本地DNS解析器缓存 有无 该域名->IP地址的映射关系 有间接拜访 无往下走4 查看TCP/IP参数中设置的首选DNS服务器 有无 该域名->IP地址的映射关系 有间接拜访 无往下走无论解析可能会变得很简单,但最终返回的不过是一个IP地址HTTP协定Request包(client/browser):GET /domains/example/ HTTP/1.1 //申请办法 申请URI HTTP协定/协定版本Host:www.iana.org //服务端的主机名User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 //浏览器信息Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //客户端能接管的mineAccept-Encoding:gzip,deflate,sdch //是否反对流压缩Accept-Charset:UTF-8,*;q=0.5 //客户端字符编码集//空行,用于宰割申请头和音讯体//音讯体,申请资源参数,例如POST传递的参数//根本申请办法: GET,POST,PUT,DELETEResponse包(server):HTTP/1.1 200 OK //状态行Server: nginx/1.0.8 //服务器应用的WEB软件名及版本Date:Date: Tue, 30 Oct 2012 04:14:25 GMT //发送工夫Content-Type: text/html //服务器发送信息的类型Transfer-Encoding: chunked //示意发送HTTP包是分段发的Connection: keep-alive //放弃连贯状态Content-Length: 90 //主体内容长度//空行 用来宰割音讯头和主体<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"... //音讯体5类状态码: 1XX 提示信息 - 示意申请已被胜利接管,持续解决 2XX 胜利 - 示意申请已被胜利接管,了解,承受 3XX 重定向 - 要实现申请必须进行更进一步的解决 4XX 客户端谬误 - 申请有语法错误或申请无奈实现 5XX 服务器端谬误 - 服务器未能实现非法的申请Go-搭建一个http服务器package mainimport( "log" "fmt" "strings" "net/http")func hello(w http.ResponseWriter, r *http.Request){ fmt.Println(r.Form) //这些信息是输入到服务器端的打印信息 fmt.Println("path", r.URL.Path) fmt.Println("scheme", r.URL.Scheme) for k, v := range r.Form { fmt.Println("key:", k) fmt.Println("val:", strings.Join(v, "")) } fmt.Fprintf(w, "Hello Go!") //这个写入到w的是输入到客户端的}func main(){ http.HandleFunc("/", hello) // 设置路由 err := http.ListenAndServe("0.0.0.0:8080", nil) if err != nil { log.Fatal("ListenAndServe: ", err) }}

October 14, 2022 · 1 min · jiezi

关于go:Go切片Silce底层实现和扩容

Go切片Silce底层实现和扩容一 切片的数据结构切片(slice)是对数组一个间断片段的援用,所以切片是一个援用类型(因而更相似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)。这个片段能够是整个数组,或者是由起始和终止索引标识的一些项的子集。须要留神的是,终止索引标识的项不包含在切片内。切片提供了一个与指向数组的动静窗口。给定项的切片索引可能比相干数组的雷同元素的索引小。和数组不同的是,切片的长度能够在运行时批改,最小为 0 最大为相干数组的长度:切片是一个长度可变的数组。Slice 的数据结构定义如下: type slice struct { array unsafe.Pointer len int cap int}复制代码 切片的构造体由3局部形成,Pointer 是指向一个数组的指针,len 代表以后切片的长度,cap 是以后切片的容量。cap 总是大于等于 len 的。 二 创立切片make 函数容许在运行期动静指定数组长度,绕开了数组类型必须应用编译期常量的限度。创立切片有两种模式,make 创立切片,空切片。make 和切片字面量 func makeslice(et *_type, len, cap int) slice { // 依据切片的数据类型,获取切片的最大容量 maxElements := maxSliceCap(et.size) // 比拟切片的长度,长度值域应该在[0,maxElements]之间 if len < 0 || uintptr(len) > maxElements { panic(errorString("makeslice: len out of range")) } // 比拟切片的容量,容量值域应该在[len,maxElements]之间 if cap < len || uintptr(cap) > maxElements { panic(errorString("makeslice: cap out of range")) } // 依据切片的容量申请内存 p := mallocgc(et.size*uintptr(cap), et, true) // 返回申请好内存的切片的首地址 return slice{p, len, cap}}复制代码还有一个 int64 的版本: ...

October 14, 2022 · 3 min · jiezi