Docker安装Logstash,并结合logback实现ELK日志收集

news2024/9/22 17:20:05

拉取镜像

docker pull docker.elastic.co/logstash/logstash:8.14.3

创建文件夹

mkdir /mnt/data/logstash

创建默认文件

先不做目录挂载,run出一个容器

docker run -d --rm -it docker.elastic.co/logstash/logstash:8.14.3

将config和pipeline从容器cp到宿主机

docker cp confident_driscoll:/usr/share/logstash/config /mnt/data/logstash/

docker cp confident_driscoll:/usr/share/logstash/pipeline /mnt/data/logstash/

停止容器(启动时配置了 --rm ,停止后会自动删除)

docker stop confident_driscoll

创建容器

docker run --name logstash \
--network es-net \
-p 5044:5044 \
-v /mnt/data/logstash/pipeline/:/usr/share/logstash/pipeline/ \
-v /mnt/data/logstash/config/:/usr/share/logstash/config/ \
--restart=always \
-d docker.elastic.co/logstash/logstash:8.14.3

修改配置文件

修改挂载出来的默认 logstash.yml 文件,这里位于 /mnt/data/logstash/config/ 目录,修改后配置如下:

http.host: "0.0.0.0"
xpack.monitoring.elasticsearch.hosts: [ "http://es:9200" ]
xpack.monitoring.elasticsearch.username: "elastic"
xpack.monitoring.elasticsearch.password: "123456"

注意:这里明文的用户民和密码安全性较差,可改用使用 API 密钥的方式。

修改默认的 logstash.conf 文件,创建日志处理规则,位于 /mnt/data/logstash/pipeline/ 目录,修改后如下:

input {  
  tcp {  
    port => 5044  
    codec => json_lines  
  }  
}  
  
output {  
  elasticsearch {  
    hosts => ["es:9200"]  
    index => "iov-%{+YYYY.MM.dd}"  
    document_type => "_doc"
    user => "elastic"
    password => "123456"  
  }
}

注:上面的各个属性值,根据实际环境进行配置。
修改后重启 logstash 服务。

配置logback

引入相关依赖包,这里使用的 gradle ,maven同理,如下:


    // Logback Classic
    implementation 'ch.qos.logback:logback-classic:1.2.3' // 使用适合你项目的版本

    // Logstash Logback Encoder
    implementation 'net.logstash.logback:logstash-logback-encoder:6.6' // 使用适合你项目的版本

修改 logback-spring.xml 文件,若不存在则创建。位于 resources 下(这里是springboot项目),内容如下:

<configuration>
    <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>192.168.1.110:5044</destination> <!-- Logstash的TCP端口 -->
        <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
    </appender>

    <root level="info">
        <appender-ref ref="LOGSTASH" />
        <!-- 可以添加其他appender,如控制台或文件appender -->
    </root>
</configuration>

根据实际情况将上面的192.168.1.110:5044部分改成真实 logstash 的IP和端口。

验证功能

确认 ES 和 Logstash 已经正常启动,然后启动 spring boot 服务。进入 Kibana 可以看到已经有日志文件写入到es中。如下:
image.png

格式化日志

未处理前日志信息统一在message字段中,不方便我们管理。
这里主要针对设备的上传的原始数据进行格式化。
比如iov中的日志格式如下:

// 正常的设备上报信息,用imei区分
logger.warn("IMEI: {},DATA: {}", imei, ByteBufUtil.hexDump(rawMessage));

// 打印的错误日志
logger.error("测试报错日志");

// 系统报错
throw new DecoderException();

修改logstash.conf

为了更方便管理日志,将 logstash.conf 修改如下:

input {
  tcp {
    port => 5044
    codec => json_lines
  }
}

filter {

  # 使用grok过滤器来从message字段中提取imei和data
  grok {
    match => { "message" => "IMEI: %{WORD:imei},DATA: %{GREEDYDATA:data}" }
    # 注意:如果imei可能包含特殊字符(如空格、冒号等),您可能需要调整WORD为正则表达式模式
    # 例如:(?<imei>[^,]+) 来匹配从IMEI:到逗号之前的所有内容

    tag_on_failure => ["_grok_failed"] # 当grok失败时添加此标签
  }

  # 检查grok是否成功以及level字段的值
  if "_grok_failed" in [tags] and [level] == "WARN" {
    # 如果grok失败且level为WARN,则丢弃事件(可以通过drop过滤器或在输出阶段排除)
    drop {}
  } else if [level] in ["ERROR", "FATAL", "SEVERE"] and "_grok_failed" in [tags] {
    # 如果level是ERROR及以上,但grok失败或没有imei和data,则设置imei为"error",data为message
    mutate {
      add_field => { "imei" => "error" }
      add_field => { "data" => "%{message}" }
      remove_field => ["tags"]
    }
  }

  # ruby {
  #  code => "
  #   require 'time'
  #    event.set('datetime', event.get('@timestamp').time.localtime().strftime('%Y-%m-%d %H:%M:%S'))
  #  "
  # }

  # 使用mutate过滤器来移除不需要的字段
  mutate {
    remove_field => ["level_value", "@version", "message"]
  }

}

