什么?通过 Prometheus 编写巡检脚本

原文来源: https://tidb.net/blog/894d0118

背景

笔者最近在驻场,发现这里的 tidb 集群是真的多,有将近150套集群。而且集群少则6个节点起步,多则有200多个节点。在这么庞大的集群体量下,巡检就变得非常的繁琐了。

那么有没有什么办法能够代替手动巡检,并且能够快速准确的获取到集群相关信息的方法呢?答案是,有但不完全有。其实可以利用 tidb 的 Prometheus 来获取集群相关的各项数据,比如告警就是一个很好的例子。可惜了,告警只是获取了当前数据进行告警判断,而巡检需要使用一段时间的数据来作为判断的依据。而且,告警是已经达到临界值了,巡检却是要排查集群的隐患,提前开始规划,避免出现异常。

那直接用 Prometheus 获取一段时间的数据,并且把告警值改低不就行了?

认识 PromQL

要使用 Prometheus ,那必须要先了解什么是 PromQL 。

PromQL查询语言和日常使用的数据库SQL查询语言(SELECT * FROM ...)是不同的,PromQL 是一种 嵌套的函数式语言 ,就是我们要把需要查找的数据描述成一组嵌套的表达式,每个表达式都会评估为一个中间值,每个中间值都会被用作它上层表达式中的参数,而查询的最外层表达式表示你可以在表格、图形中看到的最终返回值。比如下面的查询语句:

histogram_quantile(  # 查询的根,最终结果表示一个近似分位数。0.9,  # histogram_quantile() 的第一个参数,分位数的目标值# histogram_quantile() 的第二个参数,聚合的直方图sum by(le, method, path) (# sum() 的参数,直方图过去5分钟每秒增量。rate(# rate() 的参数,过去5分钟的原始直方图序列demo_api_request_duration_seconds_bucket{job="demo"}[5m]))
)

然后还需要认识一下告警的 PromQL 中,经常出现的一些函数:

rate

用于计算变化率的最常见函数是 rate() rate() 函数用于计算在指定时间范围内计数器平均每秒的增加量。因为是计算一个时间范围内的平均值,所以我们需要在序列选择器之后添加一个范围选择器。

irate

由于使用 rate 或者 increase 函数去计算样本的平均增长速率,容易陷入长尾问题当中,其无法反应在时间窗口内样本数据的突发变化。

例如,对于主机而言在 2 分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致 CPU 占用 100%的情况,但是通过计算在时间窗口内的平均增长率却无法反应出该问题。

为了解决该问题,PromQL 提供了另外一个灵敏度更高的函数***irate(v range-vector)***。 irate 同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。

histogram_quantile

获取数据的分位数。histogram_quantile(φ scalar, b instant-vector) 函数用于计算历史数据指标一段时间内的分位数。该函数将目标分位数 (0 ≤ φ ≤ 1) 和直方图指标作为输入,就是大家平时讲的 pxx,p50 就是中位数,参数 b 一定是包含 le 这个标签的瞬时向量,不包含就无从计算分位数了,但是计算的分位数是一个预估值,并不完全准确,因为这个函数是假定每个区间内的样本分布是线性分布来计算结果值的,预估的准确度取决于 bucket 区间划分的粒度,粒度越大,准确度越低。

该部分引用: Prometheus基础相关--PromQL 基础(2) 想学习的同学可以去看看原文

修改 PromQL

要让巡检使用 PromQL ,就必须要修改告警中的 PromQL。这里需要介绍一个函数:max_over_time(range-vector),它是获取区间向量内每个指标的最大值。其实还有其他这类时间聚合函数,比如avg_over_time、min_over_time、sum_over_time等等,但是我们只需要获取到最大值,来提醒 dba 就行了。

Prometheus 是支持子查询的,它允许我们首先以指定的步长在一段时间内执行内部查询,然后根据子查询的结果计算外部查询。子查询的表示方式类似于区间向量的持续时间,但需要冒号后添加了一个额外的步长参数:***[<duration>:<resolution>]***。

举个例子:

# 原版
sum(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance)
​
# 修改
max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance)[24h:1m])

