Prometheus监控组件在SpringBoot项目中使用实践

news2024/11/26 4:24:17

Prometheus监控组件在SpringBoot项目中使用实践

  • 时间:2024/7/29

  • 背景:本人最近参与的一个项目,要监控远程软硬件以及本地软硬件,实现远程监控以及告警功能。

  • 开发环境: JDK1.8,Maven,PostgreSQL,SpringBoot2.5.7,Prometheus

  • 需解决问题:1、如何获取远程软硬件监控信息 2、如何获取本地软硬件监控信息 3、远程和本地监控信息是否统一处理 4、如何设置Prometheus告警发送,并通过Java转发给前端

  • PostgreSQL,Prometheus依赖

    #PostgreSQL jdbc依赖
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.2.5</version>
        <scope>runtime</scope>
    </dependency>
    
    #Prometheus依赖
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
    
    <dependency>
        <groupId>io.prometheus</groupId>
        <artifactId>simpleclient</artifactId>
        <version>0.11.0</version>
    </dependency>
    <dependency>
        <groupId>io.prometheus</groupId>
        <artifactId>simpleclient_common</artifactId>
        <version>0.11.0</version>
    </dependency>
    <dependency>
        <groupId>io.prometheus</groupId>
        <artifactId>simpleclient_hotspot</artifactId>
        <version>0.11.0</version>
    </dependency>
    <dependency>
        <groupId>io.prometheus</groupId>
        <artifactId>simpleclient_httpserver</artifactId>
        <version>0.11.0</version>
    </dependency>
    
    <dependency>
        <groupId>io.prometheus</groupId>
        <artifactId>simpleclient</artifactId>
        <version>0.11.0</version>
    </dependency>
    

​ 如果你对Prometheus完全不了解,可以先看第五部分Prometheus的简单介绍。第一到第四部分围绕项目中遇到的问题展开。

1、如何获取远程软硬件监控信息

1.需求分析

​ 由于要监控的远程服务器软硬件,本身属于涉密相关,无法通过传统的远程ssh等方式直接连接等方式来获取数据。所以第三方给我们提供了一个接口,和调用该接口的token,通过调用该接口拿到远程软硬件监控信息。

2.实现思路

​ 因此只需要在我们SpringBoot中写一个Ctroller接口来调用第三方接口并将其返回的监控数据转成Prometheus需要的格式。然后在Prometheus设置定时从我们这个接口拿数据(这块是运维人员来设置的)

3.主要问题与解答