output {
  elasticsearch {
    hosts => ["es:9200"]
    index => "iov-%{+YYYY.MM.dd}"
    document_type => "_doc"
    user => "elastic"
    password => "123456"
  }
}

具体更改内容,看上面的注解。
更改后重启logstash服务。

添加es动态模板

根据上面的 logstash.conf 配置文件,动态生成的索引。数据结构除了 @timestamp 字段为date类型,其他的都将默认声明为 text 类型,即都将分词。如下:

"iov-2024.07.30": {
    "aliases": {},
    "mappings": {
      "properties": {
        "@timestamp": {
          "type": "date"
        },
        "data": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "imei": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "level": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "logger_name": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "thread_name": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
      }
    },
    "settings": {
      "index": {
        "routing": {
          "allocation": {
            "include": {
              "_tier_preference": "data_content"
            }
          }
        },
        "number_of_shards": "1",
        "provided_name": "iov-2024.07.30",
        "creation_date": "1722317697099",
        "number_of_replicas": "1",
        "uuid": "5OoDo8eITpqdhBMKPMBaeg",
        "version": {
          "created": "8505000"
        }
      }
    }
  }
}

这会导致浪费大量的空间和性能,比如我们的imei、logger_name、thread_name和level字段,完全没有必要进行分词,精准查找即可,所以现在我们需要在logstash上传日志到es时,精准的建立字段的类型。这里采用es动态索引模板的方式。
使用 Kibana 开发者工具执行如下指令,创建索引模板:

PUT /_index_template/iov_template  
{  
  "index_patterns": ["iov-*"],   
  "template": {  
    "mappings": {  
      "properties": {  
        "@timestamp": {  
          "type": "date"  
        },  
        "data": {  
          "type": "text" ,
          "fields": {  
            "keyword": {  
              "type": "keyword",  
              "ignore_above": 256  
            }  
          }  
        },
        "imei": {  
          "type": "keyword"  
        },  
        "level": {  
          "type": "keyword"  
        },  
        "logger_name": {  
          "type": "keyword"  
        },  
        "thread_name": {  
          "type": "keyword"  
        }  
      }  
    }
  }  
}

这个模板将自动应用于任何以iov-开头的新索引。当让适用于以iov-开头,后跟日期(如iov-2024.07.30)的索引名称。如果字段名称可以和模板对应上,那么字段类型将跟模板一致。
创建模板后,再次测试日志插入,查看类型,发现模板已经生效:

{
  "iov-2024.07.30": {
    "aliases": {},
    "mappings": {
      "properties": {
        "@timestamp": {
          "type": "date"
        },
        "data": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "imei": {
          "type": "keyword"
        },
        "level": {
          "type": "keyword"
        },
        "logger_name": {
          "type": "keyword"
        },
        "thread_name": {
          "type": "keyword"
        }
      }
    },
    "settings": {
      "index": {
        "routing": {
          "allocation": {
            "include": {
              "_tier_preference": "data_content"
            }
          }
        },
        "number_of_shards": "1",
        "provided_name": "iov-2024.07.30",
        "creation_date": "1722320800301",
        "number_of_replicas": "1",
        "uuid": "pGrsRAN4Rg6HsP2mmhrGpA",
        "version": {
          "created": "8505000"
        }
      }
    }
  }
}

好了,现在日志数据已经有了需要的字段,并且正确的设置了字段类型。

设置索引声明周期

创建ILM配置文件:

PUT _ilm/policy/log_retention_policy  
{  
  "policy": {  
    "phases": {  
      "hot": {  
        "min_age": "0ms",  
        "actions": {  
          "rollover": {  
            "max_age": "2d",  // 这里设置为2天,但实际上在单机模式下,你可能需要外部脚本来控制滚动  
            "max_size": "30gb"  // 你可以设置一个足够大的大小限制,以避免基于大小的滚动  
          }  
        }  
      },  
      "delete": {  
        "min_age": "7d",  
        "actions": {  
          "delete": {}  
        }  
      }  
    }  
  }  
}

