关于go:go源码阅读databasesql

一、介绍database/sql 采纳策略模式,每个driver实现驱动,提供一个注册连接词。 var ( driversMu sync.RWMutex drivers = make(map[string]driver.Driver))// Register makes a database driver available by the provided name.// If Register is called twice with the same name or if driver is nil,// it panics.func Register(name string, driver driver.Driver) { driversMu.Lock() defer driversMu.Unlock() if driver == nil { panic("sql: Register driver is nil") } if _, dup := drivers[name]; dup { panic("sql: Register called twice for driver " + name) } drivers[name] = driver}当咱们应用不同的数据库的时候,只有把应用的驱动注册进去就能够了。比方在 github.com/go-sql-driver/mysql mysql驱动库里。有一段代码github.com/go-sql-driver/mysql/driver.go ...

July 14, 2022 · 1 min · jiezi

关于go:执行-go-vendor-时第三方包-go-版本冲突问题的解决方法

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

July 14, 2022 · 2 min · jiezi

关于go:go源码阅读databasesqldriver接口-与mysql-实现-gosqldriver库

一、简介database/sql 是一个给sql-like数据库应用的官网库,次要实现了 driver (sql-like)数据库的驱动接口sql相干的办法在地址 https://github.com/golang/go/wiki/SQLDrivers有具体实现了database/sql的sql-like驱动库列表 在 go源码 src/database/sql/sql.go有一个注册driver办法: func Register(name string, driver driver.Driver) { driversMu.Lock() defer driversMu.Unlock() if driver == nil { panic("sql: Register driver is nil") } if _, dup := drivers[name]; dup { panic("sql: Register called twice for driver " + name) } drivers[name] = driver}在每一个driver驱动实现,都会调用这个办法。比方mysql驱动go-sql-driver的实现如下:在源码 github.com/go-sql-driver/mysql/drver.go 中 func init() { sql.Register("mysql", &MySQLDriver{})}就把go-sql-driver驱动实现,注入到了database/sql中了。而后咱们应用的时候,就能够这样用了: import ( "database/sql" _ "github.com/go-sql-driver/mysql" "testing")func TestOpen(t *testing.T) { db, err := sql.Open("mysql", "dns") t.Log(db, err)}在这里咱们援用了 _ "github.com/go-sql-driver/mysql"但却应用 database/sql 外面的办法,因为go-sql-driver只是一个实现。因为在go-sql-driver咱们注册了 "mysql" 的值,而后就能够在database/sql调用了。 ...

July 14, 2022 · 1 min · jiezi

关于go:gozero微服务实战系列十分布式事务如何实现

在分布式应用场景中,分布式事务问题是不可回避的,在目前风行的微服务场景下更是如此。比方在咱们的商城零碎中,下单操作波及创立订单和库存扣减操作两个操作,而订单服务和商品服务是两个独立的微服务,因为每个微服务独占一个数据库实例,所以下单操作就波及到分布式事务问题,即要把整个下单操作看成一个整体,要么都胜利要么都不胜利。本篇文章咱们就一起来学习下分布式事务的相干常识。 基于音讯实现最终一致性咱们去店里就餐的时候,付钱点餐后往往服务员会先给咱们一张小票,而后拿着小票去出餐口期待出餐。为什么要把付钱和取餐两个动作离开呢?很重要的一个起因是使他们的接客能力更强,对应到服务来说就是使并发解决能力更强。只有咱们拿着小票,最终咱们是能够拿到咱们点的餐的,依附小票这个凭证(音讯)实现最终一致性。 对应到咱们的下单操作来说,当用户下单后,咱们能够学生成订单,而后发一条扣减库存的音讯到音讯队列中,这时候订单就算实现,但理论还没有扣减库存,因为库存的扣减和下单操作是异步的,也就是这个时候产生了数据的不统一。当生产到了扣减库存的音讯后进行库存扣减操作,这个时候数据实现了最终一致性。 基于音讯实现最终一致性这种策略实用于并发量比拟高同时对于数据一致性要求不高的场景。咱们商城中的一些非骨干逻辑能够采纳这种形式来晋升吞吐,比方购买商品后获取优惠券等非核心逻辑并不需要数据的强统一,能够异步的给用户发放优惠券。 如果在生产到音讯后,执行操作的时候失败了该怎么办呢?首先须要做重试,如果重试屡次后依然失败,这个时候须要收回告警或者记录日志,须要人工染指解决。 如果对数据有强统一要求的话,那这种形式是不实用的,请看下上面的两阶段提交协定。 XA协定说起XA协定,这个名词你未必据说过,但一提到2PC你必定据说过,这套计划依赖于底层数据库的反对,DB这层首先得要实现XA协定。比方MySQL InnoDB就是反对XA协定的数据库计划,能够把XA了解为一个强统一的中心化原子提交协定。 原子性的概念就是把一系列操作合并成一个整体,要么都执行,要么都不执行。而所谓的2PC就是把一个事务分成两步来提交,第一步做筹备动作,第二步做提交/回滚,这两步之间的协调是由一个中心化的Coordinator来治理,保障多步操作的原子性。 第一步(Prepare):Coordinator向各个分布式事务的参与者下达Prepare指令,各个事务别离将SQL语句在数据库执行但不提交,并且将准备就绪状态上报给Coordinator。 第二步(Commit/Rollback):如果所有节点都已就绪,那么Coordinator就下达Commit指令,各参与者提交本地事务,如果有任何一个节点不能就绪,Coordinator则下达Rollback指令进行本地回滚。 在咱们的下单操作中,咱们须要创立订单同时商品须要扣减库存,接下来咱们来看下2PC是怎么解决这个问题的。2PC引入了一个事务协调者的角色,来协调订单和商品服务。所谓的两阶段是指筹备阶段和提交阶段,在筹备阶段,协调者别离给订单服务和商品服务发送筹备命令,订单和商品服务收到筹备命令后,开始执行筹备操作,筹备阶段须要做哪些事件呢?你能够了解为,除了提交数据库事务以外的所有工作,都要在筹备阶段实现。比方订单服务在筹备阶段须要实现: 在订单库开启一个数据库事务;在订单表中写入订单数据留神这里咱们没有提交订单数据库事务,最初给书屋协调者返回筹备胜利。协调者在收到两个服务筹备胜利的响应后,开始进入第二阶段。进入提交阶段,提交阶段就比较简单了,协调者再给这两个零碎发送提交命令,每个零碎提交本人的数据库事务而后给协调者返回提交胜利响应,协调者收到有响应之后,给客户端返回胜利的响应,整个分布式事务就完结了,以下是这个过程的时序图: 以上是失常状况,接下来才是重点,异常情况怎么办呢?咱们还是分两阶段来阐明,在筹备阶段,如果任何异步呈现谬误或者超时,协调者就会给两个服务发送回滚事务申请,两个服务在收到申请之后,回滚本人的数据库事务,分布式事务执行失败,两个服务的数据库事务都回滚了,相干的所有数据回滚到分布式事务执行之前的状态,就像这个分布式事务没有执行一样,以下是异常情况的时序图: 如果筹备阶段胜利,进入提交阶段,这个时候整个分布式事务就只能胜利,不能失败。如果产生网络传输失败的状况,须要重复重试,直到提交胜利为止,如果这个阶段产生宕机,包含两个数据库宕机或者订单服务、商品服务宕机,还是可能呈现订单库实现了提交,但商品库因为宕机主动回滚,导致数据不统一的状况,然而,因为提交的过程非常简单,执行十分迅速,呈现这种状况的概率比拟低,所以,从实用的角度来说,2PC这种分布式事务办法,理论的数据一致性还是十分好的。 但这种分布式事务有一个人造缺点,导致XA特地不适宜用在互联网高并发的场景外面,因为每个本地事务在Prepare阶段,都要始终占用一个数据库的连贯资源,这个资源直到第二阶段Commit或者Rollback之后才会被开释。但互联网场景的个性是什么?是高并发,因为并发量特地高,所以每个事务必须尽快开释掉所持有的数据库连贯资源。事务执行工夫越短越好,这样能力让别的事务尽快被执行。 所以,只有在须要强统一,并且并发量不大的场景下,才思考2PC。 2PC也有一些改良版本,比方3PC,大体思维和2PC是差不多的,解决了2PC的一些问题,然而也会带来新的问题,实现起来也更简单,限于篇幅咱们没法每个都具体的去解说,在了解了2PC的根底上,大家能够自行搜寻相干材料进行学习。 分布式事务框架想要本人实现一套比较完善且没有bug的分布式事务逻辑还是比较复杂的,好在咱们不必反复造轮子,曾经有一些现成的框架能够帮咱们实现分布式事务,这里次要介绍应用和go-zero联合比拟好的DTM。 援用DTM官网的的介绍,DTM是一款变革性的分布式事务框架,提供了傻瓜式的应用形式,极大地升高了分布式事务的应用门槛,改了变了”能不必分布式事务就不必“的行业现状,优雅的解决了服务间的数据一致性问题。 本文作者在写这篇文章之前听过DTM,但素来没有应用过,大略花了十几分钟看了下官网文档,就能照葫芦画瓢地应用起来了,也足以阐明DTM的应用是非常简单的,置信聪慧的你必定也是一看就会。接下来咱们就应用DTM基于TCC来实现分布式事务。 首先须要装置dtm,我应用的是mac,间接应用如下命令装置: brew install dtm给DTM创立配置文件dtm.yml,内容如下: MicroService: Driver: 'dtm-driver-gozero' # 配置dtm应用go-zero的微服务协定 Target: 'etcd://localhost:2379/dtmservice' # 把dtm注册到etcd的这个地址 EndPoint: 'localhost:36790' # dtm的本地地址# 启动dtmdtm -c /opt/homebrew/etc/dtm.yml在seckill-rmq中生产到订单数据后进行下单和扣库存操作,这里改成基于TCC的分布式事务形式,留神 dtmServer 和DTM配置文件中的Target对应: var dtmServer = "etcd://localhost:2379/dtmservice"因为TCC由三个局部组成,别离是Try、Confirm和Cancel,所以在订单服务和商品服务中咱们给这三个阶段别离提供了对应的RPC办法, 在Try对应的办法中次要做一些数据的Check操作,Check数据满足下单要求后,执行Confirm对应的办法,Confirm对应的办法是真正实现业务逻辑的,如果失败回滚则执行Cancel对应的办法,Cancel办法次要是对Confirm办法的数据进行弥补。代码如下: var dtmServer = "etcd://localhost:2379/dtmservice"func (s *Service) consumeDTM(ch chan *KafkaData) { defer s.waiter.Done() productServer, err := s.c.ProductRPC.BuildTarget() if err != nil { log.Fatalf("s.c.ProductRPC.BuildTarget error: %v", err) } orderServer, err := s.c.OrderRPC.BuildTarget() if err != nil { log.Fatalf("s.c.OrderRPC.BuildTarget error: %v", err) } for { m, ok := <-ch if !ok { log.Fatal("seckill rmq exit") } fmt.Printf("consume msg: %+v\n", m) gid := dtmgrpc.MustGenGid(dtmServer) err := dtmgrpc.TccGlobalTransaction(dtmServer, gid, func(tcc *dtmgrpc.TccGrpc) error { if e := tcc.CallBranch( &product.UpdateProductStockRequest{ProductId: m.Pid, Num: 1}, productServer+"/product.Product/CheckProductStock", productServer+"/product.Product/UpdateProductStock", productServer+"/product.Product/RollbackProductStock", &product.UpdateProductStockRequest{}); err != nil { logx.Errorf("tcc.CallBranch server: %s error: %v", productServer, err) return e } if e := tcc.CallBranch( &order.CreateOrderRequest{Uid: m.Uid, Pid: m.Pid}, orderServer+"/order.Order/CreateOrderCheck", orderServer+"/order.Order/CreateOrder", orderServer+"/order.Order/RollbackOrder", &order.CreateOrderResponse{}, ); err != nil { logx.Errorf("tcc.CallBranch server: %s error: %v", orderServer, err) return e } return nil }) logger.FatalIfError(err) }}结束语本篇文章次要和大家一起学习了分布式事务相干的常识。在并发比拟高且对数据没有强一致性要求的场景下咱们能够通过音讯队列的形式实现分布式事务达到最终一致性,如果对数据有强一致性的要求,能够应用2PC,然而数据强统一的保障必然会损失性能,所以个别只有在并发量不大,且对数据有强一致性要求时才会应用2PC。3PC、TCC等都是针对2PC的一些毛病进行了优化革新,因为篇幅限度所以这里没有具体开展来讲,感兴趣的敌人能够自行搜寻相干材料进行学习。最初基于TCC应用DTM实现了一个下单过程分布式事务的例子,代码实现也非常简单易懂。对于分布式事务心愿大家能先搞明确其中的原理,理解了原理后,不论应用什么框架那都不在话下了。 ...

July 14, 2022 · 1 min · jiezi

关于go:这个新-Go-错误处理提案能解决问题不