这是获取 TiKV raftstore 线程池 CPU 使用率的告警项。原版是直接将1分钟内所有线程的变化率相加,而笔者的修改版是将1分钟内所有线程的使用率取平均值,并且从此刻向后倒24小时内,每一分钟执行一次获取平均线程使用率的查询,再取最大值。

也就是说,从24小时前,到现在,每分钟执行一次(步长为1分钟): avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance) ,并获取其中最大的一次值。这样就满足了我们需要使用一段时间的数据来判断集群是否有风险的依据了。

然后我们可以选取合适的 PromQL 来加上时间聚合函数和查询时间及步长信息:

# TiKV 1
'TiDB.tikv.TiKV_server_is_down': {'pql': 'probe_success{group="tikv",instance=~".*"} == 0','pql_max': '','note': 'TiKV 服务不可用'
},
'TiDB.tikv.TiKV_node_restart': {'pql': 'changes(process_start_time_seconds{job="tikv",instance=~".*"}[24h])> 0','pql_max': 'max(changes(process_start_time_seconds{job="tikv",instance=~".*"}[24h]))','note': 'TiKV 服务5分钟内出现重启'
},
'TiDB.tikv.TiKV_GC_can_not_work': {'pql_max': '','pql': 'sum(increase(tikv_gcworker_gc_tasks_vec{task="gc", instance=~".*"}[2d])) by (instance) < 1 and (sum(increase(''tikv_gc_compaction_filter_perform{instance=~".*"}[2d])) by (instance) < 1 and sum(increase(''tikv_engine_event_total{cf="write",db="kv",type="compaction",instance=~".*"}[2d])) by (instance) >= 1)','note': 'TiKV 服务GC无法工作'
},
# TiKV 2
'TiDB.tikv.TiKV_raftstore_thread_cpu_seconds_total': {'pql_max': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance)[24h:1m])','pql': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance)[24h:1m])  > 0.8','note': 'TiKV raftstore 线程池 CPU 使用率过高'
},
'TiDB.tikv.TiKV_approximate_region_size': {'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance=~".*"}[1m])) ''by (le,instance))[24h:1m])','pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance=~".*"}[1m])) ''by (le,instance))[24h:1m]) > 1073741824','note': 'TiKV split checker 扫描到的最大的 Region approximate size 大于 1 GB'
},
'TiDB.tikv.TiKV_async_request_write_duration_seconds': {'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket''{type="write", instance=~".*"}[1m])) by (le, instance, type))[24h:1m])','pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket''{type="write", instance=~".*"}[1m])) by (le, instance, type))[24h:1m]) > 1','note': 'TiKV 中Raft写入响应时间过长'
},
'TiDB.tikv.TiKV_scheduler_command_duration_seconds': {'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket[20m])) by (le, instance, type)  / 1000)[24h:20m]) ','pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_command_duration_seconds_bucket[20m])) by (le, instance, type)  / 1000)[24h:20m])  > 20 ','note': 'TiKV 调度器请求响应时间过长'
},
'TiDB.tikv.TiKV_scheduler_latch_wait_duration_seconds': {'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket[20m])) by (le, instance, type))[24h:20m]) ','pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_scheduler_latch_wait_duration_seconds_bucket[20m])) by (le, instance, type))[24h:20m])  > 20','note': 'TiKV 调度器锁等待响应时间过长'
},
'TiDB.tikv.TiKV_write_stall': {'pql_max': 'max_over_time(delta(tikv_engine_write_stall{instance=~".*"}[10m])[24h:10m])','pql': 'max_over_time(delta(''tikv_engine_write_stall{instance=~".*"}[10m])[24h:10m]) > 10','note': 'TiKV 中存在写入积压'
},
​
# TiKV 3
'TiDB.tikv.TiKV_server_report_failure_msg_total': {'pql_max': 'max_over_time(sum(rate(tikv_server_report_failure_msg_total{type="unreachable"}[10m])) BY (instance)[24h:10m])','pql': 'max_over_time(sum(rate(tikv_server_report_failure_msg_total{type="unreachable"}[10m])) BY (instance)[24h:10m]) > 10','note': 'TiKV 节点报告失败次数过多'
},
'TiDB.tikv.TiKV_channel_full_total': {'pql_max': 'max_over_time(sum(rate(tikv_channel_full_total{instance=~".*"}[10m])) BY (type, instance)[24h:10m])','pql': 'max_over_time(sum(rate(tikv_channel_full_total{instance=~".*"}[10m])) BY (type, instance)[24h:10m]) > 0','note': 'TIKV 通道已占满 tikv 过忙'
},
'TiDB.tikv.TiKV_raft_log_lag': {'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance=~".*"}[1m])) by (le,instance))[24h:10m])','pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance=~".*"}[1m])) by (le, ''instance))[24h:10m]) > 5000','note': 'TiKV 中 raft 日志同步相差过大'
},
'TiDB.tikv.TiKV_thread_unified_readpool_cpu_seconds': {'pql_max': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"unified_read_po*", instance=~".*"}[1m])) by (instance)[24h:1m])','pql': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"unified_read_po*", instance=~".*"}[1m])) ''by (instance)[24h:1m]) > 0.7','note': 'unifiled read 线程池使用率大于70%'
},
'TiDB.tikv.TiKV_low_space': {'pql_max': 'sum(tikv_store_size_bytes{type="available"}) by (instance) / sum(tikv_store_size_bytes{type="capacity"}) by (instance)','pql': 'sum(tikv_store_size_bytes{type="available"}) by (instance) / sum(tikv_store_size_bytes{type="capacity"}) by (instance) < 0.3','note': 'TiKV 当前存储可用空间小于阈值'
},

