关于go:go-时间字符串转标准时间

package mainimport ( "fmt" "time")func main() { layout := "2006-01-02 15:04:05" str := "2022-09-05T12:03:15Z" t := DateTimeStringFormat(str, layout) fmt.Println(t)}// DateTimeStringFormat 工夫格式化 字符串格式化----后果字符串func DateTimeStringFormat(timeValue string, fmt string) string { timeLayout := "2006-01-02T15:04:05.999999999Z07:00" //所需模板 loc, _ := time.LoadLocation("Local") //***获取时区*** theTime, _ := time.ParseInLocation(timeLayout, timeValue, loc) //应用模板在对应时区转化为time.time类型 // 0001-01-01T00:00:00Z这里也示意工夫为null if theTime.IsZero() { return "" } else { //工夫戳转日期 //dataTimeStr := theTime.Format("2006-01-02 15:04:05") //应用模板格式化为日期字符串 dataTimeStr := theTime.Format(fmt) //应用模板格式化为日期字符串 return dataTimeStr }}

September 9, 2022 · 1 min · jiezi

关于go:go里边一个让新手误解让老炮犯错的小知识点

问题:在一个nil变量上调用函数,会产生什么?个别会认为panic。为了不产生panic,每次调用办法前,都要判断变量是否为nil。这么做尽管没错,但究竟使代码看起来不那么优雅。那么肯定会panic吗?让咱们来做个试验 type PlanConfig struct { No int64}func (p *PlanConfig) IsEmpty() bool { if p == nil { fmt.Println("hello") return false } return reflect.ValueOf(*p).IsZero()}var p *PlanConfigfmt.Println(p)fmt.Println(p.IsEmpty())该代码会输入<nil>hellofalse能够看到,新申明的指针类型变量p,是nil,然而并不障碍他调用IsEmpty()办法。 如果强制赋nil呢,还会执行吗? type PlanConfig struct { No int64}func (p *PlanConfig) IsEmpty() bool { if p == nil { fmt.Println("hello") return false } return reflect.ValueOf(*p).IsZero()}var p *PlanConfigfmt.Println(p)p = nilfmt.Println(p.IsEmpty())仍然输入<nil>hellofalse是不是很倔强。 再模仿一下咱们理论代码中的用法,比方从一个函数获取某个变量,然而返回值nil,而后用这个变量调用办法 type PlanConfig struct { No int64}func (p *PlanConfig) IsEmpty() bool { if p == nil { fmt.Println("hello") return false } return reflect.ValueOf(*p).IsZero()}func getPlan() *PlanConfig { return nil}p := getPlan()fmt.Println(p.IsEmpty())输入hellofalse ...

September 9, 2022 · 1 min · jiezi

关于go:golang连接sqlserver数据库