这个策略将包含两个阶段:hotdeletehot 阶段将不执行任何特殊操作(除了可能的滚动),而 delete 阶段将在索引达到7天时删除它。
注意:在单机模式下,rollover 操作可能不会自动按 max_age 触发,因为Elasticsearch的ILM滚动通常依赖于索引的大小或索引中事件的时间戳(如果配置了max_docs_per_partition)。在这种情况下,你可能需要依赖外部脚本来控制索引的滚动。

修改索引模板

修改索引模板,使用上面定义的ILM策略。修改后如下:

PUT /_index_template/iov_template  
{  
  "index_patterns": ["iov-*"],   
  "template": {  
    "mappings": {  
      "properties": {  
        "@timestamp": {  
          "type": "date"  
        },  
        "data": {  
          "type": "text" ,
          "fields": {  
            "keyword": {  
              "type": "keyword",  
              "ignore_above": 256  
            }  
          }  
        },
        "imei": {  
          "type": "keyword"  
        },  
        "level": {  
          "type": "keyword"  
        },  
        "logger_name": {  
          "type": "keyword"  
        },  
        "thread_name": {  
          "type": "keyword"  
        }  
      }  
    },
    "settings": {  
      "number_of_shards": 1,  
      "number_of_replicas": 1,  
      "index.lifecycle.name": "log_retention_policy",  
      "index.lifecycle.rollover_alias": "iov_logs"  
    }
    
  }  
}

在索引模板中,指定了之前定义的ILM策略,并设置一个别名用于索引滚动。

查看日志数据

使用Kibana开发者工具

在 Kibana 开发者工具中执行如下语句可以查看到对应日志记录:

GET /iov-2024.07.30/_search

查询结果如下:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 3,
      "relation": "eq"
    },
    "max_score": 1,
    "hits": [
      {
        "_index": "iov-2024.07.30",
        "_id": "Lo9RApEBFb2jjdrpyxet",
        "_score": 1,
        "_ignored": [
          "data.keyword"
        ],
        "_source": {
          "data": """io.netty.handler.codec.DecoderException
	at com.thgcgps.iovservice.network.codec.MsgFrameDecode.decode(MsgFrameDecode.java:109)
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:493)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:432)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:271)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:829)
""",
          "thread_name": "nioEventLoopGroup-5-1",
          "@timestamp": "2024-07-30T06:26:38.415Z",
          "level": "ERROR",
          "logger_name": "com.thgcgps.iovservice.network.server.TcpServerHandler",
          "imei": "error"
        }
      },
      {
        "_index": "iov-2024.07.30",
        "_id": "L49RApEBFb2jjdrpyxet",
        "_score": 1,
        "_source": {
          "data": "测试报错日志",
          "thread_name": "nioEventLoopGroup-5-1",
          "@timestamp": "2024-07-30T06:26:38.413Z",
          "level": "ERROR",
          "logger_name": "com.thgcgps.iovservice.network.codec.MsgFrameDecode",
          "imei": "error"
        }
      },
      {
        "_index": "iov-2024.07.30",
        "_id": "LY9RApEBFb2jjdrpyxet",
        "_score": 1,
        "_source": {
          "data": "78780a1344050400020035c60e0d0a",
          "thread_name": "nioEventLoopGroup-5-1",
          "@timestamp": "2024-07-30T06:26:38.412Z",
          "level": "WARN",
          "logger_name": "com.thgcgps.iovservice.network.codec.MsgFrameDecode",
          "imei": "0868755128633153"
        }
      }
    ]
  }
}

可以看到,这里的数据即我们代码中测试的相关日志,即系统报错,打印的error日志和最关注的设备上报日志。但这里的结果并不方便观察和整理。并且时间格式也不理想,时区也并不是北京时间。

使用Kibana的Discover功能

进入 Kibana 管理界面,点击 Discover ,如图:
image.png

进入后创建一个数据视图:
image.png

创建时,输入名称,和索引模式即可:
image.png
这里的索引模式需要和匹配的源对应,支持通配符,上图即使用 * 通配符创建。填写好后,点击使用而不保存(即临时的数据视图,刷新页面或跳转页面后就销毁了)或者 点击保存数据视图到Kibana(下次进入可以直接选择无需重建)。这里因为日志日期会有变动,创建了一个临时的。

创建好的数据视图如下:
image.png

点击“可用字段”后的“+”号,即可查看指定字段。如图:
image.png
“选中字段”即当前产看的字段。

