在使用Golang做后端开发的工程中,我们通常需要声明一些一些配置类或服务单例等在业务逻辑层面较为底层的实例。为了节省内存或是冷启动开销,我们通常采用lazy-load懒加载的方式去初始化这些实例。初始化单例这个行为是一个非常经典的并发处理的案例,比如在java当中,我们可能用到建立双重锁+volatile的方式保证初始化逻辑只被访问一次,并且所有线程最终都可以读取到初始化完成的实例产物。这段经典的代码可以按如下的方式编写:
【从零单排Golang】第十四话:使用rate和ratelimit实现限流限速
在研发中,我们经常会面对到处理并发逻辑的场景,尤其是有时候在与第三方平台对接的场景下,会遇到请求限流限QPS的要求。对于限流或者限速,我们通常会用两种算法来满足需要:
- 令牌桶算法:在特定容量的桶里面装令牌,当令牌数量小于桶的容量时,会持续以我们预期的限流速率生产令牌;不管桶里面是不是空的,业务都得等到拿到令牌,才能继续执行业务逻辑
- 漏桶算法:业务先统一进入桶里,桶满了之后,会以我们预期的限流速率,一个个把在等待的业务漏出去,然后各个业务才开始执行业务逻辑
这两种算法,虽然实际QPS数值可能都会有波动,但都能把速率限制在一个合理的水位。在Golang里面,这两种算法都有现成的实现可以直接用。咱们今天,就来看看这块的例子。
【从零单排Golang】第十三话:使用WaitGroup等待多路并行的异步任务
在后端开发当中,经常会遇到这样的场景:请求给了批量的输入,对于每一个输入,我们都要给外部发请求等待返回,然后才能继续其它自己的业务逻辑。在这样的case下,如果每一个输入串行处理的话,那么很大一部分时间都会损耗在给外部发请求这个环节,因此我们会希望把这些请求放到各个goroutine里异步执行,等待批量执行完成之后再继续后面的逻辑。这个时候,我们就可以用到这个东西:sync.WaitGroup
WaitGroup提供了增减计数以及阻塞等待计数归零的线程安全接口。当主goroutine增加计数并等待的时候,子goroutine的逻辑中若引用了一个WaitGroup实例的话,也可以在结束(defer)的时候去减少计数,这样当主goroutine自旋等待计数归零时,等待的逻辑就返回了,就继续后面的内容。整体上,就达到了等待多路并行的异步任务这一效果。
一个典型的代码案例如下:
【测试人生】批量刷数的发布流程与风险点
在项目日常开发过程中,批量刷数也是有一定频率的变更场景之一,同时也是容易产生风险的一种变更操作。本文就来聊下,批量刷数的发布需要经历哪些流程,以及流程中每个环节中有什么风险点需要考虑。
首先需要了解批量刷数这个操作是个什么意义。无论是新增/删除一些数据也好,还是UPDATE某些已有线上数据也好,其背后要么对应着一个新业务开发,要么对应着一个bugfix。因此,批量刷数的本质其实就是一个业务需求,需要有完整的一套需求评审 -> 技术评审 -> 线下测试 -> 线上发布 -> 发布验证的过程。
第一个环节是需求评审。需求评审的主要的目的,一是为了做刷数的可行性评估,二是做刷数记录归档。
【测试人生】SQL变更的问题拦截手段
在DB做变更发布的各类场景当中,通过SQL更改DB数据内容,是最为常见的场景。既然是最为常见的场景,那么可能产生线上问题的概率也就越大。本篇文章就来探讨一下,要尽量减少SQL发布产生线上问题的可能性,需要采取什么样的手段。
首先是针对SQL语句本身而言,需要做积累检查。假设用的是MySQL,那么SQL类型大体可能分为以下几种:
- 新增类:CREATE表/列/索引、INSERT数据
- 更改类:UPDATE数据
- 删除类:DELETE数据、DROP表/列/索引(包含替换场景)、TRUNCATE
针对这些SQL类型,其产生线上风险的概率也不尽相同,需要分别进行治理。
【从零单排Golang】第十二话:用singleflight解决单服务处理高并发任务的问题
在后端开发场景,我们经常会遇到高并发的事务处理逻辑。虽然在微服务治理的机制下,能够实现多个无状态服务节点+负载均衡高并发处理请求,但对于单个服务节点来讲,如果存在某些耗时的任务需要被高并发访问,那么如果没有一套妥善的机制处理,就很容易出现大量这类任务实例堆积,导致任务返回延迟,或者节点内存暴涨的情况。今天,就来介绍Golang内部处理这种场景的一套方案:singleflight。
singleflight的应用场景
singleflight主要解决单服务处理高并发任务问题,尤其是服务冷启动时候没有足够请求hit到缓存的场景。假设业务有一个【获取Token】的场景,存在如下的实际约束:
- 访问方式:一个业务(Business),获取一个特定区域(Region)的Token
- Token区域:分为欧亚美非四个区域,每个区域获取Token的时间不一样,但无论怎么输入,都需要【几秒】后才能得到结果
- 访问量级:单个业务下可能存在很多需要获取Token的服务,总的访问这个接口的QPS可能达到【百级或千级】
- 服务数量:你只有【1】个服务节点实例处理这些请求
为了解决这个问题,你可能会考虑通过以下的方式:
【DIY小记】工作中失误了,应该如何对待?
今天整理博客时,恰好瞄到CSDN创作活动里,最近在搞这个主题:如何对待工作中的失误?笔者个人觉得,这个话题是非常有意义的,尤其是在现在经济形势不好,人人都担忧失业的情况下,怎么样对待工作的失误,甚至是工作本身,不论是从心态上,还是从策略(抓手)上,都是值得去深入讨论的。因此,笔者决定为【DIY小记】系列特别撰写一篇文章,来谈一下工作中失误了之后,到底需要如何对待。
那么接下来,就以CSDN创作模板里的几个问题入手,来讨论这个话题吧!
【GitHub探索】用python写web前端之reactpy探索
你有想象过用python来写web前端这种操作么?近期在github-trending上就有这样的一个项目reactpy,可以满足你在python上写web前端的欲望。为此,笔者也决定踩踩坑,看看这个项目的形式到底如何,能不能很方便地实际投产。
要用到这个项目,除了reactpy库本身外,还需要一个backend-implementation来部署前端开发环境到本地端口。这里我们采取的操作是pip install reactpy[fastapi],这样就能直接安装上以fastapi为后端实现的前端开发环境部署。
要开始写一个简单的网页,可以参考reactpy的官方网站。基本代码如下:
【测试人生】管控数据类变更的重要性
大多数的事故来源于变更,这句话并不是妄言,而且确实是具有统计学意义的。在持续集成的过程中,一次发布对应的是一系列的变更,而变更意味着从一个已经稳定的状态切换到一个仅预期稳定的状态,这就导致了线上风险实际是在降低的。为了防止最终的发布的效果与预期不符,造成事故产生,除了对变更内容做业务功能上的测试之外,还需要考虑很多事情,比如分析变更影响到了哪些上下游业务跟服务性能,变更的时间是不是业务的高峰期,变更的行为是不是有更多的相关人员感知。充分考虑了这些因素,每一次变更发布才可以尽可能不产生事故或者事故的影响面被控制住。
对于一个业务而言,发布变更的方式有多种。占大部分的是代码变更,一个新业务的开发,老缺陷的bugfix,或是底层的技术优化,都需要代码变更来直接体现;其次是服务配置的变更,比如遇到特定feature关停或开放的场景,或是调整缓存/DB的容量,抑或是满足业务代码变更的需要,都需要通过配置变更的方式来体现。除这两个大头之外,数据变更也同样是占据一定比例的变更方式。数据变更主要场景有如下几块:
【从零单排Golang】第十一话:make和new的区别和用法
在Golang当中,对于常用数据结构的初始化方式,通常有2种:make跟new。这两种初始化方法用途不同,效果不同。本篇文章就来详细讲一下make和new具体都怎么用,在什么场景下会用上。
首先来看make。比起new,make所用到的场景非常特定,一般没法直接避开。我们看下面的代码例子:
1 | func TestMake(t *testing.T) { |
两个代码似乎都会初始化长度为5的“列表”m,但其实两个m的内在构造并不相同。实际上会出现如下的效果: