关于go:Go垃圾回收系列三屏障机制

“强-弱” 三色不变式上篇讲到如果在三色标记法去掉STW环节之后,可能会产生对象失落景象,即一个非法援用的对象被gc给当作垃圾对象谬误回收掉了。 而为了防止这种状况的呈现须要毁坏这种景象造成的两个前提条件: 条件1: 一个红色对象被彩色对象援用(红色被挂在彩色下)条件2: 灰色对象与它之间的可达关系的红色对象受到毁坏(灰色同时丢了该红色)强三色不变式如果咱们毁坏第一个条件,即强制性的不容许彩色对象援用红色对象,这样就不会呈现有红色对象被误删的状况,这个就叫做强三色不变式。 弱三色不变式假如容许第一个条件,只毁坏第二个条件,即彩色对象能够援用红色对象,然而这个红色对象必须存在其余灰色对象对它的援用,或者可达它的链路上游存在灰色对象。这个就叫做弱三色不变式。 这样实则是彩色对象援用红色对象,红色对象处于一个危险被删除的状态,然而上游灰色对象的援用,能够爱护该红色对象,使其平安。 为了遵循上述的两个形式,GC算法演进到两种屏障形式,他们别离是“插入屏障”和“删除屏障”。 插入屏障(满足强三色不变式)具体操作:在A对象援用B对象的时候,B对象被标记为灰色。(将B挂在A上游,B必须被标记为灰色) 满足: 强三色不变式不存在彩色对象援用红色对象的状况了, 因为红色会强制变成灰色 实现伪代码:A.增加上游对象(nil, B) //A 之前没有上游, 新增加一个上游对象B, B被标记为灰色A.增加上游对象(C, B) //A 将上游对象C 更换为B, B被标记为灰色这段伪码逻辑就是写屏障,. 咱们晓得,彩色对象的内存槽有两种地位, 栈和堆. 栈空间的特点是容量小,然而要求相应速度快,因为函数调用弹出频繁应用, 所以“插入屏障”机制,在栈空间的对象操作中不应用. 而仅仅应用在堆空间对象的操作中. 然而如果栈不增加,当全副三色标记扫描之后,栈上有可能仍然存在红色对象被援用的状况,所以要对栈从新进行三色标记扫描, 但这次为了对象不失落, 要对本次标记扫描启动STW暂停. 直到栈空间的三色标记完结. 删除屏障(满足弱三色不变式)具体操作被删除的对象,如果本身为灰色或者红色,那么被标记为灰色。 满足: 弱三色不变式爱护灰色对象到红色对象的门路不会断 实现伪代码:A.增加上游对象(B, nil) //A对象,删除B对象的援用。 B被A删除,被标记为灰(如果B之前为白)A.增加上游对象(B, C) //A对象,更换上游B变成C。 B被A删除,被标记为灰(如果B之前为白)这种形式的回收精度低,一个对象即便被删除了最初一个指向它的指针也仍旧能够活过这一轮,在下一轮GC中被清理掉。 混合写屏障(Go1.8引入)插入写屏障和删除写屏障的短板: 插入写屏障:完结时须要STW来从新扫描栈,标记栈上援用的红色对象的存活;删除写屏障:回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会爱护开始时刻的所有存活对象。Go V1.8版本引入了混合写屏障机制(hybrid write barrier),防止了对栈re-scan的过程,极大的缩小了STW的工夫。联合了两者的长处。 混合写屏障规定具体操作: 1、GC开始将栈上的对象全副扫描并标记为彩色(之后不再进行第二次反复扫描,无需STW), 2、GC期间,任何在栈上创立的新对象,均为彩色。 3、被删除的对象标记为灰色。 4、被增加的对象标记为灰色。 满足: 变形的弱三色不变式 伪代码增加上游对象(以后上游对象slot, 新上游对象ptr) { //1 标记灰色(以后上游对象slot) //只有以后上游对象被移走,就标记灰色 //2 标记灰色(新上游对象ptr) //3 以后上游对象slot = 新上游对象ptr}小结GoV1.3- 一般标记革除法,整体过程须要启动STW,效率极低。GoV1.5- 三色标记法, 堆空间启动写屏障,栈空间不启动,全副扫描之后,须要从新扫描一次栈(须要STW),效率一般GoV1.8-三色标记法,混合写屏障机制, 栈空间不启动,堆空间启动。整个过程简直不须要STW,效率较高。

August 26, 2023 · 1 min · jiezi

关于go:Go垃圾回收系列二三色标记法

Golang中的垃圾回收次要利用三色标记法,GC过程和其余用户goroutine可并发运行,但须要肯定工夫的STW(stop the world),所谓三色标记法实际上就是通过三个阶段的标记来确定要革除的对象都有哪些 具体步骤第一步 , 每次新创建的对象,默认的色彩都是标记为“红色”。 第二步, 每次GC回收开始, 会从根节点开始遍历所有对象,把遍历到的对象从红色汇合放入“灰色”汇合。这里 要留神的是,本次遍历是一次遍历,非递归模式,是从程序抽次可到达的对象遍历一层。 第三步, 遍历灰色汇合,将灰色对象援用的对象从红色汇合放入灰色汇合,之后将此灰色对象放入彩色汇合。 第四步, 反复第三步, 直到灰色中无任何对象。 当全副的可达对象都遍历完后,灰色标记表将不再存在灰色对象,目前全副内存的数据只有两种色彩,彩色和红色。那么彩色对象就是咱们程序逻辑可达(须要的)对象,这些数据是目前撑持程序失常业务运行的,是非法的有用数据,不可删除,红色的对象是全副不可达对象,目前程序逻辑并不依赖他们,那么红色对象就是内存中目前的垃圾数据,须要被革除。 第五步: 回收所有的红色标记表的对象. 也就是回收垃圾。剩下的就是全副依赖的彩色对象。 问题为了在GC过程中保证数据的平安,咱们在开始三色标记之前就会加上STW,在扫描确定黑白对象之后再放开STW。然而很显著这样的GC扫描的性能切实是太低了。 那么Go是如何解决标记-革除(mark and sweep)算法中的卡顿(stw,stop the world)问题的呢? 没有STW的三色标记法如果没有STW,那么也就不会再存在性能上的问题,那么接下来咱们假如如果三色标记法不退出STW会产生什么事件? 如果三色标记过程不启动STW,那么在GC扫描过程中,任意的对象均可能产生读写操作,这样就会存在一种状况,就是同时满足以下两个条件: 条件1: 一个红色对象被彩色对象援用(红色被挂在彩色下)条件2: 灰色对象与它之间的可达关系的红色对象受到毁坏(灰色同时丢了该红色)如果当以上两个条件同时满足时,就会呈现对象失落景象,即一个非法援用的对象被gc给当作垃圾对象谬误回收掉。 为了避免这种景象的产生,最简略的形式就是STW,间接禁止掉其余用户程序对对象援用关系的烦扰,然而STW的过程有显著的资源节约,对所有的用户程序都有很大影响。那么是否能够在保障对象不失落的状况下正当的尽可能的进步GC效率,缩小STW工夫呢?答案是能够的,咱们只有应用一种机制,尝试去毁坏下面的两个必要条件就能够了。 这就引出了咱们接下来要讲的屏障机制。

August 26, 2023 · 1 min · jiezi

关于go:Go垃圾回收系列一标记清除算法

垃圾回收(Garbage Collection,简称GC)是编程语言中提供的主动的内存管理机制,主动开释不须要的内存对象,让出存储器资源。GC过程中无需程序员手动执行。GC机制在古代很多编程语言都反对,GC能力的性能与优劣也是不同语言之间对比度指标之一。 Golang在GC的演进过程中也经验了很屡次改革 Go1.3 之前采纳一般的标记-革除算法Go1.5 引入了三色标记法和屏障技术Go1.8 引入混合写屏障技术本篇咱们就依照工夫线,从最开始的标记革除算法开始。 Go V1.3之前的标记-革除(mark and sweep)算法此算法次要有两个次要的步骤: 标记(Mark phase)革除(Sweep phase)具体步骤第一步,暂停程序业务逻辑, 分类出可达和不可达的对象,而后做上标记。 第二步, 开始标记,程序找出它所有可达的对象,并做上标记。 第三步, 标记完了之后,而后开始革除未标记的对象。 第四步, 进行暂停,让程序持续跑。而后循环反复这个过程,直到process程序生命周期完结。 STW(stop the world)操作非常简单,然而有一点须要额定留神:mark and sweep算法在执行的时候,须要程序暂停!即 STW(stop the world),STW的过程中,CPU不执行用户代码,全副用于垃圾回收,这个过程的影响很大,所以STW也是一些回收机制最大的难题和心愿优化的点。所以在执行第三步的这段时间,程序会暂定进行任何工作,卡在那期待回收执行结束。 标记革除法的毛病标记革除算法明了,过程显明罗唆,然而也有十分重大的问题。 ● STW,stop the world;让程序暂停,程序呈现卡顿 (重要问题);● 标记须要扫描整个heap;● 革除数据会产生heap碎片。 无论怎么优化,Go V1.3都面临这个一个重要问题,就是mark-and-sweep 算法会暂停整个程序 。 如何解决?(三色标记法)

August 26, 2023 · 1 min · jiezi

关于go:基于go语言的阴阳历转换工具包和阴阳历生日重合查询

查问 两个主函数1.输出你的生日,返回你的阴历生日和阳历生日重合的年份2.输出年份,查问的年月,返回该月份内有哪些天的生日重合年份蕴含输出的年份用的时候将另一个主函数正文掉就行了 一个猜测:农历的每个月的初一到二十九 生日重合的年份是雷同的例如2000年的10月27号至11月25号是农历的十月初一至十月三十,其中至二十九雷同。2001年的8月19号至9月16号是农历的七月初一至七月二十九,雷同。2000年的7月31号到8月28号是农历的七月初一至七月二十九,雷同。 package mainimport ( "awei/tool" "time")//func main() {// solarDate := "2001-08-19"// res := tool.FindYear(solarDate)// for _, v := range res {// println(v)// }//}func FindMatchingDates(year1 int, year2 int, month int) []time.Time { var matchingDates []time.Time for day := 1; day <= 30; day++ { date := time.Date(year2, time.Month(month), day, 0, 0, 0, 0, time.Local) years := tool.FindYear(date.Format("2006-01-02")) if contains(years, year1) { matchingDates = append(matchingDates, date) } } return matchingDates}func contains(slice []int, item int) bool { for _, value := range slice { if value == item { return true } } return false}func main() { res := FindMatchingDates(2058, 2001, 9) for _, v := range res { println(v.Date()) }}工具包 ...

August 25, 2023 · 6 min · jiezi

关于go:Golang-简单易用的反射工具库-支持设置嵌套结构体字段-函数调用-etc

介绍在理论业务开发中, 常常须要用到反射的能力, 比方联合近程配置动静批改构造体的字段, 这样无需公布即可实现性能变更, 再比方拓展插件的场景, 应用表驱动的形式调用一些异构的函数(无奈形象为接口)等. 这里对罕用的操作进行了getter, setter的封装, 并加强了一些能力, 比方反对设置多层嵌套构造体的字段, 针对构造体指针主动创立等. 地址: https://github.com/morrisxyang/xreflect 中文文档: https://github.com/morrisxyang/xreflect/blob/main/README_CN.md如果感觉有用欢送 Star 和 PR, 有问题间接提Issue xreflect 一个简略的, 易用的反射工具库. 次要反对如下个性: 设置构造体字段值, 反对通过门路比方A.B.C设置嵌套构造体字段的值获取构造体字段的值, 类型, Tag 等.遍历构造体所有字段, 反对 select 模式和 range 模式, 如果应用深度遍历办法比方 FieldsDeep 将遍历所有嵌套构造.函数调用, 办法调用, 反对可变参数.新建实例, 判断接口实现等等.装置和文档装置命令 go get github.com/morrisxyang/xreflect. 文档见 https://pkg.go.dev/github.com/morrisxyang/xreflect 疾速开始设置嵌套构造体字段值 person := &Person{ Name: "John", Age: 20, Country: Country{ ID: 0, Name: "Perk", },}_ = SetEmbedField(person, "Country.ID", 1)// Perk's ID: 1 fmt.Printf("Perk's ID: %d \n", person.Country.ID)调用函数 ...

August 25, 2023 · 2 min · jiezi

关于go:GMP调度系列二什么是GMP调度

GMP调度模型接上一篇文章的内容,本篇切入正题,搞清楚这个GMP到底是个什么玩意。 首先咱们来看看GMP外面波及到的三个基本概念, 线程 M、Goroutine G 和处理器 P G — 示意 Goroutine,它是一个待执行的工作;M — 示意操作系统的线程,它由操作系统的调度器调度和治理;P — 示意处理器,它能够被看做运行在线程上的本地调度器;晓得了以上三个根底概念后,咱们再来直观的感触以下GMP模型: 全局队列(Global Queue):寄存期待运行的G。P的本地队列:同全局队列相似,寄存的也是期待运行的G,存的数量无限,不超过256个。新建G'时,G'优先退出到P的本地队列,如果队列满了,则会把本地队列中一半的G挪动到全局队列。P列表:所有的P都在程序启动时创立,并保留在数组中,最多有GOMAXPROCS(可配置)个。M:线程想运行工作就得获取P,从P的本地队列获取G,P队列为空时,M也会尝试从全局队列拿一批G放到P的本地队列,或从其余P的本地队列偷一半放到本人P的本地队列。M运行G,G执行之后,M会从P获取下一个G,一直反复上来。Goroutine调度器和OS调度器是通过M联合起来的,每个M都代表了1个内核线程,OS调度器负责把内核线程调配到CPU的核上执行。 无关P和M的个数问题1、P的数量: 由启动时环境变量$GOMAXPROCS或者是由runtime的办法GOMAXPROCS()决定。这意味着在程序执行的任意时刻都只有$GOMAXPROCS个goroutine在同时运行。2、M的数量: go语言自身的限度:go程序启动时,会设置M的最大数量,默认10000.然而内核很难反对这么多的线程数,所以这个限度能够疏忽。runtime/debug中的SetMaxThreads函数,设置M的最大数量一个M阻塞了,会创立新的M。M与P的数量没有相对关系,一个M阻塞,P就会去创立或者切换另一个M,所以,即便P的默认数量是1,也有可能会创立很多个M进去。 P和M何时会被创立P何时创立:在确定了P的最大数量n后,运行时零碎会依据这个数量创立n个P。M何时创立:没有足够的M来关联P并运行其中的可运行的G。比方所有的M此时都阻塞住了,而P中还有很多就绪工作,就会去寻找闲暇的M,而没有闲暇的,就会去创立新的M。GMP调度器的设计策略复用线程防止频繁的创立、销毁线程,而是对线程的复用。 work stealing机制当本线程无可运行的G时,尝试从其余线程绑定的P偷取G,而不是销毁线程 hand off机制当本线程因为G进行零碎调用阻塞时,线程开释绑定的P,把P转移给其余闲暇的线程执行 利用并行GOMAXPROCS设置P的数量,最多有GOMAXPROCS个线程散布在多个CPU上同时运行。GOMAXPROCS也限度了并发的水平,比方GOMAXPROCS = 核数/2,则最多利用了一半的CPU核进行并行。 抢占在coroutine中要期待一个协程被动让出CPU才执行下一个协程,在Go中,一个goroutine最多占用CPU 10ms,避免其余goroutine被饿死,这就是goroutine不同于coroutine的一个中央。 全局G队列在新的调度器中仍然有全局G队列,当P的本地队列为空时,优先从全局队列获取,如果全局队列为空时则通过work stealing机制从其余P的本地队列偷取G。 go func() 调度流程 从上图咱们能够剖析出几个论断: 1、咱们通过 go func()来创立一个goroutine; 2、有两个存储G的队列,一个是部分调度器P的本地队列、一个是全局G队列。新创建的G会先保留在P的本地队列中,如果P的本地队列曾经满了就会保留在全局的队列中; 3、G只能运行在M中,一个M必须持有一个P,M与P是1:1的关系。M会从P的本地队列弹出一个可执行状态的G来执行,如果P的本地队列为空,就会想其余的MP组合偷取一个可执行的G来执行; 4、一个M调度G执行的过程是一个循环机制; 5、当M执行某一个G时候如果产生了syscall或则其余阻塞操作,M会阻塞,如果以后有一些G在执行,runtime会把这个线程M从P中摘除(detach),而后再创立一个新的操作系统的线程(如果有闲暇的线程可用就复用闲暇线程)来服务于这个P; 6、当M零碎调用完结时候,这个G会尝试获取一个闲暇的P执行,并放入到这个P的本地队列。如果获取不到P,那么这个线程M变成休眠状态, 退出到闲暇线程中,而后这个G会被放入全局队列中。 M0和G0M0是启动程序后的编号为0的主线程,这个M对应的实例会在全局变量runtime.m0中,不须要在heap上调配,M0负责执行初始化操作和启动第一个G, 在之后M0就和其余的M一样了。G0是每次启动一个M都会第一个创立的gourtine,G0仅用于负责调度的G,G0不指向任何可执行的函数, 每个M都会有一个本人的G0。在调度或零碎调用时会应用G0的栈空间, 全局变量的G0是M0的G0。

August 24, 2023 · 1 min · jiezi

关于go:gocarbon-224-版本发布-轻量级语义化对开发者友好的Golang时间处理库

carbon 是一个轻量级、语义化、对开发者敌对的 golang 工夫解决库,反对链式调用。 目前已被 awesome-go 收录,如果您感觉不错,请给个 star 吧 github.com/golang-module/carbon gitee.com/golang-module/carbon 装置应用Golang 版本大于等于1.16// 应用 github 库go get -u github.com/golang-module/carbon/v2import ( "github.com/golang-module/carbon/v2" )// 应用 gitee 库go get -u gitee.com/golang-module/carbon/v2import ( "gitee.com/golang-module/carbon/v2")Golang 版本小于1.16// 应用 github 库go get -u github.com/golang-module/carbonimport ( "github.com/golang-module/carbon" )// 应用 gitee 库go get -u gitee.com/golang-module/carbonimport ( "gitee.com/golang-module/carbon" )更新日志修复局部办法因并发读写造成的线程不平安的 bug修复 DiffInMonths 办法输入谬误的 bug新增 CreateFromStdTime 办法,并给 FromStdTime, Time2Carbon , Carbon2Time 办法增加弃用标识减少对波斯语的反对,由 erfanMomeniii 翻译将单元测试覆盖率晋升到 100%

August 24, 2023 · 1 min · jiezi

关于go:GMP调度系列一为什么要有GMP调度

家喻户晓,新事物的产生必然是有他存在的意义,放在计算机的世界里,一个新的模型或工具的产生必然是用来解决某些理论问题的,那回到咱们的明天要讲的主题——GMP调度,它的呈现是用来解决什么问题呢? 调度器的由来为了说分明GMP调度,咱们先理解一下调度器的由来。 过程串行的问题首先,最后的单过程时代是不须要调度器的,因为所有的程序在操作系统上只能串行产生。 然而紧接着就带来两个问题: 繁多的执行流程,计算机只能一个工作一个工作解决。过程阻塞所带来的CPU工夫节约。那能不能让多个过程“同时”执行呢,为了解决这两个问题。就呈现了“多过程并发”的概念。也就是当一个过程阻塞的时候,切换到另外期待执行的过程,这样就能尽量把CPU利用起来,CPU就不节约了。 实现多过程并发(调度器的呈现)在多过程/多线程的操作系统中,就解决了阻塞的问题,因为一个过程阻塞cpu能够立即切换到其余过程中去执行,而且调度cpu的算法能够保障在运行的过程都能够被调配到cpu的运行工夫片。这样从宏观来看,仿佛多个过程是在同时被运行。 新的问题过程领有太多的资源,过程的创立、切换、销毁,都会占用很长的工夫,CPU尽管利用起来了,但如果过程过多,CPU有很大的一部分都被用来进行过程调度了。 CPU调度切换的是过程和线程。只管线程看起来很美妙,但实际上多线程开发设计会变得更加简单,要思考很多同步竞争等问题,如锁、竞争抵触等。 进步CPU的利用率(协程的呈现)进一步,工程师们就发现,其实一个线程分为“内核态“线程和”用户态“线程内核线程仍然叫“线程(thread)”,用户线程叫“协程(co-routine)". 既然一个协程(co-routine)能够绑定一个线程(thread),那么能不能多个协程(co-routine)绑定一个或者多个线程(thread)上呢 解决办法N个协程绑定1个线程长处:就是协程在用户态线程即实现切换,不会陷入到内核态,这种切换十分的轻量疾速。毛病,1个过程的所有协程都绑定在1个线程上,一旦某协程阻塞,造成线程阻塞,本过程的其余协程都无奈执行了,基本就没有并发的能力了1个协程绑定1个线程长处:这种最容易实现,协程的调度都由CPU实现了,不存在N:1毛病毛病:协程的创立、删除和切换的代价都由CPU实现,有点略显低廉了 M个协程绑定N个线程是N:1和1:1类型的联合,克服了以上2种模型的毛病,但实现起来最为简单线程由CPU调度是抢占式的,协程由用户态调度是合作式的 小结至此,咱们就根本理解了调度器是怎么来的,为什么要有调度器(解决阻塞问题实现过程并发执行),以及调度器的优化(M:N模型)在防止阻塞问题的同时尽量减少过程切换开销,进步CPU应用效率。 被废除的调度- GM调度(没有P)家喻户晓,技术的倒退不是欲速不达的,始终在随着时代一直变革和演进,Go的调度器也是如此,明天妇孺皆知的GMP调度也是改良后的后果。 Go目前应用的调度器是2012年从新设计的,因为之前的调度器性能存在问题,所以应用4年就被废除了,那么咱们先来剖析一下被废除的调度器是如何运作的? M想要执行、放回G都必须拜访全局G队列,并且M有多个,即多线程拜访同一资源须要加锁进行保障互斥/同步,所以全局G队列是有互斥锁进行爱护的。 GM调度的毛病: 创立、销毁、调度G都须要每个M获取锁,这就造成了强烈的锁竞争。M转移G会造成提早和额定的零碎负载。比方当G中蕴含创立新协程的时候,M创立了G’,为了继续执行G,须要把G’交给M’执行,也造成了很差的局部性,因为G’和G是相干的,最好放在M上执行,而不是其余M'。零碎调用(CPU在M之间的切换)导致频繁的线程阻塞和勾销阻塞操作减少了零碎开销。为了解决以上问题,必须得引入一套新的机制,此时GMP便应运而生了。

August 23, 2023 · 1 min · jiezi

关于go:golang中的接口实现调用修改值接收者指针接收者

比方有以下阐明:阐明备注接口比方一个coder的interface,有code和debug办法办法接收者(实现接口)可能有多个,比方phper,golanger,可能是值接管(对象),也可能是指针接管(对象指针)调用者为:接口就是接口coder调用调用者为:办法接收者比方phper,golanger,调用者可能是值调用(对象),或者指针调用(对象指针)对于调用调用者为 办法接收者无论办法接收者(实现接口)是值接管(对象),还是是指针接管(对象指针),都能够用调用者间接调用办法。此时调用者不受是值还是指针的限度。 换句话说:无论接收者是值类型还是指针类型,都能够通过值类型或者指针类型调用。起因是这里有寻址及解援用的隐形操作。 调用者为 接口先说调用者为接口的意思是: type Coder interface { code() debug()}func Work(c Coder){ c.code() c.debug()}type Golang struct {}type PHP struct {}func (g *Golang) code(){ fmt.Println("golang code")}func (g *Golang) debug(){ fmt.Println("golang debug")}func (g *PHP) code(){ fmt.Println("php code")}func (g PHP) debug(){ fmt.Println("php debug")}//这里了解为调用者为接口,var c Coder=&PHP{}c.debug()c.code()这里次要波及到接口转换,及须要把coder转为phper或者golanger论断:实现接收者是值类型的接口办法时,能够失常地用值或指针进行接口转换,但实现接收者是指针类型的办法时,接口转换只能应用指针,不能应用值类型。 起因:通过值能够找到值对应惟一的指针(无论是应用值还是应用指针,办法集都是一样的)。 然而,实现接收者是指针类型的办法时,接口转换只能应用指针,不能应用值类型。这是因为指针办法冀望的是一个能够被取地址的对象,而通过值类型进行接口转换时,你可能会传递一个无奈取地址的长期对象,这会导致无奈调用指针办法。 对于批改如果办法的接收者是值类型,无论调用者是对象还是对象指针,批改的都是其正本,调用者自身不会更改。如果办法接收者是指针类型,批改的就是调用者自身。 当你不须要批改状态且要防止并发问题时,值类型接收者是个不错的抉择。当你须要批改状态、须要在并发环境下操作或者须要缩小值拷贝时,指针类型接收者更适合。 抉择接收者是值类型还是指针类型取决于多种因素,包含可修改性、并发操作和内存开销。 接收者是值类型的场景:不须要批改接收者的状态: 如果办法不须要批改接收者的状态,而只是基于其属性进行操作,那么能够应用值类型接收者。这样能够防止意外的副作用。 防止并发问题: 值类型接收者是线程平安的,因为它们在办法外部只操作接收者的正本,不会影响原始数据的状态。这在并发环境中往往更容易治理。 接收者是指针类型的状况:须要批改接收者的状态: 如果办法须要批改接收者的状态,那么你必须应用指针类型接收者。这样能够在办法外部批改原始数据,而不仅仅是操作正本。 防止值拷贝带来的内存开销: 对于大型的构造体或对象,应用指针类型接收者能够防止在办法调用时进行值的拷贝,缩小内存和性能开销。

August 23, 2023 · 1 min · jiezi

关于go:Go-零值和空值的判断问题

大家好,我是煎鱼。 前段时间分享了《Go 将减少内置的零值标识符 zero!》的新预约义标识符 zero。对应的签名如下: // zero is a predeclared identifier representing the zero value// for array and struct types.var zero Type我原想着还是一个有一点点新扭转。不过综合大家意见来看,因为只是针对数组(array)和构造体(struct),许多同学示意这个是比拟鸡肋的。因为依然无奈很好的解决 Go 零值和空值的辨认问题,大喜过望。 本文是对零值和空值判断现状进行梳理和分享。 疾速温习零值根本类型var a intvar b boolvar c stringfunc main() { fmt.Printf("%+v\n", a) // 0 fmt.Printf("%+v\n", b) // false fmt.Printf("%+v\n", c) // ""}复合类型var a []intvar b map[string]intvar c [7]intvar d *intvar g chan intvar p Persontype Person struct { Name string Age int}func main() { fmt.Printf("%+v\n", a) // [] fmt.Printf("%+v\n", b) // map[] fmt.Printf("%+v\n", c) // [0 0 0 0 0 0 0] fmt.Printf("%+v\n", d) // <nil> fmt.Printf("%+v\n", g) // <nil> fmt.Printf("%+v\n", p) // {Name: Age:0}}进行空值判断在理论的 Go 业务利用中,咱们须要对数据的零值和空值进行辨别,以便于实现一些空值的业务逻辑解决。 ...

August 22, 2023 · 2 min · jiezi

关于go:十大常见的排序算法go语言实现

十大常见的排序算法(go语言实现)冒泡排序(Bubble Sort)抉择排序(Selection Sort)插入排序(Insertion Sort)希尔排序(Shell Sort)归并排序(Merge Sort)疾速排序(Quick Sort)堆排序(Heap Sort)计数排序(Counting Sort)桶排序(Bucket Sort)基数排序(Radix Sort) go语言实现package mainimport "fmt"// 冒泡排序func BubbleSort(arr []int) { n := len(arr) for i := 0; i < n-1; i++ { for j := 0; j < n-i-1; j++ { if arr[j] > arr[j+1] { arr[j], arr[j+1] = arr[j+1], arr[j] } } }}// 抉择排序func SelectionSort(arr []int) { n := len(arr) for i := 0; i < n-1; i++ { minIndex := i for j := i + 1; j < n; j++ { if arr[j] < arr[minIndex] { minIndex = j } } arr[i], arr[minIndex] = arr[minIndex], arr[i] }}// 插入排序func InsertionSort(arr []int) { n := len(arr) for i := 1; i < n; i++ { key := arr[i] j := i - 1 for j >= 0 && arr[j] > key { arr[j+1] = arr[j] j-- } arr[j+1] = key }}// 希尔排序func ShellSort(arr []int) { n := len(arr) gap := n / 2 for gap > 0 { for i := gap; i < n; i++ { j := i for j-gap >= 0 && arr[j-gap] > arr[j] { arr[j-gap], arr[j] = arr[j], arr[j-gap] j -= gap } } gap /= 2 }}// 归并排序func MergeSort(arr []int) []int { if len(arr) <= 1 { return arr } mid := len(arr) / 2 left := MergeSort(arr[:mid]) right := MergeSort(arr[mid:]) return merge(left, right)}func merge(left, right []int) []int { result := make([]int, 0) i, j := 0, 0 for i < len(left) && j < len(right) { if left[i] < right[j] { result = append(result, left[i]) i++ } else { result = append(result, right[j]) j++ } } result = append(result, left[i:]...) result = append(result, right[j:]...) return result}// 疾速排序func QuickSort(arr []int) { quickSort(arr, 0, len(arr)-1)}func quickSort(arr []int, left, right int) { if left < right { pivot := partition(arr, left, right) quickSort(arr, left, pivot-1) quickSort(arr, pivot+1, right) }}func partition(arr []int, left, right int) int { pivot := arr[right] i := left - 1 for j := left; j < right; j++ { if arr[j] < pivot { i++ arr[i], arr[j] = arr[j], arr[i] } } arr[i+1], arr[right] = arr[right], arr[i+1] return i + 1}// 堆排序func HeapSort(arr []int) { n := len(arr) for i := n/2 - 1; i >= 0; i-- { heapify(arr, n, i) } for i := n - 1; i >= 0; i-- { arr[0], arr[i] = arr[i], arr[0] heapify(arr, i, 0) }}func heapify(arr []int, n, i int) { largest := i left := 2*i + 1 right := 2*i + 2 if left < n && arr[left] > arr[largest] { largest = left } if right < n && arr[right] > arr[largest] { largest = right } if largest != i { arr[i], arr[largest] = arr[largest], arr[i] heapify(arr, n, largest) }}// 计数排序func CountingSort(arr []int) { n := len(arr) if n <= 1 { return } maxVal := arr[0] for i := 1; i < n; i++ { if arr[i] > maxVal { maxVal = arr[i] } } count := make([]int, maxVal+1) for i := 0; i < n; i++ { count[arr[i]]++ } for i, j := 0, 0; i <= maxVal; i++ { for count[i] > 0 { arr[j] = i j++ count[i]-- } }}// 桶排序func BucketSort(arr []int) { n := len(arr) if n <= 1 { return } maxVal := arr[0] minVal := arr[0] for i := 1; i < n; i++ { if arr[i] > maxVal { maxVal = arr[i] } if arr[i] < minVal { minVal = arr[i] } } bucketSize := 10 bucketCount := (maxVal-minVal)/bucketSize + 1 buckets := make([][]int, bucketCount) for i := 0; i < bucketCount; i++ { buckets[i] = make([]int, 0) } for i := 0; i < n; i++ { index := (arr[i] - minVal) / bucketSize buckets[index] = append(buckets[index], arr[i]) } k := 0 for i := 0; i < bucketCount; i++ { if len(buckets[i]) > 0 { InsertionSort(buckets[i]) copy(arr[k:], buckets[i]) k += len(buckets[i]) } }}// 基数排序func RadixSort(arr []int) { n := len(arr) if n <= 1 { return } maxVal := arr[0] for i := 1; i < n; i++ { if arr[i] > maxVal { maxVal = arr[i] } } exp := 1 for maxVal/exp > 0 { countingSortForRadix(arr, exp) exp *= 10 }}func countingSortForRadix(arr []int, exp int) { n := len(arr) count := make([]int, 10) output := make([]int, n) for i := 0; i < n; i++ { index := (arr[i] / exp) % 10 count[index]++ } for i := 1; i < 10; i++ { count[i] += count[i-1] } for i := n - 1; i >= 0; i-- { index := (arr[i] / exp) % 10 output[count[index]-1] = arr[i] count[index]-- } for i := 0; i < n; i++ { arr[i] = output[i] }}func main() { arr1 := []int{1, 3, 2, 5, 4, 7, 6, 9, 8} arr2 := []int{1, 3, 2, 5, 4, 7, 6, 9, 8} arr3 := []int{1, 3, 2, 5, 4, 7, 6, 9, 8} arr4 := []int{1, 3, 2, 5, 4, 7, 6, 9, 8} arr5 := []int{1, 3, 2, 5, 4, 7, 6, 9, 8} arr6 := []int{1, 3, 2, 5, 4, 7, 6, 9, 8} arr7 := []int{1, 3, 2, 5, 4, 7, 6, 9, 8} arr8 := []int{1, 3, 2, 5, 4, 7, 6, 9, 8} arr9 := []int{1, 3, 2, 5, 4, 7, 6, 9, 8} arr10 := []int{1, 3, 2, 5, 4, 7, 6, 9, 8} BubbleSort(arr1) fmt.Printf("arr1: %v\n", arr1) SelectionSort(arr2) fmt.Printf("arr2: %v\n", arr2) InsertionSort(arr3) fmt.Printf("arr3: %v\n", arr3) ShellSort(arr4) fmt.Printf("arr4: %v\n", arr4) arr5 = MergeSort(arr5) fmt.Printf("arr5: %v\n", arr5) QuickSort(arr6) fmt.Printf("arr6: %v\n", arr6) HeapSort(arr7) fmt.Printf("arr7: %v\n", arr7) CountingSort(arr8) fmt.Printf("arr8: %v\n", arr8) BucketSort(arr9) fmt.Printf("arr9: %v\n", arr9) RadixSort(arr10) fmt.Printf("arr10: %v\n", arr10)}输入的后果: ...