若想搜索指定的值,可在搜索框中使用KQL语法搜索数据(光标点击搜素框后即有提示),如图:
image.png
因为imei字段之前设置了字段类型,控制其不分词,所以当使用imei字段时,要精确搜索。而这里的data字段支持全文搜索,如下:
image.png
注意:这里因为设置模板时,没有指定分词器,所以当前时默认的 standard 分词器。分词效果并不好由于数据上报的日志由数字和字母组成,也不是很适合分词。可以考虑data也不进行分词。或者更改一下数据上报的日志,比如区分出心跳和定位等信息,然后即可根据特定类型进行查找。

上面的 @timestamp 字段,日期时间虽然是对的,但并不符合我们常见的表达方式,这里可以设置其格式以达到预期效果,点击该字段,选择“编辑数据视图字段”,如下:
image.png
打开“设置格式”选项,格式选择“日期”,然后根据指定规则,输入如下格式 “yy-MM-DD HH:mm:ss”,然后点击保存,如下:
image.png
保存后时间展示如下:
image.png

若需要导出,可点击“共享”,然后选择“CSV Reports”
image.png
然后点击 “Generate CSV”即可,然后右下角会弹出一个快速访问Reports的连接,点击即可跳转到下载页面
image.png
也可以点击“Management”中的“Stack Management”功能如下
image.png
然后点击“Reporting”即可看到可下载的CSV文件。
image.png

**

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

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

相关文章

BEV学习---LSS-1:论文原理及代码串讲

目前在自动驾驶领域&#xff0c;比较火的一类研究方向是基于采集到的环视图像信息&#xff0c;去构建BEV视角下的特征完成自动驾驶感知的相关任务。所以如何准确的完成从相机视角向BEV视角下的转变就变得由为重要。目前感觉比较主流的方法可以大体分为两种&#xff1a; 1、显式…

Linux驱动.之字符设备驱动框架,及手动,自动注册创建/dev下设备节点详解(一)

一、Linux 字符设备驱动框架 Linux一切皆文件&#xff0c;通过VFS虚拟文件系统&#xff0c;把所有外设的细节都隐藏起来&#xff0c;最后都以文件的形态呈现于应用层&#xff0c; 操作设备&#xff0c;就像对文件的打开、关闭、读写、控制。这种方式完美的统一了对用户的接口&a…

WIN/MAC 图像处理软件Adobe Photoshop PS2024软件下载安装

目录 一、软件概述 1.1 基本信息 1.2 主要功能 二、系统要求 2.1 Windows 系统要求 2.2 macOS 系统要求 三、下载 四、使用教程 4.1 基本界面介绍 4.2 常用工具使用 4.3 进阶操作 一、软件概述 1.1 基本信息 Adobe Photoshop&#xff08;简称PS&#xff09;是一款…

快手怎么免费的去掉视频水印?分享这三个工具给你

​ 我们经常会遇到想要保存的视频带有水印&#xff0c;这不仅影响美观&#xff0c;也不利于分享。为了解决这个问题&#xff0c;我将分享三个免费去除视频水印的工具&#xff0c;帮助你轻松去除水印&#xff0c;享受无干扰的视频体验。 工具一&#xff1a;奈斯水印助手(小程序…

挑战Infiniband, 爆改Ethernet(3)

Infiniband的特点 Infiniband在HPC集群和AI集群中获得广泛应用是和Infiniband技术的特点密切相关的&#xff0c;今天我们看看Infiniband的技术特点: 1&#xff09;网络分层模型 这个分层模型并不是Infiniband技术独有的&#xff0c;各种现代网络技术都普遍采用分层模型来设计…

在.NET开发中使用 Excel 的最佳方式之一:MiniExcel

前言 在桌面开发应用中&#xff0c;处理 Excel 文件是一个非常常见的需求。无论是生成报表、导入数据&#xff0c;还是与客户或其他系统进行数据交换&#xff0c;Excel 文件都扮演着重要角色。在 .NET 生态系统中&#xff0c;有许多处理 Excel 文件的工具和库&#xff0c;其中…

Flutter ListView 实现不同样式 item

我们在实际开发中会创建显示不同类型内容的列表。以下是使用 Flutter 创建此类结构的方法&#xff1a; 1. 创建包含不同类型项目的数据源。 2. 将数据源转换为小部件列表。 创建包含不同类型项目的数据源 项目类型 要表示列表中不同类型的项目&#xff0c;请为每种类型的项目…

链表(静态/动态创建,遍历,计数,查找,在节点的前/后方插入新节点,头插法,尾插法)

目录 一、前言 二、链表的静态创建和遍历 三、链表统计节点&#xff0c;查找节点是否存在 四、从指定节点的后方插入新节点 五、从指定节点的前方插入新节点 六、动态创建链表&尾插法 七、头插法 八、删除节点 一、前言 链表本质是一个结构体&#xff0c;结构体里…

