Prometheus告警带图完美解决方案
需求背景
告警分析处理流程
通常我们收到 Prometheus 告警事件通知后,往往都需要登录 Alertmanager 页面查看当前激活的告警,如果需要分析告警历史数据信息,还需要登录 Prometheus 页面的在 Alerts 中查询告警 promQL 表达式,然后复制到 Graph 中查询数据。
这样做无疑大大降低了故障分析和处理流程,此时有些聪明的小伙伴就在想,能不能在告警事件推送的时候,将这些历史数据绘制成图表一块推送过来,方便我们第一时间了解指标变化趋势,便于更快的处理问题。
功能需求分析
- 可靠性要求:无论告警图表数据是否能获取到,都不能影响告警信息的及时推送。(这是 webhook 最核心的功能,告警带图只是锦上添花,不可本末倒置,不能因为获取图表失败而导致告警事件无法正常推送。)
- 数据折线图显示:希望可以查询指定时间范围内数据变化趋势,便于分析排查问题,这是最核心的功能。
- 只显示异常 instance 数据:每个 job 都会对应很多 targets,我们只希望显示异常 instance 相关的数据,避免整个图表因数据过多导致显示混乱。
- 最大值最小值最近值显示:这三个值是我们最关注的信息,尤其是最近值,在告警触发时还是告警恢复后,我们都需要知道这个指标当前值是多少。
- 时间范围灵活:有些告警比较灵敏,for 时间会较短,通常不到 1 分钟(例如网络探针检测 http 状态码)。而有些告警比较迟钝,for 时间会设置的比较长,通常以为小时为单位(例如证书有效期监控)。而此时告警图表时间范围不应该统一设置为某个指定的值,而是根据 for 配置的时间灵活变化。
实现思路与效果
实现效果
废话不多说,先展示一下最终的告警效果。
grafana 渲染指标图:
方案对比分析
针对这个需求,目前主流的实现方案主要有以下两种。
方案 1:开发程序,查询 prometheus 指标数据,然后使用图表库渲染出图片,以 python 为例,可以使用 Matplotlib 或 Pandas 实现 。
优点:实现起来较为灵活,可以很好的满足各种功能需求。
缺点:图表样式单一,如果需要修改图表样式需要变更代码。
方案 2:使用 grafana-image-renderer插件渲染图片。
优点:图表样式美观,图表配置灵活方便。
缺点:需要指定 dashboard 和 panel 的 id 才能渲染,也就意味着每个告警规则都需要创建一个图表,工作量巨大。
实现思路
既然两种方案都各有优缺点,能否将两种方案的优点整合起来,既要配置实现起来灵活方便又要图表样式美观且易于配置呢?毕竟只有小孩子才做选择题,大人都是全都要。
具体实现思路如下:
- 收到 alertmanager 推送告警数据
- 根据告警数据,提取告警标题和异常的 instance 。
- 根据告警标题请求 prometheus 的 rules 接口 ,获取 for 和表达式配置。接口文档:https://prometheus.ac.cn/docs/prometheus/latest/querying/api/#rules
- 正则处理表达式,去除条件判断值,并新增 instance 标签选择,例如原始的告警规则表达式为,处理后的查询语句为
- 请求 grafana 的 api 接口,获取 grafana dashboard 配置。接口文档:https://grafana.com/docs/grafana/latest/developers/http_api/dashboard/
- 根据获取到的 dashboard 配置数据,替换表达式,时间范围, 告警标题。
- 再次 post 请求 grafana 的 api 接口,更新 dashboard 配置。
- 调用grafana-image-renderer插件,传入 dashboard 和 panels 参数,渲染图片并下载到本地。
- 将本地图片推送至企业微信、钉钉或 teams(也可上传至公有云对象存储,直接返回图片公网 url 地址)。
流程图如下:
核心代码与配置
grafana 配置
- 创建 api key 用于调用 api 接口请求 grafana。
- 创建 dashboard 和图表,并配置图表样式。
表达式、标题、时间范围可以随便写,主要是为了调试图表配置。
- 安装grafana-image-renderer 插件。
参考文档:https://grafana.com/grafana/plugins/grafana-image-renderer/。安装完成后点击渲染图像验证是否可以正常渲染。
webhook 程序
核心代码如下,需要注意的是为了确保可以正常获取图片和推送消息,建议添加重试和异常处理逻辑,增加程序可靠性。
import base64
import hashlib
import json
import re
import time
import os
import httpx
from log import logger
from config import vx_conf, grafana_conf, alertmanager_conf, prometheus_conf, aliyun_conf
from oss2 import Auth, Bucket, exceptionsclass VxRobot:"""企业微信推送"""def __init__(self, team, max_retries=3, backoff_factor=2):self.url = vx_conf[team]self.headers = {'Content-Type': 'application/json'}self.params = {}self.max_retries = max_retriesself.backoff_factor = backoff_factordef __send_data(self):"""推送企业微信数据:return:"""retries = 0with httpx.Client(verify=False) as client:while retries < self.max_retries:try:# 发送 POST 请求response = client.post(self.url, headers=self.headers, json=self.params, timeout=10)response.raise_for_status() # 如果状态码不是 2xx,会抛出异常if response.json()['errcode'] == 0:logger.info("企业微信告警推送完成")returnelse:logger.error(f"企业微信告警推送内容有误: {response.json()['errmsg']}")breakexcept (httpx.RequestError, httpx.HTTPStatusError) as exc:retries += 1logger.error(f"企业微信告警推送失败 (尝试 {retries}/{self.max_retries}): {exc}")if retries < self.max_retries:sleep_time = self.backoff_factor * retrieslogger.info(f"等待 {sleep_time} 秒后重试...")time.sleep(sleep_time) # 实现退避策略else:logger.error("达到最大重试次数,企业微信告警推送失败")raise Exception(f"无法使用企业微信告警推送: {exc}")def send_img(self, image_path):"""推送图片信息:param image_path:图片路径:return: None"""try:# 读取图片文件内容with open(image_path, "rb") as f:image_data = f.read()# 计算图片内容的 MD5 值md5_hash = hashlib.md5(image_data).hexdigest()# 生成图片内容的 Base64 编码base64_encoded = base64.b64encode(image_data).decode("utf-8")except FileNotFoundError:raise FileNotFoundError(f"图片文件 {image_path} 未找到")except Exception as e:raise RuntimeError(f"处理图片时发生错误: {str(e)}")params = {"msgtype": "image","image": {"base64": "DATA","md5": "MD5"}}# 数据替换params["image"]["base64"] = base64_encodedparams["image"]["md5"] = md5_hashlogger.info("推送企业微信图片内容,图片地址为%s" % image_path)self.params = paramsself.__send_data()def get_panel(alert_name, instance):"""获取指标图表信息:return:"""prometheus = PrometheusTools()config = prometheus.get_alert_config(alert_name)config['instance'] = instanceconfig['time_range'] = 'now-' + str(2 * config['duration']) + 's'if 2 * config['duration'] > 60:config['alert_name'] = config['alert_name'] + '(最近' + str(round(2 * config['duration'] / 60)) + '分钟)'else:config['alert_name'] = config['alert_name'] + '(最近' + str(round(2 * config['duration'])) + '秒)'grafana = GrafanaTools()dashboard_data = grafana.fetch_dashboard()dashboard_conf = grafana.update_panel_query(dashboard_data, config)grafana.update_dashboard(dashboard_conf)local_img_path = grafana.render_image(config['alert_name'])return local_img_path# aliyun_oss = AliyunTools()# oss_path = local_img_path.replace("img/", "alert_img/")# aliyun_oss.upload_image(local_img_path, oss_path)class PrometheusTools:def __init__(self, max_retries=3, backoff_factor=2):"""初始化 Prometheus 客户端"""self.url = prometheus_conf['url']self.username = prometheus_conf['username']self.password = prometheus_conf['password']self.max_retries = max_retriesself.backoff_factor = backoff_factor# 生成认证头auth_str = f"{self.username}:{self.password}"auth_bytes = base64.b64encode(auth_str.encode("utf-8")).decode("utf-8")self.headers = {"Authorization": f"Basic {auth_bytes}"}def get_alert_config(self, alert_name):"""获取告警配置:param alert_name: 告警名称:return: 告警配置"""url = f"{self.url}/api/v1/rules"params = {"rule_name[]": alert_name}retries = 0with httpx.Client(verify=False) as client:while retries < self.max_retries:try:# 发起请求response = client.get(url, params=params, headers=self.headers, timeout=10)response.raise_for_status() # 如果状态码不是 2xx,会抛出异常logger.info("成功获取 Prometheus 告警数据")logger.debug(json.dumps(response.json()))# 提取关键数据config = {'alert_name': response.json()['data']['groups'][0]['rules'][0]['name'],'query': response.json()['data']['groups'][0]['rules'][0]['query'],'duration': response.json()['data']['groups'][0]['rules'][0]['duration']}logger.debug(config)return configexcept (httpx.RequestError, httpx.HTTPStatusError) as exc:logger.error(f"Attempt {retries + 1} failed: {exc}")retries += 1if retries < self.max_retries:time.sleep(self.backoff_factor * retries) # 退避策略else:raise Exception(f"Failed to fetch alert config after {self.max_retries} retries.")class GrafanaTools:"""grafana渲染图表工具"""def __init__(self, max_retries=3, backoff_factor=2):self.grafana_url = grafana_conf['url']self.api_key = grafana_conf['api_key']self.dashboard_uid = grafana_conf['dashboard_id']self.panel_id = grafana_conf['panel_id']self.headers = {"Authorization": f"Bearer {self.api_key}","Content-Type": "application/json",}self.max_retries = max_retriesself.backoff_factor = backoff_factordef fetch_dashboard(self):"""获取仪表盘的完整配置:return: 仪表盘的完整配置"""url = f"{self.grafana_url}/api/dashboards/uid/{self.dashboard_uid}"logger.info("开始获取dashboard配置, url: %s" % url)retries = 0with httpx.Client(verify=False) as client:while retries < self.max_retries:try:# 发送请求response = client.get(url, headers=self.headers, timeout=10)response.raise_for_status() # 如果状态码不是 2xx,会抛出异常logger.info("成功获取dashboard配置")logger.debug(json.dumps(response.json(), indent=2)) # 打印详细配置return response.json()except (httpx.RequestError, httpx.HTTPStatusError) as exc:retries += 1logger.error(f"请求失败 (尝试 {retries}/{self.max_retries}): {exc}")if retries < self.max_retries:sleep_time = self.backoff_factor * retrieslogger.info(f"等待 {sleep_time} 秒后重试...")time.sleep(sleep_time) # 实现退避策略else:logger.error("达到最大重试次数,获取dashboard配置失败")raise Exception(f"无法获取仪表盘配置: {exc}")def update_panel_query(self, dashboard_data, config):"""修改grafana dashboard配置内容:param dashboard_data: dashboard配置:param config: 告警配置:return: 新的grafana dashboard配置"""# logger.error(config)for panel in dashboard_data["dashboard"]["panels"]:if panel["id"] == self.panel_id:# 修改面板查询,动态添加 instance 变量panel["title"] = config['alert_name']instance = config['instance']query = config['query']# 去除规则条件query_prom = re.sub(r'\s*(==|!=|>=|<=|>|<)\s*([0-9]+(?:\.[0-9]+)?)$', '', query)# 正则表达式,匹配 Prometheus 查询语句中的指标名称pattern = r'([a-z_][a-z0-9_]*)(?=\s*(\{|\[|$))'# 查找匹配的指标名称match = re.search(pattern, query_prom)# 如果找到了匹配项if match and instance != "none":# 获取指标名称的结束位置end_pos = match.end(1) # end(1) 获取捕获组1(指标名称)的结束位置# 判断原始指标是否存在标签选择if '{' in query_prom:query_cleaned = query_prom[:end_pos + 1] + 'instance="' + instance + '",' + query_prom[end_pos + 1:]else:query_cleaned = query_prom[:end_pos] + '{instance="' + instance + '"}' + query_prom[end_pos:]else:logger.error("指标匹配失败,使用默认指标%s" % query_prom)query_cleaned = query_prom# logger.error(query_cleaned)panel["targets"][0]["expr"] = query_cleanedbreaklogger.info("dashboard配置替换完成")logger.debug(json.dumps(dashboard_data))return dashboard_datadef update_dashboard(self, dashboard):"""更新仪表盘配置到 Grafana:param dashboard: 要更新的仪表盘配置 (dict):return: None"""url = f"{self.grafana_url}/api/dashboards/db"logger.info("开始更新Dashboard配置, url: %s" % url)retries = 0with httpx.Client(verify=False) as client:while retries < self.max_retries:try:# 发送 POST 请求response = client.post(url, headers=self.headers, json=dashboard, timeout=10)response.raise_for_status() # 如果状态码不是 2xx,会抛出异常logger.info("Dashboard更新完成")returnexcept (httpx.RequestError, httpx.HTTPStatusError) as exc:retries += 1logger.error(f"更新Dashboard失败 (尝试 {retries}/{self.max_retries}): {exc}")if retries < self.max_retries:sleep_time = self.backoff_factor * retrieslogger.info(f"等待 {sleep_time} 秒后重试...")time.sleep(sleep_time) # 实现退避策略else:logger.error("达到最大重试次数,Dashboard更新失败")raise Exception(f"无法更新仪表盘配置: {exc}")def render_image(self, alert_name):"""调用 Grafana 渲染图片 API:param alert_name: 告警标题:return: 图片保存路径"""output_path = "img/" + alert_name + ".png"render_url = (f"{self.grafana_url}/render/d-solo/{self.dashboard_uid}"f"?orgId=1&panelId={self.panel_id}&width=2000&height=1000")logger.info("开始渲染图片, render_url: %s" % render_url)retries = 0with httpx.Client(verify=False) as client:while retries < self.max_retries:try:# 发起请求response = client.get(render_url, headers=self.headers, timeout=20)response.raise_for_status() # 如果状态码不是 2xx,会抛出异常# 确保输出目录存在os.makedirs(os.path.dirname(output_path), exist_ok=True)with open(output_path, "wb") as f:f.write(response.content)logger.info(f"告警图片下载至 {output_path}")return output_pathexcept (httpx.RequestError, httpx.HTTPStatusError) as exc:retries += 1logger.error(f"渲染图片失败 (尝试 {retries}/{self.max_retries}): {exc}")if retries < self.max_retries:sleep_time = self.backoff_factor * retrieslogger.info(f"等待 {sleep_time} 秒后重试...")time.sleep(sleep_time) # 实现退避策略else:logger.error("达到最大重试次数,渲染图片失败")raise Exception(f"无法渲染图片: {exc}")class AliyunTools:def __init__(self, max_retries=3, backoff_factor=2):self.auth = Auth(aliyun_conf['access_key_id'], aliyun_conf['access_key_secret'])self.bucket = Bucket(self.auth, aliyun_conf['endpoint'], aliyun_conf['bucket_name'])self.bucket_name = aliyun_conf['bucket_name']self.endpoint = aliyun_conf['endpoint']self.max_retries = max_retriesself.backoff_factor = backoff_factordef upload_image(self, local_image_path, oss_object_name):"""上传图片到阿里云 OSS:param local_image_path: 本地图片路径:param oss_object_name: OSS 中的对象名 (路径/文件名):return: 图片的公开访问地址"""if not os.path.exists(local_image_path):raise FileNotFoundError(f"本地文件 {local_image_path} 不存在")retries = 0while retries < self.max_retries:try:logger.info(f"开始上传图片 {local_image_path} 到 OSS,目标对象名: {oss_object_name}")# 上传图片到 OSSself.bucket.put_object_from_file(oss_object_name, local_image_path)# 获取图片访问地址image_url = self.bucket.sign_url('GET', oss_object_name, 60 * 60 * 24)logger.info(f"图片成功上传至 OSS,访问地址: {image_url}")return image_urlexcept (exceptions.RequestError, exceptions.ServerError) as exc:retries += 1logger.error(f"上传图片到 OSS 失败 (尝试 {retries}/{self.max_retries}): {exc}")if retries < self.max_retries:sleep_time = self.backoff_factor * retrieslogger.info(f"等待 {sleep_time} 秒后重试...")time.sleep(sleep_time)else:logger.error("达到最大重试次数,上传图片失败")raise Exception(f"无法上传图片到 OSS: {exc}")
查看更多
崔亮的博客-专注devops自动化运维,传播优秀it运维技术文章。更多原创运维开发相关文章,欢迎访问https://www.cuiliangblog.cn
相关文章:
Prometheus告警带图完美解决方案
需求背景 告警分析处理流程 通常我们收到 Prometheus 告警事件通知后,往往都需要登录 Alertmanager 页面查看当前激活的告警,如果需要分析告警历史数据信息,还需要登录 Prometheus 页面的在 Alerts 中查询告警 promQL 表达式,然…...
深度学习模型:循环神经网络(RNN)
一、引言 在深度学习的浩瀚海洋里,循环神经网络(RNN)宛如一颗独特的明珠,专门用于剖析序列数据,如文本、语音、时间序列等。无论是预测股票走势,还是理解自然语言,RNN 都发挥着举足轻重的作用。…...
分布式在线评测系统
OnlineJudge 前言所用技术开发环境 1. 需求分析2. 项目宏观结构3. compile_server服务设计3.1 compiler服务设计3.2 runner服务设计3.3 compile_run3.4 compile_server.cpp 4. oj_server服务设计4.1 model设计4.2 view设计4.3 control设计4.3.1 获取题目列表功能4.3.2 获取单个…...
Unity中动态生成贴图并保存成png图片实现
实现原理: 要生成长x宽y的贴图,就是生成x*y个像素填充到贴图中,如下图: 如果要改变局部颜色,就是从x1到x2(x1<x2),y1到y2(y1<y2)这个范围做处理, 或者要想做圆形就是计算距某个点(x1,y1&…...
鸿蒙多线程开发——sendable共享容器
1、异步锁机制 在介绍共享容器之前,先介绍异步锁机制。 为了解决多线程并发任务间的数据竞争问题,ArkTS引入了异步锁能力。异步锁可能会被类对象持有,因此为了更方便地在并发实例间获取同一个异步锁对象,AsyncLock对象支持跨线程…...
五天SpringCloud计划——DAY1之mybatis-plus的使用
一、引言 咱也不知道为啥SpringCloud课程会先教mybatis-plus的使用,但是教都教了,就学了吧,学完之后觉得mybatis-plus中的一些方法还是很好用了,本文作为我学习mybatis-plus的总结提升,希望大家看完之后也可以熟悉myba…...
Vue.js基础——贼简单易懂!!(响应式 ref 和 reactive、v-on、v-show 和 v-if、v-for、v-bind)
Vue.js是一个渐进式JavaScript框架,用于构建用户界面。它专门设计用于Web应用程序,并专注于视图层。Vue允许开发人员创建可重用的组件,并轻松管理状态和数据绑定。它还提供了一个虚拟DOM系统,用于高效地渲染和重新渲染组件。Vue以…...
警钟长鸣,防微杜渐,遨游防爆手机如何护航安全生产?
近年来,携非防爆手机进入危险作业区引发爆炸的新闻屡见报端。2019年山西某化工公司火灾,2018年延安某煤业瓦斯爆炸,均因工人未用防爆手机产生静电打火引发。涉爆行业领域企业量大面广,相当一部分企业作业场所人员密集,…...
中国科学院大学研究生学术英语读写教程 Unit7 Materials Science TextA 原文和翻译
中国科学院大学研究生学术英语读写教程 Unit7 Materials Science TextA 原文和翻译 Why Is the Story of Materials Really the Story of Civilisation? 为什么材料的故事实际上就是文明的故事? Mark Miodownik 1 Everything is made of something. Take away co…...
win10中使用ffmpeg和MediaMTX 推流rtsp视频
在win10上测试下ffmpeg推流rtsp视频,需要同时用到流媒体服务器MediaMTX 。ffmpeg推流到流媒体服务器MediaMTX ,其他客户端从流媒体服务器拉流。 步骤如下: 1 下载MediaMTX github: Release v1.9.3 bluenviron/mediamtx GitHub…...
代码美学2:MATLAB制作渐变色
效果: %代码美学:MATLAB制作渐变色 % 创建一个10x10的矩阵来表示热力图的数据 data reshape(1:100, [10, 10]);% 创建热力图 figure; imagesc(data);% 设置颜色映射为“cool” colormap(cool);% 在热力图上添加边框 axis on; grid on;% 设置热力图的颜色…...
gitlab:使用脚本批量下载项目,实现全项目检索
目的 当需要知道gitlab中所有项目是否存在某段代码时,gitlab免费版只提供了当个项目内的检索,当项目过多时一个个查太过繁琐。下面通过 GitLab API 将指定 Group 下的所有项目克隆到本地。此脚本会自动获取项目列表并逐一克隆它们,再在本地进…...
大型语言模型LLM - Finetuning vs Prompting
资料来自台湾大学李宏毅教授机器学课程ML 2023 Spring,如有侵权请通知下架 台大机器学课程ML 2023 Springhttps://speech.ee.ntu.edu.tw/~hylee/ml/2023-spring.php2023/3/10 课程 機器如何生成文句 内容概要 主要探讨了大型语言模型的两种不同期待及其导致的两类…...
【Python中while循环】
一、深拷贝、浅拷贝 1、需求 1)拷贝原列表产生一个新列表 2)想让两个列表完全独立开(针对改操作,读的操作不改变) 要满足上述的条件,只能使用深拷贝 2、如何拷贝列表 1)直接赋值 # 定义一个…...
Selenium 包介绍
诸神缄默不语-个人CSDN博文目录 Selenium 是一个强大的工具,主要用于自动化 Web 浏览器的操作。它支持多种编程语言(如 Python、Java、C# 等)和主流浏览器(如 Chrome、Firefox、Safari、Edge 等),广泛应用…...
量化交易系统开发-实时行情自动化交易-4.4.做市策略
19年创业做过一年的量化交易但没有成功,作为交易系统的开发人员积累了一些经验,最近想重新研究交易系统,一边整理一边写出来一些思考供大家参考,也希望跟做量化的朋友有更多的交流和合作。 接下来继续说说做市策略原理。 做市策…...
C++设计模式(单例模式)
一、介绍 1.动机 在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率。 如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例? 这应该是类设计者的…...
图的深度优先搜索算法DFS
深度优先搜索(DFS)就是一种寻找图中各个顶点的方法。想象一下,如果你在一个迷宫里探险,你会怎么做呢?你可能会选择一直走到尽头,直到找不到路为止,然后再回过头来试试其他的路,这就是…...
自动泊车“哐哐撞大墙”,小米SU7智驾功能bug缠身?
文/王俣祺 导语:小米SU7,自带热度与科技光环的“流量神车”,近日却以一种极为“狼狈”的方式闯入大众视野。多达70余辆小米SU7陷入“泊车魔咒”,瞬间在网络上炸开了锅。从“科技控”到“惹祸精”的背后,究竟藏着怎样的…...
Linux宝塔部署wordpress网站更换服务器IP后无法访问管理后台和打开网站页面显示错乱
一、背景: wordpress网站搬家,更换服务器IP后,如果没有域名时,使用服务器IP地址无法访问管理后台和打开网站页面显示错乱。 二、解决方法如下: 1.wordpress搬家后,在新服务器上,新建站点时&am…...
Http文件上传
方式一:HttpClient public static String uploadFile(String url, Map<String, FileWrapper> fileParam, Map<String,String> otherParam){long start System.currentTimeMillis();log.info("uploadFile url: {}.",url);HttpClient client …...
哈希C++
文章目录 一.哈希的概念1.直接定址法2.负载因子 二.哈希函数1.除法散列法 / 除留余数法2.乘法散列法3.全域散列法(了解) 三.处理哈希冲突哈希冲突:1.开放定址法(1)线性探测:(2)二次探…...
C++11(中)
C11(中) 1.可变参数模板1.1.使用场景 2.lambda表达式(重要)2.1.使用说明2.2.函数对象与lambda表达式 3.线程库3.1.thread3.2.atomic原子库操作3.3.mutex3.3.1.mutex的种类3.3.2.lock_guard3.3.3.unique_lock 🌟&#x…...
vim 如何高亮/取消高亮
高亮 :在ESC模式下使用 shift # 取消高亮:在ESC模式下输入英文输入 :nohl (no highlight)...
C#中面试的常见问题008
1.内存泄露 内存泄露的原因: 未释放动态分配的内存:在使用malloc、new等动态内存分配函数后,未能正确释放内存。引用计数错误:在引用计数管理内存的语言中,增加引用计数但未相应减少,导致内存无法释放。循…...
【系统架构设计师】真题论文: 论数据访问层设计技术及其应用(包括解题思路和素材)
更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 真题题目(2016年 试题3)解题思路论文素材参考(1)数据访问层设计 JDBC 技术(2)ORM 框架技术 - Hibernate(3)ORM 框架技术 - MyBatis(4)数据访问层设计模式 - DAO 模式(5)数据访问层设计模式 - Repositor…...
力扣整理版九:贪心算法(待整理)
局部最优 全局最优 局部最优可以推出全局最优 并且想不出反例 ----------------------------- (1) 455 分发饼干 (2) 1005 k次取反后最大化的数组和 (3) 860 柠檬水找零 (2) 376 摆动序列 (3) 122 买卖股票的最佳时机2 (4) 135 分发糖果 (4) 55 跳跃游戏 (5) 45 跳…...
香橙派--安装RKMPP、x264、libdrm、FFmpeg(支持rkmpp)以及opencv(支持带rkmpp的ffmpeg)(适用于RK3588平台)
1. 安装RKMPP git clone https://github.com/rockchip-linux/mppcd mpp/build/linux/aarch64./make-Makefiles.bashmake -j8sudo make installRKMPP:用于编解码测试,支持RK3588平台。 2. 安装x264 git clone https://code.videolan.org/videolan/x264…...
计算机毕业设计Python+大模型美食推荐系统 美食可视化 美食数据分析大屏 美食爬虫 美团爬虫 机器学习 大数据毕业设计 Django Vue.js
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
1138:将字符串中的小写字母转换成大写字母
【题目描述】 给定一个字符串,将其中所有的小写字母转换成大写字母。 【输入】 输入一行,包含一个字符串(长度不超过100,可能包含空格)。 【输出】 输出转换后的字符串。 【输入样例】 helloworld123Ha 【输出样例】…...
Wireshark抓取HTTPS流量技巧
一、工具准备 首先安装wireshark工具,官方链接:Wireshark Go Deep 二、环境变量配置 TLS 加密的核心是会话密钥。这些密钥由客户端和服务器协商生成,用于对通信流量进行对称加密。如果能通过 SSL/TLS 日志文件(例如包含密钥的…...
Unity UGUI原理剖析
UI最重要的两部分 UI是如何渲染出来的点击事件如何触发何时发生UI重绘 1:UI如何渲染出来的 UI渲染一定是有顶点的,没有顶点就没法确定贴图的采样,UGUI的顶点在一张Mesh上创建,经过渲染管线UI就渲染到屏幕上了,UI的渲染…...
实现Excel文件和其他文件导出为压缩包,并导入
导出 后端: PostMapping("/exportExcelData")public void exportExcelData(HttpServletRequest request, HttpServletResponse response, RequestBody ResData resData) throws IOException {List<Long> menuIds resData.getMenuIds();List<Co…...
Linux:基础开发工具
目录 软件包管理器yum 什么是软件包? 查看软件包 安装软件 卸载软件 vim vim的基本操作 gcc/g使用 预处理 编译 汇编 连接 make/Makefile .PHONY伪目标 定义使用变量 版本控制器Git 安装git git的使用 git add git commit git push git pull …...
【mac】终端左边太长处理,自定义显示名称(terminal路径显示特别长)
1、打开终端 2、步骤 (1)修改~/.zshrc文件 nano ~/.zshrc(2)添加或修改PS1,我是自定义了名字为“macminiPro” export PS1"macminiPro$ "(3)使用 nano: Ctrl o (字母…...
嵌入式硬件设计:从概念到实现的全流程
嵌入式硬件设计是现代电子技术中一个至关重要的领域,涉及从硬件架构设计到硬件调试的各个方面。它为我们日常生活中的各类智能设备、家电、工业控制系统等提供了强大的支持。本文将介绍嵌入式硬件设计的基本流程、关键技术、常用工具以及常见的挑战和解决方案&#…...
【Nginx】核心概念与安装配置解释
文章目录 1. 概述2. 核心概念2.1.Http服务器2.2.反向代理2.3. 负载均衡 3. 安装与配置3.1.安装3.2.配置文件解释3.2.1.全局配置块3.2.2.HTTP 配置块3.2.3.Server 块3.2.4.Location 块3.2.5.upstream3.2.6. mine.type文件 3.3.多虚拟主机配置 4. 总结 1. 概述 Nginx是我们常用的…...
数据库-MySQL-MybatisPlus实战
文章目录 前言一、整合mybatis-plus二、CRUD操作1、insert操作2、update操作3、delete操作 三、条件构造器(Wrapper)QueryWrapperUpdateWrapperLambdaQueryWrapperLambdaUpdateWrapper 四、分页查询五、自定义主键生成器六、总结 前言 mybatis相信都不陌生,目前互联…...
Vue2学习记录
前言 这篇笔记,是根据B站尚硅谷的Vue2网课学习整理的,用来学习的 如果有错误,还请大佬指正 Vue核心 Vue简介 Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。 它基于标准 HTML、CSS 和 JavaScr…...
thinkphp中对请求封装
请求的封装 //调用 $res Http::post($this->baseUrl . $url,$params,[CURLOPT_HTTPHEADER > [Content-Type: application/json,Content-Length: . strlen($params),],]);<?php namespace fast; /*** 字符串类*/ class Http {/*** 发送一个POST请求*/public static …...
网络安全中的数据科学如何重新定义安全实践?
组织每天处理大量数据,这些数据由各个团队和部门管理。这使得全面了解潜在威胁变得非常困难,常常导致疏忽。以前,公司依靠 FUD 方法(恐惧、不确定性和怀疑)来识别潜在攻击。然而,将数据科学集成到网络安全中…...
通过指令导入/导出vscode扩展插件
导出扩展: 打开VSCode终端: 在VSCode中,你可以通过菜单栏的“终端”选项打开终端,或者使用快捷键Ctrl (反引号,通常在键盘左上角)。运行导出命令: 在终端中,输入以下命…...
vscode添加环境变量(mujoco)
文章目录 前言一、创建.env文件二、编写setting.jason 前言 之前一直用pycharm,最近改用cursor了,在pycharm中设置环境变量修改运行配置就行了,vscode要麻烦一些,记录一下。 一、创建.env文件 以mujoco环境变量为例,…...
0-1背包问题(1):贪心算法
问题: 有 n 个物品和背包的容量,每个物品的重量为 w[i],价值为 v[i],背包的容量为 W。选若干个物品放入购物车,物品不可分割,使价值最大。 问题分析: 首先考虑贪心策略: 每次挑选…...
Qt界面篇:QMessageBox高级用法
1、演示效果 2、用法注意 2.1 设置图标 用于显示实际图标的pixmap取决于当前的GUI样式。也可以通过设置icon pixmap属性为图标设置自定义pixmap。 QMessageBox::Icon icon(...
计算机操作系统——进程控制(Linux)
进程控制 进程创建fork()函数fork() 的基本功能fork() 的基本语法fork() 的工作原理fork() 的典型使用示例fork() 的常见问题fork() 和 exec() 结合使用总结 进程终止与$进程终止的本质进程终止的情况正常退出(Exit)由于信号终止非…...
游戏引擎学习第23天
实时代码编辑功能的回顾 当前实现的实时代码编辑功能已经取得了显著的成功,表现出强大的性能和即时反馈能力。该功能允许开发者在修改代码后几乎立即看到变化在运行中的程序中体现出来,极大提升了开发效率。尽管目前的演示内容较为简单,呈现…...
0基础学java之Day25
Vector /** 知识点:Vector独有的方法 理解: * Vector在JDK1.0开始就已经存在 -- 元老级别的集合类, * 集合框架的概念是JDK1.2开始才有的, * 开发人员为了将Vector保留下来…...
android集成FFmpeg步骤以及常用命令,踩坑经历
1、入坑第一步:首先集成的库必须正确。最好是有ndk的,FFmpeg有许多个版本,我才开始接触的时候随便选了一个,一般的 方法没有问题。但是涉及到需要使用libx264等条件进行编码时,老是报错,网上搜索资料也没有人说需要ndk的支持才行。这个问题困扰了好几天,怎么试不行,最后…...
Mac——鼠标增强插件Mos
功能说明: 能够解决鼠标断续、不灵敏等鼠标问题。 下载地址: Mac——鼠标增强插件Mos...