先装置好Golang环境MacBook Linux 树莓派raspberrypi装置Golang环境 package mainimport ( "database/sql" "flag" "fmt" "log" _ "github.com/denisenkom/go-mssqldb")var ( debug = flag.Bool("debug", false, "enable debugging") password = flag.String("password", "", "the database password") port *int = flag.Int("port", 1433, "the database port") server = flag.String("server", "", "the database server") user = flag.String("user", "", "the database user"))func main() { flag.Parse() if *debug { fmt.Printf(" password:%s\n", *password) fmt.Printf(" port:%d\n", *port) fmt.Printf(" server:%s\n", *server) fmt.Printf(" user:%s\n", *user) } connString := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d", *server, *user, *password, *port) if *debug { fmt.Printf(" connString:%s\n", connString) } conn, err := sql.Open("mssql", connString) if err != nil { log.Fatal("Open connection failed:", err.Error()) } defer conn.Close() stmt, err := conn.Prepare("select 1, 'abc'") if err != nil { log.Fatal("Prepare failed:", err.Error()) } defer stmt.Close() row := stmt.QueryRow() var somenumber int64 var somechars string err = row.Scan(&somenumber, &somechars) if err != nil { log.Fatal("Scan failed:", err.Error()) } fmt.Printf("somenumber:%d\n", somenumber) fmt.Printf("somechars:%s\n", somechars) fmt.Printf("bye\n")}

September 8, 2022 · 1 min · jiezi

关于go:Go十大常见错误第10篇Goroutine和循环变量一起使用的坑

前言这是Go十大常见谬误系列的第10篇:Goroutine和循环变量一起应用的坑。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go十大常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 常见谬误对于Go初学者,很容易犯的一个谬误就是goroutine和循环变量联合在一起应用时,谬误地应用了循环变量。 比方上面这个例子: ints := []int{1, 2, 3}for _, i := range ints { go func() { fmt.Printf("%v\n", i) }()}这段程序的输入后果应该是什么? Go初学者可能认为输入后果应该是1 2 3,但理论状况并不是。 这个例子里,3个goroutine共享同一个变量i,最初输入的后果大概率是输入3 3 3。 要解决这个问题,次要有2个解决方案。 解决方案1把循环变量i作为goroutine函数的一个参数,编译器在执行go func(i int)时,就会解析到i的值,确保每个goroutine能够拿到本人想要的值。 ints := []int{1, 2, 3}for _, i := range ints { go func(i int) { fmt.Printf("%v\n", i) }(i)}解决方案2创立一个新的变量,用于goroutine。 ints := []int{1, 2, 3}for _, i := range ints { i := i go func() { fmt.Printf("%v\n", i) }()}举荐浏览Go十大常见谬误第1篇:未知枚举值Go十大常见谬误第2篇:benchmark性能测试的坑Go十大常见谬误第3篇:go指针的性能问题和内存逃逸Go十大常见谬误第4篇:break操作的注意事项Go十大常见谬误第5篇:Go语言Error治理Go十大常见谬误第6篇:slice初始化常犯的谬误Go十大常见谬误第7篇:不应用-race选项做并发竞争检测Go十大常见谬误第8篇:并发编程中Context应用常见谬误Go面试题系列,看看你会几题?开源地址文章和示例代码开源在GitHub: Go语言高级、中级和高级教程。 ...

September 8, 2022 · 1 min · jiezi

关于go:拆解Shifu从部署K8s集群到玩转数字孪生

拆解Shifu——从部署K8s集群到玩转数字孪生在生产环境中Shifu能够间接部署在K8s集群中,并且Shifu的生产部署非常容易。但在测试时,咱们须要在本地接入设施做一些调试的工作,须要进行本机装置测试,您须要在本地创立集群,一些工具如 kind 能够帮忙咱们在本人的电脑上创立这样的集群。简介此篇文章拆解了Shifu安装包的内容,应用命令行一步一步在本地创立集群。这种形式有助于您了解Shifu运作过程中应用的组件。步骤第一步:装置 Docker Desktop信息 Docker Desktop 可能在桌面操作系统( Windows/ macOS/ 桌面版Ubuntu 等)装置 Docker ,并提供可视化的界面可供治理。 Shifu 应用 Docker 将每一个理论的物理设施 (edgeDevice) 转为一个数字孪生设施 (deviceShifu), Docker 所起的作用次要是虚拟化和隔离。 请查看_Docker_官网来在本人的电脑上装置 Docker Desktop 。 第二步:确认 Docker Desktop 已装置且启动应用上面的命令来确定 Docker Desktop 已装置且启动,输入如下则阐明胜利: $ sudo docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES如果输入为 Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?则阐明 Docker Desktop 未启动; 如果输入为 command not found则阐明 Docker Desktop 未装置。 第三步:装置 kubectl信息 ...

September 8, 2022 · 1 min · jiezi

关于go:Golang泛型实战-使用泛型结构体实现gorm-baseMapper

Golang泛型实战 | 应用泛型构造体实现gorm baseMapper指标实现一个根底数据库操作构造体,在gorm中一个新的表中可间接应用增删改查操作 实现实例化数据库客户端在这个例子中,咱们应用MySQL数据库进行示范,首先咱们须要实例化一个MySQL数据库客户端: package mysqlimport ( "brain/config" // 数据库参数保留在该配置文件中 "github.com/sirupsen/logrus" "gorm.io/driver/mysql" "gorm.io/gorm" "time")var ( Client *gorm.DB)func init() { if Client == nil { var err error Client, err = gorm.Open(mysql.Open(config.BrainCfg.Mysql.Source), &gorm.Config{}) db, _ := Client.DB() db.SetMaxOpenConns(config.BrainCfg.Mysql.MaxOpenConn) db.SetMaxIdleConns(config.BrainCfg.Mysql.MaxIdleConn) db.SetConnMaxLifetime(time.Duration(config.BrainCfg.Mysql.ConnMaxLifeTime) * time.Second) if err != nil { logrus.Errorf("mysql client create failed: %+v", err) } else { logrus.Info("mysql client create successed") } }}定义泛型构造体咱们定义一个泛型构造体BaseMapper,在该构造体中实现数据库的增删改查操作: package mapperimport ( "brain/internal/util/client/mysql" "errors" "github.com/sirupsen/logrus" "gorm.io/gorm")type BaseMapper[T any] struct {}func (m BaseMapper[T]) Insert(entity T) bool { logrus.Infof("insert %+v", entity) err := mysql.Client.Create(&entity).Error if err != nil { logrus.Errorf("insert %+v failed, %+v", entity, err) return false } return true}func (m BaseMapper[T]) Delete(entity T) bool { logrus.Infof("delete %+v", entity) err := mysql.Client.Delete(*&entity).Error if err != nil { logrus.Errorf("delete %+v failed, %+v", entity, err) return false } return true}func (m BaseMapper[T]) Update(entity T) bool { logrus.Infof("update %+v", entity) err := mysql.Client.Updates(*&entity).Error if err != nil { logrus.Errorf("update %+v failed, %+v", entity, err) return false } return true}func (m BaseMapper[T]) List(entity T) (res []T) { logrus.Infof("list %+v", entity) err := mysql.Client.Find(&res, *&entity).Error if err != nil { logrus.Errorf("list %+v failed, %+v", entity, err) } return res}func (m BaseMapper[T]) Detail(entity T) (res T, err error) { logrus.Infof("detail %+v", entity) result := mysql.Client.First(&res, *&entity) if errors.Is(result.Error, gorm.ErrRecordNotFound) { return res, result.Error } return res, nil}泛型构造体的应用假如咱们新增一个用户表,蕴含如下对象: ...

September 8, 2022 · 2 min · jiezi

关于go:mongodb-基本概念

文档是 mongodb 的最小数据集单位,是多个键值对有序租户在一起的数据单元,相似于关系型数据库的记录 汇合一组文档的汇合,文档寄存的是数据,汇合内的构造是能够不同的,汇合就相似于关系型数据库的表 库数据库,由多个汇合组成的。每个数据库都市齐全独立的,有本人的用户,权限信息,独立的存储文件夹 实例在零碎运行库的过程及节点集,一个实例能够有多个数据库 关系型数据库和 mongodb 比照例如 mongodb 和 mysql 进行比照 文件名MongoDBMysql服务名mongodmysqld客户端名mongomysql相似于这这种数据库开源组件,服务名字前面都会有一个 d 客户端程序,是用于与服务端程序通信的 关系型数据库 和 mongodb 基本概念比照学习 概念关系型数据库Mongdb数据库databasedatabase表tablecollection数据行row datadocument字段columnfield索引indexindex表关联joinembedding 或 linkding分片 / 分区partitionshard分区键partition keysharding key上述表格中,咱们能够清晰的看出,mongodb 中的 文档 对应着关系型数据库的行数据,mongodb 中的 汇合 对应着关系型数据库的 表格 mongodb 的数据类型后面图上有提到,mongodb 中的文档相似于 json 对象,属于 json 中的一种,成为 bson。 文档中字段中的值能够包含其余文档,成为内嵌文档,也能够包含数组和文档数据 对于文档存储的长处有这些: 文档即为对象,对应于许多编程语言中的本机数据类型 嵌入式文档和数组缩小了对连贯的需要动静模式反对晦涩的多态性咱们一起来看看 bson 都有哪些数据类型,bson 是 json 文档的二进制示意模式,bson 蕴含了比 json 更多的数据类型,如下: typenumber阐明Double1双精度浮点值String2字符串,UTF-8才是非法的Object3用于内嵌文档Array4数组Binary data5二进制数据Udefined6“undefined”Objectid7对象 idBoolean8布尔Date9日期工夫,unix 规范Null10创立空值Regular Expression11正则表达式DBPointer12“dbPointer”JavaScript13“javascript“Symbol14“symbol”JavaScript(with scope)15“javascript with scope”32 - bit integer16int 类型Timestamp17mongodb 复制和 sharing 应用的非凡外部类型,前 4 个字节是增量,挨着的 4 字节是工夫戳64-bit integer18long 类型Decimal12819decimalMin key-1比失常 bson 类型元素值都低的类型 与 255 是同样的成果Max key127比失常 bson 类型元素值都高的类型须要留神的一点: ...

September 7, 2022 · 1 min · jiezi

关于go:一起玩转SOFA-飞船-Layotto-星球登陆计划

一、SOFA 飞船即刻启程!这次 SOFAStack 开源社区启动 SOFA 飞船,行将踏上充斥挑战的星球登陆之旅,第一站将来到 Layotto 星球。星球登陆工作已被打包放在守护星球的各个空间站中。想要成为开源先锋的小伙伴快来参加摸索,做进击的开源人吧! Layotto 是一款应用 Golang 开发的利用运行时, 旨在帮忙开发人员疾速构建云原生利用,帮忙利用和基础设施解耦。Layotto 提供了各种分布式的能力,比方状态治理、配置管理、事件公布订阅等能力,以简化利用的开发。 二、一起摸索空间站!咱们会继续挖掘更新 Layotto 星球上的各个空间站。小伙伴们能够退出本人感兴趣的空间站并进行摸索,通过工作实现积攒能量值,去成为空间站站长,率领更多本站的小伙伴实现星球登陆吧! 「空间站直通车」:https://github.com/mosn/layotto/issues/776 已探寻到的空间站一览● Bug 空间站: [ ] UT of mosn.io/layotto/components/rpc/invoker/mosn/channel failed #550[ ] he sayHello demo fails #757● Component 空间站: [ ] 集成 wasmtime 运行时 #755[ ] 让阿里云 OSS 组件不必配密钥 #748● Demo 空间站: [ ] 在 quickstart 中增加 Javscript demo #686[ ] 在 configuration API quickstart 中增加 java demo #687[ ] 在 lock API quickstart 中增加 java demo #688[ ] 在 secret API quickstart 中增加 java demo #689[ ] 在 RPC quickstart 中增加 java demo #690● WebAssembly 空间站: ...

September 7, 2022 · 1 min · jiezi

关于go:String的底层实现1

String的底层实现(1)原理字符串能够分为两种: 在编译时确定其长度,值不能够批改不在编译时确定其长度,在运行时能够动静批改其长度,值能够批改计算机中应用二进制来体现数据,字符串在计算机中也是以二进制进行示意。那么问题来了:怎么确定哪段二进制示意的是字符串而不是其余类型的数据? 字符串的首地址咱们能够很容易失去,重点在于怎么确定字符串的完结地位。 所以咱们须要拆分二进制,能够通过分隔符来拆分(遇到分隔符示意从zi),也能够通过实现确定字符串的长度来拆分。 C语言通过字符数组的最初一位为"\0"作为字符串的定界符Go应用长度来记录字符串边界,字符的长度就是数组 大小应用分隔符进行拆分须要N+1的字符数组来示意一个长度为N的字符串,并且字符数组最初一位总是空字符"\0"。 string的概念// string is the set of all strings of 8-bit bytes, conventionally but not// necessarily representing UTF-8-encoded text. A string may be empty, but// not nil. Values of string type are immutable.type string stringstring 是所有 8 位字节字符串的汇合,通常但不肯定代表 UTF-8 编码的文本。字符串能够为空(长度为 0),但不会是 nil。字符串类型的值是不可变的。Go中的字符串实际上是一个只读的字节切片[]byte。这答复了"当我在地位n索引 Go 字符串时,为什么我没有失去第 n 个字符?"这个问题:字符串是从字节构建的,因而对它们进行索引会产生字节,而不是字符。字符串甚至可能不蕴含字符。 字符串常量会在编译期调配到只读段,对应数据地址不可写入,雷同的字符串常量不会反复存储 如果是代码中存在的字符串,编译器会将其标记成只读数据 SRODATA 。 package mainfunc main() { s := "golang" println(s)}go.string."golang" SRODATA dupok size=6 0x0000 67 6f 6c 61 6e 67 golangstringStructtype stringStruct struct { // str是字符串的首地址 str unsafe.Pointer // len是字符串的长度 len int} ...

September 7, 2022 · 2 min · jiezi

关于go:为什么-signalNotify-使用缓冲通道

在本文中,咱们将向您介绍为什么signal.Notify要应用缓冲通道。当咱们想做优雅的敞开时,咱们会应用这个性能来失常敞开服务或连贯。通过signal,咱们能够检测到信号的起源,并进行后续工作(敞开DB连贯,查看工作是否实现……等)。 package mainimport ( "fmt" "os" "os/signal")func main() { // Set up channel on which to send signal notifications. // We must use a buffered channel or risk missing the signal // if we're not ready to receive when the signal is sent. c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) // Block until a signal is received. s := <-c fmt.Println("Got signal:", s)下面的示例分明地表明,如果您不应用缓冲通道,则存在肯定的无奈捕捉信号的危险。那么为什么会有这样的形容呢?让咱们看看其余例子。 应用无缓冲通道将代码更改为以下内容: package mainimport ( "fmt" "os" "os/signal")func main() { c := make(chan os.Signal) signal.Notify(c, os.Interrupt) // Block until a signal is received. s := <-c fmt.Println("Got signal:", s)}运行下面的代码,按ctrl + c,你会看到Got signal: interrupt,那么如果在承受channle之前咱们还有一些很简单的工作要做会发什么,先time.Sleep用来测试一下。 ...

September 7, 2022 · 1 min · jiezi

关于go:net包的使用

net包的应用疾速开始连贯到服务器// 连贯客户端conn, err := net.Dial("tcp", "golang.org:80")if err != nil { // handle error}fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")status, err := bufio.NewReader(conn).ReadString('\n')// ...创立服务器// 创立监听器ln, err := net.Listen("tcp", ":8080")if err != nil { // handle error}for { // 监听客户端连贯申请 conn, err := ln.Accept() if err != nil { // handle error } // 解决客户端申请,该函数由本人编写 go handleConnection(conn)}

September 7, 2022 · 1 min · jiezi

关于go:Go中的网络轮询器1Epoll在Go中的抽象

Go中的网络轮询器(1)--Epoll在Go中的形象Go用netpoll实现对不同操作系统的I/O多路复用的形象(或者说是封装),Go中多路复用与平台无关。编译器在编译时依据平台的不同应用不同的多路复用器进行编译。 无关netpoll的函数epoll、kqueue、solaries 等多路复用模块都要实现以下五个函数 // 新建多路复用器// Initialize the poller. Only called once.// func netpollinit()// 监听文件描述符上的边缘触发事件,创立事件并退出监听// Disable notifications for fd. Return an errno value.// func netpollopen(fd uintptr, pd *pollDesc) int32// 监听多路复用器,返回一组曾经就绪的goroutine// func netpoll(delta int64) gList// Poll the network. If delta < 0, block indefinitely. If delta == 0,// poll without blocking. If delta > 0, block for up to delta nanoseconds.// Return a list of goroutines built by calling netpollready.// 唤醒多路复用器// func netpollBreak()// Wake up the network poller, assumed to be blocked in netpoll.// 判断文件描述符是否被多路复用器应用// func netpollIsPollDescriptor(fd uintptr) bool// Reports whether fd is a file descriptor used by the poller.上面是Linux上的epoll的相干实现 ...

September 7, 2022 · 6 min · jiezi

关于go:Golang实现多存储驱动设计SDK

本文已收录编程学习笔记。涵盖PHP、JavaScript、Linux、Golang、MySQL、Redis和开源工具等等相干内容。意识GocacheGocache是一个基于Go语言编写的多存储驱动的缓存扩大组件。它为您带来了许多缓存数据的性能。 反对性能多个缓存驱动存储:反对内存、redis或您自定义存储驱动。反对如下性能: ✅链式缓存:应用具备优先级程序的多个缓存(例如,内存而后回退到redis共享缓存)。 ✅可加载缓存:容许您调用回调函数将数据放回缓存中。 ✅指标缓存,可让您存储无关缓存应用状况的指标(命中、未命中、设置胜利、设置谬误……)。 ✅主动编组/解组缓存值作为构造的编组器。 ✅在存储中定义默认值并在设置数据时笼罩它们。 ✅通过过期工夫和/或应用标签缓存生效。 ✅泛型的应用。 默认状况下,Gocache反对如下几种缓存驱动: 内存 (bigcache) (allegro/bigcache)。内存 (ristretto) (dgraph-io/ristretto)。内存 (go-cache) (patrickmn/go-cache)。内存缓存(bradfitz/memcache)。Redis (go-redis/redis)。闲暇缓存(coocood/freecache)。Pegasus ( apache/incubator-pegasus )基准测试。开发原因在作者的官网博客中提到这样的几句话: 当我开始在 GraphQL Go 我的项目上实现缓存时,它曾经有一个内存缓存,它应用了一个具备简略 API 的小库,但也应用了另一个内存缓存库来应用具备不同库和 API 的批处理模式加载数据,做同样的事件:缓存我的项目。起初,咱们还有一个需要:除了这个内存缓存之外,咱们还想应用 Redis 增加一层分布式缓存,次要是为了防止咱们的新 Kubernetes pod 在将新版本的应用程序投入生产时呈现空缓存。 因而,作者想到是时候领有一个对立的API来治理多个缓存存储:内存、redis、memcache 或任何你想要的。 如何应用装置要开始应用最新版本的 go-cache,您能够应用以下命令: go get github.com/eko/gocache/v3为防止尝试导入库时呈现任何谬误,请应用以下导入语句: import ( "github.com/eko/gocache/v3/cache" "github.com/eko/gocache/v3/store")如果您遇到任何谬误,请务必运行go mod tidy以清理您的 go.mod 文件。 存储适配器首先,当您要缓存我的项目时,您必须抉择要缓存我的项目的地位:在内存中?在共享的 redis 或 memcache 中?或者可能在另一个存储中。目前,Gocache 实现了以下存储: BigCache:内存中的存储。Ristretto : DGraph 提供的另一个内存存储。Memcache:基于 bradfitz/gomemcache 客户端库的 memcache 存储。Redis:基于 go-redis/redis 客户端库的 redis 存储。所有这些商店都实现了一个非常简单的 API,它遵循以下接口: type StoreInterface interface { Get(key interface{}) (interface{}, error) Set(key interface{}, value interface{}, options *Options) error Delete(key interface{}) error Invalidate(options InvalidateOptions) error Clear() error GetType() string}此接口代表您能够在商店中执行的所有操作,并且每个操作都调用客户端库中的必要办法。所有这些存储都有不同的配置,具体取决于您要应用的客户端库,例如,初始化 Memcache 存储: ...

September 6, 2022 · 2 min · jiezi

关于go:go-hack五使用go-rpc和metasploit联动

go网络安全代码地址 筹备工作开启metasploit的rpc通信 kali中msfconsole 进入交互模式load msgrpc Pass=123 Serverhost=0.0.0.0 开启rpcmetasploit应用messagePack进行通信, go get gopkg.in/vmihailenco/msgpack.v2 应用第三方的messagepack包基础知识metaploit apiflag \`msgpack:",asArray"\` \`msgpck:",omitempty"\`msgpack强制为索引数组 _msgpack struct{} \`msgpack:",asArray"\`package rpcimport ( "bytes" "fmt" "log" "net/http" "gopkg.in/vmihailenco/msgpack.v2")// sessionlist 申请的构造体type SessionListReq struct { _msgpack struct{} `msgpack:",asArray"` // 当做索引数组解析 Method string Token string}// sessionList 的响应type SessionListRes struct { ID uint32 `msgpack:",omitempty"` // 可选参数 Type string `msgpack:"type"` TunnelLocal string `msgpack:"tunnel_local"` TunnelPeer string `msgpack:"tunnel_peer"` ViaExploit string `msgpack:"via_exploit"` ViaPayload string `msgpack:"via_payload"` Desc string `msgpack:"desc"` Info string `msgpack:"info"` Workspace string `msgpack:"workspack"` SessionHost string `msgpack:"session_host"` SessionPort int `msgpack:"session_port"` Username string `msgpack:"username"` UUID string `msgpack:"uuid"` ExploitUUID string `msgpack:"exploit_uuid"`}// 登录申请type loginReq struct { _msgpack struct{} `msgpack:",asArray"` Method string Username string Pass string}// 登录返回type loginRes struct { Result string `msgpack:"result"` Token string `msgpack:"token"` Error bool `msgpack:"error"` ErrorClass string `msgpack:"error_class"` ErrorMessage string `msgpack:"error_message"`}//登出申请type logoutReq struct { _msgpack struct{} `msgpack:",asArray"` Method string Token string LogoutToken string}// 登出响应type logoutRes struct { Result string `msgpack:"result"`}// 通用信息type Msf struct { host string user string pass string token string}// 初始化func New(host, user, pass string) (*Msf, error) { rtn := &Msf{ host: host, user: user, pass: pass, } if err := rtn.Login(); err != nil { return nil, err } return rtn, nil}func (msf *Msf) send(req interface{}, res interface{}) error { buf := new(bytes.Buffer) //https://blog.csdn.net/flyfreelyit/article/details/80291945 bytes.Buffer 应用 // encodereq放到buf中 msgpack.NewEncoder(buf).Encode(req) dst := fmt.Sprintf("http://%s/api", msf.host) resp, err := http.Post(dst, "binary/message-pack", buf) if err != nil { log.Printf("%s", err) return err } defer resp.Body.Close() if err = msgpack.NewDecoder(resp.Body).Decode(res); err != nil { log.Printf("%s", err) return err } fmt.Println(res) return nil}func (msf *Msf) Login() error { ctx := &loginReq{ Method: "auth.login", Username: msf.user, Pass: msf.pass, } var res loginRes // send 的第二个参数为interface 能够接管任何类型 if err := msf.send(ctx, &res); err != nil { log.Printf("%s", err) return err } msf.token = res.Token return nil}func (msf *Msf) Logout() error { ctx := &logoutReq{ Method: "auth.logout", Token: msf.token, LogoutToken: msf.token, } var res logoutRes if err := msf.send(ctx, &res); err != nil { log.Println(err) return err } msf.token = "" return nil}func (msf *Msf) SessionList() (map[uint32]SessionListRes, error) { req := &SessionListReq{ Method: "session.list", Token: msf.token, } res := make(map[uint32]SessionListRes) if err := msf.send(req, &res); err != nil { log.Fatal(err) return nil, err } for id, session := range res { session.ID = id res[id] = session } return res, nil}

September 6, 2022 · 2 min · jiezi

关于go:游戏服务器AOI兴趣点算法原理四叉树与九宫格-golang

定义获取感兴趣的区域(Area Of Interest)的算法。次要用于罕用的游戏服务器压力,升高网络风暴的可能。 常见的AOI算法有九宫格,四叉树,灯塔,十字链表等算法。本文次要举例九宫格和四叉树两种算法的golang版本实现。 九宫格九宫格能够说是最容易了解的一种AOI趣味算法。 举例: 世界坐标是X[20,200],Y[50,230],划分成6×6的网格为: 从图可看出,九宫格是把地图依据x,y轴坐标划分为等比例的一张网格地图,每个格子带有一个id编号,当玩家每次挪动后依据玩家坐标将玩家置入到相应的格子中,通过把玩家在内的九个格子 的所有玩家数据取出失去趣味用户。 数据结构type Grid struct { GID int //格子ID Entities sync.Map //以后格子内的实体} type GridManger struct { StartX int // X区域左边界坐标 StartY int // Y区域上边界坐标 AreaWidth int // 格子宽度(长=宽) GridCount int // 格子数量 grids map[int]*Grid pool sync.Pool}通过横纵坐标获取对应的格子IDfunc (g *GridManger) getGIDByPos(x, y float64) int { gx := (int(x) - g.StartX) / g.gridWidth() gy := (int(y) - g.StartY) / g.gridWidth() return gy*g.GridCount + gx}依据格子的gID失去以后周边的九宫格信息func (g *GridManger) getSurroundGrids(gID int) []*Grid { grids := g.pool.Get().([]*Grid) defer func() { grids = grids[:0] g.pool.Put(grids) }() if _, ok := g.grids[gID]; !ok { return grids } grids = append(grids, g.grids[gID]) // 依据gID, 失去格子所在的坐标 x, y := gID%g.GridCount, gID/g.GridCount for i := 0; i < 8; i++ { newX := x + dx[i] newY := y + dy[i] if newX >= 0 && newX < g.GridCount && newY >= 0 && newY < g.GridCount { grids = append(grids, g.grids[newY*g.GridCount+newX]) } } return grids}增删查func (g *GridManger) Add(x, y float64, key string) { entity := entityPool.Get().(*Entity) entity.X = x entity.Y = y entity.Key = key ID := g.getGIDByPos(x, y) grid := g.grids[ID] grid.Entities.Store(key, entity)} func (g *GridManger) Delete(x, y float64, key string) { ID := g.getGIDByPos(x, y) grid := g.grids[ID] if entity, ok := grid.Entities.Load(key); ok { grid.Entities.Delete(key) entityPool.Put(entity) }} func (g *GridManger) Search(x, y float64) []string { result := resultPool.Get().([]string) defer func() { result = result[:0] resultPool.Put(result) }() ID := g.getGIDByPos(x, y) grids := g.getSurroundGrids(ID) for _, grid := range grids { grid.Entities.Range(func(_, value interface{}) bool { result = append(result, value.(*Entity).Key) return true }) } return result}四叉树能够显著看到九宫格算法须要一次性开拓出所有的网格,无论格子中是否存在肯定数量的玩家。当一次性呈现陈千上万的网格,对服务端的资源节约可想而知。相似的算法与灯塔算法亦是如此。当然也有一些算法对此做了优化但终有取舍。 ...

September 6, 2022 · 3 min · jiezi

关于go:Go-clientSet-Watch-运行后随机性失效

背景List 和 Watch 机制是 kubernetes 中重要的机制之一。控制器通过 API Server 的 List API 来获取所有最新版本的 API 对象,通过 Watch API 来监听所有 API 对象的变动。在程序设计过程中,往往也须要利用 List && Watch 机制,来察看 API 对象的状态,从而调用 EventHandler,做出响应。基于此背景,Go 语言官网的 clientSet 中提供了相应的 API 接口供开发者应用。然而,笔者在应用 Watch 机制中踩到了不小坑。 问题笔者在程序中创立 clientSet,并调用其 Watch 办法,监听某 Secret 资源变动,伪代码如下: secretWatch, err := clientSet.CoreV1().Secrets("命名空间").Watch(context.TODO(), metav1.ListOptions{FieldSelector: fields.OneTermEqualSelector("metadata.name", "API 对象名").String()})for { for range secretWatch.ResultChan() { // 响应操作 }}笔者在启动后,通过几番调试的确能够监听到信息,安心提交。然而,通过一段运行工夫后,Watch 机制忽然失灵,而且无奈复原。本地 DeBug 也始终找不到异样点。 解决方案问题剖析clientSet 的 Watch 接口缺失可能监听了须要的信息,然而其难以解决各类异样。如下源码所示,因而当异样产生时,Watch 会主动敞开。 // Interface can be implemented by anything that knows how to watch and report changes.type Interface interface { // Stops watching. Will close the channel returned by ResultChan(). Releases // any resources used by the watch. Stop() // Returns a chan which will receive all the events. If an error occurs // or Stop() is called, this channel will be closed, in which case the // watch should be completely cleaned up. !!!明确说了在呈现谬误或者被调用Stop时,通道会主动敞开的 ResultChan() <-chan Event}解决方案应用应用 RetryWatcher 来替换传统 Watcher,简略来说,此类 RetryWatcher 可能保障 Watch 主动敞开后,从新拉起一个 Watcher,使得程序持续失常运行。应用办法如下: ...

September 6, 2022 · 3 min · jiezi

关于go:go安全开发二实现tcp代理

git地址 假如 用户和dst不通,然而部署这个服务的设施和dst通,将这台设施收到的内容发送给dst,将dst返回的内容,返回给客户 用到的go根底库 net.Listen 和listener.Accept 建设本地的tcp连贯net.Dail() 向远端发动tcp申请package mainimport ( "io" "log" "net")const dst_host = "www.baidu.com:80"// 假如 用户和dst不通,然而部署这个服务的设施和dst通,将这台设施收到的内容发送给dst,将dst返回的内容,返回给客户func main() { listener, err := net.Listen("tcp", ":80") if err != nil { log.Fatal("谬误", err) } for { conn, err := listener.Accept() if err != nil { log.Fatalln("accept出错", err) } handler(conn) }}func handler(src net.Conn) { dst, err := net.Dial("tcp", dst_host) if err != nil { log.Fatalln("连贯出错", err) } defer dst.Close() // 避免阻塞,应用协程 go func() { // 将源的内容发送给dst _, err := io.Copy(dst, src) if err != nil { log.Fatalln("源到目标copy出错", err) } }() _, err = io.Copy(src, dst) if err != nil { log.Fatalln("目标到源copy出错", err) }}

September 6, 2022 · 1 min · jiezi

关于go:Go设计模式单例模式

单例模式确保任何状况下一个类只有一个实例通常而言, 单例实例会在构造体首次初始化时创立。 为了实现这一操作, 咱们在构造体中定义一个 get­Instance获取实例办法。 该办法将负责创立和返回单例实例。 创立后, 每次调用 get­Instance时都会返回雷同的单例实例。 协程方面又有什么须要留神的吗? 每当多个协程想要拜访实例时, 单例构造体就必须返回雷同的实例。 正因如此, 单例设计模式的施行工作很容易出错。 饿汉模式在初始阶段创立实例而不是在应用时创立实例。不须要加锁,更平安;然而当程序中不应用该实例会节约空间,减慢启动速度。 // 应用全局变量type single struct {}var instance = new(single)func GetInstance() *single{ return instance}// init函数创立实例// 咱们能够在 init函数中创立单例实例。 这仅实用于实例的晚期初始化工作曾经确定时。init函数仅会在包中的每个文件里调用一次, 所以咱们能够确定其只会创立一个实例。type single struct {}var instance *singlefunc init() { instance = new(single)}func GetInstance() *single{ return instance}懒汉模式sync.Once举荐应用package mainimport ( "fmt" "sync")var once sync.Oncetype single struct {}var singleInstance *singlefunc getInstance() *single { if singleInstance == nil { once.Do( func() { fmt.Println("Creating single instance now.") singleInstance = &single{} }) } else { fmt.Println("Single instance already created.") } return singleInstance}func main() { for i := 0; i < 30; i++ { go getInstance() } // Scanln is similar to Scan, but stops scanning at a newline and // after the final item there must be a newline or EOF. fmt.Scanln()}双重检测最开始时会有 nil查看, 确保 sin­gle­Instance单例实例在最开始时为空。 这是为了避免在每次调用 get­Instance办法时都去执行耗费微小的锁定操作。 如果查看不通过, 则就意味着 sin­gle­Instance字段已被填充。sin­gle­Instance构造体将在锁定期间创立。在获取到锁后还会有另一个 nil查看。 这是为了确保即使是有多个协程绕过了第一次查看, 也只能有一个能够创立单例实例。 否则, 所有协程都会创立本人的单例构造体实例。package mainimport ( "fmt" "sync")var lock = &sync.Mutex{}type single struct {}var singleInstance *singlefunc getInstance() *single { if singleInstance == nil { lock.Lock() defer lock.Unlock() if singleInstance == nil { fmt.Println("Creating single instance now.") singleInstance = &single{} } else { fmt.Println("Single instance already created.") } } else { fmt.Println("Single instance already created.") } return singleInstance}func main() { for i := 0; i < 30; i++ { go getInstance() } // Scanln is similar to Scan, but stops scanning at a newline and // after the final item there must be a newline or EOF. fmt.Scanln()}简略工厂模式

September 5, 2022 · 2 min · jiezi

关于go:atomic

atomicatomic 操作的对象是一个地址,你须要把可寻址的变量的地址作为参数传递给办法,而不是把变量的值传递给办法。 Add// AddInt32 atomically adds delta to *addr and returns the new value.// AddInt32 以原子形式将 delta 增加到 *addr 并返回新值。func AddInt32(addr *int32, delta int32) (new int32)// AddUint32 atomically adds delta to *addr and returns the new value.// To subtract a signed positive constant value c from x, do AddUint32(&x, ^uint32(c-1)).// In particular, to decrement x, do AddUint32(&x, ^uint32(0)).// AddUint32 以原子形式将 delta 增加到 *addr 并返回新值。//要从 x 中减去一个有符号的失常数值 c,请执行 AddUint32(&x, ^uint32(c-1))。//特地是,要减1,请执行 AddUint32(&x, ^uint32(0))// AddInt64 atomically adds delta to *addr and returns the new value.func AddInt64(addr *int64, delta int64) (new int64)// AddUint64 atomically adds delta to *addr and returns the new value.// To subtract a signed positive constant value c from x, do AddUint64(&x, ^uint64(c-1)).// In particular, to decrement x, do AddUint64(&x, ^uint64(0)).func AddUint64(addr *uint64, delta uint64) (new uint64)// AddUintptr atomically adds delta to *addr and returns the new value.func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)CAS// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)// CompareAndSwapInt64 executes the compare-and-swap operation for an int64 value.func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)// CompareAndSwapUint32 executes the compare-and-swap operation for a uint32 value.// CompareAndSwapInt32 对 int32 值执行比拟和替换操作。func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)// CompareAndSwapUint64 executes the compare-and-swap operation for a uint64 value.func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)// CompareAndSwapUintptr executes the compare-and-swap operation for a uintptr value.func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)// CompareAndSwapPointer executes the compare-and-swap operation for a unsafe.Pointer value.func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)比拟以后 addr 地址里的值是不是 old,如果不等于 old,就返回 false;如果等于 old,就把此地址的值替换成 new 值,返回 true。 ...

September 5, 2022 · 11 min · jiezi

关于go:syncMap

sync.Map数据结构type Map struct { // 互斥锁 mu Mutex // 只读,原子的操作read read atomic.Value // readOnly // 加锁才能够拜访 dirty map[any]*entry // 记录未命中次数,当未命中次数等于dirty长度时,将dirty晋升为ready misses int}// readOnly 是一个不可变构造,以原子形式存储在 Map.read 字段中type readOnly struct { m map[any]*entry // dirty中有不在read中的key ===> amended = true amended bool // true if the dirty map contains some key not in m.}// expunged 是一个任意指针,用于标记从dirty map中已删除的条目// 在map中删除一个条目只是将其标记为expunge,并不是真正的删除吗,期待有机会在删除var expunged = unsafe.Pointer(new(any))// entry是map中对应于特定key的槽。type entry struct { // p指向为entry存储的 interface{} 值。 // 如果 p == nil,则该entry已被删除,并且 m.dirty == nil或m.dirty[key]=e。 // 如果 p == expunged,则该entry已被删除,m.dirty != nil,并且m.dirty 中短少该entry(dirty != nil, and the entry is missing from m.dirty)。 // 否则,该entry无效并记录在 m.read.m[key] 中,如果是 m.dirty!= nil,在 m.dirty[key] 中。 // 能够通过用 nil 原子替换来删除entry:当 m.dirty 为下一次创立,它会原子地用 expunged 替换 nil 并来到m.dirty[key] 未设置。 // 一个entry的关联值能够通过原子替换来更新,前提是p != expunged。如果 p == expunged,则在第一次设置 m.dirty[key] = e 之后才应用dirty找到entry更新entry p unsafe.Pointer // *interface{}}newEntryfunc newEntry(i any) *entry { return &entry{p: unsafe.Pointer(&i)}}Load//Load 返回存储在 map 中的 key 值,如果没有值存在则返回 nil//ok后果示意是否在map中找到值。func (m *Map) Load(key any) (value any, ok bool) { // 从read开始,read不须要锁 read, _ := m.read.Load().(readOnly) e, ok := read.m[key] // read中不存在key并且dirty中存在key if !ok && read.amended { // dirty 加锁 m.mu.Lock() // 双重检测 // 防止在load时有其余协程写入数据而本人没有感知到,造成虚伪的未命中 read, _ = m.read.Load().(readOnly) e, ok = read.m[key] if !ok && read.amended { e, ok = m.dirty[key] // 不论entry是否存在,记录一次未命中(miss + 1):this key将采纳慢速门路,直到将dirty map晋升为read map。 m.missLocked() } m.mu.Unlock() } // 在read map和dirty map都未找到key,返回nil和false if !ok { return nil, false } // 返回读取到的对象 return e.load()}func (e *entry) load() (value any, ok bool) { // LoadPointer 以原子形式加载 *addr // func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) p := atomic.LoadPointer(&e.p) // entry被删除 if p == nil || p == expunged { return nil, false } return *(*any)(p), true}func (m *Map) missLocked() { // 未命中次数加一 m.misses++ // 如果未命中次数小于dirty长度间接返回 if m.misses < len(m.dirty) { return } // 把dirty晋升为read m.read.Store(readOnly{m: m.dirty}) // 清空dirty m.dirty = nil // misses置为0 m.misses = 0}Store// 设置键值对func (m *Map) Store(key, value any) { read, _ := m.read.Load().(readOnly) // read中存在key,更新值 if e, ok := read.m[key]; ok && e.tryStore(&value) { return } // read中不存在key或cas更新失败,加锁拜访dirty m.mu.Lock() read, _ = m.read.Load().(readOnly) // 双重检测,read中是否存在key if e, ok := read.m[key]; ok { // (p->expunged) ==> (p->nil) ==> (p->失常) if e.unexpungeLocked() { // The entry was previously expunged, which implies that there is a // non-nil dirty map and this entry is not in it. // 此我的项目先前曾经被删除了,通过将它的值设置为nil,标记为unexpunged // 勾销删除标记 m.dirty[key] = e } // 更新 e.storeLocked(&value) } else if e, ok := m.dirty[key]; ok { // 在dirty map中间接更新 e.storeLocked(&value) } else { // 新建一个键值对 if !read.amended { // We're adding the first new key to the dirty map. // Make sure it is allocated and mark the read-only map as incomplete. // 须要创立dirty对象,并且标记read的amended为true,阐明有元素它不蕴含而dirty蕴含 m.dirtyLocked() // (p->nil) ==> (p->expunged) m.read.Store(readOnly{m: read.m, amended: true}) } // 将新值减少到dirty对象中 m.dirty[key] = newEntry(value) } m.mu.Unlock()}// tryStore stores a value if the entry has not been expunged.//// If the entry is expunged, tryStore returns false and leaves the entry// unchanged.func (e *entry) tryStore(i *any) bool { for { p := atomic.LoadPointer(&e.p) if p == expunged { return false } if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { return true } }}// unexpungeLocked ensures that the entry is not marked as expunged.//// If the entry was previously expunged, it must be added to the dirty map// before m.mu is unlocked.// unexpungeLocked 确保entry未被标记为已删除。//如果该entry之前已被革除,则必须在 m.mu 解锁之前将其增加到dirty map中func (e *entry) unexpungeLocked() (wasExpunged bool) { return atomic.CompareAndSwapPointer(&e.p, expunged, nil)}// storeLocked unconditionally stores a value to the entry.//// The entry must be known not to be expunged.//storeLocked 无条件地将值存储到条目中。////必须晓得该条目不会被删除。func (e *entry) storeLocked(i *any) { atomic.StorePointer(&e.p, unsafe.Pointer(i))}func (m *Map) dirtyLocked() { if m.dirty != nil { return } read, _ := m.read.Load().(readOnly) m.dirty = make(map[any]*entry, len(read.m)) for k, e := range read.m { if !e.tryExpungeLocked() { m.dirty[k] = e } }}func (e *entry) tryExpungeLocked() (isExpunged bool) { p := atomic.LoadPointer(&e.p) for p == nil { if atomic.CompareAndSwapPointer(&e.p, nil, expunged) { return true } p = atomic.LoadPointer(&e.p) } return p == expunged}Delete// Delete deletes the value for a key.func (m *Map) Delete(key any) { m.LoadAndDelete(key)}// LoadAndDelete deletes the value for a key, returning the previous value if any.// The loaded result reports whether the key was present.func (m *Map) LoadAndDelete(key any) (value any, loaded bool) { read, _ := m.read.Load().(readOnly) e, ok := read.m[key] // 双重检测 if !ok && read.amended { m.mu.Lock() read, _ = m.read.Load().(readOnly) e, ok = read.m[key] if !ok && read.amended { e, ok = m.dirty[key] delete(m.dirty, key) // Regardless of whether the entry was present, record a miss: this key // will take the slow path until the dirty map is promoted to the read // map. m.missLocked() } m.mu.Unlock() } if ok { return e.delete() } return nil, false}func (e *entry) delete() (value any, ok bool) { for { p := atomic.LoadPointer(&e.p) if p == nil || p == expunged { return nil, false } // (p->失常) ==> (p->nil) if atomic.CompareAndSwapPointer(&e.p, p, nil) { return *(*any)(p), true } }}状态value 状态:entry 外面寄存的是真正的值。此时 entry 对应的 key,有三种状况,key 只在 read 中均存在;key 只在 dirty 中存在;在 read 和 dirty 中均存在。nil 状态:read 中的 key 被删除的时候将其对应的 value(即 entry) p 值设置为 nil。此时 key 只存在 read 中 (ditry 为 nil);在 read 和 dirty 中均存在。expunge 状态: 此时 key 只存在 read 中而在 dirty 中不存在。将 sync.Map 形象为 存、取、删除 三种操作。在这三种操作中来看 entry 状态的扭转。 1、存:对于初始状态的 sync.Map, 一个新键值对 k: A v: A, 首先寄存在 dirty 中,此时 entry 为 value 状态,key 只存在 dirty 中。 2、取:首先 read 中不存在,从 dirty 中取值。数次从 read 中取不中,将 dirty 晋升为 read, dirty 被清空。entry 仍为 value 状态,对应的 key 存在于 read 中。 3、存:另一键值对k:B v:B,存入时,产生 read 向 dirty 拷贝,同时 read 的 amended 标记为 true。此时 key A 存在于 read 和 dirty 中,entry 为 value 状态。 4、删除:删除 key A,A 的 entry 中 p 值被设置为 nil。此时 key A 存在于 read 和 dirty 中,entry 为 nil 状态。 5、存:另一键值对k:C v:C。 6、取:屡次读取 key C,触发 dirty 晋升为 read,dirty 被清空。此时 key A 只存在于 read 中, entry 为 nil 状态。 7、存:另一键值对 k:D v:D, 存入时,产生 read 向 dirty 拷贝,key A 的 entry 由 nil 状态转变为 expunge 状态,key A 只存在于 read 中。 8、取:屡次读取 key D,触发 dirty 晋升为 read, dirty 被清空,此时 key A 被齐全删除。 ...

September 5, 2022 · 5 min · jiezi

关于go:syncOnce

OnceOnce 能够用来执行且仅仅执行一次动作,经常用于单例对象的初始化场景。 once可用于实现单例模式中懒汉实现: package mainimport ( "fmt" "sync")var once sync.Oncetype single struct {}var singleInstance *singlefunc getInstance() *single { if singleInstance == nil { once.Do( func() { fmt.Println("Creating single instance now.") singleInstance = &single{} }) } else { fmt.Println("Single instance already created.") } return singleInstance}func main() { for i := 0; i < 30; i++ { go getInstance() } // Scanln is similar to Scan, but stops scanning at a newline and // after the final item there must be a newline or EOF. fmt.Scanln()}Once构造体type Once struct { done uint32 // done 批示操作是否已执行 m Mutex}Dofunc (o *Once) Do(f func())sync.Once 只裸露了一个办法 Do,你能够屡次调用 Do 办法,然而只有第一次调用 Do办法时 f 参数才会执行,这里的 f 是一个无参数无返回值的函数。因为当且仅当第一次调用 Do 办法的时候参数 f 才会执行,即便第二次、第三次、第 n 次调用时 f 参数的值不一样,也不会被执行。 ...

September 5, 2022 · 1 min · jiezi

关于go:条件变量-Cond

条件变量 CondGo 规范库提供 Cond 原语的目标是,为期待 / 告诉场景下的并发问题提供反对。Cond 通常利用于期待某个条件的一组 goroutine,等条件变为 true 的时候,其中一个 goroutine或者所有的 goroutine 都会被唤醒执行。 顾名思义,Cond 是和某个条件相干,这个条件须要一组 goroutine 合作共同完成,在条件还没有满足的时候,所有期待这个条件的 goroutine 都会被阻塞住,只有这一组goroutine 通过合作达到了这个条件,期待的 goroutine 才可能持续进行上来。那这里期待的条件是什么呢?期待的条件,能够是某个变量达到了某个阈值或者某个工夫点,也能够是一组变量别离都达到了某个阈值,还能够是某个对象的状态满足了特定的条件。总结来讲,期待的条件是一种能够用来计算结果是 true 还是 false 的条件 应用condfunc NeWCond(l Locker) *Condfunc (c *Cond) Broadcast()func (c *Cond) Signal()func (c *Cond) Wait()首先,Cond 关联的 Locker 实例能够通过 c.L 拜访,它外部保护着一个先入先出的期待队列。 Signal 办法,容许调用者 Caller 唤醒一个期待此 Cond 的 goroutine。如果此时没有等待的 goroutine,显然无需告诉 waiter;如果 Cond 期待队列中有一个或者多个期待的goroutine,则须要从期待队列中移除第一个 goroutine 并把它唤醒 调用 Signal 办法时,不强求你肯定要持有 c.L 的锁。 Broadcast 办法,容许调用者 Caller 唤醒所有期待此 Cond 的 goroutine。如果此时没有期待的 goroutine,显然无需告诉 waiter;如果 Cond 期待队列中有一个或者多个期待的goroutine,则清空所有期待的 goroutine,并全副唤醒 ...

September 5, 2022 · 1 min · jiezi

关于go:WaitGroup

WaitGroup:协同期待,工作编排利器func (wg *WaitGroup) Add(delta int)func (wg *WaitGroup) Done()func (wg *WaitGroup) Wait()WaitGroup编排须要启动多个 goroutine 执行工作,主 goroutine 须要期待子 goroutine 都实现后才继续执行的工作。 type WaitGroup struct { noCopy noCopy state1 uint64 state2 uint32}state// state 返回指向存储在 wg.state*中的 state 和 sema 字段的指针。func (wg *WaitGroup) state() (statep *uint64, semap *uint32) { if unsafe.Alignof(wg.state1) == 8 || uintptr(unsafe.Pointer(&wg.state1))%8 == 0 { // state1 is 64-bit aligned: nothing to do. return &wg.state1, &wg.state2 } else { // state1 is 32-bit aligned but not 64-bit aligned: this means that // (&state1)+4 is 64-bit aligned. state := (*[3]uint32)(unsafe.Pointer(&wg.state1)) return (*uint64)(unsafe.Pointer(&state[1])), &state[0] }}Addfunc (wg *WaitGroup) Add(delta int) { statep, semap := wg.state() if race.Enabled { _ = *statep // trigger nil deref early if delta < 0 { // Synchronize decrements with Wait. race.ReleaseMerge(unsafe.Pointer(wg)) } race.Disable() defer race.Enable() } // 给counter加delta state := atomic.AddUint64(statep, uint64(delta)<<32) v := int32(state >> 32) // 以后计数值 w := uint32(state) // waiter数 if race.Enabled && delta > 0 && v == int32(delta) { // The first increment must be synchronized with Wait. // Need to model this as a read, because there can be // several concurrent wg.counter transitions from 0. race.Read(unsafe.Pointer(semap)) } if v < 0 { panic("sync: negative WaitGroup counter") } if w != 0 && delta > 0 && v == int32(delta) { panic("sync: WaitGroup misuse: Add called concurrently with Wait") } if v > 0 || w == 0 { return } // This goroutine has set counter to 0 when waiters > 0. // Now there can't be concurrent mutations of state: // - Adds must not happen concurrently with Wait, // - Wait does not increment waiters if it sees counter == 0. // Still do a cheap sanity check to detect WaitGroup misuse. if *statep != state { panic("sync: WaitGroup misuse: Add called concurrently with Wait") } // Reset waiters count to 0. // 以后计数值=0,并且w!=0 ==> state的就是waiter的数量 *statep = 0 // 唤醒所有sema中协程 for ; w != 0; w-- { runtime_Semrelease(semap, false, 0) }}waitfunc (wg *WaitGroup) Wait() { statep, semap := wg.state() if race.Enabled { _ = *statep // trigger nil deref early race.Disable() } for { state := atomic.LoadUint64(statep) v := int32(state >> 32) // 以后计数值 w := uint32(state) // waiter数量 if v == 0 { // Counter is 0, no need to wait. if race.Enabled { race.Enable() race.Acquire(unsafe.Pointer(wg)) } return } // Increment waiters count. if atomic.CompareAndSwapUint64(statep, state, state+1) { if race.Enabled && w == 0 { // Wait must be synchronized with the first Add. // Need to model this is as a write to race with the read in Add. // As a consequence, can do the write only for the first waiter, // otherwise concurrent Waits will race with each other. race.Write(unsafe.Pointer(semap)) } // 阻塞休眠期待 runtime_Semacquire(semap) if *statep != 0 { panic("sync: WaitGroup is reused before previous Wait has returned") } if race.Enabled { race.Enable() race.Acquire(unsafe.Pointer(wg)) } return } }}Donefunc (wg *WaitGroup) Done() { wg.Add(-1)}

September 5, 2022 · 3 min · jiezi

关于go:读写锁RWMutex

读写锁RWMutextype RWMutex struct { w Mutex // 互斥锁解决多个writer的竞争 writerSem uint32 // writer信号量 readerSem uint32 // reader信号量 readerCount int32 // reader的数量 readerWait int32 // writer期待实现的reader的数量}// 最大的 reader 数量。const rwmutexMaxReaders = 1 << 30RLock/RUnlock当 writer 申请锁的时候,是无奈扭转既有的reader 持有锁的事实的,也不会强制这些 reader 开释锁,它的优先权只是限定起初的reader 不要和它抢。所以,rUnlockSlow 将持有锁的 reader 计数缩小 1 的时候,会查看既有的 reader 是不是都曾经开释了锁,如果都开释了锁,就会唤醒 writer,让 writer 持有锁。 func (rw *RWMutex) RLock() { if race.Enabled { _ = rw.w.state race.Disable() } if atomic.AddInt32(&rw.readerCount, 1) < 0 { // A writer is pending, wait for it. runtime_SemacquireMutex(&rw.readerSem, false, 0) } if race.Enabled { race.Enable() race.Acquire(unsafe.Pointer(&rw.readerSem)) }}func (rw *RWMutex) RUnlock() { if race.Enabled { _ = rw.w.state race.ReleaseMerge(unsafe.Pointer(&rw.writerSem)) race.Disable() } if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { // Outlined slow-path to allow the fast-path to be inlined rw.rUnlockSlow(r) } if race.Enabled { race.Enable() }}func (rw *RWMutex) rUnlockSlow(r int32) { if r+1 == 0 || r+1 == -rwmutexMaxReaders { race.Enable() fatal("sync: RUnlock of unlocked RWMutex") } // A writer is pending. if atomic.AddInt32(&rw.readerWait, -1) == 0 { // The last reader unblocks the writer. runtime_Semrelease(&rw.writerSem, false, 1) }}Lock一旦一个 writer 取得了外部的互斥锁,就会反转 readerCount 字段,把它从原来的正整数 readerCount(>=0) 批改为正数(readerCount-rwmutexMaxReaders),让这个字段放弃两个含意(既保留了 reader 的数量,又示意以后有 writer)。 ...

September 5, 2022 · 3 min · jiezi

关于go:LRU算法go版本

package LRUtype LRUCache struct { head *Node tail *Node Map map[int]*Node cap int}type Node struct { key int value int next *Node prev *Node}func newNode(k, v int) *Node { return &Node{ key: k, value: v, }}func Constructor(capacity int) LRUCache { l := LRUCache{ head: newNode(0, 0), tail: newNode(0, 0), Map: make(map[int]*Node), cap: capacity, } l.head.next = l.tail l.tail.prev = l.head return l}func (this *LRUCache) Get(key int) int { v, ok := this.Map[key] if !ok { return -1 } this.moveToTail(v, v.value) return v.value}func (this *LRUCache) Put(key int, value int) { _, ok := this.Map[key] if ok { this.moveToTail(this.Map[key], value) } else { if len(this.Map) == this.cap { toBeDelete := this.head.next this.deleteNode(toBeDelete) delete(this.Map, toBeDelete.key) } node := newNode(key, value) this.Map[key] = node this.insertToTail(node) }}func (this *LRUCache) moveToTail(node *Node, newValue int) { this.deleteNode(node) node.value = newValue this.insertToTail(node)}func (this *LRUCache) deleteNode(node *Node) { node.prev.next = node.next node.next.prev = node.prev}func (this *LRUCache) insertToTail(node *Node) { this.tail.prev.next = node node.prev = this.tail.prev node.next = this.tail this.tail.prev = node}

September 5, 2022 · 1 min · jiezi

关于go:go实现归并排序

package mergeSortfunc mergeSort(nums []int) []int { if len(nums) < 2 { return nums } m := len(nums) / 2 l := mergeSort(nums[:m]) r := mergeSort(nums[m:]) return merge(l, r)}func merge(left, right []int) []int { i, j := 0, 0 n, m := len(left), len(right) reslet := make([]int, n+m) for i < n && j < m { if left[i] <= right[j] { reslet[i+j] = left[i] i++ } else { reslet[i+j] = right[j] j++ } } for i < n { reslet[i+j] = left[i] i++ } for j < m { reslet[i+j] = right[j] j++ } return reslet}package mergeSortimport ( "math/rand" "testing" "time")func TestMergeSort(t *testing.T) { var input [100]int r := rand.New(rand.NewSource(time.Now().Unix())) // 初始化数组input[] for i := 0; i < 100; i++ { input[i] = r.Intn(1000) } output := mergeSort(input[:]) for i := 0; i <= 10; i++ { println(output[i]) }}

September 5, 2022 · 1 min · jiezi

关于go:go实现插入排序

package InsertionSortfunc insertionSort(nums []int) { for j := 1; j < len(nums); j++ { key := nums[j] // insert A[j] into the sorted sequence A[0..j-1] // A[0..j-1]升序排序 i := j - 1 // key比曾经排好序的A[0..j-1]小,将key插入适宜的地位 // 当找到适宜的地位退出循环 for i >= 0 && nums[i] > key { // 整体向右挪动yiwei nums[i+1] = nums[i] i-- } // A[i+1]为空缺地位 nums[i+1] = key }}package InsertionSortimport ( "math/rand" "testing" "time")func TestInsertionSort(t *testing.T) { var input [100]int r := rand.New(rand.NewSource(time.Now().Unix())) // 初始化数组input[] for i := 0; i < 100; i++ { input[i] = r.Intn(1000) } insertionSort(input[:]) for i := 0; i < 10; i++ { println(input[i]) }}

September 5, 2022 · 1 min · jiezi

关于go:关键字和标识符

关键字和标识符关键字关键字是一些非凡的用来帮忙编译器了解和解析源代码的单词。 截止目前(Go 1.17),Go中共有25个关键字。 break default func interface selectcase defer go map structchan else goto package switchconst fallthrough if range typecontinue for import return var标识符无效的标识符必须以字母(能够应用任何 UTF-8 编码的字符或 _)结尾,而后紧跟着 0 个或多个字符或 Unicode 数字,如:X56、group1、_x23、i、12。 以下是有效的标识符: 1ab(以数字结尾)case(Go 语言的关键字)a+b(运算符是不容许的)注意事项 Go语言对大小写敏感。(go辨别大小写,Name与name是两个不同的标识符。go标识符以大写结尾是导出标识符(可认为是Public)非大写字母结尾的是非导出标识符)关键字不可能作标识符应用。标识符_是一个特殊字符,它叫做空标识符。它能够像其余标识符那样用于变量的申明或赋值(任何类型都能够赋值给它),但任何赋给这个标识符的值都将被摈弃,因而这些值不能在后续的代码中应用,也不能够应用这个标识符作为变量对其它变量进行赋值或运算。一个由Unicode大写字母结尾的标识符称为导出标识符。 这里导出能够被了解为公开(public)。 其它(即非Unicode大写字母结尾的)标识符称为非导出标识符。非导出能够被了解为公有(private)。 截至目前(Go 1.17),西方字符都被视为非导出字符。非导出有时候也被称为未导出。

September 5, 2022 · 1 min · jiezi

关于go:空结构体

空构造体int字节大小随零碎变动而变动指针字节大小随零碎变动而变动空构造体的字节为零type zeroStruct struct{}func main(){ a := zeroStruct fmt.Println(unsafe.Szieof(a)) //0 fmt.Printf("%p",&a) //0x8a82f8 }所有独立的空构造体的地址对立为zerobase(不蕴含在其余构造体中)

September 5, 2022 · 1 min · jiezi

关于go:slice底层实现

切片 slicetype slice struct { // 指向底层数组的指针 array unsafe.Pointer // slice援用底层数组的长度 len int // 底层数组的长度 cap int} slice的创立// 依据数组创立arr[0:3]slice[0:3]// 字面量创立slice := []int{1,2,3}// makeslice := make([]int,3)字面量创立切片时先创立数组再将调用runtime.newobject 创立slice构造体 # 字面量创立LEAQ type.[3]int(SB), AXPCDATA $1, $0NOPCALL runtime.newobject(SB)MOVQ $1, (AX)MOVQ $2, 8(AX)MOVQ $3, 16(AX)make创立切片时间接调用slice.makeslice LEAQ type.int(SB), AXMOVL $3, BXMOVQ BX, CXPCDATA $1, $0CALL runtime.makeslice(SB)func makeslice(et *_type, len, cap int) unsafe.Pointer { // func math.MulUintptr(a uintptr, b uintptr) (uintptr, bool) // MulUintptr 返回a *b 以及乘法是否溢出。 在受反对的平台上,这是编译器升高的外在属性。 mem, overflow := math.MulUintptr(et.size, uintptr(cap)) // maxAlloc 是调配的最大大小。在 64 位上, 实践上能够调配 1<<heapAddrBits bytes。在32位上,它比 1<<32 小1,因为地址空间中的字节数实际上不适宜在uintptr. if overflow || mem > maxAlloc || len < 0 || len > cap { //留神:产生“len out of range”谬误而不是"cap out of range" //当有人 make([]T, bignumber) 时呈现“下限超出范围”谬误。 //“下限超出范围”也是如此,但因为下限只是 //隐含地提供,说 len 更分明。 mem, overflow := math.MulUintptr(et.size, uintptr(len)) if overflow || mem > maxAlloc || len < 0 { panicmakeslicelen() } panicmakeslicecap() } // func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer // 调配一个大小为 bytes的对象。 小对象是从 per-P 缓存的闲暇列表中调配的。 大对象 (> 32 kB) 间接从堆中调配。 return mallocgc(mem, et, true)}示例: ...

September 5, 2022 · 2 min · jiezi

关于go:new与make的区别

new()与make()的区别// new()分配内存。第一个参数是一个类型,不是一个值,返回的值是一个指向新调配该类型零值的指针func new(Type) *Type// make()调配并初始化一个类型的对象,仅限slice,map,chan。// 和new() 一样,第一个参数是一个类型,而不是一个值// 与new()不同,make()的返回类型与它的参数类型雷同// 而不是指向它的指针。后果的规格取决于类型。// slice:大小能够指定长度。切片的容量等于它的长度。// 第二个整数参数能够指定不同的容量;它必须不小于长度// 例如,make([]int, 0, 10) 调配一个大小为10底层数组// 并返回长度为 0、容量为 10 的切片。// Map:一个空的map被调配了足够的空间来寄存指定数量的元素// 大小能够省略,在这种状况下调配一个小的起始大小// Channel: 应用指定的缓冲容量初始化通道的缓冲区(有缓冲)// 如果为零,或省略大小,则channel为无缓冲func make(t Type, size ...IntegerType) Type

September 5, 2022 · 1 min · jiezi

关于go:互斥锁Mutex

互斥锁Mutex应用锁爱护共享资源 Locker()是一个interface。 type Locker interface { Lock() Unlock()}mutex构造体type Mutex struct { state int32 sema uint32}state:state 是一个复合型的字段,一个字段蕴含多个意义,这样能够通过尽可能少的内存来实现互斥锁。这个字段的第一位(最小的一位)来示意这个锁是否被持有,第二位代表是否有唤醒的 goroutine,第三位代表锁开释处于饥饿状态,残余的位数代表的是期待此锁的 goroutine 数。 sema:sema是个信号量变量,用来管制期待 goroutine 的阻塞休眠和唤醒。 Lock()Mutex 能够处于 2 种操作模式:失常模式和饥饿模式。在失常模式下,waiters按 FIFO 程序排队,然而被唤醒的waiters不领有mutex并与新达到的 goroutines 竞争mutex所有权。新达到的 goroutine 有一个劣势——它们是曾经在 CPU 上运行并且可能有很多,所以被唤醒waiter可能会失败。在这种状况下,唤醒的waiter会在排在期待队列的后面。如果waiter超过 1ms 未能获取mutex,它将mutex切换到饥饿模式。在饥饿模式下,mutex的所有权间接从解锁 goroutine 移交给队列后面的waiter。新达到的 goroutine 不会尝试获取互斥锁,即便它看起来已解锁,也不会尝试自旋。相同,他们将本人排在期待队列的尾部。如果waiter收到mutex的所有权并看到 (1) 它是队列中的最初一个期待者(2) 它期待的工夫不到 1ms,它将互斥锁切换回失常操作模式。失常模式具备更好的性能,因为即便有阻塞的waiter,goroutine 能够间断屡次取得mutex。饥饿模式对于避免尾部提早的病理状况很重要。 func (m *Mutex) Lock() { // Fast path: grab unlocked mutex. // cas获取到锁 if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { if race.Enabled { race.Acquire(unsafe.Pointer(m)) } return } // Slow path (outlined so that the fast path can be inlined) // 尝试自旋竞争或饥饿状态下饥饿goroutine竞争 m.lockSlow()}func (m *Mutex) lockSlow() { // 记录此 goroutine 申请锁的初始工夫 var waitStartTime int64 // 此goroutine的饥饿标记 starving := false // 唤醒标记 awoke := false // 自旋次数 iter := 0 // 以后锁的状态 old := m.state for { // Don't spin in starvation mode, ownership is handed off to waiters // so we won't be able to acquire the mutex anyway. // 锁是非饥饿状态,锁未开释,自旋尝试获取锁 // 如果无奈间接获取锁,进行屡次自旋尝试;屡次尝试失败,进入sema队列休眠 if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) { // Active spinning makes sense. // Try to set mutexWoken flag to inform Unlock // to not wake other blocked goroutines. // 尝试设置 mutexWoken 标记以告诉 Unlock 不要唤醒其余阻塞的 goroutine。 if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 && atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) { awoke = true } runtime_doSpin() iter++ // 再次获取锁的状态,之后会查看是否锁被开释了 old = m.state continue } new := old // Don't try to acquire starving mutex, new arriving goroutines must queue. // 不要尝试获取饥饿的互斥锁,新达到的 goroutine 必须排队。 if old&mutexStarving == 0 { // 非饥饿状态,加锁 new |= mutexLocked } if old&(mutexLocked|mutexStarving) != 0 { // waiter数量加1 new += 1 << mutexWaiterShift } // The current goroutine switches mutex to starvation mode. // But if the mutex is currently unlocked, don't do the switch. // Unlock expects that starving mutex has waiters, which will not // be true in this case. // 以后的 goroutine 将 mutex 切换到饥饿模式。 if starving && old&mutexLocked != 0 { // 设置饥饿状态 new |= mutexStarving } if awoke { // The goroutine has been woken from sleep, // so we need to reset the flag in either case. // goroutine 曾经从睡眠中唤醒, // 所以咱们须要在任何一种状况下重置标记。 if new&mutexWoken == 0 { throw("sync: inconsistent mutex state") } // 新状态革除唤醒标记 new &^= mutexWoken } // cas胜利设置新状态 if atomic.CompareAndSwapInt32(&m.state, old, new) { // 原来锁的状态已开释,并且不是饥饿状态,失常申请到了锁,返回 if old&(mutexLocked|mutexStarving) == 0 { break // locked the mutex with CAS } // If we were already waiting before, queue at the front of the queue. // 如果咱们之前曾经在期待,请在队列的后面排队。 // 判断是否第一次退出到 waiter 队列 queueLifo := waitStartTime != 0 if waitStartTime == 0 { waitStartTime = runtime_nanotime() } // 阻塞期待 // 将此 waiter 退出到队列,如果是首次,退出到队尾,先进先出。如果不是首次,那么退出到队首,这样期待最久的 goroutine 优先可能获取到锁。此 goroutine 会进行休眠。 runtime_SemacquireMutex(&m.sema, queueLifo, 1) // 唤醒之后查看锁是否应该处于饥饿状态,此时曾经被唤醒 // 查看等待时间是否大于1ms starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs old = m.state // 如果锁曾经处于饥饿状态,间接抢到锁,返回 if old&mutexStarving != 0 { // If this goroutine was woken and mutex is in starvation mode, // ownership was handed off to us but mutex is in somewhat // inconsistent state: mutexLocked is not set and we are still // accounted as waiter. Fix that. if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 { throw("sync: inconsistent mutex state") } // 加锁,waiter数减1 delta := int32(mutexLocked - 1<<mutexWaiterShift) if !starving || old>>mutexWaiterShift == 1 { // Exit starvation mode. // Critical to do it here and consider wait time. // Starvation mode is so inefficient, that two goroutines // can go lock-step infinitely once they switch mutex // to starvation mode. // 退出饥饿模式。 // 此 waiter 曾经是队列中的最初一个 waiter 了,没有其它的期待锁的 goroutine 了; // 此 waiter 的等待时间小于 1 毫秒。 delta -= mutexStarving } atomic.AddInt32(&m.state, delta) break } awoke = true iter = 0 } else { old = m.state } } if race.Enabled { race.Acquire(unsafe.Pointer(m)) }}Unlock()func (m *Mutex) Unlock() { if race.Enabled { _ = m.state race.Release(unsafe.Pointer(m)) } // Fast path: drop lock bit. new := atomic.AddInt32(&m.state, -mutexLocked) if new != 0 { // Outlined slow path to allow inlining the fast path. // To hide unlockSlow during tracing we skip one extra frame when tracing GoUnblock. m.unlockSlow(new) }}func (m *Mutex) unlockSlow(new int32) { if (new+mutexLocked)&mutexLocked == 0 { fatal("sync: unlock of unlocked mutex") } if new&mutexStarving == 0 { old := new for { // If there are no waiters or a goroutine has already // been woken or grabbed the lock, no need to wake anyone. // In starvation mode ownership is directly handed off from unlocking // goroutine to the next waiter. We are not part of this chain, // since we did not observe mutexStarving when we unlocked the mutex above. // So get off the way. // 如果 Mutex 处于失常状态,如果没有 waiter,或者曾经有在解决的状况了,那么开释就好,不做额定的解决。 if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 { return } // Grab the right to wake someone. // waiter 数减 1,设置mutexWoken 标记,通过 CAS 更新 state 的值。 new = (old - 1<<mutexWaiterShift) | mutexWoken if atomic.CompareAndSwapInt32(&m.state, old, new) { runtime_Semrelease(&m.sema, false, 1) return } old = m.state } } else { // Starving mode: handoff mutex ownership to the next waiter, and yield // our time slice so that the next waiter can start to run immediately. // Note: mutexLocked is not set, the waiter will set it after wakeup. // But mutex is still considered locked if mutexStarving is set, // so new coming goroutines won't acquire it. // 如果 Mutex 处于饥饿状态,间接唤醒期待队列中的 waiter。 runtime_Semrelease(&m.sema, true, 1) }}TryLock()当一个 goroutine 调用这个TryLock 办法申请锁的时候,如果这把锁没有被其余 goroutine 所持有,那么,这个goroutine 就持有了这把锁,并返回 true;如果这把锁曾经被其余 goroutine 所持有,或者是正在筹备交给某个被唤醒的 goroutine,那么,这个申请锁的 goroutine 就间接返回false,不会阻塞在办法调用上。 ...

September 5, 2022 · 5 min · jiezi

关于go:类型别名与类型定义的区别

类型别名与类型定义的区别// 类型别名// intAlias与int是雷同的类型,即根本数据类型雷同type intAlias = int// 类型定义(依据根本数据类型申明一个新的数据类型)// myInt与int是两个不同的数据类型type myInt int类型别名和原类型齐全一样,只不过是另一种叫法而已齐全一样意味着这两种类型的数据能够相互赋值,而类型定义要和原始类型赋值的时候须要类型转换。 类型别名和原类型是雷同的,而类型定义和原类型是不同的两个类型。类型定义的类型的办法集和原始类型的办法集没有任何关系,而类型别名和原始类型的办法集是一样的

September 5, 2022 · 1 min · jiezi

关于go:Go十大常见错误第9篇使用文件名称作为函数输入

前言这是Go十大常见谬误系列的第9篇:应用文件名称作为函数输出。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go十大常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 问题场景一个常见谬误是把文件名作为函数参数,在函数里读取文件内容。 比方,咱们要实现一个函数,用来统计指定文件里有多少空行,很多人最容易联想到的实现形式如下: func count(filename string) (int, error) { file, err := os.Open(filename) if err != nil { return 0, errors.Wrapf(err, "unable to open %s", filename) } defer file.Close() scanner := bufio.NewScanner(file) count := 0 for scanner.Scan() { if scanner.Text() == "" { count++ } } return count, nil}这段代码逻辑很简略: 文件名作为函数入参函数里读取文件每一行数据,判断是否为空行,如果是空行就计数+1这种形式其实也没大问题,比拟好了解。只是可扩展性不强,没有充分利用到Go语言里对于数据读写的接口(interface)类型的劣势。 试想下,如果你想对一个HTTP body里的内容实现雷同的逻辑,那下面的代码无奈反对,要另外实现一个新的函数。 解决方案Go语言里有2个很好的形象接口(interface),别离是io.Reader和io.Writer。 和下面函数传参应用文件名不一样,咱们能够应用io.Reader作为函数的参数类型。 因为文件、HTTP body、bytes.Buffer都实现了io.Reader,所以 应用io.Reader作为函数参数能够兼容不同类型的数据源。不同的数据源,能够对立应用io.Reader类型里的Read办法来读取数据。具体到这个例子里,咱们能够应用bufio.Reader和其ReadLine办法,代码如下所示: func count(reader *bufio.Reader) (int, error) { count := 0 for { line, _, err := reader.ReadLine() if err != nil { switch err { default: return 0, errors.Wrapf(err, "unable to read") case io.EOF: return count, nil } } if len(line) == 0 { count++ } }}err为io.EOF,就示意读到了空行。 ...

September 5, 2022 · 1 min · jiezi

关于go:Go中的错误和异常处理最佳实践

本文已收录编程学习笔记。涵盖PHP、JavaScript、Linux、Golang、MySQL、Redis和开源工具等等相干内容。谬误意识谬误在Go中,谬误是一种示意程序谬误状态。蕴含了在程序在运行时、编译时的状态信息。个别咱们在编写Go代码中,都会碰到如下的解决形式。 file, err := os.Create("test.txt")fmt.Println(file)if err != nil { fmt.Println(err) return}咱们应用os库创立一个名为test.txt的文件,该办法返回一个文件指针或err的错误信息。 err示意文件创建失败时的错误信息。当存储谬误时,咱们则对程序做错误处理;不存在谬误时,则失常执行其余的逻辑代码。 自定义谬误在Go中是容许咱们自定义错误信息的。自定义错误信息须要利用自带的error报中的New()函数。如下示例代码: package mainimport ( "errors" "fmt")func printError() (a int, err error) { err = errors.New("打印错误信息") a = 1 return}func main() { i, err := printError() fmt.Println("i value is", i) if err != nil { fmt.Println(err) return }}具体的打印信息:i value is 1 打印错误信息。 实现原理在应用errors.New()函数时,该包中申明了一个构造体errorString并且实现了error接口体中的办法Error()。 // errors包package errorsfunc New(text string) error { return &errorString{text}}type errorString struct { s string}func (e *errorString) Error() string { return e.s}// error接口type error interface { Error() string}异样意识异样异样是程序在编译时或者运行时产生的异样信息。如果不对异样做解决,可能导致程序终止程序或者抛出异样信息,导致程序无奈失常运行。不论是在程序编译或者运行时,都须要对异样进行严格解决。如下代码,程序在编译时就会触发异样,导致无奈进行失常编译: ...

September 5, 2022 · 1 min · jiezi

关于go:php程序员转go常犯的错误sql中in功能的使用

我想要实现这样的成果select * from table_name where id in (1,2,3)代码是这么写的。 where := `id in (?)`sb := sqlbuilder.NewStruct(data{}).SelectFrom("table_name")sqlText, args := sb.Where(where).Build()var temp []stringfor _, v := range IDs { temp = append(temp, fmt.Sprintf("%d", v))}IDString := strings.Join(temp, ",")args = []interface{}{IDString}rows, err := client.DBClient(ctx).Query(sqlText, args...)err = orm.ScanRows(rows, &dataResult)// dataResult 返回后果空值log.Infof("rawsql=%v", util.FormatSql(sqlText, args...))通过log打印进去的sql是这个样子的select * from table_name where id in (1,2,3) 完全符合预期,拿这个sql在数据库里间接执行,也能查出后果。这个让我很奇怪。这不就是把sql拼进去搞进去一个字符串吗?难道是 in 应该用大写 IN ?试了下,不行。 而后我就想到还有一个go里封装好的专门用于 in 语句的办法,于是改成这样 sb := sqlbuilder.NewStruct(data{}).SelectFrom("table_name")sqlText, args := sb.Where(sb.In(`id`, sqlbuilder.Flatten(IDs)...)).Build()这么写就齐全ok了。狐疑是不是mysql遇到这种,先pre,而后变量替换的场景时,为了平安,执行前把字符串型值进行了非凡解决,例如加了引号? 问了专家,专家说数据安全性由应用程序保障,数据库还是只做它业余的事件。那就有可能是go客户端给加了引号之类的吧,跟了下代码,也没找到是哪解决的。 ...

September 4, 2022 · 1 min · jiezi

关于go:go-slice切片到底是指针吗为什么p输出的切片是地址

咱们先来看下slice的构造体 // runtime/slice.gotype slice struct { array unsafe.Pointer // 元素指针 len int // 长度 cap int // 容量}咱们先看一下创立slice的办法,咱们用go1.11和go1.18做比拟,go1.11返回的是构造体(slice),go1.18返回的是slice外面的array指针,而后在下层调用方再创立reflect.SliceHeader.这一扭转是在cmd/compile: move slice construction to callers of makeslice go1.11 // runtime/slice.gofunc makeslice(et *_type, len, cap int) slice { // NOTE: The len > maxElements check here is not strictly necessary, // but it produces a 'len out of range' error instead of a 'cap out of range' error // when someone does make([]T, bignumber). 'cap out of range' is true too, // but since the cap is only being supplied implicitly, saying len is clearer. // See issue 4085. maxElements := maxSliceCap(et.size) if len < 0 || uintptr(len) > maxElements { panicmakeslicelen() } if cap < len || uintptr(cap) > maxElements { panicmakeslicecap() } p := mallocgc(et.size*uintptr(cap), et, true) return slice{p, len, cap}}// cmd/compile/internal/gc/walk.go // n escapes; set up a call to makeslice. // When len and cap can fit into int, use makeslice instead of // makeslice64, which is faster and shorter on 32 bit platforms. if t.Elem().NotInHeap() { yyerror("%v is go:notinheap; heap allocation disallowed", t.Elem()) } len, cap := l, r fnname := "makeslice64" argtype := types.Types[TINT64] // Type checking guarantees that TIDEAL len/cap are positive and fit in an int. // The case of len or cap overflow when converting TUINT or TUINTPTR to TINT // will be handled by the negative range checks in makeslice during runtime. if (len.Type.IsKind(TIDEAL) || maxintval[len.Type.Etype].Cmp(maxintval[TUINT]) <= 0) && (cap.Type.IsKind(TIDEAL) || maxintval[cap.Type.Etype].Cmp(maxintval[TUINT]) <= 0) { fnname = "makeslice" argtype = types.Types[TINT] } fn := syslook(fnname) fn = substArgTypes(fn, t.Elem()) // any-1 n = mkcall1(fn, t, init, typename(t.Elem()), conv(len, argtype), conv(cap, argtype))go1.18 ...

September 4, 2022 · 5 min · jiezi

关于go:Go十大常见错误第8篇并发编程中Context使用常见错误

前言这是Go十大常见谬误系列的第8篇:并发编程中Context应用常见谬误。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go十大常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 Context是什么Go语言规范库里有一个package叫context,该package里定义了context.Context类型,在并发编程里十分有用,然而也常常被开发者误会。 官网对Context的表述是: Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.光看这段形容,还是很容易让人迷糊的,咱们接下来具体看看Context到底是什么以及能够帮忙咱们做什么事件。 Context顾名思义,示意的是goroutine的上下文,Context定义如下所示: // A Context carries a deadline, cancellation signal, and request-scoped values// across API boundaries. Its methods are safe for simultaneous use by multiple// goroutines.type Context interface { // Done returns a channel that is closed when this Context is canceled // or times out. Done() <-chan struct{} // Err indicates why this context was canceled, after the Done channel // is closed. Err() error // Deadline returns the time when this Context will be canceled, if any. Deadline() (deadline time.Time, ok bool) // Value returns the value associated with key or nil if none. Value(key interface{}) interface{}}Context能够通过超时设置、携带勾销信号、附加参数信息来不便goroutine里做相应的逻辑管制。 ...

September 4, 2022 · 2 min · jiezi

关于go:Go-的零值有什么用看看这-4-个场景

大家好,我是煎鱼。 前文给大家分享了《10+ 条 Go 官网谚语,你晓得几条?》,明天进一步讲讲第 5 条谚语 Make the zero value useful(让零值变得有用),看看外面想的是什么意思。 背景Go 语言中有一个有些非凡的概念,叫做零值。许多转语言的同学常常会弄混同,一开始会不适应。 代码如下: func main() { var i int var f float64 var b bool var s string fmt.Printf("%v %v %v %q\n", i, f, b, s)}输入后果: 0 0 false ""这会导致大家在定义各种数据库字段时比拟纠结,又会说 Go 这零值,例如:整形的零值 0,跟数据库里的 0 枚举值抵触。又或是做入参判断时怎么区别开? 一时半会想不明确,为什么 Go 要定义这个零值来减少复杂度? 有什么用官网诠释Go 外围团队的 @Dave Cheney 在《What is the zero value, and why is it useful?》中对零值进行了具体的阐释。 将一个值设置为已知默认值的个性,对于程序的安全性和正确性十分重要。能够使你的 Go 程序更加简略和紧凑。这就是 Go 程序员常说的 "给你的构造一个有用的零值"。官网案例以下是 Go 官网给出的几个零值的例子,十分具备代表性。别离是: ...

September 4, 2022 · 2 min · jiezi

关于go:在-Go-里用-CGO这-7-个问题你要关注

大家好,我是煎鱼。 明天给大家分享的是 Go 谚语中的 Cgo is not Go,原文章同名,略有批改,作者是 @Dave Cheney。以下的 “我” 均指代原作者。 借用 JWZ 的一句话:有些人在面对一个问题时,认为 "我晓得,我会应用 cgo(来解决)"。 在应用 cgo 后,他们就会遇到两个新问题。 Cgo 是什么Cgo 是一项了不起的技术,它容许 Go 程序与 C 语言库互相操作,这是一个十分有用的性能。 没有它,Go 就不会有明天的位置。cgo 是在 Android 和 iOS 上运行 Go 程序的要害。 被适度应用我集体认为 cgo 在 Go 我的项目中被适度应用了,当面临在 Go 中从新实现一大段 C 语言代码时,程序员会抉择应用 cgo 来包装库,认为这是个更容易解决的问题。但我认为这是一种谬误的抉择行为。 显然,在某些状况下,cgo 是不可避免的,最显著的是你必须与图形驱动或窗口零碎进行互操作,而后者只能以二进制 blob 的模式提供。在这些场景下,cgo 的应用证实了它的衡量是正当的,比许多人筹备抵赖的要少得多。 以下是一份不残缺的衡量清单,当你把 Go 我的项目建设在 cgo 库上时,你可能没有意识到这些衡量。 你须要对此进行思考。 构建工夫变长当你在 Go 包中导入 "C" 时,go build 须要做更多的工作来构建你的代码。 构建你的包不再是简略地将范畴内的所有 .go 文件的列表传递给 go 工具编译的一次调用,而是蕴含以下工作项: ...

September 4, 2022 · 2 min · jiezi

关于go:Slice-capacity-相关的两个场景

场景 1// s 为 []int, capacity 未知。s1 := append(s, 9)s2 := append(s, 12)s1 和 s2 的值未知,s[0] 可能是 12,也有可能是9。如果在 append 一个元素之后 s 不会扩容,那么 s1[len(s)] 的值将会是12。 场景 2对于 slice 的 cap 到底是多少的问题。 s := []int{} // cap == 0s := make([]int{}, 0, 1) // cap == 1s := []byte{} // cap == 0逃逸状况的 capfunc capVal() []byte { raw := "" s := []byte(raw) s2 := []byte(raw) fmt.Printf("s cap: %d, s2 cap: %d.\n", cap(s), cap(s2)) return s2}// s cap: 32, s2 cap: 0.这里特地要小心,因为函数 capVal 返回了 s2,因而 s2 被视作逃逸变量,因而和在栈上的变量 s,应用了不同的初始化 capacity。 ...

September 3, 2022 · 1 min · jiezi

关于go:Go-版本改动个人理解记录

重要阐明: 本文仅记录了集体浏览 go release notes 之后的一些浮浅了解,肯定会存在大量的谬误。有很多重要的改变没有被列出(可能是因为自己无奈了解或者不感兴趣)。切记不要当做 Go release notes 的中文翻译,仅仅是一个小笔记。具体的内容请肯定请参考原文阐明。官网 Release Notes:https://go.dev/doc/go1.x (替换 url 最初的版本号即可) 因为自己是从 Go 1.3/1.4 阶段开始接触 Go 的,因而1.5之前的一些批改就不关注了。 Go 1.5GC 改变(打消 stw)。调度器改变(并发度更高)。Bootstrapping、移出 C 代码,编译器工具链用 Go(大部分由 C转化而来);移出 6a/6l/6g 等。(负面)编译器的速度极大的变慢。在 x86 下面的动静链接个性。Go 1.6退出了 race detector。编译器前端解析器手写(不应用 yacc)。vendoring;挂掉的时候打出所有 routine 堆栈;GC 优化。Go 1.7编译器后端优化 SSA(针对 64-bit x86),性能晋升。Toolchain 优化:新的导出格局;编译器和链接器性能晋升;更小的二进制输入;Go 1.8Assembler 退出某些 64-bit 机器的新指令。去除 yacc 相干的内容。SSA 应用到全副的反对架构上。新的 compiler 前端,用户无感知然而丢后续性能优化十分重要。优化 compiler 和 linker 性能。Plugin 的初步反对。优化了 GC 和 defer,以及一些规范库。Go 1.9Package 内并发编译。Compiler 和 linker DWARF 信息相干改变。ARM assembler MULA 指令 bug fix。工具 pprof 不须要二进制代码就可能查看各种符号(代码行等)信息;pprof.Do;pprof.Label 更新。GC only blocks the calling routine.新的 bits 规范库;sync.Map 规范库;Go 1.10Build 时候只依照代码来推断是否须要从新编译(而不是批改工夫或者 metadata/build flags)。所以不须要 -a 的编译参数了。和第一条相似,go test 也会正确的 cache 后果。go vet 集成到 go test 调用中。在 pprof 中 blocking 和 mutex 的 profile 文件也自带符号表了。Assembler 反对更多的指令(在不同的平台)。Go 1.11 改变:Go mods 首次退出。Build 时 cache 默认开启;一些内联函数优化;新引入的包导出格局;switch 的未应用变量查看;链接器反对 AVX512;DEBUG信息优化;默认压缩的DWARF 信息;memprof默认应用 alloc 的 profile;vet 优化;Runtime: a sparse heap layout.Map 遍历删除和 slice extension 优化。编译器优化:bounds-check and branch elimination, transitive relations;各种看不懂的优化。Go 1.12后续不反对 binary library.Go doc 和 go vet 代替 godoc 和 go tool vet。GC 和内存治理相干的优化。Go 1.13数字表达方式更改。go module 默认行为扭转;GOPRIVATE环境变量;go get 行为扭转;二进制中蕴含模块版本信息;逃逸剖析改良(重写);批改 Error wrapper 写法.默认应用 MADV_FREE 来向 os 开释内存(会导致 RSS 变高)。Go 1.14overlapping interfaces proposal 嵌入 struct 的重名机制。晋升 defer 性能到简直0开销(和间接调用该函数相比)。支流平台上实现 goroutin 的异步抢占?(asynchronously preemptible);零碎调用的时候会减少很多 EINTR 须要本人解决。CPU 数量很多的状况下,大块内存并行高频调配时候的缩小锁竞争优化。各种 timer, tick, wait 的优化,缩小锁和上下文切换开销。减少一个 bytes 的 hash 函数。Go 1.12 - Go 1.14 ...

September 3, 2022 · 2 min · jiezi

关于go:go-channel原理及使用场景

转载自:go channel原理及应用场景 源码解析type hchan struct { qcount uint // Channel 中的元素个数 dataqsiz uint // Channel 中的循环队列的长度 buf unsafe.Pointer // Channel 的缓冲区数据指针 elemsize uint16 // 以后 Channel 可能收发的元素大小 closed uint32 elemtype *_type // 以后 Channel 可能收发的元素类型 sendx uint // Channel 的发送操作解决到的地位 recvx uint // Channel 的接管操作解决到的地位 recvq waitq // 以后 Channel 因为缓冲区空间有余而阻塞的 Goroutine 列表,双向链表(sugog) sendq waitq // 以后 Channel 因为缓冲区空间有余而阻塞的 Goroutine 列表,双向链表(sugog) // lock protects all fields in hchan, as well as several // fields in sudogs blocked on this channel. // // Do not change another G's status while holding this lock // (in particular, do not ready a G), as this can deadlock // with stack shrinking. lock mutex} ...

September 1, 2022 · 8 min · jiezi

关于go:深入Go底层原理重写Redis中间件实战完整无密

download:深刻Go底层原理,重写Redis中间件实战(残缺无密)ArrayDeque深度解析概述ArrayDeque这个容器不晓得大家在平时的工作中使用的多吗?它是一个十分弱小的容器,既能够作为队列实现FIFO先进先出的功能,也具备栈的功能实现FILO先进后出的功能,那么它到底是怎么样的呢?性能又如何?ArrayDeque介绍ArrayDeque次要是基于数组实现的Deque(Double Ended Queue)双端队列,即双端队列,它既能够当作栈使用,也能够当作队列使用。 ArrayDeque是一个没有容量限度的双端队列,底层是基于数组实现,会主动扩容。ArrayDeque不是线程平安的。ArrayDeque不能够存取null元素。当作为栈使用时,性能比Stack好;当作为队列使用时,性能比LinkedList好。 实现了Queue接口,它实践上是一个单端队列,只能操作队列的一端。实现了Deque接口,Deque集成了Queue接口,有api能够同时操作队列的双端。实现了Cloneable接口,阐明该队列反对clone。实现了Serializable接口,标记该接口反对序列化操作。 构造方法 办法阐明ArrayDeque()结构一个初始容量为16的数组双端队列ArrayDeque(int numElements)结构一个初始容量为numElements的数组双端队列ArrayDeque(Collection<? extends E> c)结构一个初始内容未c的数组双端队列要害办法增加相干办法 等价办法阐明add(e)addLast(e)向队列尾部增加元素offer(e)offerLast(e)向队列尾部增加元素addFirst(e)offerFirst(e)向队列头部增加元素 add前缀的办法,假如超过容量限度,增加失败,会抛出运行时异样offer前缀的办法,比拟非凡,假如超过容量限度,会返回指定值true false 队列获取元素相干办法 等价办法阐明remove()removeFirst()获取并且删除队列头部元素poll()pollFirst()获取并且删除队列头部元素removeLast()pollLast()获取并且删除队列尾部 remove前缀的办法,假如容量为空,会抛出运行时异样offer前缀的办法,假如容量为空,会返回指定值null 办法等价办法阐明element()getFirst()查看队列头部元素peek()peekFirst()查看队列头部元素getLast()peekLast()查看队列尾部元素 peek前缀的办法,假如容量为空,会返回指定true,false,其余办法失败会抛出异样。 栈相干办法等价办法阐明push(e)addFirst(e)向栈中增加元素pop()removeFirst()获取栈顶元素peek()peekFirst()查看栈顶元素其余办法阐明removeFirstOccurrence(Object o)删除队列中第一次相等的元素removeLastOccurrence(Object o)删除队列中最初一个相等的元素tips:粗疏操作是返回指定值还是抛出异样,倡议看源码的javadoc,写的十分分明了。使用案例 测试队列功能 @Test public void test1() { Deque<String> deque = new ArrayDeque<>(); deque.add("1"); deque.offer("2"); deque.offerLast("3"); System.out.println(deque); String poll = deque.poll(); System.out.println(poll); System.out.println(deque);}测试栈的功能 @Test public void test2() { Deque<String> deque = new ArrayDeque<>(); deque.push("1"); deque.push("2"); deque.push("3"); String pop = deque.pop(); System.out.println(pop);}测试存储null数据 @Test public void test3() { Deque<String> deque = new ArrayDeque<>(); boolean offerResult = deque.offer(null); System.out.println(offerResult); System.out.println(deque);}测试poll和remove的区别 ...

September 1, 2022 · 1 min · jiezi

关于go:Go语言实现的Java-Stream-API

学习Go语言时实现的汇合操作工具库,相似于Java 8 中新增的Stream API。因为Go语言不反对泛型,所以基于反射实现。只用于学习目标,不要用于生产(PS:当然也不会有人用)。 我的项目地址:https://github.com/tk103331/s... 汇合操作包含生成操作、两头操作和终止操作。生成操作返回值是Steam对象,相当于数据的源头,能够调用Stream的其余办法;两头操作返回值是Stream对象,能够持续调用Stream的办法,即能够链式调用办法;终止操作不能持续调用办法。 上面介绍下这个库的API: 数据筹备前面的操作都是基于汇合数据的,先筹备一些测试数据。 type student struct { id int name string ageint scores []int}func (s *student) String() string { return fmt.Sprintf("{id:%d, name:%s, age:%d,scores:%v}", s.id, s.name, s.age, s.scores)}func createStudents() []student { names := []string{"Tom", "Kate", "Lucy", "Jim", "Jack", "King", "Lee", "Mask"} students := make([]student, 10) rnd := func(start, end int) int { return rand.Intn(end-start) + start } for i := 0; i < 10; i++ { students[i] = student{ id: i + 1, name: names[rand.Intn(len(names))], age:rnd(15, 26), scores: []int{rnd(60, 100), rnd(60, 100), rnd(60, 100)}, } } return students}type node struct { id int next *node}func createNodes() *node { i := 10 n := &node{id: i} for i > 0 { i-- n = &node{id: i, next: n} } return n}循环遍历 ForEach循环遍历汇合中的每一个元素,须要提供一个蕴含一个参数的处理函数作为参数,形如 func(o T),循环遍历时会把每个元素作为处理函数的实参。ForEach 办法是终止操作。 ...

September 1, 2022 · 5 min · jiezi

关于go:用go实现getpost请求调用api

最近的一次demo,相当于一次温习吧,把踩的坑都记录一遍 先温习一下调用接口的过程 须要url是get办法,还是post办法?url须要加参数吗?序列化?1. 返回的response 的body 默认类型是[]bytebody, err := ioutil.ReadAll(resp.Body)//查看返回值的类型fmt.Println(reflect.TypeOf(body))//终端显示body的类型是[]uint82. 将[]byte转换成string转换过程中依据《go专家编程》讲的来看,须要进行内存拷贝 fmt.Println(string(body))3.将获取到的json中提取某一个对象留神在获取的response时,body的默认数据类型是[]byte,须要转换成string,能力显示成json难看的格局,通过变成的string格局来获取某一个key-value //go get "github.com/thedevsaddam/gojsonq"jsonX := string(body)t := gojsonq.New().FromString(jsonX).Find("unique_token")//因为返回的t是一个空接口类型,须要类型断言转换成string类型return t.(string)4.go发送post申请调用apifunc SubmitOrder(params []byte) { //申明下单的url PostOrderUrl := "https://www.testapi.com/test" //设置post申请,第三个参数传byte类型,很要害! req, err := http.NewRequest("POST", PostOrderUrl, bytes.NewBuffer(params)) if err != nil { log.Println(err) } //受权!!! //这里也很要害,千万不要设置漏了,否则可能后端接口返回不出正确的后果! req.Header.Set("Content-Type", "application/json") req.Header.Set("authorization", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiMzdlYWMwMTMtOTM5Yy00NThjLTgxMDktYmM5MDFjMTIyY2I0IiwiZXhwIjoxNzMzODQ2NTEzfQ._p6nCjIx1nq6sbMa4B-yJ9P_vThJESsF5tLIRoMRZXA") //获取客户端对象,发送申请 client := &http.Client{} resp, err := client.Do(req) if err != nil { log.Println(err) } defer resp.Body.Close() //读取返回值 res, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) } fmt.Println(string(res))}5. 获取空接口类型 的类型或者值xx1:=reflect.Typeof(x)xx2:=reflect.Valueof(x)6. 强制转换空接口类型tt是interface{}类型,每一个interface{}类型中都保留了他自身的value和type,须要通过发射获取值,实现办法见下面 ...

September 1, 2022 · 3 min · jiezi

关于go:HTTP之body去哪儿了

问题形容 业务反馈Golang服务在解析申请参数的时候,偶现呈现"EOF"谬误,狐疑网关或者两头链路失落了HTTP申请体,业务谬误日志统计如下: 阐明一下,Golang服务基于gin框架,解析POST申请参数形式如下: func Handle(c *gin.Context) { err:= c.ShouldBindJSON(&req) //呈现err io.EOF} HTTP申请没有body时候,就会呈现这种谬误。屡次确认,客户端日志显示申请都带有body,而且依据traceid查问某个异样申请的客户端日志,也显示带有body。难道真是两头节点丢了申请体?可是不应该啊,网关(Nginx)在转发申请的时候,不可能失落body啊,而且客户端申请都带有"Content-Length",如果网关没有收到申请body,校验HTTP申请不残缺,也会间接返回400谬误啊。 查问网关access日志,显示request-body的确为空,阐明网关接管到的申请的确没有body。 须要阐明一下,从客户端到Golang服务,整个拜访链路为:client ——> ECDN ——> LVS ——> 网关Nginx ——> Golang服务 LVS只是四层负载平衡,也不会是它的问题。腾讯云ECDN,有可能,须要找服务方帮忙排查下。 ECDN排查 业务日志查问出异样申请,提供申请url,申请工夫,客户端IP给ECDN服务方,查问ECDN日志。结果显示,所有申请都是带有body,即便存在回源失败的状况,重试的时候也都带有body。 红框中的两个数字,第一个是head-length,第二个是body-length。 网关日志只能看到request-body为空,以及request_length,然而申请头以及申请体长度是看不到的。 然而发现,失常申请时候,网关日志request_length = ECDN日志head-length + body-length;异样申请时候,网关日志request_length = ECDN日志head-length。大概率ECDN的确没有带body。 另外确认,ECDN针对客户端的携带的header "Content-Length",也会转发给源站。网关节点批改日志格局,增加字段Content-Length;察看一段时间,申请失常时候Content-Length也是失常的,申请出错的时候Content-Length=0。根本能够确认,ECDN转发过去的申请的确没有携带body,以及Content-Length=0。 查找多个异样case,发现出错的时候,ECDN都存在出错重试状况。在ECDN批改配置,去掉重试之后,EOF谬误再也没有了。经ECDN服务方排查确认,重试逻辑存在bug,重试的确没有带body。 持续摸索 为什么第一次申请会失败呢?ECDN服务方给出线索,失败状况日志显示的谬误是"SSL Alert Close Notify。查问理解到,这谬误是HTTPS在建设加密链接的时候,源站SSL_shutdown被动敞开链接导致。 源站为什么会被动敞开链接呢?排查问题的时候,腾讯云ECDN方还进行了线上抓包,给出了局部抓包数据: 因为是HTTPS加密数据,抓包并不能看到具体的数据,wireshark导入网站密钥之后,发现仍然不能解密。最初才发现,加密算法采纳的是ECDHE,wireshark不反对此类密文的解密。 不过还是能够看到,第21号包返回的应该就是所谓的SSL Alert Close Notify,前面就是链接的FIN敞开了。 再次全局剖析一下,第一次申请在绝对0时刻收回,第二次申请在绝对时刻120秒收回。120秒如同有点相熟,查看网关Nginx配置,发现: http{ keepalive_timeout 120;} 长连贯keepalive_timeout配置刚好是120秒,即120秒之内没有申请的话,Nginx(这里就是源站)会被动断开链接。 Nginx keepalive解决 解决实现以后申请时候,如果是长连贯Nginx会增加定时器,超时工夫刚好为keepalive_timeout,超时之后,被动敞开以后长链接。 static void ngx_http_set_keepalive(ngx_http_request_t *r){ //超时后处理办法 rev->handler = ngx_http_keepalive_handler; ngx_add_timer(rev, clcf->keepalive_timeout);}static void ngx_http_keepalive_handler(ngx_event_t *rev){ if (rev->timedout || c->close) { ngx_http_close_connection(c); return; }}voidngx_http_close_connection(ngx_connection_t *c){#if (NGX_HTTP_SSL) if (c->ssl) { if (ngx_ssl_shutdown(c) == NGX_AGAIN) { c->ssl->handler = ngx_http_close_connection; return; } }#endif} Nginx有两个配置能够影响源站被动敞开链接(都归属与ngx_http_core_module): ...

August 31, 2022 · 1 min · jiezi

关于go:Gin-绑定参数

Gin 提供了两类绑定办法:Must bind 和 Should bind Must bind 的办法有 Bind,BindJSON,BindXML,BindQuery,BindYAML,这些办法属于 BindWith 的具体调用。 Must bind 如果产生绑定谬误,则申请终止,并触发 c.AbortWithError(400, err).SetType(ErrorTypeBind)。响应状态码被设置为 400 并且 Content-Type 被设置为 text/plain; charset=utf-8。如果您在此之后尝试设置响应状态码,Gin会输入日志 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422。 Should bind的办法有 ShouldBind,ShouldBindJSON,ShouldBindXML,ShouldBindQuery,ShouldBindYAML,这些办法属于 ShouldBindWith 的具体调用。 Should bind 如果产生绑定谬误,Gin 会返回谬误并由开发者处理错误和申请。应用 Bind 办法时,Gin 会尝试依据 Content-Type 推断如何绑定。如果你明确晓得要绑定什么,能够应用 MustBindWith 或 ShouldBindWith。 参考: https://icode.best/i/97680946...

August 31, 2022 · 1 min · jiezi

关于go:熔断原理分析与源码解读

熔断机制(Circuit Breaker)指的是在股票市场的交易工夫中,当价格的稳定幅度达到某一个限定的指标(熔断点)时,对其暂停交易一段时间的机制。此机制如同保险丝在电流过大时候熔断,故而得名。熔断机制推出的目标是为了防备系统性危险,给市场更多的沉着工夫,防止恐慌情绪蔓延导致整个市场稳定,从而避免大规模股价上涨景象的产生。 同样的,在高并发的分布式系统设计中,也应该有熔断的机制。熔断个别是在客户端(调用端)进行配置,当客户端向服务端发动申请的时候,服务端的谬误一直地增多,这时候就可能会触发熔断,触发熔断后客户端的申请不再发往服务端,而是在客户端间接拒绝请求,从而能够爱护服务端不会过载。这里说的服务端可能是rpc服务,http服务,也可能是mysql,redis等。留神熔断是一种有损的机制,当熔断后可能须要一些降级的策略进行配合。 熔断原理古代微服务架构根本都是分布式的,整个分布式系统是由十分多的微服务组成。不同服务之间互相调用,组成简单的调用链路。在简单的调用链路中的某一个服务如果不稳固,就可能会层层级联,最终可能导致整个链路全副挂掉。因而咱们须要对不稳固的服务依赖进行熔断降级,临时切断不稳固的服务调用,防止部分不稳固因素导致整个分布式系统的雪崩。 说白了,我感觉熔断就像是那些容易异样服务的一种代理,这个代理可能记录最近调用产生谬误的次数,而后决定是持续操作,还是立刻返回谬误。 熔断器外部保护了一个熔断器状态机,状态机的转换关系如下图所示: 熔断器有三种状态: Closed状态:也是初始状态,咱们须要一个调用失败的计数器,如果调用失败,则使失败次数加1。如果最近失败次数超过了在给定工夫内容许失败的阈值,则切换到Open状态,此时开启一个超时时钟,当达到超时时钟工夫后,则切换到Half Open状态,该超时工夫的设定是给了零碎一次机会来修改导致调用失败的谬误,以回到失常的工作状态。在Closed状态下,谬误计数是基于工夫的。在特定的工夫距离内会主动重置,这可能避免因为某次的偶尔谬误导致熔断器进入Open状态,也能够基于间断失败的次数。Open状态:在该状态下,客户端申请会立刻返回谬误响应,而不调用服务端。Half-Open状态:容许客户端肯定数量的去调用服务端,如果这些申请对服务的调用胜利,那么能够认为之前导致调用失败的谬误曾经修改,此时熔断器切换到Closed状态,同时将谬误计数器重置。如果这肯定数量的申请有调用失败的状况,则认为导致之前调用失败的的问题依然存在,熔断器切回到断开状态,而后重置计时器来给零碎肯定的工夫来修改谬误。Half-Open状态可能无效避免正在复原中的服务被忽然而来的大量申请再次打挂。下图是Netflix的开源我的项目Hystrix中的熔断器的实现逻辑: 从这个流程图中,能够看到: 有申请来了,首先allowRequest()函数判断是否在熔断中,如果不是则放行,如果是的话,还要看有没有达到一个熔断工夫片,如果熔断工夫片到了,也放行,否则间接返回谬误。每次调用都有两个函数makeSuccess(duration)和makeFailure(duration)来统计一下在肯定的duration内有多少是胜利还是失败的。判断是否熔断的条件isOpen(),是计算failure/(success+failure)以后的错误率,如果高于一个阈值,那么熔断器关上,否则敞开。Hystrix会在内存中保护一个数据,其中记录着每一个周期的申请后果的统计,超过时长长度的元素会被删除掉。熔断器实现理解了熔断的原理后,咱们来本人实现一套熔断器。 相熟go-zero的敌人都晓得,在go-zero中熔断没有采纳下面介绍的形式,而是参考了《Google Sre》 采纳了一种自适应的熔断机制,这种自适应的形式有什么益处呢?下文会基于这两种机制做一个比照。 上面咱们基于下面介绍的熔断原理,实现一套本人的熔断器。 代码门路:go-zero/core/breaker/hystrixbreaker.go 熔断器默认的状态为Closed,当熔断器关上后默认的冷却工夫是5秒钟,当熔断器处于HalfOpen状态时默认的探测工夫为200毫秒,默认应用rateTripFunc办法来判断是否触发熔断,规定是采样大于等于200且错误率大于50%,应用滑动窗口来记录申请总数和谬误数。 func newHystrixBreaker() *hystrixBreaker { bucketDuration := time.Duration(int64(window) / int64(buckets)) stat := collection.NewRollingWindow(buckets, bucketDuration) return &hystrixBreaker{ state: Closed, coolingTimeout: defaultCoolingTimeout, detectTimeout: defaultDetectTimeout, tripFunc: rateTripFunc(defaultErrRate, defaultMinSample), stat: stat, now: time.Now, }}func rateTripFunc(rate float64, minSamples int64) TripFunc { return func(rollingWindow *collection.RollingWindow) bool { var total, errs int64 rollingWindow.Reduce(func(b *collection.Bucket) { total += b.Count errs += int64(b.Sum) }) errRate := float64(errs) / float64(total) return total >= minSamples && errRate > rate }}每次申请都会调用doReq办法,在该办法中,首先通过accept()办法判断是否回绝本次申请,回绝则间接返回熔断谬误。否则执行req()真正的发动服务端调用,胜利和失败别离调用b.markSuccess()和b.markFailure() ...

August 31, 2022 · 3 min · jiezi

关于go:CONNMIX-开发-WebSocket-视频弹幕

CONNMIX 开发 WebSocket 视频弹幕应用WebSocket制作一个单机版弹幕零碎非常简单,然而当单机性能达到瓶颈,须要扩大为集群部署时就会面临很多分布式问题,应用CONNMIX则无需放心这些问题,很少的代码即可实现一个高性能分布式WebSocket集群。 要求connmix >= v1.0.4设计思路客户端在ws连贯胜利后,订阅一个视频ID的通道 video:<videoid>在发送弹幕的接口中,调用connmix任意节点的 /v1/mesh/publish 接口往对应 video_id 发送实时弹幕,所有订阅该通道的ws客户端都将会收到音讯。以上都是增量推送设计,全量通常都是在页面加载时通过一个全量api接口获取。交互协定设计当用户发送 100001@video 咱们在 lua 代码中就执行订阅 video:100001 通道。前端切换视频时,可先发送勾销弹幕音讯,而后发送新的订阅弹幕音讯。性能json格局订阅弹幕{"op":"subscribe","channel":"100001@video"}勾销弹幕{"op":"unsubscribe","channel":"100001@video"}弹幕事件{"event":"@video","data":{"uid":1001,"video_id":100001,"msg":"Hello,World!"}}胜利{"result":true}谬误{"code":1,"msg":"Error"}装置引擎CONNMIX https://connmix.com/docs/1.0/#/zh-cn/install-engine批改配置在 connmix.yaml 配置文件的 options 选项,批改websocket的url门路 options: - name: path value: /barrage-videosCONNMIX 编码批改 entry.websocket.lua 的 on_message 办法如下: 当承受到subscribe、unsubscribe音讯时,执行订阅/勾销订阅对应的通道function on_message(msg) --print(msg) if msg["type"] ~= "text" then conn:close() return end local conn = mix.websocket() local data, err = mix.json_decode(msg["data"]) if err then mix_log(mix_DEBUG, "json_decode error: " .. err) conn:close() return end local op = data["op"] local channel_raw = data["channel"] local channel_table = mix.str_split(channel_raw, "@") if table.getn(channel_table) ~= 2 then mix_log(mix_DEBUG, "invalid channel: " .. channel_raw) conn:close() return end local video_id = channel_table[1] --Lua的table索引默认从1开始 local channel_type = channel_table[2] if op == "subscribe" and channel_type == "video" then local err = conn:subscribe("video:" .. video_id) if err then mix_log(mix_DEBUG, "subscribe error: " .. err) conn:close() return end end if op == "unsubscribe" and channel_type == "video" then local err = conn:unsubscribe("video:" .. video_id) if err then mix_log(mix_DEBUG, "unsubscribe error: " .. err) conn:close() return end end conn:send('{"result":true}')endAPI 编码接下来在现有零碎的框架中实现被动弹幕推送 ...

August 30, 2022 · 1 min · jiezi

关于go:go-的-slice-底层的-陷阱

数组是一片间断的内存。slice 则是一个构造体,蕴含三个字段:长度 len、容量 cap、底层数组。 // runtime/slice.gotype slice struct { array unsafe.Pointer // 元素指针 len int // 长度 cap int // 容量}slice 的生命周期围绕着按 cap 事后调配 底层数组 及 cap 有余时重新分配 cap*2 的 底层数组。 特地要留神的时,当咱们应用下标范畴来失去 子切片 时,子切片 和 父切片 指向的 底层数组 在此时是 同一片内存地址。后续 子切片 可能会因为 容量cap 有余而从新申请容量时,从 父切片 的 底层数组 中独立进去。 package mainimport ( "fmt")func main() { slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} s1 := slice[2:5] //len3 cap8 s2 := s1[2:6:7] //len4 cap5 fmt.Println(s1, len(s1), cap(s1)) fmt.Println(s2, len(s2), cap(s2)) fmt.Println(strings.Repeat("=", 10)) //s2的容量已满 如果后续再追加成员,将重新分配一套底层数组 s2 = append(s2, 100) //s1和s2还在专用一套底层数组 s1[2] == s2[0] s1[2] = 20 fmt.Println(s1, len(s1), cap(s1)) fmt.Println(s2, len(s2), cap(s2)) fmt.Println(strings.Repeat("=", 10)) //s2的重新分配底层数组 容量裁减为原来的2倍=5*2 s2 = append(s2, 200) //s1与s2的底层数组已不同 s1[2] 已无奈影响 s2[0] s1[2] = 200 fmt.Println(s1, len(s1), cap(s1)) fmt.Println(s2, len(s2), cap(s2))}只有当子切片的 容量cap 有余,从新申请调配新的容量内存段时,才会从父切片的底层数组中独立查出来。 ...

August 29, 2022 · 1 min · jiezi

关于go:使用viper解析ini配置文件

在我的项目中,常常应用ini和yaml作为配置文件。 对于ini配置文件,始终应用https://github.com/go-ini/ini这个库来解析配置。次要是因为简略易用并且中文文档反对较好。 对于viper,之前是不反对ini配置的,对于viper为什么不反对ini,官网QA有一段解释 Q: Why not INI files?A: Ini files are pretty awful. There’s no standard format, and they are hard to validate. Viper is designed to work with JSON, TOML or YAML files. If someone really wants to add this feature, I’d be happy to merge it. It’s easy to specify which formats your application will permit.看来ini格局始终不受viper待见... 最近翻看viper文档的时候,发现在v1.6.0的版本中曾经增加了对ini的反对,大略体验了下。 轻易找了一个ini配置文件 # possible values : production, developmentapp_mode = development[paths]# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)data = /home/git/grafana[server]# Protocol (http or https)protocol = "http"# The http port to usehttp_port = 9999# Redirect to correct domain if host header does not match domain# Prevents DNS rebinding attacksenforce_domain = true// 指定要解析的配置文件viper.SetConfigFile("my.ini")if err := viper.ReadInConfig(); err != nil { log.Fatal(err)}// 默认section为defaultfmt.Printf("app_mode:%v\n", viper.GetString(`default.app_mode`))fmt.Printf("server.protocol:%v\n", viper.GetString(`server.protocol`))// 获取所有配置all := viper.AllSettings()fmt.Printf("%#v\n", all)// 解析section到structserver := struct { Protocol string `mapstructure:"protocol"` HttpPort int `mapstructure:"http_port"` EnforceDomain bool `mapstructure:"enforce_domain"`}{}if err := viper.UnmarshalKey("server", &server); err != nil { fmt.Println("ini解析到struct 异样: ", err)} else { fmt.Println("struct: ", server)}输入 ...

August 28, 2022 · 2 min · jiezi

关于go:go常用类库

webgo get -u github.com/gin-gonic/gin@1.8.1jsongo get -u github.com/tidwall/gjson@1.14.3loggo get -u go.uber.org/zap@1.23.0http clientgo get -u github.com/valyala/fasthttp@1.39.0go get -u github.com/go-resty/resty@v2.7.0

August 28, 2022 · 1 min · jiezi

关于go:golang-http-client一定要close-ResponseBody吗

咱们晓得个别在调用http client后都会close Response.Body,如下: client := http.DefaultClientresp, err := client.Do(req)if err != nil { return nil, err}defer resp.Body.Close()上面咱们来看下为什么resp.Body须要Close,肯定须要Close吗? 咱们先通过"net/http/httptrace"来验证下:1.不应用Close代码: package mainimport ( "fmt" "net/http" "net/http/httptrace")func main() { req, err := http.NewRequest("GET", "https://www.baidu.com/", nil) if err != nil { panic(err) } trace := &httptrace.ClientTrace{ GetConn: func(hostPort string) { fmt.Println("GetConn:", hostPort) }, GotConn: func(connInfo httptrace.GotConnInfo) { fmt.Printf("GotConn Reused=%v WasIdle=%v IdleTime=%v\n", connInfo.Reused, connInfo.WasIdle, connInfo.IdleTime) fmt.Printf("GotConn localAddr: %s\n", connInfo.Conn.LocalAddr()) fmt.Printf("GotConn remoteAddr: %s\n", connInfo.Conn.RemoteAddr()) }, PutIdleConn: func(err error) { fmt.Printf("PutIdleConn err=%v\n", err) }, } req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) client := &http.Client{ Transport: &http.Transport{ MaxIdleConns: 1, //最大Idle MaxConnsPerHost: 2, //每个host最大conns }} for i := 1; i <= 10; i++ { fmt.Printf("==============第[%d]次申请================\n", i) resp, err := client.Do(req) if err != nil { fmt.Println(err) return } fmt.Println(resp.Status) }}输入: ...

August 28, 2022 · 7 min · jiezi

关于go:Go十大常见错误第7篇不使用race选项做并发竞争检测

前言这是Go十大常见谬误系列的第7篇:不应用-race选项做并发竞争检测。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go十大常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 背景并发编程里很容易遇到并发拜访抵触的问题。 Go语言里多个goroutine同时操作某个共享变量的时候,如果一个goroutine对该变量做写操作,其它goroutine做读操作,假如没有做好并发访问控制,就容易呈现并发拜访抵触,导致程序crash。 大家能够看如下的代码示例: package mainimport ( "fmt")func main() { c := make(chan bool) m := make(map[string]string) go func() { m["1"] = "a" // First conflicting access. c <- true }() m["2"] = "b" // Second conflicting access. <-c for k, v := range m { fmt.Println(k, v) }}下面的代码会呈现并发拜访抵触,2个goroutine同时对共享变量m做写操作。 通过-race选项,咱们就能够利用编译器帮咱们疾速发现问题。 $ go run -race main.go ==================WARNING: DATA RACEWrite at 0x00c000074180 by goroutine 7: runtime.mapassign_faststr() /usr/local/opt/go/libexec/src/runtime/map_faststr.go:202 +0x0 main.main.func1() /Users/xxx/github/go-tutorial/workspace/senior/p28/data-race/main.go:11 +0x5dPrevious write at 0x00c000074180 by main goroutine: runtime.mapassign_faststr() /usr/local/opt/go/libexec/src/runtime/map_faststr.go:202 +0x0 main.main() /Users/xxx/github/go-tutorial/workspace/senior/p28/data-race/main.go:14 +0xcbGoroutine 7 (running) created at: main.main() /Users/xxx/github/go-tutorial/workspace/senior/p28/data-race/main.go:10 +0x9c==================1 a2 bFound 1 data race(s)exit status 66常见问题一个常见的谬误是开发者测试Go程序的时候,不应用-race选项。 ...

August 26, 2022 · 1 min · jiezi

关于go:Golang-常见设计模式之单例模式

之前咱们曾经看过了 Golang 常见设计模式中的装璜和选项模式,明天要看的是 Golang 设计模式里最简略的单例模式。单例模式的作用是确保无论对象被实例化多少次,全局都只有一个实例存在。依据这一个性,咱们能够将其利用到全局唯一性配置、数据库连贯对象、文件拜访对象等。Go 语言实现单例模式的办法有很多种,上面咱们就一起来看一下。 饿汉式饿汉式实现单例模式非常简单,间接看代码: package singletontype singleton struct{}var instance = &singleton{}func GetSingleton() *singleton { return instance}singleton 包在被导入时会主动初始化 instance 实例,应用时通过调用 singleton.GetSingleton () 函数即可取得 singleton 这个构造体的单例对象。 这种形式的单例对象是在包加载时立刻被创立,所以这个形式叫作饿汉式。与之对应的另一种实现形式叫作懒汉式,懒汉式模式下实例会在第一次被应用时被创立。 须要留神的是,只管饿汉式实现单例模式的形式简略,但大多数状况下并不举荐。因为如果单例实例化时初始化内容过多,会造成程序加载用时较长。 懒汉式接下来咱们再来看下如何通过懒汉式实现单例模式: package singletontype singleton struct{}var instance *singletonfunc GetSingleton() *singleton { if instance == nil { instance = &singleton{} } return instance}相较于饿汉式的实现,懒汉式将实例化 singleton 构造体局部的代码移到了 GetSingleton () 函数外部。这样可能将对象实例化的步骤提早到 GetSingleton () 第一次被调用时。 不过通过 instance == nil 的判断来实现单例并不非常牢靠,如果有多个 goroutine 同时调用 GetSingleton () 就无奈保障并发平安。 反对并发的单例如果你应用 Go 语言写过并发编程,应该很快能想到该如何解决懒汉式单例模式并发平安问题,比方像上面这样: ...

August 26, 2022 · 2 min · jiezi

关于go:go-的-httpClientgorestyresty-简单的封装实例

对 go-resty/resty 做了简略的封装。 package utilsimport ( "crypto/tls" "github.com/go-resty/resty/v2" "time")// ResponseJson json 响应约定type ResponseJson struct { Code uint32 `json:"code"` Msg string `json:"msg"` Data interface{} `json:"data"`}const httpClientTimeOut = 3const httpClientRetryCount = 3// HttpGetResJson get request and json responsefunc HttpGetResJson(url string, queryParams map[string]string, result interface{}) (res *resty.Response, err error) { client := resty.New() client.SetTimeout(time.Second * httpClientTimeOut) client.SetRetryCount(httpClientRetryCount) client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) res, err = client.R(). SetQueryParams(queryParams). SetHeader("Accept", "application/json"). SetResult(result). Get(url) return res, err}func HttpPostFormResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendFormResJson(url, "POST", formData, result)}func HttpPutFormResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendFormResJson(url, "PUT", formData, result)}func HttpPatchFormResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendFormResJson(url, "PATCH", formData, result)}func HttpDeleteFormResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendFormResJson(url, "DELETE", formData, result)}func HttpOptionsFormResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendFormResJson(url, "OPTIONS", formData, result)}func HttpHeadFormResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendFormResJson(url, "HEAD", formData, result)}func HttpPostJsonResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendJsonResJson(url, "POST", formData, result)}func HttpPutJsonResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendJsonResJson(url, "PUT", formData, result)}func HttpPatchJsonResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendJsonResJson(url, "PATCH", formData, result)}func HttpDeleteJsonResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendJsonResJson(url, "DELETE", formData, result)}func HttpOptionsJsonResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendJsonResJson(url, "OPTIONS", formData, result)}func HttpHeadJsonResJson(url string, formData map[string]string, result interface{}) (res *resty.Response, err error) { return HttpSendJsonResJson(url, "HEAD", formData, result)}// HttpSendFormResJson send formData and response jsonfunc HttpSendFormResJson(url, method string, formData map[string]string, result interface{}) (res *resty.Response, err error) { client := resty.New() client.SetTimeout(time.Second * httpClientTimeOut) client.SetRetryCount(httpClientRetryCount) client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) req := client.R(). SetHeader("Content-Type", "application/x-www-form-urlencoded"). SetHeader("Accept", "application/json"). SetFormData(formData). SetResult(result) switch strings.ToLower(method) { case "post": res, err = req.Post(url) case "put": res, err = req.Put(url) case "patch": res, err = req.Patch(url) case "delete": res, err = req.Delete(url) case "options": res, err = req.Options(url) default: res, err = req.Head(url) } return res, err}// HttpSendJsonResJson send json and response jsonfunc HttpSendJsonResJson(url, method string, body interface{}, result interface{}) (res *resty.Response, err error) { client := resty.New() client.SetTimeout(time.Second * httpClientTimeOut) client.SetRetryCount(httpClientRetryCount) client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) req := client.R(). SetHeader("Content-Type", "application/json"). SetHeader("Accept", "application/json"). SetBody(body). SetResult(result) switch strings.ToLower(method) { case "post": res, err = req.Post(url) case "put": res, err = req.Put(url) case "patch": res, err = req.Patch(url) case "delete": res, err = req.Delete(url) case "options": res, err = req.Options(url) default: res, err = req.Head(url) } return res, err}

August 25, 2022 · 2 min · jiezi

关于go:Google翻译的坑

对于API V2如果应用API V2版本的SDK,会呈现内容字符数超过5K(1024*5),没法翻译的问题,报错:Your client has issued a malformed or illegal request. <ins>That’s all we know.</ins>,也就是说如果用V2版本的SDK没法翻译长文本,在官网文档中对应的是base版本。 API V3API V3版本长度尽管也有限度,然而最大能够提交30KB,绝大部分场景够用,然而这里会产生两个问题。 第一种状况是,翻译代码片段的时候,如果手动把须要翻译的代码片段局部利用正则替换成<span class="notranslate">--key--</span>,而后本地把响应的标记key以及对应的代码段缓存成为map,而后翻译好后传递回来的数据再进行手动替换,则会导致一种状况:尽管来说,对不须要翻译的局部标记了notranslate,尽管这部分标签不翻译,会保留,然而某些状况下,这个标记不翻译局部的<span>标签中定义的key局部,会作为一段纯文本插入翻译后的程序某个局部,而后须要手动依据具体情况,而且这种状况有时候还可能不可意料。 另外一种状况是,实际上自身VPI V3接口不会对<code>局部内容做翻译,然而会导致本来<code>中的换行失落,最初饭回来的翻译内容就是一堆毫无任何格局的内容,看起来乌七八糟的。如果不采纳第一种标记的模式,间接扔给谷歌翻译的话,须要在提交翻译前,对须要翻译的内容中的换行符,也就是\n进行替换为<br>,当然这个\n在windows下可能会是\r\n。 以上针对谷歌翻译SDK的两个版本以及三个状况的总结,基本上如果须要应用谷歌翻译的人都会遇到,然而在找材料的时候,实际上没有太多相干材料,解决起来比拟麻烦还麻烦。留这篇文章以做总结,心愿后来者遇到相干的状况无奈着手的时候,能够提供肯定的参考。

August 24, 2022 · 1 min · jiezi

关于go:Golang-中变量定义的坑

首先,文件构造如下: .├── a│   └── a.go├── b│   └── b.go├── go.mod└── main.goa.go: package avar Hello = "a"func NewHello() { Hello = "aa"}b.go: package bimport ( "fmt" "one/a")var World = a.Hellofunc NewWorld() { fmt.Printf(World)}main.go: package mainimport ( "one/a" "one/b")func main() { a.NewHello() b.NewWorld()}这里次要的问题体现在b.go中,自身以我的想法,在main中实例化程序的时候,首先实例化a,而后我再实例化b,那么是有个先后顺序的,那么在b中,我冀望的后果应该是"aa",实际上我失去的后果仍旧是"a"。 这里就疏忽了golang作为编译型语言,var初始化的时候,如果有赋值,那么编译的时候会间接默认为已知值,另外golang的值都是值传递形式,而在程序运行中产生的批改不会对从新定义的变量进行批改。 所以b.go批改成 var World = &a.Hellofunc NewWorld() { fmt.Printf(*World)}获取原变量的指针地址,而后以指针取原变量的数据值即可。 其实如果放在单文件来看,不会犯这种谬误,然而把两个变量定义形式放在两个独立的程序文件后,居然就没想到这一层。

August 24, 2022 · 1 min · jiezi

关于go:Golang-中使用Event-Listener解耦逻辑让你的应用更简单

当一个订单实现时,老板说:发个短信给用户,再发个邮件吧,同时更新下统计数据,巴拉巴拉(真事多)。 咱们当然能够用 goroutine 解决这些耗时的工作,但优雅的一种形式是应用「事件零碎(Event - Listener 模式)」来进行代码的解耦,让代码逻辑更简略! 跟 Laravel 的应用办法完全一致,Phper 请安心服用。第一步:生成工作类go run . artisan make:event PodcastProcessedgo run . artisan make:listener SendPodcastNotification第二步:注册事件和监听器package providersimport ( "github.com/goravel/framework/contracts/events" "github.com/goravel/framework/support/facades")type EventServiceProvider struct {}...func (receiver *EventServiceProvider) listen() map[events.Event][]events.Listener { return map[events.Event][]events.Listener{ &events.OrderShipped{}: { &listeners.SendShipmentNotification{}, }, }}第三步:实现 Listener 逻辑巴拉巴拉 第四步:调度事件err := facades.Event.Job(&events.OrderShipped{}, []events.Arg{ {Type: "string", Value: "abcc"}, {Type: "int", Value: 1234}, }).Dispatch()Over, 更多功能详见文档,小伙伴们开始欢快的搞事件吧! 对于 GoravelGoravel 是一个性能齐备、具备良好扩大能力的 Web 应用程序框架。 作为一个起始脚手架帮忙 Golang 开发者疾速构建本人的利用。 我的项目地址:https://github.com/goravel/goravel 文档地址:www.goravel.dev

August 23, 2022 · 1 min · jiezi

关于go:初学Go-值得深研的7大开源项目

本文已收录Golang学习库。本库涵盖PHP、JavaScript、Linux、Golang、MySQL、Redis和开源工具等等相干内容。go-admingo-admin基于Gin + Vue + Element UI的前后端拆散权限管理系统,零碎初始化极度简略,只须要配置文件中,批改数据库连贯,零碎反对多指令操作,迁徙指令能够让初始化数据库信息变得更简略,服务指令能够很简略的启动api服务。 go-admin反对多租户、用户治理、部门治理、菜单治理、角色治理、字典治理、参数治理、操作日志、登录日志、接口文档、代码生成、表单构建、服务监控、内容治理、定时工作等相干性能。 go-admin遵循 RESTful API 设计规范、基于 GIN WEB API 框架,提供了丰盛的中间件反对(用户认证、跨域、拜访日志、追踪ID等)、基于Casbin的 RBAC 访问控制模型、JWT 认证、反对 Swagger 文档(基于swaggo)、基于 GORM 的数据库存储,可扩大多种类型数据库、配置文件简略的模型映射,疾速可能失去想要的配置、代码生成工具、表单构建工具、多指令模式、多租户的反对、TODO: 单元测试等性能。 官网地址:https://github.com/go-admin-t... 成果预览: go-gin-apigo-gin-api基于 Gin 进行模块化设计的 API 框架,封装了罕用性能,应用简略,致力于进行疾速的业务研发。比方,反对 cors 跨域、jwt 签名验证、zap 日志收集、panic 异样捕捉、trace 链路追踪、prometheus 监控指标、swagger 文档生成、viper 配置文件解析、gorm 数据库组件、gormgen 代码生成工具、graphql 查询语言、errno 对立定义错误码、gRPC 的应用、cron 定时工作 等等。 go-gin-api反对 rate 接口限流、反对 panic 异样时邮件告诉、反对 cors 接口跨域、反对 Prometheus 指标记录、反对 Swagger 接口文档生成、反对 GraphQL 查询语言、反对 trace 我的项目外部链路追踪、反对 pprof 性能分析、反对 errno 对立定义错误码、反对 zap 日志收集、反对 viper 配置文件解析、反对 gorm 数据库组件、反对 go-redis 组件、反对 RESTful API 返回值标准、反对 生成数据表 CURD、控制器办法 等代码生成器、反对 cron 定时工作,在后盾可界面配置、反对 websocket 实时通信,在后盾有界面演示、反对 web 界面,应用的 Light Year Admin 模板等性能。 ...

August 22, 2022 · 1 min · jiezi

关于go:go源码系列之syncMutex互斥锁

前言golang 的sync包下有种锁,一种是sync.RWMutex,另一种是sync.Mutex,本文将解说下sync.Mutex是如何实现的?如何防止读/写 饥饿问题?就让咱们带着这些问题来看源码是如何实现的 概述// Mutex fairness.//// Mutex can be in 2 modes of operations: normal and starvation.// In normal mode waiters are queued in FIFO order, but a woken up waiter// does not own the mutex and competes with new arriving goroutines over// the ownership. New arriving goroutines have an advantage -- they are// already running on CPU and there can be lots of them, so a woken up// waiter has good chances of losing. In such case it is queued at front// of the wait queue. If a waiter fails to acquire the mutex for more than 1ms,// it switches mutex to the starvation mode.//// In starvation mode ownership of the mutex is directly handed off from// the unlocking goroutine to the waiter at the front of the queue.// New arriving goroutines don't try to acquire the mutex even if it appears// to be unlocked, and don't try to spin. Instead they queue themselves at// the tail of the wait queue.//// If a waiter receives ownership of the mutex and sees that either// (1) it is the last waiter in the queue, or (2) it waited for less than 1 ms,// it switches mutex back to normal operation mode.//// Normal mode has considerably better performance as a goroutine can acquire// a mutex several times in a row even if there are blocked waiters.// Starvation mode is important to prevent pathological cases of tail latency.以上摘自golang源码中对mutex的正文,我感觉用来概括解释十分清晰 ...

August 21, 2022 · 4 min · jiezi

关于go:伙计Go项目怎么使用枚举

前言哈喽,大家好,我是asong。枚举是一种很重要的数据类型,在java、C语言等支流编程语言中都反对了枚举类型,然而在Go语言中却没有枚举类型,那有什么代替计划吗? 本文咱们来聊一聊这个事件;为什么要有枚举咱们以java语言为例子,在JDK1.5之前没有枚举类型,咱们通常会应用int常量来示意枚举,个别应用如下: public static final int COLOR_RED = 1;public static final int COLOR_BLUE = 2;public static final int COLOR_GREEN = 3;应用int类型会存在以下隐患: 不具备安全性,申明时如果没有应用final就会造成值被篡改的危险;语义不够明确,打印int型数字并不知道其具体含意于是乎咱们就想到用常量字符来示意,代码就变成了这样: public static final String COLOR_RED = "RED";public static final String COLOR_BLUE = "BLUE";public static final String COLOR_GREEN = "GREEN";这样也同样存在问题,因为咱们应用的常量字符,那么有些程序猿不按套路出牌就能够应用字符串的值进行比拟,这样的代码会被一直模拟变得越来越多的,而后屎山就呈现了; 所以咱们迫切需要枚举类型的呈现来起到束缚的作用,假如应用一个枚举类型做入参,枚举类型就能够限定沙雕用户不按套路传参,这样就能够怼他了,哈哈~; 应用枚举的代码就能够变成这样,传了枚举之外的类型都不能够了; public class EnumClass { public static void main(String [] args){ Color color = Color.RED; convert(color); System.out.println(color.name()); } public static void convert(Color c){ System.out.println(c.name()); }}enum Color{ RED,BLUE,GREEN;}Go语言就没有枚举类型,咱们该应用什么办法来代替呢? ...

August 21, 2022 · 2 min · jiezi

关于go:几个提升Go语言开发效率的小技巧

前言哈喽,大家好,我是asong。 每门语言都有本人的语法糖,像java的语法糖就有办法变长参数、拆箱与装箱、枚举、for-each等等,Go语言也不例外,其也有本人的语法糖,把握这些语法糖能够助咱们进步开发的效率,所以本文就来介绍一些Go语言的语法糖,总结的可能不能全,欢送评论区补充。 可变长参数Go语言容许一个函数把任意数量的值作为参数,Go语言内置了...操作符,在函数的最初一个形参能力应用...操作符,应用它必须留神如下事项: 可变长参数必须在函数列表的最初一个;把可变长参数当切片来解析,可变长参数没有没有值时就是nil切片可变长参数的类型必须雷同func test(a int, b ...int){ return}既然咱们的函数能够接管可变长参数,那么咱们在传参的时候也能够传递切片应用...进行解包转换为参数列表,append办法就是最好的例子: var sl []intsl = append(sl, 1)sl = append(sl, sl...)append办法定义如下: // slice = append(slice, elem1, elem2)// slice = append(slice, anotherSlice...)func append(slice []Type, elems ...Type) []Type申明不定长数组数组是有固定长度的,咱们在申明数组时肯定要申明长度,因为数组在编译时就要确认好其长度,然而有些时候对于想偷懒的我,就是不想写数组长度,有没有方法让他本人算呢?当然有,应用...操作符申明数组时,你只管填充元素值,其余的交给编译器本人去搞就好了; a := [...]int{1, 3, 5} // 数组长度是3,等同于 a := [3]{1, 3, 5}有时咱们想申明一个大数组,然而某些index想设置特地的值也能够应用...操作符搞定: a := [...]int{1: 20, 999: 10} // 数组长度是100, 下标1的元素值是20,下标999的元素值是10,其余元素值都是0init函数Go语言提供了先于main函数执行的init函数,初始化每个包后会主动执行init函数,每个包中能够有多个init函数,每个包中的源文件中也能够有多个init函数,加载程序如下: 从以后包开始,如果以后包蕴含多个依赖包,则先初始化依赖包,层层递归初始化各个包,在每一个包中,依照源文件的字典序从前往后执行,每一个源文件中,优先初始化常量、变量,最初初始化init函数,当呈现多个init函数时,则依照程序从前往后顺次执行,每一个包实现加载后,递归返回,最初在初始化以后包!init函数实现了sync.Once,无论包被导入多少次,init函数只会被执行一次,所以应用init能够利用在服务注册、中间件初始化、实现单例模式等等,比方咱们常常应用的pprof工具,他就应用到了init函数,在init函数外面进行路由注册: //go/1.15.7/libexec/src/cmd/trace/pprof.gofunc init() { http.HandleFunc("/io", serveSVGProfile(pprofByGoroutine(computePprofIO))) http.HandleFunc("/block", serveSVGProfile(pprofByGoroutine(computePprofBlock))) http.HandleFunc("/syscall", serveSVGProfile(pprofByGoroutine(computePprofSyscall))) http.HandleFunc("/sched", serveSVGProfile(pprofByGoroutine(computePprofSched))) http.HandleFunc("/regionio", serveSVGProfile(pprofByRegion(computePprofIO))) http.HandleFunc("/regionblock", serveSVGProfile(pprofByRegion(computePprofBlock))) http.HandleFunc("/regionsyscall", serveSVGProfile(pprofByRegion(computePprofSyscall))) http.HandleFunc("/regionsched", serveSVGProfile(pprofByRegion(computePprofSched)))}疏忽导包Go语言在设计师有代码洁癖,在设计上尽可能防止代码滥用,所以Go语言的导包必须要应用,如果导包了然而没有应用的话就会产生编译谬误,但有些场景咱们会遇到只想导包,然而不应用的状况,比方上文提到的init函数,咱们只想初始化包里的init函数,然而不会应用包内的任何办法,这时就能够应用 _ 操作符号重命名导入一个不应用的包: ...

August 21, 2022 · 2 min · jiezi

关于go:超全总结Go语言如何操作文件

前言哈喽,大家好,我是asong。 咱们都晓得在Unix中万物都被称为文件,文件解决是一个十分常见的问题,所以本文就总结了Go语言操作文件的常见形式,整体思路如下: Go语言版本:1.18 本文所有代码曾经上传github:https://github.com/asong2020/... 操作文件包含哪些操作一个文件离不开这几个动作: 创立文件关上文件读取文件写入文件敞开文件打包/解包压缩/解压缩扭转文件权限删除文件挪动文件重命名文件清空文件所以本文就针对这些操作总结了一些示例办法供大家参考; Go语言操作文件可应用的库Go语言官网库:os、io/ioutil、bufio涵盖了文件操作的所有场景, os提供了对文件IO间接调用的办法,bufio提供缓冲区操作文件的办法,io/ioutil也提供对文件IO间接调用的办法,不过Go语言在Go1.16版本曾经弃用了io/ioutil库,这个io/ioutil包是一个定义不明确且难以了解的货色汇合。该软件包提供的所有性能都已移至其余软件包,所以io/ioutil中操作文件的办法都在io库有雷同含意的办法,大家当前在应用到ioutil中的办法是能够通过正文在其余包找到对应的办法。 文件的根底操作这里我把 创立文件、关上文件、敞开文件、扭转文件权限这些归为对文件的基本操作,对文件的基本操作间接应用os库中的办法即可,因为咱们须要进行IO操作,来看上面的例子: import ( "log" "os")func main() { // 创立文件 f, err := os.Create("asong.txt") if err != nil{ log.Fatalf("create file failed err=%s\n", err) } // 获取文件信息 fileInfo, err := f.Stat() if err != nil{ log.Fatalf("get file info failed err=%s\n", err) } log.Printf("File Name is %s\n", fileInfo.Name()) log.Printf("File Permissions is %s\n", fileInfo.Mode()) log.Printf("File ModTime is %s\n", fileInfo.ModTime()) // 扭转文件权限 err = f.Chmod(0777) if err != nil{ log.Fatalf("chmod file failed err=%s\n", err) } // 扭转拥有者 err = f.Chown(os.Getuid(), os.Getgid()) if err != nil{ log.Fatalf("chown file failed err=%s\n", err) } // 再次获取文件信息 验证扭转是否正确 fileInfo, err = f.Stat() if err != nil{ log.Fatalf("get file info second failed err=%s\n", err) } log.Printf("File change Permissions is %s\n", fileInfo.Mode()) // 敞开文件 err = f.Close() if err != nil{ log.Fatalf("close file failed err=%s\n", err) } // 删除文件 err = os.Remove("asong.txt") if err != nil{ log.Fatalf("remove file failed err=%s\n", err) }}写文件快写文件os/ioutil包都提供了WriteFile办法能够疾速解决创立/关上文件/写数据/敞开文件,应用示例如下: ...

August 21, 2022 · 6 min · jiezi

关于go:面试官哥们Go语言的读写锁了解多少

前言哈喽,大家好,我是asong。 在上一文中:面试官:哥们Go语言互斥锁理解到什么水平了?咱们一起学习了Go语言中互斥锁是如何实现的,本文咱们就来一起学习Go语言中读写锁是如何设计的,互斥锁能够保障多线程在拜访同一片内存时不会呈现竞争来保障并发平安,因为互斥锁锁定代码临界区,所以当并发量较高的场景下会加剧锁竞争,执行效率就会越来越差;因而就引申出更细粒度的锁:读写锁,实用于读多写少的情景,接下来咱们就具体看看读写锁。 Golang版本:1.118 读写锁简介互斥锁咱们都晓得会锁定代码临界区,当有一个goroutine获取了互斥锁后,任何goroutine都不能够获取互斥锁,只能期待这个goroutine将互斥锁开释,无论读写操作都会加上一把大锁,在读多写少场景效率会很低,所以大佬们就设计出了读写锁,读写锁顾名思义是一把锁分为两局部:读锁和写锁,读锁容许多个线程同时取得,因为读操作自身是线程平安的,而写锁则是互斥锁,不容许多个线程同时取得写锁,并且写操作和读操作也是互斥的,总结来说:读读不互斥,读写互斥,写写互斥; 为什么要有读锁有些敌人可能会有纳闷,为什么要有读锁,读操作又不会批改数据,多线程同时读取雷同的资源就是平安的,为什么还要加一个读锁呢? 举个例子阐明,在Golang中变量的赋值不是并发平安的,比方对一个int型变量执行count++操作,在并发下执行就会呈现预期之外的后果,因为count++操作分为三局部:读取count的值、将count的值加1,而后再将后果赋值给count,这不是一个原子性操作,未加锁时在多个线程同时对该变量执行count++操作会造成数据不统一,通过加上写锁能够解决这个问题,然而在读取的时候咱们不加读锁会怎么样呢?写个例子来看一下,只加写锁,不加读锁: package mainimport "sync"const maxValue = 3type test struct { rw sync.RWMutex index int}func (t *test) Get() int { return t.index}func (t *test)Set() { t.rw.Lock() t.index++ if t.index >= maxValue{ t.index =0 } t.rw.Unlock()}func main() { t := test{} sw := sync.WaitGroup{} for i:=0; i < 100000; i++{ sw.Add(2) go func() { t.Set() sw.Done() }() go func() { val := t.Get() if val >= maxValue{ print("get value error| value=", val, "\n") } sw.Done() }() } sw.Wait()}运行后果: ...

August 21, 2022 · 3 min · jiezi

关于go:GoLand-2022-注册码

GoLand 2022是Mac上弱小的GO语言集成开发工具,领有语法高亮显示,内置终端,内置工具和集成,代码生成,疾速导航,重构,语法高亮显示等多种性能,为前段和后端的开发提供了强有力的工具。 GoLand 2022 注册码

August 21, 2022 · 1 min · jiezi

关于go:go源码系列之syncRWMutex-读写锁

前言golang 的sync包下有种锁,一种是sync.RWMutex,另一种是sync.Mutex,本文将解说下sync.RWMutex是如何实现的?实用于什么场景?如何防止读/写 饥饿问题?就让咱们带着这些问题来看源码是如何实现的 例子package mainimport ( "fmt" "math/rand" "sync")type Content struct { rw sync.RWMutex val int}func (c *Content) Read() int { c.rw.RLock() defer c.rw.RUnlock() return c.val}func (c *Content) Write(v int) { c.rw.Lock() defer c.rw.Unlock() c.val = v}func main() { const ( readerNum = 100 writerNum = 3 ) content := new(Content) var wg sync.WaitGroup for i := 0; i < writerNum; i++ { wg.Add(1) go func() { defer wg.Done() content.Write(rand.Intn(10)) }() } for i := 0; i < readerNum; i++ { wg.Add(1) go func() { defer wg.Done() fmt.Println(content.Read()) }() }}互斥性读读不互斥读写互斥写写互斥源码type RWMutex struct { w Mutex // held if there are pending writers //当要获取写锁时,须要对w加锁 writerSem uint32 // semaphore for writers to wait for completing readers //writers应用的信号量,用于期待readers实现读操作 readerSem uint32 // semaphore for readers to wait for completing writers //readers应用的信号量,用于期待writers实现写申请 readerCount int32 // number of pending readers //以后正在读的readers数量,也即曾经获取读锁胜利的数量 readerWait int32 // number of departing readers //期待readers实现读操作的数量,从readerCount拷贝过去,用于写锁申请时,示意还剩多少读锁未开释}获取读锁func (rw *RWMutex) RLock() { ... if atomic.AddInt32(&rw.readerCount, 1) < 0 { // A writer is pending, wait for it. runtime_SemacquireMutex(&rw.readerSem, false, 0) } ...}若readerCount大于0时,阐明曾经有reader获取读锁,那么间接返回胜利,示意获取读锁胜利,若atomic.AddInt32(&rw.readerCount, 1)<0示意曾经有写锁再排队,此时写锁会将readerCount置为一个很小的正数(下文源码会解释),那么这个时候有reader来获取读锁时,只能在 readerSem中排队,这样就不会导致写锁饥饿. ...

August 21, 2022 · 2 min · jiezi

关于go:signal-SIGSEGV-segmentation-violation

出错的用法package mainimport "fmt"var a *stringfunc main() { fmt.Println(*a)}正确的用法package mainimport "fmt"var a = new(string)func main() { fmt.Println(*a)}起因var a *string只是申明了变量a,并没有初始化(分配内存),没有分配内存,*天然是取不到地址的。 所以在申明的同时应用内建函数new分配内存后再赋值即可解决。

August 21, 2022 · 1 min · jiezi

关于go:go-使用slice或者map-来聚合-一一对应的关系数据

最近在写一个需要如下,有5中金币类型,每种金币类型,有对应的金币数,以及金币支付下限。原来的代码,是如下写法: //金币类型const CoinType1 = 1const CoinType2 = 2const CoinType3 = 3const CoinType4 = 4const CoinType5 = 5//金币类型 对应的 金币数const CoinType1CoinNum = 11const CoinType2CoinNum = 12const CoinType3CoinNum = 13const CoinType4CoinNum = 14const CoinType5CoinNum = 15//金币类型 对应的 金币支付下限const CoinType1LimitTimes = 101const CoinType2LimitTimes = 102const CoinType3LimitTimes = 103const CoinType4LimitTimes = 104const CoinType5LimitTimes = 105随着金币类型的减少,每次,都要减少三个变量,且获取的时候,还要对应关系,想到用k,v构造来优化 变量太多,不利于保护,能不加新的,最好不加计划一:slice 来示意一一对应//金币类型const CoinType1 = 1const CoinType2 = 2const CoinType3 = 3const CoinType4 = 4const CoinType5 = 5var CoinNum = []int{ CoinType1: 11, CoinType2: 12, CoinType3: 13, CoinType4: 14, CoinType5: 15,}var CoinLimitTimes = []int{ CoinType1: 101, CoinType2: 102, CoinType3: 103, CoinType4: 104, CoinType5: 105,}计划二:map 来示意一一对应//金币类型const CoinType1 = 1const CoinType2 = 2const CoinType3 = 3const CoinType4 = 4const CoinType5 = 5var CoinNum = map[int]int{ CoinType1: 11, CoinType2: 12, CoinType3: 13, CoinType4: 14, CoinType5: 15,}var CoinLimitTimes = map[int]int{ CoinType1: 101, CoinType2: 102, CoinType3: 103, CoinType4: 104, CoinType5: 105,}

August 19, 2022 · 1 min · jiezi

关于go:go-bytesbuffer-介绍以及使用案例

1.简介bytes.buffer为go语言内置提供的一个 字节缓存区。源码在 src/bytes/buffer.go // A Buffer 提供了可见的 读,写办法// 零值是一个空字节slice.type Buffer struct { buf []byte // contents are the bytes buf[off : len(buf)] off int // read at &buf[off], write at &buf[len(buf)] lastRead readOp // last read operation, so that Unread* can work correctly.} 二、罕用的办法1、申明一个buffervar b bytes.Buffer //申明一个Buffer变量b := new(bytes.Buffer) //应用New申明Buffer变量b := bytes.NewBuffer(s []byte) //从一个[]byte切片,结构一个Bufferb := bytes.NewBufferString(s string) //从一个string变量,结构一个Buffer能够通过 b.Grow来初始化一个 容量大小为n的buffer // Grow 减少一个n长度的 buffer, 当应用Grow(n)后,至多能够有b个本人,能够写入到buffer外面,如果n是一个正数,Grow会panic,如果buffer不能Grow,会导致panic而后报错 ErrTooLargeb.Grow(n int)应用案例: func TestNew(t *testing.T) { b := bytes.Buffer{} b.Grow(100) t.Log(b) //空字符 b1 := bytes.NewBufferString("hello") t.Log(b1) //hello b2 := bytes.NewBuffer([]byte{'h', 'e', 'l', 'l', 'o'}) t.Log(b2) //hello}2、写入一个bufferb.Write(d []byte) (n int, err error) //将切片d写入Buffer尾部b.WriteString(s string) (n int, err error) //将字符串s写入Buffer尾部b.WriteByte(c byte) error //将字符c写入Buffer尾部b.WriteRune(r rune) (n int, err error) //将一个rune写入Buffer尾部应用案例: ...

August 19, 2022 · 1 min · jiezi

关于go:马哥高端Go语言百万并发高薪班7期2022最新完结无密含文档源码

download:马哥高端Go语言百万并发高薪班7期-2022最新完结无密含文档源码spring MVC源码探索之AbstractHandlerMethodMappingAbstractHandlerMethodMapping 是什么解释是这样的。/** Abstract base class for {@link HandlerMapping} implementations that definea mapping between a request and a {@link HandlerMethod}. *<p>For each registered handler method, a unique mapping is maintained withsubclasses defining the details of the mapping type {@code <T>}.@param <T> The mapping for a {@link HandlerMethod} containing the conditionsneeded to match the handler method to incoming request. */public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean 复制代码我的理解为AbstractHandlerMethodMapping为每个注册的handler method,对于每个子类映射类型都保护着其唯一的一个映射,就是保护handler method 和URL的关系。次要用于@Controller,@RequestMapping 等注解AbstractHandlerMethodMapping 实现了InitializingBean接口,InitializingBean是在程序启动的时候执行其唯一的afterPropertiesSet()方法,那咱们就先看一下启动时候的要做哪些操作。initHandlerMethods()/** ...

August 18, 2022 · 2 min · jiezi

关于go:Go十大常见错误第6篇slice初始化常犯的错误

前言这是Go十大常见谬误系列的第6篇:slice初始化常犯的谬误。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go十大常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 场景假如咱们晓得要创立的slice的长度,你会怎么创立和初始化这个slice? 比方咱们定义了一个构造体叫Bar,当初要创立一个slice,外面的元素就是Bar类型,而且该slice的长度是已知的。 办法1有的人可能这么来做,先定义slice var bars []Barbars := make([]Bar, 0)每次要往bars这个slice插入元素的时候,通过append来操作 bars = append(bars, barElement)slice实际上是一个构造体类型,蕴含3个字段,别离是 array: 是指针,指向一个数组,切片的数据理论都存储在这个数组里。len: 切片的长度。cap: 切片的容量,示意切片以后最多能够存储多少个元素,如果超过了现有容量会主动扩容。slice底层的数据结构定义如下: type slice struct { array unsafe.Pointer len int cap int}type Pointer *ArbitraryType如果依照下面的示例先创立一个长度为0的slice,那在append插入元素的过程中,bars这个slice会做主动扩容。 如果bars的长度比拟大,可能会产生屡次扩容。每次扩容都要创立一个新的内存空间,而后把旧内存空间的数据拷贝过去,效率比拟低。 办法2在定义slice的时候指定长度,代码示例如下: func convert(foos []Foo) []Bar { bars := make([]Bar, len(foos)) for i, foo := range foos { bars[i] = fooToBar(foo) } return bars}这行代码 bars := make([]Bar, len(foos))间接指定了slice的长度,无需扩容。 办法3在定义slice的时候提前指定容量,长度设置为0,代码示例如下: func convert(foos []Foo) []Bar { bars := make([]Bar, 0, len(foos)) for _, foo := range foos { bars = append(bars, fooToBar(foo)) } return bars}这种办法也能够,也无需扩容。 ...

August 18, 2022 · 1 min · jiezi

关于go:Go微服务入门到容器化实践落地可观测的微服务电商项目内附文档源码

download:Go微服务入门到容器化实际,落地可观测的微服务电商我的项目内附文档源码解析 Golang 定时工作库 gron 设计和原理简略说,每一个位都代表了一个工夫维度,* 代表选集,所以,下面的语义是:在每天早上的4点05分触发工作。但 cron 毕竟只是一个操作系统级别的工具,如果定时工作失败了,或者压根没启动,cron 是没法提醒开发者这一点的。并且,cron 和 正则表达式都有一种魔力,不知道大家是否感同身受,这里引用共事的一句名言: 这世界上有些语言非常相似: shell脚本, es查问的那个dsl语言, 定时工作的crontab, 正则表达式. 他们相似就相似在每次要写的时候基本都得从新现学一遍。 刚巧,最近看到了 gron 这个开源我的项目,它是用 Golang 实现一个并发安全的定时工作库。实现非常简略精美,代码量也不多。明天咱们就来一起拆散源码看一下,怎么基于 Golang 的能力做进去一个【定时工作库】。 Gron provides a clear syntax for writing and deploying cron jobs. gron 是一个泰国小哥在 2016 年开源的作品,它的个性就在于非常简略和清晰的语义来定义【定时工作】,你不必再去记 cron 的语法。咱们来看下作为使用者怎么上手。首先,咱们还是一个 go get 安装依赖:$ go get github.com/roylee0704/gron复制代码假设咱们期望在【时机】到了当前,要做的工作是打印一个字符串,每一个小时执行一次,咱们就可能这样:package main import ( "fmt""time""github.com/roylee0704/gron") func main() { c := gron.New()c.AddFunc(gron.Every(1*time.Hour), func() { fmt.Println("runs every hour.")})c.Start()}

August 18, 2022 · 1 min · jiezi

关于go:百亿数据百亿花-库若恒河沙复沙Go-lang118入门精炼教程由白丁入鸿儒Go-lang数据库操作实践EP12

Golang能够通过Gorm包来操作数据库,所谓ORM,即Object Relational Mapping(数据关系映射),说白了就是通过模式化的语法来操作数据库的行对象或者表对象,比照绝对灵便简约的SQL语句,ORM上手简略,通用性较高,然而在性能层面略有损耗,Gorm的底层是构造体对象,对于构造体,请移玉步至:你有对象类,我有构造体,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang构造体(struct)的应用EP06。 Gorm的装置与配置首先如果要应用Gorm操作数据库,得先有数据库才行,这里为了全平台统一标准,咱们应用Docker来装置Mysql数据库,Docker的装置请参见:一寸宕机一寸血,十万容器十万兵|Win10/Mac零碎下基于Kubernetes(k8s)搭建Gunicorn+Flask高可用Web集群,运行命令运行mysql容器: docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql:8.0.19留神这里如果宿主机曾经有Mysql服务了,须要将:右边的端口号错开,改成3307或者其余什么端口。 随后在终端运行命令装置Gorm包: go get -u github.com/jinzhu/gorm这里-u参数的意思是为以后用户装置,并不局限于某个我的项目。 随后装置Mysql数据库链接驱动包: go get -u github.com/go-sql-driver/mysql接着在任意地位编写test.go脚本: package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) func main() { db, err := gorm.Open("mysql", "root:root@(localhost)/mytest?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { fmt.Println(err) fmt.Println("连贯数据库出错") return } defer db.Close() fmt.Println("链接Mysql胜利") }这里导入输入包和Gorm包,同时通过下划线的模式导入mysql驱动包,这样做的益处是mysql驱动的init()函数会在被导入时执行,因为咱们并不需要驱动包的具体模块或者函数,而仅仅是用它连一下数据库而已。 随后,创立构造体变量db,留神Open函数对应的Mysql参数是否正确。 留神,构造体变量赋值过程中如果报错,须要判断err变量内容,并且应用return关键字提前结束逻辑,对于golang的错误处理,可参见:人非圣贤孰能无过,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang错误处理机制EP11 最初,应用defer关键字在所有逻辑执行后敞开Mysql数据库链接。 编译执行后,程序返回: 链接Mysql胜利当然Gorm并不仅仅只能操作Mysql,其余支流数据库也都反对,比方说Sqllite3,事实上,在老本无限或者缓存体系比拟齐备的状况下,Sqllite3齐全能够代替Mysql,首先装置sqllite3驱动: go get -u github.com/jinzhu/gorm/dialects/sqlite而后批改test.go文件: package main import ( "fmt" "github.com/jinzhu/gorm" //_ "github.com/jinzhu/gorm/dialects/mysql" _ "github.com/jinzhu/gorm/dialects/sqlite" ) func main() { //db, err := gorm.Open("mysql", "root:root@(localhost)/mytest?charset=utf8mb4&parseTime=True&loc=Local") db, err := gorm.Open("sqlite3", "/tmp/gorm.db") if err != nil { fmt.Println(err) fmt.Println("连贯数据库出错") return } defer db.Close() fmt.Println("链接sqllite3胜利") }编译执行后返回: ...

August 18, 2022 · 2 min · jiezi

关于go:一门语言的作用域和函数调用是如何实现的

前言上次利用 Antlr 重构一版 用 Antlr 重构脚本解释器 之后便着手新增其余性能,也就是当初看到的反对了作用域以及函数调用。 int b= 10;int foo(int age){ for(int i=0;i<10;i++){ age++; } return b+age;}int add(int a,int b) { int e = foo(10); e = e+10; return a+b+3+e;}add(2,20);// Output:65整个语法规定大部分参考了 Java,现阶段反对了: 函数申明与调用。函数调用的入栈和出栈,保障了函数局部变量在函数退出时销毁。作用域反对,外部作用域能够拜访内部作用域的变量。根本的表达式语句,如 i++, !=,==这次实现的重点与难点则是作用域与函数调用,实现之后也算是满足了我的好奇心,不过在讲作用域与函数调用之前先来看看一个简略的变量申明与拜访语句是如何实现的,这样后续的了解会更加容易。 变量申明int a=10;a;因为还没有实现内置函数,比方控制台输入函数 print(),所以这里就间接拜访变量也能拿到数据运行后后果如下: 首先看看变量申明语句的语法: variableDeclarators : typeType variableDeclarator (',' variableDeclarator)* ;variableDeclarator : variableDeclaratorId ('=' variableInitializer)? ;typeList : typeType (',' typeType)* ;typeType : (functionType | primitiveType) ('[' ']')* ;primitiveType : INT | STRING | FLOAT | BOOLEAN ; 只看语法不太直观,间接看下生成的 AST 树就明确了: 编译期 右边这棵 BlockVardeclar 树对应的就是 int a=10;,左边的 blockStm 对应的就是变量拜访 a。 ...

August 18, 2022 · 1 min · jiezi

关于go:sqlgen支持-gorm-xorm-sql-sqlx-bun-等-orm的新型代码生成工具

始终找不到一个好的 sqlgen 生成工具,参考 sqlc,goctl 写了一个工具,反对 bun, gorm, sql, sqlx, xorm 生成,应用还是比较简单,开发者只须要写一个 sql 文件即可通过 sqlgen 生成代码。 反对从 sql 文件和 dsn 两种数据起源 https://github.com/anqiansong...

August 17, 2022 · 1 min · jiezi

关于go:go-file-文件权限

一、文件权限简介1、咱们应用 ls -lih 失去本目录具体的文件详情➜ file git:(master) ✗ ls -lih 57935473 -rw-r--r-- 1 zhanghaisheng staff 12B Aug 16 15:23 a.txt57963493 -rw-r--r-- 1 zhanghaisheng staff 309B Aug 17 09:43 dir_test.go整顿如下: inode权限硬链接个数属主所归属的组文件的大小最初拜访或批改工夫文件名57935473-rw-r--r--1zhanghaishengstaff12BAug 16 15:23a.txt57963493-rw-r--r--1zhanghaishengstaff309BAug 17 09:43dir_test.go2、文件的权限linux 下有2种文件权限示意形式,即“符号示意”和“八进制示意”。 1、所有者符号示意形式: - --- --- ---type owner group others任何文件都有使用者,群组,other的概念。文件的使用者:1 拥有者2 所在群组的用户3 其余组的用户 2、文件权限符号示意形式:这里简略的说一下 r w x 别离代表的是什么意思r示意可读w示意可写x示意可执行如果没有那一个权限,用 - 代替例如:-rw-r--r--权限所代表的是r = 4, w = 2, x = 1 二、go读取权限func TestStat(t *testing.T) { f, err := os.Stat("a.txt") if errors.Is(err, os.ErrNotExist) { t.Log("文件不存在") } else { t.Log(f.Mode()) }}输入: ...

August 17, 2022 · 1 min · jiezi

关于go:Go高性能

Benchmark(基准测评) 参考文章:https://blog.logrocket.com/be... 字符串拼接性能及其原理 Go官网中举荐应用StringBuffer来拼接字符串在Go语言中,字符串是不可变的,拼接字符串事实上是创立了一个新的字符串变量。可想而知,如果大量应用"+"运算符,则程序的性能将大打折扣。string底层应用了byte数组... 常见的拼接形式https://geektutu.com/post/hpg...1 应用 + func plusConcat(n int, str string) string { s := "" for i := 0; i < n; i++ { s += str } return s}2 应用 fmt.Sprintf func sprintfConcat(n int, str string) string { s := "" for i := 0; i < n; i++ { s = fmt.Sprintf("%s%s", s, str) } return s}3 应用 bytes.Buffer func bufferConcat(n int, s string) string { buf := new(bytes.Buffer) for i := 0; i < n; i++ { buf.WriteString(s) } return buf.String()}4 应用 []byte ...

August 17, 2022 · 2 min · jiezi

关于go:go-file-文件常见增删改查

一、go查看文件是否存在func TestStat(t *testing.T) { f, err := os.Stat("hi.txt") if errors.Is(err, os.ErrNotExist) { t.Log("文件不存在") } else { t.Log(f) }}二、go创立文件func TestCreate(t *testing.T) { f, err := os.Create("hi.txt") defer f.Close() if err != nil { t.Fatal(err) } t.Log(f)}三、go关上一个文件1.只读func TestOpen(t *testing.T) { f, err := os.Open("hi.txt") defer f.Close() if err != nil { t.Fatal(err) } t.Log(f.Name())}2.读、写func TestOpenFile(t *testing.T) { f, err := os.OpenFile("hi.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { t.Fatal(err) } defer f.Close() if _, err2 := f.WriteString("hello world"); err2 != nil { t.Fatal(err2) }}咱们关上一下hi.txt会发现,写入了内容。 ...

August 17, 2022 · 1 min · jiezi

关于go:Go入门系列变量声明

首先明确一点就是Go语言是动态语言,意味着对于变量的应用来说,必须先进行申明之后才能够应用。变量的申明办法通用的变量申明格局如下: 这里和kotlin的变量申明形式有点相似, 比方都是把变量类型放在变量名前面,只不过,kotlin多一个“:”符号以及“;”分号,在之前的Go入门系列中咱们提到Go编码中能够省略尾部的分号的。如下是Kotlin变量通用申明格局:/*关键字 变量类型 ↓ ↓ */var price: Int = 100; /* ↑ ↑变量名 变量值 */复制代码Go语言还提供了变量申明块(block)的语法模式,能够用一个 var 关键字将多个变量申明放在一起,示例如下var ( a int = 110b int8 = 8s string = "hello world"c rune = 'D' // Go中字符类型t bool = true)复制代码以及在一行中申明多个变量,如下格局var a, b, c int = 1, 2, 3等价于:var a int = 1var b int = 2var c int = 3复制代码变量类型初值 与此同时,Go为开发者更好的应用,提供了两种“语法糖” 省略类型信息的申明:Go开发中反对省略类型信息,由go语言主动依据左边变量初值主动推导出变量类型,并赋予变量初值对应的默认类型。示例如下, 会主动推导出b的类型为int.var b = 8复制代码如果不想应用默认类型,能够通过如下形式显示的设置所需类型。var b = int32(13)复制代码短变量申明:应用短变量申明时,咱们甚至能够省去var关键字以及类型信息,它的规范范式是“varName := initExpression”。a := 16b := 'A'c := "hello world"复制代码变量的分类包级变量在包级别可见的变量。如果是导出变量(大写字母结尾),那么这个包级变量也能够被视为全局变量。包级变量只能应用带有 var 关键字的变量申明模式,不能应用短变量申明模式。也就是只能应用var b int = 100这种申明模式, 而不能应用b := 100 这种模式。申明并初始化var varName = initExpression// 多变量var ( a = 13 b = int32(17) f = float32(3.14))复制代码申明但提早初始化。对于申明后不显示初始化的变量,能够通过如下模式进行申明。var a int32var b float64复制代码就近准则咱们尽可能在凑近第一次应用变量的地位申明这个变量。局部变量Go函数或办法体内申明的变量,仅在函数或办法体内可见,这种在编程语言通用的概念。申明但提早初始化的局部变量,应用通用形式, 和包级变量一样。var a string复制代码对于申明且显式初始化的局部变量,倡议应用短变量申明模式a := 100b := "hello world"复制代码 ...

August 17, 2022 · 1 min · jiezi

关于go:go-获取当前目录绝对路径pwd

一、go应用os.Getwd()获取当前目录的绝对路径,等同于pwdfunc TestPwd(t *testing.T) { pwd, err := os.Getwd() t.Log(pwd, err)}输入: /Users/zhanghaisheng/study/dataStructure/file <nil>二、源码浏览os.Getwd()源码门路在 src/os/getwd.go // Getwd 获取当前目录绝对路径func Getwd() (dir string, err error) { if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return syscall.Getwd() } // Clumsy but widespread kludge: // if $PWD is set and matches ".", use it. dot, err := statNolog(".") if err != nil { return "", err } dir = Getenv("PWD") if len(dir) > 0 && dir[0] == '/' { d, err := statNolog(dir) if err == nil && SameFile(dot, d) { return dir, nil } }.........省略咱们看到 有一行代码 ...

August 17, 2022 · 1 min · jiezi

关于go:Go入门系列变量声明

首先明确一点就是Go语言是动态语言,意味着对于变量的应用来说,必须先进行申明之后才能够应用。 变量的申明办法通用的变量申明格局如下: 这里和kotlin的变量申明形式有点相似, 比方都是把变量类型放在变量名前面,只不过,kotlin多一个“:”符号以及“;”分号,在之前的Go入门系列中咱们提到Go编码中能够省略尾部的分号的。 如下是Kotlin变量通用申明格局: /*关键字 变量类型 ↓ ↓ */var price: Int = 100; /* ↑ ↑ 变量名 变量值 */Go语言还提供了变量申明块(block)的语法模式,能够用一个 var 关键字将多个变量申明放在一起,示例如下 var ( a int = 110 b int8 = 8 s string = "hello world" c rune = 'D' // Go中字符类型 t bool = true)以及在一行中申明多个变量,如下格局 var a, b, c int = 1, 2, 3等价于:var a int = 1var b int = 2var c int = 3变量类型初值 ...

August 17, 2022 · 1 min · jiezi

关于go:彩虹女神跃长空Go语言进阶之Go语言高性能Web框架Iris项目实战项目入口与路由EP01

书接上回,咱们曾经装置好Iris框架,并且构建好了Iris我的项目,同时配置了fresh主动监控我的项目的实时编译,万事俱备,只欠东风,彩虹女神蓄势待发。当初咱们来看看Iris的根底性能,如何编写我的项目入口文件以及配置路由零碎。 我的项目入口事实上,Iris遵循的是繁多入口模式,说白了就是繁多入口文件main.go解决我的项目所有的起源申请,如此,我的项目就防止了因为多个文件解决不同的申请而减少的安全性危险,同时也更便于我的项目的兼顾治理。在上一篇文章:急如闪电快如风,彩虹女神跃长空,Go语言进阶之Go语言高性能Web框架Iris我的项目实战-初始化我的项目EP00中,咱们曾经编写好了入口文件main.go: package main import "github.com/kataras/iris/v12" func main() { app := iris.New() app.Use(iris.Compression) app.Get("/", func(ctx iris.Context) { ctx.HTML("你好 <strong>%s</strong>!", "女神") }) app.Listen(":5000") }这里解释一下各行代码含意,首先申明包名: package main随后导入Iris包,留神这里的版本是最新的v12: import "github.com/kataras/iris/v12"接着申明入口(main)函数,并且初始化Iris构造体: app := iris.New()随后加载iris.Compression模块: app.Use(iris.Compression)这里Compression是Iris外部对IO数据进行压缩的模块,能够进步数据传输速度。 接着编写路由注册,并应用ctx构造体变量来打印数据: app.Get("/", func(ctx iris.Context) { ctx.HTML("你好 <strong>%s</strong>!", "女神") })最初监听系统的5000端口: app.Listen(":5000")在此基础上,进行进一步的革新: type Article struct { Title string `json:"题目"` }这里咱们申明一个叫做Artile(文章)的构造体,该构造体能够了解为博客零碎中文章的对象类,构造体内有一个数据类型为字符串的字段(属性)Title(题目),其隐射到Json后果的形容为“题目”。 接着申明函数: func list(ctx iris.Context) { article := []Article{ {"iris第一章"}, {"iris第二章"}, {"iris第三章"}, } ctx.JSON(article) }这里咱们申明一个叫做list的函数,参数为ctx构造体,作用是将文章的题目列表(切片),通过Json的模式返回。 最初将全局注册的路由,革新为子路由注册: articleAPI := app.Party("/") { articleAPI.Use(iris.Compression) articleAPI.Get("/", list) }这里应用Iris构造体变量内置的Party办法。 ...

August 16, 2022 · 3 min · jiezi

关于go:illegal-base64-data-at-input-byte

问题d, err := base64.StdEncoding.DecodeString(param)if err != nil { panic(err) // illegal base64 data at input byte}golang从javascript申请的URL中读出base64编码的字符串参数param,在解码时报错。 通过比照javascript生成的参数和golang接管到的参数发现,原数据中的+被替换为了空格,进而导致解码失败。 解决在golang中将空格替换为原来的+: d, err := base64.StdEncoding.DecodeString( strings.Replace(param, " ", "+", -1))这样的确能够解决眼下的问题,但实质问题还是没有解决,因为URL中的特殊字符不止+一个。 所以: 除非参数的内容百分百确定没有特殊字符,否则应该应用body传参。

August 16, 2022 · 1 min · jiezi

关于go:go源码阅读-实现itoa

一、简介itoa 全称是 int 桶 ascii在c语言,go语言等,都有原生的实现反对。我在源码 src/os/str.go 发现了go的实现 // 简略的实现itoa防止再去调用strconv.package os// Convert integer to decimal stringfunc itoa(val int) string { if val < 0 { return "-" + uitoa(uint(-val)) } return uitoa(uint(val))}// Convert unsigned integer to decimal stringfunc uitoa(val uint) string { if val == 0 { // avoid string allocation return "0" } var buf [20]byte // big enough for 64bit value base 10 i := len(buf) - 1 for val >= 10 { q := val / 10 buf[i] = byte('0' + val - q*10) i-- val = q } // val < 10 buf[i] = byte('0' + val) return string(buf[i:])}二、源码浏览1、辨别正负号// 把int类型转化成10进制对应的stringfunc itoa(val int) string { if val < 0 { return "-" + uitoa(uint(-val)) } return uitoa(uint(val))}先判断是否小于0,来增加“-”负号 ...

August 16, 2022 · 1 min · jiezi

关于go:go源码阅读-使用osStat-获取-file-文件信息

一、建设一个文件a.txtmkdir stat && cd stat && touch a.txt && touch stat_test.go二、应用 os.Stat() 获取 file 文件信息关上 stat_test.go 输出: package statimport ( "os" "testing")func TestStat(t *testing.T) { fileInfo, err := os.Stat("a.txt") if err != nil { t.Error(err) } t.Log(fileInfo.Name()) //文件名称 t.Log(fileInfo.Size()) //文件的大小 t.Log(fileInfo.Mode()) //fileMode t.Log(fileInfo.ModTime()) //批改工夫 t.Log(fileInfo.IsDir()) //是否是目录}输入: a.txt0-rw-r--r--2022-08-16 15:23:46.956139863 +0800 CSTfalse三、os.Stat()源码剖析在源码src/os/stat.go咱们能够看到 // Stat 返回 一个形容这个文件的 FileInfo 接口 .// If there is an error, it will be of type *PathError.func Stat(name string) (FileInfo, error) { testlog.Stat(name) return statNolog(name)}咱们在看一下 FileInfo 接口 ...

August 16, 2022 · 1 min · jiezi

关于go:go-使用-context-退出子协程-goroutine

一、案例先看上面一段代码,子协程要在 100个小时候退出,导致主协程始终卡在那边。 func TestCancel1(t *testing.T) { wg := sync.WaitGroup{} wg.Add(1) go func() { t.Log("goroutine 1") time.Sleep(100 * time.Hour) wg.Done() }() wg.Wait() t.Log("goroutine main")}输入:可见主协程始终 期待状态,没发退出。 二、应用context.WithTimeout,给协程一个退出工夫咱们在上面的代码里,写了一个context.WithTimeout给3秒,3秒后这个程序没有执行完就退出。 func TestTimeout(t *testing.T) { wg := sync.WaitGroup{} wg.Add(1) parentContext := context.Background() cancelCtx, cancelFunc := context.WithTimeout(parentContext, time.Second*3) go func(ctx context.Context) { for { t.Log("goroutine 1") select { case <-cancelCtx.Done(): wg.Done() return case <-time.After(time.Hour * 100): wg.Done() } } }(cancelCtx) wg.Wait() cancelFunc() t.Log("goroutine main")}三、应用context.WithCancel,在子协程退出func TestCancel2(t *testing.T) { wg := sync.WaitGroup{} wg.Add(1) parentContext := context.Background() cancelCtx, cancelFunc := context.WithCancel(parentContext) go func(ctx context.Context) { for { select { case <-cancelCtx.Done(): return case <-time.After(time.Hour * 100): cancelFunc() wg.Done() case <-time.After(time.Second * 3): cancelFunc() wg.Done() } t.Log("goroutine 1") } }(cancelCtx) wg.Wait() t.Log("goroutine main")}输入: ...

August 16, 2022 · 1 min · jiezi

关于go:go使用recover来捕捉panic防止程序崩溃

一、在协程中的panic(),导致整个程序解体func TestPanic(t *testing.T) { t.Log("1") go func() { panic("panic") }() time.Sleep(time.Second * 1) //加一下,避免 多个协程(panic协程还没走到,主协程就完结了) t.Log("2")}输入: 1panic: panicgoroutine 7 [running]:github.com/hisheng/dataStructure/panic.TestPanic.func1() /Users/zhanghaisheng/study/dataStructure/panic/panic_test.go:16 +0x39created by github.com/hisheng/dataStructure/panic.TestPanic /Users/zhanghaisheng/study/dataStructure/panic/panic_test.go:15 +0x85咱们能够看到,子协程的panic,间接导致了,主协程也蹦了,没法执行上来。 二、go应用recover()来捕获panic(),避免程序解体func TestRecover(t *testing.T) { t.Log("1") go func() { defer func() { recover() }() panic("panic") }() time.Sleep(time.Second * 1) //加一下,避免 多个协程(panic协程还没走到,主协程就完结了) t.Log("2")}输入: 12三、go应用recover()来做一些逻辑解决recover()有返回值,咱们能够用 recover() != nil来做一些解决 //当panic的时候写入到日志外面func TestRecover2(t *testing.T) { t.Log("1") go func() { defer func() { r := recover() if r != nil { //写入日志 } }() panic("panic") }() time.Sleep(time.Second * 1) //加一下,避免 多个协程(panic协程还没走到,主协程就完结了) t.Log("2")}

August 15, 2022 · 1 min · jiezi

关于go:人非圣贤孰能无过Go-lang118入门精炼教程由白丁入鸿儒Go-lang错误处理机制EP11

人非圣贤,孰能无过,有则改之,无则加勉。在编程语言层面,错误处理形式大体上有两大流派,别离是以Python为代表的异样捕捉机制(try....catch);以及以Go lang为代表的谬误返回机制(return error),前者是自动化流程,模式化的语法隔离失常逻辑和谬误逻辑,而后者,须要将错误处理判断编排在失常逻辑中。尽管模式化语法更容易让人了解,但从系统资源开销角度看,谬误返回机制显著更具劣势。 返回谬误Go lang的谬误(error)也是一种数据类型,谬误用内置的error 类型示意,就像其余的数据类型的,比方字符串、整形之类,谬误的具体值能够存储在变量中,从函数中返回: package main import "fmt" func handle() (int, error) { return 1, nil } func main() { i, err := handle() if err != nil { fmt.Println("报错了") return } fmt.Println("逻辑失常") fmt.Println(i) }程序返回: 逻辑失常 1这里的逻辑是,如果handle函数胜利执行并且返回,那么入口函数就会失常打印返回值i,假如handel函数执行过程中呈现谬误,将返回一个非nil谬误。 如果一个函数返回一个谬误,那么实践上,它必定是函数返回的最初一个值,因为在执行阶段中可能会返回失常的值,而谬误地位是未知的,所以,handle函数返回的值是最初一个值。 go lang中处理错误的常见形式是将返回的谬误与nil进行比拟。nil值示意没有产生谬误,而非nil值示意呈现谬误。在咱们的例子中,咱们查看谬误是否为nil。如果它不是nil,咱们会通过fmt.Println办法揭示用户并且从主函数返回,完结逻辑。 再来个例子: package main import ( "fmt" "net/http" ) func main() { resp, err := http.Get("123123") if err != nil { fmt.Println(err) return } fmt.Println(resp.StatusCode) }这回咱们应用规范库包http向一个叫做123123的网址发动申请,当然了,申请过程中有可能产生一些未知谬误,所以咱们应用err变量获取Get办法的最初一个返回值,如果err不是nil,那么就阐明申请过程中报错了,这里打印具体谬误,而后从主函数中返回。 程序返回: Get "123123": unsupported protocol scheme ""很显著,必定报错了,因为Go lang并不知道所谓的123123到底是什么网络协议。 ...

August 15, 2022 · 3 min · jiezi

关于go:急如闪电快如风彩虹女神跃长空Go语言高性能Web框架Iris项目实战初始化项目ep00

在Golang Web编程的世界里,君不言高性能则已,言高性能必称Iris。彩虹女神的名号响彻寰宇、名动江湖,单论一个快字,无人能出其右,就连以简洁轻量著称于世的Gin也难以望其项背,只见彩虹女神Iris回眸一笑撩人心扉:“尽管你们也不是那么慢,但我还是快那么一点点......”,本次就让咱们来一睹彩虹女神Iris的芳颜,感触宇宙最快Web框架的神乎其神。 女神本神(Iris)抉择一款框架有诸多的参考层面,比方灵活性、扩展性、API敌对水平、文档具体水平、我的项目活跃度、社区奉献等等,然而性能和内存占用相对是优先参考的一个重要层面,原因无他,天下文治,唯快不破,正所谓一快遮百丑,经济上行,降本增效的大背景之下,高性能框架无疑占据极大的劣势,说白了,老本相仿的前提下,我单位工夫内网络申请吞吐量是你的一倍,还没使劲,你就倒下了,你怎么跟我打?游戏还没开始,就曾经完结了。 空口白牙,不足为据,参见2022年最新申请吞吐量比照图: 事实上,Iris实质上也是社区驱动的Go语言Web框架,反对http2/3,齐备的MVC反对,奉行极简主义格调,轻量化与扼要格调比起Gin来说,也不遑多让,与此同时,社区活跃度和文档反对都十分到位,但其领有的极其恐怖的高性能个性,其余框架则是可望不可即。在Iris身上,咱们能够看到她对性能的近乎于偏执的完满谋求,Iris为了优化性能,不惜本人开发和集成最快的组件,比方日志记录内置了golog模块,比方json序列化就抉择了第三方库jsoniter,从框架设计的态度上,极尽完满之能事。 建设我的项目IrisBlog参照Iris官网文档:https://github.com/kataras/ir...\_ZH.md,咱们借助彩虹女神Iris的垂爱,打造一款史上最快的在线博客零碎,首先建设文件夹IrisBlog: mkdir IrisBlogcd IrisBlog随后通过go mod命令初始化我的项目: C:\Users\liuyue\www\iriblog>go mod init IrisBlog go: creating new go.mod: module IrisBlog对于go mod不熟的敌人,请移玉步至层次分明井然有条,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang包管理机制(package)EP10,对于go mod的应用,这里不再赘述。 接着,因为诸位能够了解的起因,请确保应用国内的装置源: go env -w GOPROXY=https://goproxy.cn,direct随后装置彩虹女神Iris: go get -u github.com/kataras/iris零碎返回: C:\Users\liuyue\www\iriblog>go get -u github.com/kataras/iris go: downloading github.com/kataras/iris v0.0.2 go: downloading github.com/BurntSushi/toml v0.3.1 go: downloading github.com/kataras/golog v0.0.18 go: downloading github.com/kataras/pio v0.0.8 go: downloading github.com/kataras/sitemap v0.0.5 go: downloading github.com/BurntSushi/toml v1.2.0 go: downloading github.com/kataras/tunnel v0.0.1 go: downloading github.com/kataras/golog v0.1.7 go: downloading github.com/kataras/pio v0.0.10 go: downloading gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 go: downloading github.com/kataras/tunnel v0.0.4 go: downloading gopkg.in/yaml.v3 v3.0.1 go: downloading github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 go: downloading github.com/fatih/structs v1.1.0 go: downloading github.com/andybalholm/brotli v1.0.1-0.20200619015827-c3da72aa01ed go: downloading github.com/iris-contrib/schema v0.0.2 go: downloading github.com/andybalholm/brotli v1.0.4 go: downloading github.com/iris-contrib/schema v0.0.6 go: downloading github.com/json-iterator/go v1.1.10 go: downloading github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 go: downloading github.com/klauspost/compress v1.10.10 go: downloading github.com/klauspost/compress v1.15.9 go: downloading github.com/microcosm-cc/bluemonday v1.0.3 go: downloading github.com/russross/blackfriday/v2 v2.0.1 go: downloading github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1 go: downloading golang.org/x/net v0.0.0-20200707034311-ab3426394381 go: downloading github.com/russross/blackfriday v1.5.2 go: downloading github.com/vmihailenco/msgpack v4.0.4+incompatible go: downloading github.com/microcosm-cc/bluemonday v1.0.19 go: downloading github.com/vmihailenco/msgpack/v5 v5.3.5 go: downloading golang.org/x/text v0.3.3 go: downloading golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e go: downloading github.com/russross/blackfriday/v2 v2.1.0 go: downloading github.com/russross/blackfriday v1.6.0 go: downloading google.golang.org/protobuf v1.25.0 go: downloading golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 go: downloading golang.org/x/net v0.0.0-20220812174116-3211cb980234 go: downloading google.golang.org/protobuf v1.28.1 go: downloading golang.org/x/text v0.3.7 go: downloading golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de go: downloading golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9 go: downloading github.com/schollz/closestmatch v2.1.0+incompatible go: downloading golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa go: downloading golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab go: downloading gopkg.in/ini.v1 v1.57.0 go: downloading gopkg.in/ini.v1 v1.67.0 go: downloading github.com/ryanuber/columnize v2.1.0+incompatible go: downloading github.com/CloudyKit/jet/v4 v4.1.0 go: downloading github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible go: downloading github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 go: downloading github.com/ryanuber/columnize v2.1.2+incompatible go: downloading github.com/iris-contrib/jade v1.1.4 go: downloading github.com/CloudyKit/jet v2.1.2+incompatible go: downloading github.com/iris-contrib/pongo2 v0.0.1 go: downloading github.com/kataras/blocks v0.0.2 go: downloading github.com/yosssi/ace v0.0.5 go: downloading github.com/kataras/blocks v0.0.6 go: downloading github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 go: downloading github.com/chris-ramon/douceur v0.2.0 go: downloading github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd go: downloading github.com/vmihailenco/tagparser v0.1.1 go: downloading google.golang.org/appengine v1.6.5 go: downloading github.com/vmihailenco/tagparser v0.1.2 go: downloading github.com/shurcooL/sanitized_anchor_name v1.0.0 go: downloading google.golang.org/appengine v1.6.7 go: downloading github.com/google/uuid v1.1.2-0.20200519141726-cb32006e483f go: downloading github.com/google/uuid v1.3.0 go: downloading github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 go: downloading github.com/valyala/bytebufferpool v1.0.0 go: downloading github.com/aymerick/douceur v0.2.0 go: downloading github.com/gorilla/css v1.0.0 go: downloading github.com/golang/protobuf v1.4.1 go: downloading github.com/golang/protobuf v1.5.2 go: downloading github.com/vmihailenco/tagparser/v2 v2.0.0 go: added github.com/BurntSushi/toml v1.2.0 go: added github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 go: added github.com/CloudyKit/jet/v4 v4.1.0 go: added github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 go: added github.com/andybalholm/brotli v1.0.4 go: added github.com/aymerick/douceur v0.2.0 go: added github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible go: added github.com/chris-ramon/douceur v0.2.0 go: added github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 go: added github.com/fatih/structs v1.1.0 go: added github.com/golang/protobuf v1.5.2 go: added github.com/google/uuid v1.3.0 go: added github.com/gorilla/css v1.0.0 go: added github.com/iris-contrib/jade v1.1.4 go: added github.com/iris-contrib/pongo2 v0.0.1 go: added github.com/iris-contrib/schema v0.0.6 go: added github.com/json-iterator/go v1.1.12 go: added github.com/kataras/blocks v0.0.6 go: added github.com/kataras/golog v0.1.7 go: added github.com/kataras/iris v0.0.2 go: added github.com/kataras/pio v0.0.10 go: added github.com/kataras/sitemap v0.0.5 go: added github.com/kataras/tunnel v0.0.4 go: added github.com/klauspost/compress v1.15.9 go: added github.com/microcosm-cc/bluemonday v1.0.19 go: added github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd go: added github.com/modern-go/reflect2 v1.0.2 go: added github.com/russross/blackfriday/v2 v2.1.0 go: added github.com/ryanuber/columnize v2.1.2+incompatible go: added github.com/schollz/closestmatch v2.1.0+incompatible go: added github.com/shurcooL/sanitized_anchor_name v1.0.0 go: added github.com/valyala/bytebufferpool v1.0.0 go: added github.com/vmihailenco/msgpack/v5 v5.3.5 go: added github.com/vmihailenco/tagparser v0.1.2 go: added github.com/vmihailenco/tagparser/v2 v2.0.0 go: added github.com/yosssi/ace v0.0.5 go: added golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa go: added golang.org/x/net v0.0.0-20220812174116-3211cb980234 go: added golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab go: added golang.org/x/text v0.3.7 go: added golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 go: added google.golang.org/appengine v1.6.7 go: added google.golang.org/protobuf v1.28.1 go: added gopkg.in/ini.v1 v1.67.0 go: added gopkg.in/yaml.v3 v3.0.1装置结束之后,能够关上我的项目中go.mod文件查看Iris的依赖列表: ...

August 14, 2022 · 5 min · jiezi

关于go:如何在海量数据中找出最大的k个数Top-K问题

如何在领有海量数据的数组N中找到最大的K条数据?或者如何找到频率呈现最高的前k个数?这种问题就是Top K问题,有哪些办法能够解决呢,上面来剖析一下。 1、全局排序第一个天然想到的就是将所有的数据通过快排排好序,取前K个数,工夫复杂度是O(nlogn),然而如果数据量特地大,是十分耗费内存的,而且明明只有前K个数,要把所有的数据都排序一遍也是很节约的,有没有办法能部分排序呢? 2、部分排序求最大的k个值,很天然的就想到冒泡排序了,每冒一次泡,拿到数组的最大值,工夫复杂度是O(N*k),求10个数的话将数组扫描10次就拿到后果了。 3、堆排用数组的前k个元素生成一个小顶堆,接着,从第k+1个元素开始扫描,和堆顶(堆中最小的元素)比拟,如果被扫描的元素大于堆顶,则替换堆顶的元素,并调整堆,以保障堆内的k个元素,总是以后最大的k个元素。扫描结束,堆中的元素就是最大的K个元素了。工夫复杂度:O(N*log(K)) 4、随机抉择排序随机抉择排序相似随机快排,每次partition都会确定一个元素的地位,比它大的数在它左边,比它小的数在它右边,如果p比指标下标小,就递归右子区间,否则递归左子区间,跟快排相比这样就能够把原来递归两个区间变成只递归一个区间,进步了工夫效率,这是一个典型的减治算法,它的工夫复杂度是O(n)。 只管它们是无序的,题目并没有要将这k个数排序,这样的话,只有在倒数第K个地位的元素分区完的时候,拿它左边的K的数,就是指标解了。 func FindKLargest(arr []int, index int) []int { arrLen := len(arr) return findKLargest(arr, 0, arrLen-1, arrLen-index)}func findKLargest(arr []int, start, end, index int) []int { p := partition(arr, start, end) if p == index { return arr[index:] } else if p < index { return findKLargest(arr, p+1, end, index) } return findKLargest(arr, start, p-1, index)}func partition(arr []int, start, end int) int { i := start pivot := arr[end] //比拟值 for k := start; k <= end-1; k++ { if arr[k] < pivot { arr[k], arr[i] = arr[i], arr[k] i++ } } arr[end], arr[i] = arr[i], arr[end] return i}

August 14, 2022 · 1 min · jiezi

关于go:Go语言-context包源码学习

你必须十分致力,能力看起来毫不费力! 微信搜寻公众号[ 漫漫Coding路 ],一起From Zero To Hero ! 前言日常 Go 开发中,Context 包是用的最多的一个了,简直所有函数的第一个参数都是 ctx,那么咱们为什么要传递 Context 呢,Context 又有哪些用法,底层实现是如何呢?置信你也肯定会有摸索的欲望,那么就跟着本篇文章,一起来学习吧! 需要一开发中必定会调用别的函数,比方 A 调用 B,在调用过程中常常会设置超时工夫,比方超过2s 就不期待 B 的后果了,间接返回,那么咱们须要怎么做呢? // 睡眠5s,模仿长时间操作func FuncB() (interface{}, error) { time.Sleep(5 * time.Second) return struct{}{}, nil}func FuncA() (interface{}, error) { var res interface{} var err error ch := make(chan interface{}) // 调用FuncB(),将后果保留至 channel 中 go func() { res, err = FuncB() ch <- res }() // 设置一个2s的定时器 timer := time.NewTimer(2 * time.Second) // 监测是定时器先完结,还是 FuncB 先返回后果 select { // 超时,返回默认值 case <-timer.C: return "default", err // FuncB 先返回后果,敞开定时器,返回 FuncB 的后果 case r := <-ch: if !timer.Stop() { <-timer.C } return r, err }}func main() { res, err := FuncA() fmt.Println(res, err)}下面咱们的实现,能够实现超过等待时间后,A 不期待 B,然而 B 并没有感触到勾销信号,如果 B 是个计算密度型的函数,咱们也心愿B 感知到勾销信号,及时勾销计算并返回,缩小资源节约。 ...

August 14, 2022 · 9 min · jiezi

关于go:经典算法之二分查找及其变体

前言在有序数组中查找元素的时候,如果是从头到尾遍历一遍查找,它的工夫复杂度是O(N),而通过二分查找,每次查找过滤一半元素,能够优化到O(logn)。 如图所示,low 和 high示意待查找区间的下标,mid 示意待查找区间的两头元素下标,通过管制low、high的地位来放大查找区间的范畴。 1、二分查找思路1、mid是low、high的两头节点,每次循环与target目标值比照大小2、如果mid大于target,则low挪动到mid+1的地位 如果mid小于target,则high挪动到mid-1的地位 如果mid等于target则阐明找到指定的元素,返回后果3、如果low、high重合了还没找到指定元素,则阐明该元素不存在 留神:1、终止条件是min <= max,此时min、max重合,查找区间为02、mid的取值,用mid=(low+high)/2的话,如果值特地大是可能会溢出的,所以用mid= min + (max-min)/2 func BinarySearch(a []int, v int) int { length := len(a) min := 0 max := length - 1 for min <= max { mid := min + (max-min)/2 if a[mid] == v { return mid } else if a[mid] < v { min = mid + 1 } else if a[mid] > v { max = mid - 1 } } return -1}变体1:查找第一个值等于给定值的元素思路查第一个呈现的目标值,就是在查到目标值当前,批改max的地位持续往左查找,直到mid的地位为0了或者mid的右边地位元素不等于目标值了进行。 ...

August 14, 2022 · 2 min · jiezi

关于go:层次分明井然有条Go-lang118入门精炼教程由白丁入鸿儒Go-lang包管理机制packageEP10

Go lang应用包(package)这种概念元素来兼顾代码,所有代码性能上的可调用性都定义在包这个级别,如果咱们须要调用依赖,那就“导包”就行了,无论是外部的还是内部的,应用import关键字即可。但事件往往没有那么简略,Go lang在包管理机制上走了不少弯路,尽管1.18版本的包治理曾经趋于成熟,但前事不忘后事之师,咱们还是须要理解一下这段历史。 环境变量个别状况下,go lang在零碎中会依赖两个环境变量,别离是:GOPATH 和 GOROOT,有点相似于Python的解释器目录的概念,GOROOT 这个变量的作用就是为了通知以后运行的 Go 过程以后 Go装置门路,当要运行的时候去什么地位找GoSDK相干的类。 GOPATH 这个变量的设定是默认所有的我的项目和援用的第三方包都下载到GOPATH的src目录下,也就是说,你的代码只有不在GOPATH里,就没法编译。咱们能够了解这个目录就是我的项目目录,这一点跟Python区别还是挺大的,Python的pip包管理机制至今都是经典的包依赖设计模式。 GOPATH这种设定形式会导致很多问题,比方说我有很多go lang我的项目,而每个我的项目又都有本人的GOPATH目录,那所有依赖的包就都在我的项目各自目录下,导致反复包过多,反之,如果大家都用一个GOPATH目录,又会带来版本问题,每个我的项目依赖的包版本不统一,到底怎么进行兼顾又是一个问题,这就是GOPATH设定晚期被人诟病的起因。 Go modules针对因为GOPATH这种反人类的设计而导致的包治理乱象,Go lang 1.11 版本的时候推出了新个性 Go modules。 Go modules 是官网推出的依赖管理工具,Go modules 提供了3个重要的性能: 1.go.mod 文件,它和Node的package.json文件的性能类似,都是记录以后我的项目的依赖关系。 2.机器生成的传递依赖项形容文件 : go.sum。 3.不再有 GOPATH 的反人类限度,所有代码能够位于电脑的任何门路中。 go lang1.18早曾经集成了Go modules,但就像golang1.18第一篇精炼教程里写得那样,默认还是反人类的GOPATH模式,你想用,得通过命令手动开启: go env -w GO111MODULE=on为了可能向下兼容保护go1.11版本以下的我的项目,能够设置为兼容模式: go env -w GO111MODULE=auto三方包治理三方包指的是内部开源的一些包,而应用go modules机制治理三方包绝对简略,首先新建一个我的项目目录,比方c:/www/test cd c:/www/test进入我的项目目录后,初始化我的项目: go mod init test零碎返回: C:\Users\liuyue\www\test>go mod init test go: creating new go.mod: module test C:\Users\liuyue\www\test>dir 2022/08/12 12:13 <DIR> . 2022/08/12 12:13 <DIR> .. 2022/08/12 12:13 21 go.mod 1 个文件 21 字节 2 个目录 228,767,113,216 可用字节这里留神我的项目名和目录名称要吻合,go.mod 文件是开启 modules 的必备配置文件。它记录了以后我的项目援用的包数据信息。go.mod 文件中定义了以下关键词: ...

August 13, 2022 · 3 min · jiezi

关于go:go-hack一tcp端口扫描

法一 package mainimport ( "fmt" "net")func main() { var datas []int // 用来保留扫描到的port var ports chan int = make(chan int) var done chan int = make(chan int) // 创立消费者 for i := 0; i < 10; i++ { go func() { for { port, ok := <-ports if !ok { break } add := fmt.Sprintf("scanme.namp.com:%d", port) _, err := net.Dial("tcp", add) if err != nil { //log.Fatalln(err) fmt.Println(err) } else { datas = append(datas, port) } } done <- 1 }() } for i := 1; i < 100; i++ { ports <- i } for v := range done { fmt.Println(v) } fmt.Println(datas)}法二 ...

August 13, 2022 · 2 min · jiezi

关于go:Go十大常见错误第5篇go语言Error管理

前言这是Go十大常见谬误系列的第5篇:go语言Error治理。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go十大常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 场景Go语言在错误处理(error handling)机制上常常被诟病。 在Go 1.13版本之前,Go规范库里只有一个用于构建error的errors.New函数,没有其它函数。 pkg/errors包因为Go规范库里errors包的性能比拟少,所以很多人可能用过开源的pkg/errors包来解决Go语言里的error。 比拟早应用Go语言做开发,并且应用pkg/errors包的开发者也会犯一些谬误,下文会具体讲到。 pkg/errors包的代码格调很好,遵循了上面的error解决法令。 An error should be handled only once. Logging an error is handling an error. So an error should either be logged or propagated.翻译成中文就是: error只应该被解决一次,打印error也是对error的一种解决。所以对于error,要么打印进去,要么就把error返回传递给上一层。很多开发者在日常开发中,如果某个函数里遇到了error,可能会先打印error,同时把error也返回给下层调用方,这就没有遵循下面的最佳实际。 咱们接下来看一个具体的示例,代码逻辑是后盾收到了一个RESTful的接口申请,触发了数据库报错。咱们想打印如下的堆栈信息: unable to serve HTTP POST request for customer 1234 |_ unable to insert customer contract abcd |_ unable to commit transaction假如咱们应用pkg/errors包,咱们能够应用如下代码来实现: func postHandler(customer Customer) Status { err := insert(customer.Contract) if err != nil { log.WithError(err).Errorf("unable to serve HTTP POST request for customer %s", customer.ID) return Status{ok: false} } return Status{ok: true}}func insert(contract Contract) error { err := dbQuery(contract) if err != nil { return errors.Wrapf(err, "unable to insert customer contract %s", contract.ID) } return nil}func dbQuery(contract Contract) error { // Do something then fail return errors.New("unable to commit transaction")}函数调用链是postHandler -> insert -> dbQuery。 ...

August 13, 2022 · 2 min · jiezi

关于go:反转链表的两种解法

反转链表能够用两种办法来实现,一种是常见的迭代法,还有一种办法就是递归,上面来剖析一下具体是怎么实现的。 迭代法思路:初始化一个变量来存储前驱节点pre,从头节点开始遍历链表,每遍历一个节点,就将该节点的后驱节点指向pre,实现了反转,而后更新pre的值为以后节点以便下一个节点的应用,遍历完当前以前的尾节点就是新的头节点。 func (head *Node) reverse() *Node { if head == nil { return nil } var reverseHead *Node // 反转后单链表的头结点 var preNode *Node curNode := head for curNode != nil { nextNode := curNode.next if nextNode == nil { reverseHead = curNode // 尾结点转换为头结点 } // 反转实现,以后结点的前驱结点变成后驱结点 curNode.next = preNode // 设置下一个结点的前驱结点 preNode = curNode curNode = nextNode } return reverseHead}递归法思路递归的办法会一直压栈,反转(head.Next)的前提是反转(head.Next.Next),始终到终止条件head.Next == nil时拿到尾节点的值才真正开始解决,last节点的变动如下:66->56->5->46->5->4->36->5->4->3->26->5->4->3->2->1 假如reverse(head.Next)返回的曾经是反转后的节点last,当初还须要做的就是将2号节点的后驱节点指向head,以及原head节点的后驱节点指向nil,一个节点的case想明确了,其余的其实就是一样的过程了。 func reverse(head *Node) *Node { if head == nil || head.Next == nil { return head } last := reverse(head.Next) head.Next.Next = head head.Next = nil return last}参考资料:https://labuladong.github.io/...

August 13, 2022 · 1 min · jiezi

关于go:go-byte字节与ascii码

一、ascii码介绍ascii码总有有 128位,用来示意罕用的字符。在go语言中咱们用 byte 一个 uint8 (0-255)来展现ascii字符。 二、go示意ascii码 0-91、示意ascii的0-9在ascii吗中 ascii字符10进制'0'48'1'49'2'50'3'51'4'52'5'53'6'54'7'55'8'56'9'57咱们用go语言打印一下: import "testing"func TestAsciiInt(t *testing.T) { var asciiInt = []byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'} t.Log(asciiInt)}输入: [48 49 50 51 52 53 54 55 56 57]2、byte字符数组 转字符串import "testing"func TestAsciiInt(t *testing.T) { var asciiInt = []byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'} t.Log(string(asciiInt))}输入: 0123456789 //留神这是个字符串

August 13, 2022 · 1 min · jiezi

关于go:Golang连接Oracle数据库两种方式

Golang连贯Oracle 须要装置Oracle Full Client或Instant Client 驱动程序Linux装置Golang Oracle数据库驱动程序 MacBook装置Golang Oracle数据库驱动程序 形式一应用go get github.com/mattn/go-oci8在GoPath的src目录下创立oracleoci8.go,内容如下: package mainimport ( "database/sql" "fmt" _ "github.com/mattn/go-oci8" "log" "os")func main() { if len(os.Args) != 2 { log.Fatalln(os.Args[0] + " user/password@host:port/sid") } fmt.Println(os.Args[1]) db, err := sql.Open("oci8", os.Args[1]) if err != nil { log.Fatalln(err) } defer db.Close() //查问用户 rows, err := db.Query("select user from dual") if err != nil { log.Fatalln(err) } defer rows.Close() for rows.Next() { var data string rows.Scan(&data) fmt.Println(data) } if err = rows.Err(); err != nil { log.Fatalln(err) } //查问ZHUJI表的code字段 rows2, err := db.Query("SELECT code FROM ZHUJI") if err != nil { log.Fatalln(err) } defer rows2.Close() for rows2.Next() { var data string rows2.Scan(&data) fmt.Println(data) } if err = rows2.Err(); err != nil { log.Fatalln(err) }}运行#间接运行go run oracleoci8.go liang/liang@192.168.0.4:1521/orcl#或者先编译成二进制,在运行二进制文件go build oracleoci8.go./oracleoci8 liang/liang@192.168.0.4:1521/orcl形式二应用go get github.com/godror/godror在GoPath的src目录下创立oracledror.go,内容如下: ...

August 12, 2022 · 2 min · jiezi