19448 算法设计与分析(第五版)习题2-7 集合划分问题

### 思路 1. **递归公式**&#xff1a;根据提示信息&#xff0c;递归公式为&#xff1a; - \( f(n, x) f(n-1, x-1) f(n-1, x) \times x \) - 其中&#xff0c;\( f(n, x) \) 表示将 \( n \) 个元素分成 \( x \) 个非空子集的方案数。 2. **边界条件**&#xff1a; …

【STM32】串口(异步通信部分)

经典的串口接口硬件说实话在现在的电脑上已经很难见到了&#xff0c;而是被USB这种通用的串行接口替代了&#xff0c;哪怕外部设备要以串口连接到电脑&#xff0c;都需要进行各种硬件转换。但不得不说&#xff0c;在工业领域&#xff0c;串口还是一个非常常用的数据传输方式。 …

LEAP模型在能源环境发展、碳排放建模预测及分析中实践应用

采用部门分析法建立的LEAP&#xff08;Long Range Energy Alternatives Planning System/ Low emission analysis platform&#xff0c;长期能源可替代规划模型&#xff09;是一种自下而上的能源-环境核算工具&#xff0c;由斯德哥尔摩环境研究所和美国波士顿大学联合研发。该模…

后端Web之登录校验(上篇)

目录 1.概述 2.会话技术 3.JWT令牌 1.概述 基础的登录功能实际上就是查询数据库中有没有输入的用户和密码&#xff0c;有就放行&#xff0c;没有就返回错误信息&#xff0c;根据三层架构进行开发&#xff1a; controller层&#xff1a; service层&#xff1a; mapper层&…

Visual C++ 的免费绘图库 EasyX下载安装

EasyX Graphics Library 是针对 Visual C++ 的免费绘图库,支持 VC6.0 ~ VC2022,简单易用,学习成本极低,应用领域广泛。目前已有许多大学将 EasyX 应用在教学当中。 下载地址:EasyX Graphics Library for C++ 1、应用 2、文中有很多的C++应用案例 3、编程需要的数学知识 …

19530 2的幂次方表示

### 思路 1. **分解为2的幂次方**&#xff1a;将输入的正整数n分解为若干个2的幂次方之和。 2. **递归表示**&#xff1a;使用递归的方法将每个幂次方表示为2的幂次方形式。 3. **组合结果**&#xff1a;将所有的幂次方表示组合成最终的结果。 ### 需要注意的点 - 需要处理幂次…

8.21 补题

六题 C 16进制世界 链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 这是一个16进制的世界&#xff0c;比如522的16进制是20A。 在5月22日那天&#xff0c;有人送给Bob一些月饼&#xff0c;每个月饼有饱食度和幸福度两个属性。 现…

计算机网络面试真题总结(二)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 在浏览器中输入 URL 地址到显示主页的过程&#xff1f; URL解析&a…

【记录】基于Windows系统安装rust环境的过程

到官网下载安装包【入门 - Rust 程序设计语言 (rust-lang.org)】 ![[Pasted image 20240703142911.png]] 选择1&#xff0c;快速安装 选择编译配置&#xff0c;1为标准 安装完成 验证是否安装完毕 rustc --versioncargo --version验证成功&#xff01;

【数据结构篇】~二叉树(堆)

【数据结构篇】~二叉树&#xff08;堆&#xff09; 二叉树1.树2.树的组成3.二叉树4.堆1.向上调整算法2.向下调整算法3.堆排序 4.topk问题源码 二叉树 1.树 树的概念与结构​ 树是一种非线性的数据结构&#xff0c;它是由 n&#xff08;n>0&#xff09; 个有限结点组成一个…

利用“2+1链动模式小程序AI智能名片S2B2C商城源码”优化企业参与外部社群策略

摘要&#xff1a;在当今数字化时代&#xff0c;企业参与外部社群已成为其市场扩张、品牌塑造及用户增长不可或缺的一环。然而&#xff0c;面对浩如烟海的社群类型&#xff0c;包括行业论坛、地区性论坛、特定兴趣爱好的论坛以及短视频网站等&#xff0c;如何精准选择并有效介入…

16.C基础_内存管理

内存分区 1、整体框图 内存分为代码区、全局区、栈区、堆区。代码区和全局区在代码编译完之后就已经确定&#xff0c;栈区和堆区是在程序运行时进行开辟和释放的。整体内存分区框图如下&#xff1a; 对于一个进程&#xff0c;它一共有4G的空间&#xff0c;其中0~3G为上述的4个…