大家好,我是煎鱼。 Go 语言的一大特色就是它的谬误机制,因而基本上所有的错误处理提案或探讨我都会有所查看和学习,开辟不同的思考视线和解决办法。 明天分享的是 @Cristo García 所提出的提案《Simple Error Handling for Go 2》,略有批改,和煎鱼一起学习和探讨吧! Go 必须依然是 Go这一个提案的外围观点是 Go 必须依然是 Go,这意味着对于错误处理的革新须要满足如下准则: 减少尽可能少的语法。尽可能明确不便。本文中的 “我“ 均指代提案作者 @Cristo García,并非正在学习的煎鱼。 原想法原提案作者 @PeterRk 提出了以下思维: func getDivisorFromDB(key string) (uint, error) { //...}func GetDivisor(key string) (uint, error) { exit := func(err error) (uint, error) { return 1, fmt.Errorf("fail to get divisor with key \"%s\": %v", key, err) } divisor := check(getDivisorFromDB(key), exit) //... return divisor, nil}应用示例: divisor := check(getDivisorFromDB(key), exit) 等同于现有的: ...

July 13, 2022 · 2 min · jiezi

关于go:Go-原生插件使用问题全解析

文|丁飞(花名:路德 ) 蚂蚁团体高级工程师 深耕于 SOFAMesh 产品的商业化落地 次要方向为基于服务网格技术的零碎架构降级方案设计与落地 本文 4394 字 浏览 10 分钟 |前言|MOSN 作为蚂蚁团体在 ServiceMesh 解决方案中的数据面组件,从设计之初就思考到了第三方的扩大开发需要。目前,MOSN 反对通过 gRPC、WASM、以及 Go 原生插件三种机制对其进行扩大。 我在主导设计和落地基于 Go 原生插件机制的扩大能力时遇到了很多问题,鉴于这方面的相干材料很少,因此就有了这个想法来做一个十分浅显的总结,心愿能对大家有所帮忙。 注:本文只说问题和解决方案,不读代码,文章最初会给出外围源码的 checklist。 PART. 1--文章技术背景一、运行时通常而言,在计算机编程语言畛域,“运行时”的概念和一些须要应用到 VM 的语言相干。程序的运行由两个局部组成:指标代码和“虚拟机”。比方最为典型的 JAVA,即 Java Class + JRE。 对于一些看似不须要“虚拟机”的编程语言,就不太会有“运行时”的概念,程序的运行只须要一个局部,即指标代码。但事实上,即便是 C/C++,也有“运行时”,即它所运行平台的 OS/Lib。 Go 也是一样,因为运行 Go 程序不须要前置部署相似于 JRE 的“运行时”,所以它看起来仿佛跟“虚拟机”或者“运行时”没啥关系。但事实上,Go 语言的“运行时”被编译器编译成了二进制指标代码的一部分。 图 1-1. Java 程序、runtime 和 OS 关系 图 1-2. C/C++ 程序、runtime 和 OS 关系 图 1-3. Go 程序、runtime 和 OS 关系 ...

July 13, 2022 · 3 min · jiezi

关于go:gozero微服务实战系列九极致优化秒杀性能

上一篇文章中引入了音讯队列对秒杀流量做削峰的解决,咱们应用的是Kafka,看起来仿佛工作的不错,但其实还是有很多隐患存在,如果这些隐患不优化解决掉,那么秒杀抢购流动开始后可能会呈现音讯沉积、生产提早、数据不统一、甚至服务解体等问题,那么结果可想而知。本篇文章咱们就一起来把这些隐患解决掉。 批量数据聚合在SeckillOrder这个办法中,每来一次秒杀抢购申请都往往Kafka中发送一条音讯。如果这个时候有一千万的用户同时来抢购,就算咱们做了各种限流策略,一瞬间还是可能会有上百万的音讯会发到Kafka,会产生大量的网络IO和磁盘IO老本,大家都晓得Kafka是基于日志的音讯零碎,写音讯尽管大多状况下都是程序IO,但当海量的音讯同时写入的时候还是可能会扛不住。 那怎么解决这个问题呢?答案是做音讯的聚合。之前发送一条音讯就会产生一次网络IO和一次磁盘IO,咱们做音讯聚合后,比方聚合100条音讯后再发送给Kafka,这个时候100条音讯才会产生一次网络IO和磁盘IO,对整个Kafka的吞吐和性能是一个十分大的晋升。其实这就是一种小包聚合的思维,或者叫Batch或者批量的思维。这种思维也随处可见,比方咱们应用Mysql插入批量数据的时候,能够通过一条SQL语句执行而不是循环的一条一条插入,还有Redis的Pipeline操作等等。 那怎么来聚合呢,聚合策略是啥呢?聚合策略有两个维度别离是聚合音讯条数和聚合工夫,比方聚合音讯达到100条咱们就往Kafka发送一次,这个条数是能够配置的,那如果始终也达不到100条音讯怎么办呢?通过聚合工夫来兜底,这个聚合工夫也是能够配置的,比方配置聚合工夫为1秒钟,也就是无论目前聚合了多少条音讯只有聚合工夫达到1秒,那么就往Kafka发送一次数据。聚合条数和聚合工夫是或的关系,也就是只有有一个条件满足就触发。 在这里咱们提供一个批量聚合数据的工具Batcher,定义如下 type Batcher struct { opts options Do func(ctx context.Context, val map[string][]interface{}) Sharding func(key string) int chans []chan *msg wait sync.WaitGroup}Do办法:满足聚合条件后就会执行Do办法,其中val参数为聚合后的数据 Sharding办法:通过Key进行sharding,雷同的key音讯写入到同一个channel中,被同一个goroutine解决 在merge办法中有两个触发执行Do办法的条件,一是当聚合的数据条数大于等于设置的条数,二是当触发设置的定时器 代码实现比较简单,如下为具体实现: type msg struct { key string val interface{}}type Batcher struct { opts options Do func(ctx context.Context, val map[string][]interface{}) Sharding func(key string) int chans []chan *msg wait sync.WaitGroup}func New(opts ...Option) *Batcher { b := &Batcher{} for _, opt := range opts { opt.apply(&b.opts) } b.opts.check() b.chans = make([]chan *msg, b.opts.worker) for i := 0; i < b.opts.worker; i++ { b.chans[i] = make(chan *msg, b.opts.buffer) } return b}func (b *Batcher) Start() { if b.Do == nil { log.Fatal("Batcher: Do func is nil") } if b.Sharding == nil { log.Fatal("Batcher: Sharding func is nil") } b.wait.Add(len(b.chans)) for i, ch := range b.chans { go b.merge(i, ch) }}func (b *Batcher) Add(key string, val interface{}) error { ch, msg := b.add(key, val) select { case ch <- msg: default: return ErrFull } return nil}func (b *Batcher) add(key string, val interface{}) (chan *msg, *msg) { sharding := b.Sharding(key) % b.opts.worker ch := b.chans[sharding] msg := &msg{key: key, val: val} return ch, msg}func (b *Batcher) merge(idx int, ch <-chan *msg) { defer b.wait.Done() var ( msg *msg count int closed bool lastTicker = true interval = b.opts.interval vals = make(map[string][]interface{}, b.opts.size) ) if idx > 0 { interval = time.Duration(int64(idx) * (int64(b.opts.interval) / int64(b.opts.worker))) } ticker := time.NewTicker(interval) for { select { case msg = <-ch: if msg == nil { closed = true break } count++ vals[msg.key] = append(vals[msg.key], msg.val) if count >= b.opts.size { break } continue case <-ticker.C: if lastTicker { ticker.Stop() ticker = time.NewTicker(b.opts.interval) lastTicker = false } } if len(vals) > 0 { ctx := context.Background() b.Do(ctx, vals) vals = make(map[string][]interface{}, b.opts.size) count = 0 } if closed { ticker.Stop() return } }}func (b *Batcher) Close() { for _, ch := range b.chans { ch <- nil } b.wait.Wait()}应用的时候须要先创立一个Batcher,而后定义Batcher的Sharding办法和Do办法,在Sharding办法中通过ProductID把不同商品的聚合投递到不同的goroutine中解决,在Do办法中咱们把聚合的数据一次性批量的发送到Kafka,定义如下: ...

July 12, 2022 · 4 min · jiezi

关于go:golang网络数据交换

1. 问题形容在C/C++中解决构造体在网络上传输的解决 1.1 间接发送二进制的构造体数据struct DataFormat { long arg1; long arg2;};struct Result { long sum;};int main(int argc, char **argv) { ... char sendline[MAXLINE]; struct DataFormat args; struct Result result; args.arg1 = 1; args.arg2 = 2; write(sockfd, &args, sizeof(args)); if (readn(sockfd, &result, sizeof(result)) != 0) printf("%ld\n", result.sum); ...}这种做法有许多缺点,包含: 发送的多字节类型(int,long等)在不同架构的机器上的大小端形式可能不同,造成解析谬误即便是大小端统一的机器上,可能因为int、long等类型的机器字长不同而呈现谬误构造体在不同的编译器中的对其形式可能有所不同,这也会造成解析谬误1.2 解决形式针对以上3个问题,采纳的解决思路: 对于多字节类型,采纳转换成对立的网络字节序进行解决,应用htons htonl和ntohs ntohl对于类型字长不统一的状况,对立采纳固定长度的类型,比方uint8, uint16等类型构造体的对其形式都设置成packed,这样是的成员间接没有任何对其(在不同的编译器中示意形式不一样,在gcc中应用__attribute__((packed)))struct DataFormat { int32_t arg1; int32_t arg2;}__attribute__((packed));struct Result { int32_t sum;}__attribute__((packed));int main(int argc, char **argv) { ... char sendline[MAXLINE]; struct DataFormat args; struct Result result; args.arg1 = htonl(1); args.arg2 = htonl(2); write(sockfd, &args, sizeof(args)); if (readn(sockfd, &result, sizeof(result)) != 0) printf("%ld\n", ntohl(result.sum)); ...}1.3 解决形式2还有一种更好的解决形式: 咱们在两端发送和接管的时候都应用字符串进行,因为字符只有一个字节,在传输过程中不存在大小端的问题,另外字符的示意办法在各平台根本是统一的,也没有所谓的对齐问题,因而这种计划具备最大的通用性 ...

July 12, 2022 · 4 min · jiezi

关于go:go-reflect-struct-go反射与struct结构体

一、介绍reflect包次要用来查看interface{}类型的类型type和值value。咱们能够用 reflect.TypeOf() 失去 reflect.Type 反射类型reflect.ValueOf() 失去 reflect.Value 反射值二、reflect.Type在这里咱们以struct举例:关上源码 src/reflect/type.go type Type interface { //通用的3个函数 //返回kind,根底类型,Struct,Bool,Int,Int8,Array,Chan,Func,Map... Kind() Kind //返回构造体名称 Name() string //返回包门路 PkgPath() string //struct相干的3个重要办法,能够看到如果不是struct的话,会报错 // Field returns a struct type's i'th field. // It panics if the type's Kind is not Struct. // It panics if i is not in the range [0, NumField()). Field(i int) StructField // FieldByName returns the struct field with the given name // and a boolean indicating if the field was found. FieldByName(name string) (StructField, bool) // NumField returns a struct type's field count. // It panics if the type's Kind is not Struct. NumField() int}本文次要截取了reflect.Type的6个办法以及StructField构造体 ...

July 11, 2022 · 1 min · jiezi

关于go:go-string-to-time-字符串转时间戳

//字符串转工夫戳 YmdHis 格局 比方 2022-07-06 15:00:00func strToUnixTime(YmdHis string) (int64, error) { loc, _ := time.LoadLocation("Local") theTime, err := time.ParseInLocation("2006-01-02 15:04:05", YmdHis, loc) if err != nil { return 0, err } unixTime := theTime.Unix() return unixTime, nil}

July 11, 2022 · 1 min · jiezi

关于go:用原生Go写一个自己的博客搭建项目

能够在环境中设置代理goproxy.cn防止国内下包出错启动服务最开始能够先对一个端口18080启动一个服务: package mainimport ( "log" "net/http")func index(w http.ResponseWriter, r *http.Request) {}func main() { // 启动一个http服务 指明ip和端口 server := http.Server{ Addr: "127.0.0.1:18080", } //响应拜访http://localhost:18080/的申请 http.HandleFunc("/", index) // 监听对应的端口 & 启动服务 if err := server.ListenAndServe(); err != nil { log.Println(err) }}成果:没有404,阐明服务启动胜利。 增加响应接下来,咱们在index这个func能够增加响应: func index(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hey this is LiberHom"))}传递json数据当然,理论开发个别不会是这么简略的文字,而是一些web页面,须要给前端返回json信息,咱们须要在Header中指明是json。 func index(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte("hey this is LiberHom"))}成果:咱们能够结构残缺一些的例子如下: package mainimport ( "encoding/json" "log" "net/http")//结构一些数据type IndexData struct { Title string `json:"title"` Desc string `json:"desc"`}func index(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") var indexData IndexData indexData.Title = "Liber-Blog" indexData.Desc = "this is the desc part" //把构造体实例转成对应的json jsonStr, _ := json.Marshal(indexData) w.Write(jsonStr)}func main() { // 启动一个http服务 指明ip和端口 server := http.Server{ Addr: "127.0.0.1:18080", } //响应拜访http://localhost:18080/的申请 http.HandleFunc("/", index) // 监听对应的端口 & 启动服务 if err := server.ListenAndServe(); err != nil { log.Println(err) }}运行后果如下: ...

July 11, 2022 · 1 min · jiezi

关于go:Go学习笔记Zap日志

Log包Go语言提供的默认日志包:https://golang.org/pkg/log/根本用法log包定义了Logger类型,该类型提供了一些格式化输入的办法。 type Logger struct { mu sync.Mutex // ensures atomic writes; protects the following fields prefix string // prefix on each line to identify the logger (but see Lmsgprefix) flag int // properties out io.Writer // destination for output buf []byte // for accumulating text to write}mu属性次要是为了确保原子操作,prefix设置每一行的前缀,flag设置输入的各种属性,比方工夫、行号、文件门路等。out输入的方向,用于把日志存储文件。 本包也提供了一个预约义的“规范”logger,能够通过调用函数Print系列(Print|Printf|Println)、Fatal系列(Fatal|Fatalf|Fatalln)、和Panic系列(Panic|Panicf|Panicln)来应用,比自行创立一个logger对象更容易应用。 例如,咱们能够像上面的代码一样间接通过log包来调用下面提到的办法,默认它们会将日志信息打印到终端界面: log.Println("这是一条优雅的日志。")v := "优雅的"log.Printf("这是一个%s日志\n", v)//fatal系列函数会在写入日志信息后调用os.Exit(1)log.Fatalln("这是一天会触发fatal的日志") //Panic系列函数会在写入日志信息后paniclog.Panicln("这是一个会触发panic的日志。") //执行后会主动触发一个异样flag属性默认状况下的logger只会提供日志的工夫信息,然而很多状况下咱们心愿失去更多信息,比方记录该日志的文件名和行号等。log规范库中为咱们提供了定制这些设置的办法。 log规范库中的Flags函数会返回规范logger的输入配置,而SetFlags函数用来设置规范logger的输入配置。上面是flag属性对应的常量 const ( Ldate = 1 << iota // the date in the local time zone: 2009/01/23 Ltime // the time in the local time zone: 01:23:23 Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. Llongfile // full file name and line number: /a/b/c/d.go:23 Lshortfile // final file name element and line number: d.go:23. overrides Llongfile LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone Lmsgprefix // move the "prefix" from the beginning of the line to before the message LstdFlags = Ldate | Ltime // initial values for the standard logger)设置输入属性的代码如下: ...

July 11, 2022 · 6 min · jiezi

关于go:极客时间Go进阶训练营全新升级第4期完结无密

Download: 极客工夫-Go进阶训练营|全新降级第4期GO高级进阶 ETCD 操作etcd是分布式系统中高可用键值(key-value)存储数据库,有几大重要的特色: 简略:安装简单、提供了gRPC API交互 平安:反对TLS加密通道的标准 疾速:基准10000次写入/秒 牢靠:基于raft算法,实现分布式系统数据的可用性和一致性 etcd是用Go编写的,它应用Raft一致性算法来治理高可用日志 linux 下装置etcd 须要装置go 1.13以上版本 cd $GOPATH/srcmkdir go.etcd.io && cd go.etcd.iogit clone https://github.com/etcd-io/etcdcd etcd./build应用build脚本构建后生成etcd 和etcdctl 可执行程序,etcdctl是一个命令行客户端 go如何操作etcd 首先援用etcd库 import "go.etcd.io/etcd/clientv3"连贯etcd服务器 cli, err := clientv3.New(clientv3.Config{Endpoints: []string{"192.168.1.109:2379"},DialTimeout: 5 * time.Second,})if err != nil {fmt.Printf("connect to etcd failed, err:%v\n", err)return}fmt.Println("connect to etcd success")defer cli.Close()通过put将key和value存储在etcd // putctx, cancel := context.WithTimeout(context.Background(), time.Second)_, err = cli.Put(ctx, "server1", "192.168.3.10")cancel()if err != nil {fmt.Printf("put to etcd failed, err:%v\n", err)return}// 通过get获取key的值ctx, cancel = context.WithTimeout(context.Background(), time.Second)resp, err := cli.Get(ctx, "server1")cancel()if err != nil {fmt.Printf("get from etcd failed, err:%v\n", err)return} ...

July 9, 2022 · 1 min · jiezi

关于go:go语言中会发生panic的情况

切片越界func main() { // 捕捉异样 defer func() { if err := recover(); err != nil { fmt.Println(err) } }() //panic 1.切片越界,上面的会panic! arr := make([]int, 2) arr[2] = 5 fmt.Println(arr)}运行后果 runtime error: index out of range [2] with length 2反复敞开chanfunc main() { //2.反复敞开 chan c := make(chan int) close(c) close(c)}运行后果 panic: close of closed channel曾经敞开的chan持续发送func main() { //3.曾经敞开的chan 持续发送数据 d := make(chan int) close(d) d <- 1}运行后果 panic: send on closed channel空指针的状况,定义的指针变量没有初始化,就间接应用func main() { //4.空指针的状况,定义的指针变量没有初始化,就间接应用 var p *people fmt.Println(p.printName)}type people struct { Name string Age int}func (p people) printName() { fmt.Println(p.Name)}运行后果 ...

July 9, 2022 · 1 min · jiezi

关于go:Go-119要来了看看都有哪些变化第3篇

前言Go官网团队在2022.06.11公布了Go 1.19 Beta 1版本,Go 1.19的正式release版本预计会在往年8月份公布。 让咱们先睹为快,看看Go 1.19给咱们带来了哪些变动。 这是Go 1.19版本更新内容详解的第3篇,欢送大家关注公众号,及时获取本系列最新更新。 第1篇次要波及Go泛型的改变、Go内存模型和原子操作的优化,原文链接:Go 1.19版本变更内容第1篇。 第2篇次要波及Go文档正文(doc comments)、编译束缚(build constraint)以及Go命令的批改,原文链接:Go 1.19版本变更内容第2篇。 Go 1.19公布清单和Go 1.18相比,改变绝对较小,次要波及语言(Language)、内存模型(Memory Model)、可移植性(Ports)、Go Tool工具链、运行时(Runtime)、编译器(Compiler)、汇编器(Assembler)、链接器(Linker)和外围库(Core library)等方面的优化。 本文重点介绍Go 1.19版本在运行时、编译器、汇编器和链接器方面的变动。 运行时软内存限度(soft memory limit)运行时当初反对软内存限度(soft memory limit)。这个内存限度包含了Go heap里的内存以及所有其它被Go运行时治理的内存。如果不是被Go运行时治理的内存,比方二进制程序自身映射的内存、其它语言治理的内存,是不在这个软内存限度里的。 这个限度能够通过runtime/debug.SetMemoryLimit 函数或者 GOMEMLIMIT 环境变量进行设置。 软内存限度和runtime/debug.SetGCPercent 函数以及 GOGC环境变量是能够联合起来工作的,而且即便在GOGC=off模式下,软内存限度也会失效。设计目标是为了让Go程序能够最大化内存应用,晋升某些场景下的内存资源应用效率。 能够参考the GC guide查看更多软内存限度的设计实现细节,以及一些常见应用场景和最佳实际。 须要留神的是,对于数十MB或者更小的内存限度,因为思考到一些性能问题,软内存限度是有可能不会失效的,能够参考issue 52433查看更多细节。对于数百MB或者更大的内存限度,目前是能够稳固运行在生产环境上的。 当Go程序堆内存靠近软内存限度时,为了缩小GC抖动的影响,Go运行时会尝试限度GC CPU利用率不超过50%(不包含CPU闲暇工夫)。 在理论应用中,个别只在一些非凡场景才倡议应用软内存限度,当Go堆内存占用真的超过软内存限度时,新的运行时度量( runtime metric)工具/gc/limiter/last-enabled:gc-cycle会报告这个事件。 如果应用程序闲暇到足以执行一次GC时,Go运行时(Runtime)当初会在闲暇的操作系统线程里调度更少的GC工作协程(goroutine)。 Go运行时当初会依据goroutine过来栈空间的均匀应用大小来调配一个初始的goroutine栈大小,晋升栈空间的调配和应用效率。 最大文件描述符在Unix操作系统上,import了 os 包的Go程序当初会主动减少 可关上的最大文件描述符数量 到容许的最大值。 批改这个的起因是有些零碎为了兼容很老的应用select零碎调用的C语言程序,会把最大文件描述符数量设置一个比拟小的值,通常是1024。这个设置对Go程序没有用途,相同,还会带来一些问题。比方gofmt在并行处理很多文件时,很容易遇到文件描述符用尽的问题。另外这个批改须要留神的一个点是,如果Go程序和C语言混合编程,执行了很老的C代码,那在运行这个Go程序时,须要先设置最大文件描述符的hard limit,将其调小。 堆栈信息不可复原的致命谬误(例如并发的map写入,解锁一个未加锁的mutex)当初会打印更简略的堆栈信息,不再打印Go runtime的元数据,除非设置了GOTRACEBACK=system or crash。然而对于runtime自身的致命谬误,不论GOTRACEBACK 的值是什么,还是会打印蕴含元数据的所有堆栈信息。 调试ARM64架构当初反对注入了调试器的函数调用,容许用户在交互式的调试会话里调用函数。 Go 1.18新增的address sanitizer 当初能够更准确地解决函数参数和全局变量。 编译器针对GOARCH=amd64 和 GOARCH=arm64 架构,编译器当初应用跳表(jump table)来实现大整数和字符串的switch语句。 ...

July 8, 2022 · 1 min · jiezi

关于go:Go如何进行类型转换

一般来讲,应用 v1 := T(v) 这个通用办法进行转换即可。但理论应用过程中往往不止这一种转换形式,同时也有一些注意事项,现总结如下。 版本:Go 1.14数字与字符串的互相转换数字转字符串初始变量: var i int = 97var i64 int64 = 97var f32 float32 = 97.02var f64 float64 = 97.02原类型指标类型转换方法后果&备注intstringstring(i)"a"。该办法将97当做ASCII/UTF8码值,无奈失去预期的"97"。intstringstrconv.Itoa(i)"97"intstringstrconv.FormatInt(int64(i), 10)"97"intstringfmt.Sprintf("%d", i)"97"int64stringstring(i64)"a"。该办法将97当做ASCII/UTF8码值,无奈失去预期的"97"。int64stringstrconv.Itoa(int(i64))"97"int64stringstrconv.FormatInt(i64, 10)"97"int64stringfmt.Sprintf("%d", i64)"97"其余整型string略 float32stringstrconv.FormatFloat(float64(f32), 'f', 10, 32)"97.0199966431"float32stringfmt.Sprintf("%f", f32)97.019997float64stringstrconv.FormatFloat(f64, 'f', 10, 64)"97.0200000000"float32stringfmt.Sprintf("%f", f64)97.020000字符串转数字初始变量: var s1 string = "97"var s2 string = "97.02"原类型指标类型转换方法后果&备注stringintstrconv.Atoi(s1)97。对于strconv.Atoi(s2)会报错strconv.Atoi: parsing "97.02": invalid syntax并返回0。stringintstrconv.ParseInt(s1, 10, 0)不合乎预期,后果冀望是int类型,实际上是int64类型。另外,对于strconv.ParseInt(s2, 10, 0)会报错strconv.ParseInt: parsing "97.02": invalid syntax,并返回0。stringint64strconv.ParseInt(s1, 10, 64)97stringfloat32strconv.ParseFloat(s2, 64)97.02

July 8, 2022 · 1 min · jiezi

关于go:Go如何判断两个切片是否相等

办法一:一一元素进行比拟func equal( s1 []int , s2 []int ) bool { if len(s1) != len(s2) { return false } for i := 0; i < len(s1); i++ { if s1[i] != s2[i] { return false } } return true}办法二:应用reflect.DeepEqual函数func equal( s1 []int , s2 []int ) bool { return reflect.DeepEqual(s1, s2)}

July 8, 2022 · 1 min · jiezi

关于go:golang操作clickhouse使用入门

一、介绍这里应用 https://github.com/ClickHouse/clickhouse-go来做go语言调用clickhouse数据的 client 库

July 7, 2022 · 1 min · jiezi

关于go:GO如何初始化结构体切片

给定一个构造体如下所示,能够采纳不同的办法来定义一个构造体切片。 // 申明Stock构造体type Stock struct { Name string CurrentPrice float64 LowestPrice float64 HighestPrice float64}办法一:定义时间接赋值var stocks []Stock = []Stock{ { Name: "工商银行", CurrentPrice: 4.80, LowestPrice: 4.47, HighestPrice: 4.86, }, { Name: "农业银行", CurrentPrice: 3.03, LowestPrice: 2.90, HighestPrice: 3.11, },}[{工商银行 4.8 4.47 4.86} {农业银行 3.03 2.9 3.11}]办法二:先申明,再应用append一一增加 var stocks []Stock stock := Stock { Name: "工商银行", CurrentPrice: 4.80, LowestPrice: 4.47, HighestPrice: 4.86, } stocks = append(stocks, stock) stock = Stock { Name: "农业银行", CurrentPrice: 3.03, LowestPrice: 2.90, HighestPrice: 3.11, } stocks = append(stocks, stock)[{工商银行 4.8 4.47 4.86} {农业银行 3.03 2.9 3.11}]

July 7, 2022 · 1 min · jiezi

关于go:像素级模仿前yapi代码贡献者对apifox的公开信

这段时间收到很多敌人发来的信息,说一款api治理服务工具apifox跟咱们过后做的yapi很像,最近在全网推广,刚听到这个音讯,我还挺意外的,毕竟过后咱们做yapi是开源的。明天我闲来无事,下载他们的软件用了用,几乎愤慨至极。这款软件不仅抄了咱们理念, 还做成免费服务产品,这就很无耻。毕竟过后咱们做yapi的初心是心愿开发兄者有一款属于本人的API管理工具,而且是开源共建的。网上他们本人宣称本人是做借鉴产品,这就有点tx那味了,我再去搜一搜他们的创始人团队,跟敌人发我的相差不差,tx系进去的,当然没有黑tx的意思,毕竟当初yapi的成员有不少也去了tx。可能单纯文字大家领会到到底哪里剽窃了,上面就给大家一一列举apifox哪些照搬yapi的中央 一、yapi的文档设计比照apifoxVS咱们能够分明的看到,apifox除了文档主题和按钮摆放的地位不一样,文档后行的理念齐全效仿咱们yapiV 二、yapi的响应数据后果比照apifoxVS apifox的返回响应跟yapi的返回数据设置是截然不同的,这就很恶心了,除了题目不同,外面齐全截然不同...抄也没超出个新花样 三、yapi的工作流比照apifox的工作流VSmock性能间接就是照搬了咱们,所以当在研发圈宣称本人是原创产品,这就有点过分了 五、再来说说apifox抄的很失败的中央1.页面交互简单:说实话,第一次进来,我不晓得从何动手,常常看不懂我以后那在哪里个团队我的项目,页面看起来很简洁实则很简单;工具讲求的是极简,很多性能一两步就能实现通常要四五步,这是违反产品设计第一准则的。2.做了很多没用的性能:我作为一个老年程序员,很多性能我还是第一次见,每当看见这样的性能,我就会有纳闷:这个性能真的有这个需要吗? 底层理念很好,连续yapi的api设计实践,但做产品切记做大而全。这次发这个公开信,这是心愿可能给前辈们一些尊重,yapi作为收费面向coding圈的开发者产品,始终广受大家青睐广为大家应用。尽量避免商业化还给研发圈一个洁净的api管理工具。

July 6, 2022 · 1 min · jiezi

关于go:Go的省略符-omitempty和-两种方式详解

一、介绍官网文档 https://pkg.go.dev/encoding/json 有介绍 omitempty如果字段的值为空值(定义为 false、0、nil 指针、nil 接口值以及任何空数组、切片、映射或字符串),该选项在编码期间疏忽该字段"-"标签在编码和解码时总是省略字段二、omitempty空值省略2.1 json在构造体重的写法咱们常常看到如下的构造体中,用tag标注了解析json的格局首先咱们初始化一个空构造体Product package jsonimport ( "encoding/json" "testing")type Product struct { OrderId string `json:"order_id"` CommentS []Comment `json:"comment_s"`}type Comment struct { Id int Content string}func TestJson(t *testing.T) { p := Product{} s, _ := json.Marshal(p) t.Logf("%+v", p) t.Log(string(s))}输入如下: {OrderId: CommentS:[]} //p构造体{"order_id":"","comment_s":null} //p的json对象咱们发现空构造是有值的!!!和咱们料想的,一个空构造,对应一个空json {}有点不一样。是因为go中的构造体,都会有一个默认值的空值。如果空值省略??用 omitempty,代码批改如下: package jsonimport ( "encoding/json" "testing")type Product struct { OrderId string `json:"order_id,omitempty"` CommentS []Comment `json:"comment_s,omitempty"`}type Comment struct { Id int Content string}func TestJson(t *testing.T) { p := Product{} s, _ := json.Marshal(p) t.Logf("%+v", p) t.Log(string(s))}输入如下: ...

July 5, 2022 · 2 min · jiezi

关于go:使用Shifu在OpenYurt集群中接入RTSP协议摄像头

OpenYurt是一个云边计端算平台,借助OpenYurt的能力,能够将现有的Kubernetes集群转换成OpenYurt集群,并将Kubernetes的能力延长到边缘侧。OpenYurt为云边端协同开发提供了多样化的性能,如买通云边通信的YurtTunnel,为了方便管理节点单元利用部署/运维的Yurt-App-Manager以及提供了边缘自治的YurtHub。 开发者能够专一于云边端产品上的利用开发而不必放心底层架构的运维。Shifu作为Kubernetes原生的开源物联网开发架构,能够兼容各种物联网设施的协定并将其形象成一个为微服务软件对象。二者的能力有十分好的互补性。尤其是在OpenYurt中退出了YurtDeviceController当前,Shifu能够用OpenYurt原生的形式来将设施进行形象,大大提高物联网开发者的开发效率。 应用OpenYurt和Shifu当前,咱们能够将本来简单的IoT,云边协同开发转化为简略的web式开发。 简介本文是一个在OpenYurt集群中应用Shifu接入RTSP协定摄像头的指南,其中蕴含Shifu Framework, Docker, Linux, Kubernetes, OpenYurt的基本操作,任何开发者都能够浏览本文来学习Shifu Framework的开发方法。 本文中的Shifu Framework架构如下: 北向通过“deviceshifu-http-http”向上凋谢HTTP API接口,南向通过“rtsp-driver”来和理论设施交互 指标在server和edge端通过 yurtctl 部署OpenYurt, 并将edge端退出server端的集群在 edge 端部署网络摄像头的数字孪生实现通过HTTP对网络摄像头的近程自动化管控须要的设施两台运行 Linux的虚拟机,server和edge的配置别离为 4核16G内存和2核8G内存一个RTSP协定的网络摄像头,本文中用到的摄像头型号为海康威视的 “DS-2DE3Q140CN-W”软件环境-CentOS 7.9.2009 -Go v1.17.1 -yurtctl v0.6.1 -kubectl: v1.19.8 (installed by yurtctl) 步骤第一步:装置并部署OpenYurt集群本文参考了OpenYurt的官网教程,地址为: https://openyurt.io/docs/v0.6... 首先让咱们来下载OpenYurt,从官网的GitHub间接克隆我的项目: git clone https://github.com/openyurtio/openyurt.git接着让咱们下载v0.6.1版本的yurtctl curl -LO https://github.com/openyurtio/openyurt/releases/download/v0.6.1/yurtctl chmod +x yurtctlserver端的部署:在server端创立OpenYurt集群 ./yurtctl init --apiserver-advertise-address <SERVER_IP> --openyurt-version latest --passwd 123 看见如下信息即示意集群创立实现,这里的“--token”要记录一下用来将edge节点退出到集群中 接下来看一下各个Pod的运行状况,通过“kubectl get pods -A”: 遇到的几个问题如果在“ kubectl logs yurt-hub-server -n kube-system”里遇到 请尝试“kubectl apply -f config/setup/yurt-controller-manager.yaml” 办法来自: https://github.com/openyurtio... ...

July 5, 2022 · 3 min · jiezi

关于go:使用EMQX与Shifu实现设备联动

EMQX是一个在世界范畴内十分受欢迎的MQTT Broker。它领有基于Kubernetes的云原生架构,使得本身能力极为适宜当今越来越简单的物联网场景,让设施音讯的传输更为高效。因而,Shifu作为Kubernetes原生的Shifu框架,能够与EMQX完满联合,为EMQX提供智能的多协定设施联动的能力。 上面就让咱们看一下EMQX是如何与Shifu一起让设施间接更不便地联动吧。 简介本文将介绍如何在集群内部署EMQX和Shifu,接入一个以MQTT为通信形式的温度计和一个以RTSP为传输协定的海康威视摄像头,并退出一个利用与Shifu进行交互,使得每次温度计检测到超过37度的体温就会让摄像头拍下一张以后照片。 本文应用的简略架构如下 筹备本文应用了如下服务和工具: Kubernetes: 1.20.10kubectl, kubeadm, kubelet: 1.20.10golang: 1.16.10docker: 19.03.9EMQX: 4.1-rc1步骤第一步:部署Kubernetes本步能够参考Kubernetes的官网教程进行部署:https://kubernetes.io/docs/se... 在部署实现后咱们该当看到终端打印出如下信息: 第二步:部署Shifu注:Shifu尚未开源,请分割Shifu团队获取repo权限 将Shifu的GitHub repo克隆到本地 git clone https://github.com/Edgenesis/shifu.git而后能够通过下列命令部署Shifu: kubectl apply -f shifu/k8s/crd/install/shifu_install.yml部署实现后咱们该当看到Shifu的CRD controller曾经实现部署: 第三步:部署EMQX首先须要装置EMQX Operator Controller: $ curl -f -L "https://github.com/emqx/emqx-operator/releases/download/1.1.6/emqx-operator-controller.yaml" | kubectl apply -f -接着咱们写一个最简略的deployment.yaml: 而后就能够部署一个EMQX了: kubectl apply –f deployment.yaml 第四步:接入设施对于温度计,咱们只须要调整它的MQTT设置,让其能够向EMQX公布MQTT信息即可。 (如果是集群外的温度计,咱们能够通过Kubernetes Service来凋谢External IP供拜访) 对于摄像头,Shifu的repo曾经包含一个应用RTSP的海康威视摄像头的配置文件,咱们能够轻松更改配置文件中的IP、用户名、明码,将它接入Shifu: https://github.com/Edgenesis/shifu/tree/main/examples/rtspDeviceShifu/ 至此,咱们的设施曾经连贯结束,上面就能够开始联动了。 最初一步:联动利用咱们简略写一个python利用,用来实现上面的逻辑: 该利用向EMQX订阅temperature-shifu-mqtt的音讯,每次音讯都只包含一个示意以后温度的数字;如果以后温度大于37度,则操作摄像头拍摄一张照片并保留在本地。 以下是利用代码: 加个capture function封装所有摄像头的动作。接着咱们就能够将其部署到集群中,开始监督了: python3 app.py 10.244.0.33总结本文形容了如何让EMQX为Shifu赋予更高效的MQTT Broker能力,同时让Shifu与MQTT单干为设施提供联动能力。在事实的利用场景之中,咱们能够应用一个仅需一百余元的的红外温度计+摄像头组合,来代替数千元且体现并不稳固的测温摄像头,在大规模部署的状况下节俭巨额老本。 十分感谢您看到了这里,咱们期待您的反馈,如果感觉文章写得不错或者有任何倡议请毫不犹豫地留言。 本文由博客群发一文多发等经营工具平台 OpenWrite 公布

July 5, 2022 · 1 min · jiezi

关于go:如何在KubeEdge上部署Shifu

现在非常风行的开源我的项目KubeEdge给开发者提供了一个基于Kubernetes的云边协同计划。它胜利地将Kubernetes的集群编排能力交融到了物联网的边缘场景之中,使得对边缘算力的调度和治理更加轻量、也更加高效。 Shifu作为同样基于Kubernetes的开源物联网开发框架,它对于多种设施的兼容和虚拟化将为KubeEdge在边缘端的利用提供助力。事实上,二者在能力上领有十分好的互补性,在多设施兼容的同时,运行在KubeEdge上的Shifu能够轻松治理边缘端运行的轻量Pod。 有了KubeEdge + Shifu的强强联手,咱们就能够把IoT设施形象成API,把本来简单的传统物联网开发模式转化为简略的web开发模式! 上面就让咱们来看一下如何让Shifu运行在KubeEdge上,并且给开发者们提供价值吧! 简介本文将简略介绍在KubeEdge上部署Shifu的步骤,并接入一个海康威视的摄像头(应用RTSP进行视频流传输)的实例,为KubeEdge的架构退出海康威视摄像头反对。 本文应用的简略架构如下: 筹备本文应用了如下服务和工具: Kubernetes: 1.21.5kubectl, kubeadm, kubelet: 1.21.5golang: 1.16.10docker: 19.03.9KubeEdge: 1.7.2同时,KubeEdge的cloud端和edge端别离运行在不同的Linux实例上,环境均为Ubuntu Server 20.04 上述服务和工具中,cloud端须要装置全副上述服务和工具,而edge端只须要装置docker和KubeEdge。 步骤第一步:在cloud端部署Kubernetes本步能够参考Kubernetes的官网教程进行部署: https://kubernetes.io/docs/se... 在部署实现后咱们该当看到终端打印出如下信息: 第二步:在cloud端部署Shifu注:Shifu尚未开源,请分割Shifu团队获取repo权限 将Shifu的github repo克隆到本地: git clone https://github.com/Edgenesis/shifu.git而后能够通过下列命令部署Shifu: kubectl apply -f shifu/k8s/crd/install/shifu_install.yml部署实现后咱们该当看到Shifu的CRD controller曾经实现部署: 第三步:在cloud端部署KubeEdge本步能够参考KubeEdge的官网教程,应用keadm进行部署: https://kubeedge.io/zh/docs/s... 在部署实现后咱们该当看到终端打印出如下信息: 第四步:在cloud端获取token运行如下命令: keadm gettoken请保留取得的token以便edge端应用。 当初cloud端的配置告一段落,咱们当初切换到edge端的机器,让它退出集群。 第五步:在edge端退出集群在edge端运行如下命令: keadm join --cloudcore-ipport="<cloud端advertise-address>:10000" --token=<第4步取得的token>在部署实现后咱们该当看到终端打印出如下信息: 此时切换回到cloud端,查看nodes: 咱们能够看到cloud和edge都曾经部署结束了。 当初咱们能够开始部署设施了。 通过KubeEdge,咱们能够做到只在cloud端进行Kubernetes操作并且部署到edge端,同时放弃edge端无需装置Kubernetes组件,保障轻量化。 第六步:在cloud端批改海康威视摄像头的配置文件Shifu须要简略的配置文件来实现数字孪生的生成。在Shifu中,数字孪生被称为deviceShifu,以Pod的模式运行在集群里。 Shifu提供了接入海康威视摄像头的配置文件,其门路如下: https://github.com/Edgenesis/... Shifu默认将deviceShifu部署在领有残缺Kubernetes实例的机器上。在KubeEdge的环境下,边缘端无需运行残缺的Kubernetes,因而Shifu也筹备了针对云边协同环境的轻量的deviceShifu供应用。咱们能够更改“deviceshifu-camera-deployment.yaml”,让它应用边缘侧的deviceShifu,并增加“nodeName”将其部署在edge node: 第七步:部署海康威视摄像头pod在cloud端,运行下列命令: kubectl apply -f shifu/examples/rtspDeviceShifu/camera-deployment此时,咱们能够查看camera相干pod: 能够看到camera deviceShifu曾经部署到edge端了。 最初一步:在edge端进行确认在edge端,咱们能够看到camera相干docker容器曾经在运行了: 咱们能够非常简单地调用deviceShifu提供的capture/stream/info/move 等一系列HTTP API,对摄像头进行操作,比方上面的动图。 相干命令: curl edgedevice-camera/move自此,咱们就实现了在KubeEdge上运行Shifu的全副步骤。 ...

July 5, 2022 · 1 min · jiezi

关于go:如何扩展Shifu的西门子PLC驱动能力

在上篇中咱们分享了《如何利用Shifu接入公有驱动的设施》。Shifu内置了几个罕用的驱动,像是西门子S7系列的PLC,RTSP协定的摄像头等。物联网上的需要因人而异。在这次的文章中咱们将介绍如何扩大Shifu自带驱动的能力。 简介本文是一个应用Shifu Framework的西门子PLC驱动的扩大指南,其中蕴含Linux, Python, Shifu Framework, Docker, Kubernetes的基本操作,任何开发者都能够浏览本文来学习Shifu Framework的开发方法。 本文中的Shifu Framework架构如下北向通过"deviceshifu-http-http”容器向上凋谢HTTP API接口,南向通过"siemens-plc-driver”容器来和理论设施交互 指标 在本地运行K8s集群并装置Shifu Framework批改西门子PLC的驱动增加一个性能打包驱动,生成容器镜像在Shifu中部署PLC的数字孪生实现扩大西门子PLC的能力本次分享中用到的设施 开发环境(本文中为运行在Windows 11 Pro上面的WSL子系统,零碎为Ubuntu 20.04)西门子PLC(反对S7协定即可,本文中用到的型号为西门子S7-1200系列)须要的基本知识-根本的Python-Linux命令行基本操作(创立文件,装置利用,运行程序)-Docker/containerd基本操作-K8s基本操作 步骤第一步:在本地运行Kubernetes(如果已装置Shifu Framework,请间接跳到第三步)为了运行Shifu,咱们须要一个Kubernetes的集群,这里不限度用户应用的版本,本文中应用的是利用kind建设的测试Kubernetes集群。如果资源受限的话也能够思考应用k3d或者microk8s。kind的装置教程: https://kind.sigs.k8s.io/docs... 具体装置步骤本文将不再赘述。 kind装置结束后能够通过“kind version”来查看以后版本,本文中的版本为”v0.12.0”: $ kind version kind v0.12.0 go1.17.8 linux/amd64接下来应用“kind create cluster”创立集群,整个过程会继续几分钟,因网速而异: $ kind create cluster Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.23.4) ✓ Preparing nodes ✓ Writing configuration ✓ Starting control-plane ️ ✓ Installing CNI ✓ Installing StorageClass Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Not sure what to do next? Check out https://kind.sigs.k8s.io/docs/user/quick-start/创立实现后咱们能够通过“kubectl get nodes”查看集群状态,当显示“Ready”即可: ...

July 5, 2022 · 3 min · jiezi

关于go:gozero微服务实战系列八如何处理每秒上万次的下单请求

在前几篇的文章中,咱们花了很大的篇幅介绍如何利用缓存优化零碎的读性能,究其原因在于咱们的产品大多是一个读多写少的场景,尤其是在产品的初期,可能少数的用户只是过去查看商品,真正下单的用户非常少。但随着业务的倒退,咱们就会遇到一些高并发写申请的场景,秒杀抢购就是最典型的高并发写场景。在秒杀抢购开始后用户就会疯狂的刷新页面让本人尽早的看到商品,所以秒杀场景同时也是高并发读场景。那么应答高并发读写场景咱们怎么进行优化呢? 解决热点数据秒杀的数据通常都是热点数据,解决热点数据个别有几种思路:一是优化,二是限度,三是隔离。 优化优化热点数据最无效的方法就是缓存热点数据,咱们能够把热点数据缓存到内存缓存中。 限度限度更多的是一种爱护机制,当秒杀开始后用户就会一直地刷新页面获取数据,这时候咱们能够限度单用户的申请次数,比方一秒钟只能申请一次,超过限度间接返回谬误,返回的谬误尽量对用户敌对,比方 "店小二正在忙" 等敌对提醒。 隔离秒杀零碎设计的第一个准则就是将这种热点数据隔离进去,不要让1%的申请影响到另外的99%,隔离进去后也更不便对这1%的申请做针对性的优化。具体到实现上,咱们须要做服务隔离,即秒杀性能独立为一个服务,告诉要做数据隔离,秒杀所调用的大部分是热点数据,咱们须要应用独自的Redis集群和独自的Mysql,目标也是不想让1%的数据有机会影响99%的数据。 流量削峰针对秒杀场景,它的特点是在秒杀开始那一刹那霎时涌入大量的申请,这就会导致一个特地高的流量峰值。但最终可能抢到商品的人数是固定的,也就是不论是100人还是10000000人发动申请的后果都是一样的,并发度越高,有效的申请也就越多。然而从业务角度来说,秒杀流动是心愿有更多的人来参加的,也就是秒杀开始的时候心愿有更多的人来刷新页面,然而真正开始下单时,申请并不是越多越好。因而咱们能够设计一些规定,让并发申请更多的延缓,甚至能够过滤掉一些有效的申请。 削峰实质上是要更多的延缓用户申请的收回,以便缩小和过滤掉一些有效的申请,它听从申请数要尽量少的准则。咱们最容易想到的解决方案是用音讯队列来缓冲刹时的流量,把同步的间接调用转换成异步的间接推送,两头通过一个队列在一端承接刹时的流量洪峰,在另一端平滑的将音讯推送进来,如下图所示: 采纳音讯队列异步解决后,那么秒杀的后果是不太好同步返回的,所以咱们的思路是当用户发动秒杀申请后,同步返回响应用户 "秒杀后果正在计算中..." 的提示信息,当计算完之后咱们如何返回后果给用户呢?其实也是有多种计划的。 一是在页面中采纳轮询的形式定时被动去服务端查问后果,例如每秒申请一次服务端看看有没有处理结果,这种形式的毛病是服务端的申请数会减少不少。二是被动push的形式,这种就要求服务端和客户端放弃长连贯了,服务端解决完申请后被动push给客户端,这种形式的毛病是服务端的连接数会比拟多。还有一个问题就是如果异步的申请失败了该怎么办?我感觉对于秒杀场景来说,失败了就间接抛弃就好了,最坏的后果就是这个用户没有抢到而已。如果想要尽量的保障偏心的话,那么失败了当前也能够做重试。 如何保障音讯只被生产一次kafka是可能保障"At Least Once"的机制的,即音讯不会失落,但有可能会导致反复生产,音讯一旦被反复生产那么就会造成业务逻辑解决的谬误,那么咱们如何防止音讯的反复生产呢? 咱们只有保障即便生产到了反复的音讯,从生产的最终后果来看和只生产一次的后果等同就好了,也就是保障在音讯的生产和生产的过程是幂等的。什么是幂等呢?如果咱们生产一条音讯的时候,要给现有的库存数量减1,那么如果生产两条雷同的音讯就给库存的数量减2,这就不是幂等的。而如果生产一条音讯后处理逻辑是将库存的数量设置为0,或者是如果以后库存的数量为10时则减1,这样在生产多条音讯时所失去的后果就是雷同的,这就是幂等的。说白了就是一件事无论你做多少次和做一次产生的后果都是一样的,那么这就是幂等性。 咱们能够在音讯被生产后,把惟一id存储在数据库中,这里的惟一id能够应用用户id和商品id的组合,在解决下一条音讯之前先从数据库中查问这个id看是否被生产过,如果生产过就放弃。伪代码如下: isConsume := getByID(id)if isConsume { return } process(message)save(id)还有一种形式是通过数据库中的惟一索引来保障幂等性,不过这个要看具体的业务,在这里不再赘述。 代码实现整个秒杀流程图如下: 应用kafka作为音讯队列,所以要先在本地装置kafka,我应用的是mac能够用homebrew间接装置,kafka依赖zookeeper也会主动装置 brew install kafka装置完后通过brew services start启动zookeeper和kafka,kafka默认侦听在9092端口 brew services start zookeeperbrew services start kafkaseckill-rpc的SeckillOrder办法实现秒杀逻辑,咱们先限度用户的申请次数,比方限度用户每秒只能申请一次,这里应用go-zero提供的PeriodLimit性能实现,如果超出限度间接返回 code, _ := l.limiter.Take(strconv.FormatInt(in.UserId, 10))if code == limit.OverQuota { return nil, status.Errorf(codes.OutOfRange, "Number of requests exceeded the limit")}接着查看以后抢购商品的库存,如果库存有余就间接返回,如果库存足够的话则认为能够进入下单流程,发消息到kafka,这里kafka应用go-zero提供的kq库,非常简单易用,为秒杀新建一个Topic,配置初始化和逻辑如下: Kafka: Addrs: - 127.0.0.1:9092 SeckillTopic: seckill-topicKafkaPusher: kq.NewPusher(c.Kafka.Addrs, c.Kafka.SeckillTopic)p, err := l.svcCtx.ProductRPC.Product(l.ctx, &product.ProductItemRequest{ProductId: in.ProductId})if err != nil { return nil, err}if p.Stock <= 0 { return nil, status.Errorf(codes.OutOfRange, "Insufficient stock")}kd, err := json.Marshal(&KafkaData{Uid: in.UserId, Pid: in.ProductId})if err != nil { return nil, err}if err := l.svcCtx.KafkaPusher.Push(string(kd)); err != nil { return nil, err}seckill-rmq生产seckill-rpc生产的数据进行下单操作,咱们新建seckill-rmq服务,构造如下: ...

July 5, 2022 · 2 min · jiezi

关于go:如何根据mysql表生成结构体一个开源小工具的探索之旅

1. 目录 2. 背景 最近在工作中会有依据mysql表在go中编写一个对应的构造体这样的coding,尽管数据表并不是简单,字段不是很多,代码写起来也比拟快,为了疾速的实现工作我一开始就是依照数据表的列一个接着一个的来写。但我是个懒人,反复的工作心愿能够通过代码帮我实现,因为前面也有相似的工作,如果我有对应的代码生成工具会不便很多,并且用本人做进去的工具内心中或多或少会有一些成就感。所以我心生一个想法,为什么我不搞一个简略的工具,来依据表的构造生成构造体呢?所以我就钻研了一下,看了一些材料和,包含mysql的information_schame数据库, sqlx, go规范库里的text/template等内容 ,特此写下文章分享给读者敌人们,心愿读者敌人们有所播种。话不多说,让咱们开始本次的探索之旅。 3. 怎么找到mysql的表构造信息 在装置mysql的时候,会发现除了本人创立的数据库之外,还会有一些别的数据库是默认给你创立好的。咱们能够登陆mysql应用上面语句查看。 mysql> SHOW DATABASES;+--------------------+| Database |+--------------------+| elliot_test || information_schema || mysql || performance_schema || sys |+--------------------+5 rows in set (0.01 sec) 大家能够看下我本地的mysql,其中elliot_test是我创立的数据库,其余的information_schema, mysql, performance_schema, sys(5.7 以上的叫sys,5.6的自带的是test),这些是mysql自带的。那么这些数据库有什么用呢,记录的都是什么信息呢? information_schema:保留了MySQl服务所有数据库的信息。具体MySQL服务有多少个数据库,各个数据库有哪些表,各个表中的字段是什么数据类型,各个表中有哪些索引,各个数据库要什么权限能力拜访。mysql:保留MySQL的权限、参数、对象和状态信息。如哪些user能够拜访这个数据,DB的参数。performance_schema:次要用于收集数据库服务器性能参数,提供过程期待的详细信息, 保留历史的事件汇总信息,为提供MySQL服务器性能做出具体的判断。test:5.6自带,没有什么货色。sys:Sys库所有的数据源来自:performance_schema。指标是把performance_schema的把复杂度升高。 在理解了mysql自带数据库的性能之后,这时候咱们就晓得了要查看mysql的表构造信息其实咱们只须要在information_schema这外面找就好了,因为这里蕴含了所有数据库的信息,包含有哪些表,什么表有什么字段,这正是咱们须要的。因为information_schema外面表比拟多,这里就不作展现并且一一介绍了,感兴趣的读者敌人们能够登陆mysql应用use information_schema; 命令切换数据库,而后应用show tables;命令查看这上面有什么表。这里就介绍几个比拟重要的,或者说咱们可能会用到的。 SCHEMATA表:提供了以后mysql实例中所有数据库的信息。是show databases的后果取之此表。TABLES表:提供了对于数据库中的表的信息(包含视图)。具体表述了某个表属于哪个schema,表类型,表引擎,创立工夫等信息。是show tables from schemaname的后果取之此表。COLUMNS表:提供了表中的列信息。具体表述了某张表的所有列以及每个列的信息。是show columns from schemaname.tablename的后果取之此表。 咱们这里关注的是COLUMNS这张表,这张表记录的是每张表的列信息,咱们应用desc命令看下这张表都有些什么。 能够看到外面记录的货色还是很多的,如果聚焦于咱们的需要:依据表构造构建构造体。那咱们的关注点只须要晓得他的列名(COLUNM_NAME),以及对应的数据类型(DATA_TYPE)就能够了。所以咱们很容易写出上面这条sql查问到咱们须要的信息。 不过咱们也能够用下图这种形式去查看。 取得了这些信息之后,就能够开始咱们的编码工作了。 4. 技术实现 在这一章节会探讨如何实现这个需要,会探讨如何简略实现一个mysql的client,也就是一个简略的mysql驱动,当然并不是说在这里我要实现这个货色,只是探讨一下如果要写一个的话大略须要怎么做。另外会讲到实现这个需要用到的一些次要技术,包含sqlx, go规范库的text/template,还有实现这个需要的外围逻辑解说。 4.1 以何种形式与数据库交互 其实这里个别是没有探讨的必要的,抉择一个开源的库去和执行下面提到的获取数据表信息的sql语句就好了。不过如果我想把这个工具当作一个开源的我的项目去做,可能会思考为了轻量化而尽量减少这个我的项目的依赖,以及开源的mysql驱动库对于这个我的项目来说会提供一些咱们自身并不需要的性能。不过在这里我并不打算要本人入手实现一个简略的查问语句解决的工具,因为想要疾速的实现这个小工具,如果破费大量的工夫在实现别的货色下面,那么我工作就失去了焦点,这并不是我想要的。不过之前看过不少go-mysql这个开源库的源码,对数据库驱动库是怎么实现的还是有肯定理解的,实际上实现起来也并不是很难。这里的话能够和读者敌人们分享一下如果我要手动实现的话,我要怎么做。 这里思路能够简略的概括一下,就是我要吧本人模拟成mysql的client端,只有咱们遵循mysql的通信协定就能够了。就如同go的构造与其实现类,实现类只有实现了接口的办法就能够看作是这个接口的实现类。而咱们只须要遵循mysqld的client端的一些通信协定,mysql天然也会把咱们看成是一个client端,当初支流的CDC组件个别的做法就是把本人伪装成mysql的从节点去获取mysql的数据变更,其实也是一样的情理。那么要把本人变成mysql的client端咱们都须要遵循哪些协定呢? ...

July 5, 2022 · 2 min · jiezi

关于go:如何优雅地实现多数据库的发件箱模式

发件箱模式简介一个微服务可能须要执行“存数据库”和“发送事件”两个步骤。例如公布一篇文章后,须要更新作者的发文统计信息。业务上要求两个操作同时失败,或者同时胜利,而不能呈现一个胜利一个失败。如果最终文章公布了,更新发文统计失败了,就会导致数据不统一。 发件箱模式是解决这个问题的最罕用模式,其原理为: 本地业务作为一个事务运行,在提交事务之前,将事件写入到音讯表;提交事务时,会同时提交业务,以及事件通过轮询音讯表或者监听binlog形式,将事件发给音讯队列 轮询形式:每隔1s或者0.2s取出音讯表中事件,发给音讯队列,而后删除事件监听binlog形式:通过Debezium等数据库工具,监听数据库的binlog,获取事件,发送给音讯队列编写消费者,处理事件因为1中,业务和事件的提交是在同一个事务,保障了两者会同时提交。在步骤2,3中,都是不会失败的操作,如果两头产生宕机事件等,都会重试,并最终胜利。 对于前述的发文后提交统计信息场景,上述计划保障了统计信息被最终更新,数据会达到最终统一 多数据库的问题在当今风行的微服务架构下,通常一个微服务会采纳一个独自的数据库。当多个服务须要应用发件箱模式时,那么传统的发件箱架构就比拟难以保护。 采纳轮询形式获取事件:须要在轮询工作中,编写多个数据库的轮询工作采纳监听binlog获取事件:须要监听多个数据库的binlog上述两种获取事件的形式,在面对数量较多的数据库,可维护性差。而且该架构的弹性并不好,如果数据库多,而工夫产生的事件少,也会导致该架构的负载高,浪费资源。最现实的架构负载是,只跟发送的事件数量相干,跟其余因素无关。 解决方案开源分布式事务框架 https://github.com/dtm-labs/dtm 外面的二阶段音讯,能够很好的解决这个问题。上面是一个跨行转账业务的应用示例: msg := dtmcli.NewMsg(DtmServer, gid). Add(busi.Busi+"/TransIn", &TransReq{Amount: 30})err := msg.DoAndSubmitDB(busi.Busi+"/QueryPreparedB", db, func(tx *sql.Tx) error { return busi.SagaAdjustBalance(tx, busi.TransOutUID, -req.Amount, "SUCCESS")})这部分代码中 首先生成一个DTM的msg全局事务,传递dtm的服务器地址和全局事务id给msg增加一个分支业务逻辑,这里的业务逻辑为余额转入操作TransIn,而后带上这个服务须要传递的数据,金额30元而后调用msg的DoAndSubmitDB,这个函数保障业务胜利执行和msg全局事务提交,要么同时胜利,要么同时失败 第一个参数为回查URL,具体含意稍后说第二个参数为sql.DB,是业务拜访的数据库对象第三个参数是业务函数,咱们这个例子中的业务是给A扣减30元余额胜利流程DoAndSubmitDB是如何保障业务胜利执行与msg提交的原子性的呢?请看如下的时序图: 个别状况下,时序图中的5个步骤会失常实现,整个业务依照预期进行,全局事务实现。这外面有个新的内容须要解释一下,就是msg的提交是依照两个阶段发动的,第一阶段调用Prepare,第二阶段调用Commit,DTM收到Prepare调用后,不会调用分支事务,而是期待后续的Submit。只有收到了Submit,开始分支调用,最终实现全局事务。 异常情况在分布式系统中,各类的宕机和网络异样都是须要思考的,上面咱们来看看可能产生的问题: 首先咱们要达到的最重要指标是业务胜利执行和msg事务是原子操作,那么如果后面时序图中,当Prepare音讯发送胜利之后,Submit音讯发送胜利之前,出现异常宕机会如何?这个时候dtm会检测到该事务超时,会进行回查。对于开发人员来说,该回查很简略,只须要粘贴如下代码即可: app.GET(BusiAPI+"/QueryPreparedB", dtmutil.WrapHandler2(func(c *gin.Context) interface{} { return MustBarrierFromGin(c).QueryPrepared(dbGet()) }))如果您应用的不是go框架gin,那么您须要依据您的框架做一些小批改,然而该代码是通用的,适宜您的每个业务。 回查的次要原理次要是通过音讯表,然而dtm的回查通过认真的论证,可能解决以下状况: 回查时,本地事务未开始回查时,本地事务还在进行中回查时,本地事务已回滚回查时,本地事务已提交具体的回查原理有些简单,已申请了专利,这里不做具体介绍,详情能够参考https://dtm.pub/practice/msg.html 多数据库反对该计划下,如果您须要解决多数据库,运维层面,只须要给相应的库创立好消息表;代码层面,只须要在回查的中央,传入不同的数据库连贯即可。 比照于原有的轮询表,以及监听binlog计划,运维老本大大降低。该架构的负载仅仅与事件数量相干,跟数据库数量等其余因素无关,具备了更好的弹性。 更多存储引擎的反对dtm的二阶段音讯,不仅提供了数据库的反对DoAndSubmitDB,还提供了NoSQL的反对 Mongo反对上面这段代码,能够保障Mongo下的业务和音讯两者同时提交 err := msg.DoAndSubmit(busi.Busi+"/RedisQueryPrepared", func(bb *dtmcli.BranchBarrier) error { return bb.MongoCall(MongoGet(), func(sc mongo.SessionContext) error { return SagaMongoAdjustBalance(sc, sc.Client(), TransOutUID, -reqFrom(c).Amount, reqFrom(c).TransOutResult) })})Redis反对上面这段代码,能够保障Redis下的业务和音讯两者同时提交 ...

July 4, 2022 · 1 min · jiezi

关于go:golang-简洁架构介绍

在 go 开发中,首先遇到的一个问题,应该怎么设计开发架构。一个好的架构能够在后续的我的项目开发中可能更好的应用我的项目的推动,同时可能让团队的配合更加的顺利。 一个简洁的架构应该具备如下几个特点: 不依赖框架:好的架构该当是独立的,不依赖框架可扩大的不依赖 UI:UI 应该很容易批改,而不须要改业务局部代码独立于数据库:不绑定数据库,能够在不改变业务代码的状况下,更换其余数据库可测试的: 可能比拟不便测试用例的编写上面目录构造引自 《go 整洁模板》 ├─cmd 利用入口│ └─app├─config├─docs // 寄存文档├─internal│ ├─app│ ├─controller // 控制器│ │ ├─amqp_rpc│ │ └─http│ │ └─v1│ ├─entity // 实体层│ ├─middleware // 中间件│ └─usecase│ ├─repo // 数据库操作│ └─webapi // RESTful API├─migrations├─pkg //以被内部程序平安导入的包│ ├─crypto│ ├─httpresponse│ ├─httpserver│ ├─logger│ ├─mysql│ ├─postgres│ ├─rabbitmq│ └─redis分层介绍层级调用是通过外层->内层,内层是不晓得外层的存在的。在内层代码中,不应该援用外层申明的:变量、类、办法,构造体。 这样做能够保障内层的独立,如果外层扭转,不会对内层造成影响。 这里层级有四层别离为:实体(Entities)、用例(Use Cases)、接口适配器(Interface Adapters)、框架和驱动因素(Frameworks and Drivers) 。 构造的层级并不是固定的,能够依据本身需要在此基础上进行增减。 实体(Entities)internal/entity实体相似于 MVC 中的 M 层,定义了应用程序的业务对象,实体除了定义构造体外,还能够有相干办法(参数校验)。 用例(usecase) internal/usecase业务逻辑. 办法按应用领域分组(在独特的根底上)每个组都有本人的构造一个文件对应一个构造 Repositories、webapi、rpc等业务逻辑构造被注入到业务逻辑构造中接口适配器(Interface Adapters) internal/controller该层级次要用于数据传递,以 API 调用为例,将用户传递的参数进行整顿成用例(usecase)所需的数据格式,接管用例(usecase) 解决后返回的数据转换成响应的数据。 ...

July 4, 2022 · 1 min · jiezi

关于go:gozero微服务实战系列七请求量这么高该如何优化

前两篇文章咱们介绍了缓存应用的各种最佳实际,首先介绍了缓存应用的根本姿态,别离是如何利用go-zero主动生成的缓存和逻辑代码中缓存代码如何写,接着解说了在面对缓存的穿透、击穿、雪崩等常见问题时的解决方案,最初还重点解说了如何保障缓存的一致性。因为缓存对于高并发服务来说切实是太重要了,所以这篇文章咱们还会持续一起学习下缓存相干的常识。 本地缓存当咱们遇到极其热点数据查问的时候,这个时候就要思考本地缓存了。热点本地缓存次要部署在应用服务器的代码中,用于阻挡热点查问对于Redis等分布式缓存或者数据库的压力。 在咱们的商城中,首页Banner中会放一些广告商品或者举荐商品,这些商品的信息由经营在治理后盾录入和变更。这些商品的申请量十分大,即便是Redis也很难扛住,所以这里咱们能够应用本地缓存来进行优化。 在product库中先建一张商品经营表product_operation,为了简化只保留必要字段,product_id为推广经营的商品id,status为经营商品的状态,status为1的时候会在首页Banner中展现该商品。 CREATE TABLE `product_operation` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `product_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '商品id', `status` int NOT NULL DEFAULT '1' COMMENT '经营商品状态 0-下线 1-上线', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫', PRIMARY KEY (`id`), KEY `ix_update_time` (`update_time`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品经营表';本地缓存的实现比较简单,咱们能够应用map来本人实现,在go-zero的collection中提供了Cache来实现本地缓存的性能,咱们间接拿来用,反复造轮子从来不是一个理智的抉择,localCacheExpire为本地缓存过期工夫,Cache提供了Get和Set办法,应用非常简单 localCache, err := collection.NewCache(localCacheExpire)先从本地缓存中查找,如果命中缓存则间接返回。没有命中缓存的话须要先从数据库中查问经营位商品id,而后再聚合商品信息,最初回塞到本地缓存中。具体代码逻辑如下: func (l *OperationProductsLogic) OperationProducts(in *product.OperationProductsRequest) (*product.OperationProductsResponse, error) { opProducts, ok := l.svcCtx.LocalCache.Get(operationProductsKey) if ok { return &product.OperationProductsResponse{Products: opProducts.([]*product.ProductItem)}, nil } pos, err := l.svcCtx.OperationModel.OperationProducts(l.ctx, validStatus) if err != nil { return nil, err } var pids []int64 for _, p := range pos { pids = append(pids, p.ProductId) } products, err := l.productListLogic.productsByIds(l.ctx, pids) if err != nil { return nil, err } var pItems []*product.ProductItem for _, p := range products { pItems = append(pItems, &product.ProductItem{ ProductId: p.Id, Name: p.Name, }) } l.svcCtx.LocalCache.Set(operationProductsKey, pItems) return &product.OperationProductsResponse{Products: pItems}, nil}应用grpurl调试工具申请接口,第一次申请cache miss后,前面的申请都会命中本地缓存,等到本地缓存过期后又会从新回源db加载数据到本地缓存中 ...

July 4, 2022 · 1 min · jiezi

关于go:如何写好单元测试以及go单元测试工具testify简单介绍

文章目录 背景 最近在工作和业余开源奉献中,和单元测试接触的比拟频繁。然而在这两个场景之下写进去的单元测试貌似不太一样,即使是同一个代码场景,明天写进去的单元测试和昨天写的也不是很一样,我感触到了对于单元测试,我没有一个比拟对立的标准和一套单元测试实际的方法论。在写了一些单元测试之后我开始想去理解写单元测试的一些最佳实际和技巧。(其实起初我反思的时候感觉,我应该先去学习单元测试相干的最佳实际,现有一个大抵的概念,再去实操会好一些。)在这里总结成一篇文章分享给大家,心愿读者敌人们有所播种。 1. 为什么要写单元测试 单元测试是一个优良我的项目必不可少的一部分,在一个频繁变动和多人单干的我的项目中显得尤为要害。站在写程序的人的角度登程,其实很多时候你并不能百分之百确定你的代码就是一点问题都没有的,在计算机的世界里其实不确定的因素很多,比方咱们可能不确定代码中的一些依赖项,在理论代码执行的过程中他会合乎咱们的预期,咱们也不能确定咱们写的逻辑是否能够涵盖所有的场景,比方可能会存在写了if没有些else的状况。所以咱们须要写自测去自证咱们的代码没有问题,当然写自测也并不能够保障代码就完完全全没有问题了,只能说能够做到尽可能的防止问题吧。其次对于一个多人参加的我的项目来说,开源我的项目也好,工作中多人合作也好,如果要看懂一段逻辑是干嘛的,或者要理解代码是怎么运作的,最好的切入点往往是看这个我的项目的单元测试或者参加编写这个我的项目的单元测试。我集体要学习一个开源我的项目也是首先从单元测试动手的,单元测试能够通知我一段逻辑这段代码是干什么的,他的预期是输出是什么,产出是什么,什么场景会报错。 2. 如何写好单元测试 这一章节将会介绍为什么一些代码比拟难以测试,以及如何写一个比拟好的测试。在这里会联合一些我看过的一些开源我的项目的代码进行举例讲述。 2.1 什么代码比拟难测试 其实不是所有的代码都是能够测试的,或者说有的代码其实是不容易测试的,有时候为了不便测试,须要把代码重形成容易测试的样子。然而很多时候在写单元测试之前,你都不晓得你写的代码其实是不能够测的。这里我举go-mysql的一些代码例子来论述不可测或者不容易测的因素都有哪些。 go-mysql 是pingcap首席架构师唐刘大佬实现的一个mysql工具库,外面提供了一些实用的工具,比方canal模块能够生产mysql-binlog数据实现mysql数据的复制,client模块是一个简略的mysql驱动,实现与mysql的交互等等,其余性能能够去github上看readme具体介绍。最近因为工作须要看了大量这个库的源码,所以在这里拿一些代码进去举举例子。 2.1.1 代码依赖内部的环境 在咱们理论些代码的时候,理论一部分代码会比拟依赖内部的环境,比方咱们的一些逻辑可能会须要连贯到mysql,或者你会须要一个tcp的连贯。比方上面这段代码: /* Conn is the base class to handle MySQL protocol.*/type Conn struct { net.Conn bufPool *BufPool br *bufio.Reader reader io.Reader copyNBuf []byte header [4]byte Sequence uint8} 这个是go-msyql解决网络连接的构造体,咱们能够看到的是这个构造体外面包裹的是一个net.Conn接口,并不是某一个具体的实现,这样子提供了很灵便的测试形式,只须要mock一个net.Conn的实现类就能够测试他的相干办法了,如果这里封装的是net.Conn的具体实现比方TCPConn,这样就变得不好测试了,在写单元测试的时候你可能须要给他提供一个TCP的环境,这样子其实比拟麻烦了。 第二个例子来自go-mysql canal这个模块,这个模块的次要性能通过生产mysql binlog的模式来复制mysql的数据,那么这里的整体逻辑怎么测试呢,这个模块是伪装成mysql的从节点去复制数据的,那么主节点在哪里呢,这里就要切切实实的mysql环境了。咱们能够看看作者是怎么测试的,这里代码太长我就不贴出来了,把GitHub的代码链接贴在这里,感兴趣的读者能够去看点击这里看github代码。作者在CI环境里弄了一个mysql的环境,而后在测试之前通过执行一些sql语句来构建测试的环境,在测试的过程中也是通过执行sql的形式来产生对应的binlog去验证本人的逻辑。 2.1.2 代码太过冗余 有时候写代码可能就是图个痛快,一把梭哈把所有的逻辑都放在一个函数外面,这样就会导致过多的逻辑沉积在一起,测试的时候分支可能过多,所以为了单元测试看起来比拟简洁可能须要咱们把这样的逻辑进行拆分,把专门做一件事件的逻辑放在一起,去做对应的测试。而后对整段逻辑做整体测试就好。 2.2 如何写好一个单元测试 为了不便去形容这个一些内容,这里我简略的提供一个这样的函数。这个函数逻辑比较简单,就是输出一个名字,而后返回一个跟你打招呼的信息。 func Greeter(name string) string { return "hi " + name} 那么如何写这个函数的测试呢。我了解有两个要害的点,一是单元测试的命名,二是单元测试的内容架构。 2.2.1 单元测试的命名 命名其实也是有考究的,我了解单元测试也是给他人看的,所以当我看你写的单元测试的时候,最好在命名上有:测试对象,输出,预期输入。这样能够通过名字晓得这个单元测试大抵内容是什么。 ...

July 3, 2022 · 2 min · jiezi

关于go:Go题库16读写锁底层是怎么实现的

题目解析 GOLANG ROADMAP社区答案(自在)读写锁的底层是基于互斥锁实现的。 为什么有读写锁,它解决了什么问题?(应用场景)它的底层原理是什么?在这里我会联合 Go 中的读写锁 RWMutex 进行介绍。 咱们通过与 Mutex 比照得出答案。Mutex 是不辨别 goroutine 对共享资源的操作行为的,在读操作、它会上锁,在写操作,它也会上锁,当一段时间内,读操作居多时,读操作在 Mutex 的爱护下也不得不变为串行拜访,对性能的影响也就比拟大了。 RWMutex 读写锁的诞生为了辨别读写操作,在进行读操作时,goroutine 就不用傻傻的期待了,而是能够并发地访问共享资源,将串行读变成了并行读,进步了读操作的性能。 读写锁针对解决一类问题:readers-writes ,同时有多个读或者多个写操作时,只有有一个线程在执行写操作,其余的线程都不能进行读操作。 读写锁其实有三种工作模型: Read-perferring
优先读设计,可能会导致写饥饿Write-prferring
优先写设计,防止写饥饿不指定优先级
不辨别优先级,解决饥饿问题Go 中的读写锁,工作模型是 Write-prferring 计划。 答案(栾龙生)读写锁解决问题 次要利用于写操作少,读操作多的场景。读写锁满足以下四条规定。 写锁须要阻塞写锁:一个协程领有写锁时,其余协程写锁定须要阻塞;写锁须要阻塞读锁:一个协程领有写锁时,其余协程读锁定须要阻塞;读锁须要阻塞写锁:一个协程领有读锁时,其余协程写锁定须要阻塞;读锁不能阻塞读锁:一个协程领有读锁时,其余协程也能够领有读锁。读写锁底层实现 读写锁外部仍有一个互斥锁,用于将多个写操作隔离开来,其余几个都用于隔离读操作和写操作。 源码包src/sync/rmmutex.go:RWMutex中定义了读写锁的数据结构 type RWMutex struct { w Mutex // held if there are pending writers writerSem uint32 // semaphore for writers to wait for completing readers readerSem uint32 // semaphore for readers to wait for completing writers readerCount int32 // number of pending readers readerWait int32 // number of departing readers}本文由:GOLANG ROADMAP社区 公布

July 1, 2022 · 1 min · jiezi

关于go:go-nil-是什么

一、介绍nil是在go中的一个变量。在源码 src/builtin/builtin.go // nil is a predeclared identifier representing the zero value for a// pointer, channel, func, interface, map, or slice type.var nil Type // Type must be a pointer, channel, func, interface, map, or slice type// Type is here for the purposes of documentation only. It is a stand-in// for any Go type, but represents the same type for any given function// invocation.type Type intnil是一个零值, bool -> false numbers -> 0 string -> "" pointers -> nilslices -> nilmaps -> nilchannels -> nilfunctions -> nilinterfaces -> nil

July 1, 2022 · 1 min · jiezi

关于go:详解连接池参数设置边调边看

你有同感吗?当大家在开发服务端代码的时候,会不会常常有如下疑难? 纳闷 MySQL 连接池到底有多少连贯?每个连贯的生命周期继续多久?连贯异样断开的时候到底是服务端被动断的,还是客户端被动断的?当长时间没有申请的时候,底层库是否有 KeepAlive 申请?简单网络状况的解决素来都是后端开发的重点和难点之一,你是不是也为各种网络状况的调试而头顶发凉呢? 所以我写了 tproxy当我在做后端开发和写 go-zero 的时候,常常会须要监控网络连接,剖析申请内容。比方: 剖析 gRPC 连贯何时连贯、何时重连,并据此调整各种参数,比方:MaxConnectionIdle剖析 MySQL 连接池,以后多少连贯,连贯的生命周期是什么策略也能够用来察看和剖析任何 TCP 连贯,看服务端被动断,还是客户端被动断等等tproxy 的装置$ GOPROXY=https://goproxy.cn/,direct go install github.com/kevwan/tproxy@latest或者应用 docker 镜像: $ docker run --rm -it -p <listen-port>:<listen-port> -p <remote-port>:<remote-port> kevinwan/tproxy:v1 tproxy -l 0.0.0.0 -p <listen-port> -r host.docker.internal:<remote-port>arm64 零碎: $ docker run --rm -it -p <listen-port>:<listen-port> -p <remote-port>:<remote-port> kevinwan/tproxy:v1-arm64 tproxy -l 0.0.0.0 -p <listen-port> -r host.docker.internal:<remote-port>tproxy 的用法$ tproxy --helpUsage of tproxy: -d duration the delay to relay packets -l string Local address to listen on (default "localhost") -p int Local port to listen on -q Quiet mode, only prints connection open/close and stats, default false -r string Remote address (host:port) to connect -t string The type of protocol, currently support grpc剖析 gRPC 连贯tproxy -p 8088 -r localhost:8081 -t grpc -d 100ms侦听在 localhost 和 8088 端口重定向申请到 localhost:8081辨认数据包格局为 gRPC数据包提早100毫秒 ...

July 1, 2022 · 2 min · jiezi

关于go:Go题库15go-struct-能不能比较

题目解析 GOLANG ROADMAP社区答案(engine)须要具体情况具体分析,如果struct中含有不能被比拟的字段类型,就不能被比拟,如果struct中所有的字段类型都反对比拟,那么就能够被比拟。 不可被比拟的类型:① slice,因为slice是援用类型,除非是和nil比拟② map,和slice同理,如果要比拟两个map只能通过循环遍历实现③ 函数类型 其余的类型都能够比拟。 还有两点值得注意: 构造体之间只能比拟它们是否相等,而不能比拟它们的大小。只有所有属性都相等而属性程序都统一的构造体能力进行比拟。 本文由:GOLANG ROADMAP社区 公布

June 30, 2022 · 1 min · jiezi

关于go:go-make-slice-详解

一、申明slice会产生什么1.1 申明slice当咱们申明一个slice类型,它理论的值什么? func TestSlice(t *testing.T) { var sl []int if sl == nil { t.Log("nil") } t.Log(sl == nil) t.Log(len(sl)) t.Log(cap(sl)) t.Log(sl)}如上咱们申明了一个 []int的 slice切片类型输入如下: niltrue00[]咱们看到sl = nil,阐明没有调配这个变量内存。 1.2 申明并且赋值一个slice会产生什么func TestSlice2(t *testing.T) { var sl []int = []int{1, 2, 3} //或 sl := []int{1, 2, 3} if sl == nil { t.Log("nil") } t.Log(sl == nil) t.Log(len(sl)) t.Log(cap(sl)) t.Log(sl)}此时咱们赋值了一个slice,输入如下: false33[1 2 3]二、make slice 会产生什么咱们对于第一个代码,在var的时候 减少一个make func TestSlice3(t *testing.T) { var sl []int = make([]int, 0) if sl == nil { t.Log("nil") } t.Log(sl == nil) t.Log(len(sl)) t.Log(cap(sl)) t.Log(sl)}输入如下: ...

June 30, 2022 · 1 min · jiezi

关于go:go-const-常量几种好习惯用法

一、把同一类的用()括起来 type scope uint8const ( scopeInterfaceLocal scope = 0x1 scopeLinkLocal scope = 0x2 scopeAdminLocal scope = 0x4 scopeSiteLocal scope = 0x5 scopeOrgLocal scope = 0x8 scopeGlobal scope = 0xe)这是go语言源码包对于scope的几种常量设置。

June 30, 2022 · 1 min · jiezi

关于go:go-协程常见问题总结

一、构造体1.1 先看下如下的问题,咱们想开一个go协程打印 func TestStruct(t *testing.T) { type T struct { I []int } var tt = T{I: []int{1, 2, 3, 4, 5}} go func() { t.Log(tt.I) }() tt.I = nil time.Sleep(time.Second)}func TestStruct1(t *testing.T) { type T struct { I []int } var tt = T{I: []int{1, 2, 3, 4, 5}} go func() { i := tt.I t.Log(i) }() tt.I = []int{} time.Sleep(time.Second)}两个都是输入[]: [][]和咱们料想的不一样,原以为是输入 [1,2,3,4,5],后果是[],因为tt.I = nil 先执行了。 在go func()之前,能确定是程序执行,但在go func()之后,是没法确定,哪个协程先执行的。执行程序是未知的。正确应用办法: ...

June 30, 2022 · 1 min · jiezi

关于go:golang-metrics各个指标含义

go能够通过 github.com/prometheus/client_golang 所提供的api给prometheus裸露一指标信息,prometheus通过采集这些指标达到对应用服务的监控记录、告警等操作。 Gin框架引入: func main () { r := mux.NewRouter() // prometheus metrics r.Handle("/metrics", promhttp.Handler()) http.ListenAndServe(":8082", r)}指标对应含意: metrics含意go_gc_duration_seconds持续时间秒go_gc_duration_seconds_sumgc-持续时间-秒数-总和go_memstats_alloc_bytesGo内存统计调配字节go_memstats_alloc_bytes_totalGo内存统计调配字节总数go_memstats_buck_hash_sys_bytes用于分析桶散列表的堆空间字节go_memstats_frees_total内存开释统计go_memstats_gc_cpu_fraction垃圾回收占用服务CPU工作的工夫总和go_memstats_gc_sys_bytes圾回收标记元信息应用的内存字节go_memstats_heap_alloc_bytes服务调配的堆内存字节数go_memstats_heap_idle_bytes申请然而未调配的堆内存或者回收了的堆内存(闲暇)字节数go_memstats_heap_inuse_bytes正在应用的堆内存字节数go_memstats_heap_objects堆内存块申请的量go_memstats_heap_released_bytes返回给OS的堆内存go_memstats_heap_sys_bytes零碎调配的作为运行栈的内存go_memstats_last_gc_time_seconds持续时间秒go_gc_duration_seconds垃圾回收器最初一次执行工夫go_memstats_lookups_total被runtime监督的指针数go_memstats_mallocs_total服务malloc的次数go_memstats_mcache_inuse_bytesmcache构造体申请的字节数(不会被视为垃圾回收)go_memstats_mcache_inuse_bytesmcache构造体申请的字节数(不会被视为垃圾回收)go_memstats_mcache_sys_bytes操作系统申请的堆空间用于mcache的字节数go_memstats_mspan_inuse_bytes用于测试用的构造体应用的字节数go_memstats_next_gc_bytes垃圾回收器检视的内存大小go_memstats_other_sys_bytesgolang零碎架构占用的额定空间go_memstats_stack_inuse_bytes正在应用的栈字节数go_memstats_stack_sys_bytes零碎调配的作为运行栈的内存go_memstats_sys_bytes服务当初零碎应用的内go_threads线程go_goroutines协程数量go_infogo编译器版本process_cpu_seconds_total过程用户和零碎 CPU 总工夫(以秒为单位)。process_max_fds过程关上文件描述符的最大数量。process_open_fds过程关上文件描述符的数量。process_resident_memory_bytes过程驻留内存大小(以字节为单位)。process_start_time_seconds过程的开始工夫,以秒为单位(工夫戳)。process_virtual_memory_bytes过程以字节为单位的虚拟内存大小。process_virtual_memory_max_bytes可用的最大虚拟内存量(以字节为单位)。promhttp_metric_handler_requests_in_flight以后提供的抓取次数。promhttp_metric_handler_requests_total按 HTTP 状态代码的抓取总数。

June 29, 2022 · 1 min · jiezi

关于go:go-rand-创建-伪随机数

一、介绍让咱们用 Go 语言创立一个伪随机数。这是一个简略的代码:从 0 到 9 的数字输入 10 次。 func TestRandIntn(t *testing.T) { rand.Seed(time.Now().UnixNano()) for i := 0; i < 10; i++ { fmt.Println("number is ", rand.Intn(10)) }}输入: number is 7number is 1number is 6number is 1number is 8number is 7number is 3number is 4number is 9number is 9要害是在名为rand.Seed(time.Now().UnixNano())的中央给出作为随机数起源的种子。给个种子的起因是这里应用的函数rand.Intn在rand包中生成新的随机数生成器的时候如同初始化rand.NewSource(1)一样,所以给个种子,如同是须要收回不同的随机数。 上面是一个没有收获的具备雷同后果的示例。 func TestRandIntnNoSeed(t *testing.T) { // 无种子rand.Intn 随机 for i := 0; i < 10; i++ { fmt.Print(rand.Intn(10), " ") } fmt.Println(" ") // rand.NewSource初始化rand并执行 myRand := rand.New(rand.NewSource(1)) for i := 0; i < 10; i++ { fmt.Print(myRand.Intn(10), " ") } fmt.Println(" ") // 增加种子在rand myRand.Seed(time.Now().UnixNano()) for i := 0; i < 10; i++ { fmt.Print(myRand.Intn(10), " ") }}第一次运行 ...

June 29, 2022 · 1 min · jiezi

关于go:Go题库14WaitGroup的坑

题目解析 GOLANG ROADMAP社区答案(engine)① Add一个正数 如果计数器的值小于0会间接panic ② Add在Wait之后调用 比方一些子协程结尾调用Add完结调用Wait,这些 Wait无奈阻塞子协程。正确做法是在开启子协程之前先Add特定的值。 ③ 未置为0就重用 WaitGroup能够实现一次编排工作,计数值降为0后能够持续被其余工作所用,然而不要在还没应用完的时候就用于其余工作,这样因为带着计数值,很可能出问题。 ④ 复制waitgroup WaitGroup有nocopy字段,不能被复制。也意味着WaitGroup不能作为函数的参数。 本文由:GOLANG ROADMAP社区 公布

June 29, 2022 · 1 min · jiezi

关于go:Go题库13向为nil的channel发送数据会怎么样

题目解析 GOLANG ROADMAP社区答案(栾龙生)空通道即无缓冲通道。无缓冲通道上的发送操作将会阻塞,直到另一个goroutine在对应的通道上执行接管操作,这时值传送实现,两个goroutine都能够继续执行。相同,如果接管操作先执行,接管方gorountine将阻塞,直到另一个goroutine在同一个通道上发送一个值。 应用无缓冲通道进行的通信导致发送和接管goroutine同步化。因而,无缓冲通道也称为同步通道。当一个值在无缓冲通道上传递时,接管值后发送方goroutine才被再次唤醒。 本文由:GOLANG ROADMAP社区 公布

June 28, 2022 · 1 min · jiezi

关于go:golang-hethttp包

net/httpweb 示例:package mainimport ( "fmt" "net/http")func main() { http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello World") }) http.ListenAndServe("localhost:8080", nil)}进行逻辑解决func(w http.ResponseWriter, r *http.Request){ dosomeing}调用HandleFunc()注册处理器函数调用ListenAndServer()监听localhost:8080并提供服务HandleFunc()与Handle()实现原理调用档次 http.HandleFunc()与http.Handle()的底层实现都依赖于ServerMux.Handle()。http.HandleFunc()本人对ServeMux.Handle()进行了封装,封装为ServeMux.HandleFunc() 并定义了本人的数据结构HandlerFunc该构造是函数类型并且实现了Handler 接口。Mux.Handle() 承受一个类型为Handler的参数,所以调用mux.Handle()必须实现Handler接口。http.HandleFunc()通过本人的数据结构HandlerFunc实现了Handler接口所以调用http.HandleFunc()时不必本人去实现Handler接口,间接向http.HandleFUnc()传入pattern string和func(w http.ResponseWriter, r *http.Request){}即可;http.Handle()间接调用Mux.Handle() 所以须要调用者本人去定义一个数据结构并且该数据结构要实现Handler接口,在应用该数据结构前对其进行初始化,而后将pattern string 与该数据结构传入http.Handle()。 实现// Handle registers the handler for the given pattern// in the DefaultServeMux.// The documentation for ServeMux explains how patterns are matched.func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }// HandleFunc registers the handler function for the given pattern// in the DefaultServeMux.// The documentation for ServeMux explains how patterns are matched.func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler)}当咱们调用 http.HandleFunc() 注册处理器时,规范库会应用DefaultServeMux默认的 HTTP 服务器 解决申请。 ...

June 28, 2022 · 7 min · jiezi

关于go:几百行代码实现一个-JSON-解析器

前言之前在写 gscript时我就在想有没有利用编译原理实现一个更理论工具?毕竟真写一个语言的难度不低,并且也很难真的利用起来。 一次无意间看到有人提起 JSON 解析器,这类工具充斥着咱们的日常开发,使用十分宽泛。 以前我也有思考过它是如何实现的,过程中一旦和编译原理扯上关系就情不自禁的劝退了;但通过这段时间的实际我发现实现一个 JSON 解析器仿佛也不艰难,只是使用到了编译原理前端的局部常识就齐全足够了。 得益于 JSON 的轻量级,同时语法也很简略,所以外围代码大略只用了 800 行便实现了一个语法欠缺的 JSON 解析器。 <!--more--> 首先还是来看看成果: import "github.com/crossoverJie/gjson"func TestJson(t *testing.T) { str := `{ "glossary": { "title": "example glossary", "age":1, "long":99.99, "GlossDiv": { "title": "S", "GlossList": { "GlossEntry": { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": { "para": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML", true, null] }, "GlossSee": "markup" } } } }}` decode, err := gjson.Decode(str) assert.Nil(t, err) fmt.Println(decode) v := decode.(map[string]interface{}) glossary := v["glossary"].(map[string]interface{}) assert.Equal(t, glossary["title"], "example glossary") assert.Equal(t, glossary["age"], 1) assert.Equal(t, glossary["long"], 99.99) glossDiv := glossary["GlossDiv"].(map[string]interface{}) assert.Equal(t, glossDiv["title"], "S") glossList := glossDiv["GlossList"].(map[string]interface{}) glossEntry := glossList["GlossEntry"].(map[string]interface{}) assert.Equal(t, glossEntry["ID"], "SGML") assert.Equal(t, glossEntry["SortAs"], "SGML") assert.Equal(t, glossEntry["GlossTerm"], "Standard Generalized Markup Language") assert.Equal(t, glossEntry["Acronym"], "SGML") assert.Equal(t, glossEntry["Abbrev"], "ISO 8879:1986") glossDef := glossEntry["GlossDef"].(map[string]interface{}) assert.Equal(t, glossDef["para"], "A meta-markup language, used to create markup languages such as DocBook.") glossSeeAlso := glossDef["GlossSeeAlso"].(*[]interface{}) assert.Equal(t, (*glossSeeAlso)[0], "GML") assert.Equal(t, (*glossSeeAlso)[1], "XML") assert.Equal(t, (*glossSeeAlso)[2], true) assert.Equal(t, (*glossSeeAlso)[3], "") assert.Equal(t, glossEntry["GlossSee"], "markup")}从这个用例中能够看到反对字符串、布尔值、浮点、整形、数组以及各种嵌套关系。 ...

June 28, 2022 · 3 min · jiezi

关于go:面试官哥们Go语言的互斥锁了解到什么程度

前言哈喽,大家好,我是asong。 当提到并发编程、多线程编程时,都会在第一工夫想到锁,锁是并发编程中的同步原语,他能够保障多线程在拜访同一片内存时不会呈现竞争来保障并发平安;在Go语言中更推崇由channel通过通信的形式实现共享内存,这个设计点与许多支流编程语言不统一,然而Go语言也在sync包中提供了互斥锁、读写锁,毕竟channel也不能满足所有场景,互斥锁、读写锁的应用与咱们是分不开的,所以接下来我会分两篇来分享互斥锁、读写锁是怎么实现的,本文咱们先来看看互斥锁的实现。 本文基于Golang版本:1.18 Go语言互斥锁设计实现mutex介绍sync 包下的mutex就是互斥锁,其提供了三个公开办法:调用Lock()取得锁,调用Unlock()开释锁,在Go1.18新提供了TryLock()办法能够非阻塞式的取锁操作: Lock():调用Lock办法进行加锁操作,应用时应留神在同一个goroutine中必须在锁开释时能力再次上锁,否则会导致程序panic。Unlock():调用UnLock办法进行解锁操作,应用时应留神未加锁的时候开释锁会引起程序panic,曾经锁定的 Mutex 并不与特定的 goroutine 相关联,这样能够利用一个 goroutine 对其加锁,再利用其余 goroutine 对其解锁。tryLock():调用TryLock办法尝试获取锁,当锁被其余 goroutine 占有,或者以后锁正处于饥饿模式,它将立刻返回 false,当锁可用时尝试获取锁,获取失败不会自旋/阻塞,也会立刻返回false;mutex的构造比较简单只有两个字段: type Mutex struct { state int32 sema uint32}state:示意以后互斥锁的状态,复合型字段;sema:信号量变量,用来管制期待goroutine的阻塞休眠和唤醒初看构造你可能有点懵逼,互斥锁应该是一个简单货色,怎么就两个字段就能够实现?那是因为设计应用了位的形式来做标记,state的不同位别离示意了不同的状态,应用最小的内存来示意更多的意义,其中低三位由低到高别离示意mutexed、mutexWoken 和 mutexStarving,剩下的位则用来示意以后共有多少个goroutine在期待锁: const ( mutexLocked = 1 << iota // 示意互斥锁的锁定状态 mutexWoken // 示意从失常模式被从唤醒 mutexStarving // 以后的互斥锁进入饥饿状态 mutexWaiterShift = iota // 以后互斥锁上期待者的数量) mutex最开始的实现只有失常模式,在失常模式下期待的线程依照先进先出的形式获取锁,然而新创建的gouroutine会与刚被唤起的 goroutine竞争,会导致刚被唤起的 goroutine获取不到锁,这种状况的呈现会导致线程长时间被阻塞上来,所以Go语言在1.9中进行了优化,引入了饥饿模式,当goroutine超过1ms没有获取到锁,就会将以后互斥锁切换到饥饿模式,在饥饿模式中,互斥锁会间接交给期待队列最后面的goroutine,新的 goroutine 在该状态下不能获取锁、也不会进入自旋状态,它们只会在队列的开端期待。如果一个 goroutine 取得了互斥锁并且它在队列的开端或者它期待的工夫少于 1ms,那么以后的互斥锁就会切换回失常模式。 mutex的根本状况大家都曾经把握了,接下来咱们从加锁到解锁来剖析mutex是如何实现的; Lock加锁从Lock办法动手: func (m *Mutex) Lock() { // 判断以后锁的状态,如果锁是齐全闲暇的,即m.state为0,则对其加锁,将m.state的值赋为1 if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { if race.Enabled { race.Acquire(unsafe.Pointer(m)) } return } // Slow path (outlined so that the fast path can be inlined) m.lockSlow()}下面的代码次要两局部逻辑: ...

June 27, 2022 · 4 min · jiezi

关于go:如何用Shifu来接入一个私有驱动的物联网设备

在上一篇《如何写一个树莓派的驱动来管制GPIO LED》中咱们分享了驱动的编写办法。尽管实现了管制,然而驱动只能在本地执行,不具备任何的可扩展性。尤其是当设施数量多了当前管控难度将急剧减少。 这始终是一个物联网开发者的难题。那么有什么方法能够大批量,模块化的接入相似驱动的设施呢? 欢送来到边无际的这一系列的第二篇分享——如何用Shifu Framework来接入一个公有驱动物联网设施。 简介 本文是一个应用Shifu接入树莓派Python驱动的指南,其中蕴含Shifu Framework, Docker, Linux, Kubernetes的基本操作,任何开发者都能够浏览本文来学习Shifu Framework的开发方法。文中的Shifu Framework架构如下:北向通过”deviceshifu-http-http”向上凋谢HTTP API接口,南向通过”rpio-gpio-driver”来和理论设施交互。 指标 在树莓派上装置k3s集群并装置Shifu Framework打包树莓派LED驱动到一个容器镜像在Shifu中部署树莓派LED的数字孪生实现对树莓派LED的近程自动化管控本次分享中用到的设施有: 树莓派 (本文中用到的为Raspberry Pi 3B+),运行着64位的Raspberry Pi OS上篇文章连贯的电路须要的基本知识: · 根本的Python· Linux命令行基本操作(创立一个文件,装置利用,SSH,运行一个程序)· Docker/containerd基本操作· K8s/K3s基本操作 步骤 第一步:装置K3s 首先咱们要在树莓派中运行一个Kubernetes集群,这里并不限度用户应用的版本,然而为了节俭资源本文中应用的是k3s 本文参考的是装置教程https://rancher.com/docs/k3s/... 具体步骤本文将不波及。在今后的分享中将会带着大家进行具体的装置解说,纵情期待。 装置结束后,执行“kubectl version”查看以后kubernetes版本: 利用“kubectl get nodes”查看以后集群的状态,显示”Ready”即示意集群能够应用: 至此,k3s装置完结。 第二步:装置Shifu 首先将Shifu我的项目克隆到本地,我的项目地址为: https://github.com/Edgenesis/... 执行命令为:git clone https://github.com/Edgenesis/... 上面通过“kubectl apply -f shifu/k8s/crd/install/shifu_install.yml”即可一键将Shifu部署到k3s集群中: 再次执行“kubectl get pods -A”,即可看到Shifu Framework的控制器被部署到集群中: 咱们也能够通过“edgedevices”这个CRD来治理设施资源(以后没有设施): 至此,Shifu装置结束。 第三步:打包驱动 咱们须要利用Shifu提供的一个小工具来实现能够近程操纵本地驱动,具体的教程请看: https://github.com/Edgenesis/... 这个小工具实现了将用户/程序发送来的HTTP申请转换到本地命令行来执行。 教程外面提供了一个驱动示例,门路为 https://github.com/Edgenesis/... 内容如下: 能够看到实例Dockerfile分两局部,首先是用“golang”这个镜像来编译Shifu提供的“http_to_ssh_stub.go”来实现HTTP到SSH命令行的转换。接着是利用一个空的“alpine”镜像,配置SSH来供演示。 接下来让咱们来正式操作。 思考到树莓派的性能局限,本次编译将从电脑端执行,将编译好的镜像推送到Docker Hub来供近程调用即可。 首先,咱们建设一个新的文件夹,这里用的是“dev”,而后将上一篇文章中应用到的树莓派LED驱动保留到该目录: ...

June 27, 2022 · 3 min · jiezi

关于go:Go题库12slice和array区别

题目解析 GOLANG ROADMAP社区答案(engine)array是固定长度的数组,是值类型的,如果进行赋值或者作为函数参数,实际上整个数据都会被从新拷贝一份。 应用前必须申明长度 arr := [5]int而slice属于援用类型,是一个不定长的,总是指向底层的数组array的数据结构。作为函数参数时,slice传递的是指针。 创立时不须要指定长度 var s []ints := make([]int,10)s :=[]int{1,2,3}本文由:GOLANG ROADMAP社区 公布

June 27, 2022 · 1 min · jiezi

关于go:makefile与go项目入门

一、Makefile介绍1.1简略的例子创立一个新目录,并在该目录中创立一个名为Makefile的文件。如下: .└── Makefile关上Makefile,增加一个target命名hello到这个Makefile外面。 hello: echo "Hello"咱们尝试应用make命令行工具执行它: ➜ make helloecho "Hello"Hello二、一个简略的go我的项目利用咱们在目录下新建一个main.go文件,代码如下: package mainimport "fmt"func main() { fmt.Println("Hello")}

June 27, 2022 · 1 min · jiezi

关于go:goscaffold-一个基于-kratos-和-wire-依赖注入框架的脚手架

介绍架构图生命周期目录构造如何运行 go build 或 go runmakedocker-compose热重启运行子命令或脚本依赖注入配置 配置模型近程配置监听配置变更日志错误处理 转换为 HTTP 状态码将 GRPC 谬误转换为 Error组件 CasbinClient gRPC 客户端Discovery 服务发现与注册Entorm 如何配置多数据库Redis 客户端traceuidtransport 层 HTTP 响应swagger 文档生成如何拜访 swagger 文档service 层命令行功能模块cron 定时工作功能模块如何部署 Dockerfiledocker-composekubernetes地址:https://github.com/OldSmokeGu... 欢送 Star,欢送 PR,心愿大家能够一起探讨和斧正! 介绍go-scaffold 是一个基于 cobra 和 kratos 框架的脚手架,设计思维是基于 wire 实现模块和性能的组件化 go-scaffold 开箱即用,应用简略,能够疾速搭建起一个微服务进行业务代码的开发,反对性能: 依赖注入cobra 命令行cron 定时工作apollo 近程配置核心和配置监听日志切割服务注册和发现jaeger 链路追踪Swagger 文档生成docker-compose 和 Kubernetes 部署架构图 生命周期 目录构造|-- bin # 二进制文件目录|-- cmd # 编译入口| `-- app|-- deploy # 环境和部署相干目录| |-- docker-compose # docker-compose 容器编排目录| `-- kubernetes # k8s 编排配置目录|-- docs # 文档目录|-- etc # 配置文件目录|-- internal| `-- app| |-- command # 命令行功能模块| | |-- handler| | `-- script # 长期脚本| |-- component # 性能组件,如:db, redis 等| |-- config # 配置模型| |-- cron # 定时工作功能模块| | `-- job| |-- model # 数据库模型| |-- pkg # 性能类库| |-- repository # 数据处理层| |-- service # 业务逻辑层| |-- test| `-- transport| |-- grpc| | |-- api # proto 文件目录| | |-- handler # 管制层| | `-- middleware # 中间件| `-- http| |-- api # swagger 文档| |-- handler # 管制层| |-- middleware # 中间件| `-- router # 路由|-- logs # 日志目录|-- pkg # 性能类库`-- proto # 第三方 proto 文件目录如何运行首先将 etc/config.yaml.example 拷贝为 etc/config.yaml ...

June 27, 2022 · 6 min · jiezi

关于go:Go-119要来了看看都有哪些变化第2篇

Go 1.19要来了,看看都有哪些变动前言Go官网团队在2022.06.11公布了Go 1.19 Beta 1版本,Go 1.19的正式release版本预计会在往年8月份公布。 让咱们先睹为快,看看Go 1.19给咱们带来了哪些变动。 这是Go 1.19版本更新内容详解的第2篇,欢送大家关注公众号,及时获取本系列最新更新。 Go 1.19公布清单和Go 1.18相比,改变绝对较小,次要波及语言(Language)、内存模型(Memory Model)、可移植性(Ports)、Go Tool工具链、运行时(Runtime)、编译器(Compiler)、汇编器(Assembler)、链接器(Linker)和外围库(Core library)等方面的优化。 第1篇具体介绍了Go 1.19在语言、内存模型、可移植性方面的改良。 本文重点介绍Go 1.19版本在Go Tool工具链方面的变动。 文档正文文档正文(doc comments) 是Go语言里的对包(package), 常量(const), 函数(func), 类型(type)和变量(var)申明的一种正文标准。依照这个标准来正文,就能够应用go doc命令生成对应的代码阐明文档。 像大家熟知的https://pkg.go.dev/里的阐明文档就是通过编写合乎doc comments标准的文档正文来生成的。 Go 1.19 在文档正文里新增了对于链接、列表和更清晰的题目的反对,能够参考“Go Doc Comments” 理解语法细节。 作为这个批改的一部分,gofmt当初会把文档正文从新格式化,让文档款式展现更清晰。 同时,新增了一个package: go/doc/comment,能够用于解析和从新格式化文档正文,并且反对把文档正文渲染为HTML, Markdown和text格局。 新的编译束缚 unixGo语言反对应用编译束缚(build constraint)进行条件编译。Go 1.19版本新增了编译束缚 unix ,能够在//go:build前面应用unix。 //go:build unixunix示意编译的指标操作系统是Unix或者类Unix零碎。对于Go 1.19版本而言,如果GOOS是 aix, android, darwin, dragonfly, freebsd, hurd, illumos, ios, linux, netbsd, openbsd, 或 solaris中的某一个,那就满足unix这个编译束缚。 将来unix束缚还会匹配一些新的类Unix操作系统。 Go命令go build如果应用-trimpath标记,会在生成的可执行文件里打上trimpath标签,咱们能够应用 go version -m 或debug.ReadBuildInfo 查看可执行文件是否是应用-trimpath标记编译生成的。 ...

June 25, 2022 · 1 min · jiezi

关于go:Go题库11channel的应用场景

题目解析 GOLANG ROADMAP社区答案(engine)channel实用于数据在多个协程中流动的场景,有很多理论利用: ① 工作定时 比方超时解决: select { case <-time.After(time.Second):定时工作 select { case <- time.Tick(time.Second)② 解耦生产者和消费者 能够将生产者和消费者解耦进去,生产者只须要往channel发送数据,而消费者只管从channel中获取数据。 ③ 管制并发数 以爬虫为例,比方须要爬取1w条数据,须要并发爬取以提高效率,但并发量又不能过大,能够通过channel来管制并发规模,比方同时反对5个并发工作: ch := make(chan int, 5)for _, url := range urls { go func() { ch <- 1 worker(url) <- ch }}本文由:GOLANG ROADMAP社区 公布

June 24, 2022 · 1 min · jiezi

关于go:有度-Golang-版-SDK

地址github.com/go-packagist/youdu 装置go get github.com/go-packagist/youdu应用package mainimport ( "github.com/go-packagist/youdu" "github.com/go-packagist/youdu/message" "log")func main() { yd := youdu.New(&youdu.Config{ Api: "http://domain.com/api", Buin: 1111111, AppId: "22222222222222", AesKey: "3444444444444444444444444444444444", }) yd.Message().SendText("11111", "test") yd.Message().Send(&message.TextMessage{ ToUser: "11111", ToDept: "", MsgType: message.MsgTypeText, Text: &message.TextItem{ Content: "test", }, }) mediaId, err := yd.Media().Upload(youdu.MediaTypeImage, "test.jpeg") if err != nil { panic(err) } yd.Message().Send(&message.ImageMessage{ ToUser: "11111", ToDept: "", MsgType: message.MsgTypeImage, Image: &message.MediaItem{ MediaId: mediaId, }, })}参考资料有度 API 官网文档:youdu.im/doc/api/c01_00001.html

June 24, 2022 · 1 min · jiezi

关于go:gozero微服务实战系列六缓存一致性保证

只有咱们应用缓存,就必然会面对缓存和数据库间的一致性问题。如果缓存中的数据和数据库的数据不统一,那么业务利用从缓存中读取的数据就不是最新的数据,对业务的影响可想而知。比方咱们把商品的库存数据存在缓存中,如果缓存中库存数据不对,那么可能就会影响下单操作,这是业务上很难承受的。本篇文章咱们来一起聊一聊缓存的一致性问题。 如何解决缓存不统一先删缓存再更新数据库假如线程A删除缓存后,还没来得及更新数据库,这时候线程B开始读数据,线程B发现缓存缺失就只能去读数据库,等到线程B从数据库中读取完数据回塞缓存后,线程A才开始更新数据库,此时,缓存中的数据是旧值,而数据库中是最新值,两者曾经不统一了。 这种场景的解决方案是在线程A更新完数据库的值后,能够让它sleep一小段时间,再进行一次缓存删除操作,之所以要加上sleep的一段时间,就是为了让线程B可能先从数据库读取出数据而后再把缓存miss的数据回塞到缓存,而后线程A再进行删除。所以线程A的sleep工夫就须要大于线程B读取数据再写入缓存的工夫。这个工夫是多少呢?这个是须要咱们在业务中退出打点监控来统计的,依据这个统计值来估算该工夫。这样一来,其余线程读取数据时,会发现缓存缺失,就会从数据库中读取最新的值。咱们把这种模型叫做 "延时双删"。 先更新数据库再删除缓存如果线程A更新了数据库中的值,但还没来得及删除缓存中的值,线程B这时候开始读取数据,此时,线程B查问缓存时,命中了缓存,就会间接应用缓存中的值,该值为旧值。不过在这种场景下,如果并发申请量不高的话,其实基本上不会有线程读到旧值,而且线程A更新完数据库后,删除缓存是十分快的操作,所以,这种状况总体对业务影响较小。个别在生产环境中,也举荐大家采纳该模式。 重试机制能够把要删除的缓存值或者要更新的数据库的值放到音讯队列中,当利用没可能胜利地删除缓存或者是更新数据库的值的时候,能够从音讯队列中生产这些值,这里生产音讯队列的服务叫job,而后再次进行删除或者更新,起到一个兜底弥补的作用,以此来保障最终的一致性。 如果可能胜利地删除或更新,就须要把这些值从音讯队列中去除,免得反复操作,此时,咱们也能够保障数据库和缓存数据的统一了,否则的话,咱们还须要再次进行重试,如果重试超过肯定次数还是失败,这时候个别都须要记录谬误日志或者发送告警告诉。 并发读写首先第一步线程A读取缓存,这时候缓存没有命中,因为应用的是cache aside这种模式,所以接下来第二步线程A会去读数据库,这个时候线程B更新数据库,更新完数据库后通过set cache更新了缓存,最初第五步线程A把从数据库读到的值通过set cache也更新了缓存,然而这时候线程A中的数据曾经是脏数据了,因为第四步和第五步都是设置缓存,导致写入的值互相笼罩,并且操作的程序具备不确定性,从而导致了缓存不统一状况的产生。 怎么解决这个问题呢?其实十分地简略,咱们只须要把第五步的set cache操作替换成add cache即可,add cache即setnx操作,只有缓存不存在的时候才会胜利写入,相当于加了优先级,即更新数据库后的更新缓存优先级更高,而读数据库后回塞缓存的优先级较低,从而保障写操作的最新数据不会被读操作的回塞数据笼罩。 结束语本篇文章阐明了在应用缓存时最常遇见的一个问题,也就是缓存和数据库不统一的问题,针对这个问题咱们列举了一些可能导致不统一的场景以及对应场景的解决方案,特地地,对于job异步弥补的场景咱们能够应用set操作来强行笼罩缓存,保障缓存的更新为最新的数据,而对于读数据库回塞缓存的操作咱们个别应用add来更新缓存。 心愿本篇文章对你有所帮忙,谢谢。 每周一、周四更新 代码仓库: https://github.com/zhoushuguang/lebron 我的项目地址https://github.com/zeromicro/go-zero 欢送应用 go-zero 并 star 反对咱们! 微信交换群关注『微服务实际』公众号并点击 交换群 获取社区群二维码。

June 24, 2022 · 1 min · jiezi

关于go:go算法实现快排-quick-sort

一、介绍快排排序是由东尼·霍尔所倒退的一种排序算法。在均匀状况下,它的工夫复杂度是(nlogn),在最坏的状况下是(n2),不过这种状况不常见。事实上,疾速排序通常显著比其余 (nlogn) 算法更快,因为它的外部循环(inner loop)能够在大部分的架构上很有效率地被实现进去,快排是最快的排序算法之一。疾速排序应用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

June 23, 2022 · 1 min · jiezi

关于go:Go-语言使用-MySQL-的常见故障分析和应对方法

导读:很多同学在应用Go和数据库打交道的过程中,常常会遇到一些异样不晓得为什么,本文从SQL连接池的原理进行剖析,模仿了一些例子对异样的景象进行解读剖析,并给出一些常见的应答伎俩,冀望能帮忙到大家。 全文12795字,预计浏览工夫32分钟 有很多同学遇到了 MySQL 查问迟缓的问题,其可能体现为 SQL 语句很简略,然而查问耗时很长。可能是因为这样一些起因所致。 1、资源未及时开释Go 的 sql 包应用的是长连贯形式让 Client 和 SQL Server 交互,为了防止 SQL Server 链接过多,个别会在 Client 端限定最大连接数。 上面是sql 的连接池的状态图(设置了最大关上连接数的状况): SQL Client 和 Server 交互后,有些后果返回的是一个流(Stream),此时的网络连接(Conn)是被 Stream 对象持续应用的,Client 须要迭代读取后果,读取实现后应立即敞开流以回收资源(开释 conn)。 比方最长用的DB.QueryContext 办法即是如此: // QueryContext 查问一些后果// query:select * from test limit 10func (db *DB) QueryContext(ctx context.Context, query string, args ...any) (*Rows, error)type Rows struct{ Close( ) error ColumnTypes( ) ( [ ]*ColumnType, error) Columns( ) ( [ ]string, error) Err( ) error Next( ) bool NextResultSet( ) bool Scan(dest ...any) error}当还有后果的时候(即Rows.Next()==true 时),阐明还有后果未读取进去,此时必须调用 Rows.Close() 办法来对流进行敞开以开释连贯(让以后连贯变为闲暇状态以 让其余逻辑能够应用该连贯)。 ...

June 23, 2022 · 5 min · jiezi

关于go:golang泛型实现skiplist

一、写在后面skiplist是一种有序的数据结构, 不同于各种均衡树, skiplist看起来就是多层的链表, 具体点每个元素是个数组, 这个元素的数组除了0层是和下个元素直连, 1层和n层之间可能和下个, 或者下下个节点连接起来。 这些skiplist节点的多层构造,形成施行二分搜寻的根底, 实践从而达到可观的效率, 开源界赫赫有名的redis的zset一部分应用skiplist。 对于这个被吹爆了的数据,上面会应用redis的套路在go外面实现下, 是骡子是马拉进去溜溜压测下性能如何。 二、 一个根本的skiplist的样子2.1 图示 2.2 代码展现表头和节点 type Node[T any] struct { score float64 elem T // 后退指针 backward *Node[T] NodeLevel []struct { // 指向后退节点, 是指向tail的方向 forward *Node[T] span int }}type SkipList[T any] struct { head *Node[T] tail *Node[T] r *rand.Rand length int level int}三、查找3.1 图示还是以下面的skiplist为例, 上面会画出查找每个元素的搜寻门路。 skiplist外面head节点有个两个重要的特点: head节点的层数等于最大节点和层数, 个别实现中会有MaxLevel记录最大结点层数head节点是与数据节点之间有链接关系的上面的查找门路, 实线示意查找的门路,虚线示意节点间的关系查找的过程就是先从head节点的MaxLevel-1索引查找, 查找的大抵走位是向右向下向右向下...就迫近指标地位 查找5 查找8 查找10 3.2 代码展现// 获取func (s *SkipList[T]) GetWithErr(score float64) (elem T, err error) { x := s.head for i := s.level - 1; i >= 0; i-- { for x.NodeLevel[i].forward != nil && (x.NodeLevel[i].forward.score < score) { x = x.NodeLevel[i].forward } } x = x.NodeLevel[0].forward if x != nil && score == x.score { return x.elem, nil } err = ErrNotFound return}四、新增新增先应用update[]数组保护每层须要插入节点的地位, 通过抛硬币的函数决定这个新节点的level, 最初批改节点的前后关系, 就是插入链表节点的多层版本, 保护多层信息就是update[]干的事件。 ...

June 22, 2022 · 3 min · jiezi

关于go:go源码阅读sort包3种类型排序

一、介绍go doc sort.sort 包,查看包次要性能函数输入: type Interface interface { Len() int Less(i, j int) bool Swap(i, j int)}func Sort(data Interface)只有一个Sort函数,参数为实现了len(),less(),swap(),三个办法的Interface 接口。 二、排序整数、浮点数和字符串切片对于 []int, []float, []string 这种元素类型是根底类型的切片应用 sort 包提供的上面几个函数进行排序。 sort.Intssort.Float64ssort.Strings应用示例如下: func TestSort(t *testing.T) { //sort.Ints 对 []int 类型进行排序 i := []int{4, 2, 3, 1} sort.Ints(i) fmt.Println(i) // [1 2 3 4] //sort.Float64s 对 []float64 类型进行排序 f := []float64{4.2, 2.3, 3.1, 1.2} sort.Float64s(f) fmt.Println(f) // [1.2 2.3 3.1 4.2] //sort.Strings 对 []string 类型进行排序 s := []string{"b", "d", "c", "a"} sort.Strings(s) fmt.Println(s) //[a b c d]}二、对于构造体,slice的排序能够参考sort.slice ...

June 22, 2022 · 1 min · jiezi

关于go:gocv滑动验证码

以下基于macOS 第一步,装置opencvInstallationYou can install OpenCV 4.6.0 using Homebrew. If you already have an earlier version of OpenCV (3.4.x) installed, you should probably remove it before installing the new version: brew uninstall opencvYou can then install OpenCV 4.6.0: brew install opencvpkgconfig Installationpkg-config is used to determine the correct flags for compiling and linking OpenCV. You can install it by using Homebrew: brew install pkgconfigVerifying the installationTo verify your installation you can run one of the included examples. ...

June 22, 2022 · 2 min · jiezi

关于go:第八届-GopherChina-大会蓄势待发

GopherChina2022 大会又准时和大家见面了,2015年由Go中国社区发动的第一届 GopherChina大会 在上海胜利举办,历时七年已成为国内最权威和最干货的Go大会,咱们致力于为中国宽广的Gopher提供最好的交流平台。举办 GopherChina大会的目标是会集宽广 Go 语言的开发者以及大规模利用 Go 的示范企业给大家带来精彩分享,出现一场最 cool 的盛会。 大会介绍工夫: 2022-09-17 09:00 ~ 09-18 18:00 地址: 北京市海淀区丰智东路13号(朗丽兹西山花园酒店) 大会盛况 尽管大会选址仍旧是咱们的老中央,但每一期都有每一期的干货、惊喜彩蛋与来自世界各地的大佬汇集。上一期咱们邀请到了IT行业多年从业者曹大分享了《Go1.14的信号式抢占》、王发康老师带来的《MOSN在云原生的摸索和实际》以及刘浩扬老师分享的《基于Golang构建高可扩大的云原生PaaS平台》,给许多 gopher 留下了粗浅的印象和意想不到的启发,现场的Gopher甚至能够当面向讲师求教暗藏款问题,播种多多! 此外,现场的交换与独特学习更能让咱们结识到气味相投的敌人,拓宽本人的人脉圈子,能够说太值了! 这还不够,往届咱们的Go周边也是很丰盛的,Gopher们在大会完结后的战果堪称是颇丰: 往年让咱们一起期待下各大赞助商为大家精心筹备的伴手礼! 啥也别说了,也别拦我,我要报名,哪里报名? 我要报名迄今为止,GopherChina 大会曾经胜利举办了七届,并且每年都会有 gopher meetup 线下巡回见面会,大会参会者累计已冲破万人。前七届大会的胜利举办也取得了良好的口碑,在技术圈内满意度和举荐度高达95%以上。 与今年一样,本次大会前一天将进行为期一天的培训,培训内容波及 Go 语法精髓干货,Go 我的项目 / Go 包 / API 设计准则,Go 我的项目实际,我的项目中各个过程环节要思考的问题,解决的门路与参考等。线下现场只有30个名额,先报先得哦~ 依据传统咱们仍然提前凋谢有虫吃的优惠早鸟票,第一阶段截止日期为6月30日,大家放松报名呀~ 扫码报名: 议题征集同时,咱们凋谢大会议题征集通道,欢送各位有实战经验、独特观点的小伙伴勇跃分享~ 议题范畴 Go深度专题: Go语言层面的深度分享Go moduleGo泛型设计Go编译器Go 信号抢占等topicGo实战专题: Go的第三方框架/库的设计分享Go在后端架构的实际Go在PaaS平台的实战利用等Go转型专题: 从其余语言架构转到Go架构的实战分享例如滴滴PHP架构转型、Java转型等类型的分享实战Go云原生专题: 云原生基础架构的实现云原生架构部件的研发Kubernetes 二次开发议题提交扫这里哦~ 企业单干及票务团购如贵公司也正在用 Go,心愿在大会上进行宣传推广、招聘培训、票务团购、建设技术品牌等需要,欢送分割咱们,让更多的 Gopher 理解你们! 商务单干及票务团购,请分割微信:18516100522。<center> 商务单干及票务团购,请分割微信:18516100522。</center> 志愿者招募本次大会须要大量的志愿者,包含但不限于:线上文案、设计、网络宣传、现场指引、签到、材料装袋、周边发放、展台服务等。现场志愿者仅承受北京地区的小伙伴。 (偷偷通知你:志愿者会有相干福利哦~) 志愿者报名: ...

June 22, 2022 · 1 min · jiezi

关于go:Go题库10channel和锁的对比

题目解析 GOLANG ROADMAP社区答案(engine)并发问题能够用channel解决也能够用Mutex解决,然而它们的善于解决的问题有一些不同。 channel关注的是并发问题的数据流动,实用于数据在多个协程中流动的场景。 而mutex关注的是是数据不动,某段时间只给一个协程拜访数据的权限,实用于数据地位固定的场景。 本文由:GOLANG ROADMAP社区 公布

June 22, 2022 · 1 min · jiezi

关于go:gozero微服务实战系列五缓存代码怎么写

缓存是高并发服务的根底,毫不夸大的说没有缓存高并发服务就无从谈起。本我的项目缓存应用Redis,Redis是目前支流的缓存数据库,反对丰盛的数据类型,其中汇合类型的底层次要依赖:整数数组、双向链表、哈希表、压缩列表和跳表五种数据结构。因为底层依赖的数据结构的高效性以及基于多路复用的高性能I/O模型,所以Redis也提供了十分强悍的性能。下图展现了Redis数据类型对应的底层数据结构。 根本应用在go-zero中默认集成了缓存model数据的性能,咱们在应用goctl主动生成model代码的时候加上 -c 参数即可生成集成缓存的model代码 goctl model mysql datasource -url="root:123456@tcp(127.0.0.1:3306)/product" -table="*" -dir="./model" -c通过简略的配置咱们就能够应用model层的缓存啦,model层缓存默认过期工夫为7天,如果没有查到数据会设置一个空缓存,空缓存的过期工夫为1分钟,model层cache配置和初始化如下: CacheRedis: - Host: 127.0.0.1:6379 Type: nodeCategoryModel: model.NewCategoryModel(conn, c.CacheRedis)这次演示的代码次要会基于product-rpc服务,为了简略咱们间接应用grpcurl来进行调试,留神启动的时候次要注册反射服务,通过goctl主动生成的rpc服务在dev或test环境下曾经帮咱们注册好了,咱们须要把咱们的mode设置为dev,默认的mode为pro,如下代码所示: s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { product.RegisterProductServer(grpcServer, svr) if c.Mode == service.DevMode || c.Mode == service.TestMode { reflection.Register(grpcServer) }})间接应用go install装置grpcurl工具,so easy !!!妈妈再也不必放心我不会调试gRPC了 go install github.com/fullstorydev/grpcurl/cmd/grpcurl启动服务,通过如下命令查问服务,服务提供的办法,能够看到以后提供了Product获取商品详情接口和Products批量获取商品详情接口 ~ grpcurl -plaintext 127.0.0.1:8081 listgrpc.health.v1.Healthgrpc.reflection.v1alpha.ServerReflectionproduct.Product~ grpcurl -plaintext 127.0.0.1:8081 list product.Productproduct.Product.Productproduct.Product.Products咱们先往product表里插入一些测试数据,测试数据放在lebron/sql/data.sql文件中,此时咱们查看id为1的商品数据,这时候缓存中是没有id为1这条数据的 127.0.0.1:6379> EXISTS cache:product:product:id:1(integer) 0通过grpcurl工具来调用Product接口查问id为1的商品数据,能够看到曾经返回了数据 ~ grpcurl -plaintext -d '{"product_id": 1}' 127.0.0.1:8081 product.Product.Product{ "productId": "1", "name": "夹克1"}再看redis中曾经存在了id为1的这条数据的缓存,这就是框架给咱们主动生成的缓存 ...

June 22, 2022 · 4 min · jiezi

关于go:go-defer的坑

package mainimport ( "errors" "fmt" "runtime")func main() { var err error err = nil var i int64 i = 0 defer dead(err, i) defer dead2(&err, &i) defer func() { // 闭包,err,i的值会随着调用函数的语句扭转 _, _, line, _ := runtime.Caller(0) fmt.Printf("%v err:%v i:%v\n", line, err, i) }() defer func(err error, i int64) { // 尽管是闭包,然而 defer内函数的值拷贝 _, _, line, _ := runtime.Caller(0) fmt.Printf("%v err:%v i:%v\n", line, err, i) }(err, i) i = 5 err = errors.New("aaa") _, _, line, _ := runtime.Caller(0) fmt.Printf("%v err:%v i:%v\n", line, err, i)}func dead(err error, i int64) {// 值传递,defer申明的时候,error,i值曾经确定了 _, _, line, _ := runtime.Caller(0) fmt.Printf("%v err:%v i:%v\n", line, err, i)}func dead2(err *error, i *int64) {// 指针传递,err i 指向的内容,会反映出defer执行时的内容。 _, _, line, _ := runtime.Caller(0) fmt.Printf("%v err:%v i:%v\n", line, *err, *i)}

June 21, 2022 · 1 min · jiezi

关于go:golang-aes加密-扩展ecb模式

AES加密属于对称加密(当然还有非对称加密rsa),对称加密个别分为流加密(如OFB、CFB等)和块加密(如ECB、CBC等)。然而golang的官网库中没有ECB的模式,至于为什么没有ECB模式,能够查看官网issue,意思就是不平安,然而咱们的确要应用的话,怎么去实现呢,上面进入正题。 我先把ECB模式实现代码贴出来,有想看剖析怎么实现的能够看上面的剖析实现过程。 type ecb struct { b cipher.Block blockSize int}func newECB(b cipher.Block) *ecb { return &ecb{ b: b, blockSize: b.BlockSize(), }}type ecbEncrypter ecbfunc NewECBEncrypter(b cipher.Block) cipher.BlockMode { return (*ecbEncrypter)(newECB(b))}func (x *ecbEncrypter) BlockSize() int { return x.blockSize }func (x *ecbEncrypter) CryptBlocks(dst, src []byte) { if len(src)%x.blockSize != 0 { panic("crypto/cipher: input not full blocks") } if len(dst) < len(src) { panic("crypto/cipher: output smaller than input") } for len(src) > 0 { x.b.Encrypt(dst, src[:x.blockSize]) src = src[x.blockSize:] dst = dst[x.blockSize:] }}type ecbDecrypter ecbfunc NewECBDecrypter(b cipher.Block) cipher.BlockMode { return (*ecbDecrypter)(newECB(b))}func (x *ecbDecrypter) BlockSize() int { return x.blockSize }func (x *ecbDecrypter) CryptBlocks(dst, src []byte) { if len(src)%x.blockSize != 0 { panic("crypto/cipher: input not full blocks") } if len(dst) < len(src) { panic("crypto/cipher: output smaller than input") } for len(src) > 0 { x.b.Decrypt(dst, src[:x.blockSize]) src = src[x.blockSize:] dst = dst[x.blockSize:] }}应用实例 ...

June 21, 2022 · 2 min · jiezi

关于go:Go题库9同一个协程里面对无缓冲channel同时发送和接收数据有什么问题

解析整顿 GOLANG ROADMAO社区答案(engine)同一个协程里,不能对无缓冲channel同时发送和接收数据,如果这么做会间接报错死锁。 对于一个无缓冲的channel而言,只有不同的协程之间一方发送数据一方承受数据才不会阻塞。channel无缓冲时,发送阻塞直到数据被接管,接管阻塞直到读到数据。 本文由:GOLANG ROADMAP社区 公布

June 21, 2022 · 1 min · jiezi

关于go:gozero微服务实战系列四CRUD热身

上一篇文章咱们把整个我的项目的架子搭建实现,服务在本地也曾经能运行起来了,顺利成章的接下来咱们就应该开始写业务逻辑代码了,然而单纯的写业务逻辑代码是比拟干燥的,业务逻辑的代码我会一直地补充到 lerbon 我的项目中去,要害局部我也会加上正文。 那么本篇文章我次要想和大家分享下服务的根本配置和几个典型的代码示例。 日志定义go-zero的 logx 包提供了日志性能,默认不须要做任何配置就能够在stdout中输入日志。当咱们申请/v1/order/list接口的时候输入日志如下,默认是json格局输入,包含工夫戳,http申请的根本信息,接口耗时,以及链路追踪的span和trace信息。 {"@timestamp":"2022-06-11T08:23:36.342+08:00","caller":"handler/loghandler.go:197","content":"[HTTP] 200 - GET /v1/order/list?uid=123 - 127.0.0.1:59998 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36","duration":"21.2ms","level":"info","span":"23c4deaa3432fd03","trace":"091ffcb0eafe7818b294e4d8122cf8a1"}程序启动后,框架会默认输入level为stat的统计日志,用于输入以后资源的应用状况,次要为cpu和内存,内容如下: {"@timestamp":"2022-06-11T08:34:58.402+08:00","caller":"stat/usage.go:61","content":"CPU: 0m, MEMORY: Alloc=3.3Mi, TotalAlloc=7.0Mi, Sys=16.3Mi, NumGC=8","level":"stat"}当咱们不须要这类日志的时候,咱们能够通过如下形式敞开该类日志的输入: logx.DisableStat()有的时候咱们只须要记录谬误日志,能够通过设置日志等级来勾销level为info级别日志的输入: logx.SetLevel(logx.ErrorLevel)能够扩大日志输入的字段,增加了uid字段记录申请的用户的uid,日志打印内容如下: logx.Infow("order list", logx.Field("uid",req.UID)){"@timestamp":"2022-06-11T08:53:50.609+08:00","caller":"logic/orderlistlogic.go:31","content":"order list","level":"info","uid":123}咱们还能够扩大其余第三方日志库,通过logx.SetWriter来进行设置 writer := logrusx.NewLogrusWriter(func(logger *logrus.Logger) { logger.SetFormatter(&logrus.JSONFormatter{})})logx.SetWriter(writer)同时logx还提供了丰盛的配置,能够配置日志输入模式,工夫格局,输入门路,是否压缩,日志保留工夫等 type LogConf struct { ServiceName string `json:",optional"` Mode string `json:",default=console,options=[console,file,volume]"` Encoding string `json:",default=json,options=[json,plain]"` TimeFormat string `json:",optional"` Path string `json:",default=logs"` Level string `json:",default=info,options=[info,error,severe]"` Compress bool `json:",optional"` KeepDays int `json:",optional"` StackCooldownMillis int `json:",default=100"`}能够看到logx提供的日志性能还是十分丰盛的,同时反对了各种自定义的形式。日志是咱们排查线上问题十分重要的依赖,咱们还会依据日志做各种告警,所以这里咱们先做了一些日志应用的介绍。 ...

June 21, 2022 · 3 min · jiezi

关于go:Go题库8channel实现方式原理概念底层实现

面试企业 好将来、米哈游、跟谁学,字节跳动、美团、网易、新浪、滴滴、小米 题目解析 GOLANG ROADMA社区 答案(知北游)+背景: Go语言提供了一种不同的并发模型--通信顺序进程(communicating sequential processes,CSP)。设计模式:通过通信的形式共享内存channel收发操作遵循先进先出(FIFO)的设计底层构造: type hchan struct { qcount uint // channel中的元素个数 dataqsiz uint // channel中循环队列的长度 buf unsafe.Pointer // channel缓冲区数据指针 elemsize uint16 // buffer中每个元素的大小 closed uint32 // channel是否曾经敞开,0未敞开 elemtype *_type // channel中的元素的类型 sendx uint // channel发送操作解决到的地位 recvx uint // channel接管操作解决到的地位 recvq waitq // 期待接管的sudog(sudog为封装了goroutine和数据的构造)队列因为缓冲区空间有余而阻塞的Goroutine列表 sendq waitq // 期待发送的sudogo队列,因为缓冲区空间有余而阻塞的Goroutine列表 lock mutex // 一个轻量级锁}channel创立: ch := make(chan int, 3)创立channel实际上就是在内存中实例化了一个hchan构造体,并返回一个chan指针channle在函数间传递都是应用的这个指针,这就是为什么函数传递中无需应用channel的指针,而是间接用channel就行了,因为channel自身就是一个指针channel发送数据: ch <- 1ch <- 2查看 recvq 是否为空,如果不为空,则从 recvq 头部取一个 goroutine,将数据发送过来,并唤醒对应的 goroutine 即可。如果 recvq 为空,则将数据放入到 buffer 中。如果 buffer 已满,则将要发送的数据和以后 goroutine 打包成 sudog 对象放入到 sendq中。并将以后 goroutine 置为 waiting 状态。channel接收数据: ...

June 20, 2022 · 1 min · jiezi

关于go:go-编译标签-build-tag注释里的编译语法

go 编译标签( build tag)-正文里的编译语法

June 20, 2022 · 1 min · jiezi

关于go:Go-119要来了看看都有哪些变化

前言Go官网团队在2022.06.11公布了Go 1.19 Beta 1版本,Go 1.19的正式release版本预计会在往年8月份公布。 让咱们先睹为快,看看Go 1.19给咱们带来了哪些变动。 这是Go 1.19版本更新内容详解的第1篇,欢送大家关注公众号,及时获取本系列最新更新。 Go 1.19公布清单和Go 1.18相比,改变绝对较小,次要波及语言(Language)、内存模型(Memory Model)、可移植性(Ports)、Go Tool工具链、运行时(Runtime)、编译器(Compiler)、汇编器(Assembler)、链接器(Linker)和外围库(Core library)等方面的优化。 咱们一一看看具体都有哪些变动。 语言变动语言层面的变动很小,只有一个和泛型相干的优化,调整了泛型函数和办法申明里的类型参数(type parameter)的作用域,不影响现有代码。 为什么会有这个调整呢?咱们来看看如下的代码 type T[T any] struct { m1 T}这段代码定义了泛型类型T ,类型参数列表(type parameter list)里的类型参数(type parameter)命名也是T,代码能够失常编译通过。 然而如下代码呢? type T[T any] struct { m1 T}func(t T[T]) print() { fmt.Println(t.m1)}咱们定义了泛型类型T 的办法print,编译下面的代码,编译器会提醒: ./main.go:11:8: T is not a generic type也就是说编译器认为办法print里的第一个T不是泛型类型,为什么会这样呢?咱们来看看官网阐明: The scope of an identifier denoting a type parameter of a function or declared by a method receiver is the function body and all parameter lists of the function.这段阐明对应到下面的代码,编译器会认为print办法里的第2个T(类型参数T)的作用域是函数体以及函数的参数列表(这里的参数列表包含办法receiver parameter list和函数名前面的参数列表)。因而第2个T就笼罩了第1个T ,所以编译器会提醒T is not a generic type。 ...

June 19, 2022 · 1 min · jiezi

关于go:Go十大常见错误第2篇benchmark性能测试的坑

前言这是Go十大常见谬误系列的第二篇:benchmark性能测试的坑。素材来源于Go布道者,现Docker公司资深工程师Teiva Harsanyi。 本文波及的源代码全副开源在:Go十大常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。 场景go test反对benchmark性能测试,然而你晓得这里可能有坑么? 一个常见的坑是编译器内联优化,咱们来看一个具体的例子: func add(a int, b int) int { return a + b}当初咱们要对add函数做性能测试,可能会编写如下测试代码: func BenchmarkWrong(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { add(1000000000, 1000000001) }}这里可能有什么坑呢?对于编译器而言,add函数是一个叶子函数(leaf function),即add函数自身没有调用其它函数,所以编译器会对add函数的调用做内联(inline)优化,这会导致性能测试的后果不精确。因为咱们通常要测试的是本人程序自身的执行效率,而不是编译器做了优化后的执行效率,这样才不便咱们对程序的性能有一个正确的认知,而且你做go test测试时编译器的优化成果和理论生产环境运行时编译器的优化成果可能也不一样。 那怎么晓得执行go test的时候编译器是否做了内联优化呢?很简略,给go test减少-gcflags="-m"参数,-m示意打印编译器做出的优化决定。 $ go test -gcflags="-m" -v -bench=BenchmarkWrong -count 1# example.com/benchmark [example.com/benchmark.test]./go_util.go:3:6: can inline add./go_bench_test.go:19:6: inlining call to add./go_bench_test.go:16:21: b does not escape# example.com/benchmark.test/var/folders/pv/_x849j6n22x37xxd9cstgwkr0000gn/T/go-build2365344599/b001/_testmain.go:33:6: can inline init.0/var/folders/pv/_x849j6n22x37xxd9cstgwkr0000gn/T/go-build2365344599/b001/_testmain.go:41:24: inlining call to testing.MainStart/var/folders/pv/_x849j6n22x37xxd9cstgwkr0000gn/T/go-build2365344599/b001/_testmain.go:41:42: testdeps.TestDeps{} escapes to heap/var/folders/pv/_x849j6n22x37xxd9cstgwkr0000gn/T/go-build2365344599/b001/_testmain.go:41:24: &testing.M{...} escapes to heapgoos: darwingoarch: amd64pkg: example.com/benchmarkcpu: Intel(R) Core(TM) i5-5250U CPU @ 1.60GHzBenchmarkWrongBenchmarkWrong-4 1000000000 0.4601 ns/opPASSok example.com/benchmark 0.605s下面的执行后果的./go_bench_test.go:19:6: inlining call to add就示意编译器对BenchmarkWrong里的add函数调用做了内联优化。 ...

June 18, 2022 · 2 min · jiezi

关于go:go-wire-入门

go wire 入门

June 17, 2022 · 1 min · jiezi

关于go:Go题库7介绍一下通道

面试企业 知乎 题目解析 GOLANG ROADMAP社区 答案1(栾龙生)如果说goroutine是Go程序并发的执行体,通道就是它们之间的连贯。通道能够使一个goroutine发送特定值到另一个goroutine的通信机制。每一个通道都是一个具体类型的导管,叫做通道的元素类型。例如一个具备int类型元素的通道写为chan int。 通道是一个用map创立的数据结构的援用。当复制或者作为参数传递到一个函数时,复制的是援用,这样调用者和被调用者都援用同一份数据结构。和其余援用类型一样,通道的零值是nil。 通道有两个次要操作:发送(send)和接管(receive),两者统称为通信。send语句从一个goroutine传输一个值到另一个在执行接管表达式的goroutine。两个操作都应用<-操作符书写。发送语句中,通道和值别离在<-的左右两边。在接管表达式中,<-放在通道操作数后面,在接管表达式中,其后果未被应用也是非法的。 ch <- x //发送语句x = <-ch //接管语句<-ch //接管语句,抛弃后果通道反对第三个操作:敞开 (close),它设置一个标记位来批示值以后曾经发送结束,这个通道前面没有值了;敞开后的发送操作将导致宕机。在一个曾经敞开的通道上进行接管操作,将获取所有曾经发送的值,直到通道为空;这时任何接管操作会立刻实现,同时获取到一个通道元素对应的零值。通过调用内置的close函数来敞开通道: close(ch)依据通道的容量,能够将通道分为无缓冲通道和缓冲通道 无缓冲通道 ch = make(chan int) ch = make(chan int, 0)有缓冲通道 ch = make(chan int, 3)依据通道传输方向,还能够通道分为双向通道,只读通道和只写通道 只读通道 只能发送的通道,容许发送但不容许接管 chan<- int只写通道 只能接管的通道,容许接管但不容许发送 <-chan int答案2(溪尾)通道类型的值自身就是并发平安的。在申明并初始化一个通道时,能够应用内建函数make,传给这个函数第一个参数为通道具体类型的字面量(如:chan int),还能够接一个可选的整形参数作为通道的容量,然而这个整形数据不能小于零。 通道相当与一个先进先出(FIFO)的队列,各个元素严格依照发送顺序排列,先被发送的肯定会被先接管。应用操作符示意<- 如果定义通道时未指定通道的长度,那么该通道的长度为0,没有缓冲,即发送一个数据之后,通道就会阻塞,直到该元素被接管。如果定义的长度为n(n为正整数),那么通道的长度即为n。

June 17, 2022 · 1 min · jiezi

关于go:gozero微服务实战系列三API定义和表结构设计

<!-- go-zero微服务实战系列(三、API定义和表结构设计)--> 前两篇文章别离介绍了本系列文章的背景以及依据业务职能对商城零碎做了服务的拆分,其中每个服务又可分为如下三类: api服务 - BFF层,对外提供HTTP接口rpc服务 - 外部依赖的微服务,实现繁多的业务性能rmq服务 - 负责流式工作的解决,如生产kafka等等admin服务 - 对外部治理后盾提供HTTP接口,通常数据操作权限比拟高如果没看过前两篇文章可通过如下传送门查看 go-zero 微服务实战系列(一、开篇) go-zero微服务实战系列(二、服务拆分) 前两篇文章比拟偏实践,以至于文章收回去后有些同学感觉写得比拟水,十分了解大家迫切想要写代码的情绪,我也进行了粗浅的反思哈哈哈。所以从本篇开始就要进入万众期待的代码环节了。然而,所谓磨刀不误砍柴工,在真正的生产开发过程中,咱们个别都会花大量的工夫在需要的了解和协定的设计上,如果需要了解的不透彻或者协定设计的不合理就会大大增加咱们我的项目返工的可能,甚至还没上线就得重构。所以后期多投入一些工夫也齐全是值得的。当咱们把需要了解透彻,我的项目构造和协定定义清晰后,其实写代码就是因势利导的事件,速度那是大大滴快。闲言少叙,咱们开始明天的内容。 API定义可能大家在工作中都遇到过这样的场景,就是代码更新了然而文档没有更新,从而产生一些问题导致一些扯皮事件的产生。这个问题的实质是服务和文档是割裂的。咱们冀望的是文档即协定,协定即服务,这个理念与go-zero的api定义不约而同。 咱们定义了BFF层,BFF是对外提供HTTP接口的对立进口,所以咱们这里API的定义次要是针对BFF服务的API的定义。 API的兼容性咱们定义或批改API的时候肯定要思考向前兼容,如下几种状况是向前兼容的: 减少新的API接口协议申请参数增加字段,须要保障新老客户端对该字段的解决形式不同响应后果增加字段,该字段信息只会在新版本客户端中展现如下几种状况是向前不兼容的: 删除或重命名服务、字段、办法等,从实质上说,如果客户端代码能够援用某些内容,那么删除或者重命名它都是不兼容的变动,这时必须批改major版本号批改字段类型,这会导致客户端库生成的代码发生变化,因而必须减少major版本号,对于编译型动态语言来说,可能会编译谬误批改现有申请的可见行为,客户端通常依赖于API行为和语义,即便这样的行为没有被明确反对或记录。因而,在大多数状况下,批改API数据的行为或语义将被消费者视为是破坏性的给资源音讯增加 读取/写入 字段首页API定义首页性能次要分为四个局部,搜寻、Banner图、限时抢购和举荐商品列表,点击搜寻框会跳转到搜寻页,举荐局部是分页展现的,用户通过一直地往上滑动能够加载下一页。通过剖析首页咱们大抵须要提供三个接口,别离是Banner接口,限时抢购接口和举荐接口。 这里须要留神的是举荐接口,举荐接口返回的数据是须要反对分页的,这里分页采纳游标的形式,Ps参数为每页返回数据条数,默认一页返回20条数据,留神在服务端肯定须要再次校验Ps值,避免Ps歹意值导致的性能问题,比方Ps传了10000,当为非法值的时候须要把Ps置为默认值,Cursor为游标值,游标为每页最初一条数据的RecommendTime。 返回值中Products定义了返回的商品列表,IsEnd示意是否是最初一页,客户端通过判断IsEnd是否为true决定是否终止申请,RecommendTime为本页返回数据最初一条数据的举荐工夫,推动列表依照举荐工夫倒序返回。 RecommendRequest { Cursor int64 `json:"cursor"` Ps int64 `form:"ps,default=20"` // 每页大小}RecommendResponse { Products []*Product `json:"products"` IsEnd bool `json:"is_end"` // 是否最初一页 RecommendTime int64 `json:"recommend_time"` // 商品列表最初一个商品的举荐工夫}Product { ID int64 `json:"id"` // 商品ID Name string `json:"name"` // 产品名称 Description string `json:"description"` // 商品形容 Price float64 `json:"price"` // 商品价格 Stock int64 `json:"stock"` // 库存 Category string `json:"category"` // 分类 Status int64 `json:"status"` // 状态:1-失常,2-下架 CreateTime int64 `json:"create_time"` // 创立工夫 UpdateTime int64 `json:"update_time"` // 更新工夫}抢购有一个倒计时的性能,咱们这里返回抢购开始工夫,客户端计算剩余时间进行倒计时。 ...

June 17, 2022 · 7 min · jiezi

关于go:Go题库6Go和java比有什么不同

面试企业 虾皮、知乎 题目解析 GOLANG ROADMAP社区 答案(溪尾)Go也称为Golang,是一种开源编程语言,Go能够轻松构建牢靠,简略和高效的软件。Go是键入的动态编译语言。Go语言提供垃圾收机制,CSP格调的并发性,内存安全性和构造类型。 Java是一种用于个别用处的计算机编程语言,它是基于类的,并发的和面向对象的。Java专门设计为蕴含很少的实现依赖项。Java应用程序在JVM(Java虚拟机)上运行。它是当今最驰名的编程语言之一。Java是一种用于为多个平台开发软件的编程语言。Java应用程序上的编译代码或字节码能够在大多数操作系统上运行,包含Linux,Mac操作系统和Linux。Java的大部分语法都源自C ++和C语言。 go语言和java之间的区别 函数重载 Go上不容许函数重载,必须具备办法和函数的惟一名称; java容许函数重载。 速度 go的速度比java快 多态 Java默认容许多态。而Go没有。 路由配置 Go语言应用HTTP协定进行路由配置; java应用Akka.routing.ConsistentHashingRouter和Akka.routing.ScatterGatherFirstCompletedRouter进行路由配置。 可扩展性 Go代码能够主动扩大到多个外围;而,Java并不总是具备足够的可扩展性。 继承 Go语言的继承通过匿名组合实现:基类以Struct的形式定义,子类只须要把基类作为成员放在子类的定义中,反对多继承; Java的继承通过extends关键字实现,不反对多继承。

June 16, 2022 · 1 min · jiezi

关于go:Go-atomicLoadUint32-和-Uint32相关处理方法

一、介绍在go语言中,atomic包提供和内存解决的原子化操作,在同步程序处理中,常常应用。LoadUint32()函数,提供加载原子化内存*addr,并返回Uint32格局。 语法如下: // LoadUint32 atomically loads *addr.func LoadUint32(addr *uint32) (val uint32)

June 16, 2022 · 1 min · jiezi

关于go:go语言源码阅读syncOnce

一、介绍sync.Once是 Go 规范库提供的使函数只执行一次的实现,常利用于单例模式,例如初始化配置、放弃数据库连贯等。作用与 init 函数相似,但有区别。 init函数是package包被首次加载的时候,初始化应用。sync.Once能够在任意地位,全局中执行一次。sync.Once源码在 src/sync/once.go咱们用 go doc sync.once 来整体看一下 type Once struct {}func (o *Once) Do(f func())能够看到只有一个 sync.Once 构造体,以及一个执行的办法 Once.Do(). 二、应用func TestOnce(t *testing.T) { //1 申明一个 sync.Once变量 var once sync.Once for i := 0; i < 10; i++ { go func() { //2 应用Do()执行首次仅只有一次操作 once.Do(func() { fmt.Println("hi") }) }() } time.Sleep(time.Second)}备注:须要留神sync.Once的变量范畴,比方这个变量范畴在 TestOnce()函数外面,所以在其余的中央,还是能够调用fmt.Println("hi")函数的。仅是在这个函数里,调用一次。这里的仅执行一次是在 sync.Once 变量范畴内,只执行一次。 三、源码浏览

June 15, 2022 · 1 min · jiezi

关于go:Go题库5Go的GMP模型

面试企业 深服气,百度,小米,哔哩哔哩,好将来,跟谁学,学而思,网易,腾讯,知乎,高德,字节,,新浪,虾皮,Aibee。 题目解析 GOLANG ROADMAP社区 答案(溪尾)G是Goroutine的缩写,相当于操作系统的过程管制块(process control block)。它蕴含:函数执行的指令和参数,工作对象,线程上下文切换,字段爱护,和字段的寄存器。 M是一个线程,每个M都有一个线程的栈。如果没有给线程的栈分配内存,操作系统会给线程的栈调配默认的内存。当线程的栈制订,M.stack->G.stack, M的PC寄存器会执行G提供的函数。 P(处理器,Processor)是一个形象的概念,不是物理上的CPU。当一个P有工作,须要创立或者唤醒一个零碎线程去解决它队列中的工作。P决定同时执行的工作的数量,GOMAXPROCS限度零碎线程执行用户层面的工作的数量。 GO调度器的调度过程,首先创立一个G对象,而后G被保留在P的本地队列或者全局队列(global queue)。这时P会唤醒一个M。P依照它的执行程序继续执行工作。M寻找一个闲暇的P,如果找失去,将G与本人绑定。而后M执行一个调度循环:调用G对象->执行->清理线程->持续寻找Goroutine。 在M的执行过程中,上下文切换随时产生。当切换产生,工作的执行现场须要被爱护,这样在下一次调度执行能够进行现场复原。M的栈保留在G对象中,只有现场复原须要的寄存器(SP,PC等),须要被保留到G对象。 如果G对象还没有被执行,M能够将G从新放到P的调度队列,期待下一次的调度执行。当调度执行时,M能够通过G的vdsoSP, vdsoPC 寄存器进行现场复原。 P队列 P有2种类型的队列: 本地队列:本地的队列是无锁的,没有数据竞争问题,处理速度比拟高。全局队列:是用来均衡不同的P的工作数量,所有的M共享P的全局队列。线程清理 G的调度是为了实现P/M的绑定,所以线程清理就是开释P上的G,让其余的G可能被调度。 被动开释(active release):典型的例子是,执行G工作时,产生了零碎调用(system call),这时M会处于阻塞(Block)状态。调度器会设置一个超时工夫,来开释P。被动开释(passive release):如果零碎调用产生,监控程序须要扫描处于阻塞状态的P/M。 这时,超时之后,P资源会回收,程序被安顿给队列中的其余G工作。

June 15, 2022 · 1 min · jiezi

关于go:gozero-微服务实战系列二服务拆分

微服务概述微服务架构是一种架构格调,它将一个大的零碎构建为多个微服务的汇合,这些微服务是围绕业务性能构建的,服务关注繁多的业务性能,这些服务具备以下特点: 高度可保护和可测试涣散的耦合可独立部署围绕业务性能进行构建由不同的小团队进行保护微服务架构可能疾速、频繁、牢靠地交付大型、简单的应用程序,通过业务拆分实现服务组件化,应用组件进行组合从而疾速开发零碎。 服务划分咱们首先进行微服务的划分,在理论的我的项目开发中,咱们通常采纳两种微服务划分策略,第一种形式是通过业务职能进行微服务边界的划分,第二种形式是通过DDD的界线上下文进行微服务边界的划分,咱们这里采纳大家比拟容易了解的业务职能的形式进行微服务划分,再次贴上咱们电商我的项目的思维导图: 从以上思维导图能够看出整个电商零碎性能还是比拟多的,咱们依据业务职能做如下微服务的划分: 商品服务(product) - 商品的增加、信息查问、库存治理等性能购物车服务(cart) - 购物车的增删改查订单服务(order) - 生成订单,订单治理领取服务(pay) - 通过调用第三方领取实现领取性能账号服务(user) - 用户信息、等级、封禁、地址治理举荐服务(recommend) - 首页商品举荐评论服务(reply) - 商品的评论性能、评论的回复性能BFF层个别对客户端咱们都会采纳HTTP接口的形式提供服务,那是不是以上划分的这些微服务都须要间接提供HTTP接口对外提供服务呢?这样当然能够,架构整体看起来也比较简单。 但对于一个简单的高并发的零碎来说,咱们须要解决各种异样的场景,比方某个页面须要依赖多个微服务提供的数据,为了防止串行申请导致的耗时过长,咱们个别会并行的申请多个微服务,这个时候其中的某个服务申请异样的话咱们可能须要做一些非凡的解决,比方提供一些降级的数据等。还有咱们的页面展现的数据往往都是面向业务性能的,而不是单单某一个微服务的数据,这时候咱们往往须要组装多个微服务的数据来满足需要,如果咱们每个微服务都间接对外提供HTTP接口的话,那么这些简单的数据组装和异样解决等工作只能由客户端来实现。家喻户晓客户端是不宜做简单的业务逻辑的,客户端的重点应该更多是做交互体验上的优化,咱们的整体架构须要做到前轻后重,即客户端逻辑尽量少而把比拟重的业务解决逻辑下沉到服务端,而服务端又依据业务职能拆分成了不同的微服务,这些微服务只关注繁多的业务,那么这些面向业务场景的简单逻辑的解决应该放到哪里呢?咱们的解决方案就是加一层,即BFF层,通过BFF对外提供HTTP接口,客户端只与BFF进行交互。 BFF层的引入解决了咱们下面遇到的问题,但减少一层就会减少架构的复杂度,所以如果你的服务是一个单体利用的话,那么BFF是不必要的,引入它不会减少任何价值。对于咱们这个我的项目来说,咱们的应用程序依赖于微服务,同时咱们须要面向业务性能提供HTTP接口和要保障接口的高可用,所以BFF对于咱们这个我的项目来说是一个适合的抉择。 咱们能够提供多个BFF吗?答案是当然能够。BFF的目标是为客户端提供一个集中的接口,例如挪动端页面和浏览器页面的数据协定不同,这种状况下为了更好的示意数据,能够应用两个BFF,同时只供一个BFF如果该BFF异样就会导致所有的业务受影响,提供多个BFF也能够进步服务的可用性,升高业务异样的影响面。多个BFF架构图如下: 咱们的这个我的项目为了简化只会采纳一个BFF服务。 工程构造咱们采纳集中管理的形式,把所有的服务放到一个大仓库中,仓库的目录构造如下: lebron为工程名,lebron上面有apps和pkg两个目录,其中apps寄存的是咱们所有的微服务,比方order为订单相干的微服务,pkg目录为所有服务独特依赖的包的寄存门路,比方所有的服务都须要依赖鉴权就能够放到pkg目录下。 app - BFF服务cart - 购物车服务order - 订单服务pay - 领取服务product - 商品服务recommend - 举荐服务reply - 评论服务user - 账号服务在每个服务目录下咱们又会分为多个服务,次要会有如下几类服务: api - 对外的BFF服务,承受来自客户端的申请,裸露HTTP接口rpc - 对内的微服务,仅承受来自外部其余微服务或者BFF的申请,裸露gRPC接口rmq - 负责进行流式工作解决,上游个别依赖音讯队列,比方kafka等admin - 也是对内的服务,区别于rpc,更多的是面向经营侧的且数据权限较高,通过隔离可带来更好的代码级别的平安,间接提供HTTP接口apps目录下每个服务的构造如下: 大多服务都会拆分成rpc、rmq和admin来满足对内提供rpc接口和经营数据的需要,同时通过rmq来解决流式工作。比拟非凡的是app下只有api服务,因为app是BFF所有只有api服务,前面可能会减少rmq服务,比方来流式解决用户每天首次登陆加教训之类的逻辑,咱们前面能够随时扩大,临时先只提供api服务。recommend只有rpc服务,因为举荐服务须要依赖AI团队或者大数据团队提供的数据,咱们只须要申请对应的数据接口和做一些满足业务的解决即可,所以这里recommend只有rpc服务。 代码初始化整个工程的构造曾经定义分明了,上面咱们做服务代码的初始化 咱们应用goctl来进行我的项目的初始化,比方咱们先初始化order,先进入order目录下: $ cd lebron/apps/order执行如下命令即可初始化order rpc代码 $ goctl rpc new rpc生成的代码构造如下: ...

June 15, 2022 · 1 min · jiezi

关于go:如何记住Go-timeFormat风骚的使用方法

其它编程语言的开发者,在第一次应用Go的工夫格式化办法时肯定会一脸懵逼,为什么不是我相熟的yyyy、MM、dd,而是一个非凡的年、月、日数字呢? 首先看一下正确的应用办法 // 代码s := time.Now().Format("2006-01-02 15:04:05.999999")fmt.Printf("%s\n", s)// 后果(正确)2022-06-14 23:21:48.655781如果前面的工夫轻易输出呢?(其实我是复制下面的输入后果) // 代码s := time.Now().Format("2022-06-14 23:21:48.655781")fmt.Printf("%s\n", s)// 后果(出其不意)141414-22-627 1411:146:278.61818786既然必须得按人家的标准来,那咱们如何不便疾速得记住这段神奇的数字呢?官网的文档和源码中有这么一记录: // These are predefined layouts for use in Time.Format and time.Parse.// The reference time used in these layouts is the specific time stamp:// 01/02 03:04:05PM '06 -0700其实就是1月2日3点(下午)4分5秒6年-7时区(崔健:一二三四五六七呀……)咱们日常习惯都是年月日,给记忆带来了一丢丢麻烦,不论时区的话就是:612345。记住了吗?写几个试试呗? // 代码s := time.Now().Format("2006年01月02日 15时04分05秒999毫秒")fmt.Printf("%s\n", s)// 输入(发现了吗?除了毫秒都正确!)2022年06月14日 23时46分45秒999毫秒// 通过测试,发现毫秒后面必须有:,或者.,应用9或者0都能够time.Now().Format("2006年01月02日 15时04分05秒.000毫秒")// 代码(整个奇怪的形态,月日时秒不补0)s := time.Now().Format("2006/1/2 3:04:5")fmt.Printf("%s\n", s)// 输入2022/6/14 11:53:2各种细节还是得看文档: // Year: "2006" "06"// Month: "Jan" "January"// Textual day of the week: "Mon" "Monday"// Numeric day of the month: "2" "_2" "02"// Numeric day of the year: "__2" "002"// Hour: "15" "3" "03" (PM or AM)// Minute: "4" "04"// Second: "5" "05"// AM/PM mark: "PM"看了这么多,你学废了吗?如果还不会用,那就本人封装一个yyyyMMdd本人习惯的形式吧,Go纯正就是把2006、01、02这些数字当成了yyyMMdd这种形式用了…… ...

June 14, 2022 · 1 min · jiezi

关于go:Go题库4数组怎么转集合

面试企业 TCL 解析整顿 GOLANG ROADMAP社区 答案1:(溪尾) 能够应用数组的索引作为map的key,数组的值作为map的值 func main() { arr := [5]int{1, 2, 3, 4, 5} m := make(map[int]int, 5) for i, v := range arr { m[i] = v } fmt.Println(m)}

June 14, 2022 · 1 min · jiezi

关于go:惊了goto-语句让-Go-代码变成意大利面条吗

大家好,我是煎鱼。 Goto 语句在社区的探讨中常常被人诟病,认为其毁坏了结构化编程和程序的形象,是无害的,可怕的。 最早的观点来源于 1968 年,Edsger Dijkstra 写了一封信《Go To Statement Considered Harmful》,来表白其是无害的观点。 如下图: 不过,然而,其实... Go 反对了 goto 语句,很多人不了解,大喊 less is more 的 Go Team 竟然加了... 明天就由煎鱼带大家看看。 Goto 语法Goto 的语法格局,如下: goto label......label: statement代码案例,如下: package mainimport "fmt"func main() { learnGoTo()}func learnGoTo() { fmt.Println("a") goto FINISH fmt.Println("b")FINISH: fmt.Println("c")}上述代码在函数 learnGoTo 中先输入了 a,而后到了 goto FINISH 代码段,因而间接跳到了 c 的输入,所以 b 的输入代码被间接跳过。 输入后果: acGoto 的危害Goto 的危害所带来的一个经典名称是:Spaghetti code(意大利面条代码),指的是对非结构化和难以保护的源代码的贬义词。 这样的代码具备简单而纠结的控制结构,导致程序流程在概念上就像一碗意大利面,扭曲和纠结。 参考代码如下: INPUT "How many numbers should be sorted? "; T DIM n(T) FOR i = 1 TO T PRINT "NUMBER:"; i INPUT n(i) NEXT i 'Calculations: C = T E180: C = INT(C / 2) IF C = 0 THEN GOTO C330 D = T - C E = 1 I220: f = E F230: g = f + C IF n(f) > n(g) THEN SWAP n(f), n(g) f = f - C IF f > 0 THEN GOTO F230 E = E + 1 IF E > D THEN GOTO E180 GOTO I220 C330: PRINT "The sorted list is" FOR i = 1 TO T PRINT n(i) NEXT i下面这个例子,你能看到 goto 语句可能在任意控制流中到处流转,你可能还得记住它的标签是什么,跳到哪里。 ...

June 14, 2022 · 2 min · jiezi

关于go:go源码阅读syncRWMutex读写锁

一、介绍sync.RWMutex为读写锁,源码地位在src/sync/rwmutex.go咱们应用命令 go doc sync.RWMutex type RWMutex struct { w Mutex // held if there are pending writers writerSem uint32 // semaphore for writers to wait for completing readers readerSem uint32 // semaphore for readers to wait for completing writers readerCount int32 // number of pending readers readerWait int32 // number of departing readers}func (rw *RWMutex) Lock()func (rw *RWMutex) RLock()func (rw *RWMutex) RLocker() Lockerfunc (rw *RWMutex) RUnlock()func (rw *RWMutex) Unlock()咱们能够晓得 rwmutex.go 文件次要有 RWMutex构造体以及Lock()、RLock()、RLocker()、RUnlock()、Unlock() 5个办法组成。 ...

June 14, 2022 · 1 min · jiezi

关于go:gozero-微服务实战系列一开篇

前言在社区中常常看到有人问有没有基于 go-zero 的比拟残缺的我的项目参考,该类问题实质上是想晓得基于 go-zero 的我的项目的最佳实际。残缺的我的项目应该是一个残缺的产品性能,蕴含产品需要、架构设计、要害流程的编码、表设计以及各种性能优化和数据一致性等,是一个真正贴近生产级的我的项目,是能够间接拿来在本人的生产我的项目中进行参考的,而目前社区并没有相似的比拟残缺的开源我的项目参考,因而决定和大家一起从零开始基于 go-zero 构建一个残缺的贴近生产的我的项目。 该系列的主题是基于 go-zero 构建一个高并发的零碎,为神马要抉择这个主题呢?我猜大家肯定感触到了 CRUD 的干燥,而是心愿我的项目是高并发的是带有肯定挑战的,可能咱们工作中的我的项目申请量比拟低,次要以实现性能为主。然而,古代的互联网产品随时都有面对突发大流量的可能,比方咱们的商城平时可能业务流量比拟安稳,但某一天经营忽然说要做一次秒杀流动来推广,如果没有提前做好应答高并发的筹备,咱们的服务很可能是扛不住的,后果也就可想而知。所以咱们平时就须要提前储备足够多的高并发的常识,当遇到高并发需要的场景的时候就可能从容应对。 该系列为实战系列,过于根底的常识可能会比拟少波及,所以须要大家具备以下根底能力: 理解go语言语法和根本应用姿态,请参考文档本系列我的项目数据库应用Mysql,请参考文档本系列我的项目缓存应用Redis,请参考文档本系列我的项目音讯队列应用Kafka,请参考文档理解go-zero根本应用,请参考文档为什么抉择go-zero?go-zero 为咱们提供了许多高并发场景下的实用工具,比方为了升高接口耗时咱们往往须要并发的申请依赖的服务,这个时候咱们能够应用 mapreduce 并行的解决申请;面对海量申请为了升高Redis压力和进步响应工夫,咱们能够应用 collection 构建本地缓存;防止用户间断申请造成服务压力能够应用 limit 做用户级别的限流等等。好的框架可能给我的我的项目开发带来事倍功半的成果,Go-zero的简洁易用性与内置的开箱即用的工具和服务治理能力助力咱们构建一个高并发高牢靠的零碎。 产品需要为了更加贴近咱们实在的我的项目开发,我的项目会基于实在的产品需要进行构建。商城是咱们平时接触比拟多而又比较复杂的零碎,所以咱们决定通过构建商城零碎来和大家一起学习如何基于 go-zero 构建高并发零碎。商城的性能点十分多,咱们没有方法把所有的性能点都一一演示,但会实现一个 mvp版本,咱们会在mvp版本中把 go-zero 的外围性能和构建高并发商城零碎的外围点都演示进去。 如下思维导图列出了该电商零碎次要实现的性能: 产品原型图原型图是咱们实现性能的次要参考,上面列出了商城零碎次要的页面UI,通过这些UI在心中能够大抵构建出零碎的架构,构建不进去也木有问题,追随文章一点点后退即可,奥利给!!! 首页和分类原型图能够看到首页性能比较复杂,蕴含了搜寻性能、Banner轮播图、抢购入口、排行榜、举荐等,分类中蕴含了不同分类的举荐商品 购物车和我的订单原型图购物车展现了以后登录用户的购物车商品列表,除了商品根本信息还包含了商品数量。我的订单列表中展现了处于不同状态的商品列表 商品详情和商品评估原型商品详情展现了商品的详情信息,同时在该页面能够增加购物车以及立刻购买等,用户还能够点击珍藏对该商品进行珍藏,商品的评估性能是必不可少的,你是不是在购买某件商品的时候也会先看看该商品的用户评估呢? 以上贴出了商城我的项目次要实现的性能点和次要页面的原型图,咱们的我的项目也会围绕这些次要的性能点开展。比拟外围的性能点,比方申请量十分高的首页Banner咱们如何优化,抢购商品如何保障不超卖,交易过程中分布式事务的实现等咱们都会重点介绍。 系列文章目录第一篇即为本篇文章,次要对我的项目做一个概览。 第二篇文章会介绍咱们的微服务的划分以及工程目录构造的定义和应用 goctl 做一些我的项目代码的初始化工作。 第三篇文章会先定义我的项目的 API,只有定义好 API 协定之后能力真正的开始写代码,如何协定定义的不分明会大大增加我的项目返工的几率,定义好API后紧接着咱们须要进行表结构设计,数据库咱们应用 MySQL。 第四篇文章次要是和大家一起相熟下 go-zero 的根本应用,次要包含数据库的增删改查,缓存的操作,自定义中间件,罕用工具包比方 mapreduce 的应用等等。 第五六七篇咱们着重介绍缓存的应用姿态,在高并发零碎中缓存的位置显而易见,不夸大的说如何缓存设计的不好,那高并发零碎也就不复存在。咱们会介绍如何利用缓存优化申请量微小的首页Banner,商品列表缓存实现,以及常见的Cache Aside模式代码该怎么写,还有缓存的一致性、击穿、穿透、雪崩等优化等等。 第八篇和第九篇会介绍如何实现一个简略的秒杀性能,秒杀性能堪称是高并发读和高并发写的典型代表,通过这两篇能够理解到秒杀性能的优化技巧和根本姿态。 第十篇介绍在微服务架构下常常遇到的分布式事务的问题,通过微服务拆分后,微服务间独占数据库,没法利用本地事务,通过该篇文章能够理解到在 go-zero 中的分布式事务的实现形式。 第十一篇介绍咱们的业务代码如何写单元测试。 第十二篇服务的可观测性,上线后的服务须要具备可观测性,包含日志、指标监控、链路追踪等。 第十三篇咱们会把服务部署上线并进行功能测试。 结束语本篇文章首先介绍了该系列由来的背景以及咱们为什么会抉择构建高并发零碎这个主题,接着介绍了咱们须要构建的高并发的商城零碎的次要性能点以及次要的页面原型图,最初列出了本系列的文章题目列表,通过题目列表能够疾速理解整个系列的常识体系。 构建一个高并发的零碎并不是一件容易的事件,波及的知识点十分多,但我置信只有咱们一起致力就肯定能克服这些艰难,让本人的技术能力更上一层楼。好在咱们能够站在伟人的肩膀上,go-zero 为咱们提供了十分弱小的构建高并发服务的能力,为咱们的零碎保驾护航。 心愿本篇文章对你有所帮忙,谢谢。 每周一、周四更新 ...

June 14, 2022 · 1 min · jiezi

关于go:go-stringsBuilder-使用

go strings.Builder 应用前言自己最近在找工作,面试的时候有一个手撕题目,这里题目形容如下: 问题形容// 实现一个textProcessor, 要求可能将对应的文本中的tag转换为之前设置过的内容:// 示例如下// set_variable("name", "lixiande")// get_text("Dear interviewer [name], welcome to our interview! ) // 该当是Dear interviewer lixiande, welcome to our interview! // 因为最近应用go比拟多,所以间接应用go实现type TextProcessor struct { record map[string]string}func NewTextProcessor() *TextProcessor { return &TextProcessor{ record: make(map[string]string), }}func (t *TextProcessor) set_variable(key, value string) { if _, ok := t.record[key]; ok { print("[warning] key exists!") t.record[key] = value } else { t.record[key] = value }}func (t *TextProcessor) get_text(text string) string { // -1:失常 0 :[剩下来的字符串当tag, flag := -1 res := "" tag := "" for _, v := range text { if flag == -1 { if v != '[' { res += string(v) } else { flag = 0 } } else { if flag == 0 { if v != ']' { tag += string(v) } else { if tagStr, ok := t.record[tag]; ok { res += tagStr tag = "" } flag = -1 } } } } return res}到这里根本就算做进去了,然而面试官让我剖析一下工夫复杂度,我下意识就说O(n),因为只须要扫描字符串一次,以及go map的查找时间复杂度为O(1)(现实状况),所以综合思考应该是O(n).然而我粗心了,这里res += string(v)实际上有肯定的工夫复杂度开销,在go外面string的扩容实际上和slice是相似的,O(n)的工夫复杂度.那么有什么办法优化呢?我作为老javaer,天然是晓得java外面的stringBuilder,然而不晓得golang 的stringBuilder的实现,学习了一下这里也记录一下. ...

June 14, 2022 · 3 min · jiezi

关于go:gookitgoutil-Go一些常用的工具函数实现增强收集和整理

gookit/goutil Go 罕用的一些工具函数,数字,字符串,数组,Map,文件,谬误,工夫日期,非凡解决,格式化,罕用信息获取等等 工具包arrutil array/slice 相干操作的函数工具包. 如:类型转换,元素查看等等dump 简略的变量打印工具,打印 slice, map 会主动换行显示每个元素,同时会显示打印调用地位cliutil CLI 的一些工具函数包. eg: read input, exec command, cmdline parse/builderrorx 为 go 提供加强的谬误实现,容许带有堆栈跟踪信息和包装另一个谬误。envutil ENV 信息获取判断工具包. eg: get one, get info, parse varfsutil 文件系统操作相干的工具函数包. eg: file and dir check, operatemaputil map 相干操作的函数工具包. eg: convert, sub-value get, simple mergemathutil, numutil int/number 相干操作的函数工具包. eg: convert, math calc, randomnetutil/httpreq 包装 http.Client 实现的更加易于应用的HTTP客户端strutil string 相干操作的函数工具包. eg: bytes, check, convert, encode, format and moresysutil system 相干操作的函数工具包. eg: sysenv, exec, user, processtestutil test help 相干操作的函数工具包. eg: http test, mock ENV valuetimex 提供加强的 time.Time 实现。增加更多罕用的性能办法 ...

June 13, 2022 · 1 min · jiezi

关于go:ZTool一款Go语言非常好用开发工具集

官网文档地址->:文档地址一、概述ZTool是在上个gotool的根底上延长过去的,因为gotoo这个我的项目两头存在很多第三方库的依赖,然而第三方库进行更新,两头存在很多bug没有保护,造成工具包两头同样存在很多问题最终通过思考,进行保护gotool从新开一个工程,ztool不进行任何的第三方库依赖,进行开发。同时通过两年多的理论我的项目开发总结,将业务底层代码抽离,抽出了由,字符串、工夫、随机id、http客户端、加密等工具集,同时咱们也欢送宽广go爱好者进行pr,同时咱们也开拓了pr通道 二、简介ZTool是一个玲珑而快捷工具库,通过办法封装,升高相干API的学习老本,进步工作效率,让应用GO语言开发编写代码更加优雅。 ZTool中的工具办法来自咱们长达两年的多应用go语言进行理论我的项目开发总结而来,它涵盖了GO开发中罕用的办法,它既是大型项目开发中解决小问题的利器,也是小型我的项目中的效率担当; ZTool它节俭了开发人员对我的项目中专用类和专用工具办法的封装工夫,使开发专一于业务,同时能够最大限度的防止封装不欠缺带来的bug。 三、装置go开发环境装置安装包下载地址:官网地址:https://go.dev/dl/如果无奈关上请应用备选地址:备选地址:https://go.dev/dl/go env来查看和验证go的环境信息,例如我的局部配置信息如下:GO111MODULE="on"GOARCH="amd64"GOBIN=""GOCACHE="/Users/Janus/Library/Caches/go-build"GOENV="/Users/Janus/Library/Application Support/go/env"GOMODCACHE="/Users/Janus/gopath/pkg/mod"GONOPROXY="github.com"GONOSUMDB="github.com"GOPATH="/Users/Janus/gopath"GOPRIVATE="github.com"GOPROXY="https://goproxy.cn,direct"GOROOT="/usr/local/go"GOSUMDB="sum.golang.org"GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"<font color="red">留神:GO111MODULE必须是开启开启状态,若为开启请自行google或百度解决</font> 2、ztool工具包装置版本要求go >=1.15 版本查看go version go get 装置go get -u -b github.com/druidcaesa/ztoolgo mod装置require github.com/druidcaesa/ztool四、字符串操作工具<font size="3">应用<font color="red"> ztool.StrUtils</font>调用对应的办法,ztool.StrUtils是字符串操作的外围类</font> 办法参数阐明阐明SnakeStringstring驼峰转蛇形,返回类型为stringCamelStringstring蛇形转驼峰,返回类型为stringHasBlankstring不可见字符串判空,返回bool类型HasEmptystring判断是否是空字符串(""),返回bool类型RemoveSuffixstring删除文件后缀获取文件名,返回stringRemovePrefixstring获取文件拓展名,返回stringEncodeHexStringstring字符串16进制编码,返回stringDecodeHexStringstring16进制转字符串,返回(string,error)五、工夫操作工具<font size="3">应用<font color="red"> ztool.DateUtils</font>调用对应的办法,ztool.DateUtils是工夫用用的外围类</font> ztool.DateUtils的返回类型是个struct以下是对构造体的阐明,ztool.DateUtils可应用链式办法调用 type DateTime struct { t time.Time //go的工夫类型封装 weekStartDay common.Weekday //一周的开始工夫设定}办法参数阐明阐明Now无参获取以后工夫,返回类型为DateTime构造体Format...string可选参数格式化工夫,返回类型是string 字符串SetTime...time.Time可选工夫类型参数设置工夫,须要传入time.Time,返回类型为DateTime构造体OperationDayint 参数为int依照天对工夫进行加减,参数可正负,返回类型为DateTime构造体OperationMoonint 参数为int依照月对工夫进行加减,参数可正负,返回类型为DateTime构造体OperationYearsint 参数为int依照年对工夫进行加减,参数可正负,返回类型为DateTime构造体StartOfHour无参获取以后工夫的开始工夫,返回类型为DateTime构造体StartTimeOfDay无参获取当天的开工夫,返回类型为DateTime构造体StartOfMinute无参获取分钟的开始,返回类型为DateTime构造体StartOfWeek无参获取一周的开工夫,返回类型为DateTime构造体StartOfMonth无参获取当月的开始工夫 ,返回类型为DateTime构造体SetWeekStartDaycommon.Monday设置一周的开始工夫是那天 ,返回类型为DateTime构造体StartOfQuarter无参获取以后季度的开始工夫 ,返回类型为DateTime构造体StartOfYear无参获取本年度开始工夫 ,返回类型为DateTime构造体EndOfMinute无参获取以后分钟完结工夫 ,返回类型为DateTime构造体EndOfHour无参获取以后小时的完结工夫 ,返回类型为DateTime构造体EndOfDay无参获取明天的完结工夫 ,返回类型为DateTime构造体EndOfWeek无参获取本周完结工夫 ,返回类型为DateTime构造体EndOfMonth无参获取本月完结工夫 ,返回类型为DateTime构造体EndOfQuarter无参获取本季度完结工夫 ,返回类型为DateTime构造体EndOfYear无参获取本年度完结工夫 ,返回类型为DateTime构造体Parse...string字符串格式化工夫 ,返回类型为DateTime构造体六、加密操作工具<font size="3">应用<font color="red"> ztool.EncryptionUtils</font>调用对应的办法,ztool.EncryptionUtils是加密工具的外围类</font> 办法参数阐明阐明AesEncryptstring,...stringAES加密办法,返回类型为(string,error)AesDecryptstring,...stringAES解密办法,返回类型为(string,error)DesDecryptstring,...stringDES解密办法,返回类型为(string,error)DesDecryptstring,...stringDES解密办法,返回类型为(string,error)Md5EncodestringMd5签名办法,返回类型为stringMd5Checkstring,stringMd5Check,返回类型为boole七、Http客户端<font size="3">应用<font color="red"> ztool.HttpUtils</font>调用对应的办法,ztool.HttpUtils是http客户端工具的外围类</font> 办法参数阐明阐明Getstring,...interface{}发送GET申请,返回类型为(string, error)Poststring,...interface{}发送POST申请,返回类型为(string, error)Putstring,...interface{}发送PUT申请,返回类型为(string, error)Deletestring,...interface{}发送DELETE申请,返回类型为(string, error)UploadFilePathurl, filename, filePath string文件上传,返回类型为(string, error)NewRequest无参获取request,返回类型为request 构造体request构造体是自定义构造体,外面有原http的request的对应封装// Request request subjecttype Request struct { //request client cli *http.Client //Timeout setting timeout time.Duration //request handler headers map[string]string //request parameters data interface{} //logger turn on logger bool //specifies the cookie jar jar http.CookieJar //For details of the agent, please refer to the official documentation proxy func(*http.Request) (*url.URL, error) //CheckRedirect specifies the policy for handling redirects checkRedirect func(req *http.Request, via []*http.Request) error //cookies setting cookies map[string]string //set request method method string //set request url url string tlsClientConfig *tls.Config disableKeepAlive bool transport *http.Transport debug bool}八、加密工具<font size="3">应用<font color="red"> ztool.IdUtils</font>调用对应的办法,ztool.IdUtils是ID生成工具的外围类</font> ...

June 13, 2022 · 1 min · jiezi

关于go:Go题库3Go语言中是如何实现继承的

面试企业 百度 解析整顿 GOLANG ROADMAP社区 答案(溪尾)对于Go语言是否像C++、Java一样是面向对象的语言,官网给出的解释如下: Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes). ...

June 13, 2022 · 1 min · jiezi

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

download:Go开发工程师:迎接回升风口,踏入蓝海行业!【完结】Django API 开发:一个 Todo 利用的后端引言本文中,咱们将学习利用 Django 构建一个 Todo(待办事项)利用的 API 后端,而后将其与 React 前端连接。 咱们已经制作了第一个 API,并回顾了 HTTP 和 REST 的工作原理,然而您仍然可能还没有“残缺”了解它们如何拆散在一起。咱们将创建一个 todo 结构,其中蕴含咱们的后端 Django Python 代码和咱们的前端 React JavaScript 代码。最终的布局将如下所示。todo| ├──frontend| ├──React... | ├──backend| ├──Django...复制代码初始化创建后端利用任何 Django API 的第一步始终是安装 Django,而后在其之上增加 Django REST Framework。 首先在桌面上的代码目录中创建一个专用的 todo 目录。打开一个新的命令行控制台,而后输出以下命令:$ cd ~/Desktop$ cd code$ mkdir todo && cd todo复制代码注意: 确保已从上一章中停用了虚拟环境。 您可能通过键入退出来执行此操作。 命令行后面是否没有括号? 好。 那么您就不在现有的虚拟环境中。在此 todo 文件夹中将是咱们的后端和前端目录。 让咱们创建一个后端文件夹,安装 Django,而后激活一个新的虚拟环境。$ mkdir backend && cd backend $ pipenv install django==2.2.6 $ pipenv shell复制代码您应该在命令行上看到括号,以确认虚拟环境(后端)已激活。现在已经安装了 Django,咱们应该首先创建一个传统的 Django 我的项目 todo_project,在其中增加第一个应用程序 todo,而后迁徙初始数据库。(backend) $ django-admin startproject todo_project . (backend) $ python manage.py startapp todos (backend) $ python manage.py migrate复制代码在 Django 中,咱们始终需要将新利用增加到 INSTALLED_APPS 设置中,所以现在就这样做。 在文本编辑器中打开 todo_project/settings.py。 在文件底部,增加 todos.apps.TodosConfig。 ...

June 11, 2022 · 4 min · jiezi

关于go:Golang-基础之并发知识-三

大家好,明天将梳理出的 Go语言并发常识内容,分享给大家。 请多多指教,谢谢。 本次《Go语言并发常识》内容共分为三个章节,本文为第三章节。 Golang 根底之并发常识 (一)Golang 根底之并发常识 (二)Golang 根底之并发常识 (三)本章节内容根本同步原语常见的锁类型扩大内容根本同步原语Go 语言在 sync 包中提供了用于同步的一些根本原语,包含常见的互斥锁 Mutex 与读写互斥锁 RWMutex 以及 Once、WaitGroup。这些根本原语的次要作用是提供较为根底的同步性能,本次仅对 Mutex开展介绍,残余其余原语将在后续并发章节中应用。 Mutex 是什么Mutex 是 golang 规范库的互斥锁,次要用来解决并发场景下共享资源的拜访抵触问题。 Mutex 互斥锁在 sync 包中,它由两个字段 state 和 sema 组成,state 示意以后互斥锁的状态,而 sema 真正用于管制锁状态的信号量,这两个加起来只占 8 个字节空间的构造体就示意了 Go 语言中的互斥锁。 type Mutex struct { state int32 sema uint32}互斥锁的作用,就是同步访问共享资源。互斥锁这个名字来自互斥(mutual exclusion)的概念,互斥锁用于在代码上创立一个临界区,保障同一个工夫只有一个 goroutine 能够执行这个临界区代码。 package mainimport ( "fmt" "runtime" "sync")var ( counter int wg sync.WaitGroup mutex sync.Mutex // 定义代码临界区)func main() { wg.Add(2) go incCounter() go incCounter() wg.Wait() fmt.Println("counter:", counter)}func incCounter() { defer wg.Done() for count := 0; count < 2; count++ { mutex.Lock() // 临界区, 同一时刻只容许一个 goroutine 进入 { value := counter runtime.Gosched() // goroutine退出,返回队列 value++ counter = value } mutex.Unlock() // 开释锁 }}Lock() 和 Unlock() 函数调用定义的临界区里被爱护起来。 应用大括号只是为了让临界区看起来更清晰,并不是必须的。同一时刻只有一个 goroutine 能够进入临界区,直到调用 Unlock() 函数之后,其余 goroutine 能力进入临界区。 ...

June 11, 2022 · 2 min · jiezi

关于go:Go语言-WaitGroup-详解

你必须十分致力,能力看起来毫不费力! 微信搜寻公众号[ 漫漫Coding路 ],一起From Zero To Hero ! 前言在后面的文章中,咱们应用过 WaitGroup 进行工作编排,Go语言中的 WaitGroup 和 Java 中的 CyclicBarrier、CountDownLatch 十分相似。比方咱们有一个主工作在执行,执行到某一点时须要并行执行三个子工作,并且须要等到三个子工作都执行完后,再继续执行主工作。那咱们就须要设置一个检查点,使主工作始终阻塞在这,等三个子工作执行完后再放行。 阐明:本文中的示例,均是基于Go1.17 64位机器小试牛刀咱们先来个简略的例子,看下 WaitGroup 是怎么应用的。示例中应用 Add(5) 示意咱们有 5个 子工作,而后起了 5个 协程去实现工作,主协程应用 Wait() 办法期待 子协程执行结束,输入一共期待的工夫。 func main() { var waitGroup sync.WaitGroup start := time.Now() waitGroup.Add(5) for i := 0; i < 5; i++ { go func() { defer waitGroup.Done() time.Sleep(time.Second) fmt.Println("done") }() } waitGroup.Wait() fmt.Println(time.Now().Sub(start).Seconds())}/*donedonedonedonedone1.000306089*/总览WaitGroup 一共有三个办法: (wg *WaitGroup) Add(delta int)(wg *WaitGroup) Done()(wg *WaitGroup) Wait()Add 办法用于设置 WaitGroup 的计数值,能够了解为子工作的数量Done 办法用于将 WaitGroup 的计数值减一,能够了解为实现一个子工作Wait 办法用于阻塞调用者,直到 WaitGroup 的计数值为0,即所有子工作都实现失常来说,咱们应用的时候,须要先确定子工作的数量,而后调用 Add() 办法传入相应的数量,在每个子工作的协程中,调用 Done(),须要期待的协程调用 Wait() 办法,状态流转如下图: ...

June 10, 2022 · 3 min · jiezi

关于go:Go题库2对已经关闭的channel进行读写操作会发生什么

面试企业: 深服气 题目解析: GOLANG ROADMAP社区 答案1:(溪尾)1 . 读已敞开的channel 读曾经敞开的channel无影响。 如果在敞开前,通道外部有元素,会正确读到元素的值;如果敞开前通道无元素,则会读取到通道内元素类型对应的零值。 若遍历通道,如果通道未敞开,读完元素后,会报死锁的谬误。 fatal error: all goroutines are asleep - deadlock!2 . 写已敞开的通道 会引发panic: send on closed channel 3 . 敞开已敞开的通道 会引发panic: close of closed channel 总结:对于一个已初始化,但并未敞开的通道来说,收发操作肯定不会引发 panic。然而通道一旦敞开,再对它进行发送操作,就会引发 panic。如果咱们试图敞开一个曾经敞开了的通道,也会引发 panic。 //1. 读一个曾经敞开的通道func main() { channel := make(chan int, 10) channel <- 2 close(channel) x := <-channel fmt.Println(x)}/*[Output]: 不会报错,输入2*/// 遍历读敞开通道func main() { channel := make(chan int, 10) channel <- 2 channel <- 3 close(channel) //若不敞开通道,则会报死锁谬误 for num := range channel { fmt.Println(num) }}/*[Output]: 不会报错,输入2 3*///2. 写一个曾经敞开的通道func main() { channel := make(chan int, 10) close(channel) channel <- 1}/*[Output]: panic: send on closed channel*///3. 敞开一个曾经敞开的管道func main() { channel := make(chan int, 10) close(channel) close(channel)}/*[Output]: panic: close of closed channel */

June 10, 2022 · 1 min · jiezi

关于go:极客时间Go进阶训练营全新升级版第4期完结无密

download:极客工夫-Go进阶训练营全新升级版|第4期完结无密OAuth2学习中的一些高频问题的QA对于OAuth2相信很多初学者都有一些疑难,胖哥将这些疑难一一收集了起来做成了QA,或者能帮助学习者。OAuth2相干的QA Q:OAuth2 的一些罕用场景? A: OAuth2次要用于API授权,是跨API服务之间授权的解决打算。它实用于单点登录(SSO)、微服务之间的授权鉴权、API开放平台等场景。 Q: 什么是OAuth2客户端? A: 在OAuth2授权服务器上注册为客户端,并获得专属client_id标识的才是OAuth2客户端。安卓利用、IOS利用、Web前端等客户端利用也要遵循这个原则,它们本身注册到OAuth2授权服务器才能成为OAuth2客户端,否则就不是OAuth2客户端,必须是它们本身,而不是撑持它们的后端服务。 Q:OAuth2 客户端为什么分为public和confidential两种类型,别离是什么场景? A:rfc6749#section-2.1 根据OAuth2客户端自身是否有能力保护客户端凭据(client credentials)的私密性,是否能平安地通过授权服务器对客户端的资质进行认证将OAuth2客户端分为机密客户端和公共客户端。大部分的后端数据服务都应该被注册为机密客户端;无奈保障自身凭据安全的都应该被注册为公共客户端,公共客户端是没有client_sercet的,间接注册到OAuth2授权服务器的执行客户端,不通过后端利用进行拜访令牌中继的都是公共客户端,例如在一些特定场景下需要直连授权服务器的Web利用、移动利用。 Q:OAuth2 的access_token和refresh_token应该间接返回给前端吗? A:能不能返回给前端取决于这个前端是不是间接在授权服务器的OAuth2客户端,如果不是,就不能持有access_token和refresh_token,access_token和refresh_token的签发目标只能是OAuth2客户端。如果暴露面放开,则很容易被盗用。 Q: 非OAuth2客户端的客户端利用既然不能间接持有access_token和refresh_token的话,应该如何获取授权状态? A:当授权胜利后,令牌和用户客户端侧可能借助于session或者cookie进行一个映射,当然也可能考虑计算出一个不通明令牌( Opaque Token )映射,具体根据业务考量。 Q:OAuth2中的scope是什么? A:OAuth2是一个授权框架,授权天然要划定一个范畴(scope),以保障OAuth2客户端在既定的范畴内行事而不越界。它起到的作用和RBAC中的role其实类似,都是用来限度资源的拜访权限的。 role针对的是资源具备者(Resource Owner),而scope针对的是OAuth2客户端。 当然openid是一个例外,这个是OIDC 1.0的标识,算一个关键字。 Q:OAuth2 中的登录页面和授权确认页面能不能用前后端分离的形式? A:很多开发者不心愿点击授权的时候被302重定向到授权服务器提供的登录页面,然而你得明确一个情理, OAuth2客户端和授权服务器之间并不是一个残缺信赖的关系。外卖小哥给你送外卖,你必定心愿发放给他的是一个临时门禁通行码,而不是一个罕用通行码。另外ajax无奈平安地处理OAuth2授权流程中的302重定向问题,这也是一个技术问题。 Q:OAuth2 客户端是否做用户认证? A:OAuth2本身并没有定义用户如何向OAuth2客户端认证身份,这里要和授权服务器上的用户认证区别开来。OAuth2客户端在实现授权时可能拿到授权凭据,然而并不能间接拿到用户信息,如果授权服务器提供了获取用户信息的资源接口,OAuth2客户端可能通过该接口尝试获取用户信息用来表明用户的身份,这取决于用户是否授权了OAuth2客户端这样做。OIDC 1.0补充定义了OAuth2客户端对用户进行认证的细节流程。 Q:OAuth2客户端认证是什么? A:confidential类型的OAuth2客户端诚然在OAuth2授权服务器注册,它们要根据一些策略(Client Authentication Method)来向授权服务器证实自己是非法的客户端。这样它们才能调用一些OAuth2规定的端点,比如/oauth2/token令牌端点、/oauth2/revoke令牌撤销端点等等。对于OAuth2客户端认证的细节可能参考胖哥专栏中的OAuth2客户端认证过滤器详解一文。 Q:OAuth2明码模式为什么被废除了? A:准确地说目前明码模式在OAuth2.1中被移除了,包含OAuth0、okta等出名三方授权服务机构都对明码模式进行了移除处理。明码模式诞生的时候,像React、Vue这种单页利用还没有衰亡,以至连框架都还没有呢。它更像一种为理解决遗留问题而采纳的过渡打算。在传统利用中,用户习惯了把明码间接交给客户端换取资源拜访权限,而不是跳来跳去去拉授权、确认授权。OAuth2诞生之时为了让用户从传统思维中慢慢转变过去就设计了这种模式。 它冲破了托付授权的模式,升高了OAuth2的安全性。更多的细节请参考我往期的相干文章。 Q:OAuth2中的资源服务器怎么讲? A:只需蕴含了需要OAuth2客户端携带access_token拜访的资源接口的服务器都可能认为是资源服务器,包含OAuth2客户端、OAuth2授权服务器都可能根据业务和架构承担资源服务器的功能。从用户(资源所有者)角度来说,存放用户可能授权的资源接口的服务器都可能是资源服务器。资源服务器可能对拜访令牌access_token进行解码、校验,并必定本次请求是否合规。 Q:微服务是否可能不使用OAuth2? 当然可能,OAuth2只不过是目前微服务访问控制的解决打算之一,并不是唯一选项。总结这就是最近胖哥被问的比较多的一些问题,相信能够帮助各位。OAuth2的货色并不简略,通过近三年内断断续续的学习,胖哥才完残缺全理解这个货色,所以各位学习者不要心急,学的枯燥的时候先晾一时间,学这个最重要的是理解它的概念和流程,这远比各种框架重要,OAuth2本身和语言是无关的。

June 10, 2022 · 1 min · jiezi

关于go:深入Go底层原理重写Redis中间件实战网盘分享

download:深刻Go底层原理,重写Redis中间件实战【网盘分享】OAuth2学习中的一些高频问题的QA对于OAuth2相信很多初学者都有一些疑难,胖哥将这些疑难一一收集了起来做成了QA,或者能帮助学习者。OAuth2相干的QA Q:OAuth2 的一些罕用场景? A: OAuth2次要用于API授权,是跨API服务之间授权的解决打算。它实用于单点登录(SSO)、微服务之间的授权鉴权、API开放平台等场景。 Q: 什么是OAuth2客户端? A: 在OAuth2授权服务器上注册为客户端,并获得专属client_id标识的才是OAuth2客户端。安卓利用、IOS利用、Web前端等客户端利用也要遵循这个原则,它们本身注册到OAuth2授权服务器才能成为OAuth2客户端,否则就不是OAuth2客户端,必须是它们本身,而不是撑持它们的后端服务。 Q:OAuth2 客户端为什么分为public和confidential两种类型,别离是什么场景? A:rfc6749#section-2.1 根据OAuth2客户端自身是否有能力保护客户端凭据(client credentials)的私密性,是否能平安地通过授权服务器对客户端的资质进行认证将OAuth2客户端分为机密客户端和公共客户端。大部分的后端数据服务都应该被注册为机密客户端;无奈保障自身凭据安全的都应该被注册为公共客户端,公共客户端是没有client_sercet的,间接注册到OAuth2授权服务器的执行客户端,不通过后端利用进行拜访令牌中继的都是公共客户端,例如在一些特定场景下需要直连授权服务器的Web利用、移动利用。 Q:OAuth2 的access_token和refresh_token应该间接返回给前端吗? A:能不能返回给前端取决于这个前端是不是间接在授权服务器的OAuth2客户端,如果不是,就不能持有access_token和refresh_token,access_token和refresh_token的签发目标只能是OAuth2客户端。如果暴露面放开,则很容易被盗用。 Q: 非OAuth2客户端的客户端利用既然不能间接持有access_token和refresh_token的话,应该如何获取授权状态? A:当授权胜利后,令牌和用户客户端侧可能借助于session或者cookie进行一个映射,当然也可能考虑计算出一个不通明令牌( Opaque Token )映射,具体根据业务考量。 Q:OAuth2中的scope是什么? A:OAuth2是一个授权框架,授权天然要划定一个范畴(scope),以保障OAuth2客户端在既定的范畴内行事而不越界。它起到的作用和RBAC中的role其实类似,都是用来限度资源的拜访权限的。 role针对的是资源具备者(Resource Owner),而scope针对的是OAuth2客户端。 当然openid是一个例外,这个是OIDC 1.0的标识,算一个关键字。 Q:OAuth2 中的登录页面和授权确认页面能不能用前后端分离的形式? A:很多开发者不心愿点击授权的时候被302重定向到授权服务器提供的登录页面,然而你得明确一个情理, OAuth2客户端和授权服务器之间并不是一个残缺信赖的关系。外卖小哥给你送外卖,你必定心愿发放给他的是一个临时门禁通行码,而不是一个罕用通行码。另外ajax无奈平安地处理OAuth2授权流程中的302重定向问题,这也是一个技术问题。 Q:OAuth2 客户端是否做用户认证? A:OAuth2本身并没有定义用户如何向OAuth2客户端认证身份,这里要和授权服务器上的用户认证区别开来。OAuth2客户端在实现授权时可能拿到授权凭据,然而并不能间接拿到用户信息,如果授权服务器提供了获取用户信息的资源接口,OAuth2客户端可能通过该接口尝试获取用户信息用来表明用户的身份,这取决于用户是否授权了OAuth2客户端这样做。OIDC 1.0补充定义了OAuth2客户端对用户进行认证的细节流程。 Q:OAuth2客户端认证是什么? A:confidential类型的OAuth2客户端诚然在OAuth2授权服务器注册,它们要根据一些策略(Client Authentication Method)来向授权服务器证实自己是非法的客户端。这样它们才能调用一些OAuth2规定的端点,比如/oauth2/token令牌端点、/oauth2/revoke令牌撤销端点等等。对于OAuth2客户端认证的细节可能参考胖哥专栏中的OAuth2客户端认证过滤器详解一文。 Q:OAuth2明码模式为什么被废除了? A:准确地说目前明码模式在OAuth2.1中被移除了,包含OAuth0、okta等出名三方授权服务机构都对明码模式进行了移除处理。明码模式诞生的时候,像React、Vue这种单页利用还没有衰亡,以至连框架都还没有呢。它更像一种为理解决遗留问题而采纳的过渡打算。在传统利用中,用户习惯了把明码间接交给客户端换取资源拜访权限,而不是跳来跳去去拉授权、确认授权。OAuth2诞生之时为了让用户从传统思维中慢慢转变过去就设计了这种模式。 它冲破了托付授权的模式,升高了OAuth2的安全性。更多的细节请参考我往期的相干文章。 Q:OAuth2中的资源服务器怎么讲? A:只需蕴含了需要OAuth2客户端携带access_token拜访的资源接口的服务器都可能认为是资源服务器,包含OAuth2客户端、OAuth2授权服务器都可能根据业务和架构承担资源服务器的功能。从用户(资源所有者)角度来说,存放用户可能授权的资源接口的服务器都可能是资源服务器。资源服务器可能对拜访令牌access_token进行解码、校验,并必定本次请求是否合规。 Q:微服务是否可能不使用OAuth2? 当然可能,OAuth2只不过是目前微服务访问控制的解决打算之一,并不是唯一选项。总结这就是最近胖哥被问的比较多的一些问题,相信能够帮助各位。OAuth2的货色并不简略,通过近三年内断断续续的学习,胖哥才完残缺全理解这个货色,所以各位学习者不要心急,学的枯燥的时候先晾一时间,学这个最重要的是理解它的概念和流程,这远比各种框架重要,OAuth2本身和语言是无关的。

June 10, 2022 · 1 min · jiezi

关于go:基于Gin框架的web后端开发九-Gin框架的路由和路由组

tips: 如果用浏览器测试的时候呈现之前几篇博客编码的后果,能够删除一下cookie,或者关上无痕模式。个别的路由能够这么写: package mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { r := gin.Default() //拜访/index的GET申请,走这条路 r.GET("/index", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "mathod": "GET", }) }) r.POST("/index", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "method": "POST", }) }) r.DELETE("/index", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "method": "DELETE", }) }) r.PUT("/index", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "method": "PUT", }) }) //能够解决所有申请 r.Any("/index", func(c *gin.Context) { switch c.Request.Method { case http.MethodGet: c.JSON(http.StatusOK, gin.H{"method": "GET"}) case http.MethodPost: c.JSON(http.StatusOK, gin.H{"method": "POST"}) } c.JSON(http.StatusOK, gin.H{ "method": "ANY", }) }) r.NoRoute(func(c *gin.Context) { c.JSON(http.StatusNotFound, gin.H{"msg": "there is no such site, you may interested in https://segmentfault.com/u/liberhome"}) }) r.Run(":9090")}路由组能够这么写: ...

June 10, 2022 · 1 min · jiezi

关于go:微服务效率工具-goctl-深度解析上

前言本文依据 安前松 的视频分享整顿而来,视频回放地址如下: https://www.bilibili.com/video/BV1Hr4y1x7Ne goctl 的由来1. goctl 的诞生goctl 的最早性能是为了解决 GRPC 内网调试问题,大概是在 2019 年,在咱们的生产环境中,rpc 是内网隔离的,不可通过外网拜访,为了疾速去 mock 一些线上 RPC client 的申请,就简略的实现了第一版本的代码生成,次要目标是去拜访 RPC Server 做一些调试。 2. 为什么须要 goctl?升高沟通老本 沟通,是团队合作进行信息替换的一种模式,沟通的形式有很多种,会议沟通、文档沟通、聊天交换,置信不论是哪种形式,沟通都是团队中最难的一个环节,会议沟通须要占用大量工夫,动则半小时起步,文档沟通同样,也会占据大量工夫去构思和编写大篇幅的文档,最初可能还没表白出预期指标,线上聊天,须要单方都在线上能力进行信息替换,当然咱们这里沟通替换的信息更多是指开发中的一些内容,如接口信息、部署信息等。 升高团队耦合 有了沟通,那么团队之间的合作的耦合是防止不了的,例如:在前后端开发中,最大的耦合是接口的耦合,前端实现了规定 UI 绘制后,须要期待后端的接口部署到对应环境能力实现性能的调试,在此期间,前端的团队资源就会大大节约,由此还会导致我的项目的延期等问题。 进步开发效率 除了沟通老本和团队耦合以外,每个团队在进行我的项目开发时也有很多工夫是在做反复的工作,例如:咱们在开发一个新的性能时,须要去定义接口,编写接口文档,编码筹备工作,业务开发,model 文件,编写 Dockerfile 文件,编写 k8s yaml 文件,在这些下面咱们能够在每个环节上都有晋升的空间,让用户将真正的工夫集中在业务开发上。 升高错误率 在之前的开发实际中,常常会呈现 grpc server 实现不齐全的问题,grpc server 实现类常常会呈现编译不过的状况;除此之外,数据库查问层代码开发,sql 语句的编写多参,少参,参数错位,在编译过程中很难发现,个别可能到 QA 环节能力发现,更甚者会导致线上问题。 3. 怎么了解开发标准?开发标准,包含编码标准,工程标准,提交标准,代码审核标准,部署标准等等,团队内如果将开发标准对立起来,其收益可想而知,举个例子:如果你所在的公司没有对立开发标准,这时须要你去做一些跨部门合作或者反对,面目一新的开发环境让你望而生畏,还没开始就有了冲突的念头,这岂不是违反了反对的初衷。 4. 怎么了解工程效率?工程效率,要了解工程效率,能够先看看『效率』的定义: 效率是指单位工夫内实现的工作量效率的指标是快,但快并不是效率,换句话说就是在单位工夫内实现的高质量工作,这才是咱们要谋求的指标,那么工程效率就是为了『缩短从开发到线上的间隔』 二 、goctl 的装置及性能介绍1. 介绍goctl 定义 goctl 定义,精确说是定位吧,能够了解为一个代码生成脚手架,其核心思想是通过对 DSL 进行语法解析、语义剖析到数据驱动实现实现代码生成性能,旨在帮忙开发者从工程效率角度进步开发者的开发效率。解决的问题 进步开发效率:goctl 的代码生成笼罩了Go、Java、Android、iOS、TS 等多门语言,通过 goctl 能够一键生成各端代码,让开发者将精力集中在业务开发上。升高沟通老本:应用 zero-api 代替 API 文档,各端以 zero-api 为核心生成各端代码,无需再通过会议、API 文档来进行接口信息传递。升高耦合度: 在之前的开发实际中,没有 goctl 之前,咱们采纳的是传统的 API 文档来作为接口信息传递的载体,而后通过会议来进行二次 review,咱们都晓得,前端开发工作中,除了 UI 的绘制外就是 UI 联调,如果比拟踊跃的前端开发者,可能会本人利用一些脚手架或者本人 mock 一些数据去进行 UI 联调,反之,也有原地期待后端部署后再联调的 case,起因是本人写 mock 数据太耗时,也不想去动,如果前端的开发进度在后端之前,那么在后端部署到环境中这期间,前端只能宁静的原地期待,这不仅是一种资源的节约,也会造成我的项目进度的延期,有了 zero-api ,前端能够利用 goctl 去生成适量的 mock 数据来进行 UI联调。倒退历史 ...

June 10, 2022 · 9 min · jiezi

关于go:Go题库1Golang里的数组和切片有了解过吗

题目起源: 深服气、知乎、跟谁学 题目解析: GOALNG ROADMAP社区 答案 1:(溪尾)数组长度是固定的,而切片是可变长的。能够把切片看作是对底层数组的封装,每个切片的底层数据结构中,肯定会蕴含一个数组。数组能够被称为切片的底层数组,切片也能够被看作对数组某一间断片段的援用。因而,Go 中切片属于援用类型,而数组属于值类型,通过内建函数 len,能够获得数组和切片的长度。通过内建函数 cap,能够失去数组和切片的容量。然而数组的长度和容量是相等的,并且都不可变,而且切片容量是有变化规律的。 答案 2:(行飞子)数组和切片的关系: 切片一旦初始化, 切片始终与保留其元素的根底数组相关联。因而,切片会和与其领有同一根底数组的其余切片共享存储 ; 相比之下,不同的数组总是代表不同的存储。 数组和切片的区别 切片的长度可能在执行期间发生变化 ,而数组的长度不能变动,能够把切片看成一个长度可变的数组。数组作为函数参数是进行值传递的,函数外部扭转传入的数组元素值不会影响函数内部数组的元素值; 切片作为函数的参数是进行的指针传递,函数外部扭转切片的值会影响函数内部的切片元素值。数组能够比拟,切片不能比拟(对底层数组的援用)。答案 3:(栾龙生)1. Go 切片和 Go 数组 Go 切片,又称动静数组,它理论是基于数组类型做的一层封装。 Go 数组 数组是内置(build-in)类型,是一组同类型数据的汇合,它是值类型,通过从 0 开始的下标索引拜访元素值。在初始化后长度是固定的,无奈批改其长度。当作为办法的参数传入时将复制一份数组而不是援用同一指针。数组的长度也是其类型的一部分,通过内置函数 len(array)获取其长度。 Go 数组与像 C/C++等语言中数组略有不同,如下 Go 中的数组是值类型,换句话说,如果你将一个数组赋值给另外一个数组,那么,实际上就是将整个数组拷贝一份。因而,在 Go 中如果将数组作为函数的参数传递的话,那效率就必定没有传递指针高了。数组的长度也是类型的一部分,这就阐明[10]int和[20]int不是同一种数据类型。Go 切片 Go 语言中数组的长度是固定的,且不同长度的数组是不同类型,这样的限度带来不少局限性。 而切片则不同,切片(slice)是一个领有雷同类型元素的可变长序列,能够不便地进行扩容和传递,理论应用时比数组更加灵便,这也正是切片存在的意义。 切片是援用类型,因而在当传递切片时将援用同一指针,批改值将会影响其余的对象。 2. 切片底层 当初就来看一下 Go 语言切片的底层是什么样子吧! Go 切片(slice)的实现能够在源码包src/runtime/slice.go中找到。在源码中,slice 的数据结构定义如下。 type slice struct { array unsafe.Pointer //指向底层数组的指针 len int //切片长度 cap int //切片容量}能够看到,组成 Go 切片的三元组别离为指向底层数组的指针,切片长度和切片容量。 指向底层数组的指针 ...

June 9, 2022 · 5 min · jiezi

关于go:变量声明与初始化

变量申明与初始化// [1]:在申明变量时指定类型(显示指定变量的类型)// 可用于任何中央// 与var name string = "hello world"雷同var name stringname = string/* * 类型 变量值 * string hello world */var name string = "hello world"// [2]:类型推断// 可用于任何中央// 不显示指定变量的类型,使它能够被赋予任何类型的值。变量的类型能够在其初始化时,由其余程序动静的地确定var name = getString()var name = "hello world"/* * 类型 变量值 * string hello world *//* [3]:短变量申明 类型推断 * 只能在函数体外部应用 * 在函数外部,能够应用 := 短赋值语句代替隐式类型的 var 申明。 在函数之 * 外,每个语句都以关键字(var、func 等)结尾,因而 := 结构不可用。 * 函数外的申明必须应用 var、func、const 等关键字 * * 当短变量申明在函数体外应用时会报 syntax error: non-declaration statement outside function body (函数体之外的非申明语句) 谬误 * * package main * * import "fmt" * * name := "hello world" * func main() { * fmt.Printf("%T %v", name, name) * } * */name := "hello world"/* * 类型 变量值 * string hello world */类型推断带来的益处:类型推断能够明显提高程序的灵活性,使代码重构变得更加容易,同时又不会带来额定的累赘,更不会损失程序的运行效率。 ...

June 9, 2022 · 1 min · jiezi

关于go:golua实现redis事务

对于事物在这就不多做概念性的介绍,本人能够去搜寻。 对于Redis的事务简略阐明一下事务作为一个整体被执行,执行期间不会被其它客户端申请打断事务中的多个命令打包执行,当做一个整体执行,不会被打断事务在执行的中途遇到谬误,不会回滚,而是继续执行后续命令事务中能够交叉查问,依据后果执行不同操作咱们能够用lua脚本来实现事务处理 在go中应用lua脚本拜访redis时以下几点要留神脚本返回nil时,Go中失去的是err = redis.Nil(与Get找不到值雷同)脚本返回false时,Go中失去的是nil,脚本返回true时,Go中失去的是int64类型的1脚本返回number类型时,Go中失去的是int64类型传入脚本的KEYS/ARGV中的值一律为string类型,要转换为数字类型应用to_number脚本返回{"ok": ...}时,Go中失去的是redis的status类型(true/false)脚本返回{"err": ...}时,Go中失去的是err值,也能够通过return redis.error_reply("My Error")达成测试代码 cache.go package rdsimport ( "fmt" "time" rds "github.com/gomodule/redigo/redis")type Cache struct { pool *rds.Pool}func InitCache(rdsAddress string, pwd string) *Cache { p := &rds.Pool{ MaxIdle: 5, MaxActive: 30, IdleTimeout: 240 * time.Second, Dial: func() (rds.Conn, error) { return rds.Dial("tcp", rdsAddress, rds.DialPassword(pwd)) }, } return &Cache{ pool: p, }}func (cache *Cache) GetCon() rds.Conn { return cache.pool.Get()}func (cache *Cache) Close() { cache.pool.Close()}func (cache *Cache) GetAndAdd(roomId string, usrId string) (usrAccPoint string, err error) { script := rds.NewScript(2, ` local key1 = "usr:accpoint:" .. KEYS[2] local accPoint = redis.call("get", key1) key1 = "room:accpoints:" .. KEYS[1] redis.call("sadd", key1, accPoint) key1 = "room:accpoint:" .. KEYS[1] key1 = key1 .. ":" .. accPoint redis.call("sadd", key1, KEYS[2]) return accPoint `) con := cache.GetCon() defer con.Close() usrAccPoint, err = rds.String(script.Do(con, roomId, usrId)) if err != nil { fmt.Println(err.Error()) return } return}func (cache *Cache) GetAndRem(roomId string, usrId string) (cnt int64, err error) { script := rds.NewScript(2, ` local key1 = "usr:accpoint:" .. KEYS[2] local accPoint = redis.call("get", key1) key1 = "room:accpoint:" .. KEYS[1] .. ":" .. accPoint redis.call("srem", key1, KEYS[2]) local cnt = redis.call("scard", key1) if (cnt == 0) then key1 = "room:accpoints:" .. KEYS[1] redis.call("srem", key1, accPoint) end return cnt `) con := cache.GetCon() defer con.Close() accPointCnt, err := rds.Int64(script.Do(con, roomId, usrId)) if err != nil { fmt.Println(err.Error()) return -1, err } fmt.Printf("Room:%s, remains %d accPoint\n", roomId, accPointCnt) return accPointCnt, nil}main.go ...

June 8, 2022 · 2 min · jiezi

关于go:默克尔树Merkle-Tree

Merkle 树结构默克尔树(又叫哈希树)是一种典型的二叉树构造,有一个根节点、一组两头节点和一组叶节点组成。默克尔树最早由 Merkle Ralf 在 1980 年提出,曾宽泛用于文件系统和P2P零碎中。比方 git、区块链、IPFS 等。 次要特点: 最上面的叶节点蕴含存储数据或其哈希值;非叶子节点(包含两头节点和根节点)都是它的两个孩子节点内容的哈希值默克尔树能够推广到多叉树的情景,此时非叶子节点的内容为它所有的孩子节点的内容的哈希值。 默克尔树逐层记录哈希值的特点,让它具备了一些独特的性质。例如,底层数据的任何变动,都会传递到其父节点,一层层沿着门路始终到树根。这意味着根的值实际上代表了对底层所有数据的“数字摘要”。 利用场景目前,默克尔树的典型利用场景包含以下几种。 证实某个汇合中存在或不存在某个元素通过构建汇合的默克尔树,并提供该元素各级兄弟节点中的 hash 值,能够不裸露汇合残缺内容而证实某元素存在。 另外,对于能够进行排序的汇合,能够将不存在元素的地位用空值代替,以此构建稠密默克尔树(Sparse Merkle Tree)。该构造能够证实某个汇合中不包含指定元素。 疾速比拟大量数据对每组数据排序后构建默克尔树结构。当两个默克尔树根雷同时,则意味着所代表的两组数据必然雷同。否则,必然不同。 因为 hash 计算的过程能够非常疾速,预处理能够在短时间内实现。利用默克尔树结构能带来微小的比拟性能劣势 疾速定位批改以下图为例,基于数据 D0……D3 结构默克尔树,如果 D1 中数据被批改,会影响到 N1,N4 和 Root。因而,一旦发现某个节点如 Root 的数值发生变化,沿着 Root --> N4 --> N1,最多通过 O(lgN) 工夫即可疾速定位到理论产生扭转的数据块 D1。 零常识证实它指的是证实者可能在不向验证者提供任何有用的信息的状况下(没有泄露信息),使验证者置信某个论断是正确的。 有个很简略的例子:A 要向 B 证实本人领有某个房间的钥匙,假如该房间只能用钥匙关上锁,而其余任何办法都打不开。这时有两个办法: A 把钥匙出示给 B, B 用这把钥匙关上该房间的锁,从而证实 A 领有该房间的正确钥匙。B 确定该房间内有某一物体,A 用本人领有的钥匙关上该房间的门,而后把物体拿进去出示给 B,从而证实本人的确领有该房间的钥匙。前面的第二种办法属于零常识证实。它的益处在于,整个证实的过程中,B 始终不能看到钥匙的样子,从而防止了钥匙的透露。 在默克尔树中,咱们仍旧以上图为例,如何向别人证实我领有 D0 这个数据,而不必裸露更多零碎的信息呢?模拟下面的例子,验证者随机提供数据 D1、D2 和 D3,证实者结构如图的默克尔树,并颁布 N1 、N5 和 Root。验证者自行计算 Root 值,看是否统一,从而测验 D0 是否存在,因为如果存在,N0 肯定雷同,那么 N4(N0-N1) 也肯定雷同、Root(N4-N5)也肯定雷同。整个过程中验证着没有失去任何除了 D0 外的敏感信息(其余的 D)。 ...

June 8, 2022 · 1 min · jiezi

关于go:使用Go构建一款静态分析工具

介 绍Owl是一款开源我的项目依赖剖析工具,能够疾速在指定的我的项目目录下查找合乎某些特色的源代码文件或者依赖文件。为何开发了这款工具?例如很多时候咱们我的项目太大,我的项目文件夹下有很多依赖文件,如一个Java我的项目引入了log4j这个jar依赖,在我的项目中某文件存在循环依赖问题。当某个依赖包呈现了破绽时,本工具能疾速扫描我的项目目录下存在的可疑依赖文件,并且给出依赖文件所在的地址,帮忙开发者能疾速进行定位到可疑文件。 主仓地址:https://github.com/Tencent/CodeAnalysis/tools/owl,代码也不多就1000行左右,感兴趣读者能够去看看,实现原理上面也介绍了,感觉不错的话你能够给按一个。 OwlA dependency module feature scanning detection tool for static analysis. 原 理目前版本的性能比较简单,工作原理很简略,工具会对特定目录进行扫描通过内置的特色码算法匹配到特定文件,而后收集与其特色码匹配的文件地址,而后展现进去,也能够重定向到一个固定json文件中保留。 Owl相似于杀毒软件一样,和杀毒软件的工作原理差不多,Owl会依据依赖文件的特色码来扫描整个我的项目,和杀毒病毒库工作原理相似。当然如果严格依照杀毒软件那种规范做的话,可能波及一些汇编相干的,目前owl性能实现还没有那么简单,前面会版本会退出codeql代码剖析引擎,通过codeql的数据库来做动态剖析性能加强。 疾速开始如何应用owl?你能够克隆仓库而后通过如下命令: git clone github.com:Tencent/CodeAnalysis.git而后将目录切换到tools\owl下,如下: cd CodeAnalysis/tools/owl在仓库外部有一个Makefile文件能够疾速帮忙你构建相应平台的二进制文件,例如: $: make helpmake darwin | Compile executable binary for MacOS platformmake linux | Compile executable binary for Linux platformmake windows | Compile executable binary for Windows platformmake clean | Clean up executable binaryOwl起因也是为CodeAnalysis所编写的特色检测工具,所以你也能够在:https://github.com/Tencent/CodeAnalysis 这个我的项目上面的tools目录找到曾经编译好的二进制可执行文件,下载对应平台的二进制文件即可。 如何应用程序构建实现会失去一个二进制文件,程序名称为owl,如下为owl执行成果,一些子命令参数都曾经列出: $: ./owl _____ _ _ __ ( _ )( \/\/ )( ) )(_)( ) ( )(__ (_____)(__/\__)(____) v0.1.3 A dependency module feature scanning detection tool for static analysis.Usage: owl [command]Available Commands: completion Generate the autocompletion script for the specified shell help Help about any command hex File hex encoding md5 Collection file md5 run Execute the scanner version Version informationFlags: -h, --help help for owlUse "owl [command] --help" for more information about a command.如果不晓得子命令如何应用,能够在对应的子命令前面参入--help参数,即可失去帮忙信息: ...

June 8, 2022 · 1 min · jiezi

关于go:Go-Context原理详解

这篇文章是答复交换时一个老哥的问题,跟go的context相干内容,上一篇(https://www.cnblogs.com/dojo-...)讲了一些基础知识,这一篇持续在并发解决上进行钻研。次要是Go Context的应用、原理。因为工夫和精力有限,所以文章中大量援用相干材料中的内容以及图片,再此致敬。 Go ContextReact中Context次要用来跨组件传递一些数据,Go中Context其中一个作用也跟传递数据无关,不过是在goroutine中互相传递数据;Context的另一个作用在于能够便捷敞开被创立进去的goroutine。 在理论中当服务器端收到一个申请时,很可能须要发送几个申请去申请其余服务的数据,因为Go 语法上的同步阻塞写法,咱们个别会创立几个goroutine并发去做一些事件;那么这时候很可能几个goroutine之间须要共享数据,还有当request被勾销时,创立的几个goroutine也应该被勾销掉。那么这就是Go Context的用武之地。 对于协程泄露: 个别main函数是主协程,主协程执行结束后子协程也会被销毁;然而对于服务来说,主协程不会执行结束就退出。 所以如果每个申请都本人创立协程,而协程有没有受到结束信息完结信息,可能处于阻塞状态,这种状况下才会产生协程泄露 context包中外围是Context接口: type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{}}Deadline 办法返回以后Context被勾销的工夫,也就是实现工作的截止工夫(deadline);Done办法须要返回一个channel,这个Channel会在当前工作实现或者上下文被勾销之后敞开,能够在子goroutine中利用select进行监控,来回收子goroutine;屡次调用Done办法会返回同一个Channel;// Done is provided for use in select statements:// // Stream generates values with DoSomething and sends them to out// // until DoSomething returns an error or ctx.Done is closed.// func Stream(ctx context.Context, out chan<- Value) error {// for {// v, err := DoSomething(ctx)// if err != nil {// return err// }// select {// case <-ctx.Done():// return ctx.Err()// case out <- v:// }// }// }// See https://blog.golang.org/pipelines for more examples of how to use// a Done channel for cancellation.Err办法会返回以后Context完结的起因,它只会在Done返回的Channel被敞开时才会返回空值:如果以后Context被勾销就会返回Canceled谬误;如果以后Context超时就会返回DeadlineExceeded谬误;Value 办法会从Context中返回键对应的值,对于同一个上下文来说,屡次调用Value并传入雷同的Key会返回雷同的后果,该办法仅用于传递跨API和过程间跟申请域的数据。// // Package user defines a User type that's stored in Contexts.// package user// import "context"// // User is the type of value stored in the Contexts.// type User struct {...}//// // key is an unexported type for keys defined in this package.// // This prevents collisions with keys defined in other packages.// type key int// // userKey is the key for user.User values in Contexts. It is// // unexported; clients use user.NewContext and user.FromContext// // instead of using this key directly.// var userKey key// // NewContext returns a new Context that carries value u.// func NewContext(ctx context.Context, u *User) context.Context {// return context.WithValue(ctx, userKey, u)// }// // FromContext returns the User value stored in ctx, if any.// func FromContext(ctx context.Context) (*User, bool) {// u, ok := ctx.Value(userKey).(*User)// return u, ok// }ctx.Value(userKey).(*User)这里是Go语言中的类型断言(http://c.biancheng.net/view/4...) ...

June 8, 2022 · 7 min · jiezi