​ 根据上述"实现思路",分析可得出以下2个问题

  1. 如何将第三方接口返回的监控数据转成Prometheus需要的格式?

    答:Gauge类,来自package io.prometheus.client包。我们根据第三方接口返回的内容来创建不同的Gauge对象。

    以某个第三方接口返回的值为例,返回的内容如下:

    image-20240801170321151

    ​ 根据接口返回值,我们可以设置Gauge对象如下。name和help字段都是固定可以如下这样写,labelNames字段用来标记该Gauge(labelNames里面可以人为约定字段,如tag就是我们人为指定的一个字段,可用来存放一些信息,可用来区分不同的queryTime,后面也可以从tag中取值),register(commandRegistry)是将Gauge对象注册到commandRegistry注册器中(注意Gauge对象必须注册到一个注册器中,否则Prometheus接收不到该数据)。

        //在Controller类中定义注册器commandRegistry
        private static final CollectorRegistry commandRegistry = new CollectorRegistry();
        private static final Gauge queryTime = Gauge.build()
                .name("query_time")
                .help("Query Time")
                .labelNames("tag")
                .register(commandRegistry); //将queryTime注册到commandRegistry注册器中
    
        private static final Gauge commandAircraftId = Gauge.build()
                .name("command_aircraft_id")
                .help("Command Aircraft Id")
                .labelNames("tag")
                .register(commandRegistry);
    
        private static final Gauge commandAircraftName = Gauge.build()
                .name("command_aircraft_name")
                .help("Command Aircraft Name")
                .labelNames("tag")
                .register(commandRegistry);
    
        private static final Gauge commandAircraftStatus = Gauge.build()
                .name("command_aircraft_status")
                .help("Command Aircraft Status")
                .labelNames("tag")
                .register(commandRegistry);
    

    ​ 然后怎么使用这些Gauge对象呢?还是以上述第三方接口为例,请看下面一段代码。这里面的resultList是调用第三方接口接收到的数据。注意guaua对象可以通过set来指定值,但值只能是数字类型的,不能是字符串类型

    /**
         * 将List<CommandAircraftInfo.Result>转成Gauge并注册
         */
    private void resultsToGauge(List<CommandAircraftInfo.Result> resultList) {
        for (int i = 0; i < resultList.size(); i++) {
            CommandAircraftInfo.Result result = resultList.get(i);
    	    //重重重重点
            //Guaua对象可通过set来设置值,但值只能是数字类型的。这里是将Guaua对象queryTime设置为当前时间戳
            queryTime.labels(serverConfig.getCommandChinese() + i).setToCurrentTime(); 
            //将Guaua对象commandAircraftId的值设为result.getCommandAircraftId()
            commandAircraftId.labels(serverConfig.getCommandChinese() + i).set(null != result.getCommandAircraftId() ? Double.parseDouble(result.getCommandAircraftId()) : -1); //-1表示没有id
            //因为result.getCommandAircraftName()是字符串,不能用set赋值,就set(i)表示是第几个
            commandAircraftName.labels(serverConfig.getCommandChinese() + i).set(i);
            //同id,可以用set赋值
            commandAircraftStatus.labels(serverConfig.getCommandChinese() + i).set(null != result.getCommandAircraftStatus() ? Double.parseDouble(result.getCommandAircraftStatus()) : -1); //-1表示未知情况
            
        }
    }
    
    1. 如何将转成Prometheus需要的格式的数据写到Prometheus中?

      答:response.setContentType(TextFormat.CONTENT_TYPE_004); Writer writer = response.getWriter(); TextFormat.write004(writer, commandRegistry.metricFamilySamples()); writer.close();

      下面为调用第三方API获得相关数据并提交给Prometheus的接口代码

    /**
     * 调用第三方API获得相关数据并提交给Prometheus
     *
     * @param response
     * @throws IOException
     */
    @GetMapping("/metric")
    public void metric(HttpServletResponse response) throws IOException {
        RestTemplate restTemplate = new RestTemplate();
        String url = "http://" + serverConfig.getCommandAircraftIp() + ":" + serverConfig.getCommandAircraftPort() + serverConfig.getCommandAircraftInfo() + serverConfig.getIPC_TOKEN(); //调用的API地址
        CommandAircraftInfo commandAircraftInfo = restTemplate.getForObject(url, CommandAircraftInfo.class);
    
        if (commandAircraftInfo != null && "200".equals(commandAircraftInfo.getStatus())) {
            List<CommandAircraftInfo.Result> resultList = commandAircraftInfo.getResult();
            //如果resultList.size()大于commandAircraftSum
            commandAircraftSum = resultList.size(); //更新指挥机数量
            
            resultsToGauge(resultList);
            response.setContentType(TextFormat.CONTENT_TYPE_004);
            Writer writer = response.getWriter();
            TextFormat.write004(writer, commandRegistry.metricFamilySamples());
            writer.close();
        } else {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
    

2、如何获取本地软硬件监控信息

​ Prometheus可以通过配置来监控本地软硬件信息,监控远程软硬件信息。

​ 实际只要将每一项中的 targets和 metrics_path配置完整即可。

​ 例如:targets: [‘10.0.4.225:9401’] 和 metrics_path: /bdfl2/api/v1/rest/services/cms/monitor/fusion/hardware/metric 写完整。拼在一起就是10.0.4.225:9401:/bdfl2/api/v1/rest/services/cms/monitor/fusion/hardware/metric,这个路径就算我们后端服务中将第三方接口中数据转成Prometheus格式的接口路径。点进去如下

image-20240802160028221

​ 再点击Metrics,进入Prometheus监控数据界面

image-20240802160114977

​ 本项目详细prometheus.yml配置文件如下,

global:
  scrape_interval:     5s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.

# alertmanager的地址
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - alertmanager:9093
 
# 读取告警触发条件规则
rule_files:
  - 'rules/*.yml'

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ['prometheus:9090']
        labels:
          instance: prometheus
          tags: 10.0.4.29 #prometheus部署服务器IP/服务器名称 (这里就是用来监控本地服务器的硬件信息)
          
#####################主机监控############################
  - job_name: 'node_exporter'
    # 抓取间隔 
    scrape_interval: 8s
    static_configs:
      - targets: ['10.0.4.225:9401']
        labels:
          instance: 10.0.4.225
          group: '服务器-10.0.4.225' #标识:区别不同的服务器
      - targets: ['10.0.4.222:9400']
        labels:
          instance: 10.0.4.222
          group: '服务器-10.0.4.222' #标识:区别不同的服务器 
                   
#####################数据库监控#####################
  - job_name: 'postgresql'
    static_configs:
      - targets: ['10.0.4.32:9187']
        labels:
          instance: postgres
          tags: 10.0.4.225 #postgresql部署服务器IP/服务器名称
          group: 'pg数据库-10.0.4.225' #标识:区别不同的服务器的postgresql数据库
          
#####################nginx监控#####################          
  - job_name: 'nginx_exporter'
    static_configs:
      - targets: ['10.0.4.32:9113']
        labels:
          instance: nginx 
          tags: 10.0.4.225 #nginx部署服务器IP/服务器名称
          group: 'nginx-10.0.4.225' #标识:区别不同的服务器的nginx服务
                       
##################tcp监控###################################
  - job_name: "tcp_port_status"
    metrics_path: /probe #Prometheus从目标HTTP服务抓取的度量指标(metrics)的路径
    params:
      module: [tcp_connect]
    static_configs:
      - targets: ['10.0.4.225:9602']
        labels:
          instance: 10.0.4.225:9602
          tags: bdfl-server #被监控应用服务说明
          group: 'bdfl-server-10.0.4.225' #标识:区别不同的服务
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 10.0.4.32:9115
        
# # # # # # # # # 某某机 # # # # # # # # # # # # # #       
  - job_name: 'rongheji-hardware'
    scrape_interval: 8s
    metrics_path: /bdfl2/api/v1/rest/services/cms/monitor/fusion/hardware/metric
    static_configs:
      - targets: ['10.0.4.94:9602']
        labels:
          instance: 10.0.4.94 #某某机IP地址或编号
          group: '某某机-10.0.4.94' #标识:区别不同的融合处理机
      - targets: ['10.0.4.225:9602']
        labels:
          instance: 10.0.4.225 #某某机IP地址或编号
          group: '某某机-10.0.4.225' #标识:区别不同的某某机
                                 
  - job_name: 'rongheji-software'
    scrape_interval: 8s
    metrics_path: /bdfl2/api/v1/rest/services/cms/monitor/fusion/software/metric
    static_configs:
      - targets: ['10.0.4.94:9602']
        labels:
          instance: 10.0.4.94 #某某机IP地址或编号
          group: '某某机-10.0.4.94' #标识:区别不同的某某机
      - targets: ['10.0.4.225:9602']
        labels:
          instance: 10.0.4.225 #某某机IP地址或编号
          group: '某某机-10.0.4.225' #标识:区别不同的某某机
          
                        
# # # # # # # # #指挥机 # # # # # # # # # # # # # #       
  - job_name: 'Command-metric'
    scrape_interval: 8s
    metrics_path: /bdfl2/api/v1/rest/services/cms/monitor/command/aircraft/metric
    static_configs:
      - targets: ['10.0.4.94:9602']
        labels:
          instance: 10.0.4.94 #指挥机IP地址或编号
          group: '指挥机-10.0.4.94' #标识:区别不同的指挥机
      - targets: ['10.0.4.225:9602']
        labels:
          instance: 10.0.4.225 #指挥机IP地址或编号  
          group: '指挥机-10.0.4.225' #标识:区别不同的指挥机
                       
  - job_name: 'Command-starry'
    scrape_interval: 8s
    metrics_path: /bdfl2/api/v1/rest/services/cms/monitor/command/aircraft/starry-sky-info
    static_configs:
      - targets: ['10.0.4.94:9602']
        labels:
          instance: 10.0.4.94 #指挥机IP地址或编号
          group: '指挥机-10.0.4.94' #标识:区别不同的指挥机
      - targets: ['10.0.4.225:9602']
        labels:
          instance: 10.0.4.225 #指挥机IP地址或编号
          group: '指挥机-10.0.4.225' #标识:区别不同的指挥机
      
  - job_name: 'Command-wave'
    scrape_interval: 8s
    metrics_path: /bdfl2/api/v1/rest/services/cms/monitor/command/aircraft/wave-beam-info
    static_configs:
      - targets: ['10.0.4.94:9602']
        labels:
          instance: 10.0.4.94 #指挥机IP地址或编号
          group: '指挥机-10.0.4.94' #标识:区别不同的指挥机
      - targets: ['10.0.4.225:9602']
        labels:
          instance: 10.0.4.225 #指挥机IP地址或编号
          group: '指挥机-10.0.4.225' #标识:区别不同的指挥机
      
# # # # # # # # # 某某卡 # # # # # # # # # # # # # #       
  - job_name: 'Command-userCard'
    scrape_interval: 8s
    metrics_path: /bdfl2/api/v1/rest/services/cms/monitor/command/aircraft/user-card-info
    static_configs:
      - targets: ['10.0.4.94:9602']
        labels:
          instance: 10.0.4.94 #指挥机IP地址或编号
          group: '指挥机-10.0.4.94' #标识:区别不同的指挥机
      - targets: ['10.0.4.225:9602']
        labels:
          instance: 10.0.4.225 #指挥机IP地址或编号
          group: '指挥机-10.0.4.225' #标识:区别不同的指挥机

3、远程和本地监控信息是否统一处理

​ 可能有同学会有疑问,为何后端不将第三方接口数据直接返回给前端,反而要这么麻烦转成Prometheus的格式,让Prometheus拿到呢。因为Prometheus有非常强大告警功能,而后端将第三方数据返回给前端,要自己做报警功能,根据数据来判断是否报警,这增加了业务的复杂性。所以最好让远程和本地要监控的软硬件信息都统一从Prometheus中获取,统一由Prometheus发出警告。

4、如何设置Prometheus告警发送,并通过Java转发给前端

​ 见prometheus.yml配置了警告规则,然后Prometheus会提供一个获取所有告警信息的接口给你。你可以通过这个接口拿到告警信息,剩下的就根据业务来对告警信息做相应的处理就行了。比如我这的业务就是定时扫描该接口拿到告警数据保存到数据库,然后展示给前端。

​ 我这边Prometheus提供的接口为http://10.0.4.32:9093/api/v2/alerts/groups?silenced=false&inhibited=false&active=true,点进去显示如下,可以用json工具JSON在线解析及格式化验证 - JSON中文网打开,方便看

image-20240802164502563

​ 对了有的报警没有处理,一直再,那保存到数据库中是每次都保存岂不是很麻烦?Prometheus的alerts中给你提供了一个字段叫**“fingerprint”,可用来唯一标识该告警信息,你只需要把这个字段也保存到数据存,然后存告警信息前判断一下告警信息的"fingerprint"**在数据库里有没有。没有就存,有的话就不存。我的定时保存告警代码在service层中,如下

@Slf4j
@Service
public class AlertRemindServiceImpl extends ServiceImpl<AlertRemindMapper, AlertRemind> implements AlertRemindService {

    private final AlertRemindMapper alertRemindMapper;

    private final ServerConfig serverConfig;
    public AlertRemindServiceImpl(AlertRemindMapper reminderMapper, ServerConfig serverConfig) {
        this.alertRemindMapper = reminderMapper;
        this.serverConfig = serverConfig;
    }
    
    
    @Scheduled(fixedDelay = 10 * 1000)
    public void refreshShortMessageByRest() {
        log.info("向Prometheus获取全部告警信息!");
        RestTemplate restTemplate = new RestTemplate();
        //调用Prometheus告警api,List<PrometheusAlert>
        ResponseEntity<List> prometheusAlerts = restTemplate.getForEntity(serverConfig.getPrometheusAlertUrl(), List.class);
        if (CollectionUtils.isEmpty(prometheusAlerts.getBody())) {
            log.error("Prometheus告警数据为空!");
            return;
        }
        prometheusAlerts.getBody().forEach(item -> {
            JSONObject jsonObject = new JSONObject((HashMap<String, Object>) item);
            PrometheusAlert prometheusAlert = jsonObject.toJavaObject(PrometheusAlert.class);
            for (int i = 0; i < prometheusAlert.getAlerts().size(); i++) {
                String fingerprint = prometheusAlert.getAlerts().get(i).getFingerprint();

                if (StringUtils.isEmpty(fingerprint)) {
                    log.error("告警信息fingerprint为空!");
                    continue;
                }

                if (StringUtils.isEmpty(prometheusAlert.getAlerts().get(i).getLabels().getGroup())) {
                    log.error("告警信息group为空!");
                    continue;
                }

                //判断该alert的是否要保存(若description和summary已经在数据库中则不需要保存)
                if (judgeIsOrNotSave(fingerprint)) {
                    //需要保存
                    AlertRemind alertRemind = new AlertRemind();
                    alertRemind.setDescription(prometheusAlert.getAlerts().get(i).getAnnotations().getDescription());
                    alertRemind.setSummary(prometheusAlert.getAlerts().get(i).getAnnotations().getSummary());
                    alertRemind.setAfterRead(0);
                    alertRemind.setType(prometheusAlert.getAlerts().get(i).getLabels().getName());
                    alertRemind.setId(SnowFlakeIdGenerator.getInstance().nextId());
                    String startsAt = prometheusAlert.getAlerts().get(i).getStartsAt();
                    String endsAt = prometheusAlert.getAlerts().get(i).getEndsAt();
                    //将时间格式2024-07-29T01:59:04.561Z 转成时间戳并保存
                    alertRemind.setCreateTime(Instant.parse(startsAt).toEpochMilli());
                    alertRemind.setUpdateTime(Instant.parse(endsAt).toEpochMilli());
                    alertRemind.setSeverity(prometheusAlert.getAlerts().get(i).getLabels().getSeverity());
                    alertRemind.setFingerprint(prometheusAlert.getAlerts().get(i).getFingerprint());
                    alertRemind.setGroupUrl(prometheusAlert.getAlerts().get(i).getLabels().getGroup());
                    this.save(alertRemind);
                    log.info("新增告警信息:{}", alertRemind);
                }else {
                    //不需要保存
                    log.info("告警信息已存在,fingerprint:{}", fingerprint);
                }
            }
        });
    }
    private boolean judgeIsOrNotSave(String fingerprint) {
        LambdaQueryWrapper<AlertRemind> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.isNotEmpty(fingerprint),AlertRemind::getFingerprint, fingerprint);
        return this.count(wrapper) == 0;
    }
}

5、Prometheus简单介绍(这部分内容来自GPT4-0,可跳过)

Prometheus 是一个开源的系统监控和报警工具包,是监控和告警解决方案的一个重要组成部分。Prometheus 的设计思想主要是通过时间序列数据进行监控和告警。下面是对 Prometheus 的一些关键特性的简单介绍:

1. 时间序列数据存储

Prometheus 使用时间序列数据存储,即以时间为索引的数据点。每个数据点都有一个时间戳和一个对应的值。数据点通过拉取(pull)模型从目标采集。

2. 多维数据模型

Prometheus 使用多维数据模型,通过指标名称和键值对(标签)来识别数据。例如,一个 CPU 使用率的指标可以有不同的标签来区分主机、实例或应用程序。

3. 高效的数据查询语言 - PromQL

Prometheus 提供了一个功能强大的查询语言 PromQL,用于对收集到的时间序列数据进行查询和分析。用户可以使用 PromQL 来生成即时的图表、统计数据或触发告警。

4. 自定义告警管理

Prometheus 内置了告警管理功能,通过定义告警规则,当某些条件满足时,会触发告警。告警规则使用 PromQL 表达式来定义告警条件。Prometheus 还可以将告警发送到 Alertmanager 进行进一步处理,例如发送邮件、短信或其他通知。

5. 易于扩展的生态系统

Prometheus 有着丰富的生态系统,支持多种导出器(exporters)和集成。导出器用于将不同系统或服务的指标数据导出到 Prometheus。例如,Node Exporter 可以用于监控 Linux 服务器的硬件和操作系统指标。

6. 服务发现和静态配置

Prometheus 支持多种服务发现机制,可以自动发现监控目标,例如 Kubernetes、Consul、Etcd 等。也可以通过静态配置方式手动添加监控目标。

7. 图形展示

Prometheus 本身提供了一个简单的 web 界面来展示监控数据和进行 PromQL 查询。此外,Prometheus 也与 Grafana 等图形展示工具高度集成,用户可以使用 Grafana 创建更复杂和美观的监控仪表盘。

8. 支持远程存储

虽然 Prometheus 内置的存储适用于中小规模的监控需求,但对于大规模或长期数据存储需求,可以使用远程存储解决方案,例如 Cortex、Thanos 等。

使用 Prometheus 的基本流程

  1. 安装 Prometheus:可以通过二进制文件、Docker 或 Kubernetes 部署 Prometheus。
  2. 配置 Prometheus:通过 prometheus.yml 文件配置数据采集目标和告警规则。
  3. 启动 Prometheus:运行 Prometheus 服务,开始数据采集。
  4. 定义告警规则:根据监控需求定义告警规则,并配置 Alertmanager 进行通知处理。
  5. 数据查询和可视化:使用 PromQL 查询数据,使用内置 web 界面或 Grafana 进行数据可视化。

示例配置

下面是一个简单的 prometheus.yml 配置示例:

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'node'
    static_configs:
      - targets: ['localhost:9100']

这个配置文件定义了一个名为 node 的抓取任务,它会每隔 15 秒从 localhost:9100 采集数据。

总的来说,Prometheus 是一个功能强大且灵活的监控和告警系统,适用于多种场景下的监控需求。其开源特性和活跃的社区使得它成为 DevOps 和 SRE 工具链中的重要组件。

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

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

相关文章

automa自动化工作流教程(三)循环元素进行操作

循环元素 选择器选中的必须是多个元素&#xff0c;如果是css选择器&#xff0c;举例&#xff1a;class属性有多个 .postTitle 并且要生成或填上循环id 有开发需求 call me

【轨物推荐】谈谈科学、工程与技术的关系

原创 宋华振 说东道西 2022年07月05日 23:34 上海 为什么要谈这个话题&#xff1f; 前几天&#xff0c;戴老师转发了一篇《中国科学创新四十年-可能还没搞明白科学和技术的基本概念》&#xff0c;这篇文章谈及了科学与技术的差异&#xff0c;其中谈到了几个误区&#xff0c;误…

HCIP实验-MGRE+OSPF

实验拓扑图&#xff1a; 实验要求&#xff1a; 1.R6为ISP&#xff0c;只能配置IP地址&#xff0c;R1-R5的环回为私有网段 2.R1/4/5为全连的MGRE结构&#xff0c; R1/2/3为星型的拓扑结构&#xff0c;R1为中心站点 3.所有私有网段可以互相通讯&#xff0c;私有网段使用ospf协…

【C++】————哈希表

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;C 创作时间 &#xff1a;2024年8月6日 前言&#xff1a; 在计算机科学的广袤世界中&#xff0c;数据结构犹如基石&#xff0c;支撑着各种高效算法的构建与运行。而哈希表&#xff08;Hash Table&#xff09;&#…

【Python系列】使用 `isinstance()` 替代 `type()` 函数

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

24. 两两交换链表中的节点(Java)

目录 题目描述&#xff1a;示例 &#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&am…

基于飞腾E2000的科东软件Intewell工业实时操作系统方案

科东软件Intewell工业实时操作系统是完全自主开发的产品&#xff0c;具有20年以上行业应用经验。Intewell基于微内核架构设计&#xff0c;具备高实时、高可靠等特点&#xff0c;同时虚拟化技术支持GPOSRTOS等多种操作系统架构&#xff0c;可实现实时和非实时业务融合应用&#…

揭秘LoRA:利用深度学习原理在Stable Diffusion中打造完美图像生成的秘密武器

文章目录 引言LoRA的原理LoRA在角色生成中的应用LoRA在风格生成中的应用LoRA在概念生成中的应用LoRA在服装生成中的应用LoRA在物体生成中的应用结论 引言 在生成式人工智能领域&#xff0c;图像生成模型如Stable Diffusion凭借其出色的生成效果和广泛的应用场景&#xff0c;逐…

渲染技术如何应对数据增长与计算挑战

随着科技的飞速发展&#xff0c;数字内容的制作与呈现变得日益复杂和精细&#xff0c;这对渲染技术提出了前所未有的挑战。特别是在数据爆炸式增长和计算需求急剧提升的背景下&#xff0c;如何优化渲染技术&#xff0c;以应对这些挑战&#xff0c;成为了一个亟待解决的问题。 …

牛客JS题(二十三)判断质数

注释很详细&#xff0c;直接上代码 涉及知识点&#xff1a; 原型链如何优雅的判断质数 题干&#xff1a; 我的答案 <!DOCTYPE html> <html><head><meta charsetutf-8></head><body><script type"text/javascript">/*** 素…

试用期没烦恼 神器!!!使用doxygen+Graphviz自动生成函数调用关系图

大家好&#xff0c;我是 小杰学长 使用doxygenGraphviz自动生成函数调用关系图 gitee源码仓库链接跳转 前言 1.下载 首先&#xff0c;下载2个软件&#xff0c; 最新版即可&#xff0c; Doxygen 下载地址&#xff1a; https://sourceforge.net/projects/doxygen/ 下载步骤…

OLAP技术与数据仓库:深度分析与决策支持

目录 一、OLAP 概述 二、OLAP应用场景 三、OLAP对数据仓库的意义 一、OLAP 概述 OLAP&#xff08;Online Analytical Processing&#xff09;即联机分析处理&#xff0c;是一种用于多维数据分析的技术和工具。它允许用户通过多维数据模型进行复杂的分析&#xff0c;以便快速浏览…

sqli-labs(超详解)——Lass32~Lass38

Lass32&#xff08;宽字节注入&#xff09; 源码 function check_addslashes($string) {$string preg_replace(/. preg_quote(\\) ./, "\\\\\\", $string); //escape any backslash$string preg_replace(/\/i, \\\, $string); …

【iOS】——AutoReleasePool底层原理及总结

自动释放池 AutoreleasePool自动释放池用来延迟对象的释放时机&#xff0c;将对象加入到自动释放池后这个对象不会立即释放&#xff0c;等到自动释放池被销毁后才将里边的对象释放。 自动释放池的生命周期 从程序启动到加载完成&#xff0c;主线程对应的runloop会处于休眠状…

C++初学(11)

不知不觉就第11篇了QWQ 11.1、指针和自由存储空间 之前提到了计算机程序在存储数据时必须跟踪的3个基本属性&#xff1a; &#xff08;1&#xff09;信息存储在何处&#xff1b; &#xff08;2&#xff09;存储的值为多少&#xff1b; &#xff08;3&#xff09;存储的信息…

GitHub爆赞的Web安全防护指南,网络安全零基础入门必备教程!

web安全现在占据了企业信息安全的很大一部分比重&#xff0c;每个企业都有对外发布的很多业务系统&#xff0c;如何保障web业务安全也是一项信息安全的重要内容。 然而Web 安全是一个实践性很强的领域&#xff0c;需要通过大量的练习来建立对漏洞的直观认识&#xff0c;并积累…

xtrabackup搭建MySQL 8.0 主从复制

xtrabackup搭建MySQL 8.0 主从复制 安装MySQL 8.0.37安装xtrabackupGTIDs初始化从库参考&#xff1a;GTID概述GTID相较与传统复制的优势GTID自身存在哪些限制GTID工作原理简单介绍如何开启GTID复制GTID与传统模式建立复制时候语句的不同点传统复制GTID复制 GTID同步状态简单解析…

Docker技术背景与应用:解决现代开发中的关键问题

目录 Docker技术背景与应用&#xff1a;解决现代开发中的关键问题 一、Docker的技术背景 1. 什么是Docker&#xff1f; 2. Docker的核心组件 3. Docker的历史发展 二、Docker解决了哪些问题&#xff1f; 1. 环境一致性问题 2. 依赖管理问题 3. 部署复杂性问题 4. 资源…

微信小程序实现上传照片功能

案例&#xff1a; html: <view class"zhengjianCont fontSize30" style"margin-bottom: 40rpx;"><view class"kuai"><image binderror"imageOnloadError" bind:tap"upladPhoto" data-params"business…

二叉树——2.对称二叉树

力扣题目链接 给定一个二叉树&#xff0c;检查它是否是镜像对称的。 示例&#xff1a; 上述的二叉树就是对称的。 在做二叉树题目时&#xff0c;最重要的是你要找到题目中的二叉树是怎么遍历的。本题中要检查二叉树是否镜像对称&#xff0c;这就不是单独判断某个父节点的左右…