gRPC 请求抓包

前言 本文来简单介绍如何使用 wireshark 来获取 gRPC 请求。 wireshark 配置 在进行对 gRPC 请求抓包前,得来几个准备。 设置 proto 文件路径 依次打开 编辑 > 首选项 > Protocols > ProtoBuf, 点击如图选项,添加 proto 文件所在的路径。记得勾选右边的 “Load all files” 选项。 设置 TCP 消息解码 默认情况下,界面上显示的都是 TCP 数据包。依次点击菜单栏的 分析 -> 解码为… (或者右击随便一行)。 把 9000 (你的 gRPC 服务端端口) 端口的 TCP 消息解码成 HTTP2 协议信息。 开始抓包 现在开始捕获 gRPC 请求消息,为了避免其他无关的流量,在捕获选项设置筛选 tcp port 9000 只获得跟服务端相关的流量。 我们使用 postman 向服务端发送请求。回到 wireshark 界面,我们就可以看到许多流量,通过前面设置的解码,我们可以很方便的获得 gRPC 消息的具体内容。 小结 本文简单介绍了如何使用 wireshark 捕获 gRPC 请求流量。在使用 Wireshark 抓包时把未识别的 HTTP/2 协议手动设置为 HTTP/2,这样会方便很多。 ...

九月 3, 2022 · overstarry

gRPC 服务反射协议

本文主要介绍 gRPC 的服务反射协议和相关的应用。 介绍 gRPC 服务反射协议 (server reflection) 是在 gRPC 服务端定义的一个服务,它能提供该服务器端上可公开使用的 gRPC 服务的信息,简单的来说,就是服务反射向客户端提供了服务端注册的服务的信息。因此客户端不需要预编译服务定义就能与服务端交互了。 客户端想要与服务端程序进行通信,必须要有所定义的服务信息,需要编译生产客户端存根,借助 gRPC 服务反射协议,我们就可以无需编译服务定义就能通信。 使用 该如何开启服务反射协议呢?很简单,只需要通过一行代码即可开启:reflection.Register() package main import ( "context" "flag" "fmt" "log" "net" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ecpb "google.golang.org/grpc/examples/features/proto/echo" hwpb "google.golang.org/grpc/examples/helloworld/helloworld" ) var port = flag.Int("port", 50051, "the port to serve on") // hwServer is used to implement helloworld.GreeterServer. type hwServer struct { hwpb.UnimplementedGreeterServer } // SayHello implements helloworld.GreeterServer func (s *hwServer) SayHello(ctx context.Context, in *hwpb.HelloRequest) (*hwpb.HelloReply, error) { return &hwpb.HelloReply{Message: "Hello " + in.Name}, nil } type ecServer struct { ecpb.UnimplementedEchoServer } func (s *ecServer) UnaryEcho(ctx context.Context, req *ecpb.EchoRequest) (*ecpb.EchoResponse, error) { return &ecpb.EchoResponse{Message: req.Message}, nil } func main() { flag.Parse() lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) if err != nil { log.Fatalf("failed to listen: %v", err) } fmt.Printf("server listening at %v\n", lis.Addr()) s := grpc.NewServer() // Register Greeter on the server. hwpb.RegisterGreeterServer(s, &hwServer{}) // Register RouteGuide on the same server. ecpb.RegisterEchoServer(s, &ecServer{}) // Register reflection service on gRPC server. reflection.Register(s) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } } 服务端开启服务反射协议后,就可以通过 gRPC CLI 工具来检查服务端了。这里就不多介绍了,接下来我们来看看服务反射协议在 kratos 中的使用。 ...

八月 27, 2022 · overstarry

Prometheus_operato

安装 Metrics Server 有了 Metrics Server,用户就可以访问 Kubernetes 核心监控数据(core metrics)。这其中包括了 Pod、Node、容器、Service 等主要 Kubernetes 核心概念的 Metrics。 Resource MetricsAPI: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/instrumentation/resource-metrics-api.md kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml 部署 Prometheus kube-prometheus 下载存储库 git clone https://github.com/prometheus-operator/kube-prometheus 使用 manifests 中的配置文件创建监控 stack cd kube-prometheus kubectl create -f manifests/setup until kubectl get servicemonitors --all-namespaces ; do date; sleep 1; echo ""; done kubectl create -f manifests/ 访问 dashboards 通过 kubectl --namespace monitoring port-forward svc/prometheus-k8s 9090 就能展现prometheus ui grafana kubectl --namespace monitoring port-forward svc/grafana 3000 默认账户密码 admin/admin,进入后会要求修改密码,可以看到已经有了预添加了数据源 可以看到有了许多 K8S 监控的默认看板 ...

八月 23, 2022 · overstarry

