gRPC健康探针

简介 gRPC 健康探针 grpc-health-probe 是社区提供的一个工具,用来检查 gRPC 服务的健康状态,此工具 是通过 gRPC 健康检查协议公开服务的状态。 使用 我在本地使用 kratos 创建一个使用 9000 端口的 gRPC 的服务。通过 grpc-health-probe 可以检查服务的健康状态。 grpc-health-probe -addr=localhost:9000 status: SERVING 可以看到此服务目前是健康的,不健康的服务将以非零退出代码退出。 grpc_health_probe -addr=localhost:9000 -connect-timeout 250ms -rpc-timeout 100ms failed to connect service at "localhost:9000": context deadline exceeded exit status 2 grpc_health_probe 发送了一个对 /grpc.health.v1.Health/Check 的RPC 请求。如果已 SERVING 状态作为响应,就会正常成功退出,否则会给出一个非零的退出。 Kubernetes 使用 grpc_health_probe 可用于 Kubernetes对 Pod 中运行的 gRPC 服务器进行健康检查。建议您使用Kubernetes exec探针并为您的 gRPC 服务器 pod 定义活跃度和/或就绪性检查。 您可以将静态编译grpc_health_probe的内容捆绑到您的容器映像中。 RUN GRPC_HEALTH_PROBE_VERSION=v0.3.1 && \ wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ chmod +x /bin/grpc_health_probe 在您的 Kubernetes Pod 规范清单中,为容器指定一个livenessProbe和/或 :readinessProbe ...

七月 31, 2022 · overstarry

Go协程闭包的问题

问题 最近在代码中遇到了这么一个问题,现在有一个循环,每一个循环中创建一个协程用来执行函数,我发现函数运行的结果却是大部分时候都是使用最后一个循环变量,不符合实际要求。 大概的代码如下: package main import ( "fmt" "net/http" ) func main() { for i := 0; i < 10; i++ { go func() { fmt.Println(i) }() } http.ListenAndServe(":8080", nil) } 运行: 10 10 10 10 10 10 10 10 10 10 多次运行,可以发现大部分情况都是将 i = 10 代入函数执行。 原因 那这是为什么呢? 这个就是函数闭包。协程运行的是一个闭包函数,其中使用了主线程的变量i 。看上去这和第一组几乎一样。但是在每个协程中,从进入匿名函数到调用Println将i的值复制入栈之间仍需要一小段时间运行,而这段时间内足以主线程完成全部10次循环。所以终于到将i的值复制入栈调用Println时,i已经成为10且不再变化了。 解决 那该怎么解决使代码如我们的需求运行呢? 我们只需将变量 i 复制进栈中即可,改动后的代码: package main import ( "fmt" "net/http" ) func main() { for i := 0; i < 10; i++ { go func(j int) { fmt.Println(j) }(i) } http.ListenAndServe(":8080", nil) } 结果: ...

七月 10, 2022 · overstarry

Go压缩png图像大小

起因 最近在处理一个需求,需要将 png 图像按比例调整图像尺寸,要求在保证图像质量的情况下尽量缩小文件大小。在本篇文章主要介绍我将 png 文件大小缩小使用的方法。 方法 这个需求缩小图像的尺寸很好解决,但缩小后的图像大小不尽人意,缩小的图像文件大小没有变化过多,甚至更大。我通过查询,发现了一种方法,就是先将图片转换为 jpeg 格式,再进行压缩后转换为 png 即可。 Jpeg的图片压缩是很好做的,因为jpeg这个协议本身就支持调整图片质量的。在golang中,我们只需要使用标准库的image/jpeg,将图片从二进制数据解码后,降低质量再编码为二进制数据即可实现压缩。而且质量和压缩比例相对而言还不错。 func compressImageResource(data []byte) []byte { imgSrc, _, err := image.Decode(bytes.NewReader(data)) if err != nil { return data } newImg := image.NewRGBA(imgSrc.Bounds()) draw.Draw(newImg, newImg.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src) draw.Draw(newImg, newImg.Bounds(), imgSrc, imgSrc.Bounds().Min, draw.Over) buf := bytes.Buffer{} err = jpeg.Encode(&buf, newImg, &jpeg.Options{Quality: 40}) if err != nil { return data } if buf.Len() > len(data) { return data } return buf.Bytes() } 小结 本文主要介绍了将 png 图像大小缩小的一种简单方法,还有其他方法等待你们的发掘。

七月 2, 2022 · overstarry

Go处理zip解压乱码问题

问题 最近在某个场景中,需要使用 go 官方的 archive/zip 处理 zip 压缩包,在处理过程中,遇到了一个问题: go 解压后的文件存在文件名乱码的情况。 解决 我们知道在 go 中,字符串是以 UTF-8 编码的,所以有可能出现乱码的情况。 我们只要在处理压缩包中的文件时,通过判断 Flags 字段,如果 Flags 为 0 , 则使用本地编码,默认为 GBK。 如果为 1 , 则使用 UTF-8 编码。 我们只要在为 0 时对文件名进行处理就好。 代码: func Unzip(zipFile string, destDir string) error { zipReader, err := zip.OpenReader(zipFile) if err != nil { return err } defer zipReader.Close() var decodeName string for _, f := range zipReader.File { if f.Flags == 0{ i:= bytes.NewReader([]byte(f.Name)) decoder := transform.NewReader(i, simplifiedchinese.GB18030.NewDecoder()) content,_:= ioutil.ReadAll(decoder) decodeName = string(content) }else{ decodeName = f.Name } fpath := filepath.Join(destDir, decodeName) if f.FileInfo().IsDir() { os.MkdirAll(fpath, os.ModePerm) } else { if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { return err } inFile, err := f.Open() if err != nil { return err } defer inFile.Close() outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { return err } defer outFile.Close() _, err = io.Copy(outFile, inFile) if err != nil { return err } } } return nil } 参考 https://thismj.cn/2019/02/14/qian-xi-zip-ge-shi/ https://chai2010.cn/post/golang/go-zip-utf8/ https://codereview.appspot.com/54360043/ https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT

六月 25, 2022 · overstarry

Singleflight介绍

缓存击穿 对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。 缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。 我在 go-redis/cache 中发现了库使用了 singleflight , 经过查阅资料,了解了 这个库的主要作用就是将一组相同的请求合并成一个请求,实际上只会去请求一次,然后对所有的请求返回相同的结果。 这样会大大降低数据库的压力。 singleflight 使用 函数签名 type Group struct { mu sync.Mutex // protects m m map[string]*call // lazily initialized } // Do 执行函数, 对同一个 key 多次调用的时候,在第一次调用没有执行完的时候 // 只会执行一次 fn 其他的调用会阻塞住等待这次调用返回 // v, err 是传入的 fn 的返回值 // shared 表示是否真正执行了 fn 返回的结果,还是返回的共享的结果 func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) // DoChan 和 Do 类似,只是 DoChan 返回一个 channel,也就是同步与异步的区别 func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result // Forget 用于通知 Group 删除某个 key 这样后面继续这个 key 的调用的时候就不会在阻塞等待了 func (g *Group) Forget(key string) 示例 接下来我们来讲解一个简单的例子,我们来看看 singleflight 的使用方式,先来看一个简单的例子: ...

六月 18, 2022 · overstarry