August 21, 2023 · 5 min · jiezi

关于go:常见的Go项目文件结构

Go我的项目文件构造在构建 Go 语言我的项目时,一个良好的文件构造能够进步我的项目的可性、可扩展性和清晰度。尽管在 Go 中没有强制规定文件构造,然而有一些常见的操作和最佳实际,能够帮忙你组织我的项目的代码、资源和依赖。 以下是一个常见的Go语言我的项目文件构造示例: myproject/├── cmd/│ └── myapp/│ └── main.go├── internal/│ ├── api/│ │ ├── user.go│ │ └── product.go│ ├── services/│ │ ├── userservice.go│ │ └── productservice.go│ └── ...├── pkg/│ ├── utils/│ │ ├── stringutil.go│ │ └── mathutil.go│ └── ...├── web/│ ├── static/│ │ ├── css/│ │ ├── js/│ │ └── ...│ ├── templates/│ │ ├── index.html│ │ ├── about.html│ │ └── ...│ └── ...├── config/│ ├── appconfig.json│ ├── databaseconfig.json│ └── ...├── tests/│ ├── unit/│ ├── integration/│ └── ...├── go.mod└── README.md在上述示例中,各局部的作用如下: ...

August 21, 2023 · 1 min · jiezi

关于go:Go-语言中排序的-3-种方法

原文链接: Go 语言中排序的 3 种办法 在写代码过程中,排序是常常会遇到的需要,本文会介绍三种罕用的办法。 废话不多说,上面注释开始。 应用规范库依据场景间接应用规范库中的办法,比方: sort.Intssort.Float64ssort.Strings举个例子: s := []int{4, 2, 3, 1}sort.Ints(s)fmt.Println(s) // [1 2 3 4]自定义比拟器应用 sort.Slice 办法排序时,能够自定义比拟函数 less(i, j int) bool,这样就能够依据须要按不同的字段进行排序。 如果想要稳固排序的话,就应用 sort.SliceStable 办法。 举个例子: family := []struct { Name string Age int}{ {"Alice", 23}, {"David", 2}, {"Eve", 2}, {"Bob", 25},}// Sort by age, keeping original order or equal elements.sort.SliceStable(family, func(i, j int) bool { return family[i].Age < family[j].Age})fmt.Println(family) // [{David 2} {Eve 2} {Alice 23} {Bob 25}]自定义数据结构应用 sort.Sort 或者 sort.Stable 办法,它们能够对任意实现了 sort.Interface 的数据结构排序。 ...

August 18, 2023 · 2 min · jiezi

关于go:golang实现简单的可重入锁

package mainimport ( "fmt" "github.com/petermattis/goid" "sync" "sync/atomic")// Go version: V1.21.0type ReentryMutex struct { sync.Mutex owner int64//以后锁的拥有者 goroutineid reentry int32//重入次数}func (r *ReentryMutex)Lock(){ gid:=goid.Get() //如果锁的拥有者曾经是以后goroutine,记录下他曾经重入的次数 if atomic.LoadInt64(&r.owner)==gid{ r.reentry++ return } r.Mutex.Lock() atomic.StoreInt64(&r.owner,gid) //初始化可拜访次数 r.reentry=1}func (r *ReentryMutex) Unlock(){ gid:=goid.Get() //如果解锁协程不是以后协程的拥有者,就panic if atomic.LoadInt64(&r.owner)!=gid{ panic(any(fmt.Sprintf("wrong the owner(%d): %d!",r.owner,gid))) } r.reentry-- if r.reentry==0{ atomic.StoreInt64(&r.owner,-1) r.Mutex.Unlock() }}func main() { var wg sync.WaitGroup reentryMutex:=ReentryMutex{} for i := 0; i < 5; i++ { wg.Add(1) go func(index int) { defer wg.Done() reentryMutex.Lock() defer reentryMutex.Unlock() fmt.Printf("Goroutine %d acquired the lock.\n", index) reentryMutex.Lock() defer reentryMutex.Unlock() fmt.Printf("Goroutine %d acquired the lock again.\n", index) }(i) } wg.Wait() fmt.Println("All goroutines have finished.")}

August 18, 2023 · 1 min · jiezi

关于go:图解-Golang-内存分配

咱们晓得所有程序运行都须要应用内存,而内存的治理和调配又是十分重要的,它决定了你的程序能不能在无限的资源内跑的更快。能够构想一下,如果你本人来设计的一个内存调配的规定,会遇到什么问题呢?如果你有了一大块内存你要怎么去正当的调配和应用呢?明天咱们通过几张图来看看golang中的内存调配是怎么的。 前置常识:对golang的GPM模型有所理解,对GC有肯定的理解,有助于你了解上面的内容。 想一想咱们首先来想一下,如果咱们本人来分配内存的时候可能会遇到什么问题。 我想要512G,你能给吗?操作系统的内存不是你想要多少就给你多少的。比方我跟操作系统说我要512G内存,你连忙给我,不给我我就掐死你,如果你是操作系统,是不是立马就想把我给完结了? 能轻易宰割吗?如果我拿到一块内存,挺大的,你把它设想成一块地,我明天要用这块地的这个局部,必定是从两头切一块进去用,而后今天要另一个局部,而后再切出来一部分。如果轻易切,明天要一块三角形,今天要一块圆形,那么必定会留有很多小块的中央,那些中央没有方法被正当的应用,就会节约。等到想再要一块正方形的地的时候发现没中央能够切了。 不必了我须要放回去吗?如果我占用了很大一块内存资源,而后用完了,当初不须要了,那自私的人必定想着,我就偷偷始终占用不行吗?显然是不能够的,不然的话你的应用程序就每天占用着一台机器大量的资源不开释,别的人都没得用了,必定想把你干掉。所以用完了要放回去。 --其实下面的问题就是内存调配常见的一些问题,那为了高效、正当利用内存,势必须要一些人的治理和帮忙,上面咱们就来看看那些在golang中的管理者,看看他们是如何帮忙咱们去治理和分配内存的。 内存的管理者这张图外面就是golang中内存的管理者们,上面我来顺次介绍一下 OS首先是操作系统,他领有着全副的机器内存,咱们的程序必须向它要。然而他是大领导,很忙的,你不能没事总找他要,很烦,所以每次都会向他一大块内存(1M打底)他会给你一票地址,然而理论其实并不会间接给你分配内存,然而你用到了天然会有。 heap这个是咱们程序中最大的内存持有区域,堆,他治理着那些很大的内存块,同时是他向操作系统去申请内存的,全局只有他一个小人物。他还须要将从操作系统拿过去的内存进行肯定的划分,划分成一整块一整块的样子方便管理,同时记录内存的应用状况,不便整顿和回收。 central这个是二把手,有很多,他们会负责将内存划分成足够小的单元,同时须要向下提供内存的调配工作,这个时候就须要一些正当的调配措施了,这个咱们前面再说。 cache这个是最初一个小领导了,治理着最终线程须要应用的内存资源,而且每个线程都会有一个独立的cache,一对一绑定,这样应用的时候就会间接从对应的cache中去取来应用,这样的益处是不必和他人产生争抢。如果所有的线程都从一个中央进行取用,那么势必会造成你也要用,我也要用的状况。 总结从下面的图咱们能够根本明确一个总体的思路是说:须要有人总体去把控所有内存资源的应用,做到对立的调度和治理,这样能够不便后续的回收和利用。同时须要上面有人负责最终应用的调配,从而能达到一个内存的疾速调配而不产生争抢。 内存的调配构造咱们晓得了内存的管理者是谁,那么当初咱们再来看看内存到底是怎么划分的,到底是切成一个个长方形还是切成一个个圆形了呢? 这张图就示意了整个golang中内存的调配构造长什么样子。 arena这块区域最大,显著就是用来寄存咱们最终的对象,外面分成了一个个8K大小的房间,每个房间咱们称为page。(这里尽管写了它是512G,然而你心里要有B数,你电脑基本没这么大的内存,其实操作系统只是给了你地址而已)同时几个page组合在一起的大房间又叫做mspan(这个是golang中内存治理的根本单元) bitmap而后咱们再来看第二大的bitmap,它是用来示意arena中寄存的对象的一些信息,包含这个对象GC标记,还有标识这个对象是否蕴含指针。你必定就好奇,干嘛要有这个呢?这其实也很好了解,golang在进行垃圾回收的时候是依据援用的可达性剖析来确定一个对象是否能够被回收,同时采纳的是三色标记法进行标记对象,所以这里须要有bitmap来保留这些信息。(具体如果不分明垃圾回收的细节能够去看看我之前写的无关垃圾回收的局部) spans最初是spans,这里保留了mspan的指针,这个也好了解,为了方便管理那一个个大房间嘛 内存调配那么最初咱们来看看咱们创立的一个对象最初到底会经验些什么,是怎么样调配的呢?首先要阐明的是,golang很聪慧的,如果一个变量能够调配在栈上,那么就不会被调配在堆上,这样能够无效的节约资源(具体我后续还会写别的来阐明golang中的变量)。总之咱们这里探讨的是调配在堆上的状况。整个流程差不多相似就是这样,嗯,你只有把内存设想成房间,当初房价那么贵,你懂的 调配流程大对象: >32KB 的对象,间接从heap上调配小对象: 16B < obj <= 32KB 计算规格在mcache中找到适合大小的mspan进行调配(你有多大就住多大的房子竟可能的不要节约房子的空间)渺小对象: <=16B 的对象应用mcache的tiny分配器调配;(如果将多个渺小对象组合起来,用单块内存(object)存储,可无效缩小内存节约。)秉持准则:给到最合适的大小,而后能凑一起的凑一起挤一挤 扩容如果不够怎么办呢?不够必定就要扩容了呗,当不够的时候就会向领导上报,逐层上报,最终想方法拿到内存。如果cache没有相应规格大小的mspan,则向central申请如果central没有相应规格大小的mspan,则向heap申请如果heap中也没有适合大小的mspan,则向操作系统申请 回收最初还要记得,如果你用完了,不必了,会有后盾的清洁工来回收掉,最终还是会还回去的。一方面呢:cache用完了还给central,central就能够给别的cache用;central用完了就会还给heap...最终都不必的还给操作系统 总结至此golang的内存调配也就说的差不多了,其中一些细节可能没有说到,可能你还须要看看别的文章来补一补。总结一下: 你多小孩儿住多大的房间,不多给划分成正当的大小能够一起给一起回收,大小适合的宰割才不会节约用完还回去,须要标记怎么样算用完了每个人线程有独立的缓冲区来进行疾速调配,不必抢来抢去

August 18, 2023 · 1 min · jiezi

关于go:Go-将增加内置的零值标识符-zero

大家好,我是煎鱼。 大家学习和应用 Go 语言时,有一个神奇的概念:零值(zero-values)。零值这个名字是具体谁起的,又是从哪里先开始喊起的,曾经难以讲究了。 每次有新同学刚开始转语言,工程上总会提到这个,想要试图扭转零值。又或是没思考到,一旦程序用了 0 作为 default 值,又要兼容零值的问题。 明天给大家分享一个对于零值的新提案,目测曾经八九不离十了。 疾速温习零值根底类型的例子如下: 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 ""复合类型的例子如下: type Person struct { Name string Age int Weight float64}func main() { var p Person var m map[int]string var s []string fmt.Printf("%#v\n%#v\n%#v\n", p, m, s)}输入后果: main.Person{Name:"", Age:0, Weight:0}map[int]string(nil)[]string(nil)类型和零值的映射表格如下: 数据类型零值int, int8, int16, int32, int640uint, uint8, uint16, uint32, uint640uintptr0float32, float640byte0rune0string"" (empty string)complex64, complex128(0,0i)arrays of non-nillable typesarray of zero-valuesarrays of nillable typesarray of nil-values新提案:预申明标识符 zero背景事件的起因是:@Nathan Cormier 在社区提了吐槽和提案《proposal: builtin: zero function》: ...

August 17, 2023 · 2 min · jiezi

关于go:每天学点-Go-规范-context-类型的-key-有什么讲究

当初团队里简直所有的代码都须要通过 Code Review(代码审查)之后,才容许合入主分支。作为 CR 负责人之一,在 CR 中看到了不少不适宜的问题,也看到了不少值得学习的点。笔者决定从明天开始,一点一滴地记录这些做法、教训、教训,以飨读者。 这系列文章,我都会先抛出一句话标准阐明,而后解释问题的背景,最初再给出正确的标准。 一句话标准当应用 context.Context 类型保留 KV 对时, key 不能应用原生类型,而应该应用派生类型问题背景咱们晓得,能够利用 context.Context 类型来存一些自定义的键值对——当然了,须要保留新的 context 对象: ctx = context.WithValue(ctx, someKey, someValue)很快就能够留神到 key 的参数类型是 any,也就是 Go 1.17 之前的 interface{}。“可能是为了可能应用 int 吧?” ——初学者很可能会这么想。 在理论的 CR 中,能够看到很多人都应用 string 类型作为 key,比方这是一个十分典型的例子: ctx = context.WithValue(ctx, "openid", userOpenID)存在问题如果你应用了 VSCode,并且一键装置了 Go 开发工具包,那么 VSCode 大概率也装置了 golangci-lint 工具。此时,下面的这段代码会喜提一个 warning: SA1029: should not use built-in type string as key for value; define your own type to avoid collisions (staticcheck) ...

August 16, 2023 · 2 min · jiezi

关于go:Golang-PHP-数据绑定漫谈一

前言:小编好几个月都没更新啦。注意力都在体验幸福苦涩的的生存在。因为注意力被扩散,在此期间,耳机掉了两副,小米手环掉了两个,四周产生的事件间接性疏忽掉了,恍然间回过神来已过来几个月。蓦然回首工夫过滴好快呀。得给宝子们来点干货强类型语言数据绑定在强类型语言中,绑定参数分好几种类型。拿最应用宽泛的Gin web 框架介绍1、Bind 和 ShouldBind 和 NustBindGin 的 Context 为申请数据绑定提供了两大类办法:在命名上以 Bind 为前缀和以 ShouldBind、MustBind辨别。这两大类办法在行为上有些差别。类型形容must条件必须要满足should条件应该满足Bind 办法会主动将 http status 设置为 400。而且不会返回更多的信息。因为存在,错误处理简单,限度较多(仅仅反对常见类型),验证规定限度简略,可能存在平安危险等等在日常开发中简直不倡议应用bind 来绑定数据 ShouldBind、ShouldBindJSON 这些办法的区别是前者会主动依据 Header 头确定应用什么绑定器,如果团队内开发标准里约定了申请 Content-Type 都是 JSON 的话,间接选用后者更为正当。在理论开发中,ShouldBind 主动依据Header 头确定应用什么绑定器应用最多。因为团队的约定对新退出的团队的人来说没看到约定的状况下,容易呈现代码不兼容的问题。 var request struct { Email string `form:"email" binding:"required"` Name string `form:"name" binding:"required"`}// ShouldBind 惯例绑定也能够兼容post get json 参数if err := ctx.ShouldBind(&request); err != nil { core.Response(ctx, gin.H{}, err) return}// Content-Type 都是 JSON 的话,对于强类型语言来说,应用上面这段更正当,个别状况还是用下面这种。if err := ctx.ShouldBindJson(&request); err != nil { core.Response(ctx, gin.H{}, err) return}2、单次绑定和屡次绑定request body 数据有了下面的绑定根底状况下,小编遇到了这样的一个问题。权限验证中间件,在接管参数的时候应用的上面代码的接管参数的形式。接口的申请参数是Content-Type 都是 JSON 的类型的。 ...

August 16, 2023 · 1 min · jiezi

关于go:基于-JIT-技术的开源全场景高性能-JSON-库

大家好,我是Mandy,上一节咱们对Go中的切片数据类型进行了深度的分析,明天给大家分享一个字节跳动自研开源的JSON数据解析包。一个速度奇快的 JSON 序列化/反序列化库,由 JIT (即时编译)和 SIMD (单指令流多数据流)减速。 sonic 是字节跳动开源的一款 Golang JSON 库,基于即时编译(Just-In-Time Compilation)与向量化编程(Single Instruction Multiple Data)技术,大幅晋升了 Go 程序的 JSON 编解码性能。同时联合 lazy-load 设计思维,它也为不同业务场景打造了一套全面高效的 API。 自研背景Go 自身自带规范 JSON 库:encoding/json,另外还有很多优良的第三方库,比方:Json-iterator、Easyjson、Gjson、Sjson 等,其中 Json-iterator 最受欢迎(12.3+k Star)。那为什么字节跳动还会抉择自研一个JSON解析库呢? JSON(JavaScript Object Notation) 以其简洁的语法和灵便的自描述能力,被广泛应用于各互联网业务。然而 JSON 因为实质是一种文本协定,且没有相似 Protobuf 的强制模型束缚(schema),编解码效率往往非常低下。再加上有些业务开发者对 JSON 库的不失当选型与应用,最终导致服务性能急剧劣化。 依据字节跳动生产服务的整体剖析,咱们发现 JSON 序列化和反序列化的开销意外地很高:CPU 使用率靠近 10%,其中极其状况下超过 40%。因而,JSON 库的性能是进步机器利用率的关键问题。 在字节跳动,咱们也遇到了上述问题。依据此前统计的公司 CPU 占比 TOP 50 服务的性能剖析数据,JSON 编解码开销总体靠近 10%,单个业务占比甚至超过 40%,晋升 JSON 库的性能至关重要。因而咱们对业界现有 Go JSON 库进行了一番评估测试。 首先,依据支流 JSON 库 API,咱们将它们的应用形式分为三种: 泛型(generic)编解码:JSON 没有对应的 schema,只能根据自描述语义将读取到的 value 解释为对应语言的运行时对象,例如:JSON object 转化为 Go map[string]interface{};定型(binding)编解码:JSON 有对应的 schema,能够同时联合模型定义(Go struct)与 JSON 语法,将读取到的 value 绑定到对应的模型字段下来,同时实现数据解析与校验;查找(get)& 批改(set) :指定某种规定的查找门路(个别是 key 与 index 的汇合),获取须要的那局部 JSON value 并解决。其次,咱们依据样本 JSON 的 key 数量和深度分为三个量级: ...

July 13, 2023 · 8 min · jiezi

关于go:Go特殊的语言特性

摘要:本文由葡萄城技术团队于思否原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供业余的开发工具、解决方案和服务,赋能开发者。前言本文次要通过值传递和指针、字符串、数组、切片、汇合、面向对象(封装、继承、形象)和设计哲学7个方面来介绍GO语言的个性。 文章目录: 1.Go的前世今生 1.1Go语言诞生的过程 1.2逐渐成型 1.3正式公布 1.4 Go装置领导 2.GO语言非凡的语言个性 2.1值传递和指针 2.2字符串 2.3数组 2.4切片 2.5汇合 2.6面向对象 2.6.1封装 2.6.2继承 2.6.3形象 2.7设计哲学 2.7.1. Go 没有默认的类型转换 2.7.2. Go 没有默认参数,同样也没有办法重载 2.7.3. Go 不反对 Attribute 2.7.4. Go 没有 Exception Rob Pike 和平常一样启动了一个 C++我的项目的构建,依照他之前的教训,这个构建应该须要继续 1 个小时左右。这时他就和 Google公司的另外两个共事 Ken Thompson 以及 Robert Griesemer 开始吐槽并且说出了本人想搞一个新语言的想法。过后 Google 外部次要应用 C++构建各种零碎,但 C++复杂性微小并且原生短少对并发的反对,使得这三位大佬苦恼不已。 ...

July 13, 2023 · 5 min · jiezi

关于go:一文了解-ioLimitedReader类型