Go 进行浏览器网页截图

前言 在本文中将介绍使用 golang 进行加载某个网站并进行截图。 chromedp 我们将使用 chromedp 通过浏览器驱动来加载网页并截图。具体的步骤如下: 1 启动 chrome 浏览器 2 加载网页 (还可进行其他浏览器操作) 3 截图并保存 需要注意的是项目使用了 Chrome 的驱动,如果没有 Chrome 将不能顺利运行,需要运行 https://hub.docker.com/r/chromedp/headless-shell/ 来进行 或运行其他 版本的 Chrome。 安装 go get -u github.com/chromedp/chromedp 示例 package main import ( "context" "io/ioutil" "log" "github.com/chromedp/chromedp" ) func main() { ctx, cancel := chromedp.NewContext(context.Background(), chromedp.WithDebugf(log.Printf)) defer cancel() url := "https://www.minigame.vip/" filename := "minigame.png" var imageBuf []byte // 捕获某个元素的截图 if err := chromedp.Run(ctx, elementScreenshot(`https://pkg.go.dev/`, `img.Homepage-logo`, &imageBuf)); err != nil { log.Fatal(err) } if err := ioutil.WriteFile("elementScreenshot.png", imageBuf, 0644); err != nil { log.Fatal(err) } if err := chromedp.Run(ctx, ScreenshotTasks(url, &imageBuf)); err != nil { log.Fatal(err) } if err := ioutil.WriteFile(filename, imageBuf, 0644); err != nil { log.Fatal(err) } } func elementScreenshot(urlstr, sel string, res *[]byte) chromedp.Tasks { return chromedp.Tasks{ chromedp.Navigate(urlstr), chromedp.Screenshot(sel, res, chromedp.NodeVisible), } } func ScreenshotTasks(url string, imageBuf *[]byte) chromedp.Tasks { return chromedp.Tasks{ chromedp.Navigate(url), chromedp.FullScreenshot(imageBuf, 90), } } 上面的例子分别是对网页中的单个元素进行截图和对网页全局截图。 ...

八月 20, 2022 · overstarry

gopsutil 介绍

简介 psutil 是一个使用 Python 编写的跨平台平台进程和系统利用率监控库,gopsutil 就是 psutil 的 go 语言实现。 使用 安装 go get github.com/shirou/gopsutil/v3 CPU 获取 cpu 基本信息 func getCpuInfo() { cpuInfos, err := cpu.Info() if err != nil { fmt.Printf("get cpu info failed, err:%v", err) } for _, ci := range cpuInfos { fmt.Println(ci) } for { percent, _ := cpu.Percent(time.Second, false) fmt.Printf("cpu percent:%v\n", percent) } } {"cpu":0,"vendorId":"GenuineIntel","family":"205","model":"","stepping":0,"physicalId":"BFEBFBFF000406E3","coreId":"","cores":4,"modelName":"Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz","mhz":2400,"cacheSize":0,"flags":[],"microcode":""} cpu percent:[40.625] cpu percent:[15] cpu percent:[9.615384615384617] cpu percent:[28.125] 获取 cpu 负载 func getCpuLoad() { info, err := load.Avg() if err != nil { panic(err) } fmt.Printf("%v\n", info) } 内存 获取内存 func getMemInfo() { memInfo, _ := mem.VirtualMemory() fmt.Printf("mem info:%v\n", memInfo) } mem info:{"total":8472920064,"available":1921372160,"used":6551547904,"usedPerce nt":77,"free":1921372160,"active":0,"inactive":0,"wired":0,"laundry":0,"buffers" :0,"cached":0,"writeBack":0,"dirty":0,"writeBackTmp":0,"shared":0,"slab":0,"srec laimable":0,"sunreclaim":0,"pageTables":0,"swapCached":0,"commitLimit":0,"commit tedAS":0,"highTotal":0,"highFree":0,"lowTotal":0,"lowFree":0,"swapTotal":0,"swap Free":0,"mapped":0,"vmallocTotal":0,"vmallocUsed":0,"vmallocChunk":0,"hugePagesT otal":0,"hugePagesFree":0,"hugePagesRsvd":0,"hugePagesSurp":0,"hugePageSize":0} 库的其他用法可以查看相应的官方文档。 ...

八月 13, 2022 · overstarry

记一次阿里云 OSS 报错的解决

