Golang 搭建 WebSocket 应用(六) - 监控

我在上一篇文章中,提到了目前的认证方式存在一些问题,需要替换为一种更简单的认证方式。
但是最后发现,认证这个实在是没有办法简单化,认证本身又是另外一个不小的话题了,因此关于这一点先留个坑。

本文先讨论一下另外一个也比较重要的功能:监控。

为认证预留扩展点

虽然我们暂时不去实现更加完善的认证流程,但是我们依然可以先为其预留一个扩展点,
这样在未来我们要实现认证的时候,就不需要改动太多的代码了。

同样的,我们也可以基于 DIP 原则来实现,我们可以定义一个 Authenticator 接口:

type Authenticator interface {// Authenticate 验证请求是否合法,第一个返回值为用户 id,第二个返回值为错误Authenticate(r *http.Request) (string, error)
}

然后我们可以在 Hub 结构体中添加一个 authenticator 字段:

type Hub struct {// 验证器authenticator Authenticator
}

而对于我们目前的这种基于 jwt token 的认证方式,我们可以实现一个 JwtAuthenticator

var _ Authenticator = &JWTAuthenticator{}type JWTAuthenticator struct {
}func (J *JWTAuthenticator) Authenticate(r *http.Request) (string, error) {jwt := NewJwt(r.FormValue("token"))return jwt.Parse()
}

接着,我们在 newHub 中初始化这个 authenticator