由于有的告警项是获取了5分钟或者10分钟的数据,在写步长的时候也要同步修改为5分钟或者10分钟,保持一致可以保证,检查能覆盖选定的全部时间段,并且不会重复计算造成资源浪费。

顺带一提,如果不加 max_over_time 可以获取到带有时间戳的全部数据,而不是只获取到最大的一个数据。这个带时间戳的全部数据可以方便画图,像grafana那样展示数据趋势。

巡检脚本

了解了以上所有知识,我们就可以开始编写巡检脚本了。

这是笔者和同事共同编写的一部分巡检脚本,最重要的是 tasks 中的 PromQL ,在脚本执行之前要写好 PromQL,其他部分可以随意更改。如果一次性巡检天数太多,比如一次巡检一个月的时间,Prometheus 可能会因检查数据太多而报错的,所以使用的时候要注意报错信息,避免漏掉一些巡检项。

# -*- coding: utf-8 -*-
import subprocess
import re
import datetime
import requests
import sys
import pandas as pd
​
days = None
​
​
def get_cluster_name():try:command = "tiup cluster list"result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)output, error = result.communicate()cluster_name_match = re.search(r'([a-zA-Z0-9_-]+)\s+tidb\s+v', output.decode('utf-8'))if cluster_name_match:return cluster_name_match.group(1)else:return Noneexcept Exception as e:print("An error occurred:", e)return None
​
​
def display_cluster_info(cluster_name):if not cluster_name:print("Cluster name not found.")return
​try:command = "tiup cluster display {0}".format(cluster_name)result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)output, error = result.communicate()return output.decode('utf-8')except Exception as e:print("An error occurred:", e)
​
​
def extract_id_role(output):id_role_dict = {}lines = output.strip().split("\n")for line in lines[2:]:parts = line.split()if parts[0].startswith('10.'):node_id, role = parts[0], parts[1]id_role_dict[node_id] = rolereturn id_role_dict
​
​
def get_prometheus_ip(data_dict):prometheus_ip = Nonefor key, value in data_dict.items():if value == 'prometheus':prometheus_ip = keybreakreturn prometheus_ip
​
​
def get_tasks():global daystasks = {# TiKV 1'TiDB.tikv.TiKV_server_is_down': {'pql': 'probe_success{group="tikv",instance=~".*"} == 0','pql_max': '','note': 'TiKV 服务不可用'},'TiDB.tikv.TiKV_node_restart': {'pql': 'changes(process_start_time_seconds{job="tikv",instance=~".*"}[24h])> 0','pql_max': 'max(changes(process_start_time_seconds{job="tikv",instance=~".*"}[24h]))','note': 'TiKV 服务5分钟内出现重启'},'TiDB.tikv.TiKV_GC_can_not_work': {'pql_max': '','pql': 'sum(increase(tikv_gcworker_gc_tasks_vec{task="gc", instance=~".*"}[2d])) by (instance) < 1 and (sum(increase(''tikv_gc_compaction_filter_perform{instance=~".*"}[2d])) by (instance) < 1 and sum(increase(''tikv_engine_event_total{cf="write",db="kv",type="compaction",instance=~".*"}[2d])) by (instance) >= 1)','note': 'TiKV 服务GC无法工作'},# TiKV 2'TiDB.tikv.TiKV_raftstore_thread_cpu_seconds_total': {'pql_max': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance)[24h:1m])','pql': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"(raftstore|rs)_.*"}[1m])) by (instance)[24h:1m])  > 0.8','note': 'TiKV raftstore 线程池 CPU 使用率过高'},'TiDB.tikv.TiKV_approximate_region_size': {'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance=~".*"}[1m])) ''by (le,instance))[24h:1m])','pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_region_size_bucket{instance=~".*"}[1m])) ''by (le,instance))[24h:1m]) > 1073741824','note': 'TiKV split checker 扫描到的最大的 Region approximate size 大于 1 GB'},'TiDB.tikv.TiKV_async_request_write_duration_seconds': {'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket''{type="write", instance=~".*"}[1m])) by (le, instance, type))[24h:1m])','pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_storage_engine_async_request_duration_seconds_bucket''{type="write", instance=~".*"}[1m])) by (le, instance, type))[24h:1m]) > 1','note': 'TiKV 中Raft写入响应时间过长'},'TiDB.tikv.TiKV_write_stall': {'pql_max': 'max_over_time(delta(tikv_engine_write_stall{instance=~".*"}[10m])[24h:10m])','pql': 'max_over_time(delta(''tikv_engine_write_stall{instance=~".*"}[10m])[24h:10m]) > 10','note': 'TiKV 中存在写入积压'},
​# TiKV 3'TiDB.tikv.TiKV_server_report_failure_msg_total': {'pql_max': 'max_over_time(sum(rate(tikv_server_report_failure_msg_total{type="unreachable"}[10m])) BY (instance)[24h:10m])','pql': 'max_over_time(sum(rate(tikv_server_report_failure_msg_total{type="unreachable"}[10m])) BY (instance)[24h:10m]) > 10','note': 'TiKV 节点报告失败次数过多'},'TiDB.tikv.TiKV_channel_full_total': {'pql_max': 'max_over_time(sum(rate(tikv_channel_full_total{instance=~".*"}[10m])) BY (type, instance)[24h:10m])','pql': 'max_over_time(sum(rate(tikv_channel_full_total{instance=~".*"}[10m])) BY (type, instance)[24h:10m]) > 0','note': 'TIKV 通道已占满 tikv 过忙'},'TiDB.tikv.TiKV_raft_log_lag': {'pql_max': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance=~".*"}[1m])) by (le,instance))[24h:10m])','pql': 'max_over_time(histogram_quantile(0.99, sum(rate(tikv_raftstore_log_lag_bucket{instance=~".*"}[1m])) by (le, ''instance))[24h:10m]) > 5000','note': 'TiKV 中 raft 日志同步相差过大'},'TiDB.tikv.TiKV_thread_unified_readpool_cpu_seconds': {'pql_max': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"unified_read_po*", instance=~".*"}[1m])) by (instance)[24h:1m])','pql': 'max_over_time(avg(rate(tikv_thread_cpu_seconds_total{name=~"unified_read_po*", instance=~".*"}[1m])) ''by (instance)[24h:1m]) > 0.7','note': 'unifiled read 线程池使用率大于70%'},'TiDB.tikv.TiKV_low_space': {'pql_max': 'sum(tikv_store_size_bytes{type="available"}) by (instance) / sum(tikv_store_size_bytes{type="capacity"}) by (instance)','pql': 'sum(tikv_store_size_bytes{type="available"}) by (instance) / sum(tikv_store_size_bytes{type="capacity"}) by (instance) < 0.3','note': 'TiKV 当前存储可用空间小于阈值'},}for key, value in tasks.items():for inner_key, inner_value in value.items():if isinstance(inner_value, str) and 'pql' in inner_key:value[inner_key] = inner_value.replace("24h:", f"{24 * days}h:").replace("[24h]", f"[{24 * days}h]")return tasks
​
​
def request_prome(prometheus_address, query):try:response = requests.get('http://%s/api/v1/query' % prometheus_address, params={'query': query})return responseexcept:return None
​
​
def has_response(prometheus_address, query):response = request_prome(prometheus_address, query)if not response:return Falsetry:if response.json()["data"]['result']:return Trueelse:return Falseexcept:return False
​
​
def check_prome_alive(prometheus_address):# dummy query is used to judge if prometheus is alivedummy_query = 'probe_success{}'return has_response(prometheus_address, dummy_query)
​
​
def find_alive_prome(prometheus_addresses):if check_prome_alive(prometheus_addresses):return prometheus_addressesreturn None
​
​
# ip:port -> ip_port
def decode_instance(instance):return instance.replace(':', '_')
​
​
def check_metric(alert_name, prometheus_address, pql, is_value, pql_max):record = []try:is_warning = "异常"response = request_prome(prometheus_address, pql)alert_name = alert_name.split('.')result = response.json()['data']['result']
​# 判断是否出现异常if len(result) == 0:is_warning = "正常"if pql_max == '':result = [{'metric': {}, 'value': [0, '0']}]else:response = request_prome(prometheus_address, pql_max)result = response.json()['data']['result']
​for i in result:# 判断是否按节点显示if 'instance' in i['metric']:instance = i['metric']['instance']node = decode_instance(instance)else:node = '集群'# 判断是否有typeif 'type' in i['metric']:type = i['metric']['type']else:type = '无类型'value = i['value'][1]
​if value == 'NaN':value = 0else:value = round(float(value), 3)message = "%s,%s,%s,%s,%s,%s,%s,%s" % (datetime.datetime.now(), node, alert_name[1], alert_name[2], type, is_warning, is_value, value)print(message)record.append(message)except Exception as e:print(alert_name[2] + "----An error occurred check_metric:", e)returnreturn record
​
​
def csv_report(record):data = pd.DataFrame([line.split(',') for line in record],columns=['timestamp', 'ip_address', 'service', 'event_type', 'type', 'status', 'description','value'])grouped = data.groupby("service")writer = pd.ExcelWriter("inspection_report.xlsx", engine="xlsxwriter")for name, group in grouped:group.to_excel(writer, sheet_name=name, index=False)worksheet = writer.sheets[name]for i, col in enumerate(group.columns):column_len = max(group[col].astype(str).str.len().max(), len(col)) + 2worksheet.set_column(i, i, column_len)writer.save()
​
​
def run_tasks(role_metrics, prometheus_address):record = []for alert in role_metrics:pql = role_metrics[alert]['pql']is_value = role_metrics[alert]['note']pql_max = role_metrics[alert]['pql_max']message = check_metric(alert, prometheus_address, pql, is_value, pql_max)for data in message:record.append(data)csv_report(record)
​
​
def run_script(prometheus_addresses):active_prometheus_address = find_alive_prome(prometheus_addresses)
​# check if all prometheus are downif not active_prometheus_address:sys.exit()tasks = get_tasks()run_tasks(tasks, active_prometheus_address)
​
​
def get_user_input():global daystry:user_input = int(input("请输入需要巡检的天数: "))days = user_inputexcept ValueError:print("输入无效,请输入一个有效的数字。")
​
​
if __name__ == "__main__":# 输入巡检天数get_user_input()
​prometheus_ip = '10.3.65.136:9091'# prometheus_ip = Noneif prometheus_ip is None:cluster_name = get_cluster_name()cluster_info = display_cluster_info(cluster_name)id_role_dict = extract_id_role(cluster_info)print(id_role_dict)prometheus_ip = get_prometheus_ip(id_role_dict)print(prometheus_ip)run_script(prometheus_ip)

总结

一个完善的巡检脚本的编写是一个长期的工作。因为时间有限,笔者只编写了基于 Prometheus 的一部分巡检项,有兴趣的同学可以继续编写更多巡检项。

目前巡检脚本都是基于 Prometheus 的数据来作判断,但是在真实的巡检当中,dba还会查看一些 Prometheus 没有的数据,比如表的健康度、一段时间内的慢SQL、热力图、日志信息等等,这些信息在后面一些时间,可能会慢慢入到巡检脚本中。

最后,笔者能力有限,如有错误的地方欢迎指出。


----------------------------------------------------

作者介绍:caiyfc,来自神州数码钛合金战队,是一支致力于为企业提供分布式数据库TiDB整体解决方案的专业技术团队。团队成员拥有丰富的数据库从业背景,全部拥有TiDB高级资格证书,并活跃于TiDB开源社区,是官方认证合作伙伴。目前已为10+客户提供了专业的TiDB交付服务,涵盖金融、证券、物流、电力、政府、零售等重点行业。

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

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

相关文章

【Android】完美解决安卓报错Module entity with name: xxx should be available、No module问题

问题截图&#xff1a; 解决方法&#xff1a; 找到settiings.gradle文件&#xff0c;修改文件名称&#xff0c;比较项目名大小写&#xff0c;更改之后&#xff0c;果然解决了

【C语言】操作符详解(二)

目录 移位操作符 左移操作符 右移操作符 位操作符:&、|、^、~ 一道面试题 移位操作符 <<左移操作符 >>右移操作符注:移位…

智能优化算法应用:基于布谷鸟算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于布谷鸟算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于布谷鸟算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.布谷鸟算法4.实验参数设定5.算法结果6.参考文…

点评项目——用户签到模块

2023.12.13 通常app为了吸引用户每天登陆app&#xff0c;会设定一个签到模块&#xff0c;本章就来实现用户签到模块&#xff0c;包含签到功能和签到统计功能。 BitMap用法 通常使用二进制位来记录每个月的签到情况&#xff0c;签到记录为1&#xff0c;未签到记录为0。每一个bi…

MacOS环境配置一系列问题的汇总,方便以后自己查看

环境配置一系列问题的汇总&#xff0c;方便以后自己查看 man brew报错“No manual entry for brew” 解决方法记录&#xff1a; 解决问题之前尝试的方法&#xff1a; Linking manuals from Homebrew1 https://apple.stackexchange.com/questions/111061/linking-manuals-f…

2000-2022年上市公司数字化转型数据(年报词频、文本统计)

2000-2022年上市公司数字化转型数据&#xff08;年报词频、文本统计&#xff09; 1、时间&#xff1a;2000-2022年 2、来源&#xff1a;上市公司年报、巨潮资讯网 3、方法说明&#xff1a;参考管理世界中吴非&#xff08;2021&#xff09;的做法&#xff0c;对人工智能技术、…

做题总结 19. 删除链表的倒数第 N 个结点(快慢指针思想)

19. 删除链表的倒数第 N 个结点 最初的想法进阶实现&#xff08;Java&#xff09; 最初的想法 计算出链表中的节点总数&#xff0c;然后遍历找到目标节点并删除。 class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {ListNode cur head;int count0;w…

Ubuntu 设置共享文件夹

一、在Windows中建立一个英文的文件夹 注意&#xff1a;新建文件夹的名称一定要是英文的&#xff0c;不能出现中文的路径&#xff08;可能出现问题&#xff09; 二、在VMware中添加共享文件 3: VMware安装VMware Tools 一般安装成功桌面上会显示这个安装包&#xff0c;&…

大型软件编程实际应用实例:个体诊所电子处方系统,使用配方模板功能输入症状就可开出处方软件操作教程

一、前言&#xff1a; 在开电子处方的时候&#xff0c;如果能够输入症状就可以一键导入配方&#xff0c;则在很大程度上可以节省很多时间。而且这个配方可以根据自己的经验自己设置&#xff0c;下面以 佳易王诊所电子处方软件为例说明。 二、具体一键导入配方详细操作教程 点击…

前端面试题【构建工具篇】

前言 在现代前端开发中&#xff0c;构建工具是不可或缺的一部分。Webpack 和 Vite 作为两个主流的构建工具&#xff0c;都在前端开发中发挥着重要作用。本文将深入探讨一些与构建工具相关的面试题&#xff0c;涵盖Webpack的loader、主要作用&#xff0c;以及Webpack中的HMR&am…

NLP中的Seq2Seq与attention注意力机制

文章目录 RNN循环神经网络seq2seq模型Attention(注意力机制)总结参考文献RNN循环神经网络 RNN循环神经网络被广泛应用于自然语言处理中,对于处理序列数据有很好的效果,常见的序列数据有文本、语音等,至于为什么要用到循环神经网络而不是传统的神经网络,我们在这里举一个…

学习Java第68天,MVC架构模式理论简介

一.MVC架构模式 MVC&#xff08;Model View Controller&#xff09;是软件工程中的一种软件架构模式&#xff0c;它把软件系统分为模型、视图和控制器三个基本部分。用一种业务逻辑、数据、界面显示分离的方法组织代码&#xff0c;将业务逻辑聚集到一个部件里面&#xff0c;在改…

01java语言基础

一、初始计算机和Java语言 1、计算机的体系结构 a.计算机的基本概念 计算机(Computer)俗称电脑&#xff0c;是现代一种用于高级计算&#xff0c;使用非常广泛 的设备&#xff0c;主要由计算机硬件和计算机软件两个部分组成。 • 计算机硬件是客观存在的各种计算机相关设备&a…

提升工作效率,尽在Microsoft Office LTSC 2021 for Mac!

在当今的办公环境中&#xff0c;高效率的工作是每个人都追求的目标。作为全球领先的办公软件套装&#xff0c;Microsoft Office LTSC 2021 for Mac将为您提供一站式的解决方案&#xff0c;帮助您轻松应对各种工作任务。 首先&#xff0c;Microsoft Office LTSC 2021 for Mac拥…

C# 通俗讲解Public、Private以及Protected、[HideInInspector]、[SerializeField]的区别

一、故事背景 1.我画了一幅画&#xff0c;把它放在室外&#xff0c;所有人都可以看见这个画&#xff0c;所有人都可以对这个画进行修改。 2.我非常非常努力&#xff0c;赚了一大笔钱&#xff0c;这笔钱&#xff0c;只能我和我的子孙后代用&#xff0c;但如果我的孩子需要传给他…

jmap命令的实现原理解析

jmap可以做什么&#xff1f; 1、jmap -histo[:live] 通过histo选项&#xff0c;打印当前java堆中各个对象的数量、大小。 如果添加了live&#xff0c;只会打印活跃的对象。 2、jmap -dump:[live,]formatb,file 通过-dump选项&#xff0c;把java堆中的对象dump到本地文件&a…

Windows系统使用wsl执行shell脚本报错解决

Windows系统使用wsl执行Shell脚本报错解决 Shell脚本的需求说明 判断字符串str1中是否包含字符串str2&#xff0c;使用~操作符 代码编写 #!/bin/bashstr1"hello" str2"llo"if [[ $str1 ~ $str2 ]];thenecho "$str1 contain $str2" fi脚本编写…

leaflet使用热力图报L找不到的问题ReferenceError: L is not defined at leaflet-heat.js:11:3

1.在main.js中直接引入会显示找不到L 2.解决办法 直接在组件中单独引入使用 可以直接显示出来。 至于为什么main中不能引入为全局&#xff0c;我是没找到&#xff0c;我的另外一个项目可以&#xff0c;新项目不行&#xff0c;不知哪里设置的问题

【小聆送书第二期】人工智能时代AIGC重塑教育

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;正文&#x1f4dd;活动参与规则 参与活动方式文末详见。 &#x1f4cb;正文 AI正迅猛地…

js常用操作

js可选链操作符&#xff08;?.&#xff09;和空值合并操作符&#xff08;??&#xff09; 可选链操作符&#xff08;?.&#xff09;用于访问可能为 null 或 undefined 的属性或方法&#xff0c;以避免出现错误。例如&#xff1a; const person {name: John,age: 30 };con…