1. 引言io.LimitedReader 提供了一个无限的读取性能,可能手动设置最多从数据源最多读取的字节数。本文咱们将从 io.LimitedReader 的根本定义登程,讲述其根本应用和实现原理,其次,再简略讲述下具体的应用场景,基于此来实现对io.LimitedReader 的介绍。 2. 根本阐明2.1 根本定义io.LimitedReader 是Go语言提供的一个Reader类型,其包装了了一个io.Reader 接口,提供了一种无限的读取性能。io.LimitedReader的根本定义如下: type LimitedReader struct { R Reader // underlying reader N int64 // max bytes remaining}func (l *LimitedReader) Read(p []byte) (n int, err error) {}LimitedReader构造体中蕴含了两个字段,其中R 为底层Reader, 数据都是从Reader 当中读取的,而 N 则代表了残余最多能够读取的字节数。同时也提供了一个Read 办法,通过该办法来实现对数据进行读取,在读取过程中 N 的值会一直减小。 通过应用io.LimitedReader, 咱们能够管制从底层读取器读取的字节数,防止读取到不应该读取的数据,这个在某些场景下十分有用。 同时Go语言还提供了一个函数,可能应用该函数,创立出一个io.LimitedReader 实例,函数定义如下: func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} }咱们能够通过该函数创立出一个LimitedReader 实例,也可能晋升代码的可读性。 2.2 应用示例上面咱们展现如何应用io.LimitedReader 限度读取的字节数,代码示例如下: package mainimport ( "fmt" "io" "strings")func main() { // 创立一个字符串作为底层读取器 reader := strings.NewReader("Hello, World!") // 创立一个LimitedReader,限度最多读取5个字节 limitReader := io.LimitReader(reader, 5) // 应用LimitedReader进行读取操作 buffer := make([]byte, 10) n, err := limitReader.Read(buffer) if err != nil && err != io.EOF { fmt.Println("读取谬误:", err) return } fmt.Println("读取的字节数:", n) fmt.Println("读取的内容:", string(buffer[:n]))}在下面示例中,咱们应用字符串创立了一个底层Reader,而后基于该底层Reader创立了一个io.LimitedReader,同时限度了最多读取5个字节。而后调用 limitReader 的 Read 办法读取数据,此时将会读取数据放到缓冲区当中,程序将读取到的字节数和内容打印进去。函数运行后果如下: ...

July 12, 2023 · 2 min · jiezi

关于go:如何接手并维护一个项目

首发于:https://blog.debuginn.cn 在工作中,接手负责管理他人开发或者前人开发的我的项目是每个开发人员的工作工作之一,那么,如何疾速并且高效的消化接手过去的我的项目呢,本文次要解说一些办法与实际技巧,心愿能够帮忙你疾速理解你接手的我的项目。零碎文档若是有最开始的包含后续优化的相干技术文档或者零碎文档,对于接手过去的我的项目无疑是最有助于开发人员的形式。然而大家会发现往往接手过去的我的项目是没有这一类的文档的,交接过去的零碎若是对开发有极高谋求的,个别都会有文档,并且 README.md 中会有我的项目介绍包含相干文档,然而...... 往往咱们拿到手的零碎是纯代码,README.md 可能都没有这个文件,这种往往是最苦楚的,不过也是最锤炼梳理零碎这项技能的。 那么咱们就须要从上面这几个点来缓缓消化系统。 零碎权限交接过去的零碎,肯定要开好对应的权限,这对你全面理解零碎以及后续保护零碎有着至关重要的的作用,若没有权限,当零碎呈现问题时,领导找到你问问题起因,而你却在向领导申请权限,世纪名局面。 以下是常见的零碎权限: Gitlab 仓库权限;Deploy 部署零碎权限;Log 日志零碎权限;Data 数据库管理系统权限;Alert 零碎报警配置权限;Crond 任务调度器权限;Middleware 依据不同零碎的中间件权限,包含不限于 Notify、RocketMQ、Redis 等;Auth 依赖零碎受权信息权限;Test 测试平台的权限;API API 调试平台的权限;Sys 零碎相干注册管理权限;接手我的项目,开启权限是第一步也是必须要做的事件。 配置文件通过配置文件能够看到一个零碎的根本信息,比如说: 零碎环境配置信息、注册信息、协定相干信息;零碎应用数据库配置信息;零碎应用 Redis 等中间件配置信息;业务上应用的一些值定义;库表设计个别数据库表设计会寄存在 dao 模块或者目录下,基本上是一表一文件定义,能够看到表定义的字段,并且能够看到对该表的一些“增删查改”动作。 若是底层零碎设计,自身零碎就是只提供给内部服务应用的,那么从数据库库表设计基本上就能够反推业务逻辑的设计,删除、更新、新增都是根本的业务逻辑动作,查问或者组合成事务的业务相对来说比较复杂,不过依据业务代码看着了解的话也比较简单一些。 缓存设计缓存个别有两种,别离是: 被动缓存:个别是用于高并发场景,用于缓解上游中间件或者接口的刹时压力;被动缓存:这种是绝对高级的缓存策略,用于分布式数据统一数据的返回;剖析刚接手的零碎,从 cacheKey 就能够理解业务零碎中为什么这样设计的: product.info.pid.XXXX下面这个例子能够看到记录的是一个产品 pid 为 XXXX 的缓存信息。 协定文件若是接手过去的零碎依照语义命名及划分路由的话,则通过 API 接口文档来看是一个很好的形式,因为通过 API 根本能够确定接手过去的服务有哪些业务,针对不同的业务又有哪些操作。 针对不同的语言、不同的协定,也有一些轻微的差异: Java Dubbo 协定个别 Java Dubbo 协定都是对外提供 API 模块的 pom 依赖的,申明都是应用接口来实现的: // XXXX 业务模块public interface XXXX { // 获取列表 Result<DataA> getXXXList(Context ctx, XXRequest req); // 获取列表 基于 uid Result<DataA> getXXXListByUid(Context ctx, XXRequest req); // 基于 uid 删除 XX 信息 Result<Boolean> delXXInfoByUid(Context ctx, XXRequest req);}Go Thrift 协定Go Thrift 是应用 IDL 语言定义的协定,咱们会基于 IDL 申明的接口,定义好接口出入参生成的 SDK 文件,通过看 IDL 定义的接口,就能够理解到接手的我的项目提供了哪些性能了: ...

July 11, 2023 · 2 min · jiezi

关于go:容器运行时的内部结构和最新趋势2023

容器运行时的内部结构和最新趋势(2023)原文为 Akihiro Suda 在日本京都大学做的在线讲座,残缺的 PPT 可 点击此处下载 本文内容分为以下三个局部: 容器简介容器运行时的内部结构容器运行时的最新趋势1. 容器简介什么是容器?容器是一组用于隔离文件系统、CPU 资源、内存资源、零碎权限等的各种轻量级办法。容器在很多意义上相似于虚拟机,但它们比虚拟机更高效,而安全性则往往低于虚拟机。 乏味的是,“容器”目前还没有严格的定义。当虚拟机提供相似容器的接口时,例如,当它们实现 OCI(凋谢容器)标准 时,甚至虚拟机也能够被称为“容器”。这种“非容器”的容器将在前面的第三局部中探讨。 DockerDocker 是最风行的容器引擎。Docker 自身反对 Linux 容器和 Windows 容器,但 Windows 容器不在本次探讨的范畴之内。 启动 Docker 容器的典型命令行如下: docker run -p 8080:80 -v .:/usr/share/nginx/html nginx:1.25执行该命令后,能够在 http://<the host’s IP>:8080/ 中看到当前目录下 index.html 的内容。 命令中的 -p 8080:80 局部指定将主机的 TCP 8080 端口转发到容器的 80 端口。 命令中的 -v .:/usr/share/nginx/html 局部指定将主机上的当前目录挂载到容器中的 /usr/share/nginx/html。 命令中的 nginx:1.25 指定应用 Docker Hub 上的 官网 nginx 镜像。Docker 镜像与虚拟机镜像有些类似,然而它们通常不蕴含额定的诸如 systemd 和 sshd 等守护过程。 您也能够在 Docker Hub 上找到其余应用程序的官网镜像。您还能够应用称为 Dockerfile 的语言自行构建本人的镜像: ...

July 11, 2023 · 5 min · jiezi

关于go:Go-defer-去掉闭包函数靠谱吗

大家好,我是煎鱼。 在 Go 语言里,defer 关键字是大家很爱用的。因为他有着 defer+recover+panic 的组合拳打法,还有种各种 defer close 等罕用场景。 defer 常见用法在语法上,Go defer 的代码示例如下: package mainimport "fmt"func main() { defer fmt.Println("煎鱼你好!") fmt.Println("放学别走")}输入后果: 放学别走煎鱼你好!那 defer 在 Go 里的常见用法有哪些呢?首先是上文用到的,间接 defer + 函数: defer f()其次是 defer+闭包的形式: defer func() { result := f() // do something with result}()其余还有在面试题上常被讲究的传参变形: func f1() int { i := 1 defer func() { i++ }() ...}func f2() int { i := 1 defer func(i int) { i++ }(i) ....}这些代码看起来,咱们总是在对 defer 做闭包的各种申明和应用。defer 会不会就是和闭包天生一对? ...

July 11, 2023 · 1 min · jiezi

关于go:一文了解-ioCopy-函数

1. 引言io.Copy 函数是一个十分好用的函数,可能十分不便得将数据进行拷贝。本文咱们将从io.Copy 函数的根本定义登程,讲述其根本应用和实现原理,以及一些注意事项,基于此实现对io.Copy 函数的介绍。 2. 根本阐明2.1 根本定义Copy函数用于将数据从源(io.Reader)复制到指标(io.Writer)。它会继续复制直到源中的数据全副读取结束或产生谬误,并返回复制的字节数和可能的谬误。函数定义如下: func Copy(dst io.Writer, src io.Reader) (written int64, err error)其中dst 为指标写入器,用于接管源数据;src则是源读取器,用于提供数据。 2.2 应用示例上面提供一个应用 io.Copy 实现数据拷贝的代码示例,比便更好得了解和应用Copy函数,代码示例如下: package mainimport ( "fmt" "io" "os")func main() { fmt.Print("请输出一个字符串:") src := readString() // 通过io.Copy 函数可能将 src 的全副数据 拷贝到 管制台上输入 written, err := io.Copy(os.Stdout, src) if err != nil { fmt.Println("复制过程中产生谬误:", err) return } fmt.Printf("\n胜利复制了 %d 个字节。\n", written)}func readString() io.Reader { buffer := make([]byte, 1024) n, _ := os.Stdin.Read(buffer) // 如果理论读取的字节数少于切片长度,则截取切片 if n < len(buffer) { buffer = buffer[:n] } return strings.NewReader(string(buffer))}在这个例子中,咱们首先应用readString函数从规范输出中读取字符串,而后应用strings.NewReader将其包装为io.Reader返回。 ...

July 10, 2023 · 3 min · jiezi

关于go:如何评价腾讯这家公司

大家好,我是熊哥。 腾讯是我的老东家,我有很多腾讯的敌人,所以我对她有非凡的感情。我对她的爱从未变过,我见证了腾讯的成长。上次写完阿里当前,经读者要求写写腾讯。 腾讯是个聊天软件还是个卖游戏的?普通人提到腾讯的第一印象是什么? 十年前:那家做QQ的公司。十年后:那家做微信的公司。那个氪金就能变强的游戏公司。但实际上腾讯的业务曾经多到连小马哥也不晓得有多少个业务的状况,聊天软件还是游戏公司只不过是业界对腾讯的高度概括。 能够说只有是要用手机和电脑,那腾讯阿里这样的大公司的业务就无奈防止,除了本人的业务以外,投资的业务也到处都是,果然有钱真的能够任性。 截止2022年数据,腾讯投资了800余家公司,蕴含百余家上市公司及独角兽企业,投资地区涵盖寰球二十多个国家和地区。 所以对腾讯的第一个印象是:强 。 游戏圈的腾讯在游戏圈流传了一句话,你想变强吗?想变强就充钱,钱不是花掉了,而是换了种形式,在你身边。 这是很多玩家多年总结的情理,因为充钱的玩家总会失去更多的特权,更好的配备。「钱」在游戏圈里,是一个深入人心的概念,没钱别玩游戏。但实际上景象级游戏也只是靠卖卖皮肤而已。 但在阴阳师充钱也抽不到卡当前,网友粉粉心愿腾讯收买网易,充钱就能变强如同也不是那么不能承受了。 加上“微”翻新和奇葩家长的投诉腾讯总是在风口浪尖上。 而未成年防沉迷零碎让00后10后痛恨不已。 所以对腾讯的第二个印象是:长年被骂,但不会反驳公众舆论,只会虚心听取意见后默默作出改良。 大企业的担当手握海量微信QQ用户,以流量漏斗的形式来冲产品的博众,让腾讯无论做什么产品都能疾速起量。赚钱了容易让人眼红,但腾讯同样为社会做了很大的奉献。 在疫情期间,腾讯为近程办公奉献的力量也在颠覆传统办公形式,提供的高考服务工具也体现了大企业的担当。 所以对腾讯的第三个印象是:企业担当。 在腾讯工作是什么感觉?腾讯的工作体验曾经有很多人讲过了,大家轻易在网上搜搜看就能晓得,办公环境难受,人文关心好,培训体系欠缺等到。 我在腾讯工作过一段时间,因为不同的组的气氛不一样,我没方法代表所有的团队,然而我在的团队在我到职曾经好几年了,到当初为止咱们还常常一起玩,成为了一辈子的敌人兄弟姐妹,就可以看进去体验十分棒。 在这几年里我成长是十分快的,不可避免的就是每天都要加班,年轻人还是要奋斗。 不同的部门待遇是很有差距的,工作强度的差距也比拟大,越累的部门钱越多,尽管当初互联网动荡,如果有可能我还是会举荐我的敌人试试去腾讯工作的感觉。 我真的很惦记一站式的行政服务,各种班车、贷款福利、各种大食堂吃的很多又不贵。 明天我写腾讯公司可能没有方法站在十分主观的角度上来写,我打心底里认可这家公司,大家对腾讯公司的印象以及评估是怎么的呢?欢送一起探讨。 一起提高你好,我是小熊,是一个爱技术然而更爱钱的程序员。上进且佛系自律的人。喜爱发小机密/臭屁又爱夸耀。 奋斗的大学,激情的当初。赚了钱买了房,写了书出了名。当过面试官,带过师傅搬过转。 大厂外来务工人员。是我,我是小熊,是不一样的烟火欢送围观。 我的博客 机智的程序员小熊 欢送珍藏

July 7, 2023 · 1 min · jiezi

关于go:新型啃老正在悄悄出现

咱们总说毕业当前不啃老,不想留在父母身边,要外出打拼,追赶现实。 然而我发现有一些新型啃老,在人不知;鬼不觉中滋生。 “买房型”啃老从我毕业那一年,整个人指标除了扎根大城市,就是买房。就算到当初这样的房价状况,还是很多人想买房。 当初物价涨得厉害,房价也比拟高,工资却没有房价涨得那么快,所以就算是互联网大厂,也没方法齐全靠本人在三年之内买一套失常大小的房产。 有不少人不得不用工资中的一大半来偿还房贷,基本没有多余的精力和财产去关照家中的父母,比方我除了房贷还有一部分债权还结清。 我都算好的了,买房当前就没有给父母要过钱。但还是我四周有敌人买的房子比拟大,他们的父母还在反对他们还房贷,一起还房贷,甚至还有打生活费。 这间接导致咱们这一代很多人基本不想生养,因为咱们看到了那些毕业后没多久,曾经组建了家庭,开始养育孩子的年轻夫妻,同时还买了深圳的房子,雪上加霜。 尽管父母因为咱们能冲出小镇感到自豪,但看似孝顺的孩子才是真正的啃老族。这种新型啃老暗藏在完满人生的表面下,为父母带来了许多难以忍受的噩梦生存。 “留学型”啃老一名留学生爆出了他和父亲的聊天记录,宣称远赴西班牙读书是为了本人的人生现实,同时吐槽一个月一万的生活费齐全不够用。 他老板原本每个月只有一万三的工资,就曾经打给他一万了。国外生存老本高,既然家里违心供读,就老老实实节约一点过日子。 然而他常常违反约定,超值生活费,搞得老爹只能吃小米粥配青菜。 而他呢?认定了老爹是在卖惨,还在社交平台上谩骂曝光,我看了血压都高了。 这名留学生之所以会造就这样的性情,是否是家长的宠爱造成的呢?适度索取不知感恩。这种留学的同时,不知感恩,不懂节俭,便是第二种新型啃老,“留学型”啃老。 计划生育造就了资源歪斜为什么有下面两种状况?齐全就是因为计划生育。 在计划生育施行的时候就注定了生养老本的进步,因为容错率升高了,以往能够多生几个,只有有一个成器就行。 起初只能生一个,导致两个家庭的所有资源都向独生子女歪斜,胆怯一旦教育失误,二十年的心血付诸东流。 正是因为这种资源歪斜,涌现了有数的课外教育辅导,每个孩子取得的资源水平决定了他在将来的社会竞争力。为了缩小这种竞争力,就要投入更多的资源。这是一个死循环。 新一代人生观在社会压力大和生养老本高的双重压力下,导致了咱们90后不违心生养。 问了很多同龄人,很多人都决定丁克一辈子。宁肯一个人放弃高质量的单身生活,升高在日常物资上的花销。 如果无奈解决生养老本的问题,靠80后生二胎三胎,始终无奈阻挡这股洪流。咱们不是不违心生,而是不敢生,胆怯生。 对于现阶段的社会来说,真正重要的是尽快进步福利与工资待遇,让年轻人领有更多的工夫与财产,加重他们心中的焦虑。置信到了那个时候,困扰宽广父母的啃老问题也会失去缓解。 如果继续这种状况,将来人口负增长将会导致房价的个体上涨,加上00后10后的父母自身就有房,他们再也不必思考房子的问题了。 十多年之后所有的幼儿园、小学大学等都会招不到人,房子也卖不掉,个个都想躺平。到了那个时候,兴许幸福会降临呢? 一起提高你好,我是小熊,是一个爱技术然而更爱钱的程序员。上进且佛系自律的人。喜爱发小机密/臭屁又爱夸耀。 奋斗的大学,激情的当初。赚了钱买了房,写了书出了名。当过面试官,带过师傅搬过转。 大厂外来务工人员。是我,我是小熊,是不一样的烟火欢送围观。 我的博客 机智的程序员小熊 欢送珍藏

July 7, 2023 · 1 min · jiezi

关于go:使用golang120-极简-开发Prometheus-报警接口

Prometheustarget[root@vm11 prometheus]# cat prometheus.yml global: scrape_interval: 60s evaluation_interval: 65s scrape_timeout: 15s# Alertmanager configurationalerting: alertmanagers: - static_configs: - targets: ["localhost:3xxx"]rule_files: - "rule.yml"scrape_configs: - job_name: "prometheus" static_configs: - targets: ["192.168.1x4.xx:3xx"] labels: appservice: "prometheus" city: "苏州" instancehost: "192.1x.1x4.xx"rule[root@vm11 prometheus]# cat rule.yml groups:- name: Hosts.rules rules: - alert: HostDown expr: up{job=~"node-exporter|prometheus|grafana|alertmanager"} == 0 for: 0m labels: severity: critical annotations: description: "主机: 【{{ $labels.instance }}】 service is down-- Das ist ein Test." summary: "主机: 【{{ $labels.instance }}】 service is resolved" - alert: HostCpuLoadAvage expr: sum(node_load5) by (instance) > 10 for: 1m annotations: title: "5分钟内CPU负载过高" description: "主机: 【{{ $labels.instance }}】 5五分钟内CPU负载超过10 (以后值:{{ $value }})" labels: severity: 'warning' - alert: HostCpuUsage expr: (1-((sum(increase(node_cpu_seconds_total{mode="idle"}[5m])) by (instance))/ (sum(increase(node_cpu_seconds_total[5m])) by (instance))))*100 > 80 for: 1m annotations: title: "CPU使用率过高" description: "主机: 【{{ $labels.instance }}】 5五分钟内CPU使用率超过80% (以后值:{{ $value }})" labels: severity: 'warning' - alert: HostMemoryUsage expr: (1-((node_memory_Buffers_bytes + node_memory_Cached_bytes + node_memory_MemFree_bytes)/node_memory_MemTotal_bytes))*100 > 80 for: 1m annotations: title: "主机内存使用率超过80%" description: "主机: 【{{ $labels.instance }}】 内存使用率超过80% (以后使用率:{{ $value }}%)" labels: severity: 'warning' - alert: HostIOWait expr: ((sum(increase(node_cpu_seconds_total{mode="iowait"}[5m])) by (instance))/(sum(increase(node_cpu_seconds_total[5m])) by (instance)))*100 > 10 for: 1m annotations: title: "磁盘负载过高" description: "主机: 【{{ $labels.instance }}】 5五分钟内磁盘负载过高 (以后负载值:{{ $value }})" labels: severity: 'warning' - alert: HostFileSystemUsage expr: (1-(node_filesystem_free_bytes{fstype=~"ext4|xfs",mountpoint!~".*tmp|.*boot" }/node_filesystem_size_bytes{fstype=~"ext4|xfs",mountpoint!~".*tmp|.*boot" }))*100 > 70 for: 1m annotations: title: "磁盘空间残余有余" description: "主机: 【{{ $labels.instance }}】 {{ $labels.mountpoint }}分区使用率超过70%, 以后值使用率:{{ $value }}%" labels: severity: 'warning' - alert: HostSwapIsFillingUp expr: (1 - (node_memory_SwapFree_bytes / node_memory_SwapTotal_bytes)) * 100 > 80 for: 2m labels: severity: 'warning' annotations: title: "主机swap分区有余" description: "主机: 【{{ $labels.instance }}】 swap分区应用超过 (>80%), 以后值使用率: {{ $value }}%" - alert: HostNetworkConnection-ESTABLISHED expr: sum(node_netstat_Tcp_CurrEstab) by (instance) > 1000 for: 5m labels: severity: 'warning' annotations: title: "主机ESTABLISHED连接数过高" description: "主机: 【{{ $labels.instance }}】 ESTABLISHED连接数超过1000, 以后ESTABLISHED连接数: {{ $value }}" - alert: HostNetworkConnection-TIME_WAIT expr: sum(node_sockstat_TCP_tw) by (instance) > 1000 for: 5m labels: severity: 'warning' annotations: title: "主机TIME_WAIT连接数过高" description: "主机: 【{{ $labels.instance }}】 TIME_WAIT连接数超过1000, 以后TIME_WAIT连接数: {{ $value }}" - alert: HostUnusualNetworkThroughputIn expr: sum by (instance, device) (rate(node_network_receive_bytes_total{device=~"ens.*"}[2m])) / 1024 / 1024 > 100 for: 5m labels: severity: 'warning' annotations: title: "主机网卡入口流量过高" description: "主机: 【{{ $labels.instance }}】, 网卡: {{ $labels.device }} 入口流量超过 (> 100 MB/s), 以后值: {{ $value }}" - alert: HostUnusualNetworkThroughputOut expr: sum by (instance, device) (rate(node_network_transmit_bytes_total{device=~"ens.*"}[2m])) / 1024 / 1024 > 100 for: 5m labels: severity: 'warning' annotations: title: "主机网卡进口流量过高" description: "主机: 【{{ $labels.instance }}】, 网卡: {{ $labels.device }} 进口流量超过 (> 100 MB/s), 以后值: {{ $value }}" - alert: HostUnusualDiskReadRate expr: sum by (instance, device) (rate(node_disk_read_bytes_total{device=~"sd.*"}[2m])) / 1024 / 1024 > 50 for: 5m labels: severity: 'warning' annotations: title: "主机磁盘读取速率过高" description: "主机: 【{{ $labels.instance }}】, 磁盘: {{ $labels.device }} 读取速度超过(50 MB/s), 以后值: {{ $value }}" - alert: HostUnusualDiskWriteRate expr: sum by (instance, device) (rate(node_disk_written_bytes_total{device=~"sd.*"}[2m])) / 1024 / 1024 > 50 for: 2m labels: severity: 'warning' annotations: title: "主机磁盘写入速率过高" description: "主机: 【{{ $labels.instance }}】, 磁盘: {{ $labels.device }} 写入速度超过(50 MB/s), 以后值: {{ $value }}" - alert: HostOutOfInodes expr: node_filesystem_files_free{fstype=~"ext4|xfs",mountpoint!~".*tmp|.*boot" } / node_filesystem_files{fstype=~"ext4|xfs",mountpoint!~".*tmp|.*boot" } * 100 < 10 for: 2m labels: severity: 'warning' annotations: title: "主机分区Inode节点有余" description: "主机: 【{{ $labels.instance }}】 {{ $labels.mountpoint }}分区inode节点有余 (可用值小于{{ $value }}%)" - alert: HostUnusualDiskReadLatency expr: rate(node_disk_read_time_seconds_total{device=~"sd.*"}[1m]) / rate(node_disk_reads_completed_total{device=~"sd.*"}[1m]) > 0.1 and rate(node_disk_reads_completed_total{device=~"sd.*"}[1m]) > 0 for: 2m labels: severity: 'warning' annotations: title: "主机磁盘Read提早过高" description: "主机: 【{{ $labels.instance }}】, 磁盘: {{ $labels.device }} Read提早过高 (read operations > 100ms), 以后提早值: {{ $value }}ms" - alert: HostUnusualDiskWriteLatency expr: rate(node_disk_write_time_seconds_total{device=~"sd.*"}[1m]) / rate(node_disk_writes_completed_total{device=~"sd.*"}[1m]) > 0.1 and rate(node_disk_writes_completed_total{device=~"sd.*"}[1m]) > 0 for: 2m labels: severity: 'warning' annotations: title: "主机磁盘Write提早过高" description: "主机: 【{{ $labels.instance }}】, 磁盘: {{ $labels.device }} Write提早过高 (write operations > 100ms), 以后提早值: {{ $value }}ms"alertmanager[root@vm11 alertmanager]# cat alertmanager.yml route: group_by: ['alertname'] group_wait: 30s group_interval: 5m repeat_interval: 3600s receiver: 'Warning' routes: - receiver: 'Information' repeat_interval: 3600s match: severity: Information - receiver: 'Warning' repeat_interval: 3600s match: severity: Warningreceivers: - name: 'Warning' webhook_configs: - url: 'http://192.1x.1xx.xx:3xx/alertmanager/warning' send_resolved: true - name: 'Information' webhook_configs: - url: 'http://192.168.1x.1x:38xx/alertmanager/information' send_resolved: trueinhibit_rules: - source_match: severity: 'critical' target_match: severity: 'warning' equal: ['alertname', 'dev', 'instance']go code ...

July 7, 2023 · 7 min · jiezi

关于go:从php56到golang119文库App性能跃迁之路

作者 | 百度文库App 导读  本文深入浅出地分享了百度文库App服务端技术栈从PHP迁徙至Go的实战经验,蕴含了技术选型、根底建设、流量迁徙的具体计划,以及外围我的项目案例的重构实际。 全文6209字,预计浏览工夫16分钟。 01 动机长期以来,百度文库App服务端采纳 PHP 作为次要开发语言,高效地撑持了业务迭代倒退。随着平台流量的持续增长,服务端的负载越来越大逐步靠近零碎瓶颈。为了晋升零碎的负载能力,咱们采取了一些优化伎俩,其中最快最无效的办法是减少在线集群的实例数量。此外,还采纳过lua开发我的项目,承接一些逻辑简略而访问量大的接口来分担负载。因为lua自身的一些局限性,不适宜做简单的业务逻辑。 随同着IT技术的倒退潮流,咱们积极响应公司降本增效的号召,决定在2022年年中迁徙并重构服务端技术栈。旨在降级技术架构,晋升零碎负载能力。 把握技术栈迁徙和我的项目重构的机会是很难的一件事件,特地是成熟的团队要进行大的零碎改变。如果没有呈现真正的痛点,即便研发同学认为技术实现上曾经呈现诸多设计不合理和有危险的中央,往往并不被容许花大量工夫去做技术我的项目。可一旦连业务人员(产品经理、销售、经营)也感觉零碎性能须要降级的时候,比方在用户体验上,App文档搜寻接口提早比拟长,产品同学认为如果首屏渲染能显著提速的话,对点击率、付费率都会有大幅的晋升,然而研发这边基于老的技术栈曾经难以做优化了,那么此刻就很适宜做迁徙重构。 恰逢其时,产品同学提出一些晋升用户体验,同时适宜我的项目重构的需要,比方:减速搜寻后果页首屏渲染、新App首页,AIGC智能创作。这些大需要非常有利于迁徙重构工作的启动,它们把迁徙重构所需减少的额定人力占用降到最低。在已有的性能上做迁徙重构,更快更稳固的接口响应带来晦涩的用户体验,这有利于促成整体团队的okr指标达成。 回过头来,梳理下过后服务端基于php5.6的技术债权: 1、底层技术:语言版本老旧,个性落后,存在执行效率低,平安危险,资源节约的缺点; 2、开发质效:业务逻辑交叉耦合,大量废除的接口和下线的业务逻辑,升高了代码可读性、可维护性,继续减少我的项目迭代的难度。 △技术债权 02 启动之前的状态服务部署上,文库App的服务端部署形式是nginx+hhvm(HipHop VM 3.0.1;baidu version:1.1.6 (rel)),HHVM 是 Facebook 开发的高性能 PHP 虚拟机,是传统的nginx+php-fpm的一个性能优化版本。近年曾经失去了hhvm原创团队的继续保护迭代,它反对的语法个性和执行效率绝对落后,存在肯定平安危险。 查看启动迁徙之初的服务端实例用量,有赖于日常运维,首先确认在线服务的实例cpu、内存和磁盘使用率在正当的阈值内,排除了利用率较低导致资源节约、利用率过高会有容灾危险的状况。在应用层,咱们一共应用了数以千计的php5实例。 03 近景重构的投入与回报并非呈线性关系。 —— 《畛域驱动设计:软件外围复杂性应答之道》 直观的说,咱们心愿服务端降级能带来更少的代码,更稳固的零碎,更高的品质效率,更佳的用户体验。这体现在上面几点: 1、技术升级:采纳先进的语言框架,撑持我的项目高效迭代提供强劲底层引擎、安全性和成熟的利用生态; 2、改善设计:梳理代码逻辑,治理冗余,解决代码中的坏滋味,构建高复用、低耦合、可扩大的业务架构; 3、降本增效:一方面底座降级,晋升代码执行效率,升高平响,晋升服务可用性、可观测性;一方面在运维实际上,正当调配容器实例的cpu,内存和磁盘的配额,优化资源效力。 服务端降级的胜利与否,能够从两个方面来致力达成,别离是技术栈迁徙和改善既有代码设计的重构。 04 做技术选型咱们不打算应用较为小众、生态孤立的语言作为文库App服务端的技术栈。同时参考兄弟团队的技术栈降级方向,最终进入技术选型决赛圈的是两种厂内框架,基于php7的odp3框架(Online Develop Platform)和基于go的gdp2框架(Go Develop Platform)。 选项一:PHP7框架和Phaster PHP7框架是公司公布的在线业务开发平台,其提供了规范的webserver环境、规范php环境、AP框架、根底库、资源拜访层、通用服务等组件,对立业务的逻辑和部署构造。框架的亮点在于Phaster。Phaster能让你应用PHP语言开发高性能的Http、Fastcgi、Nshead服务,进行高性能的RPC调用,以极低的老本实现业务代码并行化。 Phaster和其它业界框架的比照如下。 Phaster能够作为http server,也能够作为fastcgi server。绝对传统nginx+cgi的形式,Phaster基于以上的能力实现数倍的性能晋升。具备以下亮点: 1、传统的hhvm或者php-fpm解决申请的逻辑是,每一个申请在解决时,都要先初始化php上下文,申请完结时清理上下文,回收各种资源。而phaster在开启上下文复用的状况下,能够节俭类加载,文件加载,初始化等过程消耗的工夫。举个例子,如果你的接口每次都要读取一个大文件配置,能够把读取操作放到初始化文件里。在100个申请内,这个读取操作只执行一次就够了; 2、hhvm或php-fpm并不间接反对http协定,往往后面会加上nginx作为http服务器,两者之间通过fastcgi通信。而Phaster能够间接作为http服务器启动,缩小一层nginx的解决转发; 3、协程的反对为IO密集型的业务场景,提供了高并发的根底。对于阻塞性的IO,能够放入协程里做,将阻塞变为非阻塞,在应用同步编程计划的同时,享受异步成果带来的IO性能晋升。 值得一提的是,Go都反对这些能力。 选项二:Go框架 Go 语言是由 Google 于 2009 年公布,近几年随同着云计算、微服务、分布式的倒退而迅速崛起,跻身支流编程语言之列,和 Java 相似,它是一门动态的、强类型的、编译型编程语言,为并发而生,所以天生实用于并发编程(网络编程)。 GDP2( Go Develop Platform ) 框架是一个对厂内基础设施反对好,可扩展性好、易配置、易组装、易测试的 Go 开发框架。具备欠缺的 RPC Client 和 RPC Server 能力,以及配套的通用根底库,能够用来开发 API、Web 及后端服务等各种利用。具备以下亮点: ...

July 6, 2023 · 1 min · jiezi

关于go:Go-团队将修改-for-循环变量的语义Go121-新版本即可体验

大家好,我是煎鱼。 之前有提到 Go for 循环变量的问题,许多面试题和泄露与此有关。 Russ Cox(下称:rsc)甚至一度示意他始终在钻研这个问题,认为以后语义的代价是很大的,想看看能不能进行变更。 通过 Go1 向前兼容性和向后兼容性提案的铺垫,循环变量的这个问题将失去解决。在 Go1.21 能够进行尝试应用,预计 Go1.22 开始正式变更。 回顾问题景象第一个例子在 Go 语言中,咱们写 for 语句时有时会呈现运行和猜测的后果不统一。例如以下第一个案例的代码: var all []*Itemfor _, item := range items { all = append(all, &item)}这段代码有问题吗?变量 all 内的 item 变量,存储进去的是什么?是每次循环的 item 值,每次都不一样,对吗? 实际上在 for 循环时,每次存入变量 all 的都是雷同的 item,也就是最初一个循环的 item 值。这是 Go 面试里经常出现的题目,联合 goroutine 更风骚,毕竟还会存在乱序执行等问题。 如果你想解决这个问题,就须要把程序改写成如下: var all []*Itemfor _, item := range items { item := item all = append(all, &item)}要从新申明一个局部变量 item 变量,把 for 循环的 item 变量给存储下来,再追加进去。 ...

July 5, 2023 · 2 min · jiezi

关于go:gookitgoutil-发布-v0610-版本-Go常用功能的扩展工具库

gookit/goutil Go 罕用性能的扩大工具库。蕴含:数字,字符串,slice/数组,Map,构造体,反射,文本,文件,谬误,工夫日期,测试,CLI,命令运行,零碎信息,格式化,罕用信息获取等等。 Github: https://github.com/gookit/goutilv0.6.10 更新记录残缺变更日志 v0.6.9...v0.6.10✨ 新性能✨ feat: testutil - 增加新的子包 fakeobj 以创立假对象用于测试✨ feat: testutil - 增加新的函数 NewEchoServer() 来启动echo服务器✨ feat: byteutil - Buffer 新增更多有用的办法,方便使用✨ feat: dump - 非凡解决自定义的 int、uint 类型值,将会打印 String 格局的阐明✨ feat: fsutil - 增加新的函数 Glob() 疾速列出匹配的文件♻️ feat: httpreq - 重构内置的 http 客户端 httpreq 逻辑 更新调整 up: structs - 更新 InitDefaults() 对不为空的构造体切片字段的初始化反对 up: maputil - SimpleMerge() 反对深度合并 map[string]any 数据其余调整✅ test: 更新一些文档并修复一些单元测试⬆️ dep: 更新 golang.org/x 依赖到最新版本局部新增性能应用dump 打印自定义类型dump 非凡解决自定义的 int、uint 类型值,将会打印 String 格局的阐明 ...

July 5, 2023 · 3 min · jiezi

关于go:两万字教你自己动手开发互联网搜索引擎

Github 地址:https://github.com/johnlui/DIY-Search-Engine 运行办法首先,给本人筹备一杯咖啡。 把本我的项目下载到本地编译:go build -o ese *.go批改配置文件:cp .env.example .env,而后把外面的数据库和 Redis 配置改成你的执行./ese art init创立数据库手动插入一个实在的 URL 到 pages_00 表中,只须要填充 url 和 host 两个字段执行./ese,静待坏事产生 ☕️过一段时间,等字典数据表word_dics外面填充了数据之后,关上http://127.0.0.1:10086,尝试搜一下吧! 网页间接浏览:https://lvwenhan.com/tech-epic/509.html作者信息:姓名:吕文翰GitHub:johnlui职位:住范儿 CTO代码版权本我的项目代码采纳 MIT 协定开源。

July 5, 2023 · 1 min · jiezi

关于go:提升性能的利器深入解析SectionReader

一. 简介本文将介绍 Go 语言中的 SectionReader,包含 SectionReader的根本应用办法、实现原理、应用注意事项。从而可能在适合的场景下,更好得应用SectionReader类型,晋升程序的性能。 二. 问题引入这里咱们须要实现一个根本的HTTP文件服务器性能,能够解决客户端的HTTP申请来读取指定文件,并依据申请的Range头部字段返回文件的局部数据或整个文件数据。 这里一个简略的思路,能够先把整个文件的数据加载到内存中,而后再依据申请指定的范畴,截取对应的数据返回回去即可。上面提供一个代码示例: func serveFile(w http.ResponseWriter, r *http.Request, filePath string) { // 关上文件 file, _ := os.Open(filePath) defer file.Close() // 读取整个文件数据 fileData, err := ioutil.ReadAll(file) if err != nil { // 错误处理 http.Error(w, err.Error(), http.StatusInternalServerError) return } // 依据Range头部字段解析申请的范畴 rangeHeader := r.Header.Get("Range") ranges, err := parseRangeHeader(rangeHeader) if err != nil { // 错误处理 http.Error(w, err.Error(), http.StatusBadRequest) return } // 解决每个范畴并返回数据 for _, rng := range ranges { start := rng.Start end := rng.End // 从文件数据中提取范畴的字节数据 rangeData := fileData[start : end+1] // 将范畴数据写入响应 w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, fileInfo.Size())) w.Header().Set("Content-Length", strconv.Itoa(len(rangeData))) w.WriteHeader(http.StatusPartialContent) w.Write(rangeData) }}type Range struct { Start int End int}// 解析HTTP Range申请头func parseRangeHeader(rangeHeader string) ([]Range, error){}上述的代码实现比较简单,首先,函数关上filePath指定的文件,应用ioutil.ReadAll函数读取整个文件的数据到fileData中。接下来,从HTTP申请头中Range头部字段中获取范畴信息,获取每个范畴申请的起始和终止地位。接着,函数遍历每一个范畴信息,提取文件数据fileData 中对应范畴的字节数据到rangeData中,而后将数据返回回去。基于此,简略实现了一个反对范畴申请的HTTP文件服务器。 ...

July 2, 2023 · 4 min · jiezi

关于go:CentOS-9-x64-使用-NginxSupervisor-部署-GoGolang-服务

前言在 CentOS 9 x64 零碎上,能够通过以下步骤来部署 Golang 服务。 1. 装置必要的软件包装置以下软件包: Golang:Golang 编程语言Nginx:Web 服务器Supervisor:过程管理工具Git:版本控制工具EPEL:扩大软件包能够通过以下命令来装置: yum -y updateyum install nginx golang epel-release supervisor git -y2. 生成 SSH 密钥[可选]为 Git 生成 SSH 密钥,以便于进行代码治理。能够通过以下命令来生成: cd ~ssh-keygen -t rsa -C "your_email@example.com"cat ~/.ssh/id_rsa.pub将公钥增加到 Git 仓库中。 3. 下载代码将代码下载到服务器上,能够应用 Git 命令来下载代码: cd /mkdir webcd web# or `git clone https://...`git clone git@github.com:your_name/your_repo.gitcd /web/your_repo4. 运行利用在利用根目录下运行以下命令来初始化利用: go run scripts/init/main.go5. 编译利用应用以下命令来编译利用: GOOS=linux GOARCH=amd64 go build -o dist/app-linux-amd64 cmd/app/main.go6. 配置 Supervisor在 /etc/supervisord.d 目录下创立一个新的配置文件 app.ini,并增加以下内容: ...

July 2, 2023 · 1 min · jiezi

关于go:Go-语言-context-都能做什么

原文链接: Go 语言 context 都能做什么? 很多 Go 我的项目的源码,在读的过程中会发现一个很常见的参数 ctx,而且根本都是作为函数的第一个参数。 为什么要这么写呢?这个参数到底有什么用呢?带着这样的疑难,我钻研了这个参数背地的故事。 开局一张图: 外围是 Context 接口: // A Context carries a deadline, cancelation 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{}}蕴含四个办法: ...

July 2, 2023 · 3 min · jiezi

关于go:Golang-一个支持错误堆栈-错误码-错误链的工具库

地址: https://github.com/morrisxyang/errors 如果感觉有用欢送 Star 和 PR, 有问题请间接提issue errors 简略的反对谬误堆栈, 错误码, 谬误链的工具库: 反对携带堆栈, 嵌套结构谬误链反对携带错误码, 不便接口返回反对自定义堆栈打印深度和谬误链打印格局应用 CallersFrames 代替 FuncForPC 生成堆栈, 防止非凡状况line number谬误等问题, 详见runtime: strongly encourage using CallersFrames over FuncForPC with Callers result简化堆栈信息, 一条链路屡次Wrap操作只保留最深层堆栈, 只打印一次装置和文档装置应用 go get github.com/morrisxyang/errors 文档地址是 https://pkg.go.dev/github.com/morrisxyang/errors 疾速开始结构谬误链 func a() error { err := b() err = Wrap(err, "a failed reason") return err}func b() error { err := c() err = Wrap(err, "b failed reason") return err}func c() error { _, err := os.Open("test") if err != nil { return WrapWithCode(err, 123, "c failed reason") } return nil}打印错误信息, %+v会打印堆栈, %v只打印错误信息 ...

June 29, 2023 · 1 min · jiezi

关于go:个人存档笔试编程题处理panic并打印信息

在本题中,有一个proc函数,它会调用panic(“OK”)来触发panic,咱们心愿你实现一个名为solution的函数它承受一个seconds参数,示意秒数。在solution中,每秒调用一次proc函数,继续seconds秒。在调用proc函数的同时,须要捕捉由proc函数触发的panic,打印出panic的信息。在seconds秒后,solution函数应失常完结 实现办法: package mainimport ( "fmt" "time")func proc() { panic("OK")}func solution(seconds int) { for i := 0; i < seconds; i++ { func() { defer func() { if r := recover(); r != nil { fmt.Println("Panic:", r) } }() proc() }() time.Sleep(time.Second) }}

June 28, 2023 · 1 min · jiezi

关于go:一文了解Go语言的IO接口设计

1. 引言I/O 操作在编程中扮演着至关重要的角色。它波及程序与内部世界之间的数据交换,容许程序从内部,如键盘、文件、网络等中央读取数据,也可能将外界输出的数据从新写入到指标地位中。使得程序可能与外部环境进行数据交换、与用户进行交互、实现数据长久化和文件操作、进行网络通信等。因而,理解和把握I/O操作是编程中不可或缺的一部分,上面咱们来理解一下Go语言中的 I/O 接口设计。 2. I/O 接口设计在Go语言中,I/O接口的设计基于接口形象和多态的思维,通过定义一组对立的接口和办法来解决不同类型的I/O操作。上面认真介绍Go语言中几个外围的I/O接口。 2.1 io.Reader接口io.Reader接口是Go语言中用于读取数据的根本接口,定义了读取操作的办法。具体定义如下: type Reader interface { Read(p []byte) (n int, err error)}其只定义了一个Read办法,其中参数p是一个字节切片,用于接管读取的数据。返回值n示意理论读取的字节数,err示意可能呈现的谬误。 Read办法定义的工作流程如下,首先,当调用Read办法时,它会尝试从数据源中读取数据,并将读取的数据存储到参数p指定的字节切片中。而后Read办法会返回理论读取的字节数和可能的谬误。如果读取过程中没有产生谬误,err的值为nil。如果没有更多数据可读取,Read办法会返回io.EOF谬误。 Go语言通过 io.Reader接口,对立了从不同的数据源(如文件、网络连接等)中读取数据的形式,这种统一的接口设计使得咱们可能以对立的形式解决各种类型的数据读取操作。 2.2 io.Writer接口io.Writer接口是Go语言中用于写入数据的根本接口,定义了写入操作的办法。具体定义如下: type Writer interface { Write(p []byte) (n int, err error)}其跟io.Reader接口相似,只定义了一个Write办法,其中参数p是一个字节切片,将字节切片p中的数据写入到实现了io.Writer接口的对象中,并返回写入的字节数和可能的谬误。 Write办法定义的工作流程如下,首先,当调用Write办法时,它会尝试将参数p中的数据写入到io.Writer对象中。Write办法返回理论写入的字节数和可能的谬误。如果写入过程中没有产生谬误,err的值为nil,否则返回对应的谬误。 Go语言通过io.Writer接口,对立了数据写入的形式,可能以一种对立的形式,将数据写入到不同指标(如文件、网络连接等)当中。 2.3 io.Closer接口io.Closer接口是Go语言中用于敞开资源的接口,它定义了敞开操作的办法。具体定义如下: type Closer interface { Close() error}这里Closer接口同样也只定义一个办法,为Close办法,Close办法没有任何参数,返回值error示意可能产生的敞开操作的谬误。 该接口定义的工作流程如下,当调用Close办法时,它会执行敞开资源的操作,例如敞开文件、敞开网络连接等。如果敞开过程中没有产生谬误,返回值为nil,如果报错了,则返回对应的谬误。 通过应用io.Closer接口,咱们能够不便地敞开各种资源,如文件、网络连接等。这种统一的接口设计使得咱们可能以对立的形式解决敞开操作。 3. I/O 接口设计的长处3.1 对立的形象层下面定义了三个根本的 I/O 接口,其中io.Reader定义了读取数据的规范,io.Writer定义了写入数据的规范,io.Closer定义了敞开资源的规范。 通过这几个的接口,能够将各种不同的I/O设施(如文件、网络连接、缓冲区等)视为雷同的实体。这种对立的形象层使得开发人员能够以一种通用的形式来解决不同类型的I/O操作,而无需关注具体的底层实现细节。这简化了代码的编写和保护,进步了可读性和可维护性。上面咱们通过一个代码例子来阐明: package mainimport ( "fmt" "io" "os" "strings")func main() { df, _ := os.Create("destination.txt") defer df.Close() sr := strings.NewReader("Hello, World!") err := copyData(sr, df) if err != nil { fmt.Println("Failed to copy data to file:", err) return } fmt.Println("Data copied to file successfully!")}func copyData(src io.Reader, dst io.Writer) error { _, err := io.Copy(dst, src) if err != nil { return err } return nil}这里copyData办法,通过 I/O 接口定义进去的对立的形象层,咱们能够将不同类型的数据源(内存和文件)视为雷同的实体,并应用雷同的形式来实现数据的复制操作。 ...

June 28, 2023 · 1 min · jiezi

关于go:精选Golang高频面试题和答案汇总

大家好,我是阳哥。 之前写的《 GO必知必会面试题汇总》,曾经浏览破万,珍藏230+。 也欢送大家珍藏、转发本文。 这篇文章给大家整顿了17道Go语言高频面试题和答案详解,每道题都给出了代码示例,不便大家更好的了解。 1.并发安全性Go语言中的并发安全性是什么?如何确保并发安全性?解答:并发安全性是指在并发编程中,多个goroutine对共享资源的拜访不会导致数据竞争和不确定的后果。 为了确保并发安全性,能够采取以下措施: 应用互斥锁(Mutex):通过应用互斥锁来爱护共享资源的拜访,一次只容许一个goroutine访问共享资源,从而防止竞争条件。应用原子操作(Atomic Operations):对于简略的读写操作,能够应用原子操作来保障操作的原子性,防止竞争条件。应用通道(Channel):通过应用通道来进行goroutine之间的通信和同步,防止共享资源的间接拜访。应用同步机制:应用同步机制如期待组(WaitGroup)、条件变量(Cond)等来协调多个goroutine的执行程序和状态。通过以上措施,能够确保并发程序的安全性,防止数据竞争和不确定的后果。 2.deferGo语言中的defer关键字有什么作用?请给出一个应用defer的示例。解答:defer关键字用于提早函数的执行,即在函数退出前执行某个操作。defer通常用于开释资源、敞开文件、解锁互斥锁等清理操作,以确保在函数执行结束后进行解决。 也能够应用defer语句联合time包实现函数执行工夫的统计。 代码示例:上面是一个应用defer的示例,关上文件并在函数退出前敞开文件: package mainimport ( "fmt" "os")func main() { file, err := os.Open("file.txt") if err != nil { fmt.Println("Error opening file:", err) return } defer func() { err := file.Close() if err != nil { fmt.Println("Error closing file:", err) } }() // 应用文件进行操作 // ... fmt.Println("File operations completed")}在上述代码中,咱们应用defer关键字提早了文件的敞开操作,确保在函数执行结束后敞开文件。这样能够防止遗记敞开文件而导致资源透露。 3.指针面试题:Go语言中的指针有什么作用?请给出一个应用指针的示例。解答:指针是一种变量,存储了另一个变量的内存地址。通过指针,咱们能够间接拜访和批改变量的值,而不是对变量进行拷贝。 指针在传递大型数据结构和在函数间共享数据时十分有用。 代码示例上面是一个应用指针的示例,替换两个变量的值: package mainimport "fmt"func swap(a, b *int) { temp := *a *a = *b *b = temp}func main() { x := 10 y := 20 fmt.Println("Before swap:", x, y) swap(&x, &y) fmt.Println("After swap:", x, y)}在上述代码中,咱们定义了一个swap函数,接管两个指针作为参数,并通过指针替换了两个变量的值。在主函数中,咱们通过取地址操作符&获取变量的指针,并将指针传递给swap函数。通过应用指针,咱们实现了变量值的替换。 ...

June 27, 2023 · 4 min · jiezi

关于go:保护数据隐私深入探索Golang中的SM4加密解密算法

前言最近做的我的项目对安全性要求比拟高,特别强调:零碎不能波及MD5、SHA1、RSA1024、DES高风险算法。 那用什么嘞?甲方:倡议用国产明码算法SM4。 善于麻利开发(CV大法)的我,先去GitHub找了开源我的项目、又去网络上找了一些教程,然而或多或少都有些问题: 比方golang.org/x/crypto/sm4无奈装置编译比方C站烂大巷的SM4教程,不能解决数据填充的问题,超过16位就解密失败了比方如何封装成通用的办法,供零碎进行调用更多就是复制粘贴了SM4的定义,很形象。于是我花了2天工夫钻研SM4的原理和利用,解决了下面这些问题,整顿这篇文章分享给大家,让大家能少踩坑。 我会依照上面的程序分享这篇文章,不便大家更好的了解,如果你就是喜爱拿来主义(麻利开发),能够间接copy底部的示例代码,疾速上手应用即可。 文章目录SM4的劣势IV是什么?SM4加密的形式和原理SM4的各种工作模式比照间接可用的「代码示例」外围办法的源码解析总结回顾1. SM4的劣势相比于其余加密算法,SM4加密算法具备以下几个劣势: 高安全性:SM4是一种对称加密算法,采纳128位密钥长度,具备较高的安全性和抗攻击性。它通过了宽泛的安全性剖析和评估,并通过了多个密码学规范的验证。高效性:SM4算法的加密和解密速度较快,实用于对大量数据进行加密和解密的场景。它在硬件和软件实现上都具备高效性能。简略性:SM4算法的实现绝对简略,代码量较小,易于了解和应用。它的设计指标之一是提供一种易于实现和部署的加密算法。标准化:SM4算法是中国国家明码管理局公布的明码算法规范,失去了宽泛的利用和认可。它已成为国内上公认的明码算法之一。广泛支持:SM4算法在各种平台和编程语言中都有反对和实现,包含Go、Java、C/C++等。它能够在不同的零碎和环境中进行跨平台的利用和部署。可扩展性:SM4算法反对不同的工作模式和填充形式,能够依据具体需要进行灵便配置。它能够与其余明码算法联合应用,提供更高级别的平安爱护。小小的总结一下:SM4加密算法在安全性、高效性、简略性、标准化和广泛支持等方面具备劣势,实用于各种数据保护和加密利用场景。它是一种牢靠的加密算法抉择。 2.IV是什么?我在学习的时候看到IV就蒙了,所以有必要先说分明IV的概念: Initialization Vector(IV)是一种在密码学中应用的初始值。它是一个固定长度的随机数或者随机生成的值,用于在加密算法中初始化明码算法的状态。 在加密过程中,IV的作用是引入随机性和唯一性,以减少加密的安全性。 它与密钥一起用于初始化明码算法的外部状态,确保每次加密操作都产生不同的输入,即便雷同的明文应用雷同的密钥进行加密。 IV的长度和应用形式取决于具体的加密算法和利用场景。在应用加密算法时,IV通常须要与密文一起传输给解密方,以便解密方可能正确还原明文。 须要留神的是:IV自身不须要窃密,能够与密文一起传输。然而,为了确保加密的安全性,IV应该是随机生成的,并且每次加密操作都应该应用不同的IV。这样能够避免明码剖析者通过观察加密后果的模式来破解密钥或者明文。 3. SM4加密的形式和原理SM4加密算法是一种对称加密算法,采纳分组明码的形式对数据进行加密。 上面是SM4加密的形式和原理的简要阐明: 密钥扩大:SM4应用128位的密钥,首先对密钥进行扩大,生成32个子密钥,用于后续的加密轮操作。初始轮:将明文分为4个字节的分组,与第一个子密钥进行异或操作。加密轮:SM4加密算法共进行32轮加密操作。每轮操作包含以下步骤: 字节替换:应用S盒进行字节替换。行移位:对每个分组进行行移位操作。列混同:对每个分组进行列混同操作。轮密钥加:将以后轮的子密钥与分组进行异或操作。最终轮:在最初一轮加密操作中,不进行列混同操作,只进行字节替换、行移位和轮密钥加操作。输入:通过32轮加密操作后,失去加密后的密文。SM4加密算法的安全性和强度次要来自于其简单的轮函数和密钥扩大过程。它具备较高的安全性和抗攻击性,并且在理论利用中失去了宽泛的利用和认可。 须要留神的是:SM4加密算法的安全性还依赖于密钥的保密性和随机性。在应用SM4进行加密时,应确保应用足够强度的密钥,并采取适当的密钥治理和保护措施。 4.SM4的各种工作模式比照SM4加密算法能够应用不同的工作模式,其中包含CBC(Cipher Block Chaining)模式。 我应用的是CBC模式,上面和大家分享一下CBC模式与其余模式的比照: CBC模式(Cipher Block Chaining): 特点:每个明文块与前一个密文块进行异或操作,而后再进行加密。初始块应用初始化向量(IV)。长处:具备较好的安全性,可能暗藏明文的模式和重复性。毛病:加密过程是串行的,不适宜并行处理。ECB模式(Electronic Codebook): 特点:将每个明文块独立加密,雷同的明文块会失去雷同的密文块。长处:简略、并行处理效率高。毛病:不能暗藏明文的模式和重复性,不适宜加密大量反复的数据。CFB模式(Cipher Feedback): 特点:将前一个密文块作为输出来加密以后的明文块,能够实现流明码的性能。长处:可能解决不定长的数据流,实用于实时加密和流式传输。毛病:加密过程是串行的,不适宜并行处理。OFB模式(Output Feedback): 特点:将前一个密文块作为输出来生成密钥流,而后与明文块进行异或操作,能够实现流明码的性能。长处:可能解决不定长的数据流,实用于实时加密和流式传输。毛病:加密过程是串行的,不适宜并行处理。CTR模式(Counter): 特点:应用一个计数器来生成密钥流,而后与明文块进行异或操作,能够实现流明码的性能。长处:可能解决不定长的数据流,实用于实时加密和流式传输。并行处理效率高,适宜硬件实现。毛病:须要保障计数器的唯一性,否则会导致密钥流的反复。比照总结:CBC模式和ECB模式相比,CBC模式具备更好的安全性,可能暗藏明文的模式和重复性,而ECB模式无奈暗藏这些信息。CFB模式、OFB模式和CTR模式都是流明码模式,实用于不定长的数据流加密,可能实现实时加密和流式传输。它们的次要区别在于密钥流的生成形式和加密过程的并行性。CFB模式和OFB模式的加密过程是串行的,不适宜并行处理,而CTR模式的加密过程能够并行处理,适宜硬件实现。总的来说:CBC模式在安全性方面较好,可能暗藏明文的模式和重复性。而流明码模式(CFB、OFB和CTR)实用于不定长数据流的加密,可能实现实时加密和流式传输,其中CTR模式具备较好的并行处理性能。抉择适合的加密模式取决于具体的利用需要和安全性要求。 5. 间接可用的「代码示例」我始终认为能够通过复制粘贴,间接跑通的示例代码才是好代码。 没错,我的代码示例就是这样,并且要害代码都写好了正文: package mainimport ( "bytes" "crypto/cipher" "encoding/hex" "fmt" "github.com/tjfoc/gmsm/sm4")// SM4加密func SM4Encrypt(data string) (result string, err error) { //字符串转byte切片 plainText := []byte(data) //倡议从配置文件中读取秘钥,进行对立治理 SM4Key := "Uv6tkf2M3xYSRuFv" //todo 留神:iv须要是随机的,进一步保障加密的安全性,将iv的值和加密后的数据一起返回给内部 SM4Iv := "04TzMuvkHm_EZnHm" iv := []byte(SM4Iv) key := []byte(SM4Key) //实例化sm4加密对象 block, err := sm4.NewCipher(key) if err != nil { panic(err) } //明文数据填充 paddingData := paddingLastGroup(plainText, block.BlockSize()) //申明SM4的加密工作模式 blockMode := cipher.NewCBCEncrypter(block, iv) //为填充后的数据进行加密解决 cipherText := make([]byte, len(paddingData)) //应用CryptBlocks这个外围办法,将paddingData进行加密解决,将加密解决后的值赋值到cipherText中 blockMode.CryptBlocks(cipherText, paddingData) //加密后果应用hex转成字符串,不便内部调用 cipherString := hex.EncodeToString(cipherText) return cipherString, nil}// SM4解密 传入string 输入stringfunc SM4Decrypt(data string) (res string, err error) { //秘钥 SM4Key := "Uv6tkf2M3xYSRuFv" //iv是Initialization Vector,初始向量, SM4Iv := "04TzMuvkHm_EZnHm" iv := []byte(SM4Iv) key := []byte(SM4Key) block, err := sm4.NewCipher(key) if err != nil { panic(err) } //应用hex解码 decodeString, err := hex.DecodeString(data) if err != nil { return "", err } //CBC模式 长处:具备较好的安全性,可能暗藏明文的模式和重复性。 毛病:加密过程是串行的,不适宜并行处理。 blockMode := cipher.NewCBCDecrypter(block, iv) //下文有详解这段代码的含意 blockMode.CryptBlocks(decodeString, decodeString) //去掉明文前面的填充数据 plainText := unPaddingLastGroup(decodeString) //间接返回字符串类型,不便内部调用 return string(plainText), nil}// 明文数据填充func paddingLastGroup(plainText []byte, blockSize int) []byte { //1.计算最初一个分组中明文后须要填充的字节数 padNum := blockSize - len(plainText)%blockSize //2.将字节数转换为byte类型 char := []byte{byte(padNum)} //3.创立切片并初始化 newPlain := bytes.Repeat(char, padNum) //4.将填充数据追加到原始数据后 newText := append(plainText, newPlain...) return newText}// 去掉明文前面的填充数据func unPaddingLastGroup(plainText []byte) []byte { //1.拿到切片中的最初一个字节 length := len(plainText) lastChar := plainText[length-1] //2.将最初一个数据转换为整数 number := int(lastChar) return plainText[:length-number]}func main() { //待加密的数据 模仿18位的身份证号 plainText := "131229199907097219" //SM4加密 decrypt, err := SM4Encrypt(plainText) if err != nil { return } fmt.Printf("sm4加密后果:%s\n", decrypt) //cipherString := hex.EncodeToString(cipherText) //fmt.Printf("sm4加密后果转成字符串:%s\n", cipherString) //SM4解密 sm4Decrypt, err := SM4Decrypt(decrypt) if err != nil { return } fmt.Printf("plainText:%s\n", sm4Decrypt) flag := plainText == sm4Decrypt fmt.Println("解密是否胜利:", flag)}运行后果如下: ...

June 25, 2023 · 2 min · jiezi

关于go:你的Go应用真的用了正确的-CPU-核数吗

Go 的调度模型是 GMP,其中 G 是 goroutine,M 是线程,P 是可用的 CPU 核数。多个 G 会共用一个 M。M 作为操作系统层面上的调度单位,在执行时须要绑定到 P。如果操作系统认为的某个 Go 过程可用的 CPU 数,和该过程认为的可用的 CPU 数不统一,那么即便把 M 绑定到某个 P 上,操作系统也不肯定会执行这个线程。所以是否获取精确的可用 CPU 核数会影响 Go 的调度效率。 当用户在 k8s 中设置了资源限度: spec: containers: - name: app_written_by_go resources: limits: cpu: "4"Go 会不会辨认到可用的 CPU 为 4 呢?读者可能会认为,Go 作为一个云原生时代煊赫一时的语言,应该内置了对 k8s 的反对,所以可能辨认进去。但事实并非如此。咱们能够做个小试验:如果启动 Go 过程时没有指定 GOMAXPROCS 环境变量,那么会以 runtime.NumCPU() 的输入作为可用的 CPU 核数(也即 P 的值)。让咱们加一下打印 NumCPU 的代码,会发现实际上输入的是 Node 上的 CPU 数目,跟 limits.cpu 无关。 runtime.NumCPU() 在 Linux 上是通过零碎调用 sched_getaffinity 实现的,但这个零碎调用只思考绑定 CPU 核的状况,不波及容器环境下 cgroup 对 CPU 资源的限度。以 docker 为例,docker run 时指定 --cpuset-cpus 能够设置容器运行时能够应用的 CPU 核的编号,但限度 CPU 的资源数次要用 --cpus=。只有前者(cpuset)是能被 sched_getaffinity 辨认的。具体见 https://docs.docker.com/config/containers/resource_constraint...。如果要想计算后者,那么须要读取机器上的 cgroup fs 文件。 ...

June 24, 2023 · 1 min · jiezi

关于go:Go开发者的涨薪通道

Go开发者的涨薪通道Go是一门疾速、高效和易于学习的编程语言,正变得越来越受欢迎。对于熟练掌握Go的开发者来说,他们有着广大的涨薪通道。本文将具体介绍为什么Go开发者有着良好的薪资前景以及如何晋升本人的涨薪机会。 Go的市场需求Go语言在软件开发畛域中的利用正在持续增长。其简洁、高效和并发个性使其成为构建云原生利用和分布式系统的现实抉择。随着云计算、大数据和容器化技术的遍及,对于可能高效解决并发工作的Go开发者的需要也在一直减少。 Go语言还领有一个弱小的开源社区,其中蕴含许多优良的库和框架。这些开源我的项目为Go开发者提供了更多的机会来展现他们的技术能力,并通过参加和奉献社区我的项目来晋升本人的名誉和知名度。 晋升涨薪机会的办法以下是一些能够帮忙Go开发者晋升涨薪机会的办法: 深刻学习Go语言:不断深入学习和把握Go语言的外围概念、规范库和最佳实际。熟练掌握并发编程、网络编程和性能优化等要害畛域,使本人成为Go专家。把握罕用的Go框架和工具:相熟并把握罕用的Go框架和工具,如Gin、Echo、Beego、gorm等。这些框架和工具能够帮忙开发者更高效地构建Web应用程序和RESTful API。参加开源我的项目和社区奉献:积极参与到Go开源我的项目中,奉献本人的代码和解决方案。这样能够扩大本人的技术影响力和知名度,晋升本人在Go社区中的位置。继续学习和自我晋升:继续学习新的技术趋势和行业动态。加入培训、会议和研讨会,关注最新的Go相干文章和书籍。放弃对新技术的敏感度,随时调整本人的技术栈。解决业务问题和技术挑战:通过解决理论的业务问题和技术挑战,展现本人的技术能力和解决问题的能力。通过提供高质量的解决方案来博得雇主和项目组的信赖。建设良好的职业网络:踊跃建设和保护与其余Go开发者和技术专家的分割。加入技术社区活动、参加探讨和交换,并寻找适合的导师或导师个人来领导本人的职业倒退。总结对于把握Go编程语言并具备丰盛教训的开发者来说,他们有着广大的涨薪通道。Go语言的市场需求一直增长,而优良的Go开发者在云原生利用、分布式系统和并发编程等

June 24, 2023 · 1 min · jiezi

关于go:在-K8S-中部署一个应用-下

接着上一篇持续部署利用到 K8S中 之前简略部署的简略集群,三个工作节点是运行在 docker 和 kubelet 的,还有一个是管制节点 ReplicationController , pod 和 service 本次关系之前有提到 ReplicationController , pod 和 服务是如何组合在一起的呢? 能够通过这张如图来解释一下 咱们之前创立 pod 的时候不是间接创立的,是通过 docker run 来创立的一个 replicationController ,而后基于 rc 来创立的一个 pod 实例 为了让 pod 可能被内部拜访到,所以咱们须要让 K8S 将 replicationController 治理的所有 pod 由一个服务对外裸露,因而有了 kubia-http 服务是有对外裸露 IP 的,申请打到 service 上service 将申请转到 pod 下面的 9999 端口上,而后 pod 提供服务ReplicationController 角色是啥样的通过下面的案例,咱们应该晓得 ReplicationController实际上是用于复制 pod 的,通过 ReplicationController 来创立多个 pod 正本 ReplicationController 始终确保存在一个运行中的 pod 实例 如果咱们下面创立的 pod 隐没了,那么 ReplicationController 将会创立一个新的 pod 来替换隐没的 pod ...

June 23, 2023 · 1 min · jiezi

关于go:一文了解函数设计的最佳实践

1. 引言良好设计的函数具备清晰的职责和逻辑构造,提供精确的命名和适当的参数管制。它们促成代码复用、反对团队合作,升高保护老本,并提供可测试的代码根底。通过遵循最佳实际,咱们可能编写出高质量、可读性强的代码,从而进步开发效率和软件品质。上面咱们将一一形容函数设计时可能遵循的最佳实际。 2. 遵循繁多职责准则遵循繁多职责准则是函数设计的重要准则之一。它要求一个函数只负责实现繁多的工作或性能,而不应该承当过多的责任。 通过遵循该准则,咱们设计进去的函数将具备以下几个长处: 代码可读性的进步:函数只关注繁多的工作或性能,使其逻辑更加清晰和简洁。这样的函数更易于浏览和了解,可能更疾速地了解其作用和目标,进步代码的可读性。函数复杂度的升高:繁多职责的函数具备较小的代码量和较少的依赖关系。这使得函数的逻辑更加集中和可控,缩小了函数的复杂性。在保护和批改代码时,因为函数的性能繁多,咱们能够更容易地定位和修复问题,升高了保护老本。代码可测试性的进步:遵循繁多职责准则的函数更容易进行单元测试。因为函数的性能繁多,咱们能够更准确地定义输出和冀望输入,编写针对性的测试用例。这有助于进步代码的可测试性,确保函数的正确性和稳定性。绝对的,如果函数设计时没有遵循繁多职责准则,此时将带来函数复杂性的减少,从而导致代码可读性的升高以及代码可测试性的降落。 上面是一个没有遵循繁多职责准则的函数与一个遵循该准则的函数的比照。首先是一个未遵循该准则的代码示例: func processData(data []int) { // 1. 验证数据 // 2. 清理数据 // 3. 剖析数据 // 4. 保留数据 // 5. 记录日志}在上述示例中,processData 函数负责整个数据处理流程,包含验证数据、清理数据、剖析数据、保留数据和记录日志。这个函数承当了太多的职责,导致代码逻辑简单,可读性不高,同时如果某一个节点须要变更,此时须要思考是否对其余局部是否有影响,代码的可维护性进一步升高。 上面咱们将processData函数进行革新,使其遵循繁多职责准则,从而凸显出遵循繁多职责准则的益处,代码示例如下: func processData(data []int) { // 1. 验证逻辑拆分到calidateData函数中 validateData(data) // 2. 清理数据 拆分到cleanData函数中 cleanedData := cleanData(data) // 3. 剖析数据 拆分到 analyzeData 函数中 result := analyzeData(cleanedData) //4. 保留数据 拆分到 saveData 函数中 saveData(result) //5. 记录日志 拆分到 logData 函数中 logData(result)}func validateData(data []int) { // 验证数据的逻辑 // ...}func cleanData(data []int) []int { // 清理数据的逻辑 // ...}func analyzeData(data []int) string { // 剖析数据的逻辑 // ...}func saveData(result string) { // 保留数据的逻辑 // ...}func logData(result string) { // 记录日志的逻辑 // ...}革新后的processData函数中,咱们将不同的工作拆分到不同的函数中,每个函数只负责其中一部分性能。因为每个函数只须要专一于其中一项工作,代码的可读性更好,而且每个函数只负责其中一部分性能,故代码的复杂性也明显降低了,而且代码也更容易测试了。 ...

June 23, 2023 · 2 min · jiezi

关于go:一文了解Go语言的匿名函数

1. 引言无论是在Go语言还是其余编程语言中,匿名函数都扮演着重要的角色。在本文中,咱们将具体介绍Go语言中匿名函数的概念和应用办法,同时也提供一些思考因素,从而帮忙在匿名函数和命名函数间做出抉择。 2. 根本定义匿名函数是一种没有函数名的函数。它是在代码中间接定义的函数,没有被调配一个显式的标识符或名称。匿名函数通常用于须要长期定义、简短应用或在其余函数外部应用的状况。 Go语言对匿名函数是反对的,其定义形式非常简单, func 关键字前面省略函数名,并间接编写函数体即可,上面是一个简略代码的示例: func main() { // 在这个例子中,咱们在main函数外部定义了一个匿名函数,并将其赋值给了变量greet greet := func() { fmt.Println("Hello, World!") } // 调用匿名函数 greet()}在这个示例中,咱们在main函数外部定义了一个匿名函数,并将其赋值给了变量greet。匿名函数体内的代码打印了"Hello, World!"。通过调用greet(),咱们能够执行匿名函数。 3. 匿名函数有什么长处这里咱们通过一个场景来进行阐明。假如咱们须要对一个字符串切片进行排序,并依照字符串长度的降序排列。首先,咱们不通过匿名函数来实现,代码示例如下: package mainimport ( "fmt" "sort")func sortByLength(strings []string) { sort.Slice(strings, func(i, j int) bool { return len(strings[i]) > len(strings[j]) })}func main() { strings := []string{"apple", "banana", "cherry", "date"} sortByLength(strings) fmt.Println(strings)}在上述代码中,咱们定义了一个名为 sortByLength 的函数,它承受一个字符串切片并对其进行排序。为了实现按字符串长度降序排列,咱们定义了一个匿名函数作为 sort.Slice 函数的参数。 然而,咱们能够通过应用匿名函数间接实现排序的逻辑,防止定义额定的函数。以下是应用匿名函数的改良版本: package mainimport ( "fmt" "sort")func main() { strings := []string{"apple", "banana", "cherry", "date"} sort.Slice(strings, func(i, j int) bool { return len(strings[i]) > len(strings[j]) }) fmt.Println(strings)}在这个改良的代码中,咱们将排序逻辑间接嵌入到 main 函数中,并应用匿名函数作为 sort.Slice 函数的参数。通过这种形式,咱们防止了定义额定的函数,并将代码的逻辑更严密地组织在一起。 ...

June 22, 2023 · 1 min · jiezi

关于go:一文了解Go语言的函数

1. 引言函数是编程中不可或缺的组成部分,无论是在Go语言还是其余编程语言中,函数都扮演着重要的角色。函数可能将一系列的操作封装在一起,使得代码更加模块化、可重用和易于保护。 在本文中,咱们将具体介绍Go语言中函数的概念和应用办法,包含函数的定义、参数和返回值、调用形式、可变参数、函数作为参数和返回值等方面的内容。 2. 函数的根本定义在Go语言中,定义函数须要依照以下语法: func functionName(parameter1 type1, parameter2 type2) returnType { // 函数体 // 能够蕴含一系列的语句和操作 return value // 返回值(如果有)}其中,各个局部的含意如下: func: 关键字用于定义函数。functionName: 函数名,用于惟一标识该函数。parameter1, parameter2: 参数列表,函数能够接管零个或多个参数。每个参数由参数名和参数类型组成,多个参数之间应用逗号分隔。type1, type2: 参数的类型,指定参数的数据类型。returnType: 返回类型,指定函数的返回值的数据类型。如果函数没有返回值,则返回类型为空。return value: 可选项,用于返回函数的后果。如果函数定义了返回类型,则须要应用return语句将后果返回给调用者。上面是一个示例函数的定义: func add(a int, b int) int { sum := a + b return sum}上述示例中,函数名为add,接管两个参数a和b,类型为int,并且返回类型也为int。函数体外部计算参数的和,并将后果应用return语句返回。 3. 函数参数类型3.1 值参数在Go语言中,函数参数能够是值参数或援用参数。这两种参数类型决定了函数在调用时如何传递参数值以及对参数值的批改是否会影响原始数据。 对于值参数而言,其是通过传递参数值的正本给函数,从而实现数据传递的。因而,函数外部对值参数的批改不会影响到原始数据。值参数通常实用于不须要批改原始数据的状况,或者数据量较小的状况。上面是一个应用值参数的示例: func double(n int) { n = n * 2 fmt.Println("Inside double function:", n)}func main() { num := 5 double(num) fmt.Println("After function call:", num)}输入后果为: ...

June 22, 2023 · 2 min · jiezi

关于go:Go应用性能优化的8个最佳实践快速提升资源利用效率

作者|Ifedayo Adesiyan翻译|Seal软件链接|https://earthly.dev/blog/optimize-golang-for-kubernetes/ 优化服务器负载对于确保运行在 Kubernetes 上的 Golang 应用程序的高性能和可扩展性至关重要。随着企业越来越多地采纳容器化的形式和 Kubernetes 来部署和管理应用程序,采取缩小服务器负载的最佳实际势在必行,进而达到最佳的资源利用效率、老本效益并改善用户体验。  运行 Golang 应用程序的多个容器能够放在一个 Kubernetes 集群内,并部署在多个节点上。每个容器能够应用 CPU、内存以及存储等系统资源。如果这些资源没有被高效地治理起来,可能会导致服务器负载一直减少,从而升高性能并减少收入。  因而,针对 Kubernetes 的 Golang 利用优化对于实现无效的资源利用、升高服务器负载以及保障利用在生产环境中顺利运行至关重要。  在本文中,咱们将钻研在 Kubernetes 上优化 Golang 利用的最佳办法,重点是升高服务器负载。咱们会探讨一些办法,包含用 readiness 和 liveness 探针进行监控,利用弹性伸缩等性能来优化资源利用。  通过采取这些最佳实际,能够进步在 Kubernetes 上运行的 Golang 利用的性能和可扩展性,这将改善用户体验、节约老本,并晋升运维效率。  本文中所有代码示例能够拜访下方网址查看:https://github.com/theifedayo/earthly.dev/tree/main/01-optimizing-go-k8s  后期筹备本文须要你对 Go 和 Kubernetes 有中等水平的理解。此外,须要你曾经装置 Go、Docker 和 Kubernetes (及其他运行 Kubernetes 的工具,如kubectl)并且曾经在零碎内配置结束。  你能够通过终端输出以下命令验证是否曾经装置 Go:go version  如果曾经装置,应该返回以下内容:go version go1.19.3 darwin/amd64  应用以下命令能够验证 Docker 是否装置:docker --version  输入应为(或其余相似的内容):Docker version 20.10.22, build 3a2c30b  验证 Kubernetes 是否装置也是相似的办法。  了解 Golang 应用程序和 Kubernetes谷歌开发了 Golang,也称“Go”,其指标是高效、多并发和可扩大。它对创立高性能软件很无效,特地是对须要并发解决的利用,如网络服务器、网络软件和分布式系统。  ...

June 21, 2023 · 4 min · jiezi

关于go:Go语言中的原子操作

1. 引言在并发编程中,多个协程同时拜访和批改共享数据时,如果没有应用适当的机制来避免并发问题,这个时候可能导致不确定的后果、数据不一致性、逻辑谬误等严重后果。 而原子操作是解决并发编程中共享数据拜访问题的一种常见机制。因而接下来的文章内容将深刻介绍原子操作的原理、用法以及在解决并发问题中的利用。 2. 问题引入在并发编程中,如果没有适当的并发管制机制,有可能多个协程同时拜访和批改共享数据,此时将引起竞态条件和数据竞争问题。这些问题可能导致不确定的后果和谬误的行为。 为了更好地了解并发问题,以下是一个示例代码,展现在没有进行并发管制时可能呈现的问题: package mainimport "fmt"var counter intfunc increment() { value := counter value++ counter = value}func main() { // 启动多个并发协程 for i := 0; i < 1000; i++ { go increment() } // 期待所有协程执行结束 // 这里仅为了示例目标应用了简略的期待形式 time.Sleep(10) fmt.Println("Counter:", counter) // 输入的后果可能小于 1000}在这个示例中,多个并发协程同时对counter进行读取、减少和写入操作。因为这些操作没有进行适当的并发管制,可能会导致竞态条件和数据竞争的问题。因而,最终输入的counter的值可能小于预期的 1000。 这个示例阐明了在没有进行适当的并发管制时,共享数据拜访可能导致不确定的后果和不正确的行为。为了解决这些问题,咱们须要应用适当的并发管制机制,以确保共享数据的平安拜访和批改。 在Go语言中,有多种形式能够解决并发问题,而原子操作便是其中一种实现,上面咱们将认真介绍Go语言中的原子操作。 3. 原子操作介绍3.1 什么是原子操作Go语言中的原子操作是一种在并发编程中用于对共享数据进行原子性拜访和批改的机制。原子操作能够确保对共享数据的操作在不被中断的状况下实现,要么齐全执行胜利,要么齐全不执行,防止了竞态条件和数据竞争问题。 Go语言提供了sync/atomic包来反对原子操作。该包中定义了一系列函数和类型,用于操作不同类型的数据。以下是原子操作的两个重要概念: 原子性:原子操作是不可分割的,要么全副执行胜利,要么全副不执行。这意味着在并发环境中,一个原子操作的执行不会被其余线程或协程的烦扰或中断。线程平安:原子操作是线程平安的,能够在多个线程或协程之间平安地拜访和批改共享数据,而无需额定的同步机制。原子操作是一种高效、简洁且牢靠的并发管制机制。它在并发编程中提供了一种平安访问共享数据的形式,防止了传统同步机制(如锁)所带来的性能开销和复杂性。在编写并发代码时,应用原子操作能够无效地进步程序的性能和可靠性。 3.2 反对的操作在Go语言中,应用sync/atomic包提供了一组原子操作函数,用于在并发环境下对共享数据进行原子操作。以下是一些罕用的原子操作函数: Add系列函数,如AddInt32,原子地将指定的值与指定的int32类型变量相加,并返回相加后的后果。当然,也反对int32,int64,uint32,uint64这些数据类型CompareAndSwap系列函数,如CompareAndSwapInt32,比拟并替换操作,原子地比拟指定的int32类型变量的值和旧值,如果相等则替换为新值,并返回是否替换胜利。Swap系列函数,如SwapInt32,原子地将指定的int32类型变量的值设置为新值,并返回旧值。Load系列函数,如LoadInt32,能将原子地加载并返回指定的int32类型变量的值。Store系列函数,如StoreInt32,原子地将指定的int32类型变量的值设置为新值。这些原子操作函数提供了对整数类型的原子操作反对,能够用于在并发环境下进行平安的数据拜访和批改。除了上述函数外,sync/atomic包还提供了其余一些原子操作函数,用于操作指针类型和特定的内存操作。在编写并发代码时,应用这些原子操作函数能够确保共享数据的一致性和正确性。 3.3 实现原理Go语言中的原子操作的实现,其实是依赖于底层的零碎调用和硬件反对,其中次要是CAS,Load和Store等原子指令。 CAS操作,它用于比拟并替换共享变量的值。CAS操作包含两个阶段:比拟阶段和替换阶段。在比拟阶段,零碎会比拟共享变量的以后值与期望值是否相等;如果相等,则进入替换阶段,将共享变量的新值写入。CAS操作可通过底层的零碎调用来实现原子性,保障只有一个线程或协程可能胜利执行比拟并替换的操作。而CAS操作通过底层的零碎调用(如cmpxchg)实现,利用处理器的原子指令实现比拟和替换操作。 Load和Store操作则用于原子地读取共享变量的值。这两个都是通过底层的原子指令来实现的,通过这种形式实现了原子拜访和批改。确保在读取或者写入共享数据的过程中不会被其余线程的批改所烦扰。 3.4 实际回到下面的问题,因为多个并发协程同时对counter进行读取、减少和写入操作。因为这些操作没有进行适当的并发管制,可能会导致竞态条件和数据竞争的问题。上面咱们应用原子操作来对其进行解决,代码示例如下: package mainimport ( "fmt" "sync" "sync/atomic")var counter int32var wg sync.WaitGroupfunc increment() { defer wg.Done() atomic.AddInt32(&counter, 1) }func main() { // 设置期待组的计数器 wg.Add(1000) // 启动多个并发协程 for i := 0; i < 1000; i++ { go increment() } // 期待所有协程执行结束 wg.Wait() fmt.Println("Counter:", counter) // 输入后果为 1000}在上述代码中,咱们应用 atomic.AddInt32 函数来原子地对 counter 变量进行递增操作。该函数接管一个 *int32 类型的指针作为参数,它会以原子操作的形式将指定的值增加到指标变量中。 ...

June 20, 2023 · 1 min · jiezi

关于go:Go-匿名返回值的坑

明天分享的内容比拟根底,精确地说是 Go 的语言个性——命名、匿名返回值。 先看上面的例子,猜想会输入什么? package mainfunc main() { println(A()) println(B())}func A() int { var i int defer func() { i = i + 3 }() return i}func B() (i int) { defer func() { i = i + 3 }() return i}| (想好答案再滑下来) | | | | | | | | | | 正确答案:0 3。 A 和 B 两个函数的区别在于,B 的函数返回值是命名的,所以 defer 函数执行的批改,会作用到 B 自身; 而 A 函数返回值是匿名的,取决于 return 的时候求进去的值。 ...

June 19, 2023 · 1 min · jiezi

关于go:SAP-Spartacus-UI-中的-CmsTicketInterceptor

在 Spartacus UI 发动的 OCC API 申请的 URL 中,您可能会留神到一个名为 cmsTicketId 的字段。这个字段的含意与用处如下: cmsTicketId 是一个标识符,用于关联 Spartacus UI 与 SAP Commerce Cloud 后端 CMS (Content Management System) 的会话。CMS 是一个用于治理网站内容的零碎,如页面、组件和其余与前端显示相干的元素。在 SAP Commerce Cloud 中,CMS 通过 CMS Cockpit 或者 SmartEdit 工具进行治理。为了确保在 Spartacus UI 中显示的内容与 CMS 的状态保持一致,须要应用一个会话标识符来关联二者。这就是 cmsTicketId 的作用。 当用户在 CMS 中进行更改并预览更改后果时,零碎会创立一个 CMS 会话票证(CMS Ticket)。这个票证蕴含了以后 CMS 会话的所有更改,但尚未公布到生产环境。Spartacus UI 会将这个票证的 ID 作为 cmsTicketId 参数传递给 OCC API,以便在 API 申请中获取与该票证关联的 CMS 数据。这样,用户就能够在 Spartacus UI 中实时预览他们在 CMS 中所做的更改。 cmsTicketId 参数只在开发或预览模式下应用,以确保用户可能预览未公布的 CMS 更改。在生产环境中,Spartacus UI 通常不会传递 cmsTicketId 参数,因为生产环境只显示已公布的 CMS 内容。 ...

June 19, 2023 · 1 min · jiezi

关于go:深入分析Go语言与C的异同

摘要:本文由葡萄城技术团队于思否原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供业余的开发工具、解决方案和服务,赋能开发者。前言为了更加深刻地介绍Go语言以及与C#语言的比拟,本文将会从多个维度登程进行具体的论述。首先,将从Go语言的关键字方面介绍Go与C#在语言个性上的异同,并且探讨两种语言在关键字方面的优化和不足之处。其次,本文将通过代码示例、性能测试等形式,展现Go语言在关键字方面的劣势,从而为读者呈现出Go语言的弱小之处。除此之外,为了更好地帮忙读者了解Go语言,本文还将介绍一些优良的Go语言工具和社区资源,供读者进一步学习和摸索。置信通过这些内容的全面介绍,读者们会对Go语言有更全面深刻的意识和理解。 文章目录: 1.Go的前世今生 1.1Go语言诞生的过程 1.2逐渐成型 1.3正式公布 1.4 Go装置领导 2.Go和C#的关键字比拟 2.1Go与C#都有的关键字 2.1.1.Var 2.1.2. Switch-case-default 2.1.3.If-else 2.1.4. For 2.1.5. Struct 2.2Go与C#不太一样然而意思差不多的关键字 2.2.1. Package与namespace 2.2.2. Import与using 2.2.3. Type与class 2.2.4. Defer与finally 2.3Go有而 C#没有的关键字 2.3.1. Fallthrough 2.3.2.Func 3.文章小结4.扩大链接 1.Go的前世今生 1.1Go语言诞生的过程 话说早在 2007 年 9 月的一天,Google 工程师 Rob Pike 和平常一样启动了一个 C++我的项目的构建,依照他之前的教训,这个构建应该须要继续 1 个小时左右。这时他就和 Google公司的另外两个共事 Ken Thompson 以及 Robert Griesemer 开始吐槽并且说出了本人想搞一个新语言的想法。过后 Google 外部次要应用 C++构建各种零碎,但 C++复杂性微小并且原生短少对并发的反对,使得这三位大佬苦恼不已。 ...

June 19, 2023 · 3 min · jiezi

关于go:深入理解Go语言接口

1. 引言接口是一种定义了软件组件之间交互标准的重要概念,其促成了代码的解耦、模块化和可扩展性,提供了多态性和形象的能力,简化了依赖治理和替换,不便进行单元测试和集成测试。这些个性使得接口成为构建牢靠、可保护和可扩大的软件系统的要害工具之一。 在古代编程语言中,接口是不可或缺的一个重要个性。本文将具体介绍Go语言中的接口,从而可能更好得应用Go语言。 2. Go语言接口的基本概念接口是一种约定,用于指定对象的行为和性能,而无需关注其具体实现。Go语言的接口定义和申明形式绝对简洁明了。 在Go语言中,接口通过一个办法汇合来定义,该办法汇合定义了接口的办法签名(包含办法名、参数列表和返回值)。接口申明应用关键字interface,前面跟着接口的名称和办法汇合。 上面是一个示例,演示了如何在Go语言中定义一个接口: // 定义一个接口type Writer interface { Write(data []byte) (int, error)}在上述示例中,咱们应用interface关键字定义了一个名为Writer的接口。该接口蕴含一个名为Write的办法,它接管一个[]byte类型的参数,并返回一个int和一个error类型的后果。 接口能够蕴含任意数量的办法。例如,咱们能够定义一个具备多个办法的接口: type ReaderWriter interface { Read(data []byte) (int, error) Write(data []byte) (int, error)}在上述示例中,咱们定义了一个名为ReaderWriter的接口,它蕴含一个Read办法和一个Write办法,两个办法别离用于读取和写入数据。 3. Go语言接口的个性3.1 隐式实现在Go语言中,接口的实现是隐式的,这意味着咱们无需在类型申明时显式申明实现了某个接口。只有类型实现了接口中定义的所有办法,它就被视为实现了该接口。以下是一段示例代码: package mainimport "fmt"// Writer 是一个用于写入数据的接口type Writer interface { Write(data []byte) error}// FileWriter 是 Writer 接口的隐式实现type FileWriter struct { }// Write 实现了 Writer 接口的 Write 办法func (fw FileWriter) Write(data []byte) error { // 实现文件写入逻辑 fmt.Println("Writing data to file:", string(data)) return nil}// 应用 Writer 接口作为参数的函数func processData(w Writer) { // 解决数据的逻辑 data := []byte("Some data to write") w.Write(data)}func main() { fw := FileWriter{} processData(fw)}上述代码中,咱们定义了一个接口Writer,该接口蕴含了一个Write办法。而后,咱们创立了一个类型FileWriter,它实现了Writer接口的Write办法。在main函数中,咱们通过隐式实现将FileWriter类型的变量传递给processData函数,该函数接管一个实现了Writer接口的参数。 ...

June 19, 2023 · 2 min · jiezi

关于go:本科生应该选择考研还是就业这是所有大学生应该思考的问题

亮观点首先要申明接下来的内容次要是针对互联网人来说的,不适用于所有人。 对于互联网人,特地是做技术的来说,越早待业越好。 是什么给出这个论断?我在大一的时候就决定了毕业当前找工作,读到高三曾经是12年的时光,对于过后的我来说,再读完四年大学,我再也不想读书了。 一是因为家庭条件的起因,切实不想让家里花钱了; 二是大一的课程让我发现,大学只能作为科普的中央,传授的货色不肯定有用,还是得看本人自学喜爱的货色。 过后做出这个决定比拟粗率。 工作后的感悟工作当前,四周的共事很多是研究生毕业的。在鹅厂来说研究生的职级是高一个小子级,也就是快半年的工夫而已。毕业工资绝对多两千。 这是不是真的值得呢?我始终在思考。 工作一段时间后,我发现研究生毕业的共事,的确比同期本科毕业的要好。 不论是思维深度还是做事逻辑,都要更好。 所以对于本科还是研究生,显著研究生对于公司来说是更值得的,更应该招学历更高的人。 然而对于集体来说,三年的工夫真的比三年的工作教训好吗? 如果你选错了导师呢?是不是也得熬过这三年,而选错了公司后跳槽,还能复用工作教训。 我的同学那年我毕业当前,相当一部分同学抉择考研。 后续一部分人进入互联网的时候,我曾经带人了,而且有了一套本人的方法论。 职场关系也建设起来了一些,甚至我那个时候实践上是能够带研究生的。 也就是说我同期的同学,读了三年书的成果是有可能成为我的师傅,除非后续跳槽,不然永远也无奈超过我的薪资。 三年读书时光真的值得吗? 对于市场来说互联网的低谷期在2019年之后降临,2020年之后暴发。 比我早几年入职的老同事当然就比我早几年买房,而后续楼市横盘对新出社会的年轻人来说越来越艰难。 早就是劣势,有时候总是反馈在各种中央,红利在一直的隐没。 对于压缩的市场来说,造就一个人不如间接用有教训的人,招校招生须要6个月-1年的周期,即便忠诚度更高也得失相当。而三年工作教训的人来了就能干活,第二天就能上手,三个月就能出问题,那公司为什么还要招校招生的,兴许只有大厂能承当的了培训老本。 所以用人单位为了一个人做事的能力买单,而人的能力在实在环境的实际中能力成长最快。承不承认? 博士的红利工作三年能够胜过读研三年,然而工作五、六年胜不过博士毕业。 既然要读书,不如读到底。有时候人抉择持续读书,只是为了回避步入社会。 读书能够给他带来安全感,能够躲在学校和同学老师相伴,不必思考生活费老本的问题。 但有的人读书真的是为了谋求更多的学术研究,“深造”成为高级专门人才。 商业社会的实质就是应用高级专业人才,博士研究生最终在本人的畛域深耕了太多。 一个认真钻研有成绩有能力的博士,进去当前相对能够抵得上工作5、6年的CURD工程师,能做普通人做不了的工作,比方算法钻研AI畛域等。 总结只有有能力找到好工作,社会这个大学远比学校更磨难人,且能学到更多的货色。 如果你是打算三年后为了找份更好的工作而读研,不划算,不如当初就工作。 边工作边加入商业我的项目边学货色不是更好吗? 当然了,如果是为了当前有机会去社会的止境,那学历更高再入个dang更好。 你感觉考研好还是工作好? 一起提高你好,我是小熊,是一个爱技术然而更爱钱的程序员。上进且佛系自律的人。喜爱发小机密/臭屁又爱夸耀。 奋斗的大学,激情的当初。赚了钱买了房,写了书出了名。当过面试官,带过师傅搬过转。 大厂外来务工人员。是我,我是小熊,是不一样的烟火欢送围观。 我的博客 机智的程序员小熊 欢送珍藏

June 18, 2023 · 1 min · jiezi

关于go:Go-中的格式化字符串fmtSprintf-和-fmtPrintf

在 Go 中,能够应用 fmt.Sprintf() 和 fmt.Printf() 函数来格式化字符串,这两个函数相似于 C 语言中的 scanf 和 printf 函数。 fmt.Sprintf()fmt.Sprintf() 函数返回一个格式化后的字符串,而不是将其打印到规范输入流中。上面是一个例子: package mainimport "fmt"func main() { name := "Mazey" age := 24 str := fmt.Sprintf("My name is %s and I'm %d years old.", name, age) fmt.Println(str)}输入: My name is Mazey and I'm 24 years old.在这个例子中,应用了 %s 和 %d 格式化动词来格式化字符串。s 用于字符串,d 用于整数。 fmt.Printf()fmt.Printf() 函数将格式化后的字符串打印到规范输入流中。上面是一个例子: package mainimport "fmt"func main() { name := "Mazey" age := 24 fmt.Printf("My name is %s and I'm %d years old.", name, age)}输入: ...

June 17, 2023 · 1 min · jiezi

关于go:k8s-集群部署尝试

K8S 部署形式有很多,有的形式不太敌对,须要留神很多关键点,有的形式对小白比拟敌对,部署简略不便且高效 二进制源码包的部署形式应用 二进制源码包的形式部署会比拟麻烦,大略分为如下几步: 获取源码包部署在 master 节点和 worker 节点上启动相应节点的要害服务master 节点上 api-server ,分布式存储 例如 etcd,scheduler,controller manager woker 节点上 kubelet,kube-proxy,docker 在 K8S 中,各个组件之间相互拜访都是倡议通过证书进行拜访的,所以咱们也须要生成相应的证书 生成证书例如 HTTP 的,HTTPS 的 波及的组件和工具比拟多,咱们能够后续对 K8S 比拟熟之后,再来尝试源码包的形式 应用 kubeadm 形式部署官网是举荐应用 minikube 来部署,其实他也是通过 kubeadm 来部署的,应用 kubeadm 的部署形式就绝对简略,不过步骤也是不少,有: 须要装置 kube 对应的软件,kubelet,kubeadm,kubectl初始化整个 K8S 集群增加节点到集群中执行上述 3 步,咱们就能够将 K8S 集群搭建部署起来 生成证书是主动的,不须要咱们去干涉master 节点是用来治理集群容器的,所有的容器都是运行在 master 节点中,worker 节点是用来执行工作的对于容器镜像的获取咱们能够间接通过谷歌下载镜像,或者是通过国内阿里云来下载镜像 点我查看 kubeadm 根底环境筹备筹备 3 台虚拟机,买云服务器也是能够的,定义好主机名 masternode1node2能够这么设置主机名 hostnamectl set-hostname master hostnamectl set-hostname node1 hostnamectl set-hostname node2 能够通过 hostname 查看主机名 ...

June 17, 2023 · 2 min · jiezi

关于go:有问必答Go如何优雅的对时间进行格式化

昨天 交换群 对于「Go如何优雅的对工夫进行格式化?」开展了探讨: 咋搞捏?如何在不循环的状况下,把列表数据结构体的工夫批改为咱们习惯的格局,而不是UTC模式 咱们要实现的成果如下: created_at 是go语言原生的形式,updated_at 是咱们冀望优化成的形式{ "code": 200, "data": { "count": 12, "info": [ { "created_at": "2021-03-17T07:11:24+08:00" //原生形式 "updated_at": "2021-03-17 07:11:24", //须要优化成这种 } ] }, "message": "胜利"}引入神器首先咱们引入一个包,在控制台运行 go get github.com/liamylian/jsontime下载相干依赖go mod download批改构造体,申明要解决工夫的字段 type Order struct { . . . CreatedAt time.Time `json:"created_at" time_format:"sql_datetime" time_utc:"false"` // 格式化工夫示例 UpdatedAt string `json:"updated_at"` // 原生状态示例}取值时调用MarshalToString把构造体数据转为字符串然而转完的字符串存在反斜线的问题,应用json.RawMessage()解决一下 var timeJson = jsontime.ConfigWithCustomTimeFormatfunc AllOrder(c *gin.Context) { limitStr := c.DefaultQuery("limit", "10") pageStr := c.DefaultQuery("page", "0") orderType := c.DefaultQuery("orderType", "desc") orderField := c.DefaultQuery("orderField", "id") orderSql := orderField + " " + orderType limit, _ := strconv.Atoi(limitStr) page, _ := strconv.Atoi(pageStr) count, res := model.QueryOrder(0, limit, page, orderSql) //解决1:MarshalToString bytes, _ := timeJson.MarshalToString(&res) jsonInfo := map[string]interface{}{ "count": count, //解决2:解决反斜线的问题 "info": json.RawMessage(bytes), } c.JSON(http.StatusOK, ReturnJson{ http.StatusOK, jsonInfo, "胜利", })}咱们最终实现进去的成果{ "code": 200, "data": { "count": 12, "info": [ { "updated_at": "2021-03-17 07:13:24", "created_at": "2021-03-17 07:11:24", } ] }, "message": "胜利"}好了,通过引入下面的神器就解决了咱们的问题。 ...

June 16, 2023 · 2 min · jiezi

关于go:Go语言中的结构体灵活性与可扩展性的重要角色

1. 引言构造体是Go语言中重要且灵便的概念之一。构造体的应用使得咱们能够定义本人的数据类型,并将不同类型的字段组合在一起,实现更灵便的数据结构。本文旨在深刻介绍Go语言中的构造体,揭示其重要性和灵活性,并向读者展现构造体反对的泛滥个性,展现其弱小之处。 2. 什么是构造体?在Go语言中,构造体是一种自定义的数据类型,用于将不同类型的字段组合在一起造成一个新的数据结构。构造体定义了一组字段,每个字段能够有不同的类型,这些字段一起形成了构造体的实例。通过构造体,咱们能够将相干的数据进行组织和治理,从而更不便地进行操作和传递。 构造体的定义应用关键字type和struct。以下是构造体的定义语法: type 构造体名称 struct { 字段1 类型1 字段2 类型2 // 更多字段...}在上述语法中,构造体名称是咱们为构造体类型起的名称,能够依据理论状况进行命名。而构造体的字段局部则列出了构造体蕴含的所有字段,每个字段都有一个字段名和对应的字段类型。上面咱们给出一个构造体定义的示例: type User struct { Name string Age int Address string}述构造体定义了一个名为User的构造体类型,它蕴含了两个字段:Name、Age,它们的类型别离为字符串、整数。到此为止,咱们实现了对构造体的根本介绍,可能基于此创立出一种新的数据类型。 然而构造体的定义只是创立了一种新的数据类型,应用构造体须要创立其实例,Go语言中提供了几种实例化形式,上面咱们将对其进行具体讲述。 首先,能够应用构造体字面量间接初始化构造体变量,依照字段程序给出对应的值。示例如下: person := Person{"Alice", 25, "广东深圳"}其次能够应用指定字段名的形式给出字段的初始化值,这个时候能够疏忽某些字段。示例如下: person := Person{Name: "Alice", Age: 25}也能够应用new关键字创立一个指向构造体的指针,并返回该指针。示例如下: personPtr := new(Person)personPtr.Name = "Alice"personPtr.Age = 25亦或者应用var关键字申明构造体变量,而后别离给字段赋值。示例如下: var person Personperson.Name = "Alice"person.Age = 25以上是常见的构造体实例化和初始化办法,依据理论须要抉择适合的形式。无论应用哪种形式,都能够创立并初始化构造体的实例,以便后续应用和操作构造体的字段。 到此为止,咱们介绍了什么是构造体,其实构造体能够认为是一组不同类型字段的组合,将其用来示意一个新的概念。其次咱们也介绍了几种实例化自定义构造体的形式,基于此咱们对构造体有一个大略的理解。 3. 构造体反对哪些个性呢?下面咱们对构造体有了根本的理解,构造体能够组合一组不同类型的字段,将其用来示意一个新的概念。然而构造体并不止步于此,其也反对定义方法,数据封装等。通过这些个性,构造体在Go语言中具备了灵活性、可扩展性和可读性,并且在面向对象编程、数据建模和代码复用等方面施展着重要作用。 3.1 构造体反对定义方法构造体在Go语言中反对定义方法,办法是与构造体关联的函数。这种个性使得咱们能够将特定的行为和性能与构造体关联起来,通过办法来操作构造体的数据。 上面是一个示例,演示了构造体反对定义方法的个性: package mainimport "fmt"// 定义构造体type Person struct { Name string Age int}// 定义方法:打印个人信息func (p Person) PrintInfo() { fmt.Printf("Name: %s\n", p.Name) fmt.Printf("Age: %d\n", p.Age)}// 定义方法:批改年龄func (p Person) UpdateAge(newAge int) { p.Age = newAge}func main() { // 创立一个 Person 构造体实例 person := Person{Name: "John", Age: 30} // 调用构造体的办法:打印个人信息 person.PrintInfo() // Output: Name: John Age: 30 // 调用构造体的办法:批改年龄 person.UpdateAge(35) // 再次调用办法,打印批改后的个人信息 person.PrintInfo() // Output: Name: John Age: 35}在上述代码中,咱们定义了一个 Person 构造体,它蕴含了 Name 和 Age 两个字段。而后,咱们为构造体定义了两个办法:PrintInfo() 和 UpdateAge()。 ...

June 14, 2023 · 3 min · jiezi

关于go:使用golang-自定义编写开发-Prometheus-exporter

Exporter是基于Prometheus施行的监控零碎中重要的组成部分,承当数据指标的采集工作,官网的exporter列表中曾经蕴含了常见的绝大多数的零碎指标监控,比方用于机器性能监控的node_exporter, 用于网络设备监控的snmp_exporter等等。这些已有的exporter对于监控来说,仅仅须要很少的配置工作就能提供欠缺的数据指标采集。prometheus四种类型的指标Counter 计数,Gauge 观测类,Histogram 直方,Summary 摘要 用golang语言如何结构这4种类型对应的指标,二是搞清楚批改指标值的场景和形式。不带label的根本例子package mainimport ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "net/http")func main() { // 定义指标 cpuUsage := prometheus.NewGauge(prometheus.GaugeOpts{ Name: "cpu_usage", // 指标名称 Help: "this is test metrics cpu usage", // 帮忙信息 }) // 给指标设置值 cpuUsage.Set(29.56) // 注册指标 prometheus.MustRegister(cpuUsage) // 裸露指标 http.Handle("/metrics", promhttp.Handler()) http.ListenAndServe("localhost:9100", nil)} 带有固定label指标的例子 带有非固定label指标的例子package mainimport ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "net/http")func main() { //定义带有不固定label的指标 mtu := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "interface_mtu", Help: "网卡接口MTU", }, []string{"interface", "Machinetype"}) // 给指标设置值 mtu.WithLabelValues("lo", "host").Set(1500) mtu.WithLabelValues("ens32", "host").Set(1500) mtu.WithLabelValues("eth0", "host").Set(1500) // 注册指标 prometheus.MustRegister(mtu) // 裸露指标 http.Handle("/metrics", promhttp.Handler()) http.ListenAndServe("localhost:9100", nil)} ...

June 14, 2023 · 3 min · jiezi

关于go:kratos-框架的问题

长处啥都有,相似 PHP 的各种框架。开箱即用,给你整上各种现代化的工具,甭管用不必,须要不须要全参差。依赖注入、http、gRPC、MySQL、Redis 都“搞外头“。问题构造凌乱。proto 文件散到各个目录,甚至 proto 的版本都没治理好。依赖注入纯正是为了用而用,没有用依赖注入解决任何理论问题,也没做到代码形象和隔离。对微服务的了解有问题。微服务的外围问题是服务发现、服务注册,官网 demo 竟然没有这个模块。文档中的 registry 实现也丑到不行。不晓得作者想解决的外围问题是什么? proto 的主动治理问题?第三方包的语义版本化问题显然没想分明。而且竟然俊俏到每个我的项目都要复制一份 google 的 proto 文件。外部调用的 rpc / http 灵便切换?间接用 grpc-gateway 就能够了,本人封装一层也很容易。依赖注入?先想分明为什么要依赖注入,想解决什么问题?微服务?先想分明为什么要做微服务?是不是用上了 grpc 就是微服务了?Go 我的项目开发的脚手架?你提供了哪些脚手架的性能?跟 django 比、ruby on rails 比有啥特色?云原生?服务网格?就齐全不沾边了。论断四不像的货色,不知所谓。纯属 YY,各位大神当我是小白。

June 13, 2023 · 1 min · jiezi

关于go:深入探究forrange语句

1. 引言在Go语言中,咱们常常须要对数据汇合进行遍历操作。对于数组来说,应用for语句能够很不便地实现遍历。然而,当咱们面对其余数据类型,如map、string 和 channel 时,应用一般的for循环无奈间接实现遍历。为了更加便捷地遍历这些数据类型,Go语言引入了for...range语句。本文将以数组遍历为终点,逐渐介绍for...range语句在不同数据类型中的利用。 2. 问题引入假如咱们有一个整数数组,咱们想要遍历数组中的每个元素并对其进行解决。在这种状况下,咱们能够应用for语句联合数组的长度来实现遍历,例如: package mainimport "fmt"func main() { numbers := [5]int{1, 2, 3, 4, 5} for i := 0; i < len(numbers); i++ { fmt.Println(numbers[i]) }}在上述代码中,咱们定义了一个整数数组numbers,通过一般的for循环遍历了数组并打印了每个元素。然而,当咱们遇到其余数据类型时,如map、string 或者channel时,此时应用for语句将无奈简略对其进行遍历。那有什么形式可能不便实现对map,string等类型的遍历呢? 事实上,go语言中存在for....range语句,可能实现对这些类型的遍历,上面咱们来认真介绍下for...range。 3. 根本介绍在Go语言中,for...range语句为遍历数组、切片、映射和通道等数据结构提供了一种便捷的形式。它暗藏了底层的索引或迭代器等细节,是Go语言为遍历各种数据结构提供的一种优雅而简洁的语法糖,使得遍历操作更加不便和直观。上面认真简介应用for...range实现对切片, map, channel的遍历操作。 3.1 遍历切片当应用for...range语句遍历切片时,它会一一迭代切片中的元素,并将索引和对应的值赋值给指定的变量。示例代码如下: numbers := [5]int{1, 2, 3, 4, 5}for index, value := range numbers { // 在这里解决 index 和 value}其中numbers 是咱们要遍历的切片。index 是一个变量,它在每次迭代中都会被赋值为以后元素的索引(从0开始)。value 是一个变量,它在每次迭代中都会被赋值为以后元素的值。 如果只关注切片中的值而不须要索引,能够应用下划线 _ 代替索引变量名,以疏忽它: numbers := []int{1, 2, 3, 4, 5}for _, value := range numbers { fmt.Println("Value:", value)}这样,循环体只会打印出切片中的值而不显示索引。 ...

June 13, 2023 · 2 min · jiezi

关于go:k8s-基本架构

k8s 中反对的 node 数 和 pod 数k8s 也是逐渐倒退过去的,来看看以前和当初反对的 node 数 和 pod 数比照 node 即 节点 , 晚期的 k8s 版本可能反对 100 台节点,当初 k8s 能够反对到 2000 台了 pod 数,晚期的版本能够反对 1000 个,当初的 k8s 能够反对到 150000 个了 k8s 利用部署架构对于利用部署架构,分为 无核心节点架构 和 有核心节点架构 什么是无核心节点架构?就是集群当中所有的主机之间都互为伙伴关系 , 例如 GlusterFS 分布式存储 什么是有核心节点架构?例如 HDFS 就是一个有核心节点架构 , 他有 NameNode (整个集群的管理者)和 DataNode (集群中存储数据的) 当初要学的 K8S 也是一个有核心节点的架构 通过上图咱们能够看出, K8S 有一个 master 节点, 2 个 worker 节点 worker 节点别离可能和 master 节点进行通信 ...

June 12, 2023 · 1 min · jiezi

关于go:Go语言中的init函数-特点用途和注意事项

1. 引言在Go语言中,init()函数是一种非凡的函数,用于在程序启动时主动执行一次。它的存在为咱们提供了一种机制,能够在程序启动时进行一些必要的初始化操作,为程序的失常运行做好筹备。 在这篇文章中,咱们将具体探讨init()函数的特点、用处和注意事项,心愿能帮忙你更好地了解和应用这个重要的Go语言个性。 2. init 函数的特点2.1 主动执行init()函数的一个重要特点,便是其无需手动调用,它会在程序启动时主动执行。当程序开始运行时,Go运行时零碎会主动调用每个包中的init()函数。上面是一个示例代码,演示了init()函数在程序启动时主动执行的特点: package mainimport "fmt"func init() { fmt.Println("Init function executed")}func main() { fmt.Println("Main function executed")}在这个示例代码中,咱们定义了一个init()函数和一个main()函数。init()函数会在程序启动时主动执行,而main()函数则是程序的入口函数,会在init()函数执行结束后执行。 当咱们运行这段代码时,输入后果如下: Init function executedMain function executed能够看到,init()函数在程序启动时主动执行,并且在main()函数之前被调用。这证实了init()函数在程序启动时会主动执行,能够用于在程序启动前进行一些必要的初始化操作。 2.2 在包级别变量初始化后执行当一个包被引入或应用时,其中会先初始化包级别常量和变量。而后,依照init()函数在代码中的申明程序,其会被主动执行。上面是一个简略代码的阐明: package mainimport "fmt"var ( Var1 = "Variable 1" Var2 = "Variable 2")func init() { fmt.Println("Init function executed") fmt.Println("Var1:", Var1) fmt.Println("Var2:", Var2)}func main() { fmt.Println("Main function executed")}在这个示例代码中,咱们申明了包级别的常量,并在init()函数中打印它们的值。在main()函数中,咱们打印了一条信息。当咱们运行这段代码时,输入后果如下: Init function executedVar1: Variable 1Var2: Variable 2Main function executed能够看到,init()函数在包的初始化阶段被主动执行,并且在包级别常量和变量被初始化之后执行。这验证了init()函数的执行程序。因为包级别常量和变量的初始化是在init()函数执行之前进行的。因而,在init()函数中能够平安地应用这些常量和变量。 2.3 执行程序不确定在一个包中,如果存在多个init()函数,它们的执行程序是依照在代码中呈现的程序确定的。先呈现的init()函数会先执行,后呈现的init()函数会后执行。 具体来说,依照代码中的程序定义了init()函数的先后顺序。如果在同一个源文件中定义了多个init()函数,它们的程序将依照在源代码中的呈现程序来执行。上面通过一个示例代码来阐明: package mainimport "fmt"func init() { fmt.Println("First init function")}func init() { fmt.Println("Second init function")}func main() { fmt.Println("Main function executed")}在这个示例中,咱们在同一个包中定义了两个init()函数。它们依照在源代码中的呈现程序进行执行。当咱们运行这段代码时,输入后果为: ...

June 11, 2023 · 3 min · jiezi

关于go:切片有哪些注意事项是一定要知道的呢

1. 引言在之前我写了一篇 切片比数组好用在哪 的文章,认真介绍了切片相比于数组的长处。但切片事实上也暗藏着一些潜在的陷阱和须要留神的细节,理解和把握切片的应用注意事项,能够防止意外的程序行为。本文将深入探讨Go语言切片常见的注意事项,从而可能更好得应用切片。 2. 注意事项2.1 留神一个数组能够同时被多个切片援用当创立一个切片时,它实际上是对一个底层数组的援用。这意味着对切片的批改会间接影响到底层数组以及其余援用该数组的切片。这种援用关系可能导致一些意想不到的后果,上面是一个示例代码来阐明这个问题: package mainimport "fmt"func main() { array := [5]int{1, 2, 3, 4, 5} firstSlice := array[1:4] // 创立一个切片,援用了底层数组的索引1到3的元素 secondSlice := array[1:3] fmt.Println("Original array:", firstSlice) // 输入第一个切片 [2 3 4] fmt.Println("Original slice:", secondSlice) // 输入第二个切片 [2 3] // 批改切片的第一个元素 firstSlice[0] = 10 fmt.Println("Modified array:", firstSlice) // 输入第一个切片 [10 3 4] fmt.Println("Modified slice:", secondSlice) // 输入第二个切片 [10 3]}在上述代码中,咱们创立了一个长度为5的数组array和两个援用该数组的切片firstSlice和secondSlice。当咱们批改第一个切片的第一个元素为10时,底层数组的对应地位的元素也被批改了。这里导致了数组和其余援用该数组的切片的内容也会受到影响。 如果咱们有多个切片同时援用了同一个底层数组,同时咱们并不想因为对某个切片的批改,影响到另外一个切片的数据,此时咱们能够新创建一个切片,应用内置的copy函数来复制原切片元素的值。示例代码如下: package mainimport "fmt"func main() { array := [5]int{1, 2, 3, 4, 5} slice := array[1:4] // 复制切片创立一个独立的底层数组 newSlice := make([]int, len(slice)) copy(newSlice, slice) fmt.Println("Original array:", array) // 输入原始数组 [1 2 3 4 5] fmt.Println("Original slice:", slice) // 输入初始切片 [2 3 4] fmt.Println("New slice:", newSlice) // 输入新创建的切片 [2 3 4] // 批改newSlice的第一个元素 newSlice[0] = 10 fmt.Println("Modified array:", array)// 输入批改后的数组 [1 2 3 4 5] fmt.Println("Original slice:", slice)// 输入初始切片 [2 3 4] fmt.Println("New slice:", newSlice)// 输入批改后的切片 [10 3 4]}通过创立了一个新的切片newSlice,它领有独立的底层数组,同时应用copy函数复制原切片的值,咱们当初批改newSlice不会影响原始数组或原始切片。 ...

June 10, 2023 · 2 min · jiezi

关于go:切片比数组好用在哪

1. 引言在Go语言中,数组和切片都是常见的数据结构,它们常常被用于存储数据,能够互相替换。本文将介绍Go语言中数组和切片的基本概念,同时具体探讨切片的劣势。从而可能充沛的了解切片绝对于数组的长处,更好得对切片进行应用。 2. 根本介绍2.1 数组数组是一种固定长度、具备雷同类型的元素序列。在Go语言中,数组的长度在创立时确定,并且无奈动静增长或放大。数组的申明形式为var name [size]Type,其中name是数组的标识符,size是数组的长度,Type是数组存储的元素类型,上面是数组应用的根本示例: package mainimport "fmt"func main() { // 申明一个整数数组 var numbers [2]int // 初始化数组元素 numbers[0] = 1 numbers[1] = 2 // 拜访数组元素 fmt.Println("数组中的元素:", numbers[0], numbers[1])}在下面的例子中,咱们定义了一个长度为2的整数数组,别离对其对其赋值和拜访。 2.2 切片Go语言中的切片实际上是对底层数组的一个援用。切片的长度能够动静扭转,而且能够通过切片表达式或内置的append和copy函数对切片进行操作。切片的申明形式为var name []Type,其中name是切片的标识符,Type是切片存储的元素类型,上面是切片应用的一个根本的例子: package mainimport "fmt"func main() { // 申明一个整数切片 var numbers []int // 赋值切片 numbers = []int{1, 2} // 拜访切片元素 fmt.Println("切片中的元素:", numbers[0], numbers[1]) }2.3 总述看起来数组和切片在定义和应用上有些类似,但它们在长度、内存调配、大小调整和传递形式等方面存在重要的区别。接下来,咱们将探讨切片绝对于数组的劣势,并解释为何在许多状况下抉择切片更加适合。 3. 切片劣势3.1 动静长度切片在Go语言中具备动静增长和放大的能力,这是切片绝对于数组的重要劣势之一。通过动静调整切片的长度,咱们能够依据须要无效地解决和治理数据。 在Go语言中,咱们能够应用内置的append函数向切片中增加元素。append函数承受一个切片和一个或多个元素作为参数,并返回一个新的切片,其中蕴含原切片的所有元素以及增加的新元素。如果切片的容量不足以包容新元素,append函数会主动进行内存调配并扩大底层数组的大小,以包容更多的元素。 以下是一个示例,演示了如何应用append函数向切片中增加元素: package mainimport "fmt"func main() { slice := []int{1, 2, 3} // 申明一个切片 // 应用 append 函数向切片增加元素 slice = append(slice, 4) slice = append(slice, 5, 6) fmt.Println(slice) // 输入: [1 2 3 4 5 6]}通过反复调用append函数,咱们能够依据须要动静地减少切片的长度,而不用放心底层数组的固定长度。 ...

June 9, 2023 · 2 min · jiezi

关于go:在-Golang-中执行-Shell-命令

原文题目:Executing Shell Commands in Golang](https://www.sohamkamani.com/golang/exec-shell-command/)) 作者:Soham Kamani 之前本人也写过 os/exec 包执行 Shell 命令的文章,然而没有这篇讲的具体,感兴趣能够看看,点此处。在本教程中,咱们将学习如何在 Golang 中执行shell命令(如 ls、mkdir 或 grep )。咱们还将学习如何通过 stdin 和 stdout 传递 I/O 到正在运行的命令,以及治理长时间运行的命令。 如果只是想看代码,能够在 Github 上查看Exec 包咱们能够应用官网的 os/exec 包来运行外部命令。 当咱们执行 shell 命令时,咱们是在 Go 应用程序之外运行代码。为此,咱们须要在子过程中运行这些命令。 每个命令都作为正在运行的 Go 应用程序中的子过程运行,并公开咱们能够用来从过程读取和写入数据的 Stdin 和 Stdout 属性。 运行根本的 Shell 命令要运行一个简略的命令并读取其输入,咱们能够创立一个新的 *exec.Cmd 实例并运行它。在此示例中,让咱们应用 ls 列出当前目录中的文件,并打印代码的输入: // 创立了一个新的 *Cmd 实例// 应用 "ls" 命令和 "./" 参数作为参数cmd := exec.Command("ls", "./")// 应用 `Output` 办法执行该命令并收集其输入out, err := cmd.Output()if err != nil { // 如果执行命令时呈现谬误,则输入错误信息 fmt.Println("could not run command: ", err)}// 否则,输入运行该命令的输入后果fmt.Println("Output: ", string(out))因为我在示例仓库中运行此代码,因而它会打印我的项目根目录中的文件: ...

June 7, 2023 · 3 min · jiezi

关于go:实战分享使用-Go-重构流式日志网关

我的项目背景分享之前,先来简略介绍下该我的项目在流式日志解决链路中所处的地位。 流式日志网关的次要性能是提供 HTTP 接口,接管 CDN 边缘节点上报的各类日志(拜访日志/报错日志/计费日志等),将日志作预处理并分流到多个的 Kafka 集群和 Topic 中。 越来越多的客户要求提供实时日志反对,业务量的减少让机器资源的耗费也一劳永逸,最先暴露出了流式日志解决链路的一大瓶颈——带宽资源。 能够通过给集群裁减更多的机器来晋升集群总传输带宽,但基于老本考量,重中之重是先优化网关程序。 旧版网关我的项目我的项目代号 Chopper ,其基于另一个外部 OpenResty 我的项目框架来开发的。其亮点性能有:反对从 Consul 、Redis 等其余内部零碎热加载配置及动静失效;可能加载 Lua 脚本实现灵便的日志预处理能力。 其 Kafka 生产者客户端基于 doujiang24/lua-resty-kafka 实现。通过实际考验,Chopper 的吞吐量是满足现阶段需要的。 存在的问题要害依赖库的社区活跃度低lua-resty-kafka 的社区活跃度较低,至今依然处在试验阶段;而且它用作 Kafka 生产者客户端目前没有反对消息压缩性能,而这在其余语言实现的 Kafka 客户端中都是规范的选项。 内存应用不节制单实例部署配置 4 核 8 G,仅大量申请拜访后,内存占用就稳固在 2G 而没有开释。 配置文件可维护性差理论线上用到 Consul 作为配置核心,采纳篇幅很长的 JSON 格局配置文件,不利于运维。另外在 Consul 批改配置没有回退性能,是一个高风险操作。好在目前日志网关的性能并不简单,所以咱们决定重构它。 新我的项目启动家喻户晓, Go 语言领有独特的高并发模型、较低的上手难度和丰盛的第三方生态。而且咱们小组成员都有 Go 我的项目的开发教训,所以咱们抉择应用基于 Go 语言的技术栈来从新构建 Chopper 我的项目,所以新我的项目命名为 chopper-go 。 需要梳理及概要设计从新构建一个线上我的项目的根本准则是,性能上要齐全兼容,最好可能实现线上服务的无缝降级替换。 原版外围模块的设计Chopper 的外围性能是将接管到的 HTTP 申请分流到特定 Kafka 集群及其 Topic 中。 一、HTTP 接口局部只凋谢了惟一一个对外的 API ,性能很简略: ...

June 7, 2023 · 2 min · jiezi

关于go:Go121-速览新内置函数-clearminmax-和新标准库包-cmp

大家好,我是煎鱼。 后面给大家分享了 Go1.21 正式不反对 macOS 10.13 和 10.14 的反对。吓得我连忙把我的 2017 款的老爷机从 10.14 升成 13.4。感觉 mbp 曾经变成了暖宝宝。 明天给大家分享的是 Go 1.21 中的两个双新增项,别离是新的 3 个内置办法和新的规范库包。 新内置函数本次新版本新增的内置函数别离是:clear、min、max,面向不同的场景,函数名和函数作用统一,不得不说论命名的艺术。 咱们一个个来开展介绍。 clear 函数引入背景这个 clear 内置函数的退出,真的是等的够久了。在 2022 年的《Go 大佬良心发现,违心给 map 加革除了?》的文章中,咱们有介绍过。 简略来讲,有如下两个问题: Go 始终以来大家就在吵要清空 map 等类型的内容物。须要 for-range + delete 来清空,略繁琐。有一类神奇的值,叫做:NaN(Not a Number,非数)。它是数值数据类型的一类值,示意未定义或不可示意的值。有开发者发现无奈清空 NaN 的值。有 BUG。扯到往年吵来吵去,扩充了原有的范畴。 函数作用最终 Go1.21 新退出的内置函数 clear 承受 map、slice、指向数组的指针或类型参数类型的参数。 函数签名如下: func clear[T ~[]Type | ~map[Type]Type1](t T)别离有如下的作用: 对于 map,会删除所有条目(蕴含后面提到的 NaN),将会变成一个空 map。len 属性的值会扭转,值为 0 。对于 slice,会将 slice 或 array 长度内的所有元素设置为相应元素类型的零值。len 属性的值不会扭转。对于泛型的类型参数(type parameter):类型参数的类型集必须只蕴含 map、slices 或者指针到数组的类型,clear 函数将会执行理论类型参数所对应的 clear 操作。演示代码map 演示代码如下: ...

June 7, 2023 · 3 min · jiezi

关于go:k8s-系列docker回顾k8s-起航

k8s 逐步曾经作为一个程序员不得不学的技术,尤其是做云原生的兄弟们,若你会,那么还是挺难的 学习 k8s ,实际尤为重要,如果身边有本人公司就是做云的,那么云服务器倒是不必放心,若不是,咱们能够在网上购买各大云厂商的服务器,用于学习用于精进本人 如下是 k8s 官网,最好的材料莫过于官网了 点击进入 k8s 官网 现状公司始终在倒退,各种设置也在新陈代谢,当初就有一个很显著的比照 从最开是的 IT 根底设置主机化,转向容器化从以前的人肉运维,甚至是“人工”智能,转化成了自动化的运维模式缓缓的曾经开始演变到全体系的智能化运维了时代在提高,技术在提高,咱们本人也应该自我迭代,更新思维,拥抱变动 容器咱们能够回滚一下之前的容器知识点 容器是什么呢?之前说过 docker 就相似于集装箱,每一个箱子之间做到相互隔离 咱们为了升高以前虚拟机造成的物理资源的节约,也冀望进步物理主机的资源利用率,还心愿可能像虚拟机一样能够让多个利用做到程序隔离,本人玩本人的 像这种轻量级的虚拟机,就是 容器 有需要就有市场,世界是懒人发明的,懒人总会思考更多高效的事件,来让本人既能躺着,还能高效的实现自我价值 虚拟机和容器的比照 对于一台硬件,都会有相应的服务,主机的操作系统,以及一些虚拟化的技术 那么在这台硬件上开多个虚拟机会是这个样子的 每一个虚拟机都要装置一个操作系统,天然是环境隔离的占用资源数据传输的过程中会绝对较慢如果是在这个硬件设施上开容器的话会是这个样子的 硬件上有一个容器治理引擎治理引擎中能够部署多个容器,能够提供环境的隔离每一个容器没有单独的操作系统,不会占用大量的存储资源交付不便,咱们只须要提供镜像进来即可,绝对虚拟机的镜像来说,容器的 size 就小了很多容器的管理工具容器管理工具就像以前的虚拟机管理工具一样,次要是用于容器的创立,启动,编辑,删除和敞开,会有这几个工具: 赫赫有名的 docker ,生态好很多阿里的 Pouch老牌的 LXC ,LXD,RKT 这些容器的编排工具下面的容器管理工具是做容器的根底治理,能做的事件也比拟无限 如果是要对多个容器进行部署,编排,或者是做一些简单的操作的时候,就须要容器的编排工具了,会有这些: docker compose之前咱们分享 docker 的时候,有分享到 docker compose 工具,应用这个工具咱们能够很轻松的应用 yaml 文件来配置咱们的应用服务,编排多个利用 docker machine次要为咱们的容器运行提供一个主机环境 docker swarm次要是用来治理容器主机集群的 mesos 和 marathonmesos 次要是用于分布式计算过程中,对计算机资源的治理和调配的 marathon 次要是实现负载平衡,服务发现等性能 简略来说,mesos 做资源管理,marathon 做服务治理 后面 3 个工具,也被称为 docker 三剑客 kubernetes次要用于治理云平台中多个主机上的容器化利用,指标是让部署容器化的利用简略且高效 ...

June 6, 2023 · 1 min · jiezi

关于go:现代化-protobuf-包管理-七个步骤使用cicd生成第三方包

本教程应用的是二进制的 gitlab-runner 配合 docker 生成 protobuf 的第三方包,并在分支 merge 时主动公布 tag 版本。 筹备一台 Linux 机器,能链接到 gitlab 服务器即可筹备 gitlab 对应版本的 runner 文件,能够去官网找我司的 gitlab 太古老,是 10.5.6,配套的 runer 最多只能到 10.0.2 版本 ,下载地址是:https://gitlab-runner-downloads.s3.amazonaws.com/v10.0.2/bina... 地址是猜出来的。版本对应关系在官网有一个表格,能够参考找表格到本人须要的版本。一. 装置配置 gitlab-runner:1. 在 linux 服务上配置帐号:prepare user for gitlab-runner. We need it run gitlab-runner on Linux system. sudo passwd gitlab-runnersudo gpasswd -a gitlab-runner rootsu gitlab-runner2. 配置 gitlab-runner 用户的 git 权限在 gitlab 上注册一个帐号,记住用户名和邮箱地址,这里假如: 用户名: cicdops邮箱: cicd@mycompany.com应用上述帐号生成一个 Personal access token,给与读写权限。为该用户赋予仓库的读写权限。 在服务器上进入 gitlab-runner 用户的家目录,也就是 /home/gitlab-runner/,创立两个文件并写入配置 ...

June 6, 2023 · 2 min · jiezi

关于go:我为什么放弃Go语言

腾小云导读 你在什么时候会产生“想要放弃用 Go 语言”的念头?兴许是在用 Go 开发过程中,接连不断踩坑的时候。本文作者提炼和总结《100 Go Mistakes and How to Avoid Them》里的精髓内容,并联合本身的工作教训,盘点了 Go 的常见典型谬误,撰写了这篇超全避坑指南。让咱们追随文章,一起重拾用 Go 的信念\~ 目录 1 留神 shadow 变量 2 慎用 init 函数 3 embed types 优缺点 4 Functional Options Pattern 传递参数 5 小心八进制整数 6 float 的精度问题 7 slice 相干留神点 slice 相干留神点 8 留神 range 9 留神 break 作用域 10 defer 11 string 相干 12 interface 类型返回的非 nil 问题 13 Error 14 happens before 保障 ...

June 6, 2023 · 20 min · jiezi

关于go:如何在-Go-中验证一个字符串是否是-URL

前言在理论开发过程中,有时候会遇到 URL 的校验问题,其实我也是间接调用了第三方库,然而也引发了一个思考,Go 语言中有哪些办法去验证一个字符串是否满足 URL 格局呢? URL 代表惟一资源定位符,是 URI 的子类型(只管许多人能够调换应用这两个术语)。URL 是对网络资源的援用,通常被视为网址(例如 https://golang.org)。 上面你能够看到一个 URL 的构造,它合乎 URI 的构造 URI = scheme:[//authority]path[?query][#fragment]authority = [userinfo@]host[:port]官网 URL 包在 Golang 中利用 url.ParseRequestURI 能够简略验证咱们的 URL。 func ParseRequestURI(rawurl string) (*URL, error)ParseRequestURI 将 rawurl 解析成 URL 构造。它假设在 HTTP 申请中接管到 rawurl,因而 rawurl 仅被解释为相对 URI 或绝对路径。假设字符串 rawurl 没有 #fragment 后缀。(Web 浏览器在将 URL 发送到 Web 服务器之前去除 #fragment。)ParseRequestURI 与 Parse还有另一种办法应该用于解析 URL 字符串,但有一些注意事项。它容许绝对 URL 使验证更加宽松。它是url.Parse func Parse(rawurl string) (*URL, error)如文档中所述: Parse 将 rawurl 解析成 URL 构造。 rawurl 能够是绝对的(门路,没有主机)或相对的(以计划结尾)。尝试在没有计划的状况下解析主机名和门路是有效的,但因为解析歧义,不肯定会返回谬误。比方如下的例子: ...

June 6, 2023 · 3 min · jiezi

关于go:20个Golang片段让我不再健忘-京东云技术团队

前言本文应用代码片段的模式来解释在 go 语言开发中常常遇到的小性能点,因为自己次要应用 java 开发,因而会与其作比拟,心愿对大家有所帮忙。 1. hello world新手村的第一课,毋庸置疑。 package mainimport "fmt"func main() { fmt.Printf("hello world")}2. 隐形初始化package mainimport "fmt"func main() { load()}func load() { fmt.Printf("初始化..手动%s 不错\n", "1")}func init() { fmt.Printf("隐形初始化。。\n")}在 go 中定义 init 函数,程序在运行时会主动执行。相似使 junit 的 [@before](https://my.oschina.net/u/3870904) 注解。 3. 多模块的拜访java 中 package 包的概念,go 是通过文件夹 + package 关键字来定义的。 一般而言,咱们会通过go init来创立我的项目,生成的go.mod文件位于根目录。 常见的实际是,创立文件夹并且放弃 package 名称与文件夹保持一致。这样 import 的永远是文件夹,遵循以上规定则意味着文件夹的名称即为模块名。 同一个 package 能够创立多个 .go 文件,尽管散布在不同的文件中。然而他们中的办法名称不能雷同。须要留神,这里与 java中不同类中办法能够重名不同。 此外,也没有诸如private、protected、public等包拜访权限关键字。只有定义的函数首字母为大写。则能够被内部胜利调用。 来看一下示例: go-tour└── ch3 ├── model │ └── test │ │ ├── testNest.go │ └── helper.go │ └── helper2.go │ └── main.go └── go.mod此处,ch3、model、test 均为文件夹,也能够说是 package。helper.go 位于 model 下,它的代码如下: package modelimport "fmt"var AppName = "bot"var appVersion = "1.0.0"func Say() { fmt.Printf("%s", "hello")}func init() { fmt.Printf("%s,%s", AppName, appVersion)}再来看看 main.go ...

June 6, 2023 · 8 min · jiezi

关于go:上游服务不可用了下游服务如何应对

1. 引言在零碎中,上游服务和上游服务是两个要害概念。上游服务通常指的是提供某种性能或数据的服务端,它接管来自上游服务的申请,并依据申请进行解决和响应。上游服务通常指的是发动申请并依赖上游服务的客户端,它们通过发送申请向上游服务申请数据或执行某些操作。 上游服务和上游服务之间的合作是零碎中实现整体性能的要害。上游服务提供了外围的业务逻辑和数据,上游服务则依赖于上游服务来实现其特定工作。 上游服务的稳定性和可用性间接依赖于上游服务的可靠性和性能。如果上游服务不可用或呈现故障,同时上游服务没有采取任何应答措施,此时将可能呈现以下问题: 无奈失常执行工作:上游服务可能无奈执行其性能,因为它依赖于上游服务的数据或资源。这将导致上游服务无奈失常工作。提早和性能降落:如果上游服务不对上游服务的不可用进行适当解决,而是无限期期待或一直尝试申请,零碎的响应工夫会减少,导致性能降落。级联故障:如果上游服务持续发动大量的申请到不可用的上游服务,它可能会导致上游服务资源被全副占用,无奈为其余申请提供服务。这可能导致级联故障,使整个零碎不可用。因而,上游服务应该采取适当的应答措施来解决上游服务不可用的状况,以确保零碎的稳定性和可用性。 2. 状况分类对于上游服务的不可用,此时能够辨别为短暂不可用和长时间不可用两种状况,从而采纳不同的形式来进行解决。 短暂不可用,是指上游服务在一段时间内临时无奈提供失常的服务,通常是因为网络稳定或负载过低等起因导致的。这种状况下,上游服务可能会在很短的工夫内自行复原,并从新可用。短暂不可用通常持续时间较短,能够通过重试机制来解决。上游服务能够在申请失败后进行重试,直到上游服务恢复正常。 长时间不可用,是指上游服务在较长的工夫内无奈提供失常的服务,无奈自行复原或复原工夫较长。这种状况可能是因为重大的故障、零碎解体,或其余长期性问题导致的。在这种状况下,简略地进行无限度的重试可能会导致系统被该申请全副占用,甚至引发级联故障。 短暂不可用和长时间不可用是上游服务不可用的两种常见状况,它们须要不同的应答策略。理解并辨别这两种状况对于确保零碎的稳定性和可用性至关重要。 3. 短暂不可用3.1 解决形式短暂的不可用可能只是临时性的,可能是网络拥塞或其余暂时性问题导致的。此时能够通过重试机制,上游服务能够尝试从新发送申请,减少申请胜利的机会,防止因为零碎的短暂不可用导致申请的失败。 3.2 代码示例重试机制在许多客户端库中曾经成为规范性能。这些客户端库提供了一些配置选项,以管制重试行为。上面是一个应用gRPC的示例代码,展现了如何配置和应用重试机制,而后基于此防止因为零碎的短暂不可用导致申请的失败。 首先展现服务端代码,用于向外提供服务: package mainimport ( "context" "log" "net" pb "path/to/your/protobuf" "google.golang.org/grpc")type server struct { pb.UnimplementedYourServiceServer}func (s *server) YourRPCMethod(ctx context.Context, req *pb.Request) (*pb.Response, error) { // 解决申请逻辑 return &pb.Response{Message: "Hello, world!"}, nil}func main() { listener, err := net.Listen("tcp", ":50051") if err != nil { log.Fatalf("Failed to listen: %v", err) } srv := grpc.NewServer() pb.RegisterYourServiceServer(srv, &server{}) log.Println("Server started") if err := srv.Serve(listener); err != nil { log.Fatalf("Failed to serve: %v", err) }}接下来展现客户端代码,其在服务端呈现谬误时,会进行重试,防止因为服务的短暂不可用,导致申请的失败,保证系统的可用性: ...

June 6, 2023 · 2 min · jiezi

关于go:同步个事儿

前期工作通过一个多月陆陆续续的繁忙与期待: 申请域名填写域名关联个体等信息阿里云初步审核工信部短信核验工信部审核管局审核ICP 备案胜利网安备案审核网站平安报告编写与提交网站平安报告审核网站全副审核通过网站上线目前终于把网站的主体框架搭建实现,局部公众号的内容还在龟速同步中,次要是因为笔者在搬运过程中,会对每篇文章再次浏览审校,在现有的根底上优化文章构造、语句、排版、款式、图片等,同时会批改错别字和链接,最初,出于 SEO 的需要,会为每篇文章从新设置适合的题目、description、summary 等信息。 链接公众号内所有源码剖析标签合集下的文章全副放在了博客外面,链接如下: https://dbwu.tech/公众号内所有 Go 工程化标签、Go 陷阱标签、Go 高性能标所有合集下的文章全副独自放在了一个独立的文档页面,链接如下: https://golang.dbwu.tech/情谊提醒: 点击文章底部的浏览原文能够间接关上博客,首次关上时可能会比较慢,因为服务器在美国。 请读者帮忙最近遇到很多网站盗版本公众号内容,间接将文章 100% 复制粘贴,而且齐全不保留出处,以致前两天笔者对于继续原创之路产生了一些踌躇,目前还在求教有相干教训的敌人如何解决该类问题。如果有遇到本公众号盗版源的敌人请留言告诉一声,感激! 最初欢送读者浏览博客,如果工夫容许的状况下,能够针对性地给出倡议,能够在博客留言,也能够间接在公众号留言,倡议方向包含从内容选题到用户体验等所有方面。

June 5, 2023 · 1 min · jiezi

关于go:String操作

应用下边拜访字符串// 只有英文字符时var str stringstr = "hello"fmt.Println("s0: ", str[0], " string:", string(str[0]))// 中文字符时var str2 stringstr2 = "你好"fmt.Println("s0: ", str2[0], " string:", string(str2[0]))因为字符串时, 能够失常输入下标对应的值, 然而中文字符串时就乱码了, 所以咱们能够应用 for range 的形式拜访; 遍历字符串应用for range遍历string, func main() { str3, err := find(str2, 0) fmt.Println("str3: ", str3, string(str3), "err: ", err)}func find(s string, index int) (rune, error) { for i, v := range s { if i == index { return v, nil } } return rune(0), errors.New("out of range")} ...

June 4, 2023 · 1 min · jiezi

关于go:包含引用类型字段的自定义结构体能作为map的key吗

1. 引言在 Go 语言中,map是一种内置的数据类型,它提供了一种高效的形式来存储和检索数据。map是一种无序的键值对汇合,其中每个键与一个值相关联。应用 map 数据结构能够疾速地依据键找到对应的值,而无需遍历整个汇合。 在 Go 语言中,map 是一种内置的数据类型,能够通过以下形式申明和初始化: m := make(map[keyType]valueType)在应用map时,咱们通常会应用根本数据类型作为键。然而,当咱们须要将自定义的构造体作为键时,就须要思考构造体中是否蕴含援用类型的字段。援用类型是指存储了数据的地址的类型,如指针、切片、字典和通道等。在Go中,援用类型具备动静的个性,可能会被批改或指向新的数据。这就引发了一个问题:是否将蕴含援用类型的自定义构造体作为map的键呢? 2. map的根本模型理解是否将蕴含援用类型的自定义构造体作为map的键这个问题,咱们须要先理解下map的根本模型。在Go语言中,map是应用哈希表、实现的。哈希表是一种以键-值对模式存储数据的数据结构,它通过应用哈希函数将键映射到哈希值。 哈希函数是用于将键映射到哈希值的算法。它承受键作为输出并生成一个固定长度的哈希值。Go语言的 map 应用了外部的哈希函数来计算键的哈希值。 而不同的key通过哈希函数生成的哈希值可能是雷同的,此时便产生了哈希抵触。哈希抵触指的是不同的键通过哈希函数计算后失去雷同的哈希值。因为哈希函数的输入空间远远小于键的输出空间,哈希抵触是不可避免的。此时无奈判断该key是以后哈希表中本来便曾经存在的元素还是因为哈希抵触导致不同的键映射到同一个bucket。 此时便须要判断这两个key是否相等。 因而,在map中,作为map中的key,须要保障其反对比照操作的,可能比拟两个key是否相等。 3. map 键的要求从下面map根本的模型介绍中,咱们理解到,map中的Key须要反对哈希函数的计算,同时键的类型必须反对比照操作。 在map中,计算key的哈希值,是由默认哈希函数实现的,对于map中的key并没有额定的要求。 在map中,判断两个键是否相等是通过调用键类型的相等运算符(==或!=)来实现的,因而key必须确保该类型反对 == 操作。这个要求是由 map 的实现机制决定的。map 外部应用键的相等性来确定键的存储地位和检索值。如果键的类型不可比拟,就无奈进行相等性比拟,从而导致无奈精确地定位键和检索值。 在 Go 中,根本数据类型(如整数、浮点数、字符串)和一些内置类型都是可比拟的,因而它们能够间接用作 map 的键。然而,自定义的构造体作为键时,须要确保构造体的所有字段都是可比拟的类型。如果构造体蕴含援用类型的字段,那么该构造体就不能间接用作 map 的键,因为援用类型不具备简略的相等性比拟。 因而,如果map中的键为自定义类型,同时蕴含援用字段,此时将无奈作为map的键,会间接编译失败,代码示例如下: type Person struct { Name string Age int address []Address}func main() { // 这里会间接编译不通过 m := make(map[Person]int)}其次还有一个例外,那便是自定义构造体中蕴含指针类型的字段,此时其是反对==操作的,然而其是应用指针地址来进行hash计算以及相等性比拟的,有可能咱们了解是同一个key,事实上从map来看并不是,此时非常容易导致谬误,示例如下: type Person struct { Name string Age int address *Address}func main(){ m := make(map[Person]int) p1 := Person{Name: "Alice", Age: 30, address: &Address{city: "beijing"}} p2 := Person{Name: "Alice", Age: 30, address: &Address{city: "beijing"}} m[p1] = 1 m[p2] = 2 // 输入1 fmt.Println(m[p1]) // 输入2 fmt.Println(m[p2])}这里咱们定义了一个Person构造体,蕴含一个指针类型的字段address。创立了两个对象p1和p2,在咱们的了解中,其是同一个对象,事实上在map中为两个两个互不相干的对象,次要起因都是应用地址来进行hash计算以及相等性比拟的。 ...

June 4, 2023 · 1 min · jiezi

关于go:Go-httpTransport-主要参数说明

在 Go 中创立一个 http client 时,个别会应用 Go http 包的 Transport 类型。本文次要阐明 http.Transport 须要关注的主要参数。 http.Transport 类型阐明http.Transport 主要参数DefaultTransport 配置DialContext 简略示例Referencehttp.Transport 类型阐明首先咱们要明确的是,咱们开发 http client 的时候,常常会这么写: client := &http.Client{ Transport: http.DefaultTransport,}但细看能够发现,http.Client 的 Transport 成员类型是 http.RoundTripper,而不是 http.Transport 类型。换句话说,开发者齐全能够摈弃原生的实现,本人定制一份 http.RoundTripper 接口来实现一个残缺的 http 调用。不过这不在本文阐明范畴内。 http.Transport 主要参数该类型通过一系列的参数来决定其行为。请留神的是,同样数据类型的不同参数,其表白的默认值是不同的。 参数作用默认值连贯管制类 Proxy指定应用 http 代理。这里举荐传入 http.ProxyFromEnvironment, 以反对系统配置的 http 代理nil,示意 不应用 任何代理,请留神DialContextTCP 连贯函数。开发者能够简略封装一下,个别能够用来做一些监控或者非凡的地址解析逻辑nil, 应用默认的 http 连贯超时管制类 TLSHandShakeTimeouttime.Duration 类型,示意TLS 握手超时工夫。这里举荐传入一个非零值0, 示意无限度IdleConnTimeouttime.Duration 类型,示意一个连贯在闲暇多久之后敞开。0, 示意不敞开连接数管制类 MaxIdleConns最大闲暇连接数0, 示意无限度MaxIdleConnsPerHost每一个 host 的最大连接数http.DefaultMaxIdleConnsPerHost,即 2缓冲区类 WriteBufferSize写缓冲区的大小4kBReadBufferSize读缓冲区的大小4kB其余 ForceAttemptHTTP2字面意思,是否强制尝试 HTTP2。倡议设置为 truefalse, 不尝试 DefaultTransport 配置http.DefaultTransport 是基于 http.Transport 实现的,其配置阐明如下: ...

June 3, 2023 · 1 min · jiezi

关于go:Go语言如何判断两个对象是否相等

1. 引言在编程中,判断两个对象是否相等是一项常见的工作,同时判断对象是否相等在很多状况下都十分重要,例如: 单元测试:编写单元测试时,常常须要验证函数的输入是否合乎预期,这波及到比拟对象是否相等。数据结构操作:在应用map等数据结构时,可能须要判断两个对象是否相等以进行元素查找、删除或更新等操作。缓存治理:当应用缓存零碎时,须要比拟缓存中存储的数据和期望值是否相等,以确保缓存的一致性和正确性。因而,判断对象是否相等在理论开发中十分常见且具备宽泛的利用场景。在 Go 语言中,对于不同类型的对象,有不同的办法来判断它们是否相等。了解和把握这些办法对于编写高质量的代码十分重要。在接下来的内容中,咱们将具体介绍在 Go 语言中如何判断对象是否相等的办法和技巧。 2. 根本阐明在比拟对象是否相等时,咱们须要依据具体情况抉择适合的办法。对于根本类型,间接应用 == 运算符能够失去正确的后果。对于自定义类型,咱们须要自行定义相等性的规定,通常通过实现相应的办法来进行比拟。此外,在比较复杂的数据结构或援用类型时,须要特地留神相等性的判断形式,以防止出现意外的后果。 值得注意的是,Go 中的相等性比拟也受到数据类型的限度。例如,切片、map和函数类型是不可比拟的,因为它们无奈间接进行值比拟。在比拟蕴含这些类型的自定义构造体时,须要留神应用其余形式来实现值相等的判断。 在接下来的内容中,咱们将深入探讨在 Go 中判断对象是否相等的办法和技巧,帮忙你在理论开发中正确处理对象的相等性比拟。 3. 根本类型的相等性比拟在 Go 语言中,应用 == 运算符能够比拟根本类型的相等性。根本类型包含整数、浮点数、布尔值和字符串等。上面是对于如何应用 == 运算符比拟根本类型相等性的介绍。 对于整数相等性比拟来说,如 int、int8、int16、int32、int64 等,能够间接应用 == 运算符进行比拟。例如:a == b,如果 a 和 b 的值相等,则表达式的后果为 true,否则为 false。上面展现一个简略的代码: a := 10b := 20if a == b { fmt.Println("a and b are equal")} else { fmt.Println("a and b are not equal")}对于浮点数相等性比拟,因为浮点数类型(如 float32 和 float64)存在精度限度,因而间接应用 == 运算符进行比拟可能不精确,举荐应用浮点数比拟函数(如 math.Abs 联合一个小的误差范畴)来判断浮点数的相等性。例如:math.Abs(a - b) < epsilon,其中 epsilon 是一个小的误差范畴,如果两个浮点数的差的绝对值小于 epsilon,则认为它们相等。上面展现一个简略的代码: ...

June 3, 2023 · 3 min · jiezi

关于go:为什么说-Go-语言字符串是不可变的

原文链接: 为什么说 Go 语言字符串是不可变的? 最近有读者留言说,平时在写代码的过程中,是会对字符串进行批改的,但网上都说 Go 语言字符串是不可变的,这是为什么呢? 这个问题自身并不艰难,但对于老手来说的确容易产生困惑,明天就来答复一下。 首先来看看它的底层构造: type stringStruct struct { str unsafe.Pointer len int}和切片的构造很像,只不过少了一个示意容量的 cap 字段。 str:指向一个 []byte 类型的指针len:字符串的长度所以,当咱们定义一个字符串: s := "Hello World"那么它在内存中存储是这样的: 当咱们在程序中对字符串进行从新赋值时,比方这样: s := "Hello World"s = "Hello AlwaysBeta"底层的存储就变成了这样: Go 实际上是从新创立了一个 []byte{} 切片,而后让指针指向了新的地址。 更间接一点,咱们间接批改字符串中的单个字符,比方: s := "Hello World"s[0] = 'h'这样做的话,会间接报错: cannot assign to s[0] (strings are immutable)如果肯定要这么做的话,须要对字符串进行一个转换,转换成 []byte 类型,批改之后再转换回 string 类型: s := "Hello World"sBytes := []byte(s)sBytes[0] = 'h's = string(sBytes)这样就能够了。 以上就是本文的全部内容,如果感觉还不错的话欢送点赞,转发和关注,感激反对。 ...

June 2, 2023 · 1 min · jiezi

关于go:Go语言中实现RSA加解密签名验证算法

随着互联网的高速倒退,人们对平安的要求也越来越高。密码学中两大经典算法,一个是对称加解密,另一个是非对称加解密,这里就来分享一下非对称加密算法的代表:RSA加解密。 在Go语言中实现RSA加解密还是比较简单的,网上很多教程都是基于Go原生规范库写的,代码量较多。这里分享一个好用的库:https://github.com/forgoer/openssl 。 装置go get https://github.com/forgoer/openssl秘钥生成秘钥能够生成在文件里,也是生成到Buffer里,只有实现了io.Writer即可。 import ( "io/ioutil" "os" "github.com/forgoer/openssl")func main() { // 创立私钥文件 priFile, err := os.Create("private.key") // 也能够应用buffer, priBuf := bytes.NewBuffer(nil) if err != nil { panic(err) } defer priFile.Close() // 生成私钥到文件 _ = openssl.RSAGenerateKey(1024, priFile) // 创立公钥文件 pubFile, err := os.Create("public.key") if err != nil { panic(err) } defer priFile.Close() // 通过私钥生成公钥到文件 priByte, _ := ioutil.ReadFile("private.key") _ = openssl.RSAGeneratePublicKey(priByte, pubFile)}加解密应用公钥加密,私钥解密。 src := []byte("123456") pubByte, _ := ioutil.ReadFile("public.key") // 公钥加密 dst, _ := openssl.RSAEncrypt(src, pubByte) priByte, _ := ioutil.ReadFile("private.key") // 私钥解密 res, _ := openssl.RSADecrypt(dst, priByte) fmt.Println(string(res)) // 123456签名及验证应用私钥签名,公钥验证。 ...

June 2, 2023 · 1 min · jiezi

关于go:从0到1安装启动Kratos框架

Kratos 是一套轻量级 Go 微服务框架,蕴含大量微服务相干框架及工具,上面介绍一下Kratos的装置启动过程。1、环境筹备1.1、装置Go办法一:pkg包装置装置 - Go官方网站 装置go1.18以上的版本即可,留神抉择适合本人电脑芯片的安装包,点击下一步即可 该安装包会将Go发行版装置到 /usr/local/go 中 办法二:homebrew装置brew install go brew list go能够查看go的装置目录 查看是否装置胜利go --version 查看环境信息go env 开启go mod go env -w GO111MODULE=on 配置环境变量查看以后shell是zsh还是bash, 如果是bash抉择编辑~/.bash_profile文件,如果是zsh抉择编辑~/.zshrc 文件 dscl . -read /Users/$USER UserShell GOROOT门路是go的装置门路,个别是/usr/local/go或者/usr/local/Cellar/go/1.18/libexec GOPATH是Go我的项目的工作目录,能够自定义,这里指定为$HOME/go GOPROXY是go get 下载依赖时应用的代理地址列表,应用逗号 (,) 或竖杠 (|) 分隔。可能减速下载第三方包 GOPRIVATE是公有仓库,个别是公司内网的仓库地址 将$GOPATH/bin退出 $PATH 变量,这样在终端的任何门路都能应用go包的bin目录上面的工具 关上bash_profile$ open .bash_profile如果不存在创立bash_profile$ vim ~/.bash_profile编辑:export GOROOT=/usr/local/goexport GOPATH=$HOME/goexport GO111MODULE=onexport GOPROXY=http://goproxy.cn,direct export GOPRIVATE=git.xxxx.cn/*export PATH=$GOROOT/bin:$GOPATH/bin:$PATH失效环境变量 source ~/.bash_profile # 如果是bashsource ~/.zshrc # 如果是zsh1.2、装置protoc、protoc-gen-go简介gRPC 是一个高性能、开源、通用的RPC框架,由Google推出,基于HTTP2协定规范设计开发,默认采纳Protocol Buffers数据序列化协定,反对多种开发语言。Protocol Buffers(Protobuf)是一种与语言、平台无关,可扩大的序列化结构化数据的数据描述语言,咱们经常称其为 IDL,罕用于通信协议,数据存储等等,相较于 JSON、XML,它更小、更快。在 gRPC 开发中,咱们经常须要与 Protobuf 进行打交道,而在编写了.proto 文件后,咱们会须要到一个编译器,那就是 protoc,protoc 是 Protobuf 的编译器,是用 C++ 所编写的,其次要性能是用于编译.proto 文件。针对不同的语言,还须要不同的运行时的 protoc 插件,那么对应 Go 语言就是 protoc-gen-go 插件 ...

June 2, 2023 · 1 min · jiezi

关于go:如何使用Go中的Weighted实现资源管理

1. 简介本文将介绍 Go 语言中的 Weighted 并发原语,包含 Weighted 的根本应用办法、实现原理、应用注意事项等内容。可能更好地了解和利用 Weighted 来实现资源的治理,从而进步程序的稳定性。 2. 问题引入在微服务架构中,咱们的服务节点负责接管其余节点的申请,并提供相应的性能和数据。比方账户服务,其余服务须要获取账户信息,都会通过rpc申请向账户服务发动申请。 这些服务节点通常以集群的形式部署在服务器上,用于解决大量的并发申请。每个服务器都有其解决能力的下限,超过该下限可能导致性能降落甚至解体。 在部署服务时,通常会评估服务的并发量,并为其调配适当的资源以解决预期的申请负载。然而,在微服务架构中,存在着上游服务申请上游服务的场景。如果上游服务在某些状况下没有正确思考并发量,或者因为某些异常情况导致大量申请发送给上游服务,那么上游服务可能面临超过其解决能力的问题。这可能导致上游服务的响应工夫减少,甚至无奈失常解决申请,进而影响整个零碎的稳定性和可用性。上面用一个简略的代码来阐明一下: package mainimport ( "fmt" "net/http" "sync")func main() { // 启动上游服务,用于解决申请 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { // 模仿上游服务的解决逻辑 // ... // 实现申请解决后,从期待组中删除一个期待 wg.Done() }) // 启动上游服务的 HTTP 服务器 http.ListenAndServe(":8080", nil)}这里启动一个简略的HTTP服务器,由其来模仿上游服务,来接管上游服务的申请。上面咱们启动一个简略的程序,由其来模仿上游服务发送申请: func main() { // 创立一个期待组,用于期待所有申请实现 var wg sync.WaitGroup // 模仿上游服务发送大量申请给上游服务 go func() { for i := 0; i < 1000000; i++ { wg.Add(1) go sendRequest(&wg) } }() // 期待所有申请实现 wg.Wait()}func sendRequest(wg *sync.WaitGroup) { // 模仿上游服务发送申请给上游服务 resp, err := http.Get("http://localhost:8080/") if err != nil { fmt.Println("申请失败:", err) } else { fmt.Println("申请胜利:", resp.Status) } // 申请实现后,告诉期待组 wg.Done()}这里,咱们同时启动了1000000个协程同时往HTTP服务器发送申请,如果服务器配置不够高,亦或者是申请量更多的状况下,曾经超过了服务器的解决下限,服务器没有主够的资源去解决这些申请,此时将有可能间接将服务器打挂掉,服务间接不可用。在这种状况下,如果因为上游服务的问题,导致上游服务,甚至整个链路的零碎都间接解体,这个是不合理的,此时须要有一些伎俩爱护上游服务因为异样流量导致整个零碎的解体。 ...

May 31, 2023 · 4 min · jiezi

关于go:MoE-系列五|Envoy-Go-扩展之内存安全

后面几篇介绍了 Envoy Go 扩大的根本用法,接下来几篇将介绍实现机制和原理。 Envoy 是 C++ 实现的,那 Envoy Go 扩大,实质上就相当于把 Go 语言嵌入 C++ 里了。 在 Go 圈里,将 Go 当做嵌入式语言来用的,貌似并不太多见,这外面细节还是比拟多的。比方: Envoy 有一套本人的内存管理机制,而 Go 又是一门自带 GC 的语言。Envoy 是基于 libevent 封装的事件驱动,而 Go 又是蕴含了抢占式的协程调度。为了升高用户开发时的心智累赘,咱们提供了三种平安保障。有了这三层保障,用户写 Go 来扩大 Envoy 的时候,就能够像平时写 Go 代码一样简略,而不用关怀这些底层细节。 三种平安1. 内存平安用户通过 API 获取到的内存对象,能够当做一般的 Go 对象来应用。 比方,通过 Headers.Get 失去的字符串,在申请完结之后还能够应用,而不必放心申请曾经在 Envoy 侧完结了,导致这个字符串被提前开释了。 2. 并发平安当启用协程的时候,咱们的 Go 代码将会运行在另外的 Go 线程上,而不是在以后的 Envoy worker 线程上,此时对于同一个申请,则存在 Envoy worker 线程和 Go 线程的并发。 然而,用户并不需要关怀这个细节,咱们提供的 API 都是并发平安的,用户能够不感知并发的存在。 3. 沙箱平安这一条是针对宿主 Envoy 的保障,因为咱们并不心愿某一个 Go 扩大的异样,把整个 Envoy 过程搞解体。 ...

May 31, 2023 · 1 min · jiezi

关于go:使用embed在go中嵌入静态htmljscss文件

前言在Go语言中,能够应用embed包嵌入动态文件。embed包是Go 1.16版本引入的个性,它反对将动态文件打包到可执行程序中,这样就不用内部依赖资源文件了。 用途举例:go联合html打包桌面利用 用法1.文件目录: |- main.go|- web |- index.html |- assets |- index.js |- index.css2.go文件 package mainimport ( "embed" "fmt" "net/http")//go:embed web/index.htmlvar staticFiles embed.FS//go:embed web/assets/*var assetsFiles embed.FSfunc main() { http.HandleFunc("/", serveIndex) files, err := assetsFiles.ReadDir("web/assets") if err != nil { panic(err) } for _, file := range files { fileName := file.Name() fmt.Println("/assets/" + fileName) http.HandleFunc("/assets/"+fileName, handleAssets(fileName)) } fmt.Println("Listen:8080...") err = http.ListenAndServe(":8080", nil) if err != nil { fmt.Println(err.Error()) }}func serveIndex(w http.ResponseWriter, r *http.Request) { indexHTML, err := staticFiles.ReadFile("web/index.html") if err != nil { http.Error(w, "404 Not Found", http.StatusNotFound) return } fmt.Fprintf(w, string(indexHTML))}func serveStatic(w http.ResponseWriter, r *http.Request) { staticServer := http.FileServer(http.FS(assetsFiles)) http.StripPrefix("/assets/", staticServer).ServeHTTP(w, r)}func handleAssets(fileName string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { indexHTML, err := assetsFiles.ReadFile("web/assets/" + fileName) if err != nil { // http.Error(w, "404 Not Found", http.StatusNotFound) fmt.Println(err.Error()) return } fmt.Fprintf(w, string(indexHTML)) }}3.调试拜访:http://localhost:8080/查看html,js,css文件是否被正确援用 ...

May 30, 2023 · 1 min · jiezi

关于go:Defer

defer 简介defer 艰深来讲就是提早调用。defer 会在以后函数返回之前执行 defer 注册的函数。例如: defer func_x() 这样语句会让你注册一个函数变量到 defer 的全局链表中,在 defer 语句所在的函数退出之前调用。 次要应用场景panic 场景:这个是重要的一个个性,通常能简化咱们的代码,确保无论任何场景,defer 的函数肯定调用,通常用在锁或者资源的开释场景较多;配套的两个行为代码能够放在最近的地位:创立&开释、加锁&放锁、前置&后置,使得代码更易读,编程体验优良。最近的中央是哪里?下一行 提早调用package mainfunc main() { defer println("--- defer ---") println("--- end ---")}defer 会在main函数之前调用。外围要点: 提早调用:defer 语句自身尽管是 main 的第一行,然而 fmt.Println 是先打印的;defer 关键字肯定是处于函数上下文:defer 必须放在函数外部 LIFO一个函数内有多个defer时,压栈式执行,后入先出。 package mainimport "fmt"func main() { defer func() { fmt.Println("1") }() defer func() { fmt.Println("2") }()} 异样场景留神:肯定要先注册defer,放在函数第一行,放到如下代码return之前,是无奈捕捉异样的; package mainimport "fmt"func main() { defer func() { if err := recover(); err != nil { fmt.Println("捕捉panic", err) } }() var sliceOne []int sliceOne[0] = 1 return} ...

May 29, 2023 · 1 min · jiezi

关于go:Go121-速览正式结束对-macOS-1013-和-1014-的支持

大家好,我是煎鱼。 依据 Go 语言的版本公布法则,个别是 2 月份和 8 月份各会公布一个新的版本。以后是 Go1.20。也就是在 8 月份会公布 Go1.21 这一个新版本。 在这个新版本,将会正式的完结对 macOS 10.13 和 10.14 的反对,并禁用相应的构建器。 该次变更的提案状态已为 “Accepted”,流程已走完。个别状况下,不会产生其余扭转。 但这个事。怎么再次拿进去探讨呢? 这是有背景的,因为在之前 Go1.20 公布这个音讯后。国外社区和 golang/issues 里产生了不少的拥护的声音,认为须要持续反对这两个大版本。 次要起因是: High Sierra(OSX 10.13.6) 是在旧硬件上运行的最初一个可用版本,例如:2015 年的 MacBookPro。会对其集体应用 Go 最新的版本个性产生影响。(via @glycerine)同样冀望 High Sierra 持续反对,起因是目前在 eBay 上有超过 300 台 Mac mini。将会导致无奈在这项 macOS 上测试和运行更加新的 Go 代码。(via @mikeschinkel)你会发现 macOS 10.13 和 10.14 其实是两个有里程碑意义的版本。失去对应的新个性反对,是有局部开发者不违心的。(身边也有在用老式笔记本电脑工作的敌人) 无论如何,尽管 Go 外围团队成员 @Russ Cox,间接基于 Go 申明的移植策略(PortingPolicy)规范中的 Removing old operating system and architecture versions,间接回绝了此反对。 ...

May 29, 2023 · 1 min · jiezi

关于go:go-反射的基础应用

在golang中,反射是一种机制,它容许在程序运行时查看、摸索和批改变量。在反射中,咱们能够应用 reflect 包的函数来读取变量的类型、值和办法。 package mainimport ( "fmt" "reflect")type Person struct { Name string Age int}func main() { var fruit = []string{"apple", "banana", "orange"} var p = Person{Name: "John", Age: 30} fmt.Println("利用反射来获取变量的类型和对应的值:") fmt.Println("Type:", reflect.TypeOf(fruit)) fmt.Println("Value:", reflect.ValueOf(fruit)) fmt.Println("------------------------------") fmt.Println("利用反射来获取变量名称:") for i := 0; i < reflect.TypeOf(&p).Elem().NumField(); i++ { fmt.Println("变量名称: ", reflect.TypeOf(&p).Elem().Field(i).Name) } fmt.Println("------------------------------") fmt.Println("利用反射来批改构造体中属性的值:") v := reflect.ValueOf(&p).Elem() v.FieldByName("Name").SetString("lewis") v.FieldByName("Age").SetInt(20) fmt.Printf("Name: %s, Age: %d\n", p.Name, p.Age)}输入后果: 利用反射来获取变量的类型和对应的值:Type: []stringValue: [apple banana orange]------------------------------利用反射来获取变量名称:变量名称: Name变量名称: Age------------------------------利用反射来批改构造体中属性的值:Name: lewis, Age: 20原文作者:蓝色瞳仁版权申明:转载请注明作者和链接。 ...

May 28, 2023 · 1 min · jiezi

关于go:go语言中如何实现同步操作呢

1. 简介本文探讨了并发编程中的同步操作,讲述了为何须要同步以及两种常见的实现形式:sync.Cond和通道。通过比拟它们的实用场景,读者能够更好地理解何时抉择应用不同的同步形式。本文旨在帮忙读者了解同步操作的重要性以及抉择适合的同步机制来确保多个协程之间的正确协调和数据共享的一致性。 2. 为什么须要同步操作2.1 为什么须要同步操作这里举一个简略的图像处理场景来阐明。工作A负责加载图像,工作B负责对已加载的图像进行解决。这两个工作将在两个并发协程中同时启动,实现并行执行。然而,这两个工作之间存在一种依赖关系:只有当图像加载实现后,工作B能力平安地执行图像处理操作。 在这种状况下,咱们须要对这两个工作进行协调和同步。工作B须要确保在解决已加载的图像之前,工作A曾经实现了图像加载操作。通过应用适当的同步机制来确保工作B在图像准备就绪后再进行解决,从而防止数据不一致性和并发拜访谬误的问题。 事实上,在咱们的开发过程中,常常会遇到这种须要同步的场景,所以理解同步操作的实现形式是必不可少的,上面咱们来认真介绍。 2.2 如何实现同步操作呢通过下面的例子,咱们晓得当多协程工作存在依赖关系时,同步操作是必不可免的,那如何实现同步操作呢?这里的一个简略想法,便是采纳一个简略的条件变量,一直采纳轮询的形式来查看事件是否曾经产生或条件是否满足,此时便可实现简略的同步操作。代码示例如下: package mainimport ( "fmt" "time")var condition boolfunc waitForCondition() { for !condition { // 轮询条件是否满足 time.Sleep(time.Millisecond * 100) } fmt.Println("Condition is satisfied")}func main() { go waitForCondition() time.Sleep(time.Second) condition = true // 批改条件 time.Sleep(time.Second)}在上述代码中,waitForCondition 函数通过轮询形式查看条件是否满足。当条件满足时,才继续执行上来。 然而这种轮训的形式其实存在一些毛病,首先是资源节约,轮询会耗费大量的 CPU 资源,因为协程须要一直地执行循环来查看条件。这会导致 CPU 使用率升高,节约系统资源,其次是提早,轮询形式无奈及时响应条件的变动。如果条件在循环的某个工夫点满足,但轮询查看的时机未到,则会提早对条件的响应。最初轮询形式可能导致协程的执行效率升高。因为协程须要在循环中一直查看条件,无奈进行其余有意义的工作。 既然通过轮训一个条件变量来实现同步操作存在这些问题。那go语言中,是否存在更好的实现形式,能够防止轮询形式带来的问题,提供更高效、及时响应的同步机制。其实是有的,sync.Cond 和channel便是两个能够实现同步操作的原语。 3.实现形式3.1 sync.Cond实现同步操作应用sync.Cond实现同步操作的办法,能够参考sync.Cond 这篇文章,也能够依照能够依照以下步骤进行: 创立一个条件变量:应用sync.NewCond函数创立一个sync.Cond类型的条件变量,并传入一个互斥锁作为参数。在期待条件满足的代码块中应用Wait办法:在须要期待条件满足的代码块中,调用条件变量的Wait办法,这会使以后协程进入期待状态,并开释之前获取的互斥锁。在满足条件的代码块中应用Signal或Broadcast办法:在满足条件的代码块中,能够应用Signal办法来唤醒一个期待的协程,或者应用Broadcast办法来唤醒所有期待的协程。上面是一个简略的例子,演示如何应用sync.Cond实现同步操作: package mainimport ( "fmt" "sync" "time")func main() { var cond = sync.NewCond(&sync.Mutex{}) var ready bool // 期待条件满足的协程 go func() { fmt.Println("期待条件满足...") cond.L.Lock() for !ready { cond.Wait() } fmt.Println("条件已满足") cond.L.Unlock() }() // 模仿一段耗时的操作 time.Sleep(time.Second) // 扭转条件并告诉期待的协程 cond.L.Lock() ready = true cond.Signal() cond.L.Unlock() // 期待一段时间,以便察看后果 time.Sleep(time.Second)}在下面的例子中,咱们创立了一个条件变量cond,并定义了一个布尔型变量ready作为条件。在期待条件满足的协程中,通过调用Wait办法期待条件的满足。在主协程中,通过扭转条件并调用Signal办法来告诉期待的协程条件已满足。在期待协程被唤醒后,输入"条件已满足"的音讯。 ...

May 27, 2023 · 2 min · jiezi

关于go:Go-语言-map-如何顺序读取

原文链接: Go 语言 map 如何程序读取? Go 语言中的 map 是一种十分弱小的数据结构,它容许咱们疾速地存储和检索键值对。 然而,当咱们遍历 map 时,会有一个乏味的景象,那就是输入的键值对程序是不确定的。 景象先看一段代码示例: package mainimport "fmt"func main() { m := map[string]int{ "apple": 1, "banana": 2, "orange": 3, } for k, v := range m { fmt.Printf("key=%s, value=%d\n", k, v) }}当咱们多执行几次这段代码时,就会发现,输入的程序是不同的。 起因首先,Go 语言 map 的底层实现是哈希表,在进行插入时,会对 key 进行 hash 运算。这也就导致了数据不是按顺序存储的,和遍历的程序也就会不统一。 第二,map 在扩容后,会产生 key 的搬迁,原来落在同一个 bucket 中的 key,搬迁后,有些 key 可能就到其余 bucket 了。 而遍历的过程,就是按程序遍历 bucket,同时按程序遍历 bucket 中的 key。 搬迁后,key 的地位产生了重大的变动,有些 key 被搬走了,有些 key 则原地不动。这样,遍历 map 的后果就不可能按原来的程序了。 ...

May 27, 2023 · 1 min · jiezi

关于go:Go-语言-map-是并发安全的吗

原文链接: Go 语言 map 是并发平安的吗? Go 语言中的 map 是一个十分罕用的数据结构,它容许咱们疾速地存储和检索键值对。然而,在并发场景下应用 map 时,还是有一些问题须要留神的。 本文将探讨 Go 语言中的 map 是否是并发平安的,并提供三种计划来解决并发问题。 先来答复一下题目的问题,答案就是并发不平安。 看一段代码示例,当两个 goroutine 同时对同一个 map 进行写操作时,会产生什么? package mainimport "sync"func main() { m := make(map[string]int) m["foo"] = 1 var wg sync.WaitGroup wg.Add(2) go func() { for i := 0; i < 1000; i++ { m["foo"]++ } wg.Done() }() go func() { for i := 0; i < 1000; i++ { m["foo"]++ } wg.Done() }() wg.Wait()}在这个例子中,咱们能够看到,两个 goroutine 将尝试同时对 map 进行写入。运行这个程序时,咱们将看到一个谬误: ...

May 27, 2023 · 3 min · jiezi

关于go:深入Go底层原理重写Redis中间件实战山回路转不见君

download:深刻Go底层原理,重写Redis中间件实战Elasticsearch搜寻我的项目:构建高效搜索引擎的关键技术 H1:引言 随着大数据时代的到来,搜索引擎为解决海量数据和提供高效搜寻能力的关键技术。Elasticsearch作为一种开源的散发搜寻和剖析引擎,曾经在各个领域失去广泛应用。本文将具体探讨Elasticsearch搜寻我的项目的关键技术和利用,为读者提供构建高效搜索引擎的指南。 H1: Elasticsearch的根底原理 Elasticsearch是基于Lucene的分组搜索引擎,具备疾速、可扩大和高可用性的特点。它采纳倒排搜寻的数据结构,将文档中的关键词信息进行搜寻和剖析,以实现疾速的全文搜寻和高级搜寻性能。倒排搜索引擎能够疾速确定地位文档和关键词的关联,提供高搜寻效率。 H1: Elasticsearch的内核组件 索引(Index):索引是Elasticsearch的外围组件之一,用于存储和组织文件数据。每个索引能够蕴含多个文件类型,每个文件类型能够蕴含含多个文档。通过确定正义索引的影像和片段机器,能够对数据进行分区存储和解决。 文档(Document):文档是Elasticsearch中最根底的数据元。它能够是一个JSON对象,蕴含各种字符段和属性。每个文档都有一个惟一的ID,用于标记识和查。 映射(Mapping):映射定义了文档中字符段的类型和属性。通过映射,能够控制字符段的剖析、搜寻和存储形式。映射还能够定义映射本段的分词器和过滤器,以不便进行全文搜寻和相关性排列。 查问(Query):查问是搜索引擎中最外围的性能之一。Elasticsearch提供丰盛的查询语言法和查问类型,包含全文查问、准确查问、范例围查问、联结查问等。通过灵便的查问性能,能够实现精确和高效的搜寻。 分布式框架:Elasticsearch具备良好的分布式框架,反对程度扩大和容错性。它能够通过多个节点和分片来解决大规格模型数据,提供高搜寻性能和可用性。 H1: Elasticsearch的搜寻优化 为了提供更高的搜寻性能和效率,Elasticsearch提供了一些搜寻优化技术: 倒排索引压缩:倒排索引占用大量存储空间,为了缩小小索引的大,Elasticsearch应用了一些压缩

May 26, 2023 · 1 min · jiezi

关于go:Go中Gzip-与-json-搭配使用压缩数据

在日常工作中,如果遇到数据量大的状况,在db中是不能间接存储某些字段的,个别会用json进行marshal为 byte存入。然而如果此时占用空间仍旧过大,则能够思考再用gzip 还进一步压缩。 package mainimport ( "bytes" "compress/gzip" "encoding/json")func main() {}type anyStruct struct {}// 压缩 与json搭配应用func MarshalToJsonWithGzip(jsonData anyStruct) []byte { dataAfterMarshal, _ := json.Marshal(jsonData) dataAfterGzip, err := Encode(dataAfterMarshal) if err != nil { return nil } return dataAfterGzip}// 解压 与json搭配应用func UnmarshalDataFromJsonWithGzip(msg []byte) (*anyStruct, error) { dataAfterDecode, err := Decode(msg) if err != nil { return nil, err } data := &anyStruct{} err = json.Unmarshal(dataAfterDecode, data) if err != nil { return nil, err } return data, nil}// Gzip用法 压缩数据func Encode(input []byte) ([]byte, error) { // 创立一个新的 byte 输入流 var buf bytes.Buffer // 创立一个新的 gzip 输入流 gzipWriter := gzip.NewWriter(&buf) // 将 input byte 数组写入到此输入流中 _, err := gzipWriter.Write(input) if err != nil { _ = gzipWriter.Close() return nil, err } if err := gzipWriter.Close(); err != nil { return nil, err } // 返回压缩后的 bytes 数组 return buf.Bytes(), nil}// Gzip用法 解压数据func Decode(input []byte) ([]byte, error) { // 创立一个新的 gzip.Reader bytesReader := bytes.NewReader(input) gzipReader, err := gzip.NewReader(bytesReader) if err != nil { return nil, err } defer func() { // defer 中敞开 gzipReader _ = gzipReader.Close() }() buf := new(bytes.Buffer) // 从 Reader 中读取出数据 if _, err := buf.ReadFrom(gzipReader); err != nil { return nil, err } return buf.Bytes(), nil}

May 24, 2023 · 1 min · jiezi

关于go:健身组合计划

健身组合打算 1.单臂哑铃俯身划船单臂哑铃俯身划船是众动作外面最为实用的,能够通过它来锤炼各部位肌肉,例如:手臂、外围、背部等。首先以俯卧撑起始动作为开始,而后双手各拎一个哑铃,一开始能够拎比拟轻的进行锤炼。 2.深蹲或哑铃深蹲 1.双脚站立,略宽于肩,脚尖外展约90度,放弃挺胸直背,腹部收紧。 2.锁住双肩,屈髋屈膝下蹲,直至大腿与高空平行,身材不要适度前倾,膝关节不要超过脚尖,也不要内扣,脚跟不要抬离高空。 3.下蹲并疾速站起,同时呼气,回到起始地位,反复规定次数。https://www.cw.com.tw/article/51011853.腹肌训练上面分享一组迷信的虐腹训练,学习动作规范,隔天训练一次即可,保持2个月练出清晰的腹肌线条!动作@1、仰卧卷腹保持15次,反复4组 @2.仰卧单车保持15次,反复4组@3.登山跑保持30秒,反复4组 @4.侧撑持转胯每侧15次,反复2组 4.以下动作二抉择一@1.平板开合平板撑持的状态,双腿交替开合训练,能够锤炼下腹部肌群。 @2.平板撑持 5.俯卧撑 6.波比跳 7.引体向上+弹力带

May 23, 2023 · 1 min · jiezi

关于go:MOSN-基于延迟负载均衡算法走得更快期待走得更稳

文|纪卓志(GitHub ID:jizhuozhi) 京东高级开发工程师 MOSN 我的项目 Committer 专一于云原生网关研发的相干工作,长期投入在负载平衡和流量管制畛域 前言这篇文章次要是介绍 MOSN 在 v1.5.0 中新引入的基于提早的负载平衡算法#2253。首先会对分布式系统中提早呈现的起因进行分析,之后介绍 MOSN 都通过哪些办法来升高提早,最初构建与生产环境性能散布相近的测试用例来对算法进行验证。 在开始聊基于提早的负载平衡算法之前,咱们先介绍下什么是负载平衡。 什么是负载平衡Wikipedia中 Load Balancing (Computing)) 词条是这样介绍负载平衡的: 负载平衡是将一组任务分配到一组资源(计算单元)上的过程,目标是使它们的整体解决更有效率。负载平衡能够优化响应工夫,防止负载不平均导致一些计算节点过载而其余计算节点处于闲暇状态负载平衡在大型分布式系统中是要害的组成部分。负载平衡解决了分布式系统中最重要的两个问题:可伸缩性 (Scalability) 和韧性 (Resilience) 。 可伸缩性:应用程序部署在多个雷同的正本中。当计算资源有余时能够通过部署额定的副原本减少计算资源,而当计算资源大量冗余时能够通过缩小副原本节省成本。通过负载平衡能够将申请负载散布到不同的正本中。韧性:分布式系统的故障是局部的。应用程序通过冗余正本的形式,保障在局部组件故障时仍能失常地提供服务。负载平衡通过感知节点的故障,调整流量的调配,将流量更多的调配到那些可能失常提供服务的节点上。走得更快负载平衡使得古代软件系统具备了可扩展性和韧性。但在分布式系统中还存在不容忽视的问题:提早。 提早来自哪里古代软件系统通常是多层级构造大型分布式系统,即便是只服务单个终端用户的申请,它背地也有可能通过了上百次的数据拜访,这种状况在微服务架构中更是尤为广泛。 微服务架构 (援用自 Microservices Pattern) 单台性能稳固的服务器中提早通常由以下几个方面造成: 计算工作自身的复杂度内容的传输过程中的提早申请排队期待的提早后台任务流动所导的资源竞争这些服务器之间的提早将会叠加,任何显著的提早减少都会影响终端用户的体验。此外,任何来自单个节点的提早峰值也会间接影响到终端用户体验。同时越来越多地应用私有云部署应用程序也进一步加剧了响应工夫的不可预测性。因为在这些环境中存在共享资源 (CPU、内存和 IO) 的争用,应用程序机简直不可避免地遇到性能影响,而这种影响是随时产生的。 如何缩小提早有钻研表明,在大型互联网利用中,提早往往具备长尾特点,P99 比中位数高出几个数量级。如果在利用架构的每层都可能缩小这些尾部提早,那么对终端用户整体的尾部提早将会显著升高。 在服务网格中,所有接管和发送的流量都会通过边车代理,通过边车代理能够轻松地管制网格的流量,而无需对服务进行任何批改。如果边车代理在对应用层流量进行转发时,总是通过负载平衡时选择响应工夫较短的服务器,那么将会显著升高对终端用户的尾部提早。 基于此,咱们筹备开始为 MOSN 引入基于提早的负载平衡算法,并进行适当调整来保障可能在大多数应用场景下显著缩小提早。 性能问题是部分的后面提到了,每个节点的性能受到多种因素的影响,这些影响因素是动静的,难以精确预测每个节点的性能,因而咱们无奈准确地抉择最好的节点,然而能够防止较差的节点。 在云环境中,服务器的性能经常是难以预测的,然而咱们能够通过对大量的数据进行剖析,发现服务器性能的散布大多数状况下是合乎正态分布的。因而,只管有一部分的服务器在性能方面体现比拟差,它们的数量通常都是多数的 (3Sigma) ,而绝大部分服务器节点的体现是失常的。 除了服务器之间的差别,还存在由基础设施导致的动静提早,这种提早可能是因为网络拥塞、故障或一直增长的流量所导致。这种提早通常具备持续性和局部性:持续性则示意提早会长工夫存在,不会在短时间内隐没;而局部性指的是提早往往只呈现在某些特定服务器上,而不会在全局产生。 PeakEWMA面对这些问题,咱们应用 Peak EWMA (Peak Exponentially Weighted Moving Average) 计算响应工夫指标,并依据这个指标来对节点进行负载平衡。 EWMA 是一种动静权重调整算法,各数值的加权影响力随工夫而指数式消退,越近期的数据加权影响力越重,但较旧的数据也给予肯定的加权值。 它以绝对较高的权重思考了最近响应工夫的影响,因而更具备针对性和时效性。加权的水平以常数 决定, 数值介于 0 至 1,它用来控制数据加权影响力消退的速率。 ...

May 23, 2023 · 1 min · jiezi

关于go:Go-开发实践手把手教你搭建一个登录功能

本文首发自「慕课网」(www.imooc.com) ,想理解更多IT干货内容,程序员圈内热闻,欢送关注"慕课网"及“慕课网公众号”! 作者:Codey|慕课网讲师 用 Go 语言搭建繁难登录性能如果你最近刚学习Go 语言根底个性,对 Go 语言也有了肯定把握和了解。那么接下来小慕就带你学习如何应用 Go 语言如何搭建一个提供登陆性能的 web 服务。 1. 搭建服务在 Go 语言中想要搭建一个 http 服务是非常容易的一件事件,一行代码就能够了。 代码示例: package mainimport ( "net/http")func main() { http.ListenAndServe("127.0.0.1:9300", nil) //设置监听的端口}运行以上代码能够失去一个服务,在浏览器上输出http://127.0.0.1:9300/,因为没有编写任何路由,所以只会呈现 404 的提醒: 2. 编写路由服务曾经能够运行了,接下来就是要编写能被内部拜访的路由接口,http 申请分为两种,POST 申请和 GET 申请。咱们首先想实现的是一个网站登录页面关上的路由 /index,须要编写一个能响应 GET 申请的路由。 代码示例: package mainimport ( "net/http")func main() { //设置拜访的路由 http.HandleFunc("/index", func(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { w.Write([]byte("<h1>Hello Codey!<h1>")) } }) http.ListenAndServe("127.0.0.1:9300", nil) //设置监听的端口}在浏览器中输出127.0.0.1:9300/index: 此处能够联合函数式编程的思维,将 index 的处理函数拿进去作为一个变量,代码批改后如下所示 ...

May 23, 2023 · 2 min · jiezi

关于go:极客专栏打包无密实时更新临邛道士鸿都客

download:极客专栏打包无密实时更新存储引擎InnoDB的外围架构与内存构造 InnoDB是MySQL中最罕用的存储引擎之一。它具备ACID事务反对、高性能和可靠性等特点,被宽泛用于企业级应用程序。在理解InnoDB的外围架构和内存构造之前,咱们须要先理解一些基本概念。 InnoDB架构InnoDB存储引擎采纳多版本并发管制(MVCC)的形式来治理数据。每个数据行都有一个版本号,当多个事务同时拜访同一行数据时,InnoDB会依据版本号来判断哪些是已提交的数据,哪些是未提交的数据。这种形式能够保障数据库的一致性和隔离性。 InnoDB存储引擎还采纳了缓冲池机制,将磁盘上的数据缓存在内存中,以进步数据读写效率。同时,它还反对自适应哈希索引和B+树索引,以及全文检索和空间数据类型等高级个性。 InnoDB内存构造InnoDB存储引擎的内存构造次要包含以下几个局部: (1)缓冲池 缓冲池是InnoDB存储引擎中最重要的内存构造之一。它用于缓存磁盘上的数据,以进步数据读写效率。缓冲池的大小能够通过innodb_buffer_pool_size参数进行设置。 (2)重做日志缓冲区 重做日志缓冲区是InnoDB存储引擎中另一个重要的内存构造。它用于缓存事务操作产生的重做日志,在事务提交时将其写入磁盘中。重做日志缓冲区的大小能够通过innodb_log_buffer_size参数进行设置。 (3)自适应哈希索引 自适应哈希索引是InnoDB存储引擎中用于减速查问的一种数据结构。它能够主动调整大小和地位,以适应不同的数据拜访模式。自适应哈希索引的大小能够通过innodb_adaptive_hash_index参数进行设置。 (4)B+树索引 B+树索引是InnoDB存储引擎中另一种罕用的数据结构,用于反对疾速搜寻和排序等操作。它能够解决大量数据,并且在插入、更新和删除数据时具备较好的性能。B+树索引的大小能够通过innodb_page_size参数进行设置。 总结 InnoDB存储引擎是MySQL中最罕用的存储引擎之一。它采纳MVCC形式来治理数据,并反对缓冲池、重做日志缓冲区、自适应哈希索引和B+树索引等多种内存构造。通过理解InnoDB的外围架构和内存构造,咱们能够更好地理解MySQL的工作原理,并进行优化和调整。

May 22, 2023 · 1 min · jiezi

关于go:go语言中实现生产者消费者模式有哪些方法呢

1. 简介本文将介绍在 Go 语言中实现生产者消费者模式的多种办法,并重点探讨了通道、条件变量的实用场景和优缺点。咱们将深刻探讨这些办法的特点,以帮忙开发者依据应用程序需要抉择最适宜的形式。通过灵活运用 Go 语言提供的并发原语,咱们可能实现高效、牢靠的生产者消费者模式,晋升零碎的并发性能和可维护性。 2. 生产者-消费者模式介绍2.1 生产者-消费者模式可能带来的益处生产者消费者模式是一种常见的并发编程模式,用于解决生产者和消费者之间的数据传递和解决问题。在该模式中,生产者负责生成数据(生产),而消费者负责解决数据(生产)。生产者和消费者在工夫上是解耦的,它们能够独立地以不同的速度执行。生产者消费者模式在并发编程中具备重要性,有以下几个方面的作用: 解耦生产者和消费者: 生产者和消费者之间通过两头的数据缓冲区(如通道)进行通信,从而实现理解耦。生产者和消费者能够独立地进行工作,无需关怀对方的状态或执行速度。均衡资源利用和解决能力: 生产者消费者模式能够均衡生产者和消费者之间的资源利用和解决能力。生产者能够依据消费者的解决能力进行生产,并且消费者能够依据生产者的速度进行生产,从而防止资源的节约或瓶颈。进步零碎的并发性和响应性: 生产者消费者模式容许并发执行生产者和消费者的工作,从而进步零碎的并发性和响应性。通过并发解决数据,能够更好地利用多核处理器和异步执行,从而放慢零碎的处理速度。实现异步通信和解决: 生产者消费者模式使得生产者和消费者能够异步地进行数据通信和解决。生产者能够在须要时生成数据,并将其放入缓冲区中,而消费者能够在须要时从缓冲区中获取数据进行解决,从而实现异步的数据交换和解决。提供可扩展性和模块化: 生产者消费者模式提供了一种可扩大和模块化的设计形式。通过将生产者和消费者解耦,能够不便地增加更多的生产者或消费者,以适应零碎需要的变动,同时放弃代码的可读性和维护性。总之,生产者消费者模式在并发编程中起着重要的作用,通过解耦、均衡资源利用、进步并发性和响应性等方面的劣势,能够帮忙构建高效、可扩大的并发零碎。 2.2 具体场景举例生产者消费者模式在理论的软件开发中有宽泛的利用。以下是几个常见的理论例子: 日志解决: 在日志解决中,能够将日志的生成视为生产者,而日志的生产(如写入文件、发送到近程服务器等)视为消费者。通过应用一个日志通道,生产者能够将日志音讯发送到通道,而消费者则从通道中接管日志音讯并进行相应的解决。这样能够无效地解耦日志的生成和生产,防止日志解决对业务逻辑的影响。工作队列: 在某些任务调度和解决场景中,能够应用生产者消费者模式来实现工作队列。生产者负责将工作增加到队列中,而消费者则从队列中获取工作并进行解决。这种形式能够实现工作的异步解决和负载平衡,进步零碎的并发性能。缓存更新: 在某些缓存零碎中,生产者消费者模式可用于实现缓存更新的异步解决。当数据发生变化时,生产者负责生成更新申请,而消费者则负责将更新利用到缓存中。通过将更新申请发送到缓存通道,能够实现异步的缓存更新,进步零碎的响应性能和吞吐量。在上述例子中,生产者和消费者在同一个单机环境中协同工作,通过应用通道或队列等机制进行数据交换和工作解决。这种设计能够进步零碎的并发性能、解耦数据生成和生产的逻辑,以及实现异步解决等益处。 3. 实现形式3.1 channel的实现应用通道是生产者消费者模式的另一种常见实现形式,它能够进步并发性能和升高通信开销。上面是应用带缓冲的通道实现生产者消费者模式的示例代码: package mainimport ( "fmt" "time")func producer(ch chan<- int) { for i := 1; i <= 5; i++ { ch <- i // 将数据发送到通道 fmt.Println("生产者生产:", i) time.Sleep(time.Second) // 模仿生产过程 } close(ch) // 敞开通道}func consumer(ch <-chan int, done chan<- bool) { for num := range ch { fmt.Println("消费者生产:", num) time.Sleep(2 * time.Second) // 模仿生产过程 } done <- true // 告诉主线程消费者已实现}func main() { ch := make(chan int, 3) // 创立带缓冲的通道 done := make(chan bool) // 用于告诉主线程消费者已实现 go producer(ch) // 启动生产者goroutine go consumer(ch, done) // 启动消费者goroutine // 主线程期待消费者实现 <-done fmt.Println("消费者已实现") // 主线程完结,程序退出}在示例代码中,producer函数是生产者函数,它通过通道将数据发送到消费者。consumer函数是消费者函数,它从通道中接收数据并进行生产。main函数是程序的入口,它创立了一个整型通道和一个用于告诉消费者实现的通道。 ...

May 22, 2023 · 2 min · jiezi

关于go:golang-微服务中的断路器-hystrix

之前说到过微服务容错解决,能够应用 断路器 应用断路器的起因是: 当上游的服务因为过载或故障,无奈提供服务,咱们须要及时的让上游服务知悉,且临时 熔断 调用方和提供方的调用链,这是为了防止服务雪崩景象的产生 go 外面能够应用什么形式来做断路器 呢? hystrix-gogo 中有一个我的项目实现了 这个断路器的性能: https://github.com/afex/hystrix-go Hystrix 可能在服务提供者呈现故障时,隔离调用者和提供者,避免服务级联失败 并且 Hystrix 还提供失败回滚的逻辑,是零碎疾速从异样中复原 为啥要用 Hystrix 来作为断路器? Hystrix 本身完满的是实现了断路器模式本身能够提供信号量和线程隔离的形式以爱护服务调用者的线程资源对提早和失败提供了弱小的容错能力,为零碎提供爱护和管制图解 Hystrix 运行流程如下是 golang 微服务容错解决是如何做的? 提到的断路器的 三种状态: 联合起来看看 Hystrix 具体流程 上述流程咱们能够这样来了解 应用 hystrix 的时候,hystrix 会给每一个近程调用逻辑封装成一个指令,这个指令蕴含这个近程调用的逻辑和失败回滚逻辑,这个 命令是 hystrix 惟一辨认的hystrix 依据 对应的指令获取到对应的断路器,判断断路器是否关上 若关上 执行执行失败回滚逻辑,不间接执行近程调用逻辑,因而此时服务曾经熔断了若敞开或者是半开状态 将执行池申请通行证hystrix 中咱们能够配置多个参数,最大并发数量,超时工夫,最小申请阈值,超时窗口工夫 和 谬误比例阈值等等图中咱们能够看到 Metrics 控制器, 当咱们的服务执行异样或者上游服务超时的时候, hystrix 命令就会向 Metrics 控制器 上报执行后果,并且 hystrix 命令对应的逻辑会进入到失败回滚逻辑 Metrics 控制器的作用Metrics 控制器应用滑动窗口的形式统计一段时间内的调用次数,失败次数,超时次数 和 被回绝的次数,下一篇的案例代码中,有体现如何配置 这个被回绝的次怎么了解呢?指的是在向执行池子申请通行证的时候,池子已满,故被回绝 如果这段时间内,执行谬误的频率出超过了断路器错误率的阈值,那么断路器就会关上 在重试超时定时器达到之前的申请都会间接进入失败回滚逻辑,拒绝执行真正的近程调用 ...

May 21, 2023 · 1 min · jiezi

关于go:协程并发下数据汇总除了互斥锁还有其他方式吗

1. 简介本文介绍了在并发编程中数据汇总的问题,并探讨了在并发环境下应用互斥锁和通道两种形式来保证数据安全性的办法。 首先,通过一个实例,形容了一个并发拉取数据并汇总的案例,并应用互斥锁来确保线程平安。而后,探讨了互斥锁的一些毛病,引出了通道作为一种代替计划,并介绍了通道的根本应用和个性。接下来,通过实例演示了如何应用通道来实现并发下的数据汇总。 最初,援用了etcd中应用通道实现协程并发下数据汇总的例子,展现了通道在理论我的项目中的利用。 2. 问题引入在申请处理过程中,常常须要通过RPC接口拉取数据。有时候,因为数据量较大,单个数据拉取操作可能会导致整个申请的解决工夫较长。为了放慢处理速度,咱们通常思考同时开启多个协程并发地拉取数据。一旦多个协程并发拉取数据后,主协程须要汇总这些协程拉取到的数据,而后再返回后果。在这个过程中,往往波及对共享资源的并发拜访,为了保障线程安全性,通常会应用互斥锁。上面通过一个简略的代码来展现该过程: package mainimport ( "fmt" "sync" "time")type Data struct { ID int Name string}var ( // 汇总后果 dataList []Data // 互斥锁 mutex sync.Mutex)func fetchData(page int, wg *sync.WaitGroup) { // 模仿RPC接口拉取数据的耗时操作 time.Sleep(time.Second) // 假如从RPC接口获取到了一批数据 data := Data{ ID: page, Name: fmt.Sprintf("Data %d", page), } // 应用互斥锁爱护共享数据的并发拜访 mutex.Lock() defer mutext.Unlock() dataList = append(dataList, data) wg.Done()}func main() { var wg sync.WaitGroup // 定义须要拉取的数据页数 numPages := 10 // 启动多个协程并发地拉取数据 for i := 1; i <= numPages; i++ { wg.Add(1) go fetchData(i, &wg) } // 期待所有协程实现 wg.Wait() // 打印拉取到的数据 fmt.Println("Fetched data:") for _, data := range dataList { fmt.Printf("ID: %d, Name: %s\n", data.ID, data.Name) }}在上述示例中,咱们定义了一个共享的dataList切片用于保留拉取到的数据。每个goroutine通过调用fetchData函数来模仿拉取数据的过程,并应用互斥锁mutex爱护dataList的并发拜访。主协程应用sync.WaitGroup期待所有协程实现数据拉取工作,而后打印出拉取到的数据。通过并发地拉取数据,并应用互斥锁保障线程平安,咱们能够显著进步数据拉取的速度,并且确保数据的正确性和一致性。 ...

May 21, 2023 · 3 min · jiezi

关于go:Go设计模式之装饰器模式

装璜器模式也被称为包装器模式,指的是在不扭转原有对象属性和办法的根底上,动静的给原有对象增加一些新的性能和属性。具体代码如下: 先定义一个专用的interface Phone,提供两个办法, 一个是设置手机的色彩,一个是获取手机的价格: type Phone interface { SelectColor(color string) string GetPrice() int}定义一个根底版的手机对象,该对象有尺寸、色彩、价格、内存、像素根底字段,该对象实现Phone接口。 type BasePhone struct { Size int Price int Color string Memory int Pixel int}func (p *BasePhone) SelectColor(color string) string { p.Color = color return color}func (p *BasePhone) GetPrice() int { return p.Price}上一步曾经实现根底手机的需要,然而手机售卖会有不同的系列,拍照手机广受年轻人的青睐,所以须要推出一个拍照手机系列,拍照手机具备根底版手机的性能,须要在此基础上新增手机像素的办法,调整根底像素,实现价格和色彩办法,那么就须要组合BasePhone。 type CameraPhone struct { BasePhone BasePhone}func (c *CameraPhone) SelectColor(color string) string { return c.BasePhone.SelectColor(color)}func (c *CameraPhone) GetPrice() int { return c.BasePhone.GetPrice() + CM}func (c *CameraPhone) GetPixel() int { c.BasePhone.Pixel += 50000000 return c.BasePhone.Pixel}同样的,除了拍照手机系列,还有一个plus版本,这个版本比根底版本多了128G内存,所以须要独自实现一个调整内存的办法。 ...

May 18, 2023 · 2 min · jiezi