func newHub() *Hub {return &Hub{// ... 其他代码 ...authenticator: &JWTAuthenticator{},}
}

这样,我们就可以在 serveWs 中使用这个 authenticator 了:

func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {uid, err := hub.authenticator.Authenticate(r)if err != nil {log.Println(fmt.Errorf("jwt parse error: %w", err))return}// ... 其他代码
}

在后面我们实现了更加完善的认证流程之后,我们只需要实现一个新的 Authenticator 即可。

2023 了,应用监控怎么做

发展到今天,我们已经有了很多很好用的监控相关的东西,比如 PrometheusGrafana
以及一些分布式链路追踪的组件,如 skywalkingjaeger 等。

但是他们各自的应用场景都不太一样,并不存在一个万能的监控工具,因此我们需要根据自己的需求来选择:

  • Prometheus:Prometheus 是一个开源的系统监控和报警工具。主要用于收集、存储和查询系统的监控数据,以便进行性能分析、故障排除和告警。
  • Grafana:Grafana 是一个开源的数据可视化和监控平台,用于创建、查询、分析和可视化时间序列数据。目前比较常见的组合就是 Prometheus + Grafana,通过 Prometheus 收集数据,然后通过 Grafana 展示数据。
  • 分布式链路追踪:常用语分布式系统的调用链路追踪,可以用于分析系统的性能瓶颈,以及分析系统的调用链路。常见的实现有 skywalkingjaeger 等。

在我们这个实例中,我们只需要实现一个简单的监控即可,因此我们可以使用 Prometheus + Grafana 的组合。

Prometheus 基本原理

但在此之前我们最好先了解一下 Prometheus 的工作原理,下面是来自 Prometheus 官网的架构图:

在这里插入图片描述

我们可以从两个角度来看这张图:组件、流程。

  1. 组件
  • Prometheus ServerPrometheus 服务端,主要负责数据的收集、存储、查询等。(上图中间部分)
  • AlertmanagerPrometheus 的告警组件,主要负责告警的发送。(上图右上角)
  • Prometheus web UI:可以在这个界面执行 PromQL,另外 Grafana 可以让我们以一种更直观的方式来查看指标数据(也是使用 PromQL)。(上图右下角)
  • exportersexportersPrometheus 的数据采集组件,主要负责从各个组件中采集数据,然后发送给 Prometheus Server。非常常见的如 node_exporter,也就是服务器基础指标的采集组件。除了 exporters,还有一种常见的数据采集方式是 Pushgateway,也就是将数据推送到 Pushgateway,然后由 Prometheus ServerPushgateway 中拉取数据。(也就是上图左边部分)
  1. 流程
  • 采集数据:也就是从 Pushgateway 或者 exporter 拉取一些指标数据。
  • 存储数据:Prometheus Server 会将采集到的数据存储到本地的 TSDB 中。
  • 查询数据:我们可以通过 web UI 或者 Grafana 来查看数据。

最后,我们可以在 Grafana 中看到如下图表:

在这里插入图片描述

通过这个图,我们就可以很直观的看到我们的系统的一些指标数据了,并且能看到这些指标随着时间的变化趋势。

Grafana 里面的图表都是一个个的 PromQL 查询出来的结果,对于常见的一些监控指标,Grafana 上可以找到很多现有的模板,直接使用即可。

Prometheus 采集的是什么数据

举一个简单的例子:对于一个运行中的系统而言,每一刻它的状态都是不太一样的,比如,可能上一秒 CPU 使用率是 10%,下一秒就变成了 100% 了,
但可能过 1 秒又降低到了 10%。当我们的系统出性能问题的时候,我们就需要去分析这些指标数据,找到问题所在。
比如排查一下出现性能问题的那个时间点,CPU 使用率是不是很高,如果是的话,那么就有可能是 CPU 导致的性能问题。

Prometheus 的作用就是帮助我们采集这些指标数据,然后存储起来,等待某天我们需要分析的时候,再去查询这些数据。
又或者监控到指标有异常的时候,可以通过 Alertmanager 来发送告警。

Prometheus 采集数据频率

Prometheus 采集数据的频率是可以配置的,我们一般配置为 1 分钟采集一次。
也就是说,每隔 1 分钟,Prometheus 才会从 exporter 拉取一次数据,然后存储起来。

应用指标数据采集

对于我们的应用而言,往往也有一些指标可以帮助我们看到应用内部的状态,比如:应用内的线程数、应用占用的内存、应用的 QPS 等等。
但是对于应用指标的监控,并没有一个统一的标准,我们需要根据自己应用的实际情况来决定采集哪些指标。

我们的消息推送系统如何做监控

应用指标

对于我们的消息推送系统而言,目前采集以下这两个重要指即可:

  1. 连接数:可以了解服务器当前负载

连接数我们可以直接通过 len(hub.clients) 来获取,非常简单。

  1. 等待推送的消息数:可以了解服务器能否及时处理消息

我们可以在 Hub 中添加一个 pending atomic.Int64 字段来记录当前等待推送的消息数,然后在 send 方法中进行更新:

func send(hub *Hub, w http.ResponseWriter, r *http.Request) {// ... 其他代码 ...hub.pending.Add(1)
}

同时在处理完成之后,我们也需要将其减 1,所以 writePump 也需要进行修改:

func (c *Client) writePump() {for {select {case messageLog, ok := <-c.send:c.conn.SetWriteDeadline(time.Now().Add(writeWait))if !ok {// ...c.hub.pending.Add(int64(-1 * len(c.send)))return}if err := c.conn.WriteMessage(websocket.TextMessage, []byte(messageLog.Message)); err != nil {// ...c.hub.pending.Add(int64(-1 * len(c.send)))return}}c.hub.pending.Add(int64(-1))}
}

我们在 writePump 中有三个地方需要对 pending 字段做减法:连接关闭、发送出错、发送成功。

exporter 以及 Grafana 配置

现在我们知道了我们有两个比较关键的指标需要采集,那到底是如何采集的呢?

具体来说,会有以下两步:

  1. 在消息推送系统中添加一个 /metrics 接口

这个接口的作用就是将我们的指标数据暴露出来,以便 Prometheus 采集。
它返回的就是请求时的连接数和等待推送的消息数,返回的格式也有一定要求,但也不复杂,具体来说就是:

  • 一行一个指标
  • 可以返回多个指标,多行即可
  • 每个指标前一行指定其类型(TYPE
  • 每行的格式为:<指标名称>{<标签名称>=<标签值>, ...} <指标值>

下面是一个简单的例子:

# HELP http_requests_total The total number of HTTP requests.
# TYPE http_requests_total counter
http_requests_total{method="GET", endpoint="/api"} 100
http_requests_total{method="POST", endpoint="/api"} 50

在这个示例中:

  • http_requests_total 是指标名称
  • {method="GET", endpoint="/api"} 是标签集合,用于唯一标识两个不同的时间序列。
  • 10050 是样本值,表示在特定时间点上的 HTTP 请求总数。

最终,我们得到了一个如下的 /metrics 接口:

func metrics(hub *Hub, w http.ResponseWriter, r *http.Request) {var pending = hub.pending.Load()var connections = len(hub.clients)w.Write([]byte(fmt.Sprintf("# HELP connections 连接数\n# TYPE connections gauge\nconnections %d\n", connections)))w.Write([]byte(fmt.Sprintf("# HELP pending 等待发送的消息数量\n# TYPE pending gauge\npending %d\n", pending)))
}

不要忘记了在 main 中加上一个入口:

http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {metrics(hub, w, r)
})

最终,这个接口会返回如下的数据:

# HELP connections 连接数
# TYPE connections gauge
connections 0
# HELP pending 等待发送的消息数量
# TYPE pending gauge
pending 0
  1. Prometheus 中配置 exporter

我们需要在 Prometheus 配置文件中加上以下配置:

scrape_configs:# 拉取我们的应用指标- job_name: 'websocket'static_configs:- targets: ['192.168.2.107:8181']

注意:这里不需要在后面加上 /metrics,因为 Prometheus 默认就是去拉取 /metrics 接口的。

在这里插入图片描述

然后我们就可以在 Prometheusweb UI 中看到我们的指标数据了。

  1. Grafana 中配置图表

最后,我们可以在 Grafana 中配置一个图表,来展示我们的指标数据:

在这里插入图片描述

这样,我们就可以看到一个等待发送的消息数量以及连接数的变化了。

总结

最后,再来简单回顾一下本文所讲内容,主要包括以下几个方面:

  • 认证方式是另外一个比较复杂的话题,但是我们依然可以为其预留出一个扩展点,先实现其他功能后再来完善。
  • 目前市面上有很多监控相关的组件,本文使用了 Prometheus 作为例子来演示如何在项目中采集应用的指标数据,以及如何通过 Grafana 来展示这些指标的变化。
  • Prometheus 中包含了 ``Prometheus Serverexporters等组件,其中Server是实际存储数据的地方,而exporters` 是用来采集指标数据的程序。
  • Prometheus 采集到的数据,我们可以通过 Grafana 来进行可视化展示,更加的直观。
  • 应用中,也可以暴露一个 /metrics 端口来返回应用当前的一些状态,只要遵循 Prometheus 的规范即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/635114.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

读元宇宙改变一切笔记10_支付方式

1. 元宇宙中的经济 1.1. 元宇宙被设想为一个平行世界&#xff0c;人们将在那里花大量的时间工作和生活 1.1.1. 元宇宙的实现程度部分取决于它是否建立在一个繁荣的经济体系之上 1.2. 元宇宙中的经济将大体遵循现实世界的经济模式 1.2.1. 激烈的竞争、大量营利性企业的存在、…

无偿分享一个很有用的看源码小技巧

怎么在 idea 里面查看 git 提交记录呢&#xff1f;这个界面是藏在哪里的呢&#xff0c;我的 idea 里面怎么没有呢&#xff1f; 好的&#xff0c;是我疏忽了&#xff0c;我先入为主的认为这个大家应该都知道是怎么来的。 但是确实是有一些同学是不太清楚的&#xff0c;那我这篇…

Docker技巧汇总

Docker技巧汇总 前言使用流程安装配置镜像管理创建并运行容器使用容器/常用命令导出和导入查看元数据挂载数据卷端口映射/转发VS Code连接Docker 前言 Docker 是一个开源的应用容器引擎&#xff0c;可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xf…

AI相关资料

文心一格收费,有免费额度 通义万相_AI创意作画_AI绘画_人工智能-阿里云 AI AIchatOS

254:vue+openlayers 加载HERE多种形式地图(v3软件版本)

第254个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers中添加HERE地图(v3版本的软件),并且含多种的表现形式。包括地图类型,文字标记的设置、语言的选择、PPI的设定。 v3版本和v2版本有很大的区别,关键是引用方法上,请参考文章尾部的API链接。 直接复制…

C 语言->编译和链接实现原理

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;橘橙黄又青-CSDN博客 今天学习&#xff1a;浅学编译和链接内部实现原理 前提&#xff1a;本文是在gcc编译环…

SpringBoot连接远程服务器redis

SpringBoot连接远程服务器redis 1、指定redis配置启动 进入redis安装地址&#xff0c;我这里安装的是 /usr/local/src/redis-6.2.6 先copy一份配置文件 cp redis.conf redis.conf.bck然后修改配置文件信息 vim redis.conf bind 0.0.0.0 # 守护进程&#xff0c;修改为yes后即可…

Verilog基础:强度建模(一)

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 一、强度建模基础 Verilog HDL提供了针对线网信号0、1、x、z的精准强度建模方式&#xff0c;这样可以允许将两个线网信号进行线与操作从而更加精确地描述出硬件行…

接口的返回值中所需信息作为其他接口入参使用(postman与jmeter的使用)

一、背景&#xff1a; 偶尔会用到一个场景&#xff0c;两个接口之前的调用有依赖关系&#xff0c;将其中一个的返回参数中的部分信息取出来作为入参在第二个接口中使用&#xff0c;代码内是比较好实现&#xff0c;只要定义一个变量&#xff0c;用于参数传递。 如果是测试过程中…

令牌桶算法与Guava的实现RateLimiter源码分析

令牌桶算法与Guava的实现RateLimiter源码分析 令牌桶RateLimiter简介RateLimiter使用示例导入maven依赖编写测试代码 RateLimiter的实现源码解析SmoothRateLimiterSmoothBursty恒速获取令牌acquire(int)tryAcquire(int,long,TimeUnit) 存量桶系数小结 优缺点与漏桶的区别总结 令…

Ontrack EasyRecovery2024恢复软件最新版本有哪些新功能特色?

Ontrack EasyRecovery 16是由Ontrack官方最新出品的一款全面的自助数据恢复软件&#xff0c;中文名称叫做&#xff1a;易恢复。它能够轻松恢复所有的文件类型&#xff0c;包括文档、表格、图片、音视频和其他文件等&#xff0c;支持恢复不同存储介质数据&#xff1a;硬盘、光盘…

关于C#中的LINQ的延迟执行

简介 Linq中的绝大多数查询运算符都有延迟执行的特性,查询并不是在查询创建的时候执行,而是在遍历的时候执行 实例&#xff1a; public void Test2(){List<int> items new List<int>() { -1, 1, 3, 5 };IEnumerable<int> items2 items.Where(x > x &g…

java数据结构与算法刷题-----LeetCode209. 长度最小的子数组

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 解题思路 代码:时间复杂度O(n).空间复杂度O(1) class Solution {public in…

【数据结构与算法】1.时间复杂度和空间复杂度

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…

【论文阅读】GPT4Graph: Can Large Language Models Understand Graph Structured Data?

文章目录 0、基本介绍1、研究动机2、准备2.1、图挖掘任务2.2、图描述语言&#xff08;GDL&#xff09; 3、使用LLM进行图理解流程3.1、手动提示3.2、自提示 4、图理解基准4.1、结构理解任务4.1、语义理解任务 5、数据搜集5.1、结构理解任务5.2、语义理解任务 6、实验6.1、实验设…

史上最全EasyExcel

一、EasyExcel介绍 1、数据导入&#xff1a;减轻录入工作量 2、数据导出&#xff1a;统计信息归档 3、数据传输&#xff1a;异构系统之间数据传输 二、EasyExcel特点 Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内…

以后要做GIS开发的话是学GIS专业还是学计算机专业好一些?

GIS开发其实严格来说分为前后端以及底层开发。不同的方向&#xff0c;代表了不同的开发语言。 所以大家首先要了解自己具体要做的岗位类型是什么&#xff0c;其次才是选择专业侧重点。 但是严格来说&#xff0c;选择某个专业&#xff0c;到就业方向这个过程&#xff0c;并不是…

el-table样式错乱解决方案

bug&#xff1a; 图片的椭圆框住的地方&#xff0c;在页面放大缩小之后就对不齐了。 原因&#xff1a; 主要原因是当你对页面放大缩小的时候&#xff0c;页面进行了重构&#xff0c;页面的宽高及样式进行了变化&#xff0c;但是在这个更新的过程中&#xff0c;table的反应并没…

Redis: Redis介绍

文章目录 一、redis介绍二、通用的命令三、数据结构1、字符串类型&#xff08;String&#xff09;&#xff08;1&#xff09;介绍&#xff08;2&#xff09;常用命令&#xff08;3&#xff09;数据结构 2、列表&#xff08;List&#xff09;&#xff08;1&#xff09;介绍&…

python实操之网络爬虫介绍

一、什么是网络爬虫 网络爬虫&#xff0c;也可以叫做网络数据采集更容易理解。它是指通过编程向网络服务器&#xff08;web&#xff09;请求数据&#xff08;HTML表单&#xff09;&#xff0c;然后解析HTML&#xff0c;提取出自己想要的数据。 它包括了根据url获取HTML数据、解…