Golang学习
liyunlong41
你必须非常努力,才能看起来毫不费力。
展开
-
golang no cancelable context
在开发过程中,我们可能经常遇到需要异步处理的任务,主协程可能不会等待异步任务的完成就结束,传递给异步任务的是主协程的context,主协程退出导致context被cancel, 影响异步任务。如果简单的给异步任务传递context.Background,可能导致一些value缺失,如trace信息。因此,需要一种no cancelable的context,能继承主协程中context的所有value,同时不继承其cancel机制,我们可以通过以下方法来实现。// PropagateContext原创 2022-02-25 15:19:13 · 636 阅读 · 1 评论 -
爬虫服务(chromedp)僵尸进程排查记录
目录发现现象如何解决?临时解决根本上解决修复过程发现现象爬虫服务会使用chromedp库(https://github.com/chromedp/chromedp)模拟浏览器登录,抓取网页数据,某天在pod内查看服务运行状态时,发现有大量的zombie进程,看了下是Chrome进程。爬虫服务使用Chrome,应该是以创建子进程的方式来启动Chrome,如果在子进程销毁时没有wait或者waitpid来处理,那么子进程会成为zombie进程。每个zo..原创 2021-08-04 17:52:25 · 2320 阅读 · 0 评论 -
golang手动实现error group
手动实现了下go error group,并发请求、请求数量限制、出错时cancel掉所有task,以下为示例,要求task是可cancel的!package mainimport ( "context" "errors" "fmt" "sync" "sync/atomic" "time")const ( M = 2 N = 8)func main() { ctx, cancel := context.WithTimeout(context.Background.原创 2021-02-26 16:13:12 · 711 阅读 · 0 评论 -
go实现N个goroutine交替打印数字
go中CSP模型提倡“不要以共享内存的方式来通信,相反,要以通信来共享内存”,go的CSP模型,就是通过goroutine和channel结合的形式来实现的。这里通过goroutine和channel实现了一个简单的并发控制,即通过channel在不同的goroutine之间传递信息,而不是通过锁的形式,或者是共享变量的形式来实现。例如要实现3个goroutine交替输出1-30的数字,可以参考下面的实现方法:下方会有解释func getWorker(waitCh chan int, s原创 2020-10-10 16:59:18 · 2312 阅读 · 1 评论 -
golang常驻后台类worker模板
golang中经常会用到常驻后台类的worker,实现例如消费队列、定期执行任务、定期统计数据等功能。这里自己实现了通用的worker模板,主要有以下功能:panic自动重启,最大重启次数可自定义。 optional参数,有默认参数和支持自定义参数。 busy模式和idle模式,执行完任务后睡眠不同的时间。 支持以一定的频率执行,例如每5分钟执行一次任务。 可动态的通过channel控制任务的启动和暂停。ps:此处可结合etcd实现分布式选主机制。 支持用context优雅的停止,work原创 2020-08-28 13:54:45 · 1027 阅读 · 0 评论 -
线上golang grpc服务资源泄露问题排查
前几天告警群里报出一个go服务grpc接口出现很多超时现象,排查发现是服务有内存泄露与cpu占用高的问题,在这里将排查的过程记录一下,给大家提供排查问题的方向与思路,同时借鉴教训,优化自己服务代码。发现超时现象后,登录机器看了下top,该服务总共有两台机器,发现02机器的cpu与内存占用很高(如下图第一个进程),而01机器都很低。正常情况下不会有这么高的资源占用,可能是服务有资源泄露的问题,资...原创 2020-04-02 18:37:13 · 4644 阅读 · 1 评论 -
golang sync.Mutex锁如何实现goroutine的阻塞与唤醒初探
var mu = new(sync.Mutex)func test() { mu.Lock() defer mu.Unlock() ...}这是一段mutex的加锁解锁代码,当多线程同时执行这段代码时,能保证我们临界区的代码是顺序执行的。新来的goroutine可能直接获取到锁,也可能被阻塞,这里探究一下goroutine是如何被阻塞和唤醒的。由于mutex实现复杂,牵扯知识很多,我...原创 2020-03-19 17:20:54 · 2773 阅读 · 1 评论 -
grpc负载均衡RoundRobin源码解读
grpc client端创建连接时可以用WithBalancer来指定负载均衡组件,这里研究下grpc自带的RoundRobin(轮询调度)的实现。源码在google.golang.org/grpc/balancer.go中。roundRobin结构体定义如下:type roundRobin struct { r naming.Resolver w naming.Wat...原创 2019-12-26 23:36:54 · 2188 阅读 · 0 评论 -
grpc复用client连接
以前在调用grpc接口时,总要调用Dial函数来创建一个grpc的ClientConn,如果每次都要创建,那么开销代价是很大的。grpc的ClientConn对象可以帮我们实现自动重连的机制,并且是并发安全的,因此可以定义一个全局的ClientConn,然后用到的时候就用这个连接。下面是简易实现:package delayqueueimport ( "google.golang...原创 2019-09-17 15:18:30 · 9969 阅读 · 1 评论 -
golang实现简单的goroutine worker池
实现了简单的worker池,可以执行一些后台常驻类的任务,并且可以实现worker数量的实时控制。利用两个worker数组,alive和idle,分别保存活跃的worker和空闲的worker,并且空闲的worker可以从暂停状态快速转变为活跃状态。由于每次在增加worker数量的时候没有创建goroutine的情况,所以效率会稍微高一些。type taskFunc func() err...原创 2019-09-05 20:33:39 · 688 阅读 · 0 评论 -
golang利用redis实现简单延时队列
redis的zset是一种能自动排序的数据结构,我们可以用这个特性来实现简单的延时队列。利用zadd将数据添加到zset中,每个数据的score值设置为数据的延时时间+当前时间戳,后台goroutine不断zrange轮询zset,取出score值小于当前时间戳的数据,然后再对数据进一步处理,这样就实现了简单延时队列的功能。Zaddzadd支持批量添加的功能,有需要可以自己去探索下,这里...原创 2019-05-08 19:45:57 · 2647 阅读 · 0 评论 -
golang利用redis实现消息队列Push和Pop
可以利用redis的list结构来实现消息队列功能,使用lpush、rpush来实现入队,lpop、rpop来实现出队列。我们统一从左边push、从右边pop,即用lpush和rpop组合。当list中没有元素时,rpop会返回nil,这样我们需要不断用轮询队列,直到队列中有元素,然后pop出来。为了避免不断轮询带来的性能损耗,我们这里使用brpop命令,brpop使用了系统提供的阻塞...原创 2019-05-08 18:22:16 · 6969 阅读 · 0 评论 -
golang利用redis实现限速器
利用redis的String数据结构可以实现计数器的功能,利用incr或者incrby来实现计数器的增加或者减少。同时redis对可以对key实现过期功能,这样我们就可以利用计数器和过期功能来实现限速器。例如很多时候我们需要对单个用户进行限速,每分钟或每秒钟限制limit次,可以使用user_id作为key,访问次数作为value,到redis进行存储,当用户访问一次,我们就incr一下;...原创 2019-05-05 21:11:21 · 2416 阅读 · 0 评论 -
golang functional options,优雅的初始化对象实例
当我们定义了一个对象时,一般会创建一个方法方便外部初始化一个实例。如下面的例子:type Client struct { timeout int64 dialFunc func() error healthCheck func() bool}func NewClient(timeout int64, dialFunc func() error, healthChec...原创 2019-04-05 22:55:36 · 4005 阅读 · 0 评论 -
redigo批量lpush/rpush、批量zrem
在使用redigo库时,想批量向redis list中批量push,发现像下面方式传入[]string是不行的,在这里记录一下。keys := []string{"nn", "mm"}_, err = con.Do("lpush", queueName, keys)直接传slice发现结果是整个slice被拼在一起放到一起了:后来google了下,发现需要将slice用三个点...原创 2019-03-24 19:19:57 · 4059 阅读 · 2 评论 -
漏桶算法
近期在研究Jaeger,Jaeger中有一种采集策略是速率限制类型,内部使用的是漏桶算法,在这里研究了下Jaeger漏桶算法的实现原理,自己仿照其实现了一个rateLimiter,并进行了相关测试,下面是主要实现。lck:lck是互斥锁,主要用来防止并发情况下产生错误。 rate:速率,即接口每秒限制多少个请求。在这里也就是水滴从漏桶中流出的速度,同时也是余量增加的速度。 balance:...原创 2019-03-12 21:06:36 · 1978 阅读 · 0 评论 -
golang实现简单的jaeger-demo
参考地址:https://github.com/yurishkuro/opentracing-tutorial/tree/master/gojaeger是一个比较有名的分布式链路追踪系统,底层用golang实现,兼容opentracing标准,这里利用其go-client来实现一个最简单的demo,仅供参考。1. 安装必要的包:"github.com/opentracing/open...原创 2019-02-26 13:25:42 · 36870 阅读 · 1 评论 -
golang支付宝支付生成签名
调用支付宝支付接口时,需要用商户自己的私钥生成sign,将数据与sign一起发送给支付宝来发起支付。这里总结一下签名的流程,以支付宝手机网站支付为例。实现语言为golang。请求参数网址:https://docs.open.alipay.com/203/107090/一. 生成biz_content业务参数信息:func GenBizContent(subject, outTra...原创 2018-11-08 11:21:43 · 33124 阅读 · 0 评论 -
golang线程安全的map
github地址:https://github.com/hackssssss/safemap网上找的协程安全的map都是用互斥锁或者读写锁实现的,这里用单个协程来实现下,即所有的增删查改操作都集成到一个goroutine中,这样肯定不会出现多线程并发访问的问题。基本思路是后台启动一个长期运行的goroutine,阻塞的接受自己channel中的请求req,req分为不同的请求,比如读key...原创 2018-11-19 20:37:09 · 30621 阅读 · 0 评论 -
golang删除slice中特定条件的元素,优化版
写了两种对一个slice中删除特定元素的方法,并做了性能对比,在这里记录一下。第一种方法:func DeleteSlice(a []int) []int{ for i := 0; i < len(a); i++ { if a[i] == 0 { a = append(a[:i], a[i+1:]...) i-- } } return a}解释:这里利...原创 2018-12-20 19:56:28 · 31536 阅读 · 20 评论 -
敏感词过滤golang
用golang写了敏感词过滤的工具,主要用来检测用户昵称中是否存在敏感词,同时提供剔除转移字符的功能。可以先将敏感词库存放在一个map中,敏感词可以参考这里:https://github.com/fwwdn/sensitive-stop-words将map和昵称传入,程序会检查昵称的每一个子串,判断是否在map敏感词库中。复杂度O(len(name)^2)package util...原创 2018-12-21 12:40:02 · 32085 阅读 · 0 评论 -
golang LRU Cache
github地址:https://github.com/hackssssss/lruCache用golang中的map与list双向链表实现了lru(最久未使用)算法,map value中存放数据以及key在list中的地址,list中存放map的key,当set或者get时,从map中获取key在list中的地址,将其删除(list是双向链表,删除复杂度O1),并在list尾部添加新的key...原创 2018-12-28 17:46:01 · 28914 阅读 · 0 评论 -
golang expired LRU cache(key有过期时间的,实现了LRU算法的cache)
github地址:https://github.com/hackssssss/lru_expired_cache之前实现了lru算法的cache,链接,后来发现可以添加key自动过期的策略,采用惰性过期策略。即当get或者set这个key时,才去更新key的状态,如果过期,那么将其删除。package lru_expiredcacheimport ( "container/list...原创 2018-12-29 17:25:00 · 2507 阅读 · 0 评论 -
golang实现expiredMap,key带过期时间,超时自动删除
github地址:https://github.com/hackssssss/ExpiredMap偶然间看到一篇关于超期删除key的map的文章,感觉很有意思,于是自己去实现了一下。了解了一下redis中key的过期策略,发现主要有三种策略:一种是被动删除,即key过期之后,先不将其删除,当实际访问到的时候,判断其是否过期,再采取相应的措施。第二种是主动删除,即当key过期之后,立即将这个...原创 2018-12-26 12:40:14 · 9997 阅读 · 1 评论 -
leetcode128. Longest Consecutive Sequence 最长连续序列 并查集
题目地址:https://leetcode.com/problems/longest-consecutive-sequence/方法1:并查集这个题的难点在于当更新了某个val后,不能及时更新val周围连续的数值,如val+1, val+2, val-1, val-2等。这里采用并查集的思想,将每一段连续的数值,看做是一个集合,每个集合中有一个leader或者parent。当val值有更新...原创 2019-01-02 20:28:31 · 441 阅读 · 0 评论 -
golang获取本月第一天、获取本月最后一天、获取当天0点的时间
主要是通过time.Now()函数和time.AddDate函数以及time.Date()函数来构造和加减日期,获取本月第一天和最后一天。具体可以看代码实现。package utilimport ( "time")//获取传入的时间所在月份的第一天,即某月第一天的0点。如传入time.Now(), 返回当前月份的第一天0点时间。func GetFirstDateOfMonth(...原创 2019-02-05 19:27:56 · 50419 阅读 · 0 评论 -
golang slice在其他函数中修改,易错点
回忆起写golang时候踩过的坑,那是我逝去的青春……我们可能会遇到给其他函数传递一个slice,让其他函数给这个slice做一些修改的情况。想到slice是引用传递,可以直接传递slice用作修改,于是可能出现下面这种情况:package mainimport ( "fmt" "testing")func TestModifySlice(t *testing.T) { ...原创 2019-02-06 13:33:54 · 33586 阅读 · 0 评论 -
golang实现延时队列
github源码地址:https://github.com/hackssssss/delay_queue参考了有赞延时队列的设计思路,自己写了一个延时队列,基本原理是利用redis的list实现队列,利用redis的zset实现按照时间排序功能。大体流程如下图所示:流程解读:1. 调用方调用Push接口,将data、ttr(time to return)、notify_url传...原创 2019-02-18 20:10:09 · 33118 阅读 · 1 评论 -
opentracing: jaeger在grpc中的简单实现
参考项目:https://github.com/grpc-ecosystem/grpc-opentracing之前用函数调用实现了简单jaeger-demo(https://blog.csdn.net/liyunlong41/article/details/87932953),函数之间利用context传递span信息。现在开始在grpc请求中实现简单的grpc-jaeger-demo,spa...原创 2019-02-28 21:07:45 · 32621 阅读 · 0 评论 -
用golang实现替换某个文件中的字符串
用golang实现了某个文件中字符的替换,替换为按行替换,执行后会生成新文件,如a.txt,执行后生成a.txt.mdf。新文件即修改后的内容。主要用来练习文件的读取与写入。package mainimport ( "bufio" "fmt" "io" "os" "strings")func main() { if len(os.Args) != 4 { fmt...原创 2019-02-25 20:31:37 · 43094 阅读 · 2 评论 -
Golang实现对map的并发读写
在Golang多协程的情况下使用全局map时,如果不做线程同步,会出现panic的情况。为了解决这个问题,通常有两种方式:第一种是最常见的使用互斥锁或者读写锁的方法;第二种是比较符合Golang特色的方法,启动单个协程对map进行读写,当其他协程需要读写map时,通过channel向这个协程发送信号即可。写了一个模拟程序对map中的一项进行读或者写,后台一直运行的协程阻塞的接受读写信号,...原创 2018-10-07 16:37:17 · 4107 阅读 · 0 评论