最近在使用 MinIO go-sdk 操作阿里云 OSS 时遇到了一个问题,特此记录下。 问题 在使用 sdk 调用 PutObject 方法时,发生了报错,具体报错如下: The request signature we calculated does not match the signature you provided. Check your key and signing method. 解决 可以看出报错的意思是签名不一致问题,我们首先检查 AccessKey ID 和 AccessKey Secret 是否正确,我发现同样创建的 client 在其它地方调用 方法时,没有报错,成功上传了文件对象。 后来查了多个相关的问题,发现可能是我填写的 objectName 的问题,我的 objectName 前带了 / 符号,导致在计算签名时变成 // ,使签名不一致。 我去掉前面的 ‘/’ 符号后,果然成功上传了。 总结 在编写相关操作 OSS 代码时,要注意 objectName 的格式,不要加上多余的符号。但奇怪的是原来上传至 MinIO 时没有报错,正常上传,可能是不同的存储有不同的签名规则吧。 参考 https://help.aliyun.com/document_detail/31951.htm?spm=a2c4g.11186623.0.0.3057jkGJjkGJjL https://blog.csdn.net/DCTANT/article/details/107917268 https://error-center.aliyun.com/status/search?Keyword=SignatureDoesNotMatch&source=PopGw";

八月 2, 2022 · overstarry

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 的内容捆绑到您的容器映像中。 ...

七月 31, 2022 · overstarry

GRPC 健康检查

本篇文章我来介绍 gRPC 中的健康检查相关的知识。 简介 服务的健康检测一般是指的是检测服务是否正常运行: 是否存在,因为程序逻辑错误或者 OOM 等进程不存在; 存在是否可以正常的响应请求,尽管进程存在但可能因为请求量过大或者程序逻辑错误,导致服务 hang 住,无法正常对外请求。 gRPC 定义了一个健康检查协议,它允许使用了 gRPC 服务暴露服务器的状态,这样服务的消费者就能获得服务的健康状态。服务器的健康状态是由服务器是否响应非健康状态来确定的。 当服务器还没准备好处理 rpc 请求或者根本没有响应健康探针的请求时,就会发生这种情况。 健康服务定义 gRPC 健康检查协议基于 gRPC 定义了 API。下面就是服务定义: syntax = "proto3"; package grpc.health.v1; message HealthCheckRequest { string service = 1; } message HealthCheckResponse { enum ServingStatus { UNKNOWN = 0; SERVING = 1; NOT_SERVING = 2; } ServingStatus status = 1; } service Health { rpc Check(HealthCheckRequest) returns (HealthCheckResponse); rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse); } 客户端应该调用 Check 服务来判断服务是否正常运行,并且设置 deadline。客户端可以设置需要查询的服务名称,来返回对应的服务是否正常。 服务器应手动注册所有的服务并单个设置状态,包括空服务名称及其状态。对于收到的每一个请求,从注册表中查询服务的状态并返回。如果未找到,返回 NOT_FOUND 状态。服务器也可以根据实际的业务逻辑提供更为复杂的状态返回。 ...

七月 23, 2022 · overstarry

Go 发送邮件

介绍 电子邮件是一种用电子手段提供信息交换的通信方式,是互联网应用最广的服务。通过网络的电子邮件系统,用户可以以非常低廉的价格(不管发送到哪里,都只需负担网费)、非常快速的方式(几秒钟之内可以发送到世界上任何指定的目的地),与世界上任何一个角落的网络用户联系。 电子邮件在网络中的传输需要遵从一定的协议的,常用的电子邮件协议包括 SMTP,POP3,IMAP。电子邮件的创建和发送只涉及到 SMTP 协议,本文主要就是介绍 go 使用 SMTP 协议发生邮件。 使用 我们这里使用 https://github.com/jordan-wright/email 这个 pkg 来创建和发送邮件。pkg 的安装这里就不过多介绍了。 我们知道邮箱使用 SMTP/POP3/IMAP 等协议从邮件服务器上拉取邮件。邮件并不是直接发送到邮箱的,而是邮箱请求拉取的。所以,我们需要配置 SMTP/POP3/IMAP 服务器。从头搭建固然可行,而且也有现成的开源库,但是比较麻烦。这里我使用 gmail,配置流程就不过多介绍了 (ps: Google 的安全性还是比较强的,设置了好久。 代码: package main import ( "log" "net/smtp" "github.com/jordan-wright/email" ) func main() { e := email.NewEmail() e.From = "[email protected]" e.To = []string{"[email protected]"} e.Subject = "这是主题" e.Text = []byte("测试邮件发送") err := e.Send("smtp.gmail.com:587", smtp.PlainAuth("", "[email protected]", "xx", "smtp.gmail.com")) if err != nil { log.Fatal(err) } } 过一会就顺利收到邮件了,有时候邮件会被当作垃圾邮件收入垃圾箱里,收不到时可以去垃圾箱看看。 参考 https://baike.baidu.com/item/%E7%94%B5%E5%AD%90%E9%82%AE%E4%BB%B6/111106